The following steps demonstrate how to establish a UART link between the MM32F5330 and an onboard CH340 USB-to-serial converter, utilizing pins PA9 (TX) and PA10 (RX). The implementation covers peripheral clock setup, GPIO multiplexing, UART configuration, and receive interrupt management.
Hardware Connection
The CH340 connects directly to the microcontroller: its RX line attaches to PA9 (UART1 TX), and its TX line attaches to PA10 (UART1 RX).
UART Initializasion
Data Structures
Declare three configuration structures for GPIO, NVIC, and UART:
GPIO_InitTypeDef gpio_cfg;
NVIC_InitTypeDef nvic_cfg;
UART_InitTypeDef uart_cfg;
Core Initialization Sequence
- Clock and Basic Parameters: Enable the UART1 peripheral clock, then populate the UART settings—baud rate, 8-bit word length, one stop bit, no parity, no hardware flow control, and bidirectional mode.
RCC_APB1PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);
UART_StructInit(&uart_cfg);
uart_cfg.BaudRate = Baudrate;
uart_cfg.WordLength = UART_WordLength_8b;
uart_cfg.StopBits = UART_StopBits_1;
uart_cfg.Parity = UART_Parity_No;
uart_cfg.HWFlowControl = UART_HWFlowControl_None;
uart_cfg.Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(UART1, &uart_cfg);
- GPIO Alternate Function: Clock GPIOA and map PA9/PA10 to UART1 alternate function 7. Configure the pins as high-speed push-pull alternate function.
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_7);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_7);
GPIO_StructInit(&gpio_cfg);
gpio_cfg.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
gpio_cfg.GPIO_Speed = GPIO_Speed_High;
gpio_cfg.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &gpio_cfg);
- NVIC Setup: Configure the interrupt channel for UART1 with preemption priority 0 and sub-priority 1, then enable the channel.
nvic_cfg.NVIC_IRQChannel = UART1_IRQn;
nvic_cfg.NVIC_IRQChannelPreemptionPriority = 0;
nvic_cfg.NVIC_IRQChannelSubPriority = 1;
nvic_cfg.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_cfg);
- Final Enables: Activate the receive interrupt, enable the UART1 interrupt in the NVIC, and start the UART peripheral.
UART_ITConfig(UART1, UART_IT_RX, ENABLE);
NVIC_EnableIRQ(UART1_IRQn);
UART_Cmd(UART1, ENABLE);
Combined Initialization Function
All steps are encapsulated in a single routine that accepts a baud rate:
void UART1_InitWithBaud(uint32_t baud)
{
GPIO_InitTypeDef gpio_cfg;
NVIC_InitTypeDef nvic_cfg;
UART_InitTypeDef uart_cfg;
RCC_APB1PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);
UART_StructInit(&uart_cfg);
uart_cfg.BaudRate = baud;
uart_cfg.WordLength = UART_WordLength_8b;
uart_cfg.StopBits = UART_StopBits_1;
uart_cfg.Parity = UART_Parity_No;
uart_cfg.HWFlowControl = UART_HWFlowControl_None;
uart_cfg.Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(UART1, &uart_cfg);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_7);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_7);
GPIO_StructInit(&gpio_cfg);
gpio_cfg.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
gpio_cfg.GPIO_Speed = GPIO_Speed_High;
gpio_cfg.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &gpio_cfg);
nvic_cfg.NVIC_IRQChannel = UART1_IRQn;
nvic_cfg.NVIC_IRQChannelPreemptionPriority = 0;
nvic_cfg.NVIC_IRQChannelSubPriority = 1;
nvic_cfg.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_cfg);
UART_ITConfig(UART1, UART_IT_RX, ENABLE);
NVIC_EnableIRQ(UART1_IRQn);
UART_Cmd(UART1, ENABLE);
}
Receive Interrupt Handler
The interrupt service routine reads incoming characters and assembles them into a buffer. Line endings (\n or \r) reset the buffer index so each new message starts fresh. After processing, the RX pending flag is cleared.
void UART1_IRQHandler(void)
{
static uint8_t idx = 0;
uint8_t ch = 0;
if (SET == UART_GetITStatus(UART1, UART_IT_RX))
{
ch = UART_ReceiveData(UART1);
if (ch == '\n' || ch == '\r')
{
idx = 0;
}
rx_buffer[idx] = ch;
idx++;
UART_ClearITPendingBit(UART1, UART_IT_RX);
}
}
Application Loop
In the main function, the platform is initialized, the UART is configured for 115200 baud, and the buffer is zeroed. The loop toggles an LED on PB11 every 500 ms and checks if data has arrived. When the buffer holds content, it prints the received string back via printf and clears the buffer.
uint8_t rx_buffer[RX_BUFF_SIZE];
int main(void)
{
uint32_t tick = 0;
PLATFORM_Init();
UART1_InitWithBaud(115200);
memset(rx_buffer, 0, RX_BUFF_SIZE);
while (1)
{
tick++;
PLATFORM_DelayMS(1);
if (tick > 500)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11,
(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_11) ? Bit_RESET : Bit_SET));
tick = 0;
}
if (rx_buffer[0] != 0)
{
printf("%s\n\r", rx_buffer);
memset(rx_buffer, 0, RX_BUFF_SIZE);
}
}
}
The MM32F5330 provides a straightforward path for serial communication. By following the standard initializasion pattern—clock, GPIO alternate function, UART parameters, and NVIC—developers familiar with Cortex-M based MCUs can quickly bring up a functional UART echo application.