串口与4g模块

news2024/11/26 4:33:38

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;			//开启串口中断
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/534196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Trie树

目录 一、Tire树的概念二、Trie树的应用Trie字符串统计最大异或对&#xff08;难点&#xff09;暴力遍历法Trie树优化法 一、Tire树的概念 Trie树又称字典树、单词查找树。是一种能够高效存储和查找字符串集合的数据结构。 示例&#xff1a; 利用Tire树的数据结构储存字符串 储…

跟庄买股票得新技巧(第三弹)集合竞价战法

尾盘抢筹&#xff08;参考昨天&#xff09; 57分 12.35 收盘价 12.42 股价明显上涨&#xff08;越大越好&#xff09;全天阳线&#xff0c;否则突然变高就有作线的嫌疑12.35到12.42&#xff0c;滞留大量为成交单&#xff08;买一到买十存在大量买单&#xff0c;否则有做线嫌疑…

Spring-boot 结合Thymeleaf--拦截器--文件上传

目录 Spring-boot 结合Thymeleaf 官方文档 基本介绍 ● Thymeleaf 是什么 ● Thymeleaf 的优点 ● Thymeleaf 的缺点 ● Thymeleaf 机制说明 Thymeleaf 语法 表达式 1. 表达式一览 2.字面量 3. 文本操作 4.运算符 3. 比较运算 4. 条件运算 th 属性 迭代 条件运…

4年经验面试要15K,一问自动化却以为我在刁难他?

我这次是公司招聘的面试官之一&#xff0c;主要负责一些技术上的考核&#xff0c;这段时间还真让我碰到了不少奇葩求职者 昨天公司的HR小席刚跟我吐槽&#xff1a;这个星期没有哪天不加班的&#xff01;各种招聘网站上的消息源源不断&#xff0c;连吃饭都要回消息…… 看来最…

【Java面试】JVM垃圾回收相关知识点

文章目录 1. Java中对象有哪些引用类型&#xff1f;2. 有哪些基本的垃圾回收算法&#xff1f;3. 什么是分区收集算法&#xff1f;和分代收集什么区别&#xff1f;4. 什么是Minor GC、Major GC、Full GC?5. 什么情况下会触发Full GC&#xff1f; 1. Java中对象有哪些引用类型&a…

Linux 常用指令的使用

文章目录 1. 基础命令1&#xff09;ls2&#xff09;pwd3&#xff09;cd4&#xff09;touch5&#xff09;cat6&#xff09;mkdir7&#xff09;rm8&#xff09;cp9&#xff09;mv10&#xff09;more11&#xff09;less12&#xff09;head13&#xff09;tail14&#xff09;vim15&a…

《论文阅读》常识推理的生成知识提示

《论文阅读》常识推理的生成知识提示 前言简介相关知识模型构架Generated Knowledge PromptingKnowledge GenerationKnowledge Integration via Prompting推理阶段前言 你是否也对于理解论文存在困惑? 你是否也像我之前搜索论文解读,得到只是中文翻译的解读后感到失望? 小…

linux RVM安装使用

前言 RVM&#xff08;Ruby Version Manager&#xff09;是一个支持多版本Ruby的命令行程序&#xff0c;它有助于为开发和生产环境提供一致性可靠的Ruby版本。 它能够提供最新版本的Ruby&#xff0c;并管理多个版本&#xff0c;几乎在所有操作系统上都能安装和管理 Ruby。RVM 还…

RocketMQ之底层IndexFile存储协议

RocketMQ是一款高性能、高可靠、可伸缩的分布式消息中间件。在实现以上功能方面,RocketMQ采用了深度优化的存储设计方案。其中,底层IndexFile存储协议被认为是RocketMQ存储设计的关键,因此本篇文章将重点介绍RocketMQ底层IndexFile存储协议的实现机制及优缺点。 RocketMQ底…

LitCTF 2023 复现

文章目录 Hex&#xff1f;Hex&#xff01;梦想是红色的原来你也玩原神factordbP_Leake的学问Euler* Where is P?The same common divisormd5babyLCG* easy_math* Virginia* Is this only base?你是我的关键词(Keyworld)隐晦的聊天记录* baby_xor收获与体会 Hex&#xff1f;He…

VSCode中安装GPT插件详细教程

目录 安装插件 A.安装CodeGPT B.安装chatgpt 1.VSCode安装插件&#xff0c;使用本地下载vsix文件 2.获取 ChatGPT API 密钥 3.配置settings.json GPT-4主要有三大改进点 局限性 安装插件 AB功能一样&#xff0c;A安装的人最多&#xff0c;GPT具体功能可见标题链接 A.…

BLE解调

BLE解调前奏 如果不太了解IQ、FSK、GFSK的话&#xff0c;可以看上一篇&#xff0c;有一些关于这些内容的东西&#xff0c;写的应该还算好理解点吧&#xff0c;给出了自己学习时候的参考&#xff0c;具体的看他们写的。 调频与调相之间的关系 IQ调制中&#xff0c;调频是表现…

chatgpt赋能Python-anaconda降低python版本

Anaconda降低Python版本&#xff1a;为什么你需要做到这一点&#xff1f; 作为一个有10年Python编程经验的工程师&#xff0c;我经常会发现自己需要降低Pyhton的版本以便与我要使用的库兼容。为了解决这个问题&#xff0c;Anaconda是一个非常有用的工具&#xff0c;可以管理不…

基于fpga的图像处理之3x3_5x5算子模板设计

本文的思路框架&#xff1a; ①本文采用支持3x3算子模块和5x5算子模块的生成&#xff0c;用于后一级别的算法输入 ②本例程中采用的FPGA设计技巧&#xff0c;可用于借鉴&#xff0c;一是generate if参数定义&#xff1b;二是调用xilinx和altera的sync fifo和async fifo原语实现…

基于SpringBoot的招生管理系统的设计与实现

背景 本次设计任务是要设计一个招生管理系统&#xff0c;通过这个系统能够满足管理员和学生的招生公告管理功能。系统的主要功能包括首页、个人中心、学生管理、专业信息管理、专业报名管理、录取通知管理、系统管理等功能。 管理员可以根据系统给定的账号进行登录&#xff0…

做百度百科词条怎么写词条名,认证百度百科多少钱

很多人在做百度百科词条时不知道如何写百度百科词条名&#xff0c;总是因为词条不规范或者义项名不正确&#xff0c;而导致词条通过不了。认证申请百度百科到底多少钱能通过&#xff0c;实际上百度百科是免费的&#xff0c;需要费用的是请别人帮忙创建百度百科&#xff0c;下面…

SPI(Service Provider Interface)

ServiceLoad中的spi 1、简介 JDK1.6引入的特性&#xff0c;用来实现SPI&#xff08;Service Provider Interface&#xff09;&#xff0c;一种服务发现机制。 2、JDBC举例 2.1、引入mysql依赖jar <dependency><groupId>mysql</groupId><artifactId>…

[IntelliJ IDEA] 中Lombok插件的介绍和常见使用情景

文章目录 介绍使用Lombok 介绍 在编写项目时&#xff0c;尤其是在类进行类内部成员字段封装时&#xff0c;需要编写大量的get/set方法&#xff0c;不仅写的麻烦&#xff0c;如果字段名发生改变就要进行修改&#xff0c;因此非常麻烦&#xff1b;因此使用Lombok就能解决这样的问…

C++的vector使用

vector 1.vector的介绍2.vector的使用2.1. vector的定义&#xff08;构造函数&#xff09;2.2. vector iterator的使用2.3. vector空间增长问题2.4. vector的增删改查vector 迭代器失效问题&#xff08;重点&#xff09; 1.vector的介绍 vector是和数组类似的序列容器&#xf…

一文全览机器学习建模流程(Python代码)

注&#xff1a;本文基于之前的文章做了些修改&#xff0c;重复部分可以跳过看。示例的项目为基于LR模型对癌细胞分类的任务。 随着人工智能时代的到来&#xff0c;机器学习已成为解决问题的关键工具&#xff0c;如识别交易是否欺诈、预测降雨量、新闻分类、产品营销推荐。我们接…