通信的基本概念
同步通信
一般情况下同步通信指的是通信双方根据同步信号进行通信的方式。比如通信双方有一个共同的时钟信号,大家根据时钟信号的变化进行通信,如SPI、I2C等。
异步通信
是指数据传输速度匹配依赖于通信双方有自己独立的系统时钟,大家约定好通信的速度。异步通信不需要同步信号,但是并不是说通信的过程不同步,例如UART。
串行通信
指的是同一时刻只能收或发一个bit位信息。因此只用1根信号线即可。优点:占用引脚资源少。缺点:传输速度相对较慢。
并行通信
指的是同一时刻可以收或发多个bit位的信息,因此需要多根信号线才行。优点:速度快。缺点:占用引脚资源多。
通信模式
单工:要么收,要么发,只能做接收设备或者发送设备。比如收音机
半双工:可以收,可以发,但是不能同时收发。比如对讲机
全双工:可以在同一时刻既接收,又发送。比如手机
常见通信方式总结如下表所示:
通信标准 | 引脚说明 | 通信方式 | 通信方向 |
UART (通用异步收发器) | TXD:发送端 RXD:接收端 GND:公共地 | 异步通信 | 全双工 |
单总线 (1-wire) | DQ:发送/接收端 | 异步通信 | 半双工 |
SPI | SCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入 | 同步通信 | 全双工 |
I2C | SCL:同步时钟 SDA:数据输入/输出端 | 同步通信 | 半双工 |
USART基本概念
通用同步异步收发器(USART)灵活地与外部设备进行全双工数据通信,满足外部设备对工业标准NRZ异步串行数据格式的要求。USART通过小数波特率发生器提供了多种波特率。它支持同步单向通信和半双工单线通信;还支持LIN(局域互联网络),智能卡协议与IrDA(红外数据协会)SIR ENDEC规范,以及调制解调操作(CTS/RTS)。而且,它还支持多处理器通信。
USART持同步模式,因此USART 需要同步始终信号USART_CK(如STM32 单片机),通常情况同步信号很少使用,因此USART和一般单片机UART使用方式是一样的,都使用异步模式。
STM32F051串口
STM32F051芯片内置两个通用同步/异步收发器(USART1和USART2),通信速度可达6Mbit/s。其中USART1的发送和接收引脚分别为PA9、PA10,UART2的发送和接收引脚分别为PA14、PA15。
串口通信协议
UART串口通信协议有1个起始位、8-9位数据位、1位奇偶校验位(可选)及1-2位停止位。通信时低位先发送,一般情况下串口通信选择8N1,即8位数据位、无奇偶校验位、1位停止位。
波特率
在串行通信中,用“波特率”来描述数据的传输速率。所谓波特率,既每秒传送的二进制位数,其单位为bps(bits per second)。它是衡量串行数据速度快慢的重要指标。
国际上规定一个标准的波特率系列: 110、300、600、1200、1800、2400、4800、9600、115200、14.4Kbps、19.2Kbps、……
例如:115200bps、指每秒传送115200位。通信双方必须设置同样的同学速率才能正常通信。
USART寄存器及配置方法
控制寄存器(USART_CR1)
由于STM32F051的USART控制寄存器(USART_CR1)内容十分庞杂,所以这里仅介绍一些常用功能。
位12 M: 字长
这个位决定串口字长。 由软件置1 和清零。
0: 1 个起始位,8 位数据位
1: 1 个起始位,9 个数据位
这个位域只能在USART 未被使能的时候(UE=0)改写。
位 9 PS: 校验选择
这个位选择在校验生成和检测功能被打开的时候(PCE=1)使用奇校验还是使用偶校验。 由软件置1 和清零。 校验方式会在当前字节结束后生效。
0: 偶校验
1: 奇校验
这个位域只能在USART 未被使能的时候(UE=0)改写。
位 6 TCIE: 发送完毕中断使能
由软件置1 和清零。
0: 中断禁止
1: 在USART_ISR 寄存器中的TC 位被置1 的时候会产生USART 中断
位 5 RXNEIE: 接收寄存器非空中断使能
由软件置1 和清零。
0: 中断禁止
1: 在USART_ISR 寄存器中的ORE 或者RXNE 被置1 的时候会产生USART 中断。
位 0 UE: USART 使能
当这个位被清零,USART 的预分频器和输出都立即停止,并且当前的操作也被取消。对USART 的设置都不会丢,但USART_ISR 中所有的状态标志都会被复位。由软件置1 和清零。
0: USART 预分频器和输出关闭,低功耗模式
1: USART 开启
控制寄存器(USART_CR2)
位 13:12 STOP[1:0]: 停止位
这些位用来定制停止位的个数。
00: 1 个停止位:
01: 保留
10: 2 个停止位:
11: 1.5 个停止位
这个位域只能在USART 未被使能的时候(UE=0)改写。
波特率寄存器(USART_BRR)
位31:16 保留,必须保持复位时的值。
位 15:4 DIV_Mantissa[11:0]: USARTDIV 的整数部分
这12 位定义USART 分频器除法因子的整数部分
位 3:0 DIV_Fraction[3:0]: USARTDIV 的小数部分
这4 位定义USART 分频器除法因子的小数部分,当OVER8=1时,DIV_Fraction3 位是没有用的,并且必须保持为0。
中断和状态寄存器(USART_ISR)
位 7 TXE: 发送数据寄存器空
当USART_TDR 寄存器中的值被取到移位寄存器的同时,这个位被硬件置1。 再向USART_TDR 寄存器写数据就会同时清掉这个位。TXE 标志还可以用其它方式清除,例如向USART_RQR 寄存器的TXFRQ 位写1,为了丢弃数据(仅于智能卡模式T=0,传送失败的情形)。如果USART_CR1 寄存器中的TXEIE 位被置起时,则会产生中断。
0: 没有数据被传到移位寄存器
1: 有数据被传到移位寄存器,发送数据寄存器为空。
位 5 RXNE: 接收数据寄存器非空
当接收移位寄存器的内容被传递到USART_RDR 寄存器中时,这个位被硬件置1。读取USART_TDR 寄存器的数据就会同时清掉这个位。 RXNE 标志也可以通过对USART_RQR 寄存器中的RXFRQ 位写1 来清除。如果USART_CR1 寄存器中的RXNEIE 位是1,就会产生中断请求。
0: 没收到数据
1: 收到的数据已经可读。
数据发送寄存器(USART_TDR)
位31:9 保留,必须保持复位时的值。
位 8:0 TDR[8:0]: 发送数据的值
用于写入要发送的数据字节。TDR 寄存器提供发送移位寄存器和内部总线间的并行接口(见图226)。当发送的时候设置了校验功能(USART_CR1 中的PCE=1),向最高位(位7 还是
位8 取决于设置的字长)写入的信息是无效的,因为它总是要被校验位代替了之后再去发送的。
注: 这个寄存器只能在TXE=1 的时候才能做写操作。
数据接收寄存器(USART_RDR)
位31:9 保留,必须保持复位时的值。
位 8:0 RDR[8:0]: 接收数据的值
包含所收到的字节。
RDR 寄存器提供输入移位寄存器和内部总线间的并行接口(见图226)。
当接收数据时打开了校验位,读这个寄存器得到的最高位是校验位。
STM32实现串口数据的收发
直接操作寄存器进行数据收发
根据硬件原理图,在CubeMX上配置好串口UART1为异步模式,并将波特率设置为115200。
生成工程后,通过直接操作寄存器控制UART进行数据收发的代码如下:
void Uart_Putchar(uint8_t ch) // 接收1个字节的数据
{
while(!(USART1->ISR&(1<<7))); // 等待TXE置位
USART1->TDR = ch;
}
uint8_t Uart_Getchar(void) // 发送1个字节的数据
{
while(!(USART1->ISR&(1<<5))); // 等待RXNE置位
return USART1->RDR;
}
应用HAL库函数进行数据收发
在ST公司提供的HAL库代码中,已经集成了UART串口收发的库函数,我们可以直接调用相应的函数操作串口,代码如下:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); // 串口发送数据
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); // 串口接收数据
其中huart是代表当前需要收发数据的串口的结构体指针,pData为即将接收/发送的数据首地址,Size为发送/接收的字节数,Timeout为最大阻塞时间(即阻塞Timeout毫秒后如果仍未响应则放弃本次操作,并继续执行后续代码)。代码示例如下:
uint8_t buf[32] = {0};
HAL_UART_Transmit(huart1, buf, sizeof(buf), 100); // 串口发送数据
HAL_UART_Receive(huart1, buf, sizeof(buf), 100); // 串口接收数据
应用C标准库的printf函数打印调试信息
printf函数调用的是c库中的fputc函数。因此我们如果重新写了fputc函数,就可以改变printf函数的功能,可以向串口打印输出。代码如下:
int fputc(int ch,FILE *f)
{
while((USART1->ISR&(1<<7)) == 0);
USART1->TDR=(uint8_t)ch;
return ch;
}
这样操作下来,就能通过printf函数向串口打印调试信息了。
注:本文仅由作者用于总结和记录学习ARM系列软件和Linux驱动开发的过程,未经允许请勿擅自转载,如有侵权请联系作者删除。