核心思想
采用__attribute__((used)) attribute((section (“atcmd”)))的形式
优点:
可以自由变换输出通道,实现三方通讯。
注册AT指令,只用写到模块内部,不用耦合其他文件
下面就是举例使用方法:test.c
#include "atcmd_slave.h"
#if ATCMD_EN
// 在功能模块中定义一个标准函数
static int test(atcmd_pack_t *pack) {
uint8_t buff[20] = "testrn";
strcat((char*)buff, AT_OK);
pack->reply(buff, strlen((char*)buff));
return 0;
}
static int test2(atcmd_pack_t *pack) {
if (pack->argc != 2) return -1;
uint8_t buff[20] = "";
uint8_t num = 0, num1 = 0;
sscanf((char*)(pack->data), "%c,%c", &num, &num1);
snprintf((char*)buff, 20, "%d,%d"AT_OK, num, num1);
pack->reply(buff, strlen((char*)buff));
return 1;
}
// 注册AT指令,传入标准函数
ATCMD_INIT("AT^TEST?", test);
ATCMD_INIT("AT^TEST=", test2);
#endif
用串口举例
没有使用DMA和中断,仅提供思路。
uint8_t buff[255] = {0};
uint8_t len = 0;
void uart_rx_cb(uint8_t data) {
buff[len] = data;
len++;
if (len == 1 && buff[0] != 'A')
len = 0;
else if (len == 2 && buff[1] != 'T')
len = 0;
else if (len == 3 && buff[2] != '^')
len = 0;
else if (buff[len] == 'n' && buff[len - 1] == 'r') {
atcmd_pack_t pack;
pack.reply = uart_send;
pack.data = data;
pack.len = len;
atcmd_msg_handle(&pack);
}
}
如果使用DMA和中断,直接:
void uart_rx_cb(uint8_t *data, uint16_t len) {
atcmd_pack_t pack;
pack.reply = uart_send;
pack.data = data;
pack.len = len;
atcmd_msg_handle(&pack);
}
代码:
/********************************************************************************
* @file atcmd_slave.c
* @author jianqiang.xue
* @Version V1.0.0
* @Date 2022-09-04
* @brief 从机版 AT指令 https://lisun.blog.csdn.net/article/details/126683930
********************************************************************************/
#include #include #include #include #include "atcmd_slave.h" extern atcmd_info_t __atcmd_start; extern atcmd_info_t __atcmd_end; bool atcmd_msg_handle(atcmd_pack_t* pack) { bool match = false; atcmd_info_t* atcmd; for (atcmd = &__atcmd_start; atcmd < &__atcmd_end; atcmd++) { if (atcmd->name != (char *)0xFFFFFFFF) { // 针对FLASH默认值为0xFFFFFFFFF时,不进行比较 if (strncmp((char*)(pack->data), atcmd->name, strlen(atcmd->name)) == 0) { match = true; break; } } } if (match) { // 裁剪 AT^XXXX=(保留)rn char* p = NULL; p = strrchr((char *)pack->data, '='); if (p != NULL) { pack->len = p - (char *)(pack->data) - 3; // 3 == rn + '=' pack->data = (uint8_t *)p + 1; // 移除=号前面的内容,并包含'=' pack->data[pack->len + 1] = 'n'; // 计算参数个数 pack->argc = 0; for (uint16_t i = 0; i < pack->len; i++) { if (pack->data[i] == ',') pack->argc++; } atcmd->callback(pack); return 0; } else { p = strrchr((char *)pack->data, '?'); if (p == NULL) // 没有[ '=' or '?' ],则不是标准ATCMD,直接返回错误 return -1; atcmd->callback(pack); return 0; } } return -1; } /******************************************************************************** * @file atcmd_slave.h * @author jianqiang.xue * @Version V1.0.0 * @Date 2022-09-04 * @brief 从机版 AT指令 https://lisun.blog.csdn.net/article/details/126683930 ********************************************************************************/ #ifndef __ATCMD_SLAVE_H #define __ATCMD_SLAVE_H #include #define AT_OK "rnOKrn" #define AT_ERROR "rnERRORrn" typedef struct{ uint16_t argc; // 参数个数 uint16_t len; // 参数长度 uint8_t *data; // 完整参数字符串以/0结尾 /* 传入回复函数,比如uart0_send,或者ble_send,用于发送不同通道 */ void (*reply)(uint8_t *data, uint16_t len); } atcmd_pack_t; typedef struct{ char *name; int (*callback)(atcmd_pack_t *pack); } atcmd_info_t; #define ATCMD_INIT(name, callback) static const atcmd_info_t __atcmd_##callback __attribute__((used)) __attribute__((section(".atcmd."))) = {name, callback} // 收到数据"AT^",且末尾为"rn",就传入给改函数。 bool atcmd_msg_handle(atcmd_pack_t *pack); #endif 注意:如果使用gcc或iar,则需要修改lds文件,添加atcmd段。 GCC(.lds): /* Define output sections */ SECTIONS { ....省略.... .atcmd. : { __atcmd_start = .; KEEP (*(SORT(.atcmd.*))) __atcmd_end = .; } >FLASH ....省略.... .ARM.attributes 0 : { *(.ARM.attributes) } } IAR(.icf) (带协议栈版本) define region FLASH_LAST_PAGE = mem:[from(FLASH_SIZE) - PAGE_SIZE to FLASH_SIZE-1]; define region FLASH_ATCMD_PAGE = mem:[from(FLASH_SIZE) - PAGE_SIZE*2 to FLASH_SIZE-PAGE_SIZE]; define region FLASH = mem:[from FLASH_START to FLASH_END]; define region FLASH_ALL = mem:[from FLASH_START to FLASH_END] | FLASH_LAST_PAGE | FLASH_ATCMD_PAGE; // Memory Placement **** // ATCMD place at end of FLASH_ATCMD_PAGE { readonly section .atcmd. }; keep { section .atcmd. }; define exported symbol __atcmd_end = FLASH_SIZE - PAGE_SIZE; define exported symbol __atcmd_start = FLASH_SIZE - PAGE_SIZE*2; IAR(.icf) (不带协议栈版本) define symbol PAGE_SIZE = 0x2000; define symbol FLASH_SIZE = 0x58000; /* Define a region for the on-chip flash */ define region FLASH_LAST_PAGE = mem:[from(FLASH_SIZE) - PAGE_SIZE to FLASH_SIZE - 1]; define region FLASH_ATCMD_PAGE = mem:[from(FLASH_SIZE) - PAGE_SIZE * 2 to FLASH_SIZE - PAGE_SIZE]; define region FLASH_region = mem:[from ROM_start__ to ROM_end__]; /* Place the CCA area at the end of flash */ place at end of FLASH_LAST_PAGE { readonly section .ccfg }; keep { section .ccfg }; // ATCMD place at end of FLASH_ATCMD_PAGE { readonly section .atcmd. }; keep { section .atcmd. }; define exported symbol __atcmd_end = FLASH_SIZE - PAGE_SIZE; define exported symbol __atcmd_start = FLASH_SIZE - PAGE_SIZE * 2; IAR section(“.atcmd.”) 三种写法 static const int __atcmd_abc __attribute__((used,__section__(".atcmd."))) = 0; #define PLACE_IN_REGION1 _Pragma("location=".atcmd."") PLACE_IN_REGION1 const int __atcmd_xjq = 2; static const int __atcmd_xxx __attribute__((used)) __attribute__((section(".atcmd."))) = 0
上一篇:[C语言] 16进制整数转字符串
下一篇:[单片机框架][drivers层][g_sensor][adxl34x] 加速度传感器的使用
推荐阅读最新更新时间:2024-11-08 04:44
设计资源 培训 开发板 精华推荐
- 用于 MCU 系统负载感应的 NCP300HSN30T1 3V 电压检测器的典型应用
- ZR431F005TA 可调精密齐纳并联稳压器的典型应用
- LTC7813MPUH 宽输入范围至 10V/10A 低 IQ 级联升压+降压稳压器的典型应用电路
- 使用 NXP Semiconductors 的 TDA8933 的参考设计
- FEBFDMQ8203_90W,用于以太网供电 90W 有源钳位正向 DC-DC 转换器的 GreenBridge 评估套件
- LT3091MPR 浮动 3 端子稳压器的典型应用,适用于任意高压应用
- 使用 Microchip Technology 的 TC1071 的参考设计
- 51最小系统
- 使用 Richtek Technology Corporation 的 RT6576C 的参考设计
- 环境光传感器OPT3007YMFR 验证板