QS100芯片驱动编写 初始化void Int_QS100_Init(void);配置 QS100 NB-IoT 模块唤醒并启动 UART 空闲中断接收。唤醒模块脱离低功耗模式Int_qs100_WakeUp();启动 UART3 空闲中断接收等待第一次数据HAL_StatusTypeDef statusHAL_ERROR;while(status!HAL_OK){statusHAL_UARTEx_ReceiveToIdle_IT(huart3,qs100_small_buffer,MAXSIZE);}[!info]上电后模块需要一定时间联网后续通过Int_QS100_IsNetWork等待附着网络。唤醒static void Int_QS100_WakeUp(void);通过拉高 WAKEUP 引脚 3 秒再拉低唤醒处于低功耗的 QS100 模块。HAL_GPIO_WritePin(QS100_WAKEUP_GPIO_Port,QS100_WAKEUP_Pin,GPIO_PIN_SET);Com_Delay_S(3);HAL_GPIO_WritePin(QS100_WAKEUP_GPIO_Port,QS100_WAKEUP_Pin,GPIO_PIN_RESET);参考[[IC-QS100-NB-IoT#低功耗]]中断回调接收数据void Int_QS100_CallBack(uint16_t sizes);UART 空闲中断回调记录本次接收长度并重新开启下一次接收。保存接收长度qs100_small_sizesizes;再次启动接收维持连续接收HAL_StatusTypeDef statusHAL_ERROR;while(status!HAL_OK){statusHAL_UARTEx_ReceiveToIdle_IT(huart3,qs100_small_buffer,MAXSIZE);}这个函数实现地很巧妙乍一看这个函数好像写了个死循环但是纵观整个中断逻辑来看当中断触发时这个callback函数会被触发执行中断接收任务有意思的是当接收到数据时就会再一次触发中断如此往复比轮询的效率高得多[QS100 模块] 发送: CONNECT OK\r\nNSONETID:0\r\n\r\nOK\r\n │ ├─ 第1批到达 - 触发空闲中断 - CallBack() - 记录size, 重启接收 │ │ │ └─ 主循环发现 size0 - 拷贝到 big_buffer - 清空 size │ ├─ 第2批到达 - 触发空闲中断 - CallBack() - 记录size, 重启接收 │ │ │ └─ 主循环发现 size0 - 追加到 big_buffer - 检查有没有OK │ └─ 第3批到达 - ...直到主循环在 big_buffer 中找到 OK 或 ERROR结束等待。发送 AT 命令并等待响应static void Int_qs100_SendATCMD(char *cmd);发送 AT 命令等待模块返回完整响应以 “OK” 或 “ERROR” 结束超时约 3 秒。清空小缓冲区和长度memset(qs100_small_buffer,0,strlen((char*)qs100_small_buffer));qs100_small_size0;memset(qs100_big_buffer,0,strlen((char*)qs100_big_buffer));qs100_big_size0;调用memset()函数把qs100_small_buffer和qs100_big_buffer清零发送命令HAL_UART_Transmit_IT(huart3,(uint8_t*)cmd,strlen(cmd));把转入的AT命令发送给qs100等待判断接收数据uint8_tcont4;do{while(qs100_small_size0HAL_GetTick()3000);if(qs100_small_size0)break;定义了一个count变量用于后面计数这里调用了SysTick定时器用来计时。if用于判断是否收到数据存入收到的数据memcpy(qs100_big_buffer[qs100_big_size],qs100_small_buffer,strlen((char*)qs100_small_buffer));qs100_big_sizeqs100_small_size;这里调用memcpy()函数把qs100_small_buffer里的数据拷贝到qs100_big_buffer里size也加一下清除数据memset(qs100_small_buffer,0,strlen((char*)qs100_small_buffer));qs100_small_size0;上一步收到数据后qs100_small_buffer这个变量还会被用来进行下一次接受这里把它清零size也清零判断接收状态}while(strstr((char*)qs100_big_buffer,OK)NULLstrstr((char*)qs100_big_buffer,ERROR)NULLcont--);这里使用while语句与开头do呼应。一共有三个判断条件OKERRORcount0(超时)count变量在这里防止无限等待打印完整响应调试用COM_LOGLN(全部响应的数据:%s,全部数据的长度:%d,qs100_big_buffer,qs100_big_size);[!NOTE] Tipdo-while循环会无条件地先执行一次循环体内的代码然后再去检查条件。如果条件成立就继续下一轮如果不成立就退出。适合“无论如何都要先做一次”的情况判断 QS100 是否已经联网QS100_STATE QS100_STATE Int_QS100_IsNetWork(void);查询网络附着状态Int_qs100_SendATCMD(ATCGATT?\r\n);发送AT命令判断网络附着状态参考检查响应中是否包含 “CGATT:1”if(strstr((char*)qs100_big_buffer,CGATT:1)!NULL){returnQS100_OK;}returnQS100_OK创建 Socket 通信通道QS100_STATE QS100_STATE Int_QS100_CreateSocket(void);发送Int_qs100_SendATCMD(ATNSOCRSTREAM,6,0,0\r\n);判断是否创建成功if(strstr((char*)qs100_big_buffer,CGATT:1)!NULL){returnQS100_OK;}returnQS100_ERROR;[!warning]实际应检测返回的 socket ID此处简化处理。若模块返回 “OK” 则视为成功。连接远程服务器QS100_STATE Int_QS100_ConnectServer(char *server_ip, uint16_t port);构造连接 AT 命令chartmp_array[50]{0};sprintf(tmp_array,ATNSOCO0,%s,%d\r\n,server_ip,port);发送命令Int_qs100_SendATCMD(tmp_array);检查结果if(strstr((char*)qs100_big_buffer,OK)!NULL){returnQS100_OK;}returnQS100_ERROR;上报数据方法QS100_STATE Int_QS100_UploadData2Server(char *data, uint16_t length);此函数用于把传感器传入的数据转换为十六进制数据因为QS100芯片的发送数据命令规定[[III_资源仓库/参考手册/QS-100模块AT命令手册_V1.0.pdf#page96selection以十六进制字符串格式发送的数据。|以十六进制字符串格式发送的数据。]]定义变量uint8_thex_array[512]{0};uint8_tresult_array[512]{0};hex_array用于储存传入的数据result_array用于储存转换好的数据将数据转为 HEX 字符串for(uint16_ti0;ilength;i){sprintf((char*)hex_array[i*2],%02X,data[i]);}遍历原始数据的每一个字节把它变成 2 位的大写十六进制字符[!NOTE] 举例假设要发送字符串AB在内存里是十进制的65和66(ASCII码)十六进制是0x41和0x42。当i 0时取出65%02X把它变成字符串41写入hex_array的第0和1个位置。当i 1时取出66%02X把它变成字符串42写入hex_array的第2和3个位置。构造发送命令sprintf((char*)result_array,ATNSOSD0,%d,%s,0x200\r\n,length,hex_array);发送并判断Int_qs100_SendATCMD((char*)result_array);if(strstr((char*)qs100_big_buffer,OK)!NULL){returnQS100_OK;}returnQS100_ERROR;[!info]0x200表示数据发送标志按模块手册配置。上报数据完整流程QS100_STATE Int_QS100_Upload(char *server_ip, uint16_t port, char *data, uint16_t length);集成联网、创建通道、连接服务器、上报数据的完整流程带超时与重试。等待联网最多尝试 10 次uint8_tcount11;while(Int_QS100_IsNetWork()!QS100_OK--count){Com_Delay_S(1);}if(count0){COM_LOGLN(联网超时);returnQS100_TIMEOUT;}COM_LOGLN(联网成功);创建 socket 通道重试 10 次count11;while(Int_QS100_CreateSocket()!QS100_OK--count){Com_Delay_S(1);}if(count0){COM_LOGLN(创建通道超时);returnQS100_TIMEOUT;}COM_LOGLN(创建通道成功);连接远程服务器重试 10 次count11;while(Int_QS100_ConnectServer(server_ip,port)!QS100_OK--count){COM_LOGLN(链接服务器中......);Com_Delay_S(1);}if(count0){returnQS100_TIMEOUT;}COM_LOGLN(链接成功);上报数据一次QS100_STATE resultInt_QS100_UploadData2Server(data,length);if(resultQS100_OK){COM_LOGLN(QS100上报数据成功);returnQS100_OK;}else{COM_LOGLN(QS100上报数据失败);returnQS100_ERROR;}低功耗进入低功耗void Int_QS100_EnterLP(void);发送快速休眠指令Int_qs100_SendATCMD(ATFASTOFF0\r\n);[!note]模块收到后进入低功耗可通过唤醒引脚退出。退出低功耗void Int_QS100_LeaveLP(void);拉高唤醒引脚 3 秒再拉低退出低功耗Int_qs100_WakeUp();