1.认识4G模块
1、接线与插卡:EC03-DNC4G通信模块,生产的公司是EBYTE(亿佰特)。官网上有软件和用户手册下载地址。
- 保证插卡不插错,一是使用SIM卡卡套,二是方向保证正确。如图位置是SIM卡状态灯,只有插对位置,在4G模块上电几秒钟完成初始化后这盏灯才会亮。
- 一会儿进入测试之前需要把电源、天线、SIM卡、串口线等硬件接好。
- 注:一定要有micro型号的卡套,一会是用手机提供热点给电脑连接,所以最好有双sim卡。
2、翻阅用户手册:
- 供电电压:
- 功能特点:
- Socket其实就是在调用TCP,我们使用TCP其实走的就是Socket。
- 4G模块还支持MQTT协议。
- 硬件参数:波特率默认是115200bps。
- 工作情况:
- AT指令集:省略“AT+”
-
REBT 重启模块 REGINFO 设置/查询自定义注册包信息(ASCII) UARTTS 设置/查询串口打包长度、打包间隔 VER 查询版本号 REGINFONEW 设置/查询自定义注册包信息(16 进制) MODBUS 设置/查询odbusTCP/RTU 转换功能 INFO 查询设备信息 HEARTMOD 设置/查询心跳包模式 MTCPID 设置/查询odbusTCP 事件标识符 EXAT 退出AT 指令模式 HEARTINFO 设置/查询自定义心跳包信息(ASCII) NETHEAD 设置/查询网络T指令头 RESTORE 恢复出厂设置 HEARTINFONEW 设置/查询自定义心跳包信息(16 进制) MQTTMODE 设置/查询QTT模式 UART 设置/查询串口参数 HEARTM 设置/查询心跳包时间 MQTT_ADDRESS 设置/查询物联网平台地址、端口 UARTCLR 设置/查询模块连接前是否清除串口缓存 SHORTM 设置/查询短连接时间 MQTT_CONNECT 设置/查询接入物联网平台的参数 IMEI 查询模块IMEI CREG 查询是否注册到网络 MQTT_SUBSCRIBE_TOPIC 设置/查询订阅消息的topic、消息等级 LINKSTA 查询SOCK 连接状态 CSQ 查询信号强度 MQTT_PUBLISH_TOPIC 设置/查询发布消息的topic、消息等级 LINKSTA1 查询SOCK1 连接状态 CPIN 查询IM 卡状态 MQTT_ALIAUTH 设置/查询阿里云三要素 SOCK 设置/查询SOCK 参数 POTOCOL 查询设置是否开启协议传输 APN 查询设置APN信息 SOCK1 设置/查询SOCK1 参数 UARTEX 设置/查询串口参数9600,8,N,1) APN_ENABLE 查询设置APN使能 REGMOD 设置/查询注册包模式 ICCID 查询SIM卡的ICCID 号(集成电路卡识别码) - 注:如果AT指令后面有“=”,那么“=”后面就是要配置的参数
2.4G模块AT指令测试
0、指令格式和指令错误码:
- AT指令的格式如上,其中CMD就是我们的指令字符;[op]就是等号,用来配置参数。
- <CR><LF>,其中CR(carriage return)表示回车,LF(line feed)表示换行。
- 无效的AT指令有5种。
- 成功的AT指令发送后,4G模块一般会显示"+OK",如果4G模块对AT指令的响应信息中有“=”,那么“=”后面的就是查询到的信息。
1、进入AT指令模式:不同于ESP8266模块一上电就进入AT指令模式,4G模块上电后默认是处于透传工作模式(默认是没有进入AT指令模式的),我们进入AT指令模式需要两步。
- 发送“+++”指令(不发送新行)
- 在发送“+++”指令的3s内发送除了重启指令之外的任意AT指令,才能完全进入AT指令模式,比如发送AT+CSQ
2、查看电话卡的ICCID:AT+ICCID
- 我的4G模块响应的是:“+OK=89860085111556043395”
3、修改4G模块波特率:
- 发送指令:AT+UART=9600,NONE (9600代表波特率,NONE代表没有奇偶校验位,EVEN是偶校验,ODD是奇校验)
- 设置成功,4G模块会响应“+OK”
- 成功后立即重启:AT+REBT (重启以后关闭PC上的串口助手,然后修改波特率为9600即可,此时4G模块重新进入透传工作模式)
- 重启成功,4G模块会响应“+OK”
局域网通信和公网通信:
- 局域网通信模式:我们之前在用ESP8266时,用的是局域网通信模式,我们将8266安置在51单片机上,让单片机和PC联入同一个局域网,局域网随机给这两个设备分配IP地址,我们让8266以客户端模式或者以服务器模式工作,通过建立起客户端与服务器的连接,最后51单片机就能与PC在特定的连接通道上实现数据的交互。
- 公网通信模式:公网通信没办法识别局域网的IP地址(因为世界上有很多相同的IP地址,比如有很多个192.168.252.201),这时候需要将我们的PC的IP地址映射成公网的IP地址,专门的名词称为“内网IP穿透”,内网穿透能够为局域网的设备提供一个外网可访问的地址和端口。
- 借助的工具:“花生壳”
- 对于普通的应用场景和学习来说,花生壳就够用了,下载方式是百度进入花生壳官网。
- 体验版就是限制了带宽和每个月的流量。
PC上的公网通信测试:
- 先在PC上打开一个网络调试助手,建立一个局域网下的TCP服务器(设置本地主机地址和端口号)
- 运行花生壳软件,在内网穿透 / 自定义映射中进行映射(设置内网主机和内网端口),保存后会显示连接成功。
- 再在PC上打开另一个网络调试助手,建立一个局域网下的TCP客户端,连接刚才映射的TCP服务器(设置本地主机地址和远程主机地址,其中远程主机地址就是刚才花生壳软件生成的IP地址)
- 让客户端与服务器进行交互。
注:
- 此时对于TCP服务器来说,它不区分自己是局域网下的设备还是公网下的设备,也就是说相对于局域网通信来说,这个TCP服务器多了一条走公网的数据通信途径。
- 如果家里的路由器买的比较贵,具备内网穿透功能,可以直接设置,将设备的IP地址和端口号设置成外网可访问的地址和端口。
- TCP客户端每次连接TCP服务器时,TCP客户端后面的端口号是随机分配的。
- 在建立TCP服务器后,如果让电脑断开局域网络,这个TCP服务器实际上还存在,但是已经不能从TCP客户端接受信息了,所以提前测试TCP服务器能否接收到透传信息是很有必要的。
4、查询心跳包信息:有两种指令可用。初次查询到的是默认的心跳包信息。
- 方式1:AT+HEARTINFO
-
- 我的4G模块响应的是“+OK=Smart-H”
- 方式2:AT+HEARTINFONEW
- 我的4G模块响应的是“+OK=1,Smart-H” (这里1表示心跳包数据的格式是ASCII码,0代表HEX格式)
5、设置心跳包信息:
- 方式1:AT+HEARTINFO=<data> (参数data是40字节以内的ASCII码心跳包数据)
- 设置成功,4G模块会响应“+OK”
- 方式2:AT+HEARTINFONEW=<type>,<data> (参数type有两种可选,1表示ASCII码格式,0代表HEX格式,当心跳包类型为ASCII 码时,data是40 字节之内的数据,当心跳包类型为EX 时,内容必须是合法的HEX 格式且长度必须是偶数。)
- 心跳包我设置成了c52NO1,设置成功,4G模块会响应“+OK”
6、查询心跳包时间间隔:AT+HEARTM
- 查询成功,4G模块会响应“+OK=<time>” (参数time的范围在0~65535s,其中0代表关闭)
7、设置心跳包时间间隔:AT+HEARTM=<time> (参数time的范围在0~65535s,其中0代表关闭)
- 我这里设置了3s,设置成功,4G模块会响应“+OK”
8、设置预连接的Socket服务器:AT+SOCK=TCPC,73n8f47741.goho.co,32864 (其中73n8f47741.goho.co是公网的IP地址,由花生壳获得,32864是动态端口号)
- 配置成功,4G模块会响应“+OK”
- 注意:4g模块的AT指令模式是纯配置,至于这种配置是否生效,还要重启4g模块才能确定,所以现在只是预连接阶段,并不是真正代表连接上服务器,此时4g模块也总是在响应“+OK”
9、配置成功,重启生效:AT+REBT
- 先配置,重启后才运行,不重启的话就一直在AT指令模式,重启后才能进入透传工作模式。
- 重启之后,检查一下4G模块的指示灯,第四盏黄色的灯亮说明SIM卡初始化正常,第一盏绿色的灯亮说明4G模块已经连接上公网下的TCP服务器。一切就绪后(这个过程可能有点慢),就能在PC上的网络调试助手上看到发送来的心跳包以及透传数据。
预热(4g小车):
- 目标:手机app控制小车运动
- 特点:手机用4G,51单片机也用4G。4G模块比蓝牙和wifi模块牛逼,因为突破了地域的限制,可以远距离传送。
- 方法:自己用Linux搭建一个TCP服务器,用于数据的自动中转,当手机app发送指令给TCP服务器,TCP服务器再自动将数据中转给4G模块,实现手机控制小车。
3.4G网络控制LED
1、确认单片机要发送的AT指令:
- 进入AT指令模式(两步)
查询SIM卡的ICCID号- 设置心跳包信息
- 设置心跳包时间间隔
- 连接Socket服务器
- 重启生效
波特率是提前用电脑通过AT指令给4g模块配置好的,用9600。
2、单片机处理4g模块响应信息:
AT指令配置成功的关键字眼及标志位 | 进入AT指令模式 | 设置心跳包信息 | 设置心跳包时间间隔 | 连接Socket服务器 | 重启生效 |
成功:“+OK” | 成功:“+OK” | 成功:“+OK” | 成功:“+OK” | 成功:“+OK” | |
成功标志位: AT_OK_Flag | 成功标志位: AT_OK_Flag | 成功标志位: AT_OK_Flag | 成功标志位: AT_OK_Flag | 成功标志位: AT_OK_Flag | |
透传有效指令的关键字眼 | 灯控指令 | ||||
亮灯:“:op” 灭灯:“:cl” |
3、通过硬件来窥探4g模块配置:无,配置阶段都响应+OK,没有实际意义。
4、白盒测试和黑盒测试不一致:
用如下第一个代码(实际上是不可行的)进行白盒测试,尽管通过了白盒测试(用串口模拟发送4g模块原本对AT指令的响应信息后,51单片机发送下一条AT指令,4g模块都有反应),但是用这个代码进行黑盒测试时,发现了如下问题:
- 单片机无法正常发送AT指令,导致程序卡死在空循环体。
- 4g模块不依赖于单片机指令,自动去生效上一次用PC配置好的AT指令,最后两盏灯都亮。第一盏灯亮后,TCP服务器能够接收到4g模块的心跳包。
- 无法用透传控制led。
分析可能的原因:
- 白盒测试正常,说明不是“重新上电后,4g模块在生效上次的配置信息的时候拒绝接受信息,最终导致51单片机卡死在空循环体中”导致的。
- 可能的原因是:重新上电后,4g模块默认处在透传工作模式,会自动去生效上次用PC配置好的AT指令。但我们的程序强行让4g模块进入AT指令模式,所以我们可以在白盒测试时观察到4g模块的响应信息,但是在黑盒测试中,4g模块这部分的响应信息貌似没有通过串口被51单片机接收。
不可行的代码:4g控制led
- 代码心得:
- 4g模块上电后的初始化时间有点久的,所以我这里给它了3s的上电时间(3s也是个大概,因为4g模块上电后四盏指示灯都会亮,初始化完毕后指示灯才灭掉,所以最准确的是看4g模块上所有指示灯熄灭的时间)。如果不给上电初始化的延时,那么在白盒测试时就无法在串口助手上看见4g模块的任何响应信息。这是因为这部分指令因为4g模块还在做上电初始化而被忽略了。
- 不用让单片机发送心跳包,因为4g模块自己会自动发送。
- 思路:
宏定义: 1. 定义符号常量len,用它代表用于接收SBUF中缓冲字符串的全局数组的长度: #define len 12 全局变量: 1. sfr指令直接找到AUXR寄存器: sfr AUXR = 0X8E; //因为AUXR没有在reg52.h中声明 2. “设置4g模块进入AT指令模式的操作”: char _4g_ATmode1[] = "+++"; char _4g_ATmode2[] = "AT+CSQ\r\n"; 3. “设置4g模块心跳包数据的AT指令”: char _4g_heartInfo[] = "AT+HEARTINFONEW=1,c52NO1\r\n"; 4. “设置4g模块心跳包时间间隔的AT指令”: char _4g_heartTime[] = "AT+HEARTM=3\r\n"; 5. “设置4g模块重新上电后准备连接的公网TCP服务器的AT指令”: code char _4g_linkSocket[] = "AT+SOCK=TCPC,73n8f47741.goho.co,32864\r\n"; 6. “设置4g模块重新上电的AT指令”: char _4g_restart[] = "AT+REBT\r\n" 7. 定义wifi模块响应AT指令"OK"的标志位: char AT_OK_Flag = 0; //AT_OK_Flag的传递路线为:SBUF ——> 串口中断(中断4)——> API2: _4gModule_Client_Config(); 8. 定义一个用于接收串口中缓冲区字符串的全局数组serial_buffer: char serial_buffer[len]; //serial_buffer的传递路线为:SBUF ——> 串口中断(中断4)——> 临时字符变量temp
1. 一上电先让指示灯D5和D6灭: ledD5 = ledD6 = 1; 2. 调用API1. 初始化串口: UartInit(); 3. 调用API5. 软件延时3s,给4g模块上电初始化预留时间: Delay1000ms(); Delay1000ms(); Delay1000ms(); 4. 调用API2. 配置4g模块并让其重新启动: _4gModule_Client_Config(); 5. while死循环,防止程序自行结束
中断: 中断4: 封装串口中断的中断服务程序, void Uart_Routine() interrupt 4 4.1 定义一个静态全局区的静态变量,用来表示数组serial_buffer的下标: static int i = 0; 4.2 定义一个临时字符变量temp,用于检测关键字眼,保证我们的字符串是从字符数粗的第0位开始存放的。 char temp; 4.3 中断处理程序中,对于接收中断的响应,判据是RI == 1 4.3.1 在接受到1字节数据后,程序复位RI: RI = 0; 4.3.2 串口缓冲区接收到的字符先存放在临时变量temp中: temp = SBUF; 4.3.3 从数据缓冲寄存器SBUF中读到字符后,根据我们提前设计好的关键字眼,关心四件事: "+OK"的'O'、 "cmd:op"的':',判据是temp=='O' || temp==':' 4.3.3.1 如果是,那么需要从头开始存放: i = 0; 4.3.3.2 否则,那么什么也不做,继续往下执行 4.3.4 将temp的值保存在数组serial_buffer的第i个元素中: serial_buffer[i] = temp; 4.3.5 偏移数组下标: i++; 4.3.6 判断字符数组serial_buffer是否存满了,判据是 i == len //内在逻辑:由于serial_buffer长度的限制,当字符串超过len时,我们需要覆盖掉原先的字符 4.3.6.1 如果是,那么需要从头开始存放: i = 0; 4.3.6.2 否则,那么什么也不做,继续往下执行 4.3.7 通过字符数组的第0位和第1位捕捉关键字眼,判断wifi模块是否响应"OK",判据是 serial_buffer[0]=='O' && serial_buffer[1]=='K' 4.3.7.1 如果是, 令标志位为1: AT_OK_Flag = 1; 有效指令后清空字符数组: memset(serial_buffer,'\0',len); 4.3.7.2 否则,那么什么也不做,继续往下执行 4.3.8 通过字符数组的第0位和第2位捕捉关键字眼,判断wifi模块是否收到透传数据":open",判据是 serial_buffer[0]==':' && serial_buffer[1]=='o' && serial_buffer[2]=='p' 4.3.8.1 如果是, 点亮D5: ledD5 = 0; 有效指令后清空字符数组: memset(serial_buffer,'\0',len); 4.3.8.2 否则,如果wifi模块收到透传数据"+IPD,0,n:close" 熄灭D5: ledD5 = 1; 有效指令后清空字符数组: memset(serial_buffer,'\0',len); 4.4 中断处理程序中,对于发送中断的响应,判据是TI == 1 暂时不做任何事情
/* 一级函数:f1、f2、f4、f5 */ f1. 封装初始化串口的API: void UartInit(void); f1.1 禁用ALE信号: AUXR = 0X01; f1.2 让串口以方式1工作(8位UART,可变波特率),并让REN使能允许串口接收: SCON = 0x50; f1.3 让定时器1以8位重载工作模式工作: TMOD &= 0xDF; TMOD |= 0x20; f1.4 根据波特率为9600,波特率不翻倍,设置定时器1的初值: TH1 = 0xFD; TL1 = 0xFD; f1.5 定时器开始数数: TR1 = 1; f1.6 开启串口中断: EA = 1; ES = 1; 2. “设置4g模块进入AT指令模式的操作”: char _4g_ATmode1[] = "+++"; char _4g_ATmode2[] = "AT+CSQ\r\n"; 3. “设置4g模块心跳包数据的AT指令”: char _4g_heartInfo[] = "AT+HEARTINFONEW=1,c52NO1\r\n"; 4. “设置4g模块心跳包时间间隔的AT指令”: char _4g_heartTime[] = "AT+HEARTM=3\r\n"; 5. “设置4g模块重新上电后准备连接的公网TCO服务器的AT指令”: code char _4g_linkSocket[] = "AT+SOCK=TCPC,73n8f47741.goho.co,32864\r\n"; 6. “设置4g模块重新上电的AT指令”: char _4g_restart[] = "AT+REBT\r\n" f2. 封装配置wifi模块以客户端模式工作的API: void wifiModule_Server_Config(); f2.1 设置4g模块进入AT指令模式: 调用API4,通过串口发送对应AT指令: sendString(_4g_ATmode1); 调用API5. 软件延时1s: Delay1000ms(); 调用API4,通过串口发送对应AT指令: sendString(_4g_ATmode2); 空循环体等待,直到wifi模块响应"OK": while(!AT_OK_Flag); 为了不影响下一个条指令响应,复位标志位: AT_OK_Flag = 0; f2.2 设置4g模块心跳包数据: 调用API4,通过串口发送对应AT指令: sendString(_4g_heartInfo); 空循环体等待,直到wifi模块响应"OK": while(!AT_OK_Flag); 为了不影响下一个条指令响应,复位标志位: AT_OK_Flag = 0; f2.3 设置4g模块心跳包时间间隔: 调用API4,通过串口发送对应AT指令: sendString(_4g_heartTime); 空循环体等待,直到wifi模块响应"OK": while(!AT_OK_Flag); 为了不影响下一个条指令响应,复位标志位: AT_OK_Flag = 0; f2.4 设置4g模块重新上电后准备连接的公网TCP服务器: 调用API4,通过串口发送对应AT指令: sendString(_4g_linkSocket); 空循环体等待,直到wifi模块响应"OK": while(!AT_OK_Flag); 为了不影响下一个条指令响应,复位标志位: AT_OK_Flag = 0; f2.5 设置4g模块重新上电,让上述指令生效: 调用API4,通过串口发送对应AT指令: sendString(_4g_restart); 空循环体等待,直到wifi模块响应"OK": while(!AT_OK_Flag); f4. 封装给PC发送字符串的API: void sendString(char *str); //形参是字符串的地址 f4.1 定义一个字符指针变量p用来保存字符串首地址: char *p = str; f4.2 while循环,控制循环的变量是*p,当*p != '\0' 时,进入循环,进行单个字符的发送 f4.2.1 通过指针间接访问字符串字符,再调用API3. 发送单个字符: sendByte(*p); f4.2.2 修改循环变量p的值,让指针p偏移: p++; f5. 封装软件延时1s的API,用于每隔1秒发送心跳包,以及串口初始化后的短暂休眠: void Delay1000ms();
/* 二级函数:f3 */ f3. 封装定时给PC发送一个字符的API: void sendByte(char data_msg); //形参是字符值 f3.1 往SBUF寄存器中写入字符data_msg: SBUF = data_msg; f3.2 根据串口发送中断触发位TI,利用空循环体暂停程序: while(!TI); f3.3 程序复位TI: TI = 0;
- 代码:
#include "reg52.h" #include "intrins.h" #include <string.h> #define len 12 sfr AUXR = 0x8E; char serial_buffer[len]; char _4g_ATmode1[] = "+++"; char _4g_ATmode2[] = "AT+CSQ\r\n"; char _4g_heartInfo[] = "AT+HEARTINFONEW=1,c52NO1\r\n"; char _4g_heartTime[] = "AT+HEARTM=3\r\n"; code char _4g_linkSocket[] = "AT+SOCK=TCPC,73n8f47741.goho.co,32864\r\n"; char _4g_restart[] = "AT+REBT\r\n"; char AT_OK_Flag = 0; //关键字眼+OK的标志位,用1代表AT指令响应成功,0代表AT指令响应失败 /* API1. 初始化串口 */ void UartInit(void); /* API2. 配置4g模块以客户端模式工作 */ void _4gModule_Client_Config(); /* API3. 通过串口给PC发送一个字符 */ void sendByte(char data_msg); /* API4. 通过串口给PC发送一个字符串 */ void sendString(char *str); /* API5. 用于串口初始化后的短暂休眠 */ void Delay1000ms(); void main() { ledD5 = ledD6 = 1;//灭状态灯 UartInit(); Delay1000ms();//给4g模块上电时间 Delay1000ms(); Delay1000ms(); _4gModule_Client_Config(); while(1){ } } void Uart_Routine() interrupt 4 { static int i = 0;//静态变量,被初始化一次 char temp; /* 中断处理程序中,对于接收中断的响应 */ if(RI)//中断处理函数中,对于接收中断的响应 { RI = 0;//清除接收中断标志位 temp = SBUF; if(temp == 'O' || temp == ':'){ i = 0; } serial_buffer[i] = temp; i++; if(i == len) i = 0; //4g模块响应值的判断 if(serial_buffer[0] == 'O' && serial_buffer[1] == 'K'){ AT_OK_Flag = 1; memset(serial_buffer, '\0', len); } //灯控指令 if(serial_buffer[0] == ':' && serial_buffer[1] == 'o' && serial_buffer[2]=='p'){ ledD5 = 0;//点亮D5 memset(serial_buffer, '\0', len); }else if(serial_buffer[0] == ':' && serial_buffer[1] == 'c' && serial_buffer[2]=='l'){ ledD5 = 1;//熄灭D5 memset(serial_buffer, '\0', len); } } /* 中断处理程序中,对于发送中断的响应 */ if(TI == 1){ // 暂时不做任何事情 } } void UartInit(void) //9600bps@11.0592MHz { AUXR = 0x01; SCON = 0x50; //8位UART,允许串口接收 TMOD &= 0xDF; TMOD |= 0x20; //定时器8位重载工作模式 TH1 = 0xFD; TL1 = 0xFD; //9600波特率初值 TR1 = 1; //启动定时器 EA = 1; ES = 1; //开启串口中断 } void _4gModule_Client_Config() { sendString(_4g_ATmode1); Delay1000ms(); sendString(_4g_ATmode2); while(!AT_OK_Flag); AT_OK_Flag = 0; sendString(_4g_heartInfo); while(!AT_OK_Flag); AT_OK_Flag = 0; sendString(_4g_heartTime); while(!AT_OK_Flag); AT_OK_Flag = 0; sendString(_4g_linkSocket); while(!AT_OK_Flag); AT_OK_Flag = 0; sendString(_4g_restart); while(!AT_OK_Flag); } void sendByte(char data_msg) { SBUF = data_msg; while(!TI); TI = 0; } void sendString(char *str) { char *p = str; while(*p != '\0'){ sendByte(*p); p++; } } void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); }
正确的代码:4g控制led
- 思路:删去上面的与AT指令有关的语句和函数、删去等待4g模块初始化完成的那3s的软件延时。
- 代码:
#include "reg52.h" #include <string.h> #define len 12 sfr AUXR = 0x8E; sbit ledD5 = P3^7; char serial_buffer[len]; /* API1. 初始化串口 */ void UartInit(void); void main() { ledD5 = 1;//灭状态灯 UartInit(); while(1){ } } void Uart_Routine() interrupt 4 { static int i = 0;//静态变量,被初始化一次 char temp; /* 中断处理程序中,对于接收中断的响应 */ if(RI)//中断处理函数中,对于接收中断的响应 { RI = 0;//清除接收中断标志位 temp = SBUF; if(temp == ':'){ i = 0; } serial_buffer[i] = temp; i++; if(i == len) i = 0; //灯控指令 if(serial_buffer[0] == ':' && serial_buffer[1] == 'o' && serial_buffer[2]=='p'){ ledD5 = 0;//点亮D5 memset(serial_buffer, '\0', len); }else if(serial_buffer[0] == ':' && serial_buffer[1] == 'c' && serial_buffer[2]=='l'){ ledD5 = 1;//熄灭D5 memset(serial_buffer, '\0', len); } } /* 中断处理程序中,对于发送中断的响应 */ if(TI == 1){ // 暂时不做任何事情 } } void UartInit(void) //9600bps@11.0592MHz { AUXR = 0x01; SCON = 0x50; //8位UART,允许串口接收 TMOD &= 0xDF; TMOD |= 0x20; //定时器8位重载工作模式 TH1 = 0xFD; TL1 = 0xFD; //9600波特率初值 TR1 = 1; //启动定时器 EA = 1; ES = 1; //开启串口中断 }