UART通信协议
概念
UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用于主机与辅助设备通信,如汽车音响与外接AP之间的通信,与PC机通信包括与监控调试器和其它器件,如EEPROM通信。
通信基础
并行通信
串行通信
单工通信
数据传输方向是单向。
双工通信
半双工
通信不可同时进行
全双工
波特率
波特率是衡量资料传送速率的指标。表示每秒钟传送的符号数(symbol)。一个符号代表的信息量(比特数)与符号的阶数有关。例如传输使用256阶符号,每8bit代表一个符号,资料传送速率为120字符/秒,则波特率就是120baud,比特率是120*8=960bit/s。
UART帧格式
硬件连接
控制器
一般情况下处理器都会集成UART控制器,我们使用YART进行通信时只需要对其内部相关控制器进行设置即可。
AVR单片机USART相关寄存器
I/O数据寄存器 UDR
控制和状态寄存器 UCSRA
- 位7—RXC:USART接收结束(中断标志位)
- 位6—TXC:USART发送结束
- 位5—UDRE:数据寄存器空
- 位4—FE:帧错误
- 位3—DOR:数据溢出
- 位2—PE:奇偶校验错误
- 位1—U2X:倍速发送
- 位0—MPCM:多处理器通信模式
控制和状态寄存器UCSRB
- 位7—RXCIE:接受结束中断使能,为1产生中断
- 位6—TXCIE:发送结束中断使能
- 位5—UDRIE:USART数据寄存器空 中断使能
- 位4—RXEN:接受使能,置为后将启动USART接收器
- 位3—TXEN:发送使能,置为后将启动USART发送器
- 位2—UCSZ2:字符长度
- 位1—RXB8:对于9位串行帧进行操作时,RXB8是第9个数据位。
- 位0—TXB8:对于9位串行帧进行操作时,TXB8是第9个数据位。
控制和状态寄存器UCSRC
- 位7—URSEL:寄存器选择,通过该位访问UCSRC寄存器或UBRRH寄存器。当读UCSRC时,该位为1;当写UCSRC时,该位必须写1
- 位6—UMSEL:USART模式选择,通过这一位来选择同步或异步工作模式,0为异步,1为同步
- 位5—位4 UPM:奇偶校验模式设置,00禁止,01保留,10偶校验,11奇校验
- 位3—USBS:停止位选择。0-停止位位数1;1-停止位位数2
- 位2—位1 UCSZ:字符长度设置
- 位0—UCPOL:时钟极性,仅用于同步工作模式
波特率寄存器UBRRL和UBRRH
- 位15—URSEL:寄存器选择,当读时为0,当写时为1
- 位11—0 UBRR比特率寄存器:UBRRH包含了比特率的高四位,UBRRL包含了低8位
在操作时设置最高位URSEL来区分。
- 写操作:当写入数据时,最高位为0,数据写入UBRRH;数据的最高位为1时,数据写入UCSRC 读操作
- 读操作:第一次读取的值为寄存器UBBRH的数值,如果在连续的两个时钟周期里都执行读操作,那么第二次读到的就是UCSEC的值
波特率计算公式
例子
误差计算公式
USART的使用方法
- 初始化。工作模式,帧结构等(UCSRC)
- 波特率设置(UBBRL,UBBRH)
- 中断的相关设置(UCSRB)
- 选择中断号,编写中断服务函数
初始化
//以atmega32为例
#define uchar unsigned char
#define uint unsigned int
#define fosc 7372800 //时钟频率
uchar rdata;
uchar flag;
void uart_init(uint baud)//baud为设置的比特率
{
uint a;
UCSRC=0x86;
a=fosc/16-baud-1;
UBRRL=a%256;
UBRRH=a/256;
UCSRB=0x98;
SREG|=BIT(7);
}
发送函数
void uart_send(uchar data)
{
while(!(UCSRA&BIT(UDRE))); //第五位是否为1,从而满足条件退出循环发送数据
UDR=data;
while(!(UCSRA&BIT(TXC));
UCSRA|=BIT(TXC); //写1进行清除操作
}
接受函数
#pragma interrupt_handler uartrece_isr:12 //中断号
void uartrece_isr(void)
{
UCSRB&=~BIT(7); //第七位清零
rdata=UDR;
flag=1;
UCSRB|=BIT(7); //打开中断
}
主函数
void main(void)
{
uchar i ='h';
uart_init(9600);
//uart_send(i);
while(1)
{
if(flag)
{
flag=0;
uart_send(rdata);
}
}
}
实例
// ATMEGA32
#define F_CPU 8000000UL
#define myUBRR 8 // 115200 b/s
#define myU2X 1
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
void USART_Init( unsigned int speed)//使用 UBRR 寄存器的给定值初始化 USART,该值应根据波特率 BoudRate 计算
/* 当 U2X=0
UBRR=Fosc/(16*BaudRate)-1
BaudRate=Fosc/16(UBRR+1)
当 U2X=1
UBRR=Fosc/(8*BaudRate)-1
BaudRate=Fosc/8(UBRR+1)
*/
{
UBRRH = (unsigned char)(speed>>8);
UBRRL = (unsigned char)speed;
UCSRB=(1<<RXEN)|( 1<<TXEN); //允许接收和发送USART
#if (myU2X==1) //只有当myU2X=1时才会编译以下行
UCSRA |= (1<<U2X); //传输速率加倍
#endif
UCSRC = (1<<URSEL)|(1<<USBS)|(1<<UCSZ1)|(1<<UCSZ0);// 8位传输,1个停止位
}
void USART_Transmit( unsigned char data ) //向USART通道传输一个字节
{
while ( !(UCSRA & (1<<UDRE)) ); //检查数据寄存器的释放(准备加载下一个字节)
UDR = data; //将数据加载到寄存器中进行传输
UCSRA |= (1<<TXC); // 重置传输完成位
}
int main (void)
{
char ch='O';
int i;
DDRC=0xFF;
USART_Init(myUBRR);
while (true) {
i=0;
do {
PORTC=i;// 将循环数输出到端口 C,准备在日志文件中固定新接收到的符号
if (UCSRA & (1<<RXC)){ // 控制从 USART 通道接收信息
ch=UDR;// 读取接收缓冲区以准备接收下一个字节
if (UCSRA & ((1<<FE)|(1<<DOR))){ //检查是否有接收错误
ch='.';//接收到的有错误的字节被替换为'.'
PORTC=0xFF;//便于识别日志文件中的情况
}
// 没有接收错误
PORTC=ch;//将接收到的字节输出到端口C,以便在日志文件中修复
USART_Transmit(ch);
i++;
}
}
while (i<10);
while ( !(UCSRA & (1<<TXC)) ); //检查空闲移位寄存器(传输完成)
//关闭并打开UART,
//导致需要新的接收同步(搜索起始位)
UCSRB &= ~((1<<RXEN)|( 1<<TXEN));//禁用UART发送/接收,这会重置缓冲区中累积的接收位。
PORTC=0; // 在日志文件中记录 UART 被禁用的那一刻
for (i=0;i<(3*myUBRR);i++);//持续时间等于几位传输时间的时间延迟
UCSRB|=((1<<RXEN)|( 1<<TXEN));// 启用 UART 发送/接收,开始搜索起始位
PORTC=1;// 在启用 UART 的那一刻记录在日志文件中
}
}
推荐练习
无论是汇编语言还是C语言等单片机编程,还是硬件基础知识中的常用网络通信协议,除了查看相关数据百度外,也要通过大量的练习进行掌握,我推荐可以去牛客网(点击可直达)看看,他们现在的题库内容很丰富,属于国内做的很好的了,里面的资源也是全部免费的,很好的一个平台,欢迎大家进入学习查看。