[ARM入门] 一个AT命令通信解析模块

[复制链接]
2509|10
 楼主| 我喜欢打游戏 发表于 2022-6-2 11:10 | 显示全部楼层 |阅读模式
介绍:
一种AT命令通信解析模块,支持裸机(at_chat)和OS版本(at)。适用于modem、WIFI模块、蓝牙通信。

评论

确实如此,AT命令的解析模块还是作用很大的  发表于 2022-6-13 11:05
AT指令在无线模块中应用非常广泛,可让开发者无需关注底层实现,直接一键连接模块。这个AT指令解析模块可以说非常好用了,感谢分享。  发表于 2022-6-13 10:59
 楼主| 我喜欢打游戏 发表于 2022-6-2 11:10 | 显示全部楼层
软件架构:
  • at_chat.c at_chat.h list.h

用于无OS版本,使用链式队列及异步回调方式处理AT命令收发,支持URC处理、自定义命令发送与解析作业。

  • at.c at.h at_util.h  comdef.h

用于OS版本, 使用前需要根据at_util.h规定的操作系统相关的接口进行移植,如提供信号量操作、任务延时等操作。


 楼主| 我喜欢打游戏 发表于 2022-6-2 11:11 | 显示全部楼层
使用说明:
at_chat 模块(无OS)基本概念

at_chat 模块使用链式队列进行管理,包含2条链表,空闲链表和就绪链表。它们的每一个基本工作单元称为一个作业项,对于将要执行的命令都会放到就绪链表中,命令执行完成之后由空闲链表来进行回收,作业项的定义如下:

  1. /*AT作业项*/
  2. typedef struct {
  3.     unsigned int  state : 3;
  4.     unsigned int  type  : 3;                                 /* 作业类型*/
  5.     unsigned int  abort : 1;
  6.     void          *param;                                    /* 通用参数*/
  7. void          *info;                                     /* 通用信息指针*/
  8.     struct list_head node;                                   /* 链表结点*/
  9. }at_item_t;

作业是AT控制器定义时固定分配的,没有使用动态内存,默认支持10个作业项,即同时可以允许10个AT命令排队等待处理。

基本接口与描述
  • at_send_singlline, 发送单行命令,默认等待OK响应,超时3S
  • at_send_multiline, 多行命令,默认等待OK响应,超时3S
  • at_do_cmd,支持自定义发送格式与接收匹配串
  • at_do_work,支持自定义发送与接收解析
效果演示

详细使用可以参考Demo程序wifi_task.c模块


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 我喜欢打游戏 发表于 2022-6-2 11:13 | 显示全部楼层
使用步骤

1.定义AT控制器及通信适配器接口

  1. /*
  2. * [url=home.php?mod=space&uid=247401]@brief[/url]   定义AT控制器
  3. */
  4. static at_obj_t at;

  5. const at_adapter_t adap = {  //AT适配器接口
  6. //适配GPRS模块的串口读写接口
  7. .write       = uart_write,
  8. .read        = uart_read
  9. ...
  10. };
  • 初始化AT控制器并放入任务中轮询(考虑到处理实时性,建议20ms以下)
    1. /*
    2. * @brief    wifi初始化
    3. */
    4. void wifi_init(void)
    5. {
    6. at_obj_init(&at, &adap);
    7. /*...*/
    8. }driver_init("wifi", wifi_init);

    9. /*
    10. * @brief    wifi任务(10ms 轮询1次)
    11. */
    12. void wifi_task(void)
    13. {
    14.     at_poll_task(&at);
    15. }task_register("wifi", wifi_task, 10);
    例子演示
    1. //WIFI IO配置命令
    2. => AT+GPIO_TEST_EN=1\r\n

    3. <= OK\r\n
    1. /**
    2. * @brief AT执行回调处理程序
    3. */
    4. static void test_gpio_callback(at_response_t *r)
    5. {
    6. if (r->ret == AT_RET_OK ) {
    7.      printf("Execute successfully\r\n");
    8. } else {
    9.      printf("Execute failure\r\n");
    10. }
    11. }
    12. at_send_singlline(&at, test_gpio_callback, "AT+GPIO_TEST_EN=1");



 楼主| 我喜欢打游戏 发表于 2022-6-2 11:14 | 显示全部楼层
at 模块(OS版本)

由于AT命令通信是一个比较复杂的过程,对于没有OS的环境下处理难度比较大,也很绕,对于不允许阻塞程序,除了使用状态与+回调没有其它更好的办法,所以推荐使用这个模块

基本接口与描述
  • at_do_cmd,执行AT命令,可以通过这个接口进一步封装出一常用的单行命令、多行命令。
  • at_split_respond_lines,命令响应分割器。
  • at_do_work,适用于发送组合命令,如GPRS模组发送短信或者发送socket数据需要等待"<"或者"CONNECT"提示符,可以通过这个接口自定义收发。

 楼主| 我喜欢打游戏 发表于 2022-6-2 11:14 | 显示全部楼层
使用步骤

1.定义AT控制器、通信适配器接口(包含URC回调函数表,接口缓冲区URC)

  1. static at_obj_t at;          //定义AT控制器对象

  2. static char urc_buf[128];    //URC主动上报缓冲区

  3. utc_item_t utc_tbl[] = {     //定义URC表
  4. "+CSQ: ", csq_updated_handler
  5. }

  6. const at_adapter_t adap = {  //AT适配器接口
  7. .urc_buf     = urc_buf,
  8. .urc_bufsize = sizeof(urc_buf),
  9. .utc_tbl     = utc_tbl,
  10. .urc_tbl_count = sizeof(utc_tbl) / sizeof(utc_item_t),
  11. //debug调试接口
  12. .debug       = at_debug,
  13. //适配GPRS模块的串口读写接口
  14. .write       = uart_write,
  15. .read        = uart_read
  16. };

2.创建AT控制器并创建轮询处理线程

  1. void at_thread(void)
  2. {
  3. at_obj_create(&at, &adap);
  4.     while (1) {        
  5.         at_process(&at);
  6.     }
  7. }


 楼主| 我喜欢打游戏 发表于 2022-6-2 11:15 | 显示全部楼层
例子演示例子1(查询无线模组信号质量)
  1. /** at_do_cmd 接口使用演示
  2.     查询GPRS模组信号质量命令
  3. => AT+CSQ

  4. <= +CSQ: 24, 0
  5. <= OK
  6. */

  7. /*
  8. * @brief    获取csq值
  9. */
  10. bool read_csq_value(at_obj_t *at, int *rssi, int *error_rate)
  11. {
  12. //接收缓冲区
  13. unsigned char recvbuf[32];
  14. //AT应答
  15. at_respond_t r = {"OK", recvbuf, sizeof(recvbuf), 3000};
  16. //
  17. if (at_do_cmd(at, &r, "AT+CSQ") != AT_RET_OK)
  18.   return false;
  19. //提取出响应数据
  20. return (sscanf(recv, "%*[^+]+CSQ: %d,%d", rssi, error_rate) == 2);

  21. }
例子2(发送TCP数据)
  1. /** at_do_work 接口使用演示
  2.     参考自hl8518模组Socket 数据发送命令
  3. => AT+KTCPSND=<session_id>,<ndata>

  4. <= CONNECT

  5. => <data>

  6. <= OK
  7. */

  8. /*
  9. * @brief       数据发送处理
  10. * @retval      none
  11. */
  12. static bool socket_send_handler(at_work_ctx_t *e)
  13. {
  14.     struct socket_info *i = (struct socket_info *)e->params;
  15.     struct ril_sock *s    = i->s;
  16.    
  17.     if (s->type == SOCK_TYPE_TCP)
  18.         e->printf(e, "AT+KTCPSND=%d,%d", s->session, i->bufsize);
  19.     else
  20.         e->printf(e, "AT+KUDPSND=%d,%s,%d,%d",s->session, s->host,
  21.         s->port, i->bufsize);        

  22.     if (e->wait_resp(e, "CONNECT", 5000) != AT_RET_OK) {      //等待提示符
  23.        goto Error;
  24.     }         
  25.     e->write(i->buf, i->bufsize);                             //发送数据
  26.    
  27.     e->write("--EOF--Pattern--", strlen("--EOF--Pattern--")); //发送结束符

  28.     if (e->wait_resp(e, "OK", 5000) == AT_RET_OK)
  29.         return true;
  30.     else {   
  31. Error:
  32.         e->write("--EOF--Pattern--", strlen("--EOF--Pattern--"));
  33.         return false;
  34.     }
  35. }

  36. /**
  37. * @brief       socket 数据发送
  38. * @param[in]   s   - socket
  39. * @param[in]   buf - 数据缓冲区
  40. * @param[in]   len - 缓冲区长度
  41. */
  42. static bool hl8518_sock_send(ril_obj_t *r, struct ril_sock *s, const void *buf,
  43.                        unsigned int len)
  44. {
  45.     struct socket_info info = {s, (unsigned char *)buf, len, 0};
  46.     if (len == 0)
  47.         return false;      
  48.     return at_do_work(&r->at, (at_work)socket_send_handler, &info);
  49. }




micoccd 发表于 2022-6-13 11:07 | 显示全部楼层
确实如此,AT命令的解析模块还是作用很大的
isbit 发表于 2023-11-30 10:21 | 显示全部楼层
有例程吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

80

主题

626

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部