2021年07月03日

STM32 RTC初期化注意点

今回はSTM32マイコンのRTC初期化の注意点について紹介します。STM32マイコンの多くはVBAT端子を備えており、CR2032といった電池と32.768kHzといった水晶発振子を取り付けることでマイコンのメイン電源OFFでも時刻を保持することができます。また、ファームウェアの書き換え等を行っても時刻情報を保持することができるため、非常に便利です。

基本的な使い方としてはSTM32CubeMXで自動生成されるコードそのままで問題ありませんが、頻繁にマイコンのON/OFFを繰り返す場合、RTC初期化HAL_RTC_Init関数で時刻の遅延が発生する厄介なバグがあります。STフォーラムでもディスカッション(リンク切れ?)されています。HAL_RTC_Init関数内のクロック設定で既にRTCの初期化が完了している場合に再度、クロックが設定される際にクロックが一時的に停止して遅延が発生します。電源投入後に外部から時刻を取得して電源ONのまま永続的に使用する場合には問題ありませんが、頻繁に電源をON、OFFする場合、初期化HAL_RTC_Init関数によって毎回、遅延が発生するため、時刻の遅れが無視できなくなります。

初期化HAL_RTC_Init関数によって発生する遅延を防止するため、既にRTC設定がされている場合に初期化HAL_RTC_Init関数を実行しないという分岐処理を入れることで遅延を回避する設定が必要です。

■修正前
static void MX_RTC_Init(void)
{
  ~~~
  HAL_RTC_Init(&hrtc)!=HAL_OK)
  {
     Error_Handler();
  }
  ~~~
}

■修正後
static void MX_RTC_Init(void)
{
~~~
  if((RCC->BDCR & RCC_BDCR_RTCEN)==0)//RTC初期化されていない場合
  {
     if(HAL_RTC_Init(&hrtc)!=HAL_OK)
     {
        Error_Handler();
      }
  } 
  else//RTC初期化済の場合
  {
     HAL_PWR_EnableBkUpAccess();
     __HAL_RCC_BKP_CLK_ENABLE();//シリーズによっては不要
     __HAL_RCC_RTC_ENABLE();
     HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);
     HAL_NVIC_EnableIRQ(RTC_IRQn);
  }

~~~
}

上記の設定で初期化HAL_RTC_Init関数によって発生する遅延を防止することが可能です。最近のSTM32マイコンのシリーズでは遅延がほぼ無視できますが、比較的歴史の長いF0やF1シリーズ等では遅延が1秒程度と大きいようです。今後のHALライブラリのバージョンアップで遅延防止の処理を入れ込んでほしいですね。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年06月19日

STM32 WDG機能注意点

今回はSTM32マイコンのIWDG(独立ウォッチドッグ)について紹介します。WDG(ウォッチドッグ)は名の通り、MPUの動作を見張る番犬です。WDGを有効化するとMPUに異常な処理が発生した場合や意図しない処理の無限ループに入ってしまった場合に異常としてMPUをリセットさせることができます。そのような異常な状態にならないようにコードを実装することはもちろんですが、意図しない電源の急激な変化や強い静電気や電磁波といった外乱によって万一、動作が不安定になった場合にWDGによってリセットさせることで自動的に異常な状態から復帰させることができるようになります。

WDGの動作原理として、監視の時間間隔を予め設定し、監視を開始します(HAL_IWDG_Init)。そのあと、コード上で定期的にWDGカウントリフレッシュ関数HAL_IWDG_Refreshを呼び出します。正常な場合は予め設定した監視時間よりも前にHAL_IWDG_Refresh関数で正常に動いていることをWDGに知らせます。何かしらの異常が発生した場合はHAL_IWDG_Refresh関数が予め設定した監視時間になってもHAL_IWDG_Refresh関数による知らせがWDGにないため、監視時間を超えた時点でWDGが強制的にMPUのリセットを実行する仕組みです。正常動作にも関わらず、監視時間に達してリセットされてしまうということがないように、ある程度余裕のある監視時間を設定すべきです。ただ、余裕にしすぎると万一、異常な状態になった場合にリセットがかかるまでに時間を要することになるため、バランスを見て設定する必要があります。例えば、長い処理ではリフレッシュ関数を所々に予め入れておくといった対応が想定されます。


STM32マイコンのWDGにはWWDGとIWDGの2種類ありますが、IWDGはMPUのメインクロック供給源から独立した内蔵32kHzクロックを使用しています。そのため、意図しない電源の急激な変化や強い静電気や電磁波といった外乱に強くなります。なお、WWDGはMPUと共通のクロックを使用するため、クロック系に異常が発生した場合はWWDGとMPUともに共倒れします。



WDGとして強力なIWDGはCubeMX上で設定可能です。一方でIWDGを使用する上ではいくつか注意点があります。

STM32_IWDG.jpg

■IWDG注意点@
IWDGはソフトウェアモードとハードウェアモードの2種類あります。デフォルトでは「ソフトウェアモード」が有効です。ソフトウェアの場合、独立クロックという面ではWWDGよりも有利ですが、外乱によってはリセットが働かない場合があります。ハードウェアモードの方が、ハードウェア上で監視するため、ソフトウェアに比べてより幅広い外乱や異常に対応することができます。

ソフトウェアモードとハードウェアモードの切り替えはCubeMX上ではできません。オプションバイトにアクセスして変更する必要があります。コード上でオプションバイトを操作する方法もありますが、簡単な方法は書き込みソフトウェアでオプションバイトを操作する方法です。

STLinkUtilityの場合はTarget→OptionBytesから下記の画面を表示させて、IWDG_SWのチェックを外すことでハードウェアモードになります。MPUのシリーズによって項目名が多少異なりますが、デフォルトがソフトウェアモードになっており、チェックを外すとハードウェアモードになる点では同じです。チェックを外してApplyをクリックすると設定がMPUに反映されます。



STM32CubeProgrammerの場合はOB(OptionBytes)のUserConfigurationから設定変更可能です。チェックを外してApplyをクリックすると設定がMPUに反映されます。

IWDG_CubePrg.jpg

■IWDG注意点A
IWDGはソフトウェアモードでは問題になりませんが、ハードウェアモードの場合、外乱により強い一方で特に注意が必要となります。一度、オプションバイトを操作してハードウェアモードにした場合、再度、オプションバイトを元に戻さない限り、ファームウェアを書き換えてもハードウェアモードが維持されます。このハードウェアモードの状態では必ずIWDG初期化関数HAL_IWDG_Initとリフレッシュ関数HAL_IWDG_Refresh関数が入ったファームが必要です。

コード上にIWDG初期化関数HAL_IWDG_Initとリフレッシュ関数HAL_IWDG_Refreshがない場合、起動直後にIWDGによるリセットが連続で発生し、コードが停止します。例えば、ハードウェアモードを有効化したMPUに対してIWDGを有効化する前の古いファームを書き込むとデバッガや入出力が停止して、応答が何もなくなり、MPUが文鎮化した状態になります。このような場合はコード上にIWDG初期化関数とリフレッシュ関数があるファームを書き込むか、オプションバイトを操作して、ソフトウェアモードに切り替えてから再度、ファームを書き込むことでまた元通りに正常に動くようになります。これらのことから、ファームを頻繁に入れ替えたりする場合は当面、ソフトウェアモードで運用した方が利便性が高いです。

今回はSTM32マイコンのIWDGについて紹介しました。特に注意点AについてはIWDGをハードウェアモードにしたことを忘れて、別のファームを書き込んだ際に全くマイコンが動作せず、何が原因か悩んだ挙句、IWDGがハードウェアモードだったことが原因ということがありました。オプションバイトはファームと独立しており、余計にややこしくなるため、注意が必要だと分かりました。数か月後の自身に知らせるためにもMPUに「IWDGハードウェア」の印を貼っておいた方が無難だと実感しました。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年05月22日

PIC32MX+Harmony3+ビルド設定

今回はPIC32MX250F128BをターゲットにMPLAB X v5.45+Harmony3+XC32 v2.50を使用する際のビルド設定について紹介します。Harmony3で生成したプロジェクトをXC32でビルドするとデフォルトはワーニングもエラーとして処理する設定となっています。そのため、例えば下記のように未使用の変数があった場合にエラーとなってビルドに失敗します。

../src/app.c:420:10: error: unused variable 'XXXX' [-Werror=unused-variable]

File→Project Propertiesをクリックし、左のXC32(Global Options)のxc32-gccをクリックします。「Option categories」の「Preprocessing and messages」の項目を選択し、「Make warnings into errors」のチェックを外します。OKをクリックして設定を反映させます。OKが押せない場合は左のConf項目でCompiler ToolchainからXC32の対象コンパイラを選択するとOKボタンが有効になります。


XC32_1.jpg


XC32_2.jpg

最終的には未使用変数を削除しますが、試行錯誤している場合は多々未使用変数が残ってしまうものです。デフォルト設定が少し不親切だと感じました。今後のバージョンアップで修正されることに期待です。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年05月15日

PIC32MX+Harmony3+ I2C通信

今回はPIC32MX250F128BをターゲットにMPLAB X v5.45+Harmony3を使用してI2C通信を行いました。その際にポイントとなる設定について紹介します。I2C接続の温湿度センサHS3001の読み込みを行いました。なお、ターゲットへの書き込みはPickit3を使用しまし、MPLAB Xには予め、MPLAB Harmony 3 Launcher(旧名称 MPLAB Harmony Configurator 3)をインストールしています。


@プロジェクトファイル作成
MPLAB XのFile→New ProjectからMPLAB Harmony 3のプロジェクトを作成します。

hi2c1.jpg

AHarmony 3設定
Tools→Embedded→Harmony Configurator 3を選択します。左下のAvailable ComponentsからI2C2をクリックし、I2C2を追加します。SPI1が追加されると左上(Active Components)及び中央(Project Graph)にI2C2が表示されます。
中央(Project Graph)のI2C2をクリックし、右のConfiguration Optionsの設定項目を必要に応じて変更します。通常はデフォルトで問題ありません。

また、今回はデバッグ用途にprintfを使用してuartからデバッグ情報を出力するため、UART2とSTDIOを追加しています。必要に応じてUART2のボーレートを右のConfiguration Optionsから変更してください。

hi2c2.jpg

hi2c3.jpg

Bピンアサイン設定
MHC→Tools→Pin Configurationを選択します。Pin DiagramからI2CのSDA2、SCL2を割り付けします。Pin SettingsからI2Cの外付けプルアップ抵抗を省略するためにRB2、RB3のプルアップを有効にしました。

hi2c4.jpg

Cコード生成
設定完了後にGenerate Codeでコードを生成します。

hi2c5.jpg

DI2C通信関数
I2C通信をするためのコードを追加します。I2C通信関数はSource Files/config/default/peripheral/i2c/master内のplib_i2c2_master.cで定義されています。I2C初期化関数は既にinitialization.cに追加されているため、接続するデバイスに応じてI2C読み書き関数を追加します。I2C読み書きを同時にする場合はI2C2_WriteRead関数、読み込みのみの場合はI2C2_Read、書き込みのみの場合はI2C2_Writeの関数を使用します。なお、STM32のHALライブラリでは8bitアドレス(R/Wのbitを含める)で定義しますが、Harmony3のI2Cアドレスは7bitアドレスを定義します。


#define HS300X_ADDR (0x88>>1)

void Get_HS300X_Data(volatile int16_t *tmp, volatile int16_t *hmd)
{
 uint8_t dat[4];
 int16_t tmp_dat,hmd_dat;
 float tempf;
 float humdf;
 I2C2_Read(HS300X_ADDR,dat,4);
 while(I2C2_IsBusy()==true);
 I2C2_Write(HS300X_ADDR,NULL,0);
 while(I2C2_IsBusy()==true);
 tmp_dat = (dat[2]<<8)+dat[3];
 tmp_dat = tmp_dat>>2;
 hmd_dat = ((dat[0]&0x3F)<<8)+dat[1];
 humdf=(float)hmd_dat/(16383.0f)*100.0f;
 tempf=((float)tmp_dat)/(16383.0f)*165.0f-40.0f;
 *hmd = (int16_t)(humdf*100.0f);
 *tmp = (int16_t)(tempf*100.0f);
 return;
}

温湿度センサHS3001は温湿度を読み出しする際、アドレスのみ書き込みすると測定開始される少し特殊な仕様です。そのため、 I2C2_Write ( HS300X_ADDR, NULL, 0)という空データを書き込む処理が入っています。



posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2021年05月08日

PIC32MX+Harmony3+ SPI通信

今回はPIC32MX250F128BをターゲットにMPLAB X v5.45+Harmony3を使用してSPI通信を行いました。その際にポイントとなる設定について紹介します。SPI接続のSRAM(IS62WVS5128FB)の書き込み、読み込みを行いました。なお、ターゲットへの書き込みはPickit3を使用しまし、MPLAB Xには予め、MPLAB Harmony 3 Launcher(旧名称 MPLAB Harmony Configurator 3)をインストールしています。

@プロジェクトファイル作成
MPLAB XのFile→New ProjectからMPLAB Harmony 3のプロジェクトを作成します。

hspi1.jpg

AHarmony 3設定
Tools→Embedded→Harmony Configurator 3を選択します。左下のAvailable ComponentsからSPI1をクリックし、SPI1を追加します。SPI1が追加されると左上(Active Components)及び中央(Project Graph)にSPI1が表示されます。

中央(Project Graph)のSPI1をクリックし、右のConfiguration Optionsの設定項目を必要に応じて変更します。通常はデフォルトで問題ありません。SPIのデフォルトクロック周波数が低いため、1MHz程度に変更します。

また、今回はデバッグ用途にprintfを使用してuartからデバッグ情報を出力するため、UART2とSTDIOを追加しています。必要に応じてUART2のボーレートを右のConfiguration Optionsから変更してください。

hspi2.jpg

hspi3.jpg

Bピンアサイン設定
MHC→Tools→Pin Configurationを選択します。Pin DiagramからSPI1のSDO1、SDI1、SCK1を割り付けします。また、Pin SettingsからCSとしてRA0をGPIO設定しました。Pin TableからRA0を出力に設定し、通信を安定させるためにプルアップを有効にしました。

hspi4.jpg

hspi5-1.jpg



hspi5.jpg

Cコード生成
設定完了後にGenerate Codeでコードを生成します。

hspi6.jpg

DSPI通信関数
SPI通信をするためのコードを追加します。

hspi7.jpg



SPI通信関数はSource Files/config/default/peripheral/spi/spi_master内のplib_spi1_master.cで定義されています。SPI初期化関数は既にinitialization.cに追加されているため、接続するデバイスに応じてSPI読み書き関数を追加します。SPI読み書きを同時にする場合はSPI1_WriteRead関数、読み込みのみの場合はSPI1_Read、書き込みのみの場合はSPI1_Writeの関数を使用します。なお、SPI1のConfiguration Options、Enable Interrupts設定はデフォルトで有効となっています。そのため、書き込み、読み込みした後はSPI1_IsBusy関数でSPI処理の完了を確認するか、SPI処理後に呼び出す関数をSPI1_CallbackRegisterで事前に登録する必要があります。SPI1_CallbackRegisterのサンプルはこちらに上がっているため、詳細の説明は省略します。今回はSPI1_IsBusy関数でSPI処理の完了を確認しました。

また、SPI通信で必要なCSは自動に設定(通信するときのみCSが自動でLOWになる)できますが、今回は手動でIO操作にしました。ピンアサイン設定でRA0を出力に設定したため、自動でHeader Files/config/default/peripheral/gpio内のplib_gpio.hにGPIO_RA0_Set関数やGPIO_RA0_Clear関数が追加されます。追加されたGPIO関数でCSを操作します。SRAMと通信するための全体のコードは下記です。main.cに下記のコードを追加しました。

#define SRAM_WRITE_CMD 2
#define SRAM_READ_CMD 3


void SramWrite(unsigned char *data, unsigned short length)
{
 unsigned char wcmd[4];
 wcmd[0]=SRAM_WRITE_CMD;
 wcmd[1]=0;//High Addr
 wcmd[2]=0;//Mid Addr
 wcmd[3]=0;//Low Addr

 GPIO_RA0_Clear();
 SPI1_Write(wcmd,4);
 while(SPI1_IsBusy()==true);
 SPI1_Write(data,length);
 while(SPI1_IsBusy()==true);
 GPIO_RA0_Set();
}

void SramRead(unsigned char *data, unsigned short length)
{
 unsigned char wcmd[4];
 wcmd[0]=SRAM_READ_CMD;
 wcmd[1]=0;//High Addr
 wcmd[2]=0;//Mid Addr
 wcmd[3]=0;//Low Addr
 
 GPIO_RA0_Clear();
 SPI1_Write(wcmd,4);
 while(SPI1_IsBusy()==true);
 SPI1_Read(data,length);
 while(SPI1_IsBusy()==true);
 GPIO_RA0_Set();
}


SPI1CONCLRやSPI1CONSET、LATACLR等のレジスタを操作してSPI通信やIO操作を行っていましたが、Harmony3でハードレイヤーが抽象化され、コード自動生成時点で最低限の関数が既に実装されています。設定によってはコード自動生成直後にも関わらず、ビルドエラーが発生してコードの修正が必要な場合がありますが、以前に比べると大分、改善しているように感じました。なお、SDカードといったHarmony3の機能や設定によってはSPI1_ReadやSPI1_Writeといった関数名が変わるため、注意が必要です。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする