51单片机轻松入门—基于STC15W4K系列(C语言版)
李友全 编著:http://www.51hei.com/bbs/dpj-37954-1.html
第4章 串 口 通 信 1 串口通信电路 2 串口数据发送格式 3 串口相关寄存器 4 波特率计算公式与表格 5 单片机与计算机通信的简单例子 6 数据通信中的错误校验(校验和) 7 单片机串口向计算机串口发送2进制、16进 制、数值与字符串
2 串口数据发送格式 串口数据发送格式如图4-1所示,注意这里的格式是对于单片机串口TXD引脚而言的,信 号经过SP3232或MAX232芯片后会被倒相,即+5V(逻辑1)变-9V(逻辑0,典型值是-9V, RS232标准范围:-3V~-15V),0V(逻辑0)变+9V(逻辑1,典型值是+9V,RS232标准范围:
+3V~+15V)。 图4-1 串口数据发送格式 当单片机执行一条写SBUF的指令时,就启动串行通信的发送,数据由串行发送端TXD输 出,发送时,先发送一个起始位(低电平),用来表示数据传输开始,接着将1个字节的8个位 按低位在前高位在后的顺序发送输出,第9 位通常作为奇偶校验位,最后发送停止位(高电平) 用来表示数据传送结束。这样的数据格式通常作为一个串行帧,如无奇偶校验位,即是最为常 见的N.8.1帧格式(无奇偶校验、8位数据位、1位停止位)。 接收时,只要单片机允许接收(REN=1),单片机硬件就会不断的以16倍波特率的采样速率 采样RXD引脚电压,一旦检测到RXD引脚上出现一个从“1”到“0”的负跳变(即起始 位)时,就启动接收。串行通信中,每秒钟传送二进制码的位数称为波特率,单位是 bps,即 “位/秒”,比如数据传送的波特率为9600 比特,采用N.8.1 帧格式(10 位),则每秒传送字节为9600/10=960 个,而字节中每一位传送时间即为波特率的倒 数:T = 1/9600 (S) = 104uS,根据数据传送的波特率即字节中每一位的传送时间, 我们也可通过编写程序控制普通I/O 口实现图4-1的通信时序。
图4-1数据格式进一步说明如下: l ① 起始位:发送线TXD上没有发送数据时呈高电平1状态(即5V),当需要发送一帧数 据时,首先发送一位0(低电平)信号,称起始位。 l ② 数据位:紧接起始位后是8位数据位(51单片机格式固定8位,不能修改),发送时 从数据的最低位开始,顺序发送输出)。 l ③ 奇偶校验位:紧接数据位后是1位奇偶校验位(SCON寄存器设为方式0和方式1没有 这一位),奇偶校验位无实用价值,实际运用是可靠性高的校验和、异或校验或CRC。 l ④ 停止位:在校验位后是停止位1 (高电平5V),用于表示一帧数据结束(51单片机 停止位固定1位,不能修改)。 l ⑤ 帧与帧之间间隙不固定,间隙处用空闲位1(高电平)填补。 3 串口相关寄存器 串口1控制寄存器SCON:我们把此寄存器设为“格式固定的10位串口通信,允许 接收”,固定值:0x50,几乎任何时候都不用修改用这个值,可使用定时器1或 定时器2作波特率发生器。 辅助寄存器 AUXR :使用语句AUXR &= 0xFE; 串口1选择定时器1为波特率发生 器,使用语句AUXR |= 0x01;串口1选择定时器2为波特率发生器(默认值,建 议),当然还需在程序中启动相应定时器。 电源控制寄存器PCON(复位值为0011 0000B) SMOD用于设置串口1的波特率是否加倍,其它串口波特率与此寄存器无关。 1:波特率加倍。0:波特率不加倍。 串口1数据缓冲区寄存器SBUF,复位值是xxxx xxxxB(即不确定的数据),需要发送 输出的数据放这里就能自动发送出去,串口自动接收到的数据也存放在这里 串口2控制寄存器S2CON ,我们把此寄存器设为“格式固定的10位串口通信 , 允许接收”,固定值:0x10,只能使用定时器2作波特率发生器,当然还需在程 序中启动定时器2。 串口3控制寄存器S3CON ,我们把此寄存器设为“格式固定的10位串口通信 , 允许接收”,值为0x10时使用定时器2作波特率发生器(建议),值为0x50时 使用定时器3作波特率发生器,当然还需在程序中启动相应定时器。 串口4控制寄存器S4CON ,我们把此寄存器设为“格式固定的10位串口通信 , 允许接收”,值为0x10时使用定时器2作波特率发生器(建议),值为0x50时 使用定时器4作波特率发生器,当然还需在程序中启动相应定时器。 4 波特率计算公式与表格
表4-17 常用波特率与定时器初值对应表(T1定时器8位自动重装方式) 时钟频率 | 定时器 分频模式 | 波特率(bps) | 预置初值 (SMOD=0) | 预置初值 (SMOD=1) |
11.0592 | 1T | 9600 | DCH | B8H | 57600 | FAH | F4H | 115200 | FDH | FAH |
| 12T | 9600 | FDH | FAH | 57600 | 不能实现 | FFH | 115200 | 不能实现 | 不能实现 |
|
| 22.1184 | 1T | 9600 | B8H | 70H | 57600 | F4H | E8H | 115200 | FAH | F4H |
| 12T | 9600 | FAH | F4H | 57600 | FFH | FEH | 115200 | 不能实现 | FFH |
|
|
对于表4-17中“不能实现”的波特率,一般可以通过换用16位定时器方式解决,因为16位 定时器出来的溢出信号传输速度更快,适用于波特率要求很高的场合。 注意:对于STC15系列单片机,当各个串口的波特率都相同时,各串口可以共享定时器2 作为其波特率发生器,实际使用中建议各串口都优先选择定时器T2作波特率发生器。
5 单片机与计算机通信的简单例子 例4.1 单片机向电脑发送0~255范围内不断增大的数据,使用串口1,定时器T1作波特率 发生器,波特率9600/22.1184MHz。单片机串口1接收引脚是RXD/P3.0,串口1发送引脚是 TXD/P3.1,也就是默认的程序下载引脚,程序下载完毕即可通过串口助手进行测试。 #include "STC15W4K.H" // 包含 "STC15W4K.H"寄存器定义头文件 void delay500ms(void) { // 由第一章介绍的软件计算得出 } void UART_init(void) { // 下面代码设置定时器1 TMOD = 0x20; // 0010 0000 定时器1工作于方式2(8位自动重装方式) TH1 = 0xFA; // 波特率:9600 /22.1184MHZ TL1 = 0xFA; // 波特率:9600 /22.1184MHZ TR1 = 1; // 下面代码设置定串口 AUXR = 0x00; // 很关键,使用定时器1作为波特率发生器,S1ST2=0 SCON = 0x50; // 0101 0000 SM0.SM1=01(最普遍的8位通信),REN=1 (允许接收) } void UART_send_byte(unsigned char dat) { SBUF = dat; while(!TI); TI=0; // 此句可以不要,不影响后面数据的发送,只供代码查询数据是否发送完成 } void main() { unsigned char num=0; UART_init(); while(1) { UART_send_byte(num++); delay500ms(); } } 运行结果如图所示。
例4.2 单片机接收电脑数据,加1后发回电脑,使用串口1,定时器T2作波特率发生器,波特率9600/22.1184MHz。 #include "STC15W4K.H" // 包含 "STC15W4K.H"寄存器定义头文件 unsigned char num=0; // 存放接收到的1个字节的数据 void UART_init(void) { } void main() { }
// 下面代码设置定时器2
T2H = 0xFD; // 波特率:9600 /22.1184MHZ,1T T2L = 0xC0; // 波特率:9600 /22.1184MHZ,1T AUXR = 0x15; // 0001 0101,T2R=1启动T2运行,T2x12=1,定时器2按1T计数,S1ST2=1 // 下面代码设置定串口1 SCON = 0x50; // 0101 0000 SM0.SM1=01(最普遍的8位通信),REN=1(允许接收) // 下面代码设置中断 ES = 1; // 开串口1中断 EA = 1; // 开总中断 UART_init(); while(1); void UART1(void) interrupt 4 // 串行口1中断函数 { if(TI) { } if(RI) { } }
TI = 0; RI = 0; num = SBUF; num++; SBUF = num; // 启动数据发送过程 6 数据通信中的错误校验
数据通信难免可能发生错误,为了让接收端判断数据传输过程是否发生错误,我们需要 在发送的数据中传送额外的附加数据,简单常用的附加数据是校验和。 校验和的方法就是把需要发送或接收的一组数据的所有字节进行相加,相加结果与256进行 相除,取其余数,将此余数组合成发送数据的一部分而发送出去,同样,接收数据的一方也 以相同的方式将所发送过来的数据进行相加计算,并与发送方所发过来的计算值比较,若其 值相同,则代表所发送的数据是正确的,反之则是错误的,检查错误时,接收方可能要求发 送方重新发送,以确保数据的正确性。 例如,被发送数值为 0xAB 0xCD 0xEF 0x01 0x02 0x03 ,则将它们数值相加结果是 0x026D,以十进制表示为 621,与256 相除后取余数,其值为109,再转换成16进制为 0x6D,因此发送数据时在数据的尾端再加上一个字节0x6D,因此实际发送出去的数据成为 0xAB 0xCD 0xEF 0x01 0x02 0x03 0x6D,对方收到所发送的数据后会根据以上方式再进行一 次计算,如果计算出来的结果是0x6D,表示此次发送的数据是正确的。校验和计算函数如下: unsigned char CheckSum(unsigned char *ptr, unsigned char len) { unsigned char i; unsigned char a; unsigned int Value=0; for(i=0;i<len;i++) //len结束后第一个字节为接收到的校验和 { Value = Value + ptr[ i ]; } a=Value; // 长送短,传送完整低字节 return(a); } 7 单片机串口向计算机串口发送2进制、16进制、数值与字符串 例4.15 单片机串口向计算机串口发送2进制、16进制、数值与字符串 //////////////////////////////// main.c ////////////////////////////// #include "uart_debug.h" void main() { unsigned char a=0x55; unsigned int b=0xAB98; unsigned long c=1234567890; unsigned char Buf[]="欢迎使用STC15单片机!\n"; //字符串在内存结尾必然有一个附 加字符:\0 UART_init(); // 波特率:9600 /22.1184MHZ UART_Send_Str("串口设置完毕:123ABC\n"); // 发送字符串 UART_Send_Str(Buf); UART_Send_Num(b); // 发送数值 UART_Send_StrNum("数值=:",c); // 发送字符串+数值 UART_Send_Hex(b) ; // 发送16进制 UART_Send_binary(a); // 发送2进制 while(1); }
实验结果如下图所示
本程序使用了一个程序包和程序包对应的头文件,程序移植时请将这两个 文件复制到自己的工程文件夹, 串口初始化函数UART_init(); 默认波特率: 9600 /22.1184MHZ,可调整,其余部分不要修改。 |