STM32F303k8+HALで相補PWM生成を試した

#はじめに

なんとなく相補PWM(Complementary PWM)を生成したい気分だったのでやってみました.

手探りでやったため色々と不適切な点があるかと思います.
その時は指摘していただければ修正いたします.

##内容
- CubeMXでの設定
- 生成されたプログラムへの追加点

#CubeMxでの設定

まず,303k8用にprojectを作成します.

今回はnucleo-303k8を使っていきます.

初期状態はこんな感じです。

![first.png](https://qiita-image-store.s3.amazonaws.com/0/84997/14bf3b5e-324c-858d-7160-1aac8e26782e.png)

##pinoutタブ

ツリーのPeripheralsからTIM1を展開し,Channel1の設定を
`PWM Generation CH1 CH1N`
にします.


すると,
18pin → TIM1_CH1
13pin → TIM1_CH1N
と設定されます。

![second.png](https://qiita-image-store.s3.amazonaws.com/0/84997/076b1417-e585-eb63-871d-04427b659024.png)

注意してほしい点として,303k8では相補PWMはAdvanced timerであるTIM1しか使えません.

TIM2やTIM3などのGeneral-purpose timerでは相補PWMはサポートされていないので気を付けましょう.
**[3/10追記]
TIM15,16,17にも相補のチャンネルがあるっぽいですが、これらはまだ試してないです.**

##configurationタブ

ピンの設定が完了後,configurationタブに移動し,TIM1の各値を設定していきます.

![third.png](https://qiita-image-store.s3.amazonaws.com/0/84997/35708057-64f2-8f97-0597-645a67fa5410.png)

値を設定するタブは
- Counter Settings
- PWM Generation Channel 1 and 1N
の2つです.


まずCounter SettingsタブでPWMの周波数を設定します.

今回は20kHzでPWMを出力するようにPrescalerとCounter Periodを決めます。

動作クロックは内部の8MHzを使うので
Prescaler:0
Counter Period:400-1
(カウントは0からなので1引いた399を設定する)
とします.

PWM Generation Channel 1 and 1Nタブでは
Mode: Assymetric PWM1
とします.

**[3/9追記]
デッドタイムの設定もCubeMxでできます.
Break And Dead  Time managementタブの一番下の欄に
Dead Time:10(0-255で任意の値)
と設定します.**
**CubeMx側で設定すればコード再生成時等に楽です.**

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/84997/ce5e38dc-5937-1f08-c0ee-10d99d3c8e5c.png)



ここまで設定したら,コードを生成します.
![fourth.png](https://qiita-image-store.s3.amazonaws.com/0/84997/52daa72f-b59b-1881-3060-c1d279e44882.png)

#プログラムへの追加

コードは量が多いうえに見づらいので~~後ほど~~Github等に上げます.

**[3/10追記]
[こちら](https://github.com/wakoruru/STM32F3_HAL_Projects/tree/master/Test12_IMC_alone/Test12_IMC_alone)に上がっています。_Src/main.c_が本体です。**

今回は変えたポイントと追加した関数について解説します.

##変更点
**[3/9追記]
上記の通りCubeMx側で設定してしまった方がコード再生成等の時に楽です。**

___
変えるポイントは,MX_TIM1_Init関数内の一か所です

```c:change_point
static void MX_TIM1_Init()
{
......
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 10; 
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.BreakFilter = 0;
  sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
  sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
  sBreakDeadTimeConfig.Break2Filter = 0;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
}
```

`sBreakDeadTimeConfig.DeadTime = 10; `
ここでデッドタイムを設定します.
値は8bitで0-255の間です.今回はとりあえず10とします.
___
##user関数の追加

また,PWMを扱いやすくするために以下の関数を定義します.
プロトタイプ宣言は適宜お願いします.

```c:user_function
void user_tim1_pwm_setvalue(float value)
{
   TIM_OC_InitTypeDef sConfigOC;
   sConfigOC.OCMode = TIM_OCMODE_ASSYMETRIC_PWM1;
   sConfigOC.Pulse = (uint32_t)((400)*value);
   sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
   sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
   sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
   sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
   sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
   HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
   HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
}
```

この関数が相補で出力する際のポイントとなります.

出力する値を決めます.
`sConfigOC.Pulse = (uint32_t)((400)*value);`

設定値を反映します.
`HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);`

CH1へ出力します.
`HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);`
CH1Nへ出力します.
`HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);`

一番最後の`HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);`がないと,CH1Nからは出力されません.

できたらビルドして書き込んで動かしてオシロで見ると以下のような波形が得られると思います.

![image](https://qiita-image-store.s3.amazonaws.com/0/84997/fec999ab-2c91-a532-84d4-3cee68655775.png)


やり方はとりあえず以上です.

よくわからないこと,質問等あったらよろしくお願いします.