使用MindSDK的at-server组件开发从机模组

news2024/10/7 6:47:38

使用MindSDK的at-server组件开发从机模组

文章目录

  • 使用MindSDK的at-server组件开发从机模组
    • 引言
    • AT命令应用场景
    • AT命令技术简介
    • MindSDK中的at-server组件及样例工程
      • at_port.c
      • at_cmd_led.c
      • main.c
    • 基于AT命令的人机交互应用
      • 使用串口调试助手软件发送AT命令
      • 使用Python脚本发送AT命令
    • 总结

引言

多年之前使用SIM800C模组时接触了AT命令,后来自己设计主从机通信应用场景时,需要以一种最简单方便的方式实现基于UART的控制通信协议,以实现人机交互,就一直想自行实现一套AT命令的主机框架。也找了一些开源的项目,但多少有些不尽如意。在MindSDK的开发过程中,借鉴了RT-Thread对AT组件的实现方式,实现了一套简易的AT Server的框架。AT Server特别适合小资源微控制器,做一些功能模块的接口,或者实现PC机和微控制器的人机交互。

AT命令应用场景

AT指令(Attention Commands)最早是由发明拨号调制解调器(MODEM)的贺氏公司(Hayes)为了控制MODEM而发明的控制协议。随着网络带宽的升级,速度很低的拨号MODEM基本退出了一般的使用市场,但是AT指令保留了下来。当时主要的移动电话生产厂家共同为GSM研制了一整套AT指令,用于控制手机的GSM模块。AT 指令在此基础上演化并加入GSM 07.05标准以及后来的GSM 07.07标准,实现了比较健全的标准化。

在随后的GPRS控制、3G模块等方面,均采用AT指令来控制,AT指令逐渐在产品开发中成为实际的标准。如今,AT指令也广泛应用于嵌入式开发领域,AT指令作为主芯片和通讯模块的协议接口,硬件接口一般为串口,这样主控设备可以通过简单的指令和硬件设计完成多种操作。

在嵌入式开发中,经常是使用AT命令去控制各种通讯模块,比如ESP8266 WIFI模块、4G模块、GPRS模块等等。一般就是主芯片通过硬件接口(比如串口、SPI)发送AT命令给通讯模块,模块接收到数据之后回应响应的数据。

AT命令技术简介

AT命令通信系统由AT客户端(Client)和AT服务器(Server)组成,如图x所示。

在这里插入图片描述

图x AT通信系统中的客户端和服务器

AT客户端通常是主控芯片,AT服务器通常是各种通讯模组。

AT命令由三个部分组成:

  • 前缀。AT两个字符。
  • 主体。命令和参数。
  • 结束符。一般为 <CR><LF> (“\r\n”)。
  • 例:AT+CWMODE=3\r\n

AT Server 返回给 AT Client 的数据有两种:

  • 命令响应数据。回应响应的状态和信息
  • URC 数据(unsolicited result code)。AT Server 主动发送给 AT Client 的数据。
    • 例1: AT Server接收到网络的数据后,会主动把这些数据发送给 AT Client。
    • 例2: WIFI 断开连接等,会主动发数据告知 AT Client。

MindSDK中的at-server组件及样例工程

MindSDK(https://mindsdk.mindmotion.com.cn/)是灵动微电子官方发布的适用于灵动微控制器的软件开发和发布平台,其中的at-server组件实现了AT Server的功能,可对AT命令的进行解析,并使用开发者注册到对应命令格式上的函数执行相关操作。开发者可以在MindSDK在线平台上下载任意板子上包含AT组件的样例工程(例如FTHR-F0140板上的at_basic),从而获得MindSDK的at-server组件的源码。

开发者在使用at-server组件时,需要为每个命令的不用子命令注册自定义的回调函数。每个AT命令<x>可以对应4个子命令:

命令功能说明
AT+=?测试查询参数格式及取值范围
AT+?查询返回命令参数值
AT+=…设置用户指定参数值
AT+执行执行命令

例如:可设计“LED”命令,绑定对电路板上一个小灯的控制

  • AT+LED 初始化LED灯。
  • AT+LED?查看当前LED灯状态。
  • AT+LED=1或者AT+LED=0 设置LED状态为亮或者暗。

MindSDK中为几乎所有的开发板适配了at_basic样例工程,这个工程包含了at_server组件,并展示了在实现的AT Server中添加一系列控制LED小灯的命令。

FTHR-F0140开发板为例,其at_basic工程的组织结构,如图x所示。

在这里插入图片描述

图x at-server工程组织

除了MindSDK中提供的at_server组件的源码文件at_server.hat_server.c文件,还有一些适配源码。

at_port.c

at_port.c文件中的源代码将at-server绑定到具体平台的硬件上,在本例中,使用MM32F0140微控制器的UART2作为传输AT命令的端口。其中,就是要实例化一个at_adapter_t类型的结构体对象at_adapter,并向其中注册操作硬件通信引擎的函数字段。

/*
 * Macros.
 */
#define BOARD_AT_BUF_SIZE   64u  /* the at server receive buffer size. */

/*
 * Variables.
 */

static char     at_adapter_buf[BOARD_AT_BUF_SIZE]; /* the buffer for at server. */

/*
 * Declerations.
 */

static void     at_init(void);
static uint32_t at_read(char * buf, uint32_t len);
static void     at_write(char * buf, uint32_t len);

/* initialize the at adapter. */
const at_adapter_t at_adapter =
{
    .init       = at_init, /* initialize the uart hardware. */
    .read       = at_read, /* read charactors from uart. */
    .write      = at_write, /* write charactors into uart. */
    .buf        = at_adapter_buf,
    .buf_size   = BOARD_AT_BUF_SIZE,
};

此处,.buf指定的缓冲区,将存放从UART总线上捕获到的字符串,at-server解析其中的内容,识别命令,并调用开发者注册到对应子命令的回调函数。

初始化函数.init和发送函数.write均绑定到硬件的UART端口。

/* init func. */
static void at_init(void)
{
    /* init rbuf. */
    rbuf_init(&rbuf_handler, rbuf_buf, sizeof(rbuf_buf) );
    /* init at uart port. */
    UART_Init_Type uart_init;
    uart_init.ClockFreqHz   = BOARD_AT_UART_FREQ;
    uart_init.BaudRate      = BOARD_AT_UART_BAUDRATE;
    uart_init.WordLength    = UART_WordLength_8b;
    uart_init.StopBits      = UART_StopBits_1;
    uart_init.Parity        = UART_Parity_None;
    uart_init.XferMode      = UART_XferMode_RxTx;
    uart_init.HwFlowControl = UART_HwFlowControl_None;
    uart_init.XferSignal    = UART_XferSignal_Normal;
    uart_init.EnableSwapTxRxXferSignal = false;
    UART_Init(BOARD_AT_UART_PORT, &uart_init);
    UART_Enable(BOARD_AT_UART_PORT, true);
    /* enable rx interrupt. */
    UART_EnableInterrupts(BOARD_AT_UART_PORT, UART_INT_RX_DONE, true);
    NVIC_EnableIRQ(BOARD_AT_UART_IRQn);
}

/* write data by uart. */
static void at_write(char * buf, uint32_t len)
{
    for (uint32_t i = 0u; i < len; i++)
    {
        while ( 0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(BOARD_AT_UART_PORT)) )
        {}
        UART_PutData(BOARD_AT_UART_PORT,buf[i]);
    }
}

在本例中,使用中断方式实现从UART接收数据的过程,,将at_read()函数注册到at_adapter.read字段中。这里借用了MindSDK中rbuf组件实现FIFO队列,建立UART接收中断服务程序和接口函数之间的数据管道。

#include "rbuf.h"

static uint8_t  rbuf_buf[BOARD_AT_BUF_SIZE]; /* the buffer for rbuf. */
static rbuf_handler_t rbuf_handler; /* rbuf handler. */

/* read data from rbuf. */
static uint32_t at_read(char * buf, uint32_t len)
{
    uint32_t read_cnt = 0u;
    for (; read_cnt < len; read_cnt++)
    {
        if (rbuf_is_empty(&rbuf_handler) )
        {
            break;
        }
        buf[read_cnt] = rbuf_output(&rbuf_handler);
    }
    return read_cnt;
}

/* the interrupt handler of uart. */
void BOARD_AT_UART_IRQHandler(void)
{
    uint32_t flag = UART_GetInterruptStatus(BOARD_AT_UART_PORT);

    if ( 0u!= (UART_INT_RX_DONE & flag) )
    {
        /* get byte. */
        uint8_t data = UART_GetData(BOARD_AT_UART_PORT);

        /* in queue. */
        rbuf_input(&rbuf_handler, data);
    }
    UART_ClearInterruptStatus(BOARD_AT_UART_PORT, flag);
}

at_cmd_led.c

at_cmd_led.c文件中,创建了AT+LED命令组,实例化一个at_cmd_t类型的对象at_cmd_led,以及其中的函数字段。其中的函数字段就对应了该命令的不同子命令的具体执行内容。

/* to query the parameter format and value range for AT+LED command. */
at_result_t at_cmd_led_test(at_server_write_t write);

/* to return command parameters. */
at_result_t at_cmd_led_query(at_server_write_t write);

/* to set user-specified parameters into the corresponding function. */
at_result_t at_cmd_led_setup(at_server_write_t write, char * args);

/* to perform related operations. */
at_result_t at_cmd_led_exec(at_server_write_t write);

const at_cmd_t at_cmd_led =
{
    .name = "AT+LED",
    .args_expr = "<value>",
    .test  = at_cmd_led_test,  /* AT+LED=?    */
    .query = at_cmd_led_query, /* AT+LED?     */
    .setup = at_cmd_led_setup, /* AT+LED=1, 0 */
    .exec  = at_cmd_led_exec   /* AT+LED      */
};

其中,注册到.exec字段的at_cmd_led_exec()函数将在本机(AT Server)捕获到AT+LED命令时被调用,本例中,将其实现为初始化控制LED灯对应的GPIO引脚。而注册到.setup字段的at_cmd_led_setup()函数,将在本机捕获到命令字符串AT+LED=0或者AT+LED=1时被调用,at-server组件将根据.args_expr中指定的参数格式解析传入的参数格式,用01对应控制小灯的灭和亮的状态。

/* when sending AT+LED=<value>, the value can be 0 or 1 or other, will call this function. */
at_result_t at_cmd_led_setup(at_server_write_t write, char * args)
{
    char buf[AT_CMD_BUF_SIZE];
    uint32_t param; /* the command parameter. */

    if (1u == at_req_parse_args(args, "=%d", &param))
    {
        if (param == 1)
        {
            GPIO_WriteBit(BOARD_LED0_GPIO_PORT, BOARD_LED0_GPIO_PIN, 1u);
        }
        else if (param == 0)
        {
            GPIO_WriteBit(BOARD_LED0_GPIO_PORT, BOARD_LED0_GPIO_PIN, 0u);
        }
    }
    else
    {
        sprintf(buf, "ERROR \r\n");
    }
    write(buf, strlen(buf));

    return AT_RESULT_OK;
}

/* when sending AT+LED command will call this function. to exectue led initialization. */
at_result_t at_cmd_led_exec(at_server_write_t write)
{
    char buf[AT_CMD_BUF_SIZE];

    /* LED initialize. */
    GPIO_Init_Type gpio_init;
    gpio_init.Pins  = BOARD_LED0_GPIO_PIN;
    gpio_init.PinMode  = GPIO_PinMode_Out_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(BOARD_LED0_GPIO_PORT, &gpio_init);
    GPIO_WriteBit(BOARD_LED0_GPIO_PORT, BOARD_LED0_GPIO_PIN, 1u);
    sprintf(buf, "OK\r\n");
    write(buf, strlen(buf));

    return AT_RESULT_OK;
}

main.c

at_port.c文件中将at-server组件绑定到MM32F0140硬件平台、在at_cmd_led.c文件中创建了一组自定义的AT+LED命令后,最后需要在main.c文件中为运行at-server组件创建执行线程,调用at-server的服务。

在本例工程中的main.c文件中,有代码如下:

#include "board_init.h"
#include "at_server.h"

/*
 * Declerations.
 */
extern const at_cmd_t at_cmd_at;
extern const at_cmd_t at_cmd_led;
/*
 * Variables.
 */
/* set the at command tables. */
const at_cmd_t * at_cmd_list[]=
{
    &at_cmd_at,
    &at_cmd_led
};
extern const at_adapter_t at_adapter;
at_server_t at_server_local;

/*
 * Functions.
 */
int main(void)
{
    BOARD_Init();

    /* at server initialization. */
    at_server_init(&at_server_local,(at_adapter_t *)&at_adapter);
    /* set the at command list for at server. */
    at_server_set_cmd_list( &at_server_local, (at_cmd_t **)at_cmd_list, sizeof(at_cmd_list)/sizeof(* at_cmd_list));

    while (1)
    {
        at_server_task(&at_server_local); /* loop the at server task. */
    }
}

/* EOF. */

main()函数中:

  • 首先调用at_server_init()函数,初始化一个AT Server,并传入at_adapter绑定硬件平台。
  • 调用at_server_set_cmd_list()函数中,向AT Server传入命令集at_cmd_list
  • while(1)循环中调用at_server_task()函数,解析从硬件通信通道捕获到的包含AT命令的字符串。

至此,编译整个工程,下载可执行文件到芯片中,就可以把AT命令用起来了。

基于AT命令的人机交互应用

实现AT Server的意义在于能够实现主从机的通信,但如果暂时没有微控制器作为主机,也可以直接使用PC机同AT Server小模组进行通信,实现人机交互。

这里演示两种使用PC作为主机的玩法:使用串口调试软件工具和Python脚本。其中,使用串口调试软件工具可以快速验证功能,而使用Python就更有趣了,可以在PC上运行算法(例如通过USB摄像头捕获图像数据,并使用一些重量级的工具进行图像处理和模式识别),然后连接微控制器的小板子上控制电气系统(例如控制一组舵机完成某些机械动作)。

本例实现的AT Server,使用两种不同的方法,都能实现接收到主机通过UART串口发送的AT+LED命令,控制小灯闪烁。如图x所示。

在这里插入图片描述

图x PC机通过AT命令控制FTHR-F0140电路板上的小灯闪烁

使用串口调试助手软件发送AT命令

用户可以下载SSCOM软件(http://www.sscom.vip/),支持预定义的AT命令。运行本例中的工程,如图x所示。

在这里插入图片描述

图x 使用SSCOM工具发送AT命令

使用Python脚本发送AT命令

用户可以编写Python脚本,使用PySerial类模块,使用UART串口通信。可编写如下Python脚本,发送AT+LED命令,控制小灯闪烁。

import serial
import time

# connect the UART
ser = serial.Serial()
ser.port='COM9'
ser.baudrate=9600
ser.bytesize=8
ser.stopbits=1
ser.parity='N'
ser.open()

if (ser.isOpen()):
    print('succ')
else:
    print('fail')

# init the led.
ser.write('AT+LED\r\n'.encode('utf-8'))

# control the led.
for i in range(10):
    time.sleep(0.2)
    ser.write('AT+LED=1\r\n'.encode('utf-8'))
    print('led on')

    time.sleep(0.2)
    ser.write('AT+LED=0\r\n'.encode('utf-8'))
    print('led off')

print('done.')

ser.close()

总结

MindSDK中的at-server组件,应用逻辑还是相当清晰的,抽象出了绑定硬件的函数对象、命令集等,自定义命令和注册回调函数也非常方便。at-server对于小资源微控制器来说,可是个福音,在基于主从机交互的应用环境中,将微控制器作为一个仅连接电路系统的控制器,解析并执行来自主机的命令,将大算力和大存储需求的算法和应用逻辑转移到主机(PC机)上。如此以来,不通硬件和不愿意看微控制器开发手册的Python算法工程师,也可以试着让自己的程序控制电路啦。

样例工程 fthr-f0140_at_basic_mdk.zip的下载页面:https://download.csdn.net/download/suyong_yq/87764732

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/502650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java高阶数据结构 并查集 最小生成树

并查集与最小生成树 文章目录 Java高阶数据结构 & 并查集 & 最小生成树1. 并查集1.1 并查集的原理1.1.1 例子&#xff1a;1.1.2 这样存储有什么好处呢&#xff1f; 1.2 并查集的代码实现1.2.1 类的定义与属性1.2.2 构造方法1.2.3 获取下标的方法1.2.4 获得根节点1.2.5 …

1.SpringCloud技术实用02

SpringCloud技术实用02 0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。…

ES+Redis+MySQL 高可用架构设计

一、背景 二、ES高可用方案 三、会员Redis缓存方案 四、高可用会员主库方案 五、异常会员关系治理 六、展望&#xff1a;更精细化的流控和降级策略 一、背景 会员系统是一种基础系统&#xff0c;跟公司所有业务线的下单主流程密切相关。如果会员系统出故障&#xff0c;会…

网络安全信息收集初探之域名信息收集

网络安全信息收集初探之域名信息收集 域名信息收集工具oneforall收集子域名扫描单个域名批量扫描域名oneforall 额外参数 google hacking 证书收集子域名证书子域名在线收集网站子域名收集的各种细节 域名信息收集工具 oneforall收集子域名 扫描单个域名 python oneforall.p…

进阶自动化测试,这3点你一定要知道的...

自动化测试指软件测试的自动化&#xff0c;在预设状态下运行应用程序或系统&#xff0c;预设条件包括正常和异常&#xff0c;最后评估运行结果。将人为驱动的测试行为转化为机器执行的过程。 自动化测试框架一般可以分为两个层次&#xff0c;上层是管理整个自动化测试的开发&a…

云渲染农场具有什么特点?

众所周知&#xff0c;渲染农场的出现是为了解决长时间的图像渲染问题。渲染农场的底层搭建原理是利用很多计算机、网络和操作系统来构建一个庞大的计算群组&#xff0c;把一个渲染任务从一台机器分发到这个计算群组&#xff0c;从而达到短时间内能够快速得到渲染结果。 到了20…

分布式搜索引擎es 面试突击

es elastocsearch 倒排索引是在数据查询之前建立&#xff0c;在查询的时候可以直接通过关键词定位到文档内容。用空间换时间 分布式架构原理说一下&#xff1f; es底层是基于lucene来的 大概就是一个用于全文检索的jar包 用es来做分布式的搜索引擎 可以承载一秒钟几千的…

【Vue工程】005-Vue Router

【Vue工程】005-Vue Router 文章目录 【Vue工程】005-Vue Router一、概述1、Slogan2、官网3、参考文章 二、安装三、基本使用1、定义路由2、创建路由实例3、在 main.ts 注册路由4、在 App.vue 定义路由出口 四、嵌套路由1、修改路由2、定义嵌套路由出口 五、配置404页面六、声明…

联合索引该如何选择合适的列?

前面一篇文章&#xff0c;松哥和大家聊了 MySQL 中的索引合并&#xff0c;虽然 MySQL 提供了索引合并机制来提升 SQL 执行的效率&#xff0c;然而在具体实践中&#xff0c;如果能避免发生索引合并是最好的&#xff0c;毕竟这是没办法的办法&#xff0c;是一个下下策。发生索引合…

Wikidata 模型分析+实体抽取+数据处理

Wikidata 数据分析与处理 需求&#xff1a;Wikidata 数据描述了很多实体&#xff0c;以及实体属性。比如某一个公司/组织/机构名称是&#xff1a;阿里巴巴&#xff0c;对数据内该组织的相关属性进行观察、分析、治理、抽取等&#xff0c;最后用图数据库进行存储和展示其关系&am…

为什么半导体FAB生产线需要EAP系统?

在半导体制造中&#xff0c;设备自动化系统EAP&#xff08;Equipment Automation Program&#xff09;是不可或缺的重要软件&#xff0c;它是连接MES、RMS、APC、FDC等上层系统和设备层的桥梁&#xff0c;用于管控生产线上的所有机台&#xff0c;并实现设备运行的自动化。 作为…

QT+OpenGL高级数据和高级GLSL

QTOpenGL高级数据和高级GLSL 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 高级数据 OpenGL中的缓冲区 对象管理特定的GPU内存 在将缓冲区绑定到特定的缓冲区目标时候赋予它意义 OpenGL在内部会保…

项目环境配置、不知晓问题自己搜索后得到的解答

目录 Anolis OS龙蜥操作系统 Kernel Selection 4.18.0(RHCK) Compatible with RHEL (kernel-4.18.0) 4.19.91(ANCK) Support Anolis OS verified platform (kernel-4.19.91) 这两个内核选择哪个比较好呢&#xff1f; 我的C盘有些满&#xff0c;我该如何删除一些我需要的东西…

docker网络访问和端口映射

docker网络访问和端口映射 文章目录 docker网络访问和端口映射1.docker容器网络1.1.创建一个centos7的容器1.2.docker网络原理图 2.端口映射2.1.创建一个新的IP2.2.多个IP端口映射2.3.随机端口命令 1.docker容器网络 指定映射&#xff08;docker 会 自动添加一条iptables规则来…

wisp5.0 学习日记2

学习日记 昨天的报错尝试1&#xff0c;在CCS中设置USB FET尝试2 csdn解决方案1尝试3 查看仿真器的驱动是否安装成功 昨天的报错 MSP430: Error initializing emulator: No USB FET was found 尝试1&#xff0c;在CCS中设置USB FET 打开CCS&#xff0c;选择“Window” -> …

在线文档编辑工具哪个更好?

在线文档编辑工具相当于一个轻量级、跨平台、多途径的Office。使用在线文档编辑工具&#xff0c;首先我们不用安装Office软件&#xff1b;其次在电脑网页上、手机小程序里我们都可以使用在线文档进行简单的编辑&#xff1b;最后我们编辑的文档可以实时更新、分享、协作等。今天…

供应商标准化管理难?云时通助力国药器械成功打造医疗器械行业SRM管理平台!

中国医疗器械有限公司(CMDC,简称“国药器械”)&#xff0c;始建于1966年&#xff0c;隶属于国药集团&#xff0c;是其医疗器械板块的主力军。国药器械有分子公司300家左右&#xff0c;年销售额300多亿&#xff0c;国内最大的医疗器械商业流通企业&#xff0c;产品覆盖医疗器械所…

软件测试简历?面试题?企业面试官想要什么?我不再和offer失之交臂...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 软件测试面试题简历…

vivado跨时钟域路径分析

若要查看跨时钟域路径分析报告&#xff0c;可选择以下内容之一来查看&#xff1a; A, Reports > Timing > Report Clock Interaction B, Flow Navigator > Synthesis > Report Clock Interaction C, Flow Navigator > Implementation > Report Clock Inte…

【网络安全】--win提权

win提权 提权目的提权常用命令提权实战常见的payload利用1. 安装虚拟机win2008和kali2. 创建普通用户3. 切换用户4. kali生成木马并发送到被攻击服务器上5. 被攻击方运行生成的木马文件7. 查看可利用漏洞8. 尝试利用exp提权 at/sc/ps命令提权at命令提权sc命令提权ps命令提权 提…