CH32VのオレオレArduino環境を作ろう その7 割り込みとファイル構成

概要

前回はADCで今回はI2Cの予定でしたが、I2Cがうまく動きません。アドレススキャンと限定的な読み出しまではできましたが、まだまだ実用レベルには達していません。そこで割り込み関係の調査とEVTのファイル構成を確認したいと思います。

CH32Vでの割り込みについて

CH32VはSTM32のアーキテクチャを参考にしているのでSTM32に非常によくにています。ただしSTM32はCubeIDEなどである程度設定を自動生成してくれるのですが、CH32Vはすべて自力でする必要があり、いろいろ面倒な感じになっています。

CH32Vシリーズでの割り込み一覧

ITNote
ADC_IRQHandlerADC
ADC1_IRQHandlerADC1
ADC1_2_IRQHandlerADC1_2
AWU_IRQHandlerAuto Wake up
BB_IRQHandlerBLE BB
LLE_IRQHandlerBLE LLE
CAN1_RX1_IRQHandlerCAN1 RX1
CAN1_SCE_IRQHandlerCAN1 SCE
CAN2_RX0_IRQHandlerCAN2 RX0
CAN2_RX1_IRQHandlerCAN2 RX1
CAN2_SCE_IRQHandlerCAN2 SCE
CAN2_TX_IRQHandlerCAN2 TX
CMPWakeUp_IRQHandlerCMP Wake Up
DMA1_Channel1_IRQHandlerDMA1 Channel 1
DMA1_Channel2_IRQHandlerDMA1 Channel 2
DMA1_Channel3_IRQHandlerDMA1 Channel 3
DMA1_Channel4_IRQHandlerDMA1 Channel 4
DMA1_Channel5_IRQHandlerDMA1 Channel 5
DMA1_Channel6_IRQHandlerDMA1 Channel 6
DMA1_Channel7_IRQHandlerDMA1 Channel 7
DMA1_Channel8_IRQHandlerDMA1 Channel 8
DMA2_Channel1_IRQHandlerDMA2 Channel 1
DMA2_Channel2_IRQHandlerDMA2 Channel 2
DMA2_Channel3_IRQHandlerDMA2 Channel 3
DMA2_Channel4_IRQHandlerDMA2 Channel 4
DMA2_Channel5_IRQHandlerDMA2 Channel 5
DMA2_Channel6_IRQHandlerDMA2 Channel 6
DMA2_Channel7_IRQHandlerDMA2 Channel 7
DMA2_Channel8_IRQHandlerDMA2 Channel 8
DMA2_Channel9_IRQHandlerDMA2 Channel 9
DMA2_Channel10_IRQHandlerDMA2 Channel 10
DMA2_Channel11_IRQHandlerDMA2 Channel 11
DVP_IRQHandlerDVP
ETH_IRQHandlerETH
ETH_WKUP_IRQHandlerETH WakeUp
ETHWakeUp_IRQHandlerETHWakeUp
EXTI0_IRQHandlerEXTI Line 0
EXTI1_IRQHandlerEXTI Line 1
EXTI2_IRQHandlerEXTI Line 2
EXTI3_IRQHandlerEXTI Line 3
EXTI4_IRQHandlerEXTI Line 4
EXTI9_5_IRQHandlerEXTI Line 9..5
EXTI15_10_IRQHandlerEXTI Line 15..10
EXTI7_0_IRQHandlerEXTI Line 7..0
EXTI15_8_IRQHandlerEXTI Line 15..8
EXTI25_16_IRQHandlerEXTI Line 25..16
FLASH_IRQHandlerFlash
I2C1_ER_IRQHandlerI2C1 Error
I2C1_EV_IRQHandlerI2C1 Event
I2C2_ER_IRQHandlerI2C2 Error
I2C2_EV_IRQHandlerI2C2 Event
LPTIM_IRQHandlerLPTIM
LPTIMWakeUp_IRQHandlerLPTIM Wake up
OPA_IRQHandlerOPA
OPCM_IRQHandlerOPCM
OSC32KCal_IRQHandlerOSC32 KCal
OSCWakeUp_IRQHandlerOSC Wake Up
PIOC_IRQHandlerPIOC
PVD_IRQHandlerPVD through EXTI Line detect
RCC_IRQHandlerRCC
RNG_IRQHandlerRNG
RTC_IRQHandlerRTC
RTCAlarm_IRQHandlerRTC Alarm through EXTI Line
SDIO_IRQHandlerSDIO
SPI1_IRQHandlerSPI1
SPI2_IRQHandlerSPI2
SPI3_IRQHandlerSPI3
TAMPER_IRQHandlerTAMPER
TIM1_BRK_IRQHandlerTIM1 Break
TIM1_CC_IRQHandlerTIM1 Capture Compare
TIM1_TRG_COM_IRQHandlerTIM1 Trigger and Commutation
TIM1_UP_IRQHandlerTIM1 Update
TIM2_BRK_IRQHandlerTIM2 Break
TIM2_CC_IRQHandlerTIM2 Capture Compare
TIM2_TRG_COM_IRQHandlerTIM2 Trigger and Commutation
TIM2_UP_IRQHandlerTIM2 Update
TIM2_IRQHandlerTIM2
TIM3_IRQHandlerTIM3
TIM4_IRQHandlerTIM4
TIM5_IRQHandlerTIM5
TIM6_IRQHandlerTIM6
TIM7_IRQHandlerTIM7
TIM8_BRK_IRQHandlerTIM8 Break
TIM8_CC_IRQHandlerTIM8 Capture Compare
TIM8_TRG_COM_IRQHandlerTIM8 Trigger and Commutation
TIM8_UP_IRQHandlerTIM8 Update
TIM9_BRK_IRQHandlerTIM9 Break
TIM9_CC_IRQHandlerTIM9 Capture Compare
TIM9_TRG_COM_IRQHandlerTIM9 Trigger and Commutation
TIM9_UP_IRQHandlerTIM9 Update
TIM10_BRK_IRQHandlerTIM10 Break
TIM10_CC_IRQHandlerTIM10 Capture Compare
TIM10_TRG_COM_IRQHandlerTIM10 Trigger and Commutation
TIM10_UP_IRQHandlerTIM10 Update
USART1_IRQHandlerUSART1
USART2_IRQHandlerUSART2
USART3_IRQHandlerUSART3
USART4_IRQHandlerUSART4
UART4_IRQHandlerUART4
UART5_IRQHandlerUART5
UART6_IRQHandlerUART6
UART7_IRQHandlerUART7
UART8_IRQHandlerUART8
USB_HP_CAN1_TX_IRQHandlerUSB HP and CAN1 TX
USB_LP_CAN1_RX0_IRQHandlerUSB LP and CAN1RX0
USBFS_IRQHandlerUSBFS Break
USBFSWakeUp_IRQHandlerUSBFS Wake up from suspend
USBHS_IRQHandlerUSBHS
USBHSWakeup_IRQHandlerUSBHS Wakeup
USBPD_IRQHandlerUSBPD
USBPDWakeUp_IRQHandlerUSBPD Wake up
USBWakeUp_IRQHandlerUSB Wakeup from suspend
WWDG_IRQHandlerWindow Watchdog

上記が割り込み名とその説明になります。基本はSTM32と同じ名前のはずですが、独自拡張したものも増えています。「ETH_WKUP_IRQHandler」と「ETHWakeUp_IRQHandler」などはおそらく同じ役割だと思いますが命名がわかれてしまっています。

CH32Vシリーズでの割り込みチップ別一覧

ITc
h
3
2
l
1
0
3
c
h
3
2
v
0
0
3
c
h
3
2
v
0
0
6
c
h
3
2
v
1
0
3
c
h
3
2
v
2
0
x
_
D
6
c
h
3
2
v
2
0
x
_
D
8
c
h
3
2
v
2
0
x
_
D
8
W
c
h
3
2
v
3
0
x
_
D
8
c
h
3
2
v
3
0
x
_
D
8
C
c
h
3
2
x
0
3
5
ADC_IRQHandlerO
ADC1_IRQHandlerOOO
ADC1_2_IRQHandlerOOOOOO
AWU_IRQHandlerOOO
BB_IRQHandlerO
LLE_IRQHandlerO
CAN1_RX1_IRQHandlerOOOOOO
CAN1_SCE_IRQHandlerOOOOOO
CAN2_RX0_IRQHandlerO
CAN2_RX1_IRQHandlerO
CAN2_SCE_IRQHandlerO
CAN2_TX_IRQHandlerO
CMPWakeUp_IRQHandlerO
DMA1_Channel1_IRQHandlerOOOOOOOOOO
DMA1_Channel2_IRQHandlerOOOOOOOOOO
DMA1_Channel3_IRQHandlerOOOOOOOOOO
DMA1_Channel4_IRQHandlerOOOOOOOOOO
DMA1_Channel5_IRQHandlerOOOOOOOOOO
DMA1_Channel6_IRQHandlerOOOOOOOOOO
DMA1_Channel7_IRQHandlerOOOOOOOOOO
DMA1_Channel8_IRQHandlerOOOOO
DMA2_Channel1_IRQHandlerOO
DMA2_Channel2_IRQHandlerOO
DMA2_Channel3_IRQHandlerOO
DMA2_Channel4_IRQHandlerOO
DMA2_Channel5_IRQHandlerOO
DMA2_Channel6_IRQHandlerOO
DMA2_Channel7_IRQHandlerOO
DMA2_Channel8_IRQHandlerOO
DMA2_Channel9_IRQHandlerOO
DMA2_Channel10_IRQHandlerOO
DMA2_Channel11_IRQHandlerOO
DVP_IRQHandlerO
ETH_IRQHandlerOOO
ETH_WKUP_IRQHandlerO
ETHWakeUp_IRQHandlerOO
EXTI0_IRQHandlerOOOOOOO
EXTI1_IRQHandlerOOOOOOO
EXTI2_IRQHandlerOOOOOOO
EXTI3_IRQHandlerOOOOOOO
EXTI4_IRQHandlerOOOOOOO
EXTI9_5_IRQHandlerOOOOOOO
EXTI15_10_IRQHandlerOOOOOOO
EXTI7_0_IRQHandlerOOO
EXTI15_8_IRQHandlerO
EXTI25_16_IRQHandlerO
FLASH_IRQHandlerOOOOOOOOOO
I2C1_ER_IRQHandlerOOOOOOOOOO
I2C1_EV_IRQHandlerOOOOOOOOOO
I2C2_ER_IRQHandlerOOOOOOO
I2C2_EV_IRQHandlerOOOOOOO
LPTIM_IRQHandlerO
LPTIMWakeUp_IRQHandlerO
OPA_IRQHandlerOO
OPCM_IRQHandlerO
OSC32KCal_IRQHandlerOO
OSCWakeUp_IRQHandlerOO
PIOC_IRQHandlerO
PVD_IRQHandlerOOOOOOOOOO
RCC_IRQHandlerOOOOOOOOO
RNG_IRQHandlerOO
RTC_IRQHandlerOOOOOOO
RTCAlarm_IRQHandlerOOOOOOO
SDIO_IRQHandlerOO
SPI1_IRQHandlerOOOOOOOOOO
SPI2_IRQHandlerOOOOOOO
SPI3_IRQHandlerOO
TAMPER_IRQHandlerOOOOOOO
TIM1_BRK_IRQHandlerOOOOOOOOOO
TIM1_CC_IRQHandlerOOOOOOOOOO
TIM1_TRG_COM_IRQHandlerOOOOOOOOOO
TIM1_UP_IRQHandlerOOOOOOOOOO
TIM2_BRK_IRQHandlerO
TIM2_CC_IRQHandlerO
TIM2_TRG_COM_IRQHandlerO
TIM2_UP_IRQHandlerO
TIM2_IRQHandlerOOOOOOOOO
TIM3_IRQHandlerOOOOOOOO
TIM4_IRQHandlerOOOOOOO
TIM5_IRQHandlerOOOO
TIM6_IRQHandlerOO
TIM7_IRQHandlerOO
TIM8_BRK_IRQHandlerOO
TIM8_CC_IRQHandlerOO
TIM8_TRG_COM_IRQHandlerOO
TIM8_UP_IRQHandlerOO
TIM9_BRK_IRQHandlerOO
TIM9_CC_IRQHandlerOO
TIM9_TRG_COM_IRQHandlerOO
TIM9_UP_IRQHandlerOO
TIM10_BRK_IRQHandlerOO
TIM10_CC_IRQHandlerOO
TIM10_TRG_COM_IRQHandlerOO
TIM10_UP_IRQHandlerOO
USART1_IRQHandlerOOOOOOOOOO
USART2_IRQHandlerOOOOOOOOO
USART3_IRQHandlerOOOOOOOO
USART4_IRQHandlerOO
UART4_IRQHandlerOOOOO
UART5_IRQHandlerOO
UART6_IRQHandlerOO
UART7_IRQHandlerOO
UART8_IRQHandlerOO
USB_HP_CAN1_TX_IRQHandlerOOOOOO
USB_LP_CAN1_RX0_IRQHandlerOOOOOO
USBFS_IRQHandlerOOOOOOOO
USBFSWakeUp_IRQHandlerOOOOO
USBHS_IRQHandlerO
USBHSWakeup_IRQHandlerO
USBPD_IRQHandlerOO
USBPDWakeUp_IRQHandlerOO
USBWakeUp_IRQHandlerOOOOO
WWDG_IRQHandlerOOOOOOOOOO

上記が一覧になります。チップごとに微妙に違います。割り込みをみることである程度チップごとの機能差もわかると思います。

割り込みが呼ばれる順序

定義

割り込み自体は「startup_ch32v10x.S」などというアセンブラで記述されたスタートアップコードにて定義されています。

...
    .option rvc;
    .section    .text.vector_handler, "ax", @progbits
    .weak   NMI_Handler
    .weak   HardFault_Handler
    .weak   SysTick_Handler
    .weak   SW_Handler
    .weak   WWDG_IRQHandler
    .weak   PVD_IRQHandler
    .weak   TAMPER_IRQHandler
    .weak   RTC_IRQHandler
    .weak   FLASH_IRQHandler
    .weak   RCC_IRQHandler
    .weak   EXTI0_IRQHandler
    .weak   EXTI1_IRQHandler
    .weak   EXTI2_IRQHandler
    .weak   EXTI3_IRQHandler
    .weak   EXTI4_IRQHandler
...

上記のようにweakで定義されており、仮の関数も登録されているため未定義でもビルドできるようになっています。この関数を自分で定義することにより割り込み時に呼ばれるコールバック関数になります。

宣言部

void NMI_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void HardFault_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void TIM1_CC_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

void NMI_Handler(void)
{
    while(1)
    {
    }
}

void HardFault_Handler(void)
{
    while(1)
    {
    }
}

void TIM1_CC_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM1, TIM_IT_CC1) != RESET)
    {
        printf("CH1_Val:%d\r\n", TIM_GetCapture1(TIM1));
        TIM_SetCounter(TIM1, 0);
    }

    if(TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)
    {
        printf("CH2_Val:%d\r\n", TIM_GetCapture2(TIM1));
    }

    TIM_ClearITPendingBit(TIM1, TIM_IT_CC1 | TIM_IT_CC2);
}

CH32V103のTIM/Input_Captureプロジェクトのch32v10x_it.cから抜粋したものです。WCH-Interrupt-fast属性をつけて上書きすることで割り込み時に呼び出されます。

そしてこの割り込み関数はどこに宣言しても動きます。通常は_it.cで宣言されている事が多いですが、main.cに記述があったりと非常にばらつきがあります。EVTを確認するときにも_it.cの中身も確認したほうがよいと思います。

その他のファイル構成

User/main.c

main関数が宣言されている基本となるファイル。このファイルから中を確認する。

User/ch32v10x_conf.h

内部で利用しているPeripheralを読み込む宣言部分。GPIO_Toggleだと基本的なベーシックなものがincludeされている。Arduino化するときにはすべてのファイルを列挙したものに書き換えてあります。通常使うときにも全部入りでリンク時に未使用のものを除外する動きでも問題ないと思います。

User/system_ch32v10x.c

クロックの指定をしているファイル。EVTでも例によって微妙にクロック指定が違うので気をつける必要があります。またCH32V103はMAX80MHzと書いてありますが、指定は72MHzまでしかできないなどの微妙な仕様です。

SRC/Core

RISC-V向けの関数をインラインアセンブラで記述してあるファイルです。通常は編集する必要はないと思います。

SRC/Debug

UARTの初期化や、printfをしたときにどこに出力をするのかの_write関数などが定義されています。

SRC/Ld

リンクで利用するLink.ldが保存されています。

MEMORY
{
	FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64K
	RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
}

上記のようにメモリやフラッシュ容量が記述されていますがEVTでは同じファイルを使いまわしています。

SRC/Peripheral

実際の機能を利用するためのラッパー関数が入っています。ある程度チップ差を吸収してくれるようになっていますが微妙に欲しい機能がなかったりします。

void GPIO_DeInit(GPIO_TypeDef *GPIOx)
{
    if(GPIOx == GPIOA)
    {
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, ENABLE);
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, DISABLE);
    }
    else if(GPIOx == GPIOB)
    {
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOB, ENABLE);
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOB, DISABLE);
    }
...

例えば上記のように初期状態に戻す関数はあるのですが、初期化する関数がありません。そのためENABLEする場合にはチップ別に上記と同じような処理を書く必要があります。

SRC/Startup

スタートアップのアセンブラが保存されています。チップのバリエーション等で複数のファイルが入っている場合があったり、openwchのArduino Coreだと動作させる電圧で違うスタートアップになったりするのですが細かい情報がないのでなかなか手をいれるのが難しいです。

まとめ

なかなかEVTは便利なのですが、単一機能だけのシンプルなものがなかったり、実際に利用するときのユースケースがなかったりと試行錯誤する必要があるものが多いです。

コメント