STM32FをArduino IDEで開発的なメモの5乗くらい [STM32F]
色々あって、STM32FでやるArduinoは、以下にしています。
https://github.com/stm32duino
STM32duinoでは様々な周辺機能のクラスが有ってプロトタイピングは楽なのですが、機能がサポートされていない物も有る。今回はTimerのPWM出力を使って任意のアナログ波形を生成したいと言うお題。もちろんPWM出力はLPF(Low Pass Filter)で均している。
STM32duinoはHardwareTimerクラスを持っている。折角有るのでこのHardwareTimerクラスを使ってお題をこなしたい。
https://github.com/stm32duino/Arduino_Core_STM32/blob/master/cores/arduino/HardwareTimer.h
を見るとPWM出力のメンバー関数も当然のように用意されているので、こちらを使えば、、、
しかしやりたいのは任意の”波形”であり、一定周期でPWMのDutyを変化させないと、任意の直流電圧波形になってしまう。
一定周期でPWMのDutyを変化させる方法は?
1.コンペアマッチのステータスをずっとモニターして、更新タイミングで新しいコンペアマッチの値をレジスタに代入する?
2.コンペアマッチのステータスの変化を割込みで捉えて、割込みのタイミングで新しいコンペアマッチの値をレジスタに代入する?
1のソフトウエアだけでやるのは如何にもスマートではないのでパス。2の割込みを使うのは現実的だが、まだソフトウエアの負荷が大きく、多数のチャネルでそれを行うのはちょっと忍びない。
3.更新タイミングに、DMAで新しい値を転送してしまおう!やはりこれしか無いかな?
STM32duinoはHALライブラリを使用している。HALライブラリでタイマー関連のライブラリは以下になる。
https://github.com/stm32duino/Arduino_Core_STM32/blob/master/system/Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim.h
上記の様にDMA連動の関数が存在する。
確かにHAL_TIM_PWM_Start_DMAを使えば、PWMの更新値はDMA転送で行われている様子だ。
しかしもう少し進めて、DMAの転送完了割込みを起動したい。
HardwareTimerクラスはメンバー関数にattachInterruptを持つ。しかしこのメンバー関数はBASE Timerの更新とか、コンペアマッチの一致時に使うもので、DMA転送の完了と言う訳ではない。
stm32f1xx_hal_tim.hのHALライブラリで使用されているTIM_HandleTypeDef構造体には
DMA_HandleTypeDef *hdma[7];
が存在し、DMAと連動する7種類の条件が有る。
とまぁ、PWMの更新にDMAを使い、データブロックの転送完了後に何かしらの割込みを発生させることが出来そうな雰囲気は有るのだが、どうすれば良いのか判らなくて嵌まった。
なのでSTM32の初心に帰って自分で割込みをなんとかする事とする。
そもそもDMA転送完了割込みの割込みハンドラ自体が、検索しても割込みベクターが書いてあるファイル(STM32F103VETであれば startup_stm32f103xe.s )の中に、例えばTimer3 channel1のDMA要求の接続先であるDMA1_Channel6の割込みハンドラは、
.weak DMA1_Channel6_IRQHandler
.thumb_set DMA1_Channel6_IRQHandler,Default_Handler
と記述されているが、このままではDefault_Handlerに行ってしまう。
つまり、DMA割込みを使いたかったら自前でDMA1_Channel6_IRQHandlerを用意し、NVICの設定を行い、DMAに割込みを許可すれば良い事になる、、、ホンマ?
以下、参照してみてください。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/F1_PWM_WAVE_Output_01
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/libraries
上記例ではTC(Transmit Complete)とエラーのみ、割込みを有効化していますが、もちろんHT(Half Transmit)を有効化することは有用です。
例えばADCのデータを連続的に取り込む場合にDMAはよく使います。DMAを循環モードで動かしている場合、取り込み先のメモリーはリングバッファとして扱われますので常時どこかのアドレスに対して書込みが行われています。
プログラム側はどの位置まで書込みが完了したのか把握したい場合、リングバッファの前半と後半に分けてそれぞれ書込み完了のタイミングが判れば、前半終了のHT割込みのタイミングでリングバッファの前半の内容を安全に演算や転送などの処理をできますし、後半の終了のTC割込みのタイミングでリングバッファの後半の内容を安全に演算や転送などの処理をできます。
https://github.com/stm32duino
STM32duinoでは様々な周辺機能のクラスが有ってプロトタイピングは楽なのですが、機能がサポートされていない物も有る。今回はTimerのPWM出力を使って任意のアナログ波形を生成したいと言うお題。もちろんPWM出力はLPF(Low Pass Filter)で均している。
STM32duinoはHardwareTimerクラスを持っている。折角有るのでこのHardwareTimerクラスを使ってお題をこなしたい。
https://github.com/stm32duino/Arduino_Core_STM32/blob/master/cores/arduino/HardwareTimer.h
を見るとPWM出力のメンバー関数も当然のように用意されているので、こちらを使えば、、、
しかしやりたいのは任意の”波形”であり、一定周期でPWMのDutyを変化させないと、任意の直流電圧波形になってしまう。
一定周期でPWMのDutyを変化させる方法は?
1.コンペアマッチのステータスをずっとモニターして、更新タイミングで新しいコンペアマッチの値をレジスタに代入する?
2.コンペアマッチのステータスの変化を割込みで捉えて、割込みのタイミングで新しいコンペアマッチの値をレジスタに代入する?
1のソフトウエアだけでやるのは如何にもスマートではないのでパス。2の割込みを使うのは現実的だが、まだソフトウエアの負荷が大きく、多数のチャネルでそれを行うのはちょっと忍びない。
3.更新タイミングに、DMAで新しい値を転送してしまおう!やはりこれしか無いかな?
STM32duinoはHALライブラリを使用している。HALライブラリでタイマー関連のライブラリは以下になる。
https://github.com/stm32duino/Arduino_Core_STM32/blob/master/system/Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim.h
/* Non-Blocking mode: DMA */ HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length); HAL_StatusTypeDef HAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
上記の様にDMA連動の関数が存在する。
確かにHAL_TIM_PWM_Start_DMAを使えば、PWMの更新値はDMA転送で行われている様子だ。
しかしもう少し進めて、DMAの転送完了割込みを起動したい。
HardwareTimerクラスはメンバー関数にattachInterruptを持つ。しかしこのメンバー関数はBASE Timerの更新とか、コンペアマッチの一致時に使うもので、DMA転送の完了と言う訳ではない。
stm32f1xx_hal_tim.hのHALライブラリで使用されているTIM_HandleTypeDef構造体には
DMA_HandleTypeDef *hdma[7];
が存在し、DMAと連動する7種類の条件が有る。
/** @defgroup DMA_Handle_index TIM DMA Handle Index * @{ */ #define TIM_DMA_ID_UPDATE ((uint16_t) 0x0000) /*!< Index of the DMA handle used for Update DMA requests */ #define TIM_DMA_ID_CC1 ((uint16_t) 0x0001) /*!< Index of the DMA handle used for Capture/Compare 1 DMA requests */ #define TIM_DMA_ID_CC2 ((uint16_t) 0x0002) /*!< Index of the DMA handle used for Capture/Compare 2 DMA requests */ #define TIM_DMA_ID_CC3 ((uint16_t) 0x0003) /*!< Index of the DMA handle used for Capture/Compare 3 DMA requests */ #define TIM_DMA_ID_CC4 ((uint16_t) 0x0004) /*!< Index of the DMA handle used for Capture/Compare 4 DMA requests */ #define TIM_DMA_ID_COMMUTATION ((uint16_t) 0x0005) /*!< Index of the DMA handle used for Commutation DMA requests */ #define TIM_DMA_ID_TRIGGER ((uint16_t) 0x0006) /*!< Index of the DMA handle used for Trigger DMA requests */
とまぁ、PWMの更新にDMAを使い、データブロックの転送完了後に何かしらの割込みを発生させることが出来そうな雰囲気は有るのだが、どうすれば良いのか判らなくて嵌まった。
なのでSTM32の初心に帰って自分で割込みをなんとかする事とする。
そもそもDMA転送完了割込みの割込みハンドラ自体が、検索しても割込みベクターが書いてあるファイル(STM32F103VETであれば startup_stm32f103xe.s )の中に、例えばTimer3 channel1のDMA要求の接続先であるDMA1_Channel6の割込みハンドラは、
.weak DMA1_Channel6_IRQHandler
.thumb_set DMA1_Channel6_IRQHandler,Default_Handler
と記述されているが、このままではDefault_Handlerに行ってしまう。
つまり、DMA割込みを使いたかったら自前でDMA1_Channel6_IRQHandlerを用意し、NVICの設定を行い、DMAに割込みを許可すれば良い事になる、、、ホンマ?
以下、参照してみてください。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/F1_PWM_WAVE_Output_01
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/libraries
上記例ではTC(Transmit Complete)とエラーのみ、割込みを有効化していますが、もちろんHT(Half Transmit)を有効化することは有用です。
例えばADCのデータを連続的に取り込む場合にDMAはよく使います。DMAを循環モードで動かしている場合、取り込み先のメモリーはリングバッファとして扱われますので常時どこかのアドレスに対して書込みが行われています。
プログラム側はどの位置まで書込みが完了したのか把握したい場合、リングバッファの前半と後半に分けてそれぞれ書込み完了のタイミングが判れば、前半終了のHT割込みのタイミングでリングバッファの前半の内容を安全に演算や転送などの処理をできますし、後半の終了のTC割込みのタイミングでリングバッファの後半の内容を安全に演算や転送などの処理をできます。
2021-07-29 14:28
nice!(0)
コメント(0)
コメント 0