HALライブラリを使ったstm32f0のフラッシュ書き込み

stm32CubeMXが生成してくれるHALライブラリを使ってstm32F051のFlashにデータを書き込む

stMicro提供のライブラリを使うとstm32FシリーズCPU内蔵のFlashROMへデータを書き込むのはとても簡単なのだが
以前のstdperipheralライブラリからHALに乗り換えてちょっと躓いたのでメモ。

FLASHに書き込む時には書き込み対象ページのデータを書き込み前に消去しておかないとならない。
stm32F051ではライブラリの関数を使って1KByteのページ単位でデータを消去することになる。
書き込みはstm32f0xx_hal_flash.cにある関数HAL_FLASH_Programが以前のライブラリと殆ど同じ方法で使える。
サンプルプログラムを見ると消去の方は構造体を使ったパラメータ渡しのHAL_FLASHEx_Eraseという関数を使ってあるが使い方がややこしそうでしかも同じソースファイルには含まれていない。
で、同じソースにある簡単そうな関数FLASH_PageErase(uint32_t PageAddress)を使ってみたのだがこれが間違いの元で
暫く悩んだ挙句サンプルに従ってstm32f0xx_hal_flash_ex.cに含まれているHAL_FLASHEx_Erase関数を使ったらすんなりと書き込めてしまった。

(教訓)
HALライブラリの関数を使うときはHAL_という文字で始まる関数だけを使わないとトラブルの基になる。

// フラッシュROMへデータを書き込む
void WriteToFlash(uint32_t addr, uint8_t* dat, uint16_t size)
{
  int i;
  FLASH_EraseInitTypeDef EraseInitStruct;
  uint32_t PageError = 0;
  HAL_StatusTypeDef r;

  r = HAL_FLASH_Unlock();
  if( r != HAL_OK ){
#ifdef _DEBUG_FLASH_HAL
    xprintf("WriteParamToFlash:FLASH_Unlock error r=%x\r\n",r);
#endif
    return;
  }
  EraseInitStruct.TypeErase = TYPEERASE_PAGES;
  EraseInitStruct.PageAddress = addr;
  EraseInitStruct.NbPages = 1;
  r = HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);
  if ( r != HAL_OK )
  {
#ifdef _DEBUG_FLASH_HAL
    xprintf("WriteParamToFlash:FLASH_Erase error r=%x, pageerr=%x\r\n",r,PageError);
#endif
    return;
  }
  for(i=0; i<size; i+=2){
    r = HAL_FLASH_Program(TYPEPROGRAM_HALFWORD, addr+i, *(uint16_t*)(dat+i));
#ifdef _DEBUG_FLASH_HAL
    xprintf("WriteParamToFlash i=%d, r=%x\r\n",i,r);
#endif
    if( r != HAL_OK ){
#ifdef _DEBUG_FLASH_HAL
    xprintf("WriteParamToFlash:FLASH_Program error i=%d, r=%x\r\n",i,r);
#endif
      return;
    }
  }
  r = HAL_FLASH_Lock();
  if( r != HAL_OK ){
#ifdef _DEBUG_FLASH_HAL
    xprintf("WriteParamToFlash:FLASH_Lock error r=%x\r\n",r);
#endif
  }
}

DMAを使ったUART送受信

stm32CubeMXでstm32F051のプログラムを作るのに、まずデバッグに必要なUARTの送受信から手をつけた。
今までは受信には割り込みを使い送信は直接送信を使っていたがstm32CubeMXが生成してくれるドライバHALを使うと今まで敬遠していたDMAも簡単に使えそうな気がしてトライしてみた。

思ったほど簡単ではなかったが何とか使い物になるUARTのライブラリが出来上がった。
DMA通信テストのためにJTW32にループバックテスト用画面を追加してテストした結果が次の画面。

Loopbacktest

左のウィンドウにある文字をタイマーで連続してstm32F051ボードに送信し帰ってきた文字と照合して長時間安定して通信出来ることを確認した。
設定した通信速度は1.5Mbpsなので計算上では100KB/秒以上の転送速度が可能なのだが10KB/Sec以上に上げるとエラーが発生する。
ボードから一定のパターンを繰り返し送信してそのパターンをチェックする連続受信テストでは27.8KB/Secでエラーなしに連続してデータを遅れることが確認できたのでこれがCPUの処理能力の上限で、エラーなしに連続送信が可能という結果だった。
実際にUSART通信を使うのは出来るだけ高速でボード内部の状態を送信してグラフで見たいという場合が殆どなのでとりあえずは使える送受信ライブラリが出来たことになる。

DMAの使い方だがデータを送る時はいったん送信バッファにデータを書き込みDMAがビジーでない時にDMAを起動、DMA完了割り込みで送信バッファにデータがあれば再度DMAを起動してデータを送るという仕組みにした。
受信側も最初はDMA完了割り込みを使ったりして苦労したが結局DMAをサーキュラーモードに設定し、受信チェックでCNDTRを監視してデータの操作を行うという方式に落ち着いた。
引っかかったのは送受信ともDMAを使う場合受信のDMAが途中で止まってしまうことで、これはDMAのチャンネルを送信の割り込みグループに属さないチャンネルに変更することで回避できた。