51单片机串口通信
串口发送功能的实现:
一、配置SCON串行口控制寄存器
1.REN:允许/禁止串口接收控制位
2.SM0=0、SM1=1:选择通讯方式一
3.TI=0、RI=0:发送接收中断标志位,每次发送后必须手动清零
二、配置PCON电源控制寄存器
SMOD:波特率选择位,SMOD=1使方式一二三波特率加倍,默认为0
(波特率越低 = 越稳定、越抗干扰、越不容易出错,但速度越慢)
三、配置定时器
串口发送时中断不需要配置,只需要配置定时器1并选择波特率
串口需要八位自动重装,不再是十六位(65535),因为十六位要每次赋初值会占用时间导致误差,串口通信要双八位更精准,计数值变为0-255(256次计数,255后还要加一位才溢出)
1.在这里我们先对特殊功能寄存器TMOD高四位清零,低四位置默认:TMOD&=0x0F
再设置高四位GATE=0;C/~T=0;M1、M0=1,0(八位重装模式):TMOD|=0x20
2.然后再设置定时器初值和是否倍速波特率,这里选倍速也就是PCON |= 0x80,然后我们这里是八位自动重装模式,不需要每次溢出重新赋初值,只需要把高低位都设置成我们的初值就好:TL1=0xF4;TH1=0xF4;
波特率计算:我们这里初值为0xF4,换算为十进制就是244,256-244=12,也就是每次溢出经过12次计数,12个1.085us,每次溢出时间=12*1.085069444=13.020833328
溢出率=1/每次溢出时间=0.07680000003145728MHZ
注意,我们这里是波特率倍速,所以不用除2,直接除16就好
波特率=溢出率/16=4800.00000196608HZ≈4800HZ,是不是几乎没有误差?如果是12MHZ晶振就会算出是4807.69MHZ,而且波特率越高误差就会越大
3.ET1=0;禁止定时器中断,这里不需要
4.TR1=1:启动定时器1
void Uart1_Init(void) //4800bps@11.0592MHz { PCON |= 0x80; //使能波特率倍速位SMOD SCON = 0x50; //8位数据,可变波特率 AUXR &= 0xBF; //定时器时钟12T模式 AUXR &= 0xFE; //串口1选择定时器1为波特率发生器 TMOD &= 0x0F; //设置定时器模式 TMOD |= 0x20; //设置定时器模式 TL1 = 0xF4; //设置定时初始值 TH1 = 0xF4; //设置定时重载值 ET1 = 0; //禁止定时器中断 TR1 = 1; //定时器1开始计时 }当然也可以使用STC-ISP的波特率计算器自动生成初始化代码,连配置都省了:
四、串口发送函数
我们直接将数据写入SBUF即可,在写入后加上whlie判断检测发送是否完成,防止一直发送卡住,在发送后手动将发送标志位TI清零
//串口发送一个字节数据范围0-255 void UART_SendByte(unsigned char Byte) { SBUF=Byte; //写入SBUF实现数据发送 while(TI==0); //发送结束检测,防卡死 TI=0; //发送检测位清零 }五、主函数调用
当我们需要连续发送信息时,在while循环中调用的时候要每次发送后延时,否则会由于发送过快发送错误信息,比如从66变为99,要想控制信息发送速度也可以通过延时函数粗略控制
void main() { Uartl Init (); //初始化 while(1) { UART_SendByte (0x66); //串口发送函数 Delay(100); //延时防止连续发送过快产生发送乱码 } }程序编译烧录后打开串口,选择HEX模式,可以看到
如果我们选择文本模式,那么显示的就会是数字编码后,也就是字符原本的样子
六、串口发送模块化
串口发送可以模块化为.c.h文件方便以后调用(记得养成注释的好习惯),但是接下来讲的接收中断依赖于串口中断,和主函数耦合性较强,就没法模块化了
串口接收功能的实现:
一、串口初始化
其他都和发送一样,只需要改动几个点:
1.确保串口接收使能位为打开状态:SCON的第四位置1,也就是让SCON=0x50
(其实我们在发送时也是打开的,这里就不用改了)
2.打开串口中断:EA=1(启动中断总开关),ES=1(打开串口中断)
void Uart1_Init(void) //4800bps@11.0592MHz { PCON |= 0x80; //使能波特率倍速位SMOD SCON = 0x50; //8位数据,可变波特率 AUXR &= 0xBF; //定时器时钟12T模式 AUXR &= 0xFE; //串口1选择定时器1为波特率发生器 TMOD &= 0x0F; //设置定时器模式 TMOD |= 0x20; //设置定时器模式 TL1 = 0xF4; //设置定时初始值 TH1 = 0xF4; //设置定时重载值 ET1 = 0; //禁止定时器中断 TR1 = 1; //定时器1开始计时 EA=1; ES=1; //接收数据需要开启中断 }二、中断函数
我们在技术手册查询到中断号小尾巴为interrupt 4,那中断函数我们命名为void UART_Routine()interrupt 4(函数名可以随便起,只要带上空格+中断号小尾巴就是中断函数啦),主函数空循环即可,这样我们就实现了电脑命令控制LED了
void UART_Routine() interrupt 4 //中断函数头 { if(RI==1) //发送完成进入后续 { P2=SBUF; //使用串口写入SBUF的数据控制LED(发送需要手动写入SBUF,接收时电脑会直接写入,不需要我们操作) UART_SendByte(SBUF); //在中断里将串口收到的数据发出 RI=0; //将接收检测位手动置零(发送位我们在函数中置零就好啦) } }