软件 AT指令框架学习笔记 发表于 2019-12-27 浏览量 1026 评论数 1 ### 示例代码: 思考: ```c /* 名称 | 查询格式 | 查询返回格式 | 设置格式 | 设置返回格式 */ {"CIPSEND", "", "", "=%d,%d", "> "} {NULL, NULL, NULL, NULL, "+CIPSEND: %d"} ``` ```mermaid graph TB a(Command Write) --> c(dma Write) c --> d(wait send finish) d --> e(Uart send finish) e -->f(Finish) ``` ```c uint8_t Buffer[BUFFER_SIZE]; char Payload[PAYLOAD_SIZE]; extern UART_HandleTypeDef huart3; CommandTypedef AtCommand[] = { /* name|query transmit format|query receive format|set tranmmit format|set receive format */ {"AT", "", "", NULL, NULL }, {"CGMI", "", "%s", NULL, NULL }, {"CGMM", "", "%s", NULL, NULL }, {"CGSN", "=1", "+CGSN:%s", NULL, NULL }, {"CFUN", "?", "+CFUN:%d", "=%d", "" }, {"CSQ", "", "+CSQ:%d,%*d", NULL, NULL }, {"NBAND", "?", "+NBAND:%d", "=%d", "" }, {"CGACT", "?", "+CGACT:%*d,%d", "=%d,%d", "" }, {"CGATT", "?", "+CGATT:%d", NULL, NULL }, {"CEREG", "?", "+CEREG:%*d,%d", "=%d", "" }, {"CSCON", "?", "+CSCON:%*d,%d", "=%d", "" }, {"CGPADDR", "", "+CGPADDR:%*d,%d.%d.%d.%d", NULL, NULL }, {"NCONFIG", "?", "+NCONFIG:%*11s,%s", "=%s,%s", "" }, {"CPSMS", "?", "+CPSMS:%d", "=%d", NULL }, {"NMGS", NULL, NULL, "=%u,%s", "" }, {"NNMI", "?", "+NNMI:%u", "=%u", "" }, {"NCDP", "?", "+NCDP:%u.%u.%u.%u,%u", "=%u.%u.%u.%u,%u", "" }, {"NMSTATUS","?", "+NMSTATUS:%*s", NULL, NULL }, {"NNMI", NULL, "+NNMI:%u,%s", NULL, NULL }, {"CCLK", "?", "+CCLK:%u/%u/%u,%u:%u:%u+%u", NULL, NULL }, {"NCCID", "?", "+NCCID:%s", NULL, NULL }, }; /* AT_OK的正则表达式对照组 */ static const char *atOkPattern = "\r\nOK\r\n"; /* AT_ERROR的正则表达式对照组 */ static const char *atErrorPattern = "\r\nERROR\r\n"; /** * @brief AT 查询指令或执行指令 @param command 指令名 ... 额外的参数(与query receive format 匹配) * @retval skip */ AT_StatusTypedef atreadCommand(AtCommandListTypedef command, ...) { uint16_t len; size_t index; /* 从AT指令表中取出对应指令应该具有的发送格式,然后加上"AT+"前缀 */ len = sprintf(Payload, "AT+%s%s\r\n", AtCommand[command].Name, AtCommand[command].pQueryTransmitFormat); serial_write(&huart3, (uint8_t *)Payload, len); /* 发送完成之后进入接收模式 */ len = serial_read(&huart3, Buffer, BUFFER_SIZE, 1000); /* 没有收到任何数据,返回 未响应 */ /* 此处应该是指令格式有误,AT解释器不处理该指令,因检查指令拼写*/ if(len == 0) return AT_NO_RESPONSE; /* 匹配到返回数据中有OK,认为指令执行成功 */ if(re_match(atOkPattern, (char *)Buffer) != -1) { /* 将Buffer索引起点设置在2的位置,避开开头的\r\n转义字符 */ index = 2; va_list list; va_start(list, command); vsscanf((char *)(Buffer + index), AtCommand[command].pQueryReceiveFormat, list); va_end(list); return AT_OK; } /* 匹配到返回数据中有ERROR */ /* 此处正常情况下不会发生,若发生,应该是发送的指令格式有误,应该检查指令表 */ if(re_match(atErrorPattern, (char *)Buffer) != -1) return AT_ERROR; /* 对于解析不了的返回格式错误,意味着传输过程中出现了意外 */ /* 正常情况下此处也不该发生 */ return AT_FORMAT_WRONG; } /** * @brief AT 设置指令 * @param command,指令名 * ..., 额外的参数(与set tranmmit format匹配) * @retval skip */ AT_StatusTypedef atsendCommand(AtCommandListTypedef command, ...) { size_t len = 0; len += sprintf(Payload, "AT+%s", AtCommand[command].Name); va_list list; va_start(list, command); len += vsprintf(Payload + len, AtCommand[command].pSetTransmitFormat, list); va_end(list); len += sprintf(Payload + len, "\r\n"); atCommandWrite((uint8_t*)Payload, len, 1000); len = atCommandRead(Buffer, 5000); /* 这里的返回情况和At_ReadCommand一样 */ if(len == 0) return AT_NO_RESPONSE; if(re_match(atOkPattern, (char *)Buffer) != -1) // 需要引用正则匹配库 { return AT_OK; } if(re_match(atErrorPattern, (char *)Buffer) != -1) return AT_ERROR; return AT_FORMAT_WRONG; } AT_StatusTypedef atwaitCommand(AtCommandListTypedef command, uint32_t timeout, ...) { uint16_t len; uint8_t index; len = serial_read(&huart3, Buffer, BUFFER_SIZE, timeout); if(len == 0) return AT_NO_RESPONSE; if(re_match(AtCommand[command].Name, (char *)Buffer) != -1) { index = 2; va_list list; va_start(list, timeout); vsscanf((char *)(Buffer + index), AtCommand[command].pQueryReceiveFormat, list); va_end(list); return AT_OK; } if(re_match(atErrorPattern, (char *)Buffer) != -1) return AT_ERROR; return AT_FORMAT_WRONG; } AT_StatusTypedef atSendNoATCommand(AtCommandListTypedef command, uchar *buffer, size_t length) { uint16_t len; if(AtCommand[command].Name != NULL) { len += sprintf(Payload, "AT+%s", AtCommand[command].Name); } memcpy(Payload + len, buffer, length); len += length; atCommandWrite((uint8_t*)Payload, len, 1000); len = atCommandRead(Buffer, 5000); /* 这里的返回情况和At_ReadCommand一样 */ if(len == 0) return AT_NO_RESPONSE; if(AtCommand[command].pSetReceiveFormat != NULL) { if(re_match(AtCommand[command].pSetReceiveFormat, (char *)Buffer) != -1) // 需要引用正则匹配库 { return AT_OK; } } else { if(re_match(atOkPattern, (char *)Buffer) != -1) // 需要引用正则匹配库 { return AT_OK; } if(re_match(atErrorPattern, (char *)Buffer) != -1) return AT_ERROR; } return AT_FORMAT_WRONG; } bool atCommandWrite(uchar *buff, size_t len, uint32_t timeout) { return dmaWrite(buff, len, timeout); } size_t atCommandRead(uchar *buff, uint32_t timeout) { return dmaRead(buff, timeout); } /********************* HAL Library 实现 ********************/ bool dmaWrite(uchar *buff, size_t len, uint32_t timeout) { bool ret = false; if((buff == NULL) || (len == 0)) return ret; bsp_dmaWrite(buff, len); // 拷贝 /* 等待DMA发送成功事件 */ if(xSemaphoreTake(dmaSendTI_Semaphore, (TickType_t)timeout)) { /*到这里我们获取到信号量,现在可以访问共享资源了*/ /* code */ ret = true; /* 完成访问共享资源后,必须释放信号量*/ xSemaphoreGive(dmaSendTI_Semaphore); } else { /* 没有获取到信号量,这里处理异常情况。*/ while(1); } return ret; } uint32_t dmaRead(uchar *buffer, uint32_t timeout) { uint32_t ret = 0; if(xSemaphoreTake(dmaSendIR_Semaphore, (TickType_t)timeout)) { /*到这里我们获取到信号量,现在可以访问共享资源了*/ ret = dmaFIFORead(buffer, timeout); /* 完成访问共享资源后,必须释放信号量*/ xSemaphoreGive(dmaSendIR_Semaphore); } else { while(1); } return ret; } void DMA1_IRQHandler (void) { xSemaphoreFromISR(dmaSendTI_Semaphore); } ``` ```c //AT指令列表,必须要和atCommand一一对应 typedef enum { AT = 0, AT_CGMI, AT_CGMM, AT_CGSN, AT_CFUN, AT_CSQ, AT_NBAND, AT_CGACT, AT_CGATT, AT_CEREG, AT_CSCON, AT_CGPADDR, AT_NCONFIG, AT_CPSMS, AT_NMGS, AT_NNMI, AT_NCDP, AT_NMSTATUS, AT_NNMI_RESP, AT_CCLK, AT_NCCID, } AtCommandListTypedef; /* At指令解析与打包的格式 */ typedef struct { /* 为NULL表示该指令不具有相应的数据 */ const char *Name; //指令名 const char *pQueryTransmitFormat; //查询模式下应该具有的格式 const char *pQueryReceiveFormat; //查询模式下接收的数据格式 const char *pSetTransmitFormat; //设置模式下应该具有的发送格式 const char *pSetReceiveFormat; //设置模式下接收的数据格式 } CommandTypedef; /* AT命令执行结果 */ typedef enum { AT_OK = 0, AT_ERROR, AT_NO_RESPONSE, AT_FORMAT_WRONG, } AT_StatusTypedef; AT_StatusTypedef atset_command(AtCommandListTypedef command, ...); AT_StatusTypedef atread_command(AtCommandListTypedef command, ...); AT_StatusTypedef atwait_command(AtCommandListTypedef command, uint32_t timeout,...); AT_StatusTypedef atsend_data(AtCommandListTypedef command, uchar *buffer, size_t len); ``` 框架
你好博主!请问是否能分享一份完整的AT指令框架源码学习一下?我想尝试将这套框架思路移植到没有DMA的廉价单片机上面。