嵌入式工程师进阶指南:适配器编程设计模式一

以AT指令操作WIFI模块和NB-IoT或者4G模块为例。使用一个串口发送AT指令号到外部模块,同时接收模块返回数据进行联网、TCP连接、发送数据、接收数据,以上就是对这些物联网模块的使用方法。但是当本次使用WIFI模块而下次使用4G模块的时候,我们会遇到一个AT指令不兼容的问题,也就是WIFI的AT指令无法使用在4G模块上,即使同样使用WIFI模块不同厂家的AT指令同样不能兼容。

但是无论是什么样的模块操作方法和步骤都是一样的,大概就是分为以下几种操作:

读取SIM状态(WIFI不需要)、读取信号强度、联网、连接服务器、透传模式、收发数据。

将上述的操作定义为应用层,那么发送AT指令则被下放到驱动层。从NBIOT模块切换到4G模块,应用层基本不需要做任何改变,也正常因此我们需要使用适配器模式对驱动层进行转接,而应用层不需要任何改变。而在工程开发中,尤其是在项目开发前期不稳定的时候,常常面临更换物联网模块的需求,针对不同的物联网模块开发一套程序效率比较低。对于任何项目中面临更换硬件的风险都可以采用适配器模式编程来解决这个问题。

以下使用两个4G模块进行程序上的介绍,简单说明适配器模式是什么样的使用方法。

简单的对程序的框架进行分层如下图所示:适配器编程是针对第三层也就是AT设备层进行的。

程序设计时应采用自顶向下设计:

顶层的应用层程序类似于这些:简单明了功能明确,无多余的内容。

cpp 复制代码
app_get_csq();

app_tcp_send("This is a test");

进入网络管理层之后程序将会复杂的多,首先要明确网络管理层的目的是为了对网络进行监控和管理使用的,这一部分功能是非常重要的,因为它是对网络的直接描述和管理,一旦网络断开、设备故障都需要这部分程序作出反应和处理。

因此需要设计一个简易的网络管理结构体:

cpp 复制代码
static struct{
    at_device_t  *current_dev;           //当前的网络AT设备
    rt_bool_t     network_connect;       //网络连接状态
    rt_bool_t     data_active;           //数据传输激活  
    ip_info_t     ip_info;               //IP地址信息
}g_managers;

从本管理层中需要明确指向下一层(AT设备层)的具体设备。而AT设备层也就是我们适配器编程中的核心。针对AT设备层的结构体描述如下:

cpp 复制代码
typedef struct at_device{

    at_vendor_t vendor;              //模块厂商
    at_model_t  model;              //模块型号
    hdinface    hardinterface;      //硬件接口
    const char *name;               //模块名称

    rt_bool_t initialized;          //初始化标志
    rt_bool_t powered_on;           //上电标志
    rt_bool_t sim_ready;            //SIM卡状态
    rt_bool_t network_registered;   //网络注册标志

    uint32_t baud_rate;             //波特率
    uint32_t timeout;               //超时期限
    uint8_t  retry_count;           //重试次数
    at_resp_status_t status;        //响应状态

    char *rx_buffer;                //接收数据缓存指针
    uint32_t rx_buffer_size;        //接收缓冲区数据长度
    uint16_t rx_data_len;           //接收数据长度
    uint32_t error_count;           //错误计数
    uint32_t tx_count;              //发送次数

    at_urc_handler_t urc_handler;   //URC处理程序
    void*   urc_user_data;          //URC用户数据

    rt_mutex_t lock;                //互斥锁
    rt_sem_t   rx_sem;              //信号量
    void* paiv_data;                //私有数据

    at_device_ops_t *ops;           //操作指针
}at_device_t;

struct at_device_ops_t{
    rt_err_t (*init)(at_device_t *dev);                                          //模块初始化程序
    rt_err_t (*deinit)(at_device_t *dev);                                        //模块去初始化程序
    rt_err_t (*power_on)(at_device_t *dev);                                      //模块上电程序
    rt_err_t (*power_off)(at_device_t *dev);                                     //模块掉电程序
    rt_err_t (*reset)(at_device_t *dev);                                         //模块复位程序

    rt_err_t (*get_model)(at_device_t *dev,char* buffer, uint16_t size);         //获取模块型号
    rt_err_t (*get_version)(at_device_t* dev,char *buffer, uint16_t size);       //获取模块版本号
    rt_err_t (*get_imei)(at_device_t *dev,char* buffer);                         //获取模块IMEI号
    rt_err_t (*get_iccid)(at_device_t *dev,char* buffer, uint16_t size);         //获取卡 ICCID号

    rt_err_t (*check_sim)(at_device_t* dev,rt_bool_t *sim_ready);                //检查SIM卡状态
    rt_err_t (*check_network)(at_device_t *dev, network_info_t *info);           //检查联网状态
    rt_err_t (*get_signal)(at_device_t *dev, signal_strength_t *signal);         //获取信号强度
    rt_err_t (*get_operator)(at_device_t* dev,char *name,rt_size_t size);

    rt_err_t (*open_socket)(at_device_t *dev, int *socket_id, const char *type, const char *host, int port); //开启SOCKET
    rt_err_t (*close_socket)(at_device_t *dev, int socket_id);                                               //关闭SOCKET
    rt_err_t (*send_data)(at_device_t *dev, int socket_id, const void *data, rt_size_t size);                //发送数据
    rt_err_t (*receive_data)(at_device_t *dev, int socket_id, void *buffer, rt_size_t size, rt_size_t *received); //接收数据
    rt_err_t (*vendor_specific)(at_device_t *dev, int cmd, void *arg);

};

接下来所有的程序就要围绕上述的两个结构体展开。

针对一个具体的4G模块进行程序开发--EC200U为例:

cpp 复制代码
#include "at_device_manager.h"
#include "at_common.h"
#include "board.h"
#include "com_config.h"

typedef struct{
    at_vendor_t vendor;
    at_model_t  model;
    const char *name;
}EC200AdapterData;

static EC200AdapterData ec200data;
static at_device_t      ec200dev;




static void ec200u_urc_handler()
{

}

static rt_err_t ec200_poweron(at_device_t *dev)
{
   HAL_GPIO_WritePin(dev->hardinterface.power_port, dev->hardinterface.power_pin, GPIO_PIN_SET);
   return RT_EOK;
}

static rt_err_t ec200_poweroff(at_device_t *dev)
{
   HAL_GPIO_WritePin(dev->hardinterface.power_port, dev->hardinterface.power_pin, GPIO_PIN_RESET);

   return RT_EOK;
}

static rt_err_t ec200_reset(at_device_t *dev)
{
   HAL_GPIO_WritePin(dev->hardinterface.reset_port, dev->hardinterface.reset_pin, GPIO_PIN_SET);
   rt_thread_mdelay(100);
   HAL_GPIO_WritePin(dev->hardinterface.reset_port, dev->hardinterface.reset_pin, GPIO_PIN_RESET);
   rt_thread_mdelay(100);
   return RT_EOK;
}

static rt_err_t ec200_get_model(at_device_t *dev,char* buffer, uint16_t size)
{

    return RT_EOK;
}

static rt_err_t ec200_get_imei(at_device_t *dev,char* buffer)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT_CIMI,&resp);

    if(result == RT_EOK)
    {

    }

    return  result;
}

static rt_err_t ec200_get_iccid(at_device_t *dev,char* buffer, uint16_t size)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT_CCID,&resp);

    if(result == RT_EOK)
    {

    }

    return  result;
}

static rt_err_t ec200_check_sim(at_device_t *dev)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT_CPIN,&resp);

    if(result == RT_EOK)
    {

    }

    return  result;
}

static rt_err_t ec200_check_network(at_device_t *dev,char* buffer, uint16_t size)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT_CCID,&resp);

    if(result == RT_EOK)
    {

    }

    return  result;
}

static rt_err_t ec200_get_signal(at_device_t *dev,signal_strength_t *signal)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    int csq,ber;      //csq : 信号强度    ber:误码率
    result = at_cmd_send_common(dev,CMD_AT_CSQ,&resp);

    if(result == RT_EOK)
    {
        if(sscanf(resp.raw_response,"+CSQ: %d,%d",&csq,&ber))
        {
            if(csq == 0)
            {
                signal->rssi = -115;
            }else if(csq == 1)
            {
                signal->rssi = -111;
            }else if(csq>=2 && csq <= 30)
            {
                signal->rssi = -110 + (csq-2) * 2;
            }else if(csq == 31)
            {
                signal->rssi = -51;
            }else {
                signal->rssi = -113;
            }
            signal->ber  = ber;
        }
    }

    return  result;
}
static rt_err_t ec200_at_test(at_device_t *dev)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT,&resp);
    if(result == RT_EOK && resp.status == AT_RESP_OK)
    {
        return RT_EOK;
    }

    dev->status = resp.status;
    return RT_ERROR;

}

static rt_err_t ec200_init(at_device_t *dev)
{

    rt_err_t result ;
    ec200_poweron(dev);
    ec200_reset(dev);

    result = ec200_at_test(dev);

    if(result != RT_EOK)
    {
        //加入log分析
        return RT_ERROR;
    }

    result = ec200_check_sim(dev);
}


static at_device_ops_t ec200_ops = {
        .init = ec200_init,
        .power_on = ec200_poweron,
        .power_off = ec200_poweroff,
        //其余省略
};
static at_device_register_t reg = {
           .vendor = VENDOR_QUECTEL,
           .model  = MODEL_EC200U,
           .dev    = &ec200dev,
           .name   = MODEL_NAME,
           .ops    = &ec200_ops,
           .priv_data_size = 0
};
static struct rt_semaphore semaphore ;

static struct rt_mutex     mutex;


int at_adapter_quectel_ec200u_register(UART_HandleTypeDef* huart)
{

    rt_sem_init(&semaphore, "ec200", 0, RT_IPC_FLAG_PRIO);
    rt_mutex_init(&mutex, "ec200", RT_IPC_FLAG_PRIO);


    ec200dev.hardinterface.power_pin  = POWERPIN;   //电源管脚
    ec200dev.hardinterface.power_port = POWERPORT;
    ec200dev.hardinterface.reset_pin  = RESETPIN;   //复位管脚
    ec200dev.hardinterface.reset_port = RESETPORT;

    ec200dev.hardinterface.uart_dev   = huart;      //串口号

    ec200dev.initialized              = 0;
    ec200dev.name                     = reg.name;
    ec200dev.network_registered       = 0;
    ec200dev.urc_handler              = ec200u_urc_handler;
    ec200dev.paiv_data                = RT_NULL;

    ec200dev.vendor                   = reg.vendor;
    ec200dev.model                    = reg.model;
    ec200dev.powered_on               = 0;

    ec200dev.rx_sem                   = &semaphore;
    ec200dev.lock                     = &mutex;
    ec200dev.paiv_data                = &ec200data;

    return at_device_register(&reg);
}

如果是另一个4G模块,程序写法完全一致:

cpp 复制代码
#include "at_device_manager.h"
#include "at_common.h"
#include "board.h"
#include "com_config.h"

typedef struct{
    at_vendor_t vendor;
    at_model_t  model;
    const char* name;
}ME909SAdapterData;

static ME909SAdapterData me909sdata;
static at_device_t       me909sdev;


static void me909su_urc_handler()
{

}
static rt_err_t me909s_poweron(at_device_t *dev)
{
    return RT_EOK;
}


static rt_err_t me909s_poweroff(at_device_t *dev)
{
   HAL_GPIO_WritePin(dev->hardinterface.power_port, dev->hardinterface.power_pin, GPIO_PIN_RESET);

   return RT_EOK;
}


static rt_err_t me909s_reset(at_device_t *dev)
{
   HAL_GPIO_WritePin(dev->hardinterface.reset_port, dev->hardinterface.reset_pin, GPIO_PIN_SET);
   rt_thread_mdelay(100);
   HAL_GPIO_WritePin(dev->hardinterface.reset_port, dev->hardinterface.reset_pin, GPIO_PIN_RESET);
   rt_thread_mdelay(100);
   return RT_EOK;
}

static rt_err_t me909s_get_model(at_device_t *dev,char* buffer, uint16_t size)
{

    return RT_EOK;
}

static rt_err_t me909s_get_imei(at_device_t *dev,char* buffer)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT_CIMI,&resp);

    if(result == RT_EOK)
    {

    }

    return  result;
}

static rt_err_t me909s_get_iccid(at_device_t *dev,char* buffer, uint16_t size)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT_CCID,&resp);

    if(result == RT_EOK)
    {

    }

    return  result;
}

static rt_err_t me909s_check_sim(at_device_t *dev)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT_CPIN,&resp);

    if(result == RT_EOK)
    {

    }

    return  result;
}

static rt_err_t me909s_check_network(at_device_t *dev,char* buffer, uint16_t size)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT_CCID,&resp);

    if(result == RT_EOK)
    {

    }

    return  result;
}

static rt_err_t me909s_get_signal(at_device_t *dev,signal_strength_t *signal)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    int csq,ber;      //csq : 信号强度    ber:误码率
    result = at_cmd_send_common(dev,CMD_AT_CSQ,&resp);

    if(result == RT_EOK)
    {
        if(sscanf(resp.raw_response,"+CSQ: %d,%d",&csq,&ber))
        {
            if(csq == 0)
            {
                signal->rssi = -115;
            }else if(csq == 1)
            {
                signal->rssi = -111;
            }else if(csq>=2 && csq <= 30)
            {
                signal->rssi = -110 + (csq-2) * 2;
            }else if(csq == 31)
            {
                signal->rssi = -51;
            }else {
                signal->rssi = -113;
            }
            signal->ber  = ber;
        }
    }

    return  result;
}
static rt_err_t me909s_at_test(at_device_t *dev)
{
    rt_err_t result = RT_ERROR;
    at_response_t resp = {0};
    result = at_cmd_send_common(dev,CMD_AT,&resp);
    if(result == RT_EOK && resp.status == AT_RESP_OK)
    {
        return RT_EOK;
    }

    dev->status = resp.status;
    return RT_ERROR;

}

static rt_err_t me909s_init(at_device_t *dev)
{

    rt_err_t result ;
    me909s_poweron(dev);
    me909s_reset(dev);

    result = me909s_at_test(dev);

    if(result != RT_EOK)
    {
        //加入log分析
        return RT_ERROR;
    }

    result = me909s_check_sim(dev);
}


static at_device_ops_t me909s_ops = {
        .init = me909s_init,
        .power_on = me909s_poweron,
        .power_off = me909s_poweroff,
        //其余省略
};
static at_device_register_t reg = {
           .vendor = VENDOR_HUAWEI,
           .model  = MODEL_ME909S,
           .dev    = &me909sdev,
           .name   = MODEL_NAME,
           .ops    = &me909s_ops,
           .priv_data_size = 0
};
static struct rt_semaphore semaphore ;

static struct rt_mutex     mutex;


int at_adapter_quectel_me909su_register(UART_HandleTypeDef* huart)
{

    rt_sem_init(&semaphore, "me909s", 0, RT_IPC_FLAG_PRIO);
    rt_mutex_init(&mutex, "me909s", RT_IPC_FLAG_PRIO);


    me909sdev.hardinterface.power_pin  = POWERPIN;   //电源管脚
    me909sdev.hardinterface.power_port = POWERPORT;
    me909sdev.hardinterface.reset_pin  = RESETPIN;   //复位管脚
    me909sdev.hardinterface.reset_port = RESETPORT;

    me909sdev.hardinterface.uart_dev   = huart;      //串口号

    me909sdev.initialized              = 0;
    me909sdev.name                     = reg.name;
    me909sdev.network_registered       = 0;
    me909sdev.urc_handler              = me909su_urc_handler;
    me909sdev.paiv_data                = RT_NULL;

    me909sdev.vendor                   = reg.vendor;
    me909sdev.model                    = reg.model;
    me909sdev.powered_on               = 0;

    me909sdev.rx_sem                   = &semaphore;
    me909sdev.lock                     = &mutex;
    me909sdev.paiv_data                = &me909sdata;

    return at_device_register(&reg);
}

每增加一个模块就需要单独增加一个模块,按照上述程序的写法,重点在于at_adapter_quectel_xxx_register函数的补充以及at_device_ops_t的补充。这两个结构体的是以具体的通信模组操作来实现的。

为了将上述AT_device和顶层g_managers结合在一起再次引入一个新的注册结构体:

cpp 复制代码
typedef struct{
    at_vendor_t vendor;
    at_model_t  model;
    const char *name;
    at_device_t     *dev;
    at_device_ops_t *ops;
    rt_size_t    priv_data_size;
}at_device_register_t;

通过以下的函数at_device_register将AT_device设备注册到manager里面去

cpp 复制代码
rt_err_t at_device_register(const at_device_register_t *reg)
{
    g_managers.current_dev = reg->dev;
    g_managers.current_dev->ops = reg->ops;
    return RT_EOK;
}

至此上三层已经打通。

在程序读取ICCID号的时候,两个模块读取的方法是使用了同一个函数也就是 at_cmd_send_common(dev,CMD_AT_CCID,&resp);此函数中并没有具体使用AT指令,这是因为对AT指令需要做一个映射来实现具体指向,这种方法可以在统一函数名称的同时实现解耦。

cpp 复制代码
typedef enum{
    CMD_AT,             // AT测试
    CMD_AT_NOECO,       // 关闭回显
    CMD_ATI,            // 查询厂商信息
    CMD_AT_MM,          // 模块型号
    CMD_AT_SN,          // 产品序列号
    CMD_AT_CSQ,         // 信号质量
    CMD_AT_CREG,        // 网络注册状态
    CMD_AT_CGREG,       // GPRS注册状态
    CMD_AT_CEREG,       // EPS注册状态
    CMD_AT_COPS,        // 运营商选择
    CMD_AT_CIMI,        // IMSI查询
    CMD_AT_CCID,        // ICCID查询
    CMD_AT_GSN,         // IMEI查询
    CMD_AT_CPIN,        // SIM卡状态
    CMD_AT_CMGF,        // 短信格式
    CMD_AT_CMGS,        // 发送短信
    CMD_AT_CMGR,        // 读取短信
    CMD_AT_CMGD,        // 删除短信
    CMD_AT_CGDCONT,     // 定义PDP上下文
    CMD_AT_CGACT,       // 激活PDP上下文
    CMD_AT_CGPADDR,     // 获取IP地址
    CMD_AT_CIPSTART,    // 建立TCP/UDP连接
    CMD_AT_CIPSEND,     // 发送数据
    CMD_AT_CIPCLOSE,    // 关闭连接
    CMD_AT_CIPSHUT,     // 关闭所有连接
    CMD_AT_HTTPINIT,    // HTTP初始化
    CMD_AT_HTTPPARA,    // HTTP参数设置
    CMD_AT_HTTPACTION,  // HTTP执行动作
    CMD_AT_HTTPREAD,    // 读取HTTP响应
    CMD_AT_HTTPTERM,    // HTTP终止
    CMD_AT_CGNSINF,     // GPS信息查询
    CMD_AT_CGNSPWR,     // GPS电源控制
    CMD_AT_PSMS,        // 省电模式
    CMD_MAX
} at_cmd_common_t;      //命令集


typedef struct{
    at_cmd_common_t command_cmd; //命令集
    at_vendor_t     vendor;      //厂家
    const char *cmd_string;      //AT指令
    const char *resp_pattern;    //AT指令正常回复
    rt_uint32_t default_timeout; //默认超时时间
    rt_uint32_t default_retries; //默认恢复时间
}at_cmd_mapping_t;               //命令映射结构体

核心的结构体在于at_cmd_mapping_t ,数据存储方式和核心操作函数如下

cpp 复制代码
static const at_cmd_mapping_t g_cmd_mappings[] = {
    // 基础命令
#if ( VENDOR_QUECTEL_ENABLE )
    {CMD_AT,       VENDOR_QUECTEL, "AT\r\n", "OK", 1000, 6},
    {CMD_AT_NOECO, VENDOR_QUECTEL, "ATE0\r\n", "OK", 1000, 3},
    {CMD_AT_MM, VENDOR_QUECTEL, "AT+CGMM\r\n", "OK", 1000, 3},
    {CMD_AT_SN, VENDOR_QUECTEL, "AT+CGSN\r\n", "OK", 1000, 3},
    {CMD_ATI, VENDOR_QUECTEL, "ATI\r\n", "Quectel", 1000, 1},
    {CMD_AT_CSQ, VENDOR_QUECTEL, "AT+CSQ\r\n", "+CSQ:", 2000, 2},     //信号强度
    {CMD_AT_CREG, VENDOR_QUECTEL, "AT+CREG?\r\n", "+CREG:", 2000, 2}, //网络注册状态
    {CMD_AT_CIMI, VENDOR_QUECTEL, "AT+CIMI=?\r\n", "OK", 2000, 2}, //读取IMSI
    {CMD_AT_CPIN, VENDOR_QUECTEL, "AT+CPIN?\r\n", "+CPIN:", 2000, 2},
    {CMD_AT_GSN, VENDOR_QUECTEL, "AT+CGSN\r\n", "+CGSN:", 2000, 1},  //IMEI号
    {CMD_AT_GSN, VENDOR_QUECTEL, "AT+QCCID\r\n", "OK:", 2000, 1},  //ICCID号
    {CMD_AT_CMGF, VENDOR_QUECTEL, "AT+CMGF=1\r\n", "OK", 2000, 2},
    {CMD_AT_CMGS, VENDOR_QUECTEL, "AT+CMGS=\"%s\"", ">", 10000, 2},
    {CMD_AT_CGDCONT, VENDOR_QUECTEL, "AT+CGDCONT=1,\"IP\",\"%s\"", "OK", 3000, 2},
    {CMD_AT_CIPSTART, VENDOR_QUECTEL, "AT+QIOPEN=1,0,\"TCP\",\"%s\",%d,0,1", "+QIOPEN:", 30000, 2},
#endif

#if ( VENDOR_HUAWEI_ENABLE )
    {CMD_AT, VENDOR_HUAWEI, "AT", "OK", 1000, 3},
    {CMD_AT_NOECO, VENDOR_HUAWEI, "ATE0", "OK", 1000, 3},
    {CMD_AT_MM, VENDOR_HUAWEI, "ATE0", "OK", 1000, 3},
    {CMD_AT_MM, VENDOR_HUAWEI, "ATE0", "OK", 1000, 3},
    {CMD_ATI, VENDOR_HUAWEI, "ATI", "Huawei", 1000, 1},
    {CMD_AT_CSQ, VENDOR_HUAWEI, "AT+CSQ", "+CSQ:", 2000, 2},
    {CMD_AT_CREG, VENDOR_HUAWEI, "AT+CREG?", "+CREG:", 2000, 2},
    {CMD_AT_CPIN, VENDOR_HUAWEI, "AT+CPIN?", "+CPIN:", 2000, 2},
    {CMD_AT_GSN, VENDOR_HUAWEI, "AT+CGSN", "+CGSN:", 2000, 1},
    {CMD_AT_CMGF, VENDOR_HUAWEI, "AT+CMGF=1", "OK", 2000, 2},
    {CMD_AT_CMGS, VENDOR_HUAWEI, "AT+CMGS=\"%s\"", ">", 10000, 2},
    {CMD_AT_CGDCONT, VENDOR_HUAWEI, "AT+CGDCONT=1,\"IP\",\"%s\"", "OK", 3000, 2},
    {CMD_AT_CIPSTART, VENDOR_HUAWEI, "AT^SISS=1,\"srvType\",\"Socket\"\r\nAT^SISS=1,\"conId\",\"1\"\r\nAT^SISS=1,\"address\",\"socktcp://%s:%d\"", "OK", 30000, 2},
    {CMD_AT_CIPSTART, VENDOR_ZTE, "AT+ZIPCALL=1,\"TCP\",\"%s\",%d", "+ZIPCALL:", 30000, 2},
#endif

#if (VENDOR_ZTE_ENABLE )
    {CMD_AT, VENDOR_ZTE, "AT", "OK", 1000, 3},
    {CMD_AT_NOECO, VENDOR_ZTE, "ATE0", "OK", 1000, 3},
    {CMD_AT_MM, VENDOR_ZTE, "ATE0", "OK", 1000, 3},
    {CMD_AT_MM, VENDOR_ZTE, "ATE0", "OK", 1000, 3},
    {CMD_ATI, VENDOR_ZTE, "AT+CGMM", "ZTE", 1000, 1},
    {CMD_AT_CSQ, VENDOR_ZTE, "AT+CSQ", "+CSQ:", 2000, 2},
    {CMD_AT_CREG, VENDOR_ZTE, "AT+CREG?", "+CREG:", 2000, 2},
    {CMD_AT_CPIN, VENDOR_ZTE, "AT+CPIN?", "+CPIN:", 2000, 2},
    {CMD_AT_GSN, VENDOR_ZTE, "AT+CGSN", "+CGSN:", 2000, 1},
    {CMD_AT_CMGF, VENDOR_ZTE, "AT+CMGF=1", "OK", 2000, 2},
    {CMD_AT_CMGS, VENDOR_ZTE, "AT+CMGS=\"%s\"", ">", 10000, 2},
    {CMD_AT_CGDCONT, VENDOR_ZTE, "AT+CGDCONT=1,\"IP\",\"%s\"", "OK", 3000, 2},
#endif

};

#define CMD_MAPPING_COUNT  (sizeof(g_cmd_mappings) / sizeof(at_cmd_mapping_t))


const char *at_cmd_map(at_vendor_t vendor, at_cmd_common_t common_cmd)
{
    for (int i = 0; i< CMD_MAPPING_COUNT ;i++)
    {
        if(g_cmd_mappings[i].command_cmd == common_cmd && g_cmd_mappings[i].vendor == vendor)
        {
            return g_cmd_mappings[i].cmd_string;
        }
    }

    switch(common_cmd)
    {
    case CMD_AT:
        return "AT";
        break;
    case CMD_ATI:
        return "ATI";
        break;
    case CMD_AT_CSQ:
        return "AT+CSQ";
        break;
    default:
        return "";
    }
}

const at_cmd_mapping_t* at_cmd_get_mapping(at_vendor_t vendor, at_cmd_common_t common_cmd)
{
    for (int i = 0;i < CMD_MAPPING_COUNT ;i++)
    {
        if( g_cmd_mappings[i].command_cmd == common_cmd && g_cmd_mappings[i].vendor == vendor)
        {
            return &g_cmd_mappings[i];
        }
    }

    return RT_NULL;
}

rt_err_t at_cmd_send_common(at_device_t *dev, at_cmd_common_t cmd,
                           at_response_t *resp, ...) {
    if (dev == RT_NULL) {
        return -RT_EINVAL;
    }

    const at_cmd_mapping_t *mapping = at_cmd_get_mapping(dev->vendor, cmd);
    if (mapping == RT_NULL) {
        return -RT_ERROR;
    }

    // 格式化命令(如果有参数)
    char formatted_cmd[256];
    va_list args;
    va_start(args, resp);

    if (strchr(mapping->cmd_string, '%') != RT_NULL) {
        rt_vsnprintf(formatted_cmd, sizeof(formatted_cmd), mapping->cmd_string, args);
    } else {
        strncpy(formatted_cmd, mapping->cmd_string, sizeof(formatted_cmd));
    }

    va_end(args);

    // 构建命令配置
    at_cmd_config_t config = {
        .cmd = formatted_cmd,
        .time_out = mapping->default_timeout,
        .retry_count = mapping->default_retries,
        .expected_resp = mapping->resp_pattern,
        .is_urc = RT_FALSE,
        .wait_for_resp = RT_TRUE
    };

    // 发送命令
    return at_device_send_cmd_ex(dev, &config, resp);
}

最后则是调用串口发送函数发送AT指令并处理接收数据

cpp 复制代码
rt_err_t at_device_send_cmd_ex(at_device_t *dev, const at_cmd_config_t *config, at_response_t *resp)
{
    rt_err_t result = RT_ERROR;
    rt_tick_t start_tick;

    if(dev == RT_NULL || config->cmd == RT_NULL)
    {
        return -RT_EINVAL;
    }

    rt_mutex_take(dev->lock, RT_WAITING_FOREVER);

    do{
        dev->rx_data_len = 0;

        HAL_UART_Transmit(dev->hardinterface.uart_dev, config->cmd, strlen(config->cmd), 0xff);

        dev->tx_count ++;

        resp->status = AT_RESP_NO_ANSWER;

        if(resp != RT_NULL)
        {
            start_tick = rt_tick_get();

            while(rt_tick_get() - start_tick < rt_tick_from_millisecond(config->time_out))
            {
              if(RT_EOK ==  rt_sem_take(dev->rx_sem, 50))
              {
                  if(dev->rx_data_len > 0)
                  {
                      dev->rx_buffer[dev->rx_data_len] = '\0';
                      break;
                  }
              }
            }





            if(strstr(dev->rx_buffer,resp->raw_response) != RT_NULL)
            {
                resp->status = AT_RESP_OK;
                result = RT_EOK;
            }
            else if(strstr(dev->rx_buffer,"ERROR"))
            {
                resp->status = AT_RESP_ERROR;

            }else if(strstr(dev->rx_buffer,"NO CARRIER"))
            {
                resp->status = AT_RESP_NOCARRIER;

            }else if(strstr(dev->rx_buffer,"busy"))
            {
                resp->status = AT_RESP_BUSY;
            }

        }else
        {
            break;
        }

        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);


    }while((resp->status != AT_RESP_OK) && (dev->tx_count < config->retry_count));


    if(resp != RT_NULL && resp->status == AT_RESP_OK)
    {
        resp->raw_response = dev->rx_buffer;
        resp->response_len = dev->rx_data_len;
        resp->timestamp    = rt_tick_get();
    }
    rt_mutex_release(dev->lock);

    return result;

}
相关推荐
LuDvei2 小时前
STM32串行通信DMA的使用
stm32·单片机·嵌入式硬件
2401_854151553 小时前
STM32 I2C 通信 HAL 库踩坑记录
stm32·单片机·嵌入式硬件
skywalk81633 小时前
设计并实现段言的 C FFI 绑定机制 @Trae
c语言·开发语言·python·编程
蒙塔基的钢蛋儿6 小时前
记一次 STM32H723 硬件 Bug 排查:写个 ETH 寄存器,SDRAM 数据飞了
stm32·嵌入式硬件·bug
额鹅恶饿呃6 小时前
C语言中的数据结构和变量
c语言·数据结构·算法
熊猫_豆豆6 小时前
STM32F103R6 PROTEUS9.0 FREERTOS实现双任务流水灯
stm32·单片机·嵌入式硬件
捷瑞电子工坊10 小时前
C语言条件操作符(? :)——三分钟上手
c语言·条件操作符·编程基础·三目运算符
dozenyaoyida10 小时前
LSTM 自编码器原理详解:从三门结构到 STM32 端侧异常检测落地
人工智能·stm32·lstm
A.零点10 小时前
期末复习,408考研数据结构:第一章绪论完整知识梳理与真题深度解读
c语言·数据结构·笔记·考研