技術者見習いのメモ書き

電子工作や神社めぐりなどの趣味の話を中心に書いていきます。 Twitter:@hakura_riku

中国製RISC-Vマイコン CH32V307の外部割込み機能

はじめに

中国製RISC-Vマイコン CH32V307の外部割込み機能の使用方法を解説します.

前回と同様に参考文献 1を参考にCH32V307V-EVT-R1を使用して動作確認を行いましたので, その方法をメモとして残します.

CH32V307の外部割込み機能

CH32V307には16個の外部割込みが存在し, EXTI0~EXTI15を全てのIOポート(Px0~Px15(x=A/B/C/D/E))に外部割込み/イベント機能として設定することが出来ます.

しかし, 外部割込みのチャンネルとGPIOのポート番号が一致していなければならないという制約があります. つまり, PA1に対して割り込みを設定している場合, PB1, PC1, PD1, PE1には割り込みを設定できません.

割り込みのチャンネル 対応ポート
EXTI0 PA0, PB0, PC0, PD0, PE0
EXTI1 PA1, PB1, PC1, PD1, PE1
EXTI2 PA2, PB2, PC2, PD2, PE2
EXTI15 PA15, PB15, PC15, PD15, PE15

外部割込み機能有効化の手順

WCHが公式にアップロードしている外部割込みのサンプルコードに記載されている初期化プログラムは3つの機能で構成されています.

  1. 外部入力を入れるGPIOを初期化し, 入力モードとして設定する

  2. EXTernal Interrupt(EXTI)を設定する

  3. ベクタ割込みコントローラ(NVIC)を設定する

コードの中身を確認するとPA0を割り込み用のピンとして設定しており, 信号の立下りで割り込みが入るようになっている事が分かります.

void EXTI0_INT_INIT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    EXTI_InitTypeDef EXTI_InitStructure = {0};
    NVIC_InitTypeDef NVIC_InitStructure = {0};

     /* 外部入力を入れるGPIOを初期化し, 入力モードとして設定する*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /*EXTernal Interrupt(EXTI)を設定する*/
    /* GPIOA ----> EXTI_Line0 */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;      //割り込みのトリガー条件設定
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    /* ベクタ割込みコントローラ(NVIC)を設定する*/
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //割り込み優先度
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //サブプライオリティー
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

参考文献2より引用

割り込み発生時の処理

WCHが公式にアップロードしている外部割込みの実行部分はch32v30x_it.cに記載されています.

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

/*********************************************************************
 * @fn      EXTI0_IRQHandler
 *
 * @brief   This function handles EXTI0 Handler.
 *
 * @return  none
 */
void EXTI0_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line0)!=RESET)
  {
#if 0
    printf("Run at EXTI\r\n");

#endif
    EXTI_ClearITPendingBit(EXTI_Line0);     /* Clear Flag */
  }
}

参考文献2より引用

動作確認用プログラム

参考文献1に記載されているプログラムを参考に割り込みプログラムの作成を行いました. このプログラムはPA0, PC1に割り込み機能を割り当て, スイッチのON/OFFでLED1, 2を点灯/消灯させます.

#include "debug.h"
/********************************************************************
* 関数名       : GPIO_INIT
* 機能       :LED点灯用GPIOの初期化
********************************************************************/
void GPIO_INIT(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure={0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStructure);
}
/********************************************************************
* 関数名       : EXTI_INT_INIT
* 機能       : 外部割り込みの初期化
********************************************************************/
void EXTI_INT_INIT(void)
{
   GPIO_InitTypeDef  GPIO_InitStructure={0};
   EXTI_InitTypeDef EXTI_InitStructure={0};
   NVIC_InitTypeDef NVIC_InitStructure={0};

   /*
    * EXTI0の設定
    * GPIOAの0ピンに対して割り込み機能を設定する
   */
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA,ENABLE);

   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
   GPIO_Init(GPIOA, &GPIO_InitStructure);

   GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
   EXTI_InitStructure.EXTI_Line= EXTI_Line0;
   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //割り込みのトリガー条件設定
   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
   EXTI_Init(&EXTI_InitStructure);

   NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //割り込み優先度
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;        //サブプライオリティー
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);


   /*
    * EXTI1の設定
    * GPIOCの1ピンに対して割り込み機能を設定する
   */
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOC,ENABLE);

   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
   GPIO_Init(GPIOC, &GPIO_InitStructure);


   GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource1);
   EXTI_InitStructure.EXTI_Line= EXTI_Line1;
   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //割り込みのトリガー条件設定
   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
   EXTI_Init(&EXTI_InitStructure);

   NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //割り込み優先度
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;        //サブプライオリティー
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);
}

/********************************************************************
* 関数名      :  main
* 機能   : メイン関数
*********************************************************************/
int main(void)
{
    EXTI_INT_INIT(); // 割り込み機能の初期化
    GPIO_INIT();     // GPIOの初期化
    while(1);        // 無限ループ
}

/********************************************************************
* 関数名    : EXTI0_IRQHandler
* 機能    : EXTI0の割り込み関数
*********************************************************************/
void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
volatile uint16_t LED_Status = 0; // volatileを持つ割り込みで使用される変数は、グローバル変数として扱うことができる
void EXTI0_IRQHandler(void)
{
    Delay_Ms(500); //チャタリング防止用
    EXTI_ClearFlag(EXTI_Line0); // 割り込みフラグをリセット
    LED_Status = !LED_Status ;  // LEDの状態値の反転
    GPIO_WriteBit(GPIOE, GPIO_Pin_11, LED_Status); // PE11(つまりLED1)の状態を設定する
}

/********************************************************************
* 関数名    : EXTI1_IRQHandler
* 機能    : EXTI1の割り込み関数
*********************************************************************/
void EXTI1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
volatile uint16_t LED_Status2 = 0; // volatileを持つ割り込みで使用される変数は、グローバル変数として扱うことができる
void EXTI1_IRQHandler(void)
{
    Delay_Ms(500);  //チャタリング防止用
    EXTI_ClearFlag(EXTI_Line1); // 割り込みフラグをリセット
    LED_Status2 = !LED_Status2 ;  // LEDの状態値の反転
    GPIO_WriteBit(GPIOE, GPIO_Pin_12, LED_Status2); // PE11(つまりLED1)の状態を設定する
}

動作確認時の配線

CH32V307V-EVT-R1には自由に使用できるスイッチが一つしかないので, PA0で動作確認する場合とPC1で動作確認する場合それぞれで配線を変える必要があります.

・PA0とKEYの配線 : スイッチの配線PA0の割り込み機能確認時に配線する

・PC1とKEYの配線 : スイッチの配線PC1の割り込み機能確認時に配線する

・PE10とLED1の配線 : LED1の配線

・PE11とLED2の配線 : LED2の配線

ポート

おわりに

外部割込み機能の確認を行いました. サンプルコードや情報が充実(中国語ではありますが)しているので, 使用経験のある他の中国製マイコンと比較して使いやすいマイコンだと感じました.

参考文献

[1] Xy_, VeriMake, ‘‘CH32V307教程 [第二集] [GPIO], ” https://verimake.com/d/150-ch32v307-gpio.

[2] WCH, ‘‘EXTI / EXTI0, ” https://github.com/openwch/ch32v307/blob/main/EVT/EXAM/EXTI/EXTI0/User/main.c.