完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
写在前面 这篇博客主要记录下单片机是如何通过TXD、RXD与上位机进行数据交换的。 先介绍下51单片机中与串口通信有关的各种寄存器。 首先,上位机如果要发送数据给单片机,单片机接收到数据之后,会存入到SBUF这个发送/接收寄存器,这个寄存器非常特殊,兼具发送和接收时存放数据的功能。如果是data = SBUF,则会把SBUF接收到上位机发送过来的数据存入到data中;如果是SBUF = data,则会把单片机想要发送的数据即data中的数据送入到SBUF中,然后再通过串口发送到上位机。 在接收数据时,单片机会产生中断,不然单片机不知道什么时候接收完一位数据,这个中断叫做串口中断,服务程序是interrupt4,标志位是RI,所以进入串口中断服务程序时一定要记得把RI清零,不然程序就会一直进入串口中断服务程序。控制串口中断的寄存器叫SCON,它的每一位如下: SM0、SM1这两位与TMOD中控制定时器0、1的M0、M1类似,SM0和SM1是用来控制串口工作方式的。通过改变SM0、SM1的值可以让串行口工作在4种方式: [tr]SM0SM1波特率[/tr]
上式中出现到SMOD1,这一位是由电源寄存器PCON的第七位来控制的,假设我们规定好串行口工作在方式1或3的一个初值,原本波特率为9600,SMOD置1后,波特率翻倍,会变为19200,,很好理解。 注意注意:这里的串行口工作方式0123跟定时器工作方式0123不是同一个东西,一定要分开。 串行口工作方式为0123任意一种都能通过使用定时器1工作在方式2来产生相应的波特率。 这里给出常用波特率相对应的定时器初值: 举例假如我要让串口产生9600的波特率,我使用串口工作方式1、3,定时器1工作在方式2,那么公式就等于 波特率(9600)=2^0/32 * fosc/12 * 1/(2^k-初值) ,这里我们晶振频率为11.0592MHz,波特率为9600Hz,注意单位转换,K是计数器的计数位数,定时器方式2为八位自动重装,所以K=8,那么等式就变为了 9600 = 1/32 * 11059200/12 * 1/(256-初值) ,化简一下 1/3 = 1/256-初值,那么初值就要为253,十六进制则是0xFD,则定时器每次放进TL1的初值就要为0xFD,这样就能产生9600的波特率。 再解释一下为什么定时器中的高八位和低八位相同,以定时器1举例,如果定时器1工作在方式2即八位自动重装模式,会用低八位TL1来计数,用高八位来保存计数初值。TL1计数回到0时自动将TH1中的初值送回TL1中,完成自动重装。 回到正题,SCON中REN这一位为允许接收控制位,置0则禁止串口接收数据,置1则反之。TB8和RB8是用于方式2和3中发送和接收数据的第9位,我这里不在过多解释,需要用的时候,再仔细百度。TI是发送中断标志位,发送完毕会自动置1,在发送数据前一定要先清零TI,发送完后可根据TI来判断是否发送完毕。RI则是接收中断标志位,可以根据RI的值来判断单片机是否接受完上位机发送来的数据。 总结一下,串口发送和接收涉及到的寄存器相应的位有:PCON中的SMOD,SCON中的SM0、SM1、REN、TI、RI,TH0、TL0(TH1、TL1),TMOD中的M1、M0(控制定时器的工作方式),IE中的EA、ES(允许总中断、允许串口中断),TCON中的TR0(TR1)。 单片机接收上位机数据工作过程大致为:定时器产生一定波特率——单片机与上位机通过TXD、RXD开始通信——单片机允许串口中断,允许接收数据——单片机接收到数据,进入串口中断服务程序,并将RI置1,软件将RI清零,读取SBUF。 单片机发送数据到上位机工作过程大致为:定时器产生一定波特率——单片机与上位机通过TXD、RXD开始通信——单片机赋值给TI——单片机发送数据给上位机——上位机接收到数据。 下面这两段程序是在郭天祥《新概念51单片机C语言教程》以及参考其他同学的博客写的。 郭天祥: #include typedef unsigned char uint8; typedef unsigned int uint16; uint8 flag,a,i; uint8 code table[]="I get "; void init(){ TMOD = 0x20; //定时器1工作在方式2,八位自动重装 TH1 = 0xfd; TL1 = 0xfd; TR1 = 1; //开启定时器1 SM0 = 0; SM1 = 1; //串口工作方式1 REN = 1; //接收允许 EA = 1; //开总中断 ES = 1; //开串口中断 } void main(){ init(); while(1){ if(flag){ ES = 0; //暂时关闭串口中断,防止在处理数据时再次发生串口中断 for(i=0;i<6;i++){ SBUF=table; //将I get放入发送寄存器 while(!TI); //检测是否发送完毕,发送完毕后自动置1 TI=0; //将发送完毕的标志位清零 } SBUF=a; //将接受到的值发回给主机 while(!TI); TI=0; ES=1; //重新打开串口中断 flag=0; } } } void ser()interrupt 4{ //串口中断服务程序 RI = 0; //中断标志位 a = SBUF; //将接收到的数据存入a中 flag=1; } 结合按键,按一下发送一行字符: #include typedef unsigned char uint8; typedef unsigned int uint16; #define key_state0 0 #define key_state1 1 #define key_state2 2 ***it key = P3^2; uint8 key_value; bit flag; uint8 Buf[]="hello world!n"; void delay(uint16 n) { while (n--); } /*波特率为9600*/ void UART_init(void) { SCON = 0x50; //串口方式1 TMOD = 0x21; //定时器1使用方式2自动重载,定时器0用作按键扫描 TH1 = 0xFD; //9600波特率对应的预设数,定时器方式2下,TH1=TL1 TL1 = 0xFD; TH0 = 0x4C; //50ms TL0 = 0x00; TR1 = 1; //开启定时器,开始产生波特率 TR0 = 1; ET0 = 1; EA = 1; } /*发送一个字符*/ void UART_send_byte(uint8 dat) { SBUF = dat; //把数据放到SBUF中 while (TI == 0); //未发送完毕就等待 TI = 0; //发送完毕后,要把TI重新置0 } /*发送一个字符串*/ void UART_send_string(uint8 *buf) { while (*buf != ' |