ライトな開発環境である ch32fun を使ってみます。
ch32fun
以前は CH32V003Fun という名前でした。
2024年の記事「ch32v003fun First Try」
https://qiita.com/nanbuwks/items/f7cc5126ee7eea72ba22
では当時の CH32V003Fun を使って、 ch32V003 を搭載した AKBONE2024 に対して試してみました。
その後、2025年2月頃の Commit 392eee7 において、ch32v003fun は ch32fun に変更になり、対応も CH32V系だけではなく、CH32X や CH5系も対応になったようだ。
CH32V203 の対応
2024年時点では、 CH32V203も対応はしていましたが、まだあんまりだった感じ。
今は CH32V203 への対応はどれだけ進んでいるのかな? 試してみました。
環境
- Ubuntu 22.04 LTS
- ch32fun
- wchisp
- WeActStudio の、BluePill+
インストール
インストールは以下
https://github.com/cnlohr/ch32fun
を参照しながらやっていきますが、書き込みは WCHISP を使ってチップの USB インターフェース経由で書き込むつもりなのでちょっとアレンジ。
準備
ビルドツールをお膳立てします。
$ sudo apt install build-essential libnewlib-dev gcc-riscv64-unknown-elf libusb-1.0-0-dev libudev-dev gdb-multiarch
wchisp
「wchisp を Ubuntu にインストール」
https://qiita.com/nanbuwks/items/b55e12d2ceaac5fffd4c
で導入した wch isp tool を使いました。
ch32fun を使って L チカするまで
$ git clone https://github.com/cnlohr/ch32fun.git
$ cd ch32fun
$ ls
LICENSE ch32fun examples_h41x examples_v10x examples_x00x minichlink platformio.ini
README.md examples examples_l103 examples_v20x examples_x035 misc projects
build_scripts examples_ch5xx examples_usb examples_v30x extralibs package.json
まずはスタンダードな examples を試してみます。
$ cd examples
$ ls
adc_dma_opamp direct_gpio mco_clock_output sysclk_config
adc_fixed_fs dma_gpio optionbytes systick_irq
adc_injection dma_gpio_mem2mem optiondata template
adc_polled dma_gpio_ws2812 random_numbers tim1_capture_dma
app_without_flash dma_scheduled_io self_modify_code tim1_capture_dma_pwm_in
bldc_gimbal dma_spi spi_24L01_rx tim1_phase_shifted_pwm
blink external_crystal spi_24L01_tx tim1_pwm
blink_raw exti_pin_change_isr spi_dac tim1_pwm_complementary_outputs
bootload flashtest spi_lora_sx126x tim1_tim2_cascade
branchless_gpio_lib function_in_ram spi_lora_sx127x tim2_encoder
breathe gpio_and_adc spi_max7219 tim2_pwm
cap_touch_adc hc595_demo spi_oled tim2_pwm_remap
cap_touch_exti hsitrim spi_slave uart_tx_dma
color_lcd i2c_oled standby_autowake uartdemo
constructors i2c_slave standby_btn usart_dma_rx_circular
cpp_virtual_methods input_capture struct_direct_gpio vector_free_table_irq
debugprintfdemo iwdg struct_gpio ws2812bdemo
blink を使ってみます。
$ cd blink
$ ls
Makefile blink.bin blink.c funconfig.h
$ cat Makefile
all : flash
TARGET:=blink
TARGET_MCU?=CH32V003
include ../../ch32fun/ch32fun.mk
flash : cv_flash
clean : cv_clean
ということで、TARGET_MCU? を以下に書き換えました。
TARGET_MCU?=CH32V203
$ cat blink.c
#include "ch32fun.h"
#include <stdio.h>
// use defines to make more meaningful names for our GPIO pins
#define PIN_1 PD9
#define PIN_K PD4
#define PIN_BOB PD6
#define PIN_KEVIN PC0
int main()
{
SystemInit();
funGpioInitAll(); // Enable GPIOs
funPinMode( PIN_1, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP ); // Set PIN_1 to output
funPinMode( PIN_K, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP ); // Set PIN_K to output
funPinMode( PIN_BOB, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP ); // Set PIN_BOB to output
funPinMode( PIN_KEVIN, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP ); // Set PIN_KEVIN to output
while(1)
{
funDigitalWrite( PIN_1, FUN_HIGH ); // Turn on PIN_1
funDigitalWrite( PIN_K, FUN_HIGH ); // Turn on PIN_K
funDigitalWrite( PIN_BOB, FUN_HIGH ); // Turn on PIN_BOB
funDigitalWrite( PIN_KEVIN, FUN_HIGH ); // Turn on PIN_KEVIN
Delay_Ms( 250 );
funDigitalWrite( PIN_1, FUN_LOW ); // Turn off PIN_1
funDigitalWrite( PIN_K, FUN_LOW ); // Turn off PIN_K
funDigitalWrite( PIN_BOB, FUN_LOW ); // Turn off PIN_BOB
funDigitalWrite( PIN_KEVIN, FUN_LOW ); // Turn off PIN_KEVIN
Delay_Ms( 250 );
}
}
となっている。 PIN?K とか PIN_BOB PIN_KEVIN ってなんだ?
とりあえず、 PIN_1 を以下のように設定
#define PIN_1 PB2
ビルドします。
$ make
riscv64-unknown-elf-gcc -E -P -x c -DTARGET_MCU=CH32V203 -DMCU_PACKAGE=2 -DTARGET_MCU_LD=2 -DTARGET_MCU_MEMORY_SPLIT= ../../ch32fun//ch32fun.ld > ../../ch32fun//generated_CH32V203F6P6_.ld
riscv64-unknown-elf-gcc -o blink.elf ../../ch32fun//ch32fun.c blink.c -g -Os -flto -ffunction-sections -fdata-sections -fmessage-length=0 -msmall-data-limit=8 -fno-tree-loop-distribute-patterns -march=rv32imac -mabi=ilp32 -DCH32V20x=1 -static-libgcc -I/usr/include/newlib -I../../ch32fun//../extralibs -I../../ch32fun/ -nostdlib -I. -Wall -Wl,--print-memory-usage -Wl,-Map=blink.map -lgcc -T ../../ch32fun//generated_CH32V203F6P6_.ld -Wl,--gc-sections
Memory region Used Size Region Size %age Used
FLASH: 892 B 32 KB 2.72%
EXT: 0 B 192 KB 0.00%
RAM: 0 B 10 KB 0.00%
riscv64-unknown-elf-objdump -S blink.elf > blink.lst
riscv64-unknown-elf-objcopy -R .storage -O binary blink.elf blink.bin
riscv64-unknown-elf-objcopy -j .storage -O binary blink.elf blink_ext.bin
riscv64-unknown-elf-objcopy -O ihex blink.elf blink.hex
../../ch32fun//../minichlink/minichlink -w blink.bin flash -b
minichlink version - eb926174a722040c3c79980d707c169a2c322acd
VID:0x1209, PID:0xb003
Error: Could not initialize any supported programmers
make: *** [../../ch32fun/ch32fun.mk:433: cv_flash] エラー 224
エラーが出ていますが、プログラムを minichlink を使って書こうとしているからです。 .elf ファイルはできているので、書き込んでみます。
$ wchisp flash blink.elf
04:01:25 [INFO] Opening USB device #0
04:01:25 [INFO] Chip: CH32V203C8T6[0x3119] (Code Flash: 64KiB)
04:01:25 [INFO] Chip UID: CD-AB-B2-61-0E-BC-8D-C9
04:01:25 [INFO] BTVER(bootloader ver): 02.70
04:01:25 [INFO] Code Flash protected: false
04:01:25 [INFO] Current config registers: a55aff0000ff00ffffffffff00020700cdabb2610ebc8dc9
RDPR_USER: 0x00FF5AA5
[7:0] RDPR 0xA5 (0b10100101)
`- Unprotected
[16:16] IWDG_SW 0x1 (0b1)
`- IWDG enabled by the software, and disabled by hardware
[17:17] STOP_RST 0x1 (0b1)
`- Disable
[18:18] STANDBY_RST 0x1 (0b1)
`- Disable, entering standby-mode without RST
[23:22] SRAM_CODE_MODE 0x3 (0b11)
`- CODE-228KB + RAM-32KB / CODE-160KB + RAM-32KB depending on the chip
DATA: 0xFF00FF00
[7:0] DATA0 0x0 (0b0)
[23:16] DATA1 0x0 (0b0)
WRP: 0xFFFFFFFF
`- Unprotected
04:01:25 [INFO] Read blink.elf as ELF format
04:01:25 [INFO] Found loadable segment, physical address: 0x00000000, virtual address: 0x00000000, flags: 0x5
04:01:25 [INFO] Section names: [".init", ".text"]
04:01:25 [INFO] Firmware size: 1024
04:01:25 [INFO] Erasing...
04:01:25 [WARN] erase_code: set min number of erased sectors to 8
04:01:25 [INFO] Erased 8 code flash sectors
04:01:26 [INFO] Erase done
04:01:26 [INFO] Writing to code flash...
█████████████████████████████████████████████████████████████████████████████████████████████████████████ 1024/102404:01:26 [INFO] Code flash 1024 bytes written
04:01:27 [INFO] Verifying...
█████████████████████████████████████████████████████████████████████████████████████████████████████████ 1024/102404:01:27 [INFO] Verify OK
04:01:27 [INFO] Now reset device and skip any communication errors
04:01:27 [INFO] Device reset
上手く書き込めて、 blink できました。
割り込み駆動の blink を試す
examples_v20x 中のサンプルプログラムを試してみます。
$ ls
blink debugprintfdemo eth_sfhip exti_blink otg_device spidmatest
canbus_network eth_raw_icmp extendedflash mcotest spi_dma_chain
exti_blink というのは何かな? ExtInterrupt という意味だそうです。試してみます。
$ cd exti_blink
$ cat exti_blink.c
/*
Short example to demonstrate how to use external interrupts.
When I tested my circuit I made the following connections:
PA10 ---- LED
PA1 ---- Debounced switch that pulls to ground and a pull up resistor.
You can set any pins for LED and switch by editing their defines.
*/
#include "ch32fun.h"
#define LED_PIN PA10
#define INT_PIN PA1
#define RISING_EDGE 1
#define FALLING_EDGE 0
static inline void blinkInterrupt( void );
// Setup all interrupt handlers the same way so we can later enable any of them in code
__attribute__((interrupt)) void EXTI0_IRQHandler( void ) { blinkInterrupt(); }
__attribute__((interrupt)) void EXTI1_IRQHandler( void ) { blinkInterrupt(); }
__attribute__((interrupt)) void EXTI2_IRQHandler( void ) { blinkInterrupt(); }
__attribute__((interrupt)) void EXTI3_IRQHandler( void ) { blinkInterrupt(); }
__attribute__((interrupt)) void EXTI4_IRQHandler( void ) { blinkInterrupt(); }
__attribute__((interrupt)) void EXTI9_5_IRQHandler( void ) { blinkInterrupt(); }
__attribute__((interrupt)) void EXTI15_10_IRQHandler( void ) { blinkInterrupt(); }
// set state of the LED_PIN
int state = 1;
static void setupEXTI( uint8_t pin, uint8_t rising )
{
uint8_t port = (uint8_t)(pin / 16); // GPIO pin numbers in ch32fun are defined as an integer. Each port adds 16 to the pin number.
uint8_t exti = (uint8_t)(pin / 4); // There are 4 AFIO->EXTICR registers. And each is responsible for 4 pins out of 16
AFIO->EXTICR[exti] |= port; // AFIO->EXTICR cares only about enabling certain port for it's corresponding 4 pins
if (rising) {
EXTI->RTENR |= 1 << (pin%16); // enable rising edge trigger
EXTI->FTENR &= ~(1 << (pin%16)); // disable falling edge trigger
} else {
EXTI->RTENR &= ~(1 << (pin%16)); // disable rising edge trigger
EXTI->FTENR |= 1 << (pin%16); // enable falling edge trigger
}
// There are 5 separate interrupts for pins 0-4 and two combined interrupts for pins 5-9 and 10-15
if( (pin%16) < 5 ) NVIC_EnableIRQ( (IRQn_Type)((uint8_t)EXTI0_IRQn + (pin%16)) );
else if( (pin%16) < 10 ) NVIC_EnableIRQ( EXTI9_5_IRQn );
else NVIC_EnableIRQ( EXTI15_10_IRQn );
// And here we are finally enable the pin number. (We enable port previously in AFIO->EXTICR)
EXTI->INTENR |= 1 << (pin%16); // Enable EXTIx line
}
int main()
{
SystemInit();
/*
The enables that end up being used in this code are
GPIOx (GPIO port enable for our pins)
AFIO (alternate function enable)
AFIOEN (alternate function clock enable)
*/
funGpioInitAll();
// configure LED_PIN for output and set it high
funPinMode( LED_PIN, GPIO_CFGLR_OUT_10Mhz_PP );
funDigitalWrite( LED_PIN, FUN_HIGH );
// configure interrupt pin for floating input
funPinMode( INT_PIN, GPIO_CFGLR_IN_FLOAT );
// You can also use pull-up/pull-down
// funPinMode( INT_PIN, GPIO_CFGLR_IN_PUPD );
// funDigitalWrite( INT_PIN, FUN_LOW );
// Set all registers needed for external interrupt on INT_PIN
setupEXTI( INT_PIN, RISING_EDGE );
while(1)
{
}
}
/*
Interrupt code
Checks if it was set low or high last
and toggles it based on that info.
*/
static inline void blinkInterrupt( void )
{
// verify the interrupt flag occured.
if( EXTI->INTFR & (1<<(INT_PIN%16)) )
{
if( state == 0 )
{
funDigitalWrite( LED_PIN, FUN_HIGH );
state = 1;
}
else
{
funDigitalWrite( LED_PIN, FUN_LOW );
state = 0;
}
EXTI->INTFR = 1<<(INT_PIN%16); // clear interrupt flag
}
}
- User KEY は PA0 、 push で HIGH
- LED は PB2 、正論理
なので、
#define LED_PIN PB2
#define INT_PIN PA0
に書き換えて
$ make
$ wchisp flash exti_blink.elf
で動作しました。
CH32V203 への ch32fun の対応
サンプルプログラムは少ないながらも CH32V20x 用のものが増えてきており、もうちょっと深堀してみてもいいかな?