完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
IO 口模拟通信,能了解通信的本质,但是单片机程序却需要不停的检测扫描单片机 IO 口收到的数据,大量占用了单片机的运行时间。
其实我们并不是很关心通信的过程,我们只需要一个通信的结果,最终得到接收到的数据就行了。 在单片机内部有这样的硬件模块,让它自动接收和发送数据,接收或发送完了,通知我们一下就可以了。 51单片机版: 51 单片机内部就存在这样一个 UART 模块,要正确使用它,先得把对应的特殊功能寄存器配置好。 51 单片机的 UART 串口的结构由串行口控制寄存器 SCON、发送和接收电路三部分构成。 SCON寄存器 模式 1 是最常用的,就是我们前边提到的 1 位起始位,8 位数据位和 1 位停止位。模拟串口通信的时候,串口的波特率是使用定时器 T0 的中断体现出来的。在硬件串口模块中,有一个专门的波特率发生器用来控制发送和接收数据的速度。对于STC89C52 单片机来讲,这个波特率发生器只能由定时器 T1 或定时器 T2 产生,而不能由定时器 T0 产生。 如果用定时器 2,需要配置额外的寄存器,默认是使用定时器 1 的,我们本章内容主要就使用定时器 T1 作为波特率发生器来讲解。 方式 1 下的波特率发生器必须使用定时器 T1 的模式 2,也就是自动重装载模式,定时器的重载值计算公式为: TH1 = TL1 = 256 - 晶振值/12 /2/16 /波特率 和波特率有关的还有一个寄存器,是一个电源管理寄存器 PCON,他的最高位可以把波特率提高一倍,也就是如果写 PCON |= 0x80 以后,计算公式就成了: TH1 = TL1 = 256 - 晶振值/12 /16 /波特率 公式中数字的含义这里解释一下,256 是 8 位定时器的溢出值,也就是 TL1 的溢出值,晶振值在我们的开发板上就是 11059200,12 是说 1 个机器周期等于 12 个时钟周期,值得关注的是这个 16,我们来重点说明。在 IO 口模拟串口通信接收数据的时候,采集的是这一位数据的中间位置,而实际上串口模块比我们模拟的要复杂和精确一些。他采取的方式是把一位信号采集 16 次,其中第 7、8、9 次取出来,这三次中其中两次如果是高电平,那么就认定这一位数据是 1,如果两次是低电平,那么就认定这一位是 0,这样一旦受到意外干扰读错一次数据,也依然可以保证最终数据的正确性。 了解了串口采集模式,在这里要给大家留一个思考题。“晶振值/12/2/16/波特率”这个地方计算的时候,出现不能除尽,或者出现小数怎么办,允许出现多大的偏差?把这部分理解了,也就理解了我们的晶振为何使用 11.0592M 了。 串口通信的发送和接收电路在物理上有 2 个名字相同的 SBUF 寄存器,它们的地址也都是 0x99,但是一个用来做发送缓冲,一个用来做接收缓冲。意思就是说,有 2 个房间,两个房间的门牌号是一样的,其中一个只出人不进人,另外一个只进人不出人,这样的话,我们就可以实现 UART 的全双工通信,相互之间不会产生干扰。但是在逻辑上呢,我们每次只操作 SBUF,单片机会自动根据对它执行的是“读”还是“写”操作来选择是接收 SBUF 还是发送 SBUF,后边通过程序,我们就会彻底了解这个问题。 UART 串口程序 我们编写串口通信程序的基本步骤如下所示: 1)配置串口为模式 1。 2)配置定时器 T1 为模式 2,即自动重装模式。 3)根据波特率计算 TH1 和 TL1 的初值,如果有需要可以使用 PCON 进行波特率加倍。 4)打开定时器控制寄存器 TR1,让定时器跑起来。 特别注意一下,就是在使用 T1 做波特率发生器的时候,千万不要再使能 T1 的中断了 #include void ConfigUART(unsigned int baud); void main(){ ConfigUART(9600); //配置波特率为 9600 while (1){ while (!RI); //等待接收完成 RI = 0; //清零接收中断标志位 SBUF = SBUF + 1; //接收到的数据+1 后,发送回去 while (!TI); //等待发送完成 TI = 0; //清零发送中断标志位 } } /* 串口配置函数,baud-通信波特率 */ void ConfigUART(unsigned int baud){ SCON = 0x50; //配置串口为模式 1 TMOD &= 0x0F; //清零 T1 的控制位 TMOD |= 0x20; //配置 T1 为模式 2 TH1 = 256 - (11059200/12/32)/baud; //计算 T1 重载值 TL1 = TH1; //初值等于重载值 ET1 = 0; //禁止 T1 中断 TR1 = 1; //启动 T1 } 上面的程序还是用在主循环里等待接收中断标志位和发送中断标志位的方法来编写的,而实际工程开发中,当然就不能这么干了,实际使用串口的时候就用到串口中断了,来看一下用中断实现的程序。请注意一点,因为接收和发送触发的是同一个串口中断,所以在串口中断函数中就必须先判断是哪种中断,然后再作出相应的处理。如下: #include void ConfigUART(unsigned int baud); void main(){ EA = 1; //使能总中断 ConfigUART(9600); //配置波特率为 9600 while (1); } /* 串口配置函数,baud-通信波特率 */ void ConfigUART(unsigned int baud){ SCON = 0x50; //配置串口为模式 1 TMOD &= 0x0F; //清零 T1 的控制位 TMOD |= 0x20; //配置 T1 为模式 2 TH1 = 256 - (11059200/12/32)/baud; //计算 T1 重载值 TL1 = TH1; //初值等于重载值 ET1 = 0; //禁止 T1 中断 ES = 1; //使能串口中断 TR1 = 1; //启动 T1 } /* UART 中断服务函数 */ void InterruptUART() interrupt 4{ if (RI){ //接收到字节 RI = 0; //手动清零接收中断标志位 SBUF = SBUF + 1; //接收的数据+1 后发回,左边是发送 SBUF,右边是接收 SBUF } if (TI){ //字节发送完毕 TI = 0; //手动清零发送中断标志位 } } |
|
|
|
只有小组成员才能发言,加入小组>>
3309 浏览 9 评论
2988 浏览 16 评论
3490 浏览 1 评论
9050 浏览 16 评论
4085 浏览 18 评论
1171浏览 3评论
602浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
594浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2332浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1894浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-20 20:34 , Processed in 0.933493 second(s), Total 49, Slave 40 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号