wch ch32v30x support files as a meson project
Go to file
alexis b60ac56567 Update readme 2023-04-12 20:33:27 -06:00
Core Fix stack corruption on interrupt nesting 2023-02-19 12:56:05 -07:00
Debug Get rid of the usart print debug functions 2023-02-17 21:57:52 -07:00
Ld Linker script whitespace fix 2023-04-12 20:30:56 -06:00
Peripheral Copy the AFIO fix to a few more places 2023-04-12 20:31:10 -06:00
Startup ugh ugh ugh 2023-02-18 22:24:37 -07:00
README.md Update readme 2023-04-12 20:33:27 -06:00
meson.build Clean up compile arguments a bit 2023-02-19 12:56:33 -07:00
meson_options.txt Allow skipping clock init 2023-01-11 18:16:39 -07:00


ch32v30x meson subproject

This is a divergent fork of WCH's original peripheral library code, with several modifications and bugfixes, and meson build support rolled in.



  • Default: none
  • Choices: CH32V303, CH32V305, CH32V307

Which microcontroller is being targeted.


  • Default: HSE_144MHZ
  • Choices: HSE_48MHz, HSE_56MHz, HSE_72MHz, HSE_96MHz, HSE_120MHz, HSE_144MHz, HSI_48MHz, HSI_56MHz, HSI_72MHz, HSE_96MHz, HSE_120MHz, HSE_144MHz,

Select oscillator (HSE = high speed external, HSI = high speed internal) and target frequency. Currently, all settings assume an 8 MHz reference clock.


  • Default: true
  • Choices: true, false

If false, clocks are not intialized on boot. You will need to call SetSysClock() from system_ch23v30x.h when ready.


  • Default: 2048

How many bytes are allocated to the main stack.


  • Default: 288KB_32KB
  • Choices: 192KB_128KB, 224KB_96KB, 256KB_64KB, 288KB_32KB

How much flash and SRAM are available for use. The CH32V2 and CH32V3 allow the amount of SRAM used for flash acceleration backing to be specified, changing the amount available to the user.

Note that to use a setting other than the default, the correct bits must be set in the flash option byte register. This can be done at boot or by flashing the user option row, but doing it at boot needs to happen in the assembly startup routine because otherwise the stack could end up at an invalid address (issue #3).


  • Default: false
  • Choices: false, true

Flash beyond what is set by mem_split is allowed to be used, it it just slower because there is no fast SRAM backing it. If this flag is set, the full 288KB is made available to the linker, with only the amount specified by mem_split being used for backing.

If using this option, you may wish to set Flash Enhanced Read Mode to accelerate reads to the unaided flash.

See Computation of flash bounds below for how this interacts with flash_origin and flash_size.


  • Default: 0

How far into the physical flash the application flash starts. This can be used to produce an application to be loaded alongside a bootloader, for example.


  • Default: 0

How much space in the flash segment can be used by the application. This can be used to produce a bootloader or other build target that is not able to consume the entire flash.

Three choices are available:

  • Zero: the full available flash is provided to the linker.
  • Positive: exactly flash_size is provided to the linker.
  • Negative: abs(flash_size) is subtracted from the full available flash size.

In this way, a bootloader could e.g. set flash_size to 2048 and the application could set it to -2048.

Options (advanced)


  • Default: 8000000

Specify the nominal frequency of the reference HSE clock. Note that 8 MHz is assumed — if you specify a different frequency here, the real system clock will be the one selected by sysclock scaled by the ratio of extclock / 8000000.

BUG (#1): SystemCoreClock will be set to the nominal frequency of sysclock, not the scaled one. You can fix this after startup by calling SystemCoreClockUpdate().


  • Default: 1280

Number of counts to wait for HSE to come up before failing. The units are arbitrary here — the startup code just counts up to this number while checking the status flag.

BUG (#2): SetSysClockToHSE as delivered by WCH does not handle failure to start. If there is any question of whether the clock will start, or for a safety critical application, this needs to be fixed first.

Computation of flash bounds

There are several options that all affect flash size; their interactions are well-defined. The process by which flash bounds are computed is:

  1. Origin starts at 0, and length starts at the size selected by mem_split.
  2. If allow_full_flash is set, the length is expanded to the full hardware maximum.
  3. If flash_origin is set, the origin is moved to this value.
  4. If flash_size is positive, the size is set to this value. If negative, abs(flash_size) is subtracted from the current size.
  5. If flash_offset + flash_size now exceeds the maximum allowable, it is clamped, and a warning is emitted.

Provided defines

The following defines are provided to allow the application to know how its memory is configured:

  • SYS_FLASH_ORIGIN equals the final computed flash origin
  • SYS_FLASH_LEN equals the final computed flash length
  • SYS_SPLIT_FLASH_LEN equals the full system mem_split-determined flash length

HPE and stacked ISRs

CH32V supports "HPE" (Hardware Prologue/Epilogue), which is actually a form of bank-switching where up to three sets of alternate register files are switched in on interrupt entry. WCH has a patched GCC that supports this via a function attribute, but this library supports it on stock GCC, and extends it further.

The CH32V_DEF_HPE_ISR() macro allows you to define an ISR using this feature. For example:


Additionally, the CH32V_DEF_HPE_ISR_ON_STACK() macro allows you to define an ISR that will run on a dedicated stack, helpful for the occasional hefty ISR on a multitasking system with small application stacks:

CH32V_DEF_ISTACK(isr_stack, 1024);
CH32V_DEF_HPE_ISR_ON_STACK(EXTI0_IRQHandler, isr_stack)

Note that the stack pointer manipulation increases interrupt latency by several cycles, so handlers that need to be very responsive should generally use the system stack.