STM32FをArduino IDEで開発的なメモの8乗くらい [STM32F]
色々あって、STM32FでやるArduinoは、以下にしています。
https://github.com/stm32duino
STM32Duinoでは端子の指定方法が2種類あって、これがややこしいのだが、
一つはPinNumber、もう一つはPinNameとなる。
PinNumberはvariantsフォルダーから辿っていき、それぞれのマイコンタイプ毎に分かれて、例えばSTM32F103VETでは以下のフォルダーの
https://github.com/stm32duino/Arduino_Core_STM32/tree/master/variants/STM32F1xx/F103V(C-D-E)(H-T)
variant_generic.hで定義されている、いわゆる通し番号となっている。
PinNameはこれとはまったく違うフォルダーの中に存在する。
https://github.com/stm32duino/Arduino_Core_STM32/blob/master/cores/arduino/stm32/PinNames.h
PinNameで参照しているPortAとかPortBとかは以下で定義されている。
https://github.com/stm32duino/Arduino_Core_STM32/blob/master/cores/arduino/stm32/PortNames.h
https://github.com/stm32duino
STM32Duinoでは端子の指定方法が2種類あって、これがややこしいのだが、
一つはPinNumber、もう一つはPinNameとなる。
PinNumberはvariantsフォルダーから辿っていき、それぞれのマイコンタイプ毎に分かれて、例えばSTM32F103VETでは以下のフォルダーの
https://github.com/stm32duino/Arduino_Core_STM32/tree/master/variants/STM32F1xx/F103V(C-D-E)(H-T)
variant_generic.hで定義されている、いわゆる通し番号となっている。
PinNameはこれとはまったく違うフォルダーの中に存在する。
https://github.com/stm32duino/Arduino_Core_STM32/blob/master/cores/arduino/stm32/PinNames.h
PinNameで参照しているPortAとかPortBとかは以下で定義されている。
https://github.com/stm32duino/Arduino_Core_STM32/blob/master/cores/arduino/stm32/PortNames.h
STM32FをArduino IDEで開発的なメモの7乗くらい [STM32F]
色々あって、STM32FでやるArduinoは、以下にしています。
https://github.com/stm32duino
アマゾンでHALライブラリの参考書を買ってみた。
と言うか、STM32CubeMXの使い方が書かれている。FreeRTOSやAIの導入まで書かれている。
HALライブラリとかSTM32CubeMXとか、なかなか判らんもんね~~~、助かる。
https://github.com/stm32duino
アマゾンでHALライブラリの参考書を買ってみた。
と言うか、STM32CubeMXの使い方が書かれている。FreeRTOSやAIの導入まで書かれている。
HALライブラリとかSTM32CubeMXとか、なかなか判らんもんね~~~、助かる。
STM32FをArduino IDEで開発的なメモの6乗くらい [STM32F]
色々あって、STM32FでやるArduinoは、以下にしています。
https://github.com/stm32duino
STM32DuinoでBlynkをやった話。SPIの設定でちょっと躓いたのでメモ。
使ったのはSTM32F401RCTとW5500を搭載した基板となる。
ライブラリが整備されているので、Blynkのサーバーにアクセスする事はWiznetのW5100か、W5200か、W5500を使っている限り比較的容易にできる。
W5300に関してはまだライブラリが存在していないみたいで、これは自分でドライバーを書くしか無さそう。
以下を参照してみてください。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/F4_2102_Blynk_Blink
W5500のインタフェースはSPIですが、W5500のCSを標準のNSSとかに設定していない場合、以下の行を初期化時に付け足しておく必要が有る。
Ethernet.init( W5500_CS );
以上。
https://github.com/stm32duino
STM32DuinoでBlynkをやった話。SPIの設定でちょっと躓いたのでメモ。
使ったのはSTM32F401RCTとW5500を搭載した基板となる。
ライブラリが整備されているので、Blynkのサーバーにアクセスする事はWiznetのW5100か、W5200か、W5500を使っている限り比較的容易にできる。
W5300に関してはまだライブラリが存在していないみたいで、これは自分でドライバーを書くしか無さそう。
以下を参照してみてください。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/F4_2102_Blynk_Blink
W5500のインタフェースはSPIですが、W5500のCSを標準のNSSとかに設定していない場合、以下の行を初期化時に付け足しておく必要が有る。
Ethernet.init( W5500_CS );
以上。
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割込みのタイミングでリングバッファの後半の内容を安全に演算や転送などの処理をできます。
STM32FをArduino IDEで開発的なメモの4乗くらい [STM32F]
色々あって、STM32FでやるArduinoは、以下にしています。
https://github.com/stm32duino
STM32duinoでは、STM32F_CPU_IDENTITYクラスを利用する事で、CPUの情報を色々取得する事ができます。
例えば、
#include <STM32F_CPU_Identity.h>
STM32F_CPU_IDENTITY cpu_id;としておいて、
cpu_id.sysload()でsystick timerの値、
cpu_id.hclk()でhclkの値、
cpu_id.pclk1()とcpu_id.pclk2()でpclk1とpclk2の値、
cpu_id.sysclk()でシステムクロックの値が取得できますん。
また、
で、3つのCPU IDが取得できますん。
そんなわけでCPUのクロック等の情報を取得できたのですが、では実際にArduino環境でSTM32Fの開発を行った場合、システムクロックはどうなっているんでしょうか?
と言うのが今回のお題。
STM32Fの開発に使用した基板はNUCLEOとかではなくオリジナルの基板であることから、Arduino IDEのボードの選択は”STM32F1 series”とし、Board parts numberは"Generic F103VETx"としています。つまりSTM32F103VETを搭載したマイコンボードです。
上記マイコンボードは、HSE用に8MHzのクリスタルを接続してあります。STM32F103VETは最大72MHzで動かすことができます。
しかしSTM32F_CPU_IDENTITYクラスで取得したシステムクロックは64MHzとなりました、、、う~~~ん。
結局ボードにGenericタイプを選んだ時にシステムクロックがどうなるのかと言うと、STM32duinoのツールチェインのvariantsにそれが有りました。例えば、以下の様なPATHにgeneric_clock.cが有りますん。
C:\Users\ユーザー名\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.0.0\variants\STM32F1xx\F103V(C-D-E)(H-T)
generic_clock.cのSystemClock_Config関数でシステムクロックの設定が行われており、標準ではHSIを使用し、HSIが8MHzですから8MHz÷2×16で64MHzの出来上がりです。
ですがSystemClock_Config関数はWEAK修飾子が付いています。
なのでユーザーが独自にSystemClock_Config関数を用意すれば、そちらが優先される事となります。
ユーザーがSystemClock_Config関数を生成するもっとも簡単な方法はSTM32CubeMXを使う事です。グラフィカルな画面でマウスでポチポチしていけばSystemClock_Config関数を生成してくれるので、それを取って来てスケッチに貼り付けるだけでOKです。以下参照してみてください。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/F1F4_GET_CPU_ID
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/libraries
https://github.com/stm32duino
STM32duinoでは、STM32F_CPU_IDENTITYクラスを利用する事で、CPUの情報を色々取得する事ができます。
例えば、
#include <STM32F_CPU_Identity.h>
STM32F_CPU_IDENTITY cpu_id;としておいて、
cpu_id.sysload()でsystick timerの値、
cpu_id.hclk()でhclkの値、
cpu_id.pclk1()とcpu_id.pclk2()でpclk1とpclk2の値、
cpu_id.sysclk()でシステムクロックの値が取得できますん。
また、
uint32_t uid[3]; cpu_id.uID( uid );
で、3つのCPU IDが取得できますん。
そんなわけでCPUのクロック等の情報を取得できたのですが、では実際にArduino環境でSTM32Fの開発を行った場合、システムクロックはどうなっているんでしょうか?
と言うのが今回のお題。
STM32Fの開発に使用した基板はNUCLEOとかではなくオリジナルの基板であることから、Arduino IDEのボードの選択は”STM32F1 series”とし、Board parts numberは"Generic F103VETx"としています。つまりSTM32F103VETを搭載したマイコンボードです。
上記マイコンボードは、HSE用に8MHzのクリスタルを接続してあります。STM32F103VETは最大72MHzで動かすことができます。
しかしSTM32F_CPU_IDENTITYクラスで取得したシステムクロックは64MHzとなりました、、、う~~~ん。
結局ボードにGenericタイプを選んだ時にシステムクロックがどうなるのかと言うと、STM32duinoのツールチェインのvariantsにそれが有りました。例えば、以下の様なPATHにgeneric_clock.cが有りますん。
C:\Users\ユーザー名\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.0.0\variants\STM32F1xx\F103V(C-D-E)(H-T)
generic_clock.cのSystemClock_Config関数でシステムクロックの設定が行われており、標準ではHSIを使用し、HSIが8MHzですから8MHz÷2×16で64MHzの出来上がりです。
ですがSystemClock_Config関数はWEAK修飾子が付いています。
なのでユーザーが独自にSystemClock_Config関数を用意すれば、そちらが優先される事となります。
ユーザーがSystemClock_Config関数を生成するもっとも簡単な方法はSTM32CubeMXを使う事です。グラフィカルな画面でマウスでポチポチしていけばSystemClock_Config関数を生成してくれるので、それを取って来てスケッチに貼り付けるだけでOKです。以下参照してみてください。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/F1F4_GET_CPU_ID
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/libraries