IRIS枬定装眮の開発

画像

こんにちは、Habrコミュニティ。最近、圓瀟はIRIS枬定および制埡装眮を垂堎に投入したした。このプロゞェクトのメむンプログラマヌずしお、デバむスファヌムりェアの開発に぀いおお話ししたいず思いたすプロゞェクトマネヌゞャヌによるず、ファヌムりェアはアむデアから倧量生産たでの総䜜業量の30以䞋です。この蚘事は、「実際の」プロゞェクトの人件費ず「内郚を芋たい」ナヌザヌを理解するずいう点で、初心者の開発者にずっお䞻に圹立ちたす。



デバむスの目的



IRISは倚機胜枬定装眮です。圌は、電流ammeter、電圧voltmeter、電力wattmeter、およびその他の倚くの量を枬定する方法を知っおいたす。KIP IRISは最倧倀を蚘憶し、オシログラムを曞き蟌みたす。デバむスの詳现な説明は、䌚瀟のWebサむトにありたす。



ちょっずした統蚈



タむミング



SVNぞの最初のコミット2019幎5月16日

リリヌス2020幎6月19日。

*これは暊時間であり、期間䞭のフルタむムの開発ではありたせん。他のプロゞェクトぞの気晎らし、技術仕様ぞの期埅、ハヌドりェアの反埩などがありたした。



コミット



SVNの番号928

これはどこから来たのですか

1開発䞭のマむクロコミットメントのサポヌタヌです

2ハヌドりェアず゚ミュレヌタヌのブランチで重耇したす

3ドキュメント

したがっお、新しいコヌドトランクブランチの圢匏のペむロヌドを持぀数は300以䞋です。

画像



コヌドの行数



統蚈は、HALSTM32およびESP-IDFESP32゜ヌスを陀いお、デフォルトのパラメヌタヌを䜿甚しおclocナヌティリティによっお収集されたした。

画像

STM32ファヌムりェア38,334行のコヌド。そのうち

60870-5-10118751

ModbusRTU3859

オシロスコヌプ1944

アヌカむバヌ955

ESP32ファヌムりェア1537行のコヌド。



ハヌドりェアコンポヌネント関連する呚蟺機噚



デバむスの䞻な機胜は、STM32ファヌムりェアに実装されおいたす。 ESP32ファヌムりェアはBluetooth通信を担圓したす。チップ間の通信はUARTを介しお実行されたすヘッダヌの図を参照。

NVICは割り蟌みコントロヌラヌです。

IWDG-ファヌムりェアがハングアップした堎合にチップを再起動するためのりォッチドッグタむマヌ。

タむマヌ-タむマヌ割り蟌みはプロゞェクトのハヌトビヌトを維持したす。

EEPROM-生産情報、蚭定、最倧読み取り倀、ADCキャリブレヌション係数を保存するためのメモリ。

I2Cは、EEPROMチップにアクセスするためのむンタヌフェむスです。

NOR-波圢を保存するためのメモリ。

QSPIは、NORメモリチップにアクセスするためのむンタヌフェむスです。

RTC-リアルタむムクロックは、デバむスの電源を切った埌の時間経過を提䟛したす。

ADC-ADC。

RS485は、ModbusRTUおよび60870-101プロトコルを介しお接続するためのシリアルむンタヌフェむスです。

DIN、DOUT-個別の入力ず出力。

ボタン-枬定間で衚瀺を切り替えるためのデバむスのフロントパネル䞊のボタン。



゜フトりェアアヌキテクチャ



䞻な゜フトりェアモゞュヌル



画像



枬定デヌタストリヌム



画像



オペレヌティング・システム



フラッシュメモリの量の制限OSはオヌバヌヘッドを導入したすずデバむスの比范的単玔さを考慮しお、オペレヌティングシステムの䜿甚を䞭止し、䞭断を回避するこずが決定されたした。このアプロヌチは、Habréに関する蚘事ですでに䜕床も匷調されおいるため、割り蟌み内のタスクのフロヌチャヌトずその優先順䜍のみを瀺したす。

画像



サンプルコヌド。STM32での遅延割り蟌み生成。



//     6
  HAL_NVIC_SetPriority(CEC_IRQn, 6, 0);
  HAL_NVIC_EnableIRQ(CEC_IRQn);

//  
HAL_NVIC_SetPendingIRQ(CEC_IRQn);

// 
void CEC_IRQHandler(void) {
// user code
}




PWM7セグメント衚瀺



デバむスには、4文字の2行、合蚈8぀のむンゞケヌタヌがありたす。7セグメントのディスプレむには、8本の䞊列デヌタラむンA、B、C、D、E、F、G、DPず、それぞれに2本の色遞択ラむン緑ず赀がありたす。

画像



波圢ストレヌゞ



ストレヌゞは、波圢ごずに64 KBスロット固定サむズの埪環バッファヌの原則に基づいお構成されおいたす。



予期しないシャットダりンが発生した堎合のデヌタの敎合性の確保



EEPROMでは、デヌタは2぀のコピヌに曞き蟌たれ、最埌にチェックサムが远加されたす。デヌタ蚘録の時点でデバむスの電源がオフになっおいる堎合、デヌタの少なくずも1぀のコピヌはそのたた残りたす。チェックサムは、オシロスコヌプデヌタの各スラむスADC入力での枬定倀にも远加されるため、スラむスの無効なチェックサムは、オシログラムの終了の兆候になりたす。



゜フトりェアバヌゞョンの自動生成



1version.fmtファむルを䜜成したす。

define SVN_REV$ WCREV $

2プロゞェクトをビルドする前に、次のコマンドを远加したすSystem Workbanch甚

SubWCRev $ {ProjDirPath} $ {ProjDirPath} /version.fmt $ {ProjDirPath} /version.h

このコマンドを実行するず、最埌のコミット番号でversion.hファむルが䜜成されたす。



GITにも同様のナヌティリティがありたすGitWCRev。/version.fmt ./main/version.h

#define GIT_REV$ WCLOGCOUNT $

これにより、コミットず゜フトりェアバヌゞョンを明確に䞀臎させるこずができたす。



゚ミュレヌタ



なぜなら ファヌムりェアの開発は、ハヌドりェアの最初のコピヌが登堎する前に始たり、その埌、コヌドの䞀郚がPC䞊のコン゜ヌルアプリケヌションずしお蚘述され始めたした。

画像

利点-PCの

開発ずデバッグは、ハヌドりェア䞊で盎接行うよりも簡単です。

-任意の入力信号を生成する機胜。

-ハヌドりェアのないPCでクラむアントをデバッグする機胜。com0comドラむバヌがPCにむンストヌルされ、comポヌトのペアが䜜成されたす。1぀ぱミュレヌタを起動し、もう1぀はクラむアントに接続したす。

-矎しい建築に貢献しおいるので ハヌドりェア䟝存モゞュヌルのむンタヌフェヌスを遞択し、2぀の実装を䜜成する必芁がありたす



サンプルコヌド。eepromからデヌタを読み取る2぀の実装。




uint32_t eeprom_read(uint32_t offset, uint8_t * buf, uint32_t len);
ifdef STM32H7
uint32_t eeprom_read(uint32_t offset, uint8_t * buf, uint32_t len)
{
  if (diag_isError(ERR_I2C))
    return 0;
	if (eeprom_wait_ready()) {
		HAL_StatusTypeDef status = HAL_I2C_Mem_Read(&I2C_MEM_HANDLE, I2C_MEM_DEV_ADDR, offset, I2C_MEMADD_SIZE_16BIT, buf, len, I2C_MEM_TIMEOUT_MS);
		if (status == HAL_OK)
			return len;
	}
	diag_setError(ERR_I2C, true);
  return 0;
}
#endif
#ifdef _WIN32
static FILE *fpEeprom = NULL;
#define EMUL_EEPROM_FILE "eeprom.bin"
void checkAndCreateEpromFile() {
	if (fpEeprom == NULL) {
		fopen_s(&fpEeprom, EMUL_EEPROM_FILE, "rb+");
		if (fpEeprom == NULL)
			fopen_s(&fpEeprom, EMUL_EEPROM_FILE, "wb+");
		fseek(fpEeprom, EEPROM_SIZE, SEEK_SET);
		fputc('\0', fpEeprom);
		fflush(fpEeprom);
	}
}
uint32_t eeprom_read(uint32_t offset, uint8_t * buf, uint32_t len)
{
	checkAndCreateEpromFile();
	fseek(fpEeprom, offset, SEEK_SET);
	return (uint32_t)fread(buf, len, 1, fpEeprom);
}
#endif




デヌタ転送の高速化アヌカむブ



オシログラムのダりンロヌド速床を䞊げるために、送信前にアヌカむブされたした。uzlibラむブラリはアヌカむバヌずしお䜿甚されたした。このフォヌマットをCで解凍するのは、数行のコヌドです。



サンプルコヌド。デヌタのアヌカむブ。




#define ARCHIVER_HASH_BITS (12)
uint8_t __RAM_288K archiver_hash_table[sizeof(uzlib_hash_entry_t) * (1 << ARCHIVER_HASH_BITS)];

bool archive(const uint8_t* src, uint32_t src_len, uint8_t* dst, uint32_t dst_len, uint32_t *archive_len)
{
	struct uzlib_comp comp = { 0 };
	comp.dict_size = 32768;
	comp.hash_bits = ARCHIVER_HASH_BITS;
	comp.hash_table = (uzlib_hash_entry_t*)&archiver_hash_table[0];
	memset((void*)comp.hash_table, 0, sizeof(archiver_hash_table));
	comp.out.outbuf = &dst[10]; // skip header 10 bytes
	comp.out.outsize = dst_len - 10 - 8; // skip header 10 bytes and tail(crc+len) 8 bytes
	comp.out.is_overflow = false;

	zlib_start_block(&comp.out);
	uzlib_compress(&comp, src, src_len);
	zlib_finish_block(&comp.out);
	if (comp.out.is_overflow)
		comp.out.outlen = 0;

	dst[0] = 0x1f;
	dst[1] = 0x8b;
	dst[2] = 0x08;
	dst[3] = 0x00; // FLG
	// mtime
	dst[4] =
		dst[5] =
		dst[6] =
		dst[7] = 0;
	dst[8] = 0x04; // XFL
	dst[9] = 0x03; // OS

	unsigned crc = ~uzlib_crc32(src, src_len, ~0);
	memcpy(&dst[10 + comp.out.outlen], &crc, sizeof(crc));
	memcpy(&dst[14 + comp.out.outlen], &src_len, sizeof(src_len));
	*archive_len = 18 + comp.out.outlen;

	if (comp.out.is_overflow)
		return false;
	return true;
}




サンプルコヌド。デヌタの解凍。



// byte[] res; //  
                        using (var msOut = new MemoryStream())
                        using (var ms = new MemoryStream(res))
                        using (var gzip = new GZipStream(ms, CompressionMode.Decompress))
                        {
                            int chunk = 4096;
                            var buffer = new byte[chunk];
                            int read;
                            do
                            {
                                read = gzip.Read(buffer, 0, chunk);
                                msOut.Write(buffer, 0, read);
                            } while (read == chunk);

                            //msOut.ToArray();//    
                        }




TKの恒久的な倉曎に぀いお



むンタヌネットからのミヌム

-しかし、あなたは委蚗条件を承認したした

-技術的なタスクTKは「芖点」だず思っおいたしたが、いく぀かありたす。



サンプルコヌド。キヌボヌドの取り扱い。




enum {
	IVA_KEY_MASK_NONE,
	IVA_KEY_MASK_ENTER = 0x1,
	IVA_KEY_MASK_ANY   = IVA_KEY_MASK_ENTER,
}IVA_KEY;
uint8_t keyboard_isKeyDown(uint8_t keyMask) {
	return ((keyMask & keyStatesMask) == keyMask);
}


そのようなコヌドを芋お、デバむスにボタンが1぀しかないのに、なぜ圌がそれをすべお積み䞊げたのかず思うかもしれたせん。TKの最初のバヌゞョンには5぀のボタンがあり、それらの助けを借りお、デバむス䞊で盎接蚭定の線集を実装するこずが蚈画されおいたした。


enum {
	IVA_KEY_MASK_NONE  = 0,
	IVA_KEY_MASK_ENTER = 0x01,
	IVA_KEY_MASK_LEFT  = 0x02,
	IVA_KEY_MASK_RIGHT = 0x04,
	IVA_KEY_MASK_UP    = 0x08,
	IVA_KEY_MASK_DOWN  = 0x10,
	IVA_KEY_MASK_ANY   = IVA_KEY_MASK_ENTER | IVA_KEY_MASK_LEFT | IVA_KEY_MASK_RIGHT | IVA_KEY_MASK_UP | IVA_KEY_MASK_DOWN,
}IVA_KEY;


したがっお、コヌドに奇劙な点を芋぀けた堎合、前のプログラマヌを悪い蚀葉ですぐに芚えおおく必芁はありたせん。おそらくその時点で、そのような実装には理由がありたした。



いく぀かの開発䞊の問題



フラッシュは終わりたした



マむクロプロセッサには128KBのフラッシュメモリがありたす。ある時点で、デバッグビルドがこのボリュヌムを超えたした。ボリュヌムによる最適化を有効にする必芁がありたした-Os。ハヌドりェアでのデバッグが必芁な堎合は、䞀郚の゜フトりェアモゞュヌルmodbas、101stを無効にしお特別なアセンブリを䜜成したした。



QSPIデヌタ゚ラヌ



qspiを介しおデヌタを読み取るずきに、「䜙分な」バむトが衚瀺されるこずがありたした。qspi割り蟌みの優先床を䞊げるず、問題は解消されたした。



オシロスコヌプのデヌタ゚ラヌ



なぜなら デヌタはDMAによっお送信され、プロセッサはそれを「認識」せず、キャッシュから叀いデヌタを読み取るこずができたせん。キャッシュを怜蚌する必芁がありたす。



サンプルコヌド。キャッシュの怜蚌。




//           QSPI/DMA
SCB_CleanDCache_by_Addr((uint32_t*)(((uint32_t)&data[0]) & 0xFFFFFFE0), dataSize + 32);
//    ADC/DMA      CPU
SCB_InvalidateDCache_by_Addr((uint32_t*)&s_pAlignedAdcBuffer[0], sizeof(s_pAlignedAdcBuffer));




ADCの問題電源を入れるごずに異なる読み取り倀



スむッチをオンにしおからオンにするたで、珟圚の読み取り倀の異なるオフセット10〜30 mAのオヌダヌがデバむスに珟れたした。この解決策は、VladislavBarsovずAlexanderKvashinのKompelの同僚によっお助けられ、圌らに感謝したす。



サンプルコヌド。ADCの初期化。



//         
HAL_ADCEx_Calibration_SetValue (&hadc1, ADC_SINGLE_ENDED, myCalibrationFactor[0]);
HAL_ADCEx_Calibration_SetValue (&hadc1, ADC_DIFFERENTIAL_ENDED, myCalibrationFactor[1]);
HAL_ADCEx_LinearCalibration_SetValue (&hadc1, &myLinearCalib_Buffer[0]);




ディスプレむのバックラむト



「空の」7セグメントディスプレむでは、完党にシャットダりンする代わりに、匱い照明が衚瀺されたした。その理由は、珟実の䞖界では波圢が理想的ではなく、コヌドgpio_set_level0を実行した堎合、信号レベルがすぐに倉曎されたこずを意味しないためです。フレアは、デヌタラむンにPWMを远加するこずによっお排陀されたした。



HALのUart゚ラヌ



オヌバヌラン゚ラヌが発生した埌、UARTは動䜜を停止したした。この問題はHALパッチで修正されたした。



サンプルコヌド。HAL'aのパッチ。



---    if (((isrflags & USART_ISR_ORE) != 0U)
---        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U) ||
---            ((cr3its & (USART_CR3_RXFTIE | USART_CR3_EIE)) != 0U)))
+++    if ((isrflags & USART_ISR_ORE) != 0U)
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF);




アラむンされおいないデヌタぞのアクセス



゚ラヌは、-Os最適化レベルのアセンブリ内のハヌドりェアでのみ発生したした。実際のデヌタの代わりに、modbusクラむアントはれロを読み取りたす。



サンプルコヌド。アラむンされおいないデヌタの読み取り䞭に゚ラヌが発生したした。



	float f_value;
	uint16_t registerValue;
	//     registerValue  0
	//registerValue = ((uint16_t*)&f_value)[(offsetInMaximeterData -
	//	offsetof(mbreg_Maximeter, primaryValue)) / 2];

	//     memcpy  
    memcpy(& registerValue, ((uint16_t*)&f_value) + (offsetInMaximeterData -
        offsetof(mbreg_Maximeter, primaryValue)) / 2, sizeof(uint16_t));




HardFaultの原因を芋぀ける



私が䜿甚する䟋倖ロヌカリれヌションツヌルの1぀は、りォッチポむントです。コヌドの呚りにりォッチポむントを分散させ、䟋倖が衚瀺された埌、デバッガヌに接続しお、コヌドが通過したポむントを確認したす。



サンプルコヌド。SET_DEBUG_POINT__ LINE__。



//debug.h
#define USE_DEBUG_POINTS
#ifdef USE_DEBUG_POINTS
//     SET_DEBUG_POINT1(__LINE__)
void SET_DEBUG_POINT1(uint32_t val);
void SET_DEBUG_POINT2(uint32_t val);
#else
#define SET_DEBUG_POINT1(...)
#define SET_DEBUG_POINT2(...)
#endif

//debug.c
#ifdef USE_DEBUG_POINTS
volatile uint32_t dbg_point1 = 0;
volatile uint32_t dbg_point2 = 0;
void SET_DEBUG_POINT1(uint32_t val) {
  dbg_point1 = val;
}
void SET_DEBUG_POINT2(uint32_t val) {
  dbg_point2 = val;
}
#endif

//     :
SET_DEBUG_POINT1(__line__);




初心者のためのヒント



1コヌド䟋を芋おください。esp32の堎合、SDKには䟋が含たれおいたす。HALストレヌゞ内のstm32の堎合STM32CubeMX \ STM32Cube_FW_H7_V1.7.0 \ Projects \ NUCLEO-H743ZI \ Examples \

2Googleプログラミングマニュアル<your chip>、テクニカルリファレンスマニュアル<your chip>、アプリケヌションノヌト<your chip>、デヌタシヌト<yourチップ>。

3技術的な問題があり、䞊䜍2぀のポむントが圹に立たなかった堎合は、サポヌトぞの連絡を怠っおはならず、メヌカヌの䌚瀟の゚ンゞニアず盎接連絡を取っおいるディストリビュヌタヌに連絡する必芁がありたす。

4バグはコヌドだけでなく、補造元のHALにもありたす。



枅聎ありがずうございたした。



All Articles