こんにちは。Yukiです。

今回は、CH32V003F4P6でタイマー割り込みをしたいと思います。

 

 

 

  タイマー割り込みの概要

 

基本的には、PFIC(Programmable Fast Interrupt Controller)を操作することで、タイマー割り込みを制御することができます。

 

そのため、基本的には、TIM2の設定をした後に、PFICの設定をしなければいけません。

 

 

 

  TIM2でタイマー割り込みしてみる

 

タイマーの設定は、基本的にSTM32と同じです。

 

後、今までだと、

 

GPIOA->OUTDR |= (1 << 1);

 

みたいにやってきましたが、見にくいぞゴラって怒られたので、

 

GPIO->CFGLR |= GPIO_OUTDR_ODR1;

 

とします。 これで若干見やすくなったでしょうか。

(個人的にはどっちもどっちな気もしますが…)

 

ちなみに、GPIO_OUTDR_ODR1などは、ch32v00x.hで宣言されています。

 

ビット操作が複数必要な場合、(例えば2bit分の操作が必要など)bit_replace関数を使ったほうが楽なので、その辺は、従来どおりにします。

 

それでは早速やっていきます。

 

予め、bit_replase関数を定義しておきます。

 

//  ビット置き換え関数

//  引数 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有効

    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);

 

これらは、関数化します。(今回はClock_init関数としました)

 

次に、ポート設定をします。

 

//RAポート有効

    RCC->APB2PCENR |= RCC_APB2Periph_GPIOA;

    RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;

 

    //PA1,PA2有効

    AFIO->PCFR1 &= ~(AFIO_PCFR1_PA12_REMAP);

 

    //PA1 プッシュプル出力

    GPIOA->CFGLR |= (0b11 << 4);

    //PA1 プッシュプル出力

    GPIOA->CFGLR &= ~(0b11 << 6);

    //PA2 プッシュプル出力

    GPIOA->CFGLR |= (0b11 << 8);

    //PA2 プッシュプル出力

    GPIOA->CFGLR &= ~(0b11 << 10);

 

 

今回、PA2も初期化していますが、これは不要です。

また、AFIO_PCFR1レジスタのPA12_RMを0にしています。

これにより、PA1とPA2をGPIOとして使用することができます。

 

次にタイマーを設定します。

 

    //TIM2有効 (TIM2EN)

    RCC->APB1PCENR |= RCC_APB1Periph_TIM2;

 

    //TIM2 イベント設定 (UG)

    TIM2->SWEVGR |= TIM_UG;

 

    //オートリロードプレロード有効(ARPE)

    TIM2->CTLR1 |= TIM_ARPE;

    //TIM2 Update割り込み有効(UIE)

    TIM2->DMAINTENR |= TIM_UIE;

 

    //TIM2プリスケーラ

    TIM2->PSC = 47;

 

    //TIM2 オートリロードレジスタ

    //(1kHz周期 1ms)

    TIM2->ATRLR = 1000;

 

基本的には、TIM2プリスケーラとTIM2オートリロードレジスタを設定することで、割り込み周期を設定することができます。

なお、今回は48MHzなので、

48MHz ÷ TIM2プリスケーラ (今回は 47 + 1) =  1MHz

1 ÷ 1MHz = 1uS

1uS × 1000 = 1ms

となり、1msごとの割り込みになります。

 

 

次に、PFICを設定します。

 

    //TIM2 NVIC 割り込み有効 (INTEN#38)

    PFIC->IENR[1] |= (1 << 6);

 

 

なお、基本的には、割り込み優先度などは、予め決まっているので、それらを変更しなければ、これだけで十分です。割り込み優先度やベクターなど複雑な設定を変更する場合には、もっと奥深くいじらないと行けないので、HALを使った方が楽だと思います。

 

最後にタイマー(TIM2)のカウントを有効にします。

 

//TIM2カウント有効(CEN)

TIM2->CTLR1 |= TIM_CEN;

 

 

次に、割り込み先の関数を作ります。

 

//タイマー割り込み

void TIM2_IRQHandler(void)__attribute__((interrupt("WCH-Interrupt-fast")));

 

 

ちなみに、今回はTIM2ですが、別の割り込みを生成する場合は、Startupにある、startup_ch32v00x.Sの中のキーワードを使用します。

 

これはプロトタイプ宣言なので、下の方に関数の本体を作ってあげます。

 

//1msごとの割り込み

void TIM2_IRQHandler(void) {

    //TIM2 割り込み解除(UIF)

    TIM2->INTFR &= ~(1 << 0);

 

    GPIOA->OUTDR ^= GPIO_OUTDR_ODR1;

}

 

 

ちなみに、TIM2の割り込みフラグを下げる必要があります。

今回は、PA1の出力を反転していますが、ここが1msごとに割り込みされています。

 

 

 

  割り込みしている様子

 

こんな感じで実験しています。

 

 

オシロスコープの画像です。

500Hzなので、1msごとの割り込みができていますね。

 

 

 

  ソースコード

 

今回もGoogle Driveでの配布です。

 

 

コピー等してご利用ください。