こんにちはショウです。前回SPIでDMAとバースト転送を使うことができるようになったので、ここまでで、マイクロマウスを走行させるために必要な機能の確認はできました。
今回はフラッシュを使えるようにしていきます。マイクロマウスでは電源を落としても迷路情報を保存できるようにしておくと、トラブルで電源が落ちてしまっても再度探索をすることなく最短走行をできるようになります。
また、今回フラッシュ設定時に設定を間違えてトラブルを起こしたのでその対処法も書いていきます。
プログラムの作成
まず、今回はこれまで使用していたLLライブラリを使用できません。STM32のLLライブラリにはフラッシュを使うためのライブラリが用意されていないので、直接レジスタを書き込みます。(HALライブラリにはフラッシュのライブラリが用意されています。)
STM32G4シリーズのフラッシュにはシングルバンクモードとデュアルバンクモードが存在します。この2つはフラッシュメモリの分割の仕方が違い、デュアルバンクモードの方が細かく分割されています。フラッシュにデータを書き込む時には一度フラッシュの内部のデータをイレースしてから書き込む必要があり、イレースは分割された範囲ごとに行う必要があります。今回はデフォルトで設定されているデュアルバンクモードを使用します。
フラッシュの構成表(RM0440より)
デュアルバンクモードの場合、512KBのフラッシュ領域を2つのバンクに分割し、バンク1つを128ページに分割しています。この2KBのページ毎にイレースを行います。
フラッシュ領域に書き込みやイレースをするためにはフラッシュのロックを解除する必要があります。
まず、書き込み操作をするためのイレース関数を作成します。イレースにはページ消去、バンク消去、全体消去がありますが、必要範囲のみ削除できるページ削除を使います。
ページ消去(RM0440より)
必要な処理としてはBSYビットの確認、SRレジスタのエラークリア、CRレジスタのPERビット、BKERビット、PNB(ページ設定)を設定します。設定後にCRレジスタのSTRTビットをセットすると消去が開始されるので、消去終了をBSYビットのクリアされるまで待ちます。
今回はバンク2の127ページに書き込みを行います。フラッシュにはプログラムも書き込まれるため、データを保存するフラッシュの範囲とプログラムの保存範囲がかぶらないようにする必要があります。リンカスクリプトを使うと分離することができるようですが、今回は一番最後のページを使うことでプログラムサイズが大きくならない限り書き込まれないようにしています。
以下のようにプログラムを作成しました。
flash.h
__STATIC_INLINE void FLASH_Lock(void) { FLASH->CR |= FLASH_CR_LOCK; } __STATIC_INLINE void FLASH_Unlock(void) { FLASH->KEYR = FLASH_KEY1; FLASH->KEYR = FLASH_KEY2; }
flash.c
void FLASH_WaitBusy(void) { while(((FLASH->SR & FLASH_SR_BSY)== FLASH_SR_BSY)==1); } void FLASH_Erase(void) { FLASH_WaitBusy(); FLASH->SR &= 0x00000000; FLASH->CR &= 0x00000000; FLASH->CR |= (FLASH_CR_BKER); FLASH->CR |= FLASH_CR_PER; FLASH->CR |= (FLASH_CR_PNB & (0x7F<<3)); FLASH->CR |= FLASH_CR_STRT; FLASH_WaitBusy(); }
調べてみるとSTM32F3シリーズなどはフラッシュのサイズの分割が均等になっていないので、使う場所をリンカスクリプトを使ってプログラムが書き込まれないようにする必要があるようです。
書き込み方はデータシートを見るとこのようになっています。
フラッシュ書き込み(RM0440)
BSYビットの確認、エラーフラグのクリア、CRレジスタのPGビットをセットしてからデータをダブルワード分書き込みます。書き込み後はBSYビットの確認をしてEOPフラグのチェックとクリアをして、書き込みデータをすべて書き込んだらPCビットをクリアします。
EOPフラグのチェックとクリアは操作終了割り込みを使う場合にのみ使用するビットなので今回は使用していません。
これを元にデータを書き込むプログラムを作成します。
void FLASH_WriteByte(uint32_t address, uint64_t data) { uint32_t data2 = (uint32_t)(data >> 32); FLASH_WaitBusy(); FLASH->SR &= 0x00000000; FLASH->CR &= 0x00000000; FLASH->CR |= FLASH_CR_PG; *(__IO uint32_t*)address = (uint32_t)data; *(__IO uint32_t*)(address+4) = data2; FLASH_WaitBusy(); FLASH->CR &= ~(FLASH_CR_PG); } void FLASH_WriteData(uint32_t address, uint64_t* data, uint32_t size) { FLASH_Unlock(); FLASH_Erase(); do { FLASH_WriteByte(address, *data); }while(++address, ++data, --size); FLASH_Lock(); }
フラッシュからのデータの呼び出しにはmemcpyを使って読み出します。
memcpyを使うためにflash.cではstring.hをインクルードしています。
void FLASH_ReadData(uint32_t address, uint64_t* data, uint32_t size) { memcpy(data, (uint8_t*)address,size); }
データの読み込みまで作成できたら実際に適当なデータの書き込みと読み出し、イレースを行います。
下のようにプログラムをmainに作成して一度書き込みをした後に、電源を落として書き込んだ値が読み出せるのか確認してみます。タイミング毎のデータを見たいのでprintfを使うよりもデバッグモードで確認した方がわかりやすく、実際にはデバッグモードで確認しました。
FLASH_ReadData(0x0807F800, &data, 8); FLASH_Unlock(); FLASH_Erase(); FLASH_Lock(); FLASH_ReadData(0x0807F800, &data, 8); printf("data %x\n",data); data = 0xfffffffffffff050; FLASH_Unlock(); FLASH_Erase(); FLASH_WriteByte(0x0807F800, data); FLASH_Lock(); data = 0; FLASH_ReadData(0x0807F800, &data, 8); printf("data %x\n",data);
イレース後に値が0xffffffffffffffffになって、書き込み後に再起動して読み出すと 0xfffffffffffff050が読み出せたのでフラッシュが使えるようになってることが確認できました。
設定ミスからの復帰
テスト中にシングルバンクとデュアルバンクの切り替えをしようとしてOPTRを書き換えている時に書き間違えをしてマイコンが動かなくなりました。FLASH->OPTR &= ~(FLASH_OPTR_DBANK);でシングルバンクモードに設定、FLASH->OPTR |= FLASH_OPTR_DBANK;でデュアルバンクモードに設定すればいいところを、一度間違えてFLASH->OPTR &= (FLASH_OPTR_DBANK);と書いてしまったことがありました。これを間違えると周辺のレジスタをすべて0にしてしまい、OPTRに含まれている読み出し保護レジスタRDPが0になってしまい、CubeIDE書き込み動作ができなくなります。
こうなるとCubeIDEから書き込みやデバッグをしようとするとFailed to execute MI commandと言われます。
書き込み動作が全くできないので間違いに気づいてもプログラムから修正できません。一度はここでマイコンをだめにしてしまったのかと思ったのですが、調べてるうちに修正できる方法を見つけました。
そのためにはSTM32CubeProgrammerというソフトを使います。
CubeProgrammerはUARTなどでの書き込みをする時につかうソフトですが、OPTRを操作することができる機能があります。この機能を使うとRDPを外から書き換えることができました。
RDPをAAに設定すると書き込みができるようになるので再びCubeIDEが使えるようになります。CubeProgrammerではOPTRの他のレジスタも設定することができるのでどうしてもOPTRを変更する必要があるときにはCubeProgrammerから設定したほうが安全かもしれません。
また、他にもRDPレジスタに保護がかかるパターンが存在するようで、マイコンが動かなくなる場合がありましたが、CubeProgrammerを使うことで復活しました。
MI Commandのエラーが出るときにはCubeProgrammerを使ってOPTRをチェックしてみるといいかもしれません。
今回でマウスに使う機能のチェックがすべて終了したので次回から機体作成に入ります。
参考URL
