串口是串型通信接口的简称,也叫串行通信接口或者串行通讯接口,是设备间接线通信的一种方式。
特点:
(1)串行接口是指数据一位位地顺序传送,其特点是线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传输速度较慢。
(2)双向通信,全双工。
串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。RS-232-C、RS-422与RS-485 标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。
也称标准 串口 ,最常用的一种 [ 串行通讯接口 , 比如我们的电脑主机的 9 针串口 ,最高速率为 20kb/s RS-232是为 点对点 (即只用一对收、发设备)通讯而设计的,其传送距离最大为约 15 米。所以 RS-232 适合本地设备之间的通信。
由于接收器采用高输入阻抗和发送 驱动器 比 RS232 更强的 驱动能力 ,故允许在相同传输线上连接多个接 收 节点 ,最多可接 10 个节点。即一个主设备( Master ),其余为从设备( Slave ),从设备之间不能通信,所以RS-422 支持一点对多点的双向通信。RS-422的最大传输距离为1219 米,最大传输速率为 10Mb/s 。平衡双绞线的长度与传输速率成反比
是从RS-422基础上发展而来的,无论四线还是二线连接方式总线上可多接到32个设备。
我们经常听说的UART 是指异步串行(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。 UART包含TTL电平的串口和RS232电平的串口
(1)RS232电平
逻辑1 为 -3~-15V 的电压 , 逻辑 0 为 3~15V 的电压
(2)TTL
TTL是 Transistor-Transistor Logic ,即晶体管 - 晶体管逻辑的简称,它是计算机处理器控制的设备内部各部分之间通信的标准技术。TTL 电平信号应用广泛,是因为其数据表示采用二进制规定, +5V等价于逻辑 ”1” , 0V 等价于逻辑 ”0” 。数字电路中,由TTL 电子元器件组成电路的电平是个电压范围,规定:输出高电平>=2.4V ,输出低电平 <=0.4V ;输入高电平>=2.0V ,输入低电平 <=0.8V笔记本电脑通过TTL 电平与单片机通信TX发送线(端口) 3.1RX接收线 ( 端口) 3.0USB转 TTL ,使用 ch340 通信
串口收发数据的时序电路图
在串口中,输入/ 输出数据缓冲器都叫做 SBUF , 都用 99H 地址码,但是是两个独立的 8 位寄存器代码体现为: 想要接收数据 char data = SBUF 想要发送数据 SBUF = data
回忆UART 是异步串行接口,通信双方使用时钟不同,因为双方硬件配置不同,但是需要约定通信速度,叫做波特率。直接写代码先玩一下再学相关的寄存器,我们先用软件生成串口初始化的代
(1) 通过串口发送一个字符给电脑
#include "reg52.h" #include "intrins.h" sfr AUXR = 0x8e; void UartInit(void) //[email protected] { PCON &= 0x7F; SCON &= 0x0F;//设置串行工作方式为8位波特率可变,REN允许串口收数据 SCON |= 0x50; TMOD &= 0x0F;//设置定时器1的工作方式为8位自动重装载定时器 TMOD |= 0x20; TL1 = 0xFD;//设置定时器初值 TH1 = 0xFD; TR1 = 1; //启动定时器1 } void Delay10ms() //@11.0592MHz { unsigned char i, j; i = 18; j = 235; do { while (--j); } while (--i); } void sentByte(char data_msg) { SBUF = data_msg; //Delay10ms(); while(!TI); TI = 0; } void main() { char data_msg = 'a'; //配置C51的串口通信方式 UartInit(); while(1){ // 往发送缓冲区写入数据,就完成发送数据 Delay1000ms(); sentByte(data_msg); } }
下面我们来了解一下相关寄存器(这些寄存器在芯片手册中都可以找到,下面只是截取了一部分)
通过上面软件给我们初始化的串口来看,配置了寄存器SCON寄存器,我们一般需要设置SM1、SM2、REN、以及TI、RI
PCON,我们主要关心SMOD和SOMD0,一般情况下SMOD=0;SOMD0=0;
当然还有与中断相关的寄存器,这在我们以后的代码中会用到。
(2) 发送一个字符串给电脑
#include "reg52.h" #include "intrins.h" #include
sfr AUXR = 0x8e; void UartInit(void) //[email protected] { PCON &= 0x7F; SCON &= 0x0F;//设置串行工作方式为8位波特率可变,REN不允许串口收数据 SCON |= 0x40; TMOD &= 0x0F;//设置定时器1的工作方式为8位自动重装载定时器 TMOD |= 0x20; TL1 = 0xFD;//设置定时器初值 TH1 = 0xFD; TR1 = 1; //启动定时器1 ES = 1;//串口中断打开 EA = 1;//总中断打开 } void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); } void sentByte(char data_msg) { SBUF = data_msg; //Delay10ms(); while(!TI); TI = 0; } void sentString(char *str) { while(*str){ sentByte(*str); str++; } } void main() { //配置C51的串口通信方式 UartInit(); while(1){ // 往发送缓冲区写入数据,就完成发送数据 Delay1000ms(); sentString("Hello world!\r\n"); } } 这里我们在发送字符串时,若是sentByte(char data_msg)函数中没有延迟或者while条件判断则会出现错误,因为不断向SBUF中发送数据,但是电脑还没有来的及从接收缓冲区中取回,就发现后面又送来数据了,会把之前缓冲区中的数据给淹没,所以我们要设置一个延时。
更好的解决方法是开启串口中断,判断TI(串行发送第8位数据结束时,硬件自动置一,软件清零)是否为一,是则发送下一个并清零。
(3) 每隔一秒,单片机向PC发送一个字符串 ,PC上位机串口调试助手发送字母o点亮LED,发送字母c关闭LED
#include "reg52.h" #include "intrins.h" sfr AUXR = 0x8e; sbit led5 = P3^7; void UartInit(void) //[email protected] { PCON &= 0x7F; SCON &= 0x0F;//设置串行工作方式为8位波特率可变,REN允许串口收数据 SCON |= 0x50; TMOD &= 0x0F;//设置定时器1的工作方式为8位自动重装载定时器 TMOD |= 0x20; TL1 = 0xFD;//设置定时器初值 TH1 = 0xFD; TR1 = 1; //启动定时器1 } void Delay10ms() //@11.0592MHz { unsigned char i, j; i = 18; j = 235; do { while (--j); } while (--i); } void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); } void sentByte(char data_msg) { SBUF = data_msg; //Delay10ms(); while(!TI); TI = 0; } void sentString(char *str) { while(*str){ sentByte(*str); str++; } } void main() { char cmd; //配置C51的串口通信方式 UartInit(); while(1){ // 往发送缓冲区写入数据,就完成发送数据 Delay1000ms(); sentString("Hello world!\r\n"); //怎么知道收到数据,查询RI的值,如果RI是1(收到数据后由硬件置1) if( RI == 1){ RI = 0; cmd = SBUF; if( cmd == 'o'){ led5 = 0; } else{ led5 = 1; } } }
这里我们必须开启接收使能,在SCON寄存器中配置即可。
同过判断RI是否等于1来判断第八个bit接收是否结束,结束后清零,然后跟据SBUF中的数据来决定是否点灯。
我们试试发送一个字符串来控制灯的关闭,因此我们需要设置一个数组了,当输入open,打开,close关闭灯
#include "reg52.h" #include "intrins.h" #include
#define Size 12 sfr AUXR = 0x8e; sbit led5 = P3^7; sbit led6 = P3^6; char cmd[Size]; void UartInit(void) //[email protected] { PCON &= 0x7F; SCON &= 0x0F;//设置串行工作方式为8位波特率可变,REN允许串口收数据 SCON |= 0x50; TMOD &= 0x0F;//设置定时器1的工作方式为8位自动重装载定时器 TMOD |= 0x20; TL1 = 0xFD;//设置定时器初值 TH1 = 0xFD; TR1 = 1; //启动定时器1 ES = 1;//串口中断打开 EA = 1;//总中断打开 } void Delay10ms() //@11.0592MHz { unsigned char i, j; i = 18; j = 235; do { while (--j); } while (--i); } void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); } void sentByte(char data_msg) { SBUF = data_msg; //Delay10ms(); while(!TI); TI = 0; } void sentString(char *str) { while(*str){ sentByte(*str); str++; } } void main() { led6 = 1; //配置C51的串口通信方式 UartInit(); while(1){ // 往发送缓冲区写入数据,就完成发送数据 Delay1000ms(); sentString("Hello world!\r\n"); } } void Uart_Handler() interrupt 4 { static int i=0; //怎么知道收到数据,查询RI的值,如果RI是1(收到数据后由硬件置1) if( RI == 1){ RI = 0; cmd[i++] = SBUF; if( i>=Size ){ i = 0; led6 = !led6; } if( strstr(cmd ,"en")){ led5 = 0; i = 0; memset(cmd,'\0',Size); } if( strstr(cmd ,"se")){ led5 = 1; i = 0; memset(cmd,'\0',Size); } } } 这里我们使用了串口的中断函数,当发送完第8位或接收完第8位都会发生中断,我们只判断是否接收完第八位,并将接收到的字符存入数组中,数组最大为12。由于我们收到的字符存放在数组中的位置有很多可能,所以我们只简单判断PC端输入的字符中是否含有en(open)或se(close)字串,用到了strstr()函数,烧入代码后会发现还存在一些问题,比如在PC端通过串口调试助手发送的字符过多的话灯不受控制,这里我们当i超过12时就清零并将led6点亮作为提示。
串口就先到这里,以后学习一些串口的应用使我们能更好理解和掌握。