Sending commands to CH32V003 over serial from the computer.

#include "debug.h"
#include <string.h>
/**
* CH32V003 - Full USART1 Serial Input/Output Example
*
* Wiring:
* PD5 -> TX (connect to your USB-serial adapter RX)
* PD6 -> RX (connect to your USB-serial adapter TX)
*
* Behavior:
* - Echoes every character back as you type
* - When Enter is pressed, prints the full line back with a prefix
* - Demonstrates ring buffer + ISR-driven RX, polling TX
*
*/
/* ─── Ring Buffer ──────────────────────────────────────────────── */
#define RX_BUF_SIZE 128 // Must be power of 2
typedef struct {
volatile uint8_t buf[RX_BUF_SIZE];
volatile uint16_t head;
volatile uint16_t tail;
} RingBuf;
static RingBuf rxBuf = {0};
static inline void rb_push(RingBuf *rb, uint8_t byte) {
uint16_t next = (rb->head + 1) & (RX_BUF_SIZE - 1);
if (next != rb->tail) { // drop byte if full
rb->buf[rb->head] = byte;
rb->head = next;
}
}
static inline int rb_pop(RingBuf *rb, uint8_t *out) {
if (rb->tail == rb->head) {
return 0; // empty
}
*out = rb->buf[rb->tail];
rb->tail = (rb->tail + 1) & (RX_BUF_SIZE - 1);
return 1;
}
static inline int rb_available(RingBuf *rb) {
return rb->head != rb->tail;
}
/* ─── USART1 ISR ───────────────────────────────────────────────── */
void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t byte = (uint8_t)USART_ReceiveData(USART1); // also clears RXNE
rb_push(&rxBuf, byte);
}
// Clear overrun if it occurred, otherwise RX stalls
if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET) {
(void)USART_ReceiveData(USART1);
}
}
/* ─── USART Helpers ────────────────────────────────────────────── */
void USART1_Init(uint32_t baud) {
// 1. Clocks
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1, ENABLE);
// 2. GPIO: PD5=TX, PD6=RX
GPIO_InitTypeDef gpio = {0};
gpio.GPIO_Pin = GPIO_Pin_5; // TX
gpio.GPIO_Speed = GPIO_Speed_50MHz;
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &gpio);
gpio.GPIO_Pin = GPIO_Pin_6; // RX
gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOD, &gpio);
// 3. USART peripheral
USART_InitTypeDef usart = {0};
usart.USART_BaudRate = baud;
usart.USART_WordLength = USART_WordLength_8b;
usart.USART_StopBits = USART_StopBits_1;
usart.USART_Parity = USART_Parity_No;
usart.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &usart);
// 4. Enable RXNE interrupt
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 5. NVIC
NVIC_SetPriority(USART1_IRQn, 0);
NVIC_EnableIRQ(USART1_IRQn);
// 6. Enable peripheral
USART_Cmd(USART1, ENABLE);
}
// Send a single byte (blocking)
void USART1_SendByte(uint8_t byte) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, byte);
}
// Send a null-terminated string
void USART1_SendStr(const char *s) {
while (*s) USART1_SendByte((uint8_t)*s++);
}
// Send string + CR+LF
void USART1_SendLine(const char *s) {
USART1_SendStr(s);
USART1_SendByte('\r');
USART1_SendByte('\n');
}
/* ─── Read a line from the ring buffer ────────────────────────── */
// Returns 1 when a full line (terminated by \r or \n) is ready in buf.
// Call repeatedly in your main loop; non-blocking between characters.
int USART1_ReadLine(char *buf, uint16_t maxLen, uint16_t *pos) {
uint8_t byte;
while (rb_available(&rxBuf)) {
rb_pop(&rxBuf, &byte);
// Echo character back to terminal
USART1_SendByte(byte);
if (byte == '\r' || byte == '\n') {
buf[*pos] = '\0';
*pos = 0;
if (buf[0] != '\0') { // ignore blank lines
USART1_SendByte('\r');
USART1_SendByte('\n');
return 1;
}
} else if (byte == 0x7F || byte == '\b') {
// Backspace / DEL
if (*pos > 0) {
(*pos)--;
USART1_SendStr("\b \b"); // erase on terminal
}
} else if (*pos < maxLen - 1) {
buf[(*pos)++] = (char)byte;
}
}
return 0;
}
typedef void (*CmdHandler)(void);
typedef struct {
const char *name;
CmdHandler handler;
} Command;
// --- handlers ---
void cmd_hello(void) { USART1_SendLine("Hello to you too!"); }
void cmd_ver(void) { USART1_SendLine("CH32V003 Demo v1.1"); }
void cmd_clock(void) { printf("SystemClk: %d\r\n", SystemCoreClock); }
void cmd_id(void) { printf("ChipID: %08x\r\n", DBGMCU_GetCHIPID()); }
// --- table ---
static const Command commands[] = {
{ "hello", cmd_hello },
{ "ver", cmd_ver },
{ "clock", cmd_clock },
{ "id", cmd_id },
};
#define CMD_COUNT (sizeof(commands) / sizeof(commands[0]))
// --- dispatch ---
void handle_command(const char *input) {
for (uint8_t i = 0; i < CMD_COUNT; i++) {
if (strcmp(input, commands[i].name) == 0) {
commands[i].handler();
return;
}
}
USART1_SendLine("Unknown command.");
}
/* ─── main ─────────────────────────────────────────────────────── */
int main(void) {
SystemInit(); // sets up 48 MHz HSI PLL
USART1_Init(115200);
USART1_SendLine("CH32V003 USART ready. Type something and press Enter:");
char lineBuf[64] = {0};
uint16_t linePos = 0;
while (1) {
if (USART1_ReadLine(lineBuf, sizeof(lineBuf), &linePos)) {
// USART1_SendStr("You typed: ");
// USART1_SendLine(lineBuf);
handle_command(lineBuf);
}
}
}