杰杰MQTT移植+USART线程+FreeRTOS
本文详细介绍了AT命令模块的实现方案,重点包括:1)封装AT命令处理模块,实现与底层硬件解耦;2)采用信号量机制实现命令发送与返回数据的同步控制;3)构建硬件抽象层(HAL)和内核抽象层(KAL)提升系统可移植性;4)实现带超时检测的互斥量机制;5)设计完整的数据接收解析流程,包括特殊字符处理(如"+IPD")和状态管理;6)提供全局变量和缓冲区管理方案。通过FreeRTOS任
一、解析AT命令与底层抽象
1. 封装AT命令模块
我们接下来需要实现核心的AT命令处理函数。建议对相关代码再封装一层,以提升通用性和可维护性。
2. 脱离底层依赖
3. 信号量与数据读取流程
SemaphoreHandle_t xSemaphore = NULL;
4. 抽象系统依赖
5. 平台互斥量定义与初始化
6. 互斥量获取与超时机制
7. 返回值处理与状态管理
8. 全局变量与Buffer管理
9. 互斥量初始化细节
10. 任务创建与数据解析
二、中断函数唤醒任务
1. 串口读取与初始化
2. AT命令发送与返回值处理
3. 内核初始化顺序
4. 线程与测试
三、编写接收数据包函数
1. 收包与特殊字符处理
2. 参考代码实现
#include "at_command.h"
/* 超时时间,时间根据tick频率计算,如tick=1000,那么时间就是1S */
#define AT_CMD_TIMEOUT 1000
/* resp缓冲区长度 */
#define AT_RESP_LEN 128
/* 别人封装好的抽象层我们定义使用,这个互斥量用于AT命令返回结果 */
static platform_mutex_t at_ret_mutex;
static platform_mutex_t at_packet_mutex;
/* 设置AT命令状态,不要暴露全局变量 */
static int g_at_stastus;
/* 设置全局at,buffer */
static char g_at_resp[AT_RESP_LEN];
/* 设置数据包 */
static char g_at_packet[AT_RESP_LEN];
/* AT命令数据包长度 */
static char g_at_packet_len;
/* 设置全局变量函数 */
/* stutas
* 0 - ok
* -1 - error
* -2 - timeout
*/
void Set_AT_Status(int stutas)
{
g_at_stastus = stutas;
}
/* 获得全局变量函数 */
/* stutas
* 0 - ok
* -1 - error
* -2 - timeout
*/
void Get_AT_Status(int stutas)
{
return g_at_stastus;
}
/* 初始化AT命令 */
int AT_Init(void)
{
/* 初始化我们创建的互斥量 */
platform_mutex_init(&at_ret_mutex);
/* 因为mutex初始化是1,所以我们需要先锁定一次 */
platform_mutex_lock(&at_ret_mutex); /* 之后mutex就 = 0 */
platform_mutex_init(&at_packet_mutex);
platform_mutex_lock (&at_packet_mutex); /* 之后mutex就 = 0 */
}
/* buf: "AT+CIPMOOE=1"
* len: strlen(buf);
* timeout: ms
*/
/* 发送AT命令 */
/* resp:传入指针,给null代表不关心返回数据,只关心返回值。
非空的话就需要吧数据保存进resp里面 */
int AT_Send_Cmd(char *buf,int len ,char *resp , int resp_len,int timeout_ms)
{
int ret;
int err;
/* 发生AT命令 */
HAL_AT_Send(buf, len);
HAL_AT_Send("\r\n", 2);
/* 等待结果:获得信号量
* 1 : 成功得到mutex
* 0 : 超时返回
*/
ret = platform_mutex_lock_timeout(&at_ret_mutex, AT_CMD_TIMEOUT);
if(ret)
{
/* 判断返回值 */
/* 保存resp */
err = Get_AT_Status();
if(!err && resp)
{
/* 如果长度大于我们的最大长度,就只能复制最大长度,否则复制原数据 */
memcpy(resp, g_at_resp, resp_len > AT_RESP_LEN ? AT_RESP_LEN : resp_len);
}
return err;
}
else
{
return AT_TIMOUT;
}
}
/* 是否获得特殊字符 */
int GetSpecialATString(char *buf)
{
/* 收到的数据里面有+IPD, */
if(strstr(buf, "+IPD,"))
return 1;
else
return 0;
}
/* 处理特殊字符函数 */
void ProcessSpecialATString(char *buf)
{
int i = 0;
int len = 0;
/* +IPD,n:xxxxxxxxxxxx */
while(1)
{
/* 解析出长度 */
i = 0;
while(1)
{
/* 读取WiFi模块发来的数据:使用阻塞方式 */
HAL_AT_Recv(&buf[i], portMAX_DELAY);
if(buf[i] == ':')
{
break;
}
else
{
len = len * 10 + (buf[i] - '0'); /* 两位 */
}
i++;
}
/* 读取真正的数据 */
i = 0;
while(i < len)
{
/* 读取WiFi模块发来的数据:使用阻塞方式 */
HAL_AT_Recv(&buf[i], portMAX_DELAY);
if(i < AT_RESP_LEN)
{
g_at_packet[i] = buf[i];
g_at_packet_len = i;
}
i++;
}
/* 解锁 */
platform_mutex_unlock(&at_packet_mutex);
}
}
/* 读数据包线程 */
int AT_Rend_Packet(char *buf, int len , int *resp_len, int timeout_ms)
{
int ret;
/* 解锁 */
ret = platform_mutex_lock_timeout(&at_packet_mutex, timeout_ms);
/* 是否得到数据 */
if(ret)
{
*resp_len = len > g_at_packet_len ? g_at_packet_len : len;
/* 实际长度大于我们定的长度 */
memcpy(buf, g_at_packet, *resp_len);
return AT_OK;
}
else
{
return AT_TIMOUT;
}
}
/* 解析AT命令任务 */
void ATRecvParser( void *params)
{
/* 数据缓冲 */
char buf[AT_RESP_LEN] = {0};
int i = 0;
while(1)
{
/* 读取WiFi模块发来的数据:使用阻塞方式 */
HAL_AT_Recv(&buf[i], portMAX_DELAY);
/* 解析结果 */
/* 1. 何时解析?
* 1.1 收到"\r\n"
* 1.2 收到特殊字符:"+IPD,"
*/
/* 上一个字符是\r,当前的字符是\n i!=0和i同理 */
if( (i) && (buf[i-1] == '\r') && (buf[i] == '\n'))
{
/* 得到了回车换行 先结束这一句 */
buf[i+1] = '\0';
/* 2. 怎么解析? */
/* 检索"OK\r\n"字符串 */
if(strstr(buf, "OK\r\n"))
{
/* 记录数据 */
memcpy(g_at_resp, buf, i);
/* 成功的话,设置状态 */
Set_AT_Status(AT_OK);
/* 唤醒任务 */
platform_mutex_unlock(&at_ret_mutex);
}
else if(strstr(buf, "ERROR\r\n"))
{
/* 失败的话,设置状态 */
Set_AT_Status(AT_ERROR);
platform_mutex_unlock(&at_ret_mutex);
}
/* 如果获得了特殊AT的字符串 */
else if(GetSpecialATString(buf))
{
/* 处理特殊的AT字符串 */
ProcessSpecialATString(buf);
i = -1;
}
i++;
}
}
}
/* 测试程序 */
void Task_AT_Test( void *params)
{
int ret;
while(1)
{
ret = AT_Send_Cmd("AT", 2, NULL, 0, 2000);
printf("AT_Send_Cmd = %d\r\n", ret);
}
四、解决ESP8266的错误
整体程序思路
编译与调试建议
更多推荐
























































所有评论(0)