Back to Blog

Blinking an LED on CH32L103 Using Direct Register Access

RISC-V AI Assistant 2026-09-09 02:15:49 20 views

A step-by-step guide to toggling an LED on the WCH CH32L103 RISC-V MCU by directly manipulating clock, GPIO configuration, and output registers.

The CH32L103 is one of WCH's low-power RISC-V general-purpose MCUs. For newcomers coming from STM32, the register naming looks different but the underlying mechanics are the same. This guide walks through the bare-minimum register operations needed to blink an LED.

Three registers do the work

Turning an LED on or off is ultimately a matter of three register-level actions:
  1. Enable the port clock (power switch for the GPIO peripheral).
  2. Configure the pin as a push-pull output.
  3. Drive the pin high or low.

On CH32L103, the registers are:

Enable the GPIOB clock

Before any pin on port B responds, the clock to that peripheral must be turned on. In the reference manual this is described as RCC_PB2PCENR, bit 3.

RCC->PB2PCENR |= (1 << 3);

Configure PB0..PB7 as push-pull outputs

GPIOB_CFGLR is a 32-bit register. Each pin uses 4 bits: the lower 2 bits select input or output mode, and the upper 2 bits select the output type. For a general-purpose push-pull output, write 0x3 for each pin.

GPIOB->CFGLR = 0x33333333;

Drive the pins

GPIOB_OUTDR holds the 16-bit output value for the whole port. Writing 0xFFFF turns pins high; writing 0x0 turns them low. On the evaluation board used in the original tutorial, the LED cathode is connected to PB5, so a low output lights the LED.

GPIOB->OUTDR = 0x0000;

Complete example

#include "ch32l103.h"

int main(void) { RCC->PB2PCENR |= (1 << 3); GPIOB->CFGLR = 0x33333333; GPIOB->OUTDR = 0x0000; while (1); }

Target a single pin

If you only want PB5, clear its 4-bit field first, then set it to output mode: GPIOB->CFGLR &= ~(0xF << (4 * 5)); GPIOB->CFGLR |= (0x3 << (4 * 5)); GPIOB->OUTDR &= ~(1 << 5);

Direct address version

For the truly minimalist version, use the addresses directly. This is useful when you want to see exactly what the hardware sees: ((volatile unsigned int )0x40021018) |= (1 << 3); ((volatile unsigned int )0x40010C00) = 0x33333333; ((volatile unsigned int )0x40010C0C) = 0xFFFF;

Comparison with STM32F103

The same logic maps cleanly to STM32F103 with only register-name changes:

This makes CH32L103 a comfortable drop-in alternative for anyone already used to direct register programming on Cortex-M3.

Takeaways Source: RISC-V学习之简简单单用寄存器给CH32L103点个灯 on 21ic Forum Related hardware: WCH CH32L103, CH32V003, CH32V208, CH32V307, CH32V317.
Tags: RISC-VCH32L103CH32VWCHGPIOBare-MetalMCUTutorial

Have questions about this topic?

Start a Discussion Get a Quote