串口在嵌入式系统中是一个非常重要的外设,它通信方式简单在软件开发阶段常用作调试工具。本示例中我们只实现串口的输出功能,同时还 会实现一个具有printf功能输出接口。这样咱们以后的例程中就有了一个简单的调试工具。
1. STM32串口简介
STM32的串口功能非常丰富,它可以支持双全工异步通信、LIN、IrDA、智能卡协议、单线半双工通信、支持调制解调器操作。
接下来我们将对使用STM32的串口应该进行的哪些设置给予简单说明,并对需要设置的寄存器给予简单介绍。要使用串口除了应对串口的波特率等进行配置外还需要对串口用到I/O进行设置,下面将分步进行介绍:
1) 串口时钟使能
STM32可以对每个外设进行单独的时钟控制,因此配置串口前需要打开串口的时钟。开启时钟前查看一下具体外设在哪个总线上是非常有必要的,通过查看STM32的参考手册第一章的1.2节得知串口1和6(USART1和6)在APB2上其它串口都在APB1上。
2) 设置波特率
串口波特率的设置是在USART_BRR中配置的,寄存器描述如下:
波特率配置寄存器各位描述
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
保留
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
DIV_Mantissa DIV_Fraction
STM32的波特率发生器可以使用分数作为分频值,DIV_Fraction表示小数部分DIV_Mantissa表示整数部分。分频值USARTDIV = DIV_Mantissa + DIV_Fraction /(8 * (2 - OVER8))。波特率Baud = APBxCLK / (8 * (2 – OVER8) * USARTDIV)。其中OVER8是USART_CR1中的一位取值0或1(当OVER8为1时只有DIV_Fraction[2:0]有效,DIV_Fraction[3]应设置为0)。举例说明一下这个分频值的计算:假设OVER8为0,USART_BRR为0x1BC则Mantissa = 27,Fraction = 12。分频值为27+12/16 = 27.75。
3) 串口控制
串口控制寄存器1各位描述
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
保留
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OVER8 - UE M WAKE PCE PS PEIE TXEIE TCIE RXNEIE TCIE TE RE RWU SBK
STM32共有三个控制寄存器在这里我们仅对控制寄存器1中要用到的位给予说明。OVER8是设置过采样,1表示8倍过采样0表示16倍过采样。UE为串口使能。M为字长选择,0表示8位数据位1表示9位数据位。PCE为校验控制使能,1表示开启校验0表示无需校验。PS为校验选择,0表示偶校验1表示奇校验。PEIE为校验中断使能。TE为发送使能,1表示允许发送。
4) 数据传输
STM32的数据发送与接收都是通过寄存器USART_DR来实现的。它是一个包含发送(TDR)和接收(RDR)的32位双功能寄存器,但只有9位有效位。
5) 串口状态
串口状态寄存器各位描述
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
保留
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
保留 CTS LBD TXE TC RXNE IDLE ORE NF FE PE
在这里我们只需关注TXE、TC这两个位。串口发送数据时是先把数据放在发送缓冲区,然后再放到移位寄存器中一位一位的将数据发送出去。TXE为1时就表示发送缓冲区TDR为空,TC为1则表示传输完成即移位寄存器中也没有数据。只有当TXE为1时我们才能将下一个数据写到USART_DR中。
2.本例程所用硬件资源介绍
本示例使用了串口3(在扩展板上标有UART字样),PB10对应发送引脚,PB11对应接收引脚但本例程只使用了发送引脚。
3. 例程
代码1 初始化串口
- 代码: 全选
-
/* uart.h */
void uart_init(void)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 开启GPIO_B的时钟 */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* 开启串口3的时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* 将PB10作为复用功能中的USART3通信引脚使用 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
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_Init(USART3, &USART_InitStructure);
/* 使能串口3 */
USART_Cmd(USART3, ENABLE);
}
代码2 发送数据
- 代码: 全选
-
/* uart.c */
static void _send(const char *str, unsigned int size)
{
int pos = 0;
while(size)
{
if (str[pos] == '\0')
break;
/* 等待缓冲区空 */
while(!(USART3->SR & 0x80));
/* 发送数据 */
USART3->DR = str[pos];
pos ++;
size --;
}
}
void debug(const char* fmt,...)
{
va_list ap;
char string[65];
string[64]='\0';
va_start(ap,fmt);
vsprintf(string,fmt,ap);
va_end(ap);
_send(string,64);
}