串口与4g模块

1.认识4G模块

1、接线与插卡:EC03-DNC4G通信模块,生产的公司是EBYTE(亿佰特)。官网上有软件和用户手册下载地址。

  • 保证插卡不插错,一是使用SIM卡卡套,二是方向保证正确。如图位置是SIM卡状态灯,只有插对位置,在4G模块上电几秒钟完成初始化后这盏灯才会亮。
  • 一会儿进入测试之前需要把电源、天线、SIM卡、串口线等硬件接好。
  • 注:一定要有micro型号的卡套,一会是用手机提供热点给电脑连接,所以最好有双sim卡。

 2、翻阅用户手册:

  1. 供电电压
  2. 功能特点
    1. Socket其实就是在调用TCP,我们使用TCP其实走的就是Socket。
    2. 4G模块还支持MQTT协议。
  3. 硬件参数:波特率默认是115200bps。
  4. 工作情况
  5. AT指令集:省略“AT+”
  6. 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查询模块IMEICREG查询是否注册到网络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 号(集成电路卡识别码)
    1. 注:如果AT指令后面有“=”,那么“=”后面就是要配置的参数

 

 

 

 

2.4G模块AT指令测试

0、指令格式和指令错误码:

  1. AT指令的格式如上,其中CMD就是我们的指令字符;[op]就是等号,用来配置参数。
  2. <CR><LF>,其中CR(carriage return)表示回车,LF(line feed)表示换行。
  3. 无效的AT指令有5种。
  4. 成功的AT指令发送后,4G模块一般会显示"+OK",如果4G模块对AT指令的响应信息中有“=”,那么“=”后面的就是查询到的信息。

1、进入AT指令模式:不同于ESP8266模块一上电就进入AT指令模式,4G模块上电后默认是处于透传工作模式(默认是没有进入AT指令模式的),我们进入AT指令模式需要两步。

  1. 发送“+++”指令(不发送新行)
  2. 在发送“+++”指令的3s内发送除了重启指令之外的任意AT指令,才能完全进入AT指令模式,比如发送AT+CSQ

2、查看电话卡的ICCID:AT+ICCID

  • 我的4G模块响应的是:“+OK=89860085111556043395”

3、修改4G模块波特率:

  1. 发送指令:AT+UART=9600,NONE  (9600代表波特率,NONE代表没有奇偶校验位,EVEN是偶校验,ODD是奇校验)
    1. 设置成功,4G模块会响应“+OK”
  2. 成功后立即重启:AT+REBT (重启以后关闭PC上的串口助手,然后修改波特率为9600即可,此时4G模块重新进入透传工作模式)
    1. 重启成功,4G模块会响应“+OK”

局域网通信和公网通信

  1. 局域网通信模式:我们之前在用ESP8266时,用的是局域网通信模式,我们将8266安置在51单片机上,让单片机和PC联入同一个局域网,局域网随机给这两个设备分配IP地址,我们让8266以客户端模式或者以服务器模式工作,通过建立起客户端与服务器的连接,最后51单片机就能与PC在特定的连接通道上实现数据的交互。
  2. 公网通信模式:公网通信没办法识别局域网的IP地址(因为世界上有很多相同的IP地址,比如有很多个192.168.252.201),这时候需要将我们的PC的IP地址映射成公网的IP地址,专门的名词称为“内网IP穿透”,内网穿透能够为局域网的设备提供一个外网可访问的地址和端口。
  3. 借助的工具:“花生壳”
    1. 对于普通的应用场景和学习来说,花生壳就够用了,下载方式是百度进入花生壳官网。
    2. 体验版就是限制了带宽和每个月的流量。

 PC上的公网通信测试

  1. 先在PC上打开一个网络调试助手,建立一个局域网下的TCP服务器(设置本地主机地址和端口号)
  2. 运行花生壳软件,在内网穿透 / 自定义映射中进行映射(设置内网主机和内网端口),保存后会显示连接成功。 
  3. 再在PC上打开另一个网络调试助手,建立一个局域网下的TCP客户端,连接刚才映射的TCP服务器(设置本地主机地址和远程主机地址,其中远程主机地址就是刚才花生壳软件生成的IP地址)
  4. 让客户端与服务器进行交互。

 

  • 此时对于TCP服务器来说,它不区分自己是局域网下的设备还是公网下的设备,也就是说相对于局域网通信来说,这个TCP服务器多了一条走公网的数据通信途径。
  • 如果家里的路由器买的比较贵,具备内网穿透功能,可以直接设置,将设备的IP地址和端口号设置成外网可访问的地址和端口。
  • TCP客户端每次连接TCP服务器时,TCP客户端后面的端口号是随机分配的。
  • 在建立TCP服务器后,如果让电脑断开局域网络,这个TCP服务器实际上还存在,但是已经不能从TCP客户端接受信息了,所以提前测试TCP服务器能否接收到透传信息是很有必要的。

4、查询心跳包信息:有两种指令可用。初次查询到的是默认的心跳包信息。

  1. 方式1:AT+HEARTINFO
    • 我的4G模块响应的是“+OK=Smart-H”
  • 方式2:AT+HEARTINFONEW
    • 我的4G模块响应的是“+OK=1,Smart-H” (这里1表示心跳包数据的格式是ASCII码,0代表HEX格式)

5、设置心跳包信息:

  1. 方式1:AT+HEARTINFO=<data>  (参数data是40字节以内的ASCII码心跳包数据)
    1. 设置成功,4G模块会响应“+OK”
  2. 方式2:AT+HEARTINFONEW=<type>,<data>  (参数type有两种可选,1表示ASCII码格式,0代表HEX格式,当心跳包类型为ASCII 码时,data是40 字节之内的数据,当心跳包类型为EX 时,内容必须是合法的HEX 格式且长度必须是偶数。)
    1. 心跳包我设置成了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指令:

  1. 进入AT指令模式(两步)
  2. 查询SIM卡的ICCID号
  3. 设置心跳包信息
  4. 设置心跳包时间间隔
  5. 连接Socket服务器
  6. 重启生效
  7. 波特率是提前用电脑通过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模块都有反应),但是用这个代码进行黑盒测试时,发现了如下问题:

  1. 单片机无法正常发送AT指令,导致程序卡死在空循环体。
  2. 4g模块不依赖于单片机指令,自动去生效上一次用PC配置好的AT指令,最后两盏灯都亮。第一盏灯亮后,TCP服务器能够接收到4g模块的心跳包。
  3. 无法用透传控制led。

分析可能的原因

  • 白盒测试正常,说明不是“重新上电后,4g模块在生效上次的配置信息的时候拒绝接受信息,最终导致51单片机卡死在空循环体中”导致的。
  • 可能的原因是:重新上电后,4g模块默认处在透传工作模式,会自动去生效上次用PC配置好的AT指令。但我们的程序强行让4g模块进入AT指令模式,所以我们可以在白盒测试时观察到4g模块的响应信息,但是在黑盒测试中,4g模块这部分的响应信息貌似没有通过串口被51单片机接收。

不可行的代码:4g控制led

  • 代码心得
    • 4g模块上电后的初始化时间有点久的,所以我这里给它了3s的上电时间(3s也是个大概,因为4g模块上电后四盏指示灯都会亮,初始化完毕后指示灯才灭掉,所以最准确的是看4g模块上所有指示灯熄灭的时间)。如果不给上电初始化的延时,那么在白盒测试时就无法在串口助手上看见4g模块的任何响应信息。这是因为这部分指令因为4g模块还在做上电初始化而被忽略了。
    • 不用让单片机发送心跳包,因为4g模块自己会自动发送。
  1. 思路:
    宏定义:
    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;

  2. 代码:
    #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

  1. 思路:删去上面的与AT指令有关的语句和函数、删去等待4g模块初始化完成的那3s的软件延时。
  2. 代码:
    #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;			//开启串口中断
    }

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值