小ピンマイコンの酷暑(5) CH32Vのモデル?STM32のHALレイヤの使用 その2

Joseph Halfmoon

前回書いたとおり、WCHのRISC-V搭載マイコンCH32Vは、STM32をリスペクトしているのか、HALレイヤの関数がクリソツです。先にSTM32のHALレイヤで予習しておいたらCH32VでHALレイヤを使うのも楽なんでないかと。学習教材としてはタイマからPWM出力するのをHALレベルでやってみようと。メンドイけど。

Arduino環境からHALレベルのAPIを使えることはCH32VでもSTM32でも確かめましたが、直接使うのはメンドイです。しかし、STM32では「ラッパ」レイヤが準備されているので、そいつらを呼び出してやれば、ArduinoのAPIほどお手軽ではないけれど、結構簡単に制御が可能でした。今回は、STM32の「ラッパ」レイヤの関数を「毟って」、HALレベルのAPIもしくは、直接ハードウエアレジスタを読み書きするレベルでPWM出力してみたいと思います。使用するハードウエアは以下のとおりです。

    • タイマ=TIM2
    • コンペア・キャプチャ・チャネル=CH1
    • CH1出力先=PA_5端子(STM32名。)Arduino的にはD13端子。ここにオンボードのLEDが接続されている

ここに対して、LEDの点滅が分かる比較的遅い周期(10Hzくらい)で、デューティ10%のPWM波形をPA_5に出力するのが当座の目標です。

実験に使用したソースコード

STM32の「ラッパ」レイヤのソースコードをカンニングさせていただきながら書いてみたものです。HALもしくは直接のハードウエアの読み書きをするマクロなどで書いてみたソースが以下に。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#if defined(LED_BUILTIN)
#define pin LED_BUILTIN
#else
#define pin D2
#endif
int counter = 0;
uint32_t channel;
timerObj_t _timerObj;
void sprin(const char *msg, uint16_t v) {
Serial.print(msg);
Serial.println(v, HEX);
}
void setupTimer(TIM_TypeDef *instance)
{
_timerObj.handle.Instance = nullptr;
_timerObj.handle.Instance = instance;
_timerObj.handle.Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
_timerObj.handle.hdma[0] = NULL;
_timerObj.handle.hdma[1] = NULL;
_timerObj.handle.hdma[2] = NULL;
_timerObj.handle.hdma[3] = NULL;
_timerObj.handle.hdma[4] = NULL;
_timerObj.handle.hdma[5] = NULL;
_timerObj.handle.hdma[6] = NULL;
_timerObj.handle.Lock = HAL_UNLOCKED;
_timerObj.handle.State = HAL_TIM_STATE_RESET;
_timerObj.preemptPriority = TIM_IRQ_PRIO;
_timerObj.subPriority = TIM_IRQ_SUBPRIO;
__HAL_RCC_TIM2_CLK_ENABLE();
_timerObj.handle.Init.Prescaler = 0;
_timerObj.handle.Init.Period = 0xFFFF;
_timerObj.handle.Init.CounterMode = TIM_COUNTERMODE_UP;
_timerObj.handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
_timerObj.handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&(_timerObj.handle));
}
void setMode(PinName pinx)
{
int timChannel = TIM_CHANNEL_1;
int timAssociatedInputChannel;
TIM_OC_InitTypeDef channelOC;
TIM_IC_InitTypeDef channelIC;
channelOC.OCMode = TIMER_NOT_USED;
channelOC.Pulse = __HAL_TIM_GET_COMPARE(&(_timerObj.handle), timChannel);
channelOC.OCPolarity = TIM_OCPOLARITY_HIGH;
channelOC.OCFastMode = TIM_OCFAST_DISABLE;
channelIC.ICPolarity = TIM_ICPOLARITY_RISING;
channelIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
channelIC.ICPrescaler = TIM_ICPSC_DIV1;
channelIC.ICFilter = 0;
channelOC.OCMode = TIM_OCMODE_PWM1;
HAL_TIM_PWM_ConfigChannel(&(_timerObj.handle), &channelOC, timChannel);
if (pinx != NC) {
if ((int)getTimerChannel(pinx) == timChannel) {
pinmap_pinout(pinx, PinMap_TIM);
}
}
}
void setCaptureCompare(uint32_t compare)
{
int timChannel = TIM_CHANNEL_1;
uint32_t Prescalerfactor = LL_TIM_GetPrescaler(_timerObj.handle.Instance) + 1;
uint32_t CCR_RegisterValue;
CCR_RegisterValue = ((__HAL_TIM_GET_AUTORELOAD(&(_timerObj.handle)) + 1) * compare) / 100;
if ((__HAL_TIM_GET_AUTORELOAD(&(_timerObj.handle)) == 0xFFFF)
&& (CCR_RegisterValue == 0xFFFF + 1)) {
CCR_RegisterValue = 0xFFFF;
}
__HAL_TIM_SET_COMPARE(&(_timerObj.handle), timChannel, CCR_RegisterValue);
}
void setPWM_T2C1(uint32_t frequency, uint32_t dutycycle)
{
int timChannel = TIM_CHANNEL_1;
setMode(PA_5);
LL_TIM_SetPrescaler(_timerObj.handle.Instance, 63);
__HAL_TIM_SET_AUTORELOAD(&_timerObj.handle, 0xFF19);
setCaptureCompare(dutycycle);
HAL_TIM_PWM_Start(&(_timerObj.handle), timChannel);
}
void setup() {
Serial.begin(9600);
while (!Serial) { }
Serial.println("STM32, PWM output settings check <DIRECT>.");
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
setupTimer(Instance);
setPWM_T2C1(5, 10);
}
// the loop function runs over and over again forever
void loop() {
Serial.println(counter++);
sprin("Chan : ", channel);
sprin("CNT : ", TIM2->CNT);
sprin("ARR : ", TIM2->ARR);
sprin("CR1 : ", TIM2->CR1);
sprin("CCMR1: ", TIM2->CCMR1);
sprin("CCR1 : ", TIM2->CCR1);
sprin("CCER : ", TIM2->CCER);
delay(10000);
}
#if defined(LED_BUILTIN) #define pin LED_BUILTIN #else #define pin D2 #endif int counter = 0; uint32_t channel; timerObj_t _timerObj; void sprin(const char *msg, uint16_t v) { Serial.print(msg); Serial.println(v, HEX); } void setupTimer(TIM_TypeDef *instance) { _timerObj.handle.Instance = nullptr; _timerObj.handle.Instance = instance; _timerObj.handle.Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED; _timerObj.handle.hdma[0] = NULL; _timerObj.handle.hdma[1] = NULL; _timerObj.handle.hdma[2] = NULL; _timerObj.handle.hdma[3] = NULL; _timerObj.handle.hdma[4] = NULL; _timerObj.handle.hdma[5] = NULL; _timerObj.handle.hdma[6] = NULL; _timerObj.handle.Lock = HAL_UNLOCKED; _timerObj.handle.State = HAL_TIM_STATE_RESET; _timerObj.preemptPriority = TIM_IRQ_PRIO; _timerObj.subPriority = TIM_IRQ_SUBPRIO; __HAL_RCC_TIM2_CLK_ENABLE(); _timerObj.handle.Init.Prescaler = 0; _timerObj.handle.Init.Period = 0xFFFF; _timerObj.handle.Init.CounterMode = TIM_COUNTERMODE_UP; _timerObj.handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; _timerObj.handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_Base_Init(&(_timerObj.handle)); } void setMode(PinName pinx) { int timChannel = TIM_CHANNEL_1; int timAssociatedInputChannel; TIM_OC_InitTypeDef channelOC; TIM_IC_InitTypeDef channelIC; channelOC.OCMode = TIMER_NOT_USED; channelOC.Pulse = __HAL_TIM_GET_COMPARE(&(_timerObj.handle), timChannel); channelOC.OCPolarity = TIM_OCPOLARITY_HIGH; channelOC.OCFastMode = TIM_OCFAST_DISABLE; channelIC.ICPolarity = TIM_ICPOLARITY_RISING; channelIC.ICSelection = TIM_ICSELECTION_DIRECTTI; channelIC.ICPrescaler = TIM_ICPSC_DIV1; channelIC.ICFilter = 0; channelOC.OCMode = TIM_OCMODE_PWM1; HAL_TIM_PWM_ConfigChannel(&(_timerObj.handle), &channelOC, timChannel); if (pinx != NC) { if ((int)getTimerChannel(pinx) == timChannel) { pinmap_pinout(pinx, PinMap_TIM); } } } void setCaptureCompare(uint32_t compare) { int timChannel = TIM_CHANNEL_1; uint32_t Prescalerfactor = LL_TIM_GetPrescaler(_timerObj.handle.Instance) + 1; uint32_t CCR_RegisterValue; CCR_RegisterValue = ((__HAL_TIM_GET_AUTORELOAD(&(_timerObj.handle)) + 1) * compare) / 100; if ((__HAL_TIM_GET_AUTORELOAD(&(_timerObj.handle)) == 0xFFFF) && (CCR_RegisterValue == 0xFFFF + 1)) { CCR_RegisterValue = 0xFFFF; } __HAL_TIM_SET_COMPARE(&(_timerObj.handle), timChannel, CCR_RegisterValue); } void setPWM_T2C1(uint32_t frequency, uint32_t dutycycle) { int timChannel = TIM_CHANNEL_1; setMode(PA_5); LL_TIM_SetPrescaler(_timerObj.handle.Instance, 63); __HAL_TIM_SET_AUTORELOAD(&_timerObj.handle, 0xFF19); setCaptureCompare(dutycycle); HAL_TIM_PWM_Start(&(_timerObj.handle), timChannel); } void setup() { Serial.begin(9600); while (!Serial) { } Serial.println("STM32, PWM output settings check <DIRECT>."); TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM); setupTimer(Instance); setPWM_T2C1(5, 10); } // the loop function runs over and over again forever void loop() { Serial.println(counter++); sprin("Chan : ", channel); sprin("CNT : ", TIM2->CNT); sprin("ARR : ", TIM2->ARR); sprin("CR1 : ", TIM2->CR1); sprin("CCMR1: ", TIM2->CCMR1); sprin("CCR1 : ", TIM2->CCR1); sprin("CCER : ", TIM2->CCER); delay(10000); }
#if defined(LED_BUILTIN)
#define pin  LED_BUILTIN
#else
#define pin  D2
#endif

int counter = 0;
uint32_t channel;

timerObj_t _timerObj;

void sprin(const char *msg, uint16_t v) {
  Serial.print(msg);
  Serial.println(v, HEX);
}

void setupTimer(TIM_TypeDef *instance)
{
  _timerObj.handle.Instance = nullptr;

  _timerObj.handle.Instance = instance;
  _timerObj.handle.Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
  _timerObj.handle.hdma[0] = NULL;
  _timerObj.handle.hdma[1] = NULL;
  _timerObj.handle.hdma[2] = NULL;
  _timerObj.handle.hdma[3] = NULL;
  _timerObj.handle.hdma[4] = NULL;
  _timerObj.handle.hdma[5] = NULL;
  _timerObj.handle.hdma[6] = NULL;
  _timerObj.handle.Lock = HAL_UNLOCKED;
  _timerObj.handle.State = HAL_TIM_STATE_RESET;

  _timerObj.preemptPriority = TIM_IRQ_PRIO;
  _timerObj.subPriority = TIM_IRQ_SUBPRIO;

  __HAL_RCC_TIM2_CLK_ENABLE();

  _timerObj.handle.Init.Prescaler = 0;
  _timerObj.handle.Init.Period = 0xFFFF;
  _timerObj.handle.Init.CounterMode = TIM_COUNTERMODE_UP;
  _timerObj.handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  _timerObj.handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  HAL_TIM_Base_Init(&(_timerObj.handle));
}

void setMode(PinName pinx)
{
  int timChannel = TIM_CHANNEL_1;
  int timAssociatedInputChannel;
  TIM_OC_InitTypeDef channelOC;
  TIM_IC_InitTypeDef channelIC;

  channelOC.OCMode = TIMER_NOT_USED;
  channelOC.Pulse = __HAL_TIM_GET_COMPARE(&(_timerObj.handle), timChannel);
  channelOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  channelOC.OCFastMode = TIM_OCFAST_DISABLE;
  channelIC.ICPolarity = TIM_ICPOLARITY_RISING;
  channelIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  channelIC.ICPrescaler = TIM_ICPSC_DIV1;
  channelIC.ICFilter = 0;

  channelOC.OCMode = TIM_OCMODE_PWM1;
  HAL_TIM_PWM_ConfigChannel(&(_timerObj.handle), &channelOC, timChannel);

  if (pinx != NC) {
    if ((int)getTimerChannel(pinx) == timChannel) {
      pinmap_pinout(pinx, PinMap_TIM);
    }
  }
}

void setCaptureCompare(uint32_t compare)
{
  int timChannel = TIM_CHANNEL_1;
  uint32_t Prescalerfactor = LL_TIM_GetPrescaler(_timerObj.handle.Instance) + 1;
  uint32_t CCR_RegisterValue;

  CCR_RegisterValue = ((__HAL_TIM_GET_AUTORELOAD(&(_timerObj.handle)) + 1) * compare) / 100;
  if ((__HAL_TIM_GET_AUTORELOAD(&(_timerObj.handle)) == 0xFFFF)
      && (CCR_RegisterValue == 0xFFFF + 1)) {
    CCR_RegisterValue = 0xFFFF;
  }
  __HAL_TIM_SET_COMPARE(&(_timerObj.handle), timChannel, CCR_RegisterValue);
}

void setPWM_T2C1(uint32_t frequency, uint32_t dutycycle)
{
  int timChannel = TIM_CHANNEL_1;
  setMode(PA_5);
  LL_TIM_SetPrescaler(_timerObj.handle.Instance, 63);
  __HAL_TIM_SET_AUTORELOAD(&_timerObj.handle, 0xFF19);
  setCaptureCompare(dutycycle);
  HAL_TIM_PWM_Start(&(_timerObj.handle), timChannel);
}

void setup() {
  Serial.begin(9600);
  while (!Serial) { }
  Serial.println("STM32, PWM output settings check <DIRECT>.");
  TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
  setupTimer(Instance);
  setPWM_T2C1(5, 10);
}

// the loop function runs over and over again forever
void loop() {
  Serial.println(counter++);
  sprin("Chan : ", channel);
  sprin("CNT  : ", TIM2->CNT);
  sprin("ARR  : ", TIM2->ARR);
  sprin("CR1  : ", TIM2->CR1);
  sprin("CCMR1: ", TIM2->CCMR1);
  sprin("CCR1 : ", TIM2->CCR1);
  sprin("CCER : ", TIM2->CCER);
  delay(10000);
}

メインループの中では、TIM2の主要なレジスタを直接読み出して表示してます。

実験結果

上記のソースをArduino IDE環境でビルド後、ターゲット機 Nucleo-F072RB(STM32F072RBマイコン搭載)に書き込みました。

シリアルモニタに出力されたレジスタ値は以下のようです。CNTみているとちゃんとカウントアップされているみたいデス。

ResultSTDIO

一方、ArduinoのD13端子出力をオシロで観察した結果が以下に。約11.5Hz、デューティ約10%の波形が観察できます。やったね。ResultWaveForm

やったね、といって今回は動いて当然のSTM32機。次回こそは小ピンマイコンCH32V003に戻ってタイマを操作してみたいと思います。先は長いな。

小ピンマイコンの酷暑(4) CH32Vのモデル?STM32のタイマを使ってみる に戻る