こんにちは。Yukiです。

今回は、TIM1を使ってPWM出力をしてみたいと思います。

 

 

 

  PWMの概要

 

PWMはタイマーのカウンタと比較することで、作り出すことができます。

これはいわゆるハードウェアタイマーと言われるものです。

 

下の図は、PWM出力の図です。

CH32V003マイコンは、TIMx_ARRレジスタに達すると、0に戻るような動作をします。

 

 

 

  TIM1でPWM出力

 

TIM2は、前回割り込みで使用したので、今回はTIM1を使用します。

 

ちなみに、TIM1はアドバンスドタイマーとして位置づけられており、その名の通り、高性能なタイマーです。

もしかしたらですが、TIM2を使ったほうが後々、幸せになれるかもしれません。

(CH32V003F4P6をタイマーが2Chしかないので、しょうがないんですけどね)

 

ただ、watchdogをタイマーとして使用する方法があるのかもしれません。(詳しくはしりません。とにかく今回はTIM1を使用します)

 

 

今回はTIM1_CH1(PD2)でPWM出力を行います。

また、TIM1_RM(リマップ)は 初期値 00で行います。

 

それでは早速やっていきましょう。

 

 

予め、bit_replace関数を宣言しておき、ビット列の置き換えを容易にします。

(2bit以上の置き換えが必要な際に使用します)

 

//  ビット置き換え関数

//  引数 data:置き換え前のビット列 | byte:置き換えるビット | len:置き換えるビットの長さ | shift:シフト数

//

uint32_t bit_replace(uint32_t data, uint32_t byte, uint8_t len, uint8_t shift) {

    uint32_t mask = ~(((1 << len) - 1) << shift);

    data &= mask;

    data |= byte << shift;

    return data;

}

 

次にPLLを有効にして、システムクロックを48MHzにします。

(任意です)

 

 

//クロック設定

//  クロック設定 PLL駆動

//  ADCクロック:未定

//  SYSCLK:48MHz

//  HCLK 48MHz

//  Core System Timer 48MHz

 

//PLL有効

    RCC->CTLR |= RCC_PLLON;

    //PLL安定動作まで待機

    while((RCC->CTLR & RCC_PLLRDY) == 0);

 

    //ADCプリスケーラ 16 (ADCPRE)

    RCC->CFGR0 = bit_replace(RCC->CFGR0, 0b11100, 5, 11);

 

    //クロックソース PLLに変更 (SW)

    RCC->CFGR0 = bit_replace(RCC->CFGR0, 0b10, 2, 0);

 

    //HCLKプリスケーラ 0 (HPRE)

    RCC->CFGR0 = bit_replace(RCC->CFGR0, 0b0000, 4, 4);

 

 

 

次に、GPIODを有効化します。


   

 

    //GPIOD 有効

    RCC->APB2PCENR |= RCC_IOPDEN;

 

 

次に、PD2をマルチプレクサファンクションモードでプッシュプルとして出力設定します。

 

    //PD2 出力モード (50MHz)

    GPIOD->CFGLR = bit_replace(GPIOD->CFGLR, 0b11 , 2, 8);

 

    //PD2 マルチプレクサファンクションプッシュプルモード

    GPIOD->CFGLR = bit_replace(GPIOD->CFGLR, 0b10, 2, 10);

 

 

 

次に、TIM1を有効にします。

 

    //TIM1 有効

    RCC->APB2PCENR |= RCC_TIM1EN;

 

 

 

次に、アップデートイベント発生を有効にします。

 

    //Update Event Generation有効

    TIM1->SWEVGR |= TIM_UG;

 

 

次に、PWM出力の大本である、メイン出力を有効化します。

 

    //TIM1 CHメイン出力有効

    TIM1->BDTR |= TIM_MOE;

 

 

次に、TIM1_CH1 (PD2)の出力を許可します。

これにより、PD2にPWMが出力されるようになります。

 

    //TIM1_CH1 (PD2) 出力許可

    TIM1->CCER |= TIM_CC1E;

 

 

次に、TIM1_CH1をPWMモードに設定します。

今回はPWM1モードにしました。

 

    //TIM1_CH1 PWMモード1

    TIM1->CHCTLR1 = bit_replace(TIM1->CHCTLR1, 0b110, 3, 4);

 

 

 

次に、TIM1_CH1のプリロードを有効化します。

 

    //TIM1_CH1 プリロード有効

    TIM1->CHCTLR1 |= TIM_OC1PE;

 

 

次に、TIM1のオートプリロードを有効にします。

 

    //オートプリロード有効

    TIM1->CTLR1 |= TIM_ARPE;

 

 

次に、TIM1のプリスケーラとオートリロード値を設定します。

 

    TIM1->PSC = 0;

 

    TIM1->ATRLR = 1000;

 

 

今回だと、システムクロック 48MHzなので、

48MHz ÷ (プリスケーラ値 0 + 1) = 48MHz

48MHz ÷ ATRLR 1000 = 48kHz

ということで、48kHzがPWM周波数となります。

 

 

次に、PWMのDuty比を設定します。

 

    TIM1->CH1CVR = 500;

 

 

今回だと、ATRLR が 1000 CH1CVRが500なので、 Duty比 50%となります。

 

最後にTIM1のカウントを有効にします。

 

    //TIM1 カウント有効

    TIM1->CTLR1 |= TIM_CEN;

 

 

 

後は、お好きなようにDuty比をセットして遊びましょう。

 

int i= 0;

 

    while(1){

        i++;

        TIM1->CH1CVR = i;

 

        if(i > 999){

            i = 0;

        }

 

        for(int j = 0;j <= 20000; j++){

            __asm("NOP");

        }

    }

 

 

こんな感じにすれば、のこぎり波みたいな出力になります。

 

 

 

  回路図

 

 

 

  実際の動作

 

黄色のジャンパ線はオシロスコープに接続しています。

 

 

このように、周波数固定で、パルス幅だけ変化しています。
PWMですね。

 

 

 

  余談

 

余談なのですが、加湿器を買いました。

ただ、湿度計がないので、作ろうかなと思っています。

前にAliExpressで買ったAHT20があった気がするので、それで作ろうと思います。