STM32(十三):通信协议——USART串口协议
通信接口
通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统。
通信协议:制定通信的规则,通信双方按照协议规则进行数据收发。
TX (Transmit exchange) 数据发送角 RX (Receive exchange) 数据接收角
SCL (Serial Clock) 时钟 SDA (Serial Data)数据
SCLK (Serial Clock) 时钟 MOSI (Master Output Slave Input)主机输出数据脚
MISO (Master Input Slave Output)主机输入数据角 CS (Select Chip)片选
CAN_H、CAN_L 差分数据脚 DP、DM 差分数据脚
串口通信
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。
硬件电路
简单双向串口通信有两根通信线(发送端TX和接收端RX)
TX与RX要交叉连接
当只需单向的数据传输时,可以只接一根通信线
当电平标准不一致时,需要加电平转换芯片
电平标准
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
TTL电平:+3.3V或+5V表示1,0V表示0
RS232电平:-3~-15V表示1,+3~+15V表示0
RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)串口参数及时
串口参数和时序
波特率:串口通信的速率
起始位:标志一个数据帧的开始,固定为低电平
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
校验位:用于数据验证,根据数据位计算得来
停止位:用于数据帧间隔,固定为高电平
空闲时刻是高电平,起始位是低电平
USART外设
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里。
自带波特率发生器,最高达4.5Mbits/s
可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
可选校验位(无校验/奇校验/偶校验)
支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
STM32F103C8T6 USART资源: USART1、 USART2、 USART3
USART框图:
当数据从TDR移动到移位寄存器时候,置标志位TXE(TX Empty),发送器空,检查TXE为1,就可以在TDR写入下一个数据了。 发送移位寄存器在发送器控制的控制下,向右移位,一位一位的把数据输出到TX引脚。
接收移位寄存将数据移动到RDR中后,会置RXNE(RX Not Empty)为1,检测到该位为1时,就可以把数据读走了。
nRTS(Request To Send) 请求发送,是输出脚,告诉别人能不能接收。
nCTS(Clear To Send) 清除发送,是输入脚,用于接收别人nRTS的信号。n的意思是低电平有效。
一个支持流控的串口,它的TX接到我的RX。我的RTS要输出一个能不能接收的反馈信号,接到对方的CTS,当我能接收,RTS就置低电平,请求对方发送。对方CTS接收到后就可以一直发。当我处理不过来时,RTS就会置高电平,对方CTS收到后就会暂停发送。
SCLK用于产生同步的时钟信号,配合发送移位寄存器进行输出。发送寄存器每移位一次,同步时钟电平跳变一个周期。只支持输出。用途:1. 兼容别的协议,例如串口+时钟和SPI很像。2. 自适应波特率。
唤醒单元,实现串口挂载多设备。串口点对点,只支持两个设备回想通信。多设备可以在一条总线上接多个从设备。每个设备分配一个地址,想根某个设备通信,就进行寻址,确定通信对象,再进行数据收发。唤醒单元可以实现多设备的功能。
空闲帧和断开帧是局域网的相关配置,这里先不管。
采样时钟会以波特率的16倍频率进行采样,在一位的时间里, 可以进行16次采样。最开始,空闲状态高电平,那采样就一直是1。突然采集到一个0,那么就说明在这两次采样之间,出现了下降沿,如果没有任何噪声,那之后就是起始位,会采集到16个0。但是在实际过程中,肯定会存在噪声。系统在采集到第一个0之后,然后会在地3、5、7 8、9、10个时钟,且每三位里面至少有两个0;如果有噪声,系统会置NE位,提示有噪声,使用时候请考虑。
波特率发生器
发送器和接收器的波特率由波特率寄存器BRR里的DIV确定
计算公式:波特率 = fPCLK2/1 / (16 * DIV)
使用步骤:
第一步 RCC开启USART和GPIO时钟。
第二步 GPIO初始化,把TX配置成复用输出,RX配置成输入。
第三步 配置USART,直接使用一个结构体。
接收功能还需要配置中断,在开启USART之前,再加上ITConfig和NVIC的代码就可以了。
第四步 开启USART
部分函数详解:
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);
这两个函数是用来配置同步时钟输出的,包括时钟要不要输出,时钟的极性,相位等参数。
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState); 开启USART到DMA的触发通道。
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 发送数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx); 接收数据
printf函数移植方法:
使用printf之前,打开工程选项,勾选UseMicroLIB,这个Keil为嵌入式平台优化的一个精简库。
然后堆printf进行重定向,将打印的东西输出到串口。因为printf函数默认是输出到屏幕,我们单片机没有屏幕,所以要进行重定向。
int fputc(int ch,FILE *f){
Serial_SendByte(ch);
return ch;
}
fputs是printf的底层,printf函数在打印时候就是调用fputc来一个一个打印的,把fputc函数重定向到串口,printf就输出到串口了。
如果这个改写,只有只有串口1可以用,可以采用sprintf 这样所有都能用。
char String[100];
sprintf(String,"Num=%d\r\n",666);
Serial_SendString(String);
printf(“你好世界”)编译器无法编一
在C/C++设置里加上如下指令
--no-multibyte-chars
串口接收可以使用查询和中断
查询的流程是,在主函数里不断判断RXNE标志位。 如果置1,则收到数据了。
while(1)
{
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
{
RxData =USART_ReceiveData(USART1);
OLED_ShowHexNum(1,1,RxData,2);
}
}
中断
void USART1_IRQHandler(void){
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET){
Serial_RxData =USART_ReceiveData(USART1);
Serial_RxFlag=1;
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}