STM32串口通信原理与实现详解
1. 串口通信方式
UART:universal asynchronous receiver and transmitter
通用异步收/发器,即串行异步全双工收发器。
USART: universal synchronous asynchronous receiver and transmitter
通用同步/异步收/发器,即串行异步/同步全双工收发器
说明:
(1) 串行通信和并行通信区别:
串行通信 | 每次同时只能传输 1 个二进制位 |
---|---|
并行通信 | 数据各个位同时传输 |
(2)同步和异步的区别:
同步通信 | 发送方和接收方按照同一个时钟节拍工作,所以需要时钟信号 |
---|---|
异步通信 | 发送方和接收方没有统一的时钟节拍,各自按照自己的节拍工作 |
(3) 单工、半双工、全双工的区别:
单工 | 数据传输只支持数据在一个方向上传输 |
---|---|
半双工 | 允许数据在两个方向上传输。但是,在同一时刻,只允许数据在一个方向上传输 |
全双工 | 允许数据同时在两个方向上传输 |
2.RS232 电平和 TTL 电平(都是全双工)
电平类型 | 电压与逻辑电平关系 |
---|---|
TTL(单片机电平) | 逻辑1:2.4V ~ 5V ; 逻辑0: 0V ~ 0.5V |
RS232(如电脑) | 逻辑1:-15V ~ -3V; 逻辑0: 3V ~ 15V |
注意:单片机要和PC通信时,需要电平转换芯片如CH340。
3.波特率
3.1 波特率:等同比特率,表示每秒钟传送的二进制位数,单位是比特每秒(bit/s)。
3.2 常见波特率:9600 、19200、115200
如串口的波特率为9600,表示1秒中可传输9600个bit;
其中,传输1个bit需要104us(计算1000ms/9600 = 104us)。
4.通信协议
4.1 串口传输的数据包格式
每个数据包包含1个起始位,5至9个数据位,可选的奇偶校验位和1或1.5或2个停止位:
起始位 | 数据位 | 数据位 | 停止位 |
---|---|---|---|
1bit | 5 ~ 8bit | 0 ~ 1 bit | 1 ~ 2bit |
低电平(下降沿) | 先发送低位,再发送高位 | 奇偶校验或无校验 | 高电平 |
注意:
① 空闲状态:高电平,此时无数据传输
②奇偶校验:
奇校验:如果数据位中“1”的数目是偶数,则校验位为高电平1,
如果数据位中“1”的数目是奇数,则校验位为低电平0
偶校验:如果数据位中“1”的数目是奇数,则校验位为高电平1,
如果数据位中“1”的数目是偶数,则校验位为低电平0
③停止位配置:表示传输的结束,并且提供计算机校正时钟同步的机会。
1个停止位:停止位位数的默认值。
2个停止位:可用于常规USART模式、单线模式以及调制解调器模式。
0.5个停止位:在智能卡模式下接收数据时使用。
1.5个停止位:在智能卡模式下发送和接收数据时使用。
④编程可配置的数据长度:8位或者9位(包含校验位),如9位是指8bit数据+1bit奇偶校验。
传输示例:
波特率115200,无校验,1个停止位,数据长度为8,传输数据0x55 0xAA 0x03,示波器波形如下:
4.2 STM32串口框图
说明:
如上框图所示,在接收移位寄存器、发送移位寄存器还有一个进入的箭头,分别连接到接收器控制、发送器控制。而这两者连接的又是接收器时钟、发送器时钟。也就是说,异步通信尽管没有时钟同步信号,但是在串口内部,是提供了时钟信号来进行控制的。接收器时钟和发送器时钟又被连接到同一个控制单元,也就是说它们共用一个波特率发生器。
读操作:数据从RX进入到接收移位寄存器,后进入到接收数据寄存器,最终供CPU或者DMA来进行读取;
写操作:数据从CPU或者DMA传递过来,进入发送数据寄存器,后进入发送移位寄存器,最终通过TX发送出去。
STM32与外部设备串口通信过程:
5.STM32 串口相关寄存器
参考《STM32F10x参考手册》
6.串口操作的一般步骤
代码示例:配置串口5
6.1 GPIO时钟使能,串口时钟使能。调用函数:RCC_APB2PeriphClockCmd()(可以参阅:【STM32】 STM32端口复用和重映射(AFIO辅助功能时钟) 的部分内容);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能afio时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE); //使能串口5时钟
6.2 串口复位(这一步不是必须的)。调用函数:USART_DeInit();
USART_DeInit(UART5);
6.3 GPIO外设功能下的端口模式设置。调用函数:GPIO_Init();
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //串口5输入脚上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //串口5输出脚配成多功能上下拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //多功能推挽输出
GPIO_Init(GPIOC, &GPIO_InitStructure);
6.4 串口参数初始化。调用函数:USART_Init();
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = p_baudrate; //初始化串口参数
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_Rx | USART_Mode_Tx;
USART_Init(UART5, &USART_InitStructure); //初始化串口
6.5 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)。调用函数:NVIC_Init();USART_ITConfig();
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置 USART 为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;//
/* 抢断优先级为 1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级为 1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置 NVIC */
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);
USART_ITConfig(UART5, USART_IT_TXE, ENABLE);
6.6 使能串口。调用函数:USART_Cmd();
USART_Cmd(UART5, ENABLE);
6.7 编写中断处理函数。调用函数:USARTx_IRQHandler();
void UART5_IRQHandler(void)
{
if(USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)
{
uart5_recv_int_handle();
}
if(USART_GetITStatus(UART5, USART_IT_TXE) != RESET)
{
uart5_send_int_handle();
}
}
6.8 串口数据收发。调用函数:USART_SendData();USART_ReceiveData();
void uart5_recv_int_handle(void)
{
U8 m_data;
m_data = USART_ReceiveData(UART5);
……
//处理数据
}
void uart5_send_int_handle(void)
{
U8 m_data;
if(需要发送数据)
{
USART_SendData(UART5, m_data);
}
else
{
USART_ITConfig(UART5, USART_IT_TXE, DISABLE); //关闭发送中断
}
……
//处理数据
}
注:如有问题,欢迎大家指出,谢谢!