【通用异步收发器(UART)】
CW32L083 内部集成 6 个通用异步收发器 (UART),支持异步全双工、同步半双工和单线半双工模式,支持硬件数据流控和多机通信;可编程数据帧结构,可以通过小数波特率发生器提供宽范围的波特率选择。
UART 控制器工作在双时钟域下,允许在深度休眠模式下进行数据的接收,接收完成中断可以唤醒 MCU 回到运行模式。
【主要特性】
● 支持双时钟域驱动:
‒ 配置时钟 PCLK
‒ 传输时钟 UCLK
● 可编程数据帧结构:
‒ 数据字长:8、9 位,LSB 在前
‒ 校验位:无校验、奇校验、偶校验
‒ 停止位长度:1、1.5、2 位
● 16 位整数、4 位小数波特率发生器
● 支持异步全双工、同步半双工、单线半双工
● 支持硬件流控 RTS、CTS
● 支持直接内存访问 (DMA)
● 支持多机通信,自动地址识别
● 6 个带中断标志的中断源
● 错误检测:奇偶校验错误、帧结构错误
● 低功耗模式下收发数据,中断唤醒 MCU
【功能框图】
UART 控制器挂载到 APB 总线上,配置时钟域 PCLK,固定为 APB 总线时钟 PCLK,用于寄存器配置逻辑工作;传输时钟域 UCLK,用于数据收发逻辑工作,其来源可选择 PCLK 时钟、外部低速时钟(LSE)以及内部低速时钟(LSI)。双时钟域的设计更便于波特率的设置,支持从深度休眠模式下唤醒控制器。
UART 控制器的功能框图如下图所示:
【GPIO配置】
本次串口配置使用UART1。CW32L083使用端口,必须开启复用功能。在数据手册上可以查看复用表如下:
可以通过命令开启:
PA08_AFx_UART1TXD();
PA09_AFx_UART1RXD();
【时钟管理】
本次使用默认的时钟频率为8MHz
【代码编写】
1、新建usart.c/h文件,并把他加入工程HW分组之中。
2、编写串口Init函数如下:
#define UART1_UclkFreq 8000000 void UART1_Init(uint32_t BaudRate) { GPIO_InitTypeDef GPIO_InitStructure = {0}; UART_InitTypeDef UART_InitStruct = {0}; //配置RCC //开启GPIOA时钟 //开启UART1时钟 __RCC_GPIOA_CLK_ENABLE(); __RCC_UART1_CLK_ENABLE(); // 先设置UART TX RX 复用,后设置GPIO的属性,避免口线上出现毛刺 PA08_AFx_UART1TXD(); PA09_AFx_UART1RXD(); //GPIO配置 GPIO_InitStructure.Pins = GPIO_PIN_8; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_Init(CW_GPIOA, &GPIO_InitStructure); GPIO_InitStructure.Pins = GPIO_PIN_9; GPIO_InitStructure.Mode = GPIO_MODE_INPUT; GPIO_Init(CW_GPIOA, &GPIO_InitStructure); //配置UART UART_InitStruct.UART_BaudRate = BaudRate; UART_InitStruct.UART_Over = UART_Over_16; UART_InitStruct.UART_Source = UART_Source_PCLK; UART_InitStruct.UART_UclkFreq = UART1_UclkFreq; UART_InitStruct.UART_StartBit = UART_StartBit_FE; UART_InitStruct.UART_StopBits = UART_StopBits_1; UART_InitStruct.UART_Parity = UART_Parity_No ; UART_InitStruct.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; UART_InitStruct.UART_HardwareFlowControl = UART_HardwareFlowControl_None; UART_Init(CW_UART1, &UART_InitStruct); }
3、为了使用printf打印,根据我的帖子【STM32H5系列】串口printf之三-电子产品世界论坛 (eepw.com.cn)
稍作修改,可以快速移植好,代码如下:
uint8_t CW_U1_printf(char *format,...){ uint16_t timeout = 0xffff; int i; uint8_t send_str[128] = {0}; va_list listdata; va_start(listdata,format); vsprintf((char *)send_str,format,listdata); va_end(listdata); for(i = 0; i<strlen((const char*)send_str); i++) { CW_UART1->TDR = send_str[i]; while (((REGBITS_GET(CW_UART1->ISR, UART_FLAG_TXE)) == RESET) && timeout--) //等待发送缓冲区为空 { } if (timeout == 0) { return 1; } } timeout = 0xffff; while (((REGBITS_GET(CW_UART1->ISR, UART_FLAG_TXBUSY)) == SET) && timeout--); //等待发送为空 if (timeout == 0) { return 1; } return 0; }
【测试】
在main.c中添加代码,首先初始化串口1的波特率为115200,打印系统时钟频率,代码如下:
#include "main.h" #include "usart.h" LED_InitTypeDef led; int32_t main(void) { int i; led.LED_Port = CW_GPIOC; led.LED_Pin = GPIO_PIN_3; LED_Init(&led); UART1_Init(115200); CW_U1_printf("hello world\r\n"); CW_U1_printf("%d,%c,%x\n",0x30,0x30,0x30); CW_U1_printf("%s\n%s\n",__FILE__,__TIME__); CW_U1_printf("HCLK(Hz):%d\r\n",RCC_Sysctrl_GetHClkFreq()); CW_U1_printf("PCLK(Hz):%d\r\n",RCC_Sysctrl_GetPClkFreq()); while(1) { led.LED_Toggle(&led); FirmwareDelay(100000); } }
【实验效果】
【总结】
串口为最常见的外设之一,官方的数据手册提供了详细的配置说明,在示例中也给出了非常多的例程。作为初始配置还是容易入门的。
但是,由于本次的频率为8MHz,如果需要提高主频频率,还需要对RCC进行详细的学习。在摸透系统时钟树后,再进行深入的DMA、中断等配置。
附工程源码:UART_DMA.zip