CH32V003 has one USART interface and can be configures on various pins. It can be used both is asynchronous and synchronous mode.

What is UART Interface?
A universal asynchronous receiver-transmitter (UART) interface is a serial communication protocol that allows data to be transmitted and received one bit at a time. It is a simple and efficient way to transmit data over long distances, and is commonly used in embedded systems and microcontrollers.
A UART interface consists of two main components: a transmitter and a receiver. The transmitter converts the data from a parallel format to a serial format, and then transmits the data bit by bit. The receiver converts the serial data back to a parallel format, and then outputs the data to the receiving device.
UART interfaces are typically configured with a number of parameters, such as the baud rate, parity, and number of stop bits. The baud rate is the speed at which data is transmitted, and is typically measured in bits per second (bps). The parity is a bit that is used to check for errors in the data transmission. The number of stop bits is the number of bits that are used to mark the end of a byte of data.
CH32V003 Pinouts

Configuring Pins for UART TX and RX
We will configure D5 for UART TX and D6 for UART RX
In the code below you can see D5 configured for GPIO output in alternate function mode(GPIO_Mode_AF_PP
) and D6 is configured for input mode (GPIO_Mode_IN_FLOATING
)
After GPIO Pin configuration UART is configured for baud rate, word length, stop bits, parity, flow control, etc.
and then, Interrupt is enabled for Receive and its IRQ channel and priority is set.
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
USART_InitTypeDef USART_InitStructure = {0};
NVIC_InitTypeDef NVIC_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1, ENABLE);
/* USART1 TX-->D.5 RX-->D.6 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOD, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);;
USART_Cmd(USART1, ENABLE);
}
Code to Transmit Data
In order to transmit, we need to check if previous transmission is complete or not before sending any further data.
I have created a small function which takes point to a data buffer and it’s length as input parameters.
CH32V003 uart driver already provide a function to send one data byte over uart, we can use that function and create another function to send multiple bytes as shown below
void UartBufferSend(uint8_t* buffer, uint16_t length)
{
uint16_t tmp = 0;
for(tmp =0; tmp < length; tmp++)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) /* waiting for sending finish */
{
}
USART_SendData(USART1, buffer[tmp]);
}
}
In order to use the function you have to write like this:
UartBufferSend("Hello from Pallav Aggarwal\r\n", 28);
Code to Receive Data
Now let us see how to code to receive the UART data bytes in interrupt mode.
We have to write IRQ handler function as shown below.
void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
/*********************************************************************
* @fn USART1_IRQHandler
*
* @brief This function handles USART3 global interrupt request.
*
* @return none
*/
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
// Here you can write your code to receive the data or do whatever
}
}
}
Now let us write a code which will receive 5 bytes and only them respond with a predefined message. So out IRQ handler function need be written like:
We need to have two variable, one buffer where received data will be kept(RxBuffer1
[]) , one variable to keep the count of the number of bytes received (RxCnt1
) and another one to indicate the 5 bytes are received(Rxfinish1
) in the buffer so that we can handle rest of the login in main loop as we don’t want to be inside Interrupt service routine for long.
In the code below you can see we have copied the received data in RXBuffer1 and incremented the variable RxCnt1 and when received bytes count becomes 5 or more it will reset the RxCnt1
to zero and Rxfinish1 flag is set, this flag we can check in main code and execute further logic of responding with a predefined message
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
RxBuffer1[RxCnt1++] = USART_ReceiveData(USART1);
if(RxCnt1 >= 5)
{
RxCnt1 = 0;
Rxfinish1 = 1;
}
}
}
In the Main loop you can see GPIO 4 is configured(GPIO_Toggle_INIT
) for output so that LED can be used to show data is getting transmitted. Then, UART is configured with USARTx_CFG
(), Delay of 1000 mSec is give, actually that is not really required.
In the while one loop, it check for Rxfinish1
flag to become high, only when 5 bytes are received this flag will becomes as we saw previously and when this flag becomes high, it will send the predefines message as transit output .
You can see D4/LED pin is reset to glow the LED and UART message is sent and the LED is turned OFF by setting the LED pin. and Rxfinish1
flag is set to zero again.
Now, if again 5 bytes will be received on RX it will again send the respond message and blink the LED.
Now one can also change the logic to receive more bytes or only send response message when 5 bytes are exactly “Hello”(for example), in the once 5 bytes are received we need to check if receieved bytes are Hello or not then only set the Rxfinish1 flag else we will discard the bytes and wait for further bytes on RX.
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
GPIO_Toggle_INIT();
USARTx_CFG();
Delay_Ms(1000);
while(1)
{
if(Rxfinish1)
{
GPIO_WriteBit(GPIOD, GPIO_Pin_4, Bit_RESET);
UartBufferSend("Hello from CAPUF EMBEDDED\r\n", 27);
GPIO_WriteBit(GPIOD, GPIO_Pin_4, Bit_SET);
Rxfinish1 = 0;
}
Pin Remapping RX on D5 and TX on D6
In case you want to change the RX, TX pins as allowed as per the datasheet. We can have RX on D5 and TX on D5, we can do that with the following configuration:
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
USART_InitTypeDef USART_InitStructure = {0};
NVIC_InitTypeDef NVIC_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap2_USART1, ENABLE);
/* USART1 RX-->D.5 TX-->D.6 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOD, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);;
USART_Cmd(USART1, ENABLE);
}
Now, let me show you UART data transmit and receive in action for that I will show you how it looks on board and we will use Saleae Logic Analyzer to show what is happening on TX and RX.
See how effortlessly Saleae Logic Analyzer decodes and shows you both TX channel PC sending “Hello” string and then MCU transmitting back a response “Hello from Pallav Aggarwal”.
You can also easily zoom in zoom out, do analysis on waveform like high low timing, etc byt just hovering over waveform and with mouse scroll wheel.

Saleae logic analyzers are a series of affordable, portable, and easy-to-use logic analyzers that are popular with hobbyists, students, and professionals alike. They are available in a variety of configurations, with up to 16 digital/analogue channels. The best feature of this analyzer is it comes with free PC software which decodes several protocols and gives you data on screen in a very easy-to-understand format.
I have been using Saleae Logic Analyzer for many years now and am very happy with the performance and I can’t tell you how much it has helped in my development work. I highly recommend to anyone working in the field of embedded system design to have one.
Hardware Setup
Hardware setup is same just that programmer’s RX, TX is now connected to RX TX of Ch32V003 MCU board as well.

Sending and receiving data on UART in action
See also:
- Setting up the Development Environment for CH32V003: Compile and Run first example code
- CH32V003: GPIO as Output
- CH32V003: GPIO as Input (Polling, Interrupt)
- CH32V003: UART Transmit / Receive data
- CH32V003: PWM output
- CH32V003: ADC for analog signal measurement
- CH32V003: I2C Interface
- CH32V003: SPI Interface
- CH32V003: Timer Interrupt
- CH32V003: How to read 64-bit Unique ID
I am currently working as an embedded systems design consultant and helping companies build custom embedded products, develop test automation solution for their PCB.
If you have any feedback about the blog, you can share in the comments below or you can also contact me directly.
Read more interesting articles on Embedded Systems Design.