本文为博主 日月同辉,与我共生,csdn原创首发。希望看完后能对你有所帮助,不足之处请指正!一起交流学习,共同进步!
> 发布人:@日月同辉,与我共生_单片机-CSDN博客
> 欢迎你为独创博主日月同辉,与我共生点赞❤❤❤+关注👍+收藏🌹+评论☺。
系列专栏: CSDN-单片机串口通信学习系列🎁
> 我的格言是:"尽最大努力,做最好的自己!💪
要转载,请提前告知!!!
版权声明:本文为CSDN博主「日月同辉,与我共生」的原创文章,CSDN独一份。
目录
一、码元、波特率
1.1码元
用相同时间间隔的符号来表示一个二进制数,一般来说,码元≠比特(1个二进制数相当于1个比特,用1bit表示),例如,有4种状态(0、1、2、3),分别用二进制数表示,即为00、01、10、11,因此1码元=2比特,特殊地,如果是2种状态(0、1),可以用0、1两个二进制数分别表示两种状态,此时1码元=1比特。在串口通信中,每次发送数据都是一个一个地发送,因此串口通信码元=比特。
1.2波特率
单位时间内发送的码元数,称为波特率,单位为b/s。
我们常用9600b/s,每发送1比特需要的时间为1s/9600=104us
二、数据帧
2.1起始位
1位,表示一次通信的开始,给接收器时钟一个同步,告知接收端开始接收数据。
2.2停止位
1位,一次通信的结束。
2.3数据-帧头
串口通信帧头(也称为帧起始标志)是用来标识一个串口通信帧的开始位置的特殊字符。它的作用是让接收端能够正确地识别数据帧的开始位置,以便接收端能够正确地解析整个数据帧。帧头通常是++一个固定的特定字符或字符组合++。
三、系统设计
3.1设计要求
++本次设计,最开始,单片机com1发送Wait for Serial Communication Tset Start.和Please Send a string of data:这两个字符串到虚拟串口com3,然后由虚拟串口com3发送数据给单片机com1,单片机接收数据后能够重新发回给com3。发送的数据由帧头+数据组成,帧头为AA 55 AA 55,数据有2种情况,当数据为01 02时,接P1^0的LED灯亮,当数据为02 01时,接P1^0的LED灯灭,当数据错误时,LED灯无反应(已近亮了无法灭,已近灭的无法亮)。另外,虚拟终端能接收到单片机发送的数据。++
3.2系统原理
串口发送数据不是一次性发送,而是一个一个字符/字节发送。波特率为9600b/s,发送一个比特需要时间要104us,定时器可以定时1ms,若是定时时间超过5ms(规定值,一般是3ms-8ms),则说明接收数据完成,因此可以定义定时计数变量recv_timer_cnt,该变量每+1,定时累加1ms,如果该变量值超过5,则定时超过5ms,则接收完成,每接收一个数据,定时计数变量recv_timer_cnt清0,接收的数据存储到数组recv_buf。
接收完成后要判断数据是否正确并对正确数据进行解析,可以设定一个帧头变量recv_move_index,判断数组recv_buf每一个数据是否正确,有错误的,跳出该判断,将帧头变量+1,然后继续重头判断,直到判断到正确的一串数据或结束,判断到正确的一串数据,就开始对数据进行解析(处理LED)。
3.3硬件设计
3.3.1串口设计
3.3.2LED电路
LED灯采用共阳极接法,左端接电源,右端先接电阻再接到P1^0。
3.4软件设计
3.4.1发送数据
cpp
void sendByte(unsigned char dat) //发送一帧数据功能函数
{
SBUF=dat;
while(!TI);
TI=0;
}
void sendString(unsigned char *dat)//发送字符串函数
{
while(*dat != '\0')
{
sendByte(*dat++);
}
}
3.4.2串口初始化
cpp
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFD; //设置定时初始值
TH1 = 0xFD; //设置定时重载值
ET1 = 0; //禁止定时器中断
ES=1; //串口中断打开
TR1 = 1; //定时器1开始计时
}
3.4.3接收中断
cpp
void ES_timers() interrupt 4 //接收中断
{
if(RI)
{
RI=0;
start_timer=1;//1.开定时器标志位置1
if(recv_cnt<MAX_REX_NUM) //在规定字符长度范围内接收数据
{
recv_buf[recv_cnt]=SBUF; //2.接收数据
recv_cnt++;
}
else
{
recv_cnt=MAX_REX_NUM;
}
recv_timer_cnt=0; //3.每接收一帧数据就计数清0
}
}
3.4.4定时器初始化
cpp
void Timer0_Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
ET0=1; //定时器0中断打开
TR0 = 1; //定时器0开始计时
}
3.4.5定时器中断模块
cpp
void T0_timer() interrupt 1 //利用1ms计数,判断是否接收完成
{
TR0=0;
if(start_timer == 1)//软件定时器打开
{
recv_timer_cnt++;//计数加1
if(recv_timer_cnt>MAX_timer_cnt) //计数值超过规定范围说明接收完成
{
recv_timer_cnt=0;//计数重新置0
recv_flag=1;//接收完成标志位置1
}
}
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TR0=1;
}
3.4.6处接收数据理模块
cpp
void uart_service(unsigned char *buf) //完成对接收正确性的判断及相应解析
{
unsigned char recv_move_index;//帧头变量的定义
if(recv_flag)//接收完成开始解析
{
start_timer=0;//接收完成要关闭软件定时器
recv_flag=0;//接收完成标志位置0,以便下次接收
sendString(buf);//发送接收缓冲区的数据
while((recv_cnt>=6)&&(recv_move_index<=recv_cnt))//数据字节大于等于6,帧头不能超过数据字节
{
if((buf[recv_move_index+0]==0xAA)&&(buf[recv_move_index+1]==0x55)&&(buf[recv_move_index+2]==0xAA)&&(buf[recv_move_index+3]==0x55))
//满足帧头 AA 55 AA 55
{
if((buf[recv_move_index+4]==0x01)&&(buf[recv_move_index+5]==0x02))
//帧头后数据为01 02时LED亮
{
LED=0;
}
if((buf[recv_move_index+4]==0x02)&&(buf[recv_move_index+5]==0x01))
//帧头后数据为02 01时LED灭
{
LED=1;
}
break;//满足帧头和数据退出while循环
}
recv_move_index++;//帧头移动1位
}
recv_cnt=0;
clr_recvbuffer(buf);//清除缓冲函数
}
}
四、结果
4.1发送两个字符串
4.2LED灯亮
4.3LED灯灭
下一文将着重++串口中断即时解析数据帧头的通信程序++ ,亲爱的读者敬请期待,下一文更精彩!!!
一日不读书,胸臆无佳想。我叫不白吃,喜欢我的,可以支持我,博主名叫 @日月同辉,与我共生