こんにちは。Yukiです。
今回は、CH32V003F4P6でタイマー割り込みをしたいと思います。
タイマー割り込みの概要
基本的には、PFIC(Programmable Fast Interrupt Controller)を操作することで、タイマー割り込みを制御することができます。
そのため、基本的には、TIM2の設定をした後に、PFICの設定をしなければいけません。
TIM2でタイマー割り込みしてみる
タイマーの設定は、基本的にSTM32と同じです。
後、今までだと、
みたいにやってきましたが、見にくいぞゴラって怒られたので、
とします。 これで若干見やすくなったでしょうか。
(個人的にはどっちもどっちな気もしますが…)
ちなみに、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での配布です。
コピー等してご利用ください。