春雨日記 about me tags

STM32はHALでPWMを簡単に出力する事ができますが,キャリア周波数はCubeMXのタイマ設定で行っており,面倒に感じていました.

ここではタイマのレジスタを操作して動的に周波数を変更する方法を紹介します.

というわけでお久しぶりです.

あまりにも書いてないんで研究でやってることちょっと残しとこうかなと.笑

今回のターゲットはF401CCU6,あの黒いボードです.

普通にタイマを設定

まずCubeIDEで適当にプロジェクトを作り,タイマを設定しましょう.

こんな感じで,とりあえずauto-reloadだけは有効にしておきます.

STM32のPWMについて

STM32のタイマ周波数は,主に以下の2つのパラメータを用いて決定されます.

Prescaler

名前の通り分周器で,供給されているクロックをこの値で割ります.

なお,-1されている値なので0で1です.

Counter Period

カウンタの上限値です.この値を超えると0に戻ります.

こっちも-1されているので,999が1000です.

図: 三角波との比較によるPWM波生成イメージ

(イラスト汚すぎワロタ)

STM32の場合,このキャリア波がタイマによって増加される値(ノコギリ波)となります.

Mode1だと閾値がカウンタの値より下回っていたら出力がSET,Mode2だとその逆になります.

両方揃えれば逆の出力を得られてFET制御では嬉しい感じですが,普通にPWM Generation CH1 CH1Nに設定すればNOT出力も得られる上にデッドタイムを付加できるので圧倒的に使いやすいという…

値を設定するときに__HAL_TIM_SET_COMPAREを使用すると思いますが,なんでCOMPAREかというと比較する値を操作するマクロだからですね.

Internal Clock Divisionを使うと更に2か4で分周できますが,ここでは置いておきます.

手動で設定する時,Prescalerを83,Counterを999

にすることで1kHzのPWM波形を出力するのを多用していましたが(※クロックが84MHzの場合),ちゃんと設定することで多種多様な周波数を得る事ができます.

関数で指定した周波数を出力する

自分で計算するのは面倒な上につぶしがきかないので,これを関数にしてみました.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
uint32_t cmax = 0;
void setPSC(uint32_t TIMHz,uint32_t destFreq, TIM_HandleTypeDef* htim){
	uint32_t psc = 1;
	while((cmax = ((TIMHz/psc)/destFreq)) >65535){
		psc++;
		if(psc > 65535)
			break;
	}

	uint32_t count = (TIMHz/psc)/destFreq;

	htim->Instance->PSC = psc - 1;
	htim->Instance->ARR = count - 1;
}

カウンタが適当な範囲になるまで割り続け,良い感じになったらペリフェラルに設定するようになっています.

先ほど述べたとおり,__HAL_TIM_SET_COMPAREはカウンタの値と比較するため,カウンタの最大値をcmaxとして外部で参照できるようにしてあります.

使用するときはこんな感じ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_TIM_Base_Start(&htim1);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  uint16_t HZ = 1;
  while (1)
  {
	  HAL_Delay(100);
	  HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
	  setPSC(84e6, HZ, &htim1);
	  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,cmax/2);

	  HZ+=10;
	  if(HZ>=10e4)
		  while(1);
    /* USER CODE END WHILE */

10倍ずつ増やしています.

こんな感じ.

おわりに

HALと生で触るのを併用するのが最強だと思います(謎)