STM32F4のTIMでPWMを出してDACっぽく使うサンプル

## 使用リソース

* PB6
* TIM3(Basic)
* TIM4(PWM)
* DMA1 ch5

TIM4 ch1をPB6に接続してPWMを出力し、外部のLPFで平滑します。また、TIM3でタイミングを作ってDMAをトリガすることで、周期的にPWMのパルス幅を変更します。

## ソースコード

```c++
constexpr uint16_t period = 42;

constexpr uint16_t buff_len = 25;
static uint16_t buff[buff_len] = {};

for (uint32_t i = 0; i < buff_len; i++)
{
    buff[i] = static_cast<uint16_t>(
        period * 0.5f +
        period * 0.5f * 0.8f * sinf(static_cast<float>(i) / buff_len * 3.141592f * 2.f) +
        0.5f);
}

__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
__HAL_RCC_TIM4_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();

GPIO_InitTypeDef gpio = {
    .Pin = GPIO_PIN_6,
    .Mode = GPIO_MODE_AF_PP,
    .Pull = GPIO_NOPULL,
    .Speed = GPIO_SPEED_HIGH,
    .Alternate = GPIO_AF2_TIM4};
HAL_GPIO_Init(GPIOB, &gpio);

TIM_HandleTypeDef htim1 = {
    .Instance = TIM4,
    .Init = TIM_Base_InitTypeDef{
        .Prescaler = 0,
        .CounterMode = TIM_COUNTERMODE_UP,
        .Period = period - 1}};
HAL_TIM_PWM_Init(&htim1);

TIM_OC_InitTypeDef oc = {
    .OCMode = TIM_OCMODE_PWM1,
    .Pulse = period / 2 - 1};
HAL_TIM_PWM_ConfigChannel(&htim1, &oc, TIM_CHANNEL_1);

TIM_HandleTypeDef htim2 = {
    .Instance = TIM3,
    .Init = TIM_Base_InitTypeDef{
        .Prescaler = 0,
        .CounterMode = TIM_COUNTERMODE_UP,
        .Period = 84 - 1}};
HAL_TIM_Base_Init(&htim2);

__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE);

DMA_HandleTypeDef hdma = {
    .Instance = DMA1_Stream2,
    .Init = DMA_InitTypeDef{
        .Channel = DMA_CHANNEL_5,
        .Direction = DMA_MEMORY_TO_PERIPH,
        .PeriphInc = DMA_PINC_DISABLE,
        .MemInc = DMA_MINC_ENABLE,
        .PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD,
        .MemDataAlignment = DMA_MDATAALIGN_HALFWORD,
        .Mode = DMA_CIRCULAR,
        .Priority = DMA_PRIORITY_MEDIUM,
        .FIFOMode = DMA_FIFOMODE_DISABLE}};
HAL_DMA_Init(&hdma);

HAL_DMA_Start(&hdma,
                reinterpret_cast<uint32_t>(buff),
                reinterpret_cast<uint32_t>(&htim1.Instance->CCR1),
                buff_len);

HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_Base_Start(&htim2);
```

## 結果

![SDS00012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/546507cb-92b1-d5bf-53d4-83bdbf80c484.png)

黄色がPWM出力、紫色がCR LPFの後ろです(以下同様)。

168MHzのコアで、2分周がTIMに入り、42分周しているので、2MHzのPWMが出力されています。デューティー比50%付近では三角波状になっていますね。

***

![SDS00013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/3cf6dc88-f5a3-04f0-ba5c-d411c4d0f8af.png)

2MHzなので1周期500ナノ秒、50%で250ナノ秒、のパルスでトリガしています。

PWMに対して通常のエッジトリガを使うと、アナログ波形の位相がロックされず、見づらくなってしまうので、パルス幅でトリができるオシロがあると便利です。アナログ波形のエッジでトリガした場合はPWMの位相がロックされなくなってしまうので、パルス幅でトリガしたほうが見やすいです。

***

![SDS00011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/acdc76f1-a4e2-08fe-21f3-c0e3ceece407.png)

波形転送は1MHzで行い、25ポイントで1周期なので、40kHzのピークが出ています。高調波も出ていますが-40dB程度であまり問題はないと思います。

***

![SDS00010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/1d6bad8b-7126-ecf6-4e6a-33e47bbee318.png)

PWMが2MHzなので、その付近にもピークが出ています。40kHzの第2高調波よりも高く出ています。

***

今回は2MHzから40kHzを出しているので、50倍程度の差となり、1段のCR LPFでは高周波成分を除去しきれていません。PWM周波数を上げれば多少は改善しますが、今度はデューティー分解能が低下してしまいます。