AT指令框架--从机版]
- 一、核心思想
- 二、下面就是举例使用方法:test.c
- 三、用串口举例
- 四、代码:
- 五、GCC链接文件修改(.lds):
- 六、IAR链接文件修改(.icf) (带协议栈版本)
- 七、IAR链接文件修改(.icf) (不带协议栈版本)
- 八、IAR链接文件修改(.icf) (OAD版本)
- 九、IAR链接文件整合(OAD_IMG_B+OAD_IMG_A+正常APP)
- IAR __section__(".atcmd.") 三种写法
一、核心思想
- 采用__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] = "test\r\n";
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指令
********************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#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=(保留)\r\n
pack->len -= strlen(atcmd->name) - 2; // 3 == \r\n
pack->data += strlen(atcmd->name); // 移除前面的内容,并包含'=' or '?' or '=?'
pack->data[pack->len + 1] = '\0';
atcmd->callback(pack);
return 0;
}
return -1;
}
/********************************************************************************
* @file atcmd_slave.h
* @author jianqiang.xue
* @Version V1.0.0
* @Date 2022-09-04
* @brief 从机版 AT指令
* ATCMD_INIT定义时,要按照下面顺序,不然有可能遍历遗漏。如先检测到'=',直接执行AT+<x>=,导致'=?'被跳过。
* 测试指令
* AT+<x>=? 用于枚举可配置的参数
* 读指令
* AT+<x>? 用于读取目前配置的参数
* 写指令
* AT+<x>=<…> 用于设置参数
* 执行指令
* AT+<x> 执行某项指令,比如复位“AT+RESET"。
********************************************************************************/
#ifndef __ATCMD_SLAVE_H
#define __ATCMD_SLAVE_H
#include <stdint.h>
#define AT_OK "\r\nOK\r\n"
#define AT_ERROR "\r\nERROR\r\n"
typedef struct{
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^",且末尾为"\r\n",就传入给改函数。
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链接文件修改(.icf) (OAD版本)
九、IAR链接文件整合(OAD_IMG_B+OAD_IMG_A+正常APP)
使用方法:
- EIDE
.eide–release.options.arm.iar.json–[linker]–[controls]–变更[–config_def OAD_IMG_x=1] (X=A or B)
如果删除[–config_def OAD_IMG_x=1]则运行正常APP,否则将按OAD方法使用。不然跑步起来。 - IAR
OAD模式中,需要烧录三个文件[imga_boot\xxx_Oad_Program.hex][imga_boot\cc26x2r1lp_bim_onchip.hex][build/xx/APP.hex],或者烧录合并后的文件[build/xx/xx_ALL.hex]。
/******************************************************************************
@file cc26xx_app_and_stack_agama.icf
@brief IAR ARM Linker Configuration File - BLE Application and Stack together
Imported Symbols
Note: Linker defines are located in the IAR IDE project using
--config_def in Options->Linker->Extra Options.
CCxxxx: Device Name (e.g. CC2650). In order to define this
symbol, the tool chain requires that it be set to
a specific value, but in fact, the actual value does
not matter as it is not used in the linker control
file. The only way this symbol is used is based on
whether it is defined or not, not its actual value.
There are other linker symbols that do specifically
set their value to 1 to indicate R1, and 2 to
indicate R2, and these values are checked and do make
a difference. However, it would appear confusing if
the device name's value did not correspond to the
value set in other linker symbols. In order to avoid
this confusion, when the symbol is defined, it should
be set to the value of the device's ROM that it
corresponds so as to look and feel consistent. Please
note that a device name symbol should always be
defined to avoid side effects from default values
that may not be correct for the device being used.
FLASH_ROM_BUILD: Build Flash portion of BLE stack to work with ROM
code. =1 for R1 devices, =2 for R2 devices.
ENCRYPTION_ROM: Reserve ECC Engine RAM space (necessary for Secure
Connections when using a FLASH_ONLY_BUILD).
Exported Symbols
Note: Can be used as externs in C code.
PAGE_SIZE: Size of Flash sector, in bytes.
STACK_TOP: Location of the top of RAM.
Group: WCS, BTS
Target Device: cc13x2_26x2
******************************************************************************
Copyright (c) 2013-2021, Texas Instruments Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Texas Instruments Incorporated nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************
*****************************************************************************/
// Memory Sizes
define symbol FLASH_BASE = 0x00000000;
define symbol RAM_BASE = 0x20000000;
define symbol ROM_BASE = 0x10000000;
define symbol RAM_SIZE = 0x00014000; // 80K
define symbol FLASH_SIZE = 0x00058000; // 352K
define symbol ROM_SIZE = 0x00040000; // 256K
// Memory Definitions
// CM3 FW
//
define symbol FW_START = 0x10000000;
define symbol FW_END = 0x10004BFF;
// RAM
//
if ( isdefinedsymbol(FLASH_ROM_BUILD) )
{
if ( FLASH_ROM_BUILD == 1 ) // R1
{
define symbol RESERVED_RAM_SIZE = 0x00000B08;
define symbol RAM_START = RAM_BASE;
define symbol RAM_END = RAM_START+RAM_SIZE-RESERVED_RAM_SIZE-1;
}
else // default to FLASH_ROM_BUILD == 2 // For CC26X2_V2 PG 2.0 ROM Build
{
define symbol RESERVED_RAM_SIZE = 0x00000EB3;
define symbol RAM_START = RAM_BASE + 0x0000012C + RESERVED_RAM_SIZE; // PG 2.0 Agama RAM_START
define symbol RAM_END = RAM_BASE+RAM_SIZE-1;
}
}
else // FlashOnly build
{
define symbol RAM_START = RAM_BASE;
// No RAM reserve is needed for source builds of the BLESTACK
define symbol RAM_END = RAM_START+RAM_SIZE-1;
}
// Flash
//
define symbol WORD_SIZE = 4;
define symbol PAGE_SIZE = 0x2000;
export symbol PAGE_SIZE;
if (isdefinedsymbol(PAGE_ALIGN))
{
define symbol FLASH_MEM_ALIGN = PAGE_SIZE;
}
else
{
define symbol FLASH_MEM_ALIGN = WORD_SIZE;
}
define symbol PAGE_MASK = 0xFFFFE000;
if ( isdefinedsymbol(CC2642) )
{
define symbol SNV_START = 0x00034000;
}
define symbol IMG_A_FLASH_START = 0x00038000;
define symbol ATCMD_SIZE = (1*PAGE_SIZE);
// OAD specific
// When compiling the application without the SECURITY define, OAD_HDR_SIZE should be 0x50
define symbol OAD_HDR_SIZE = 0x90; // linker needs word alignment.
if ( isdefinedsymbol(OAD_IMG_B) ) // RTOS in ROM
{
define symbol OAD_HDR_START = FLASH_BASE;
define symbol OAD_HDR_END = OAD_HDR_START + OAD_HDR_SIZE - 1;
define symbol ENTRY_SIZE = 0x40;
define symbol ENTRY_START = OAD_HDR_END + 1;
define symbol ENTRY_END = ENTRY_START + ENTRY_SIZE - 1;
define symbol APP_START = ENTRY_END + 1;
define symbol NUM_RESERVED_PAGES = 1; /*last page i define in FLASH_LAST_PAGE */
define symbol RESERVED_SIZE = (NUM_RESERVED_PAGES * PAGE_SIZE);
define symbol RESERVED_END = (FLASH_SIZE - 1);
define symbol RESERVED_START = (RESERVED_END - RESERVED_SIZE + 1);
define symbol FLASH_END = (FLASH_BASE + IMG_A_FLASH_START - 1);
//define symbol FLASH_END = (SNV_START - 1);
}
else if ( isdefinedsymbol(OAD_IMG_A) )
{
define symbol NUM_RESERVED_PAGES = 1;
define symbol RESERVED_SIZE = (NUM_RESERVED_PAGES * PAGE_SIZE);
define symbol RESERVED_END = (FLASH_SIZE - 1);
define symbol RESERVED_START = (RESERVED_END - RESERVED_SIZE + 1);
define symbol OAD_HDR_START = IMG_A_FLASH_START;
define symbol OAD_HDR_END = OAD_HDR_START + OAD_HDR_SIZE - 1;
// Entry Section
define symbol ENTRY_SIZE = 0x40;
define symbol ENTRY_START = OAD_HDR_END + 1;
define symbol ENTRY_END = ENTRY_START + ENTRY_SIZE - 1;
export symbol ENTRY_START;
export symbol ENTRY_END;
define symbol FLASH_END = (RESERVED_START - 1);
define symbol APP_START = ENTRY_END + 1;
// define symbol IMAGE_END = ENTRY_END;
// export symbol IMAGE_END;
}
else
{
define symbol NUM_RESERVED_PAGES = 1; /*last page i define in FLASH_LAST_PAGE */
define symbol RESERVED_SIZE = (NUM_RESERVED_PAGES * PAGE_SIZE);
define symbol FLASH_START = FLASH_BASE;
define symbol FLASH_END = FLASH_SIZE - RESERVED_SIZE - 1;
}
define symbol ATCMD_START = (SNV_START - ATCMD_SIZE);
define symbol ATCMD_END = (ATCMD_START + ATCMD_SIZE - 1);
// Stack
//
define symbol STACK_SIZE = 0x800; //0x400;
define symbol STACK_START = RAM_END + 1;
define symbol STACK_END = STACK_START - STACK_SIZE;
//
define symbol STACK_TOP = RAM_END + 1;
export symbol STACK_TOP;
// Flash Interrupt Vector Table
//
define symbol INTVEC_NUM_ENTRIES = 50 + 1; // first entry is stack location
define symbol INTVEC_SIZE = INTVEC_NUM_ENTRIES + 4;
// Memory Regions
define memory mem with size = 4G;
if ( isdefinedsymbol(OAD_IMG_B) )
{
define region RAM = mem:[from RAM_START to RAM_END];
define region FLASH_IMG_HDR = mem:[from OAD_HDR_START to OAD_HDR_END];
define region ENTRY_FLASH = mem:[from ENTRY_START to ENTRY_END];
define region FLASH_APP = mem:[from APP_START to (ATCMD_START-1)];
define region FLASH_ATCMD_PAGE = mem:[from ATCMD_START to (ATCMD_END)];
define region FLASH_RESERVE = mem:[from ATCMD_END to (FLASH_END)];
define region FLASH_ALL = mem:[from (APP_START) to (FLASH_END)];
}
else if ( isdefinedsymbol(OAD_IMG_A) )
{
define region RAM = mem:[from RAM_START to RAM_END];
define region FLASH_IMG_HDR = mem:[from OAD_HDR_START to OAD_HDR_END];
define region ENTRY_FLASH = mem:[from ENTRY_START to ENTRY_END];
define region FLASH_APP = mem:[from APP_START to (ATCMD_START-1)];
define region FLASH_ATCMD_PAGE = mem:[from ATCMD_START to (ATCMD_END)];
define region FLASH_RESERVE = mem:[from ATCMD_END to (FLASH_END)];
define region FLASH_ALL = mem:[from (APP_START) to (FLASH_END)];
}
else
{
define region RAM = mem:[from RAM_START to RAM_END];
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. };
if ( isdefinedsymbol(OAD_IMG_B) || isdefinedsymbol(OAD_IMG_A))
{
define exported symbol __atcmd_end = ATCMD_END;
define exported symbol __atcmd_start = ATCMD_START;
}
else
{
define exported symbol __atcmd_end = FLASH_SIZE - PAGE_SIZE;
define exported symbol __atcmd_start = FLASH_SIZE - PAGE_SIZE * 2;
}
if ( isdefinedsymbol(OAD_IMG_B) || isdefinedsymbol(OAD_IMG_A))
{
// Flash OAD Image Header
place at start of FLASH_IMG_HDR { readonly section .img_hdr };
keep { readonly section .img_hdr };
}
if ( isdefinedsymbol(OAD_IMG_B) || isdefinedsymbol(OAD_IMG_A))
{
// Interrupt Vector Table
place at address mem:ENTRY_START { readonly section .intvec };
keep { readonly section .intvec };
}
else
{
// Interrupt Vector Table
place at address mem:FLASH_START { readonly section .intvec };
keep { readonly section .intvec };
}
define block ROSTK with alignment=WORD_SIZE{ ro };
// All other pieces of codes:
place in FLASH_ALL {block ROSTK };
// RAM Vector Table
define block VTABLE { section .vtable_ram };
// Runtime Stack
define block CSTACK with alignment = 8, size = STACK_SIZE { section .stack };
define block RWDATA with alignment = 8 { rw };
define section .heap_start { public heapStart: };
define section .heap_end { public heapEnd: };
define block HEAP_END with size = 1 { section .heap_end };
place at end of RAM { block HEAP_END };
place in RAM { first block VTABLE,
block RWDATA,
block CSTACK,
last section .heap_start
};
// Initialization
initialize by copy { readwrite };
do not initialize
{
section .noinit,
section .stack,
};
// The USE_TIRTOS_ROM symbol is defined internally in the build flow (using
// --config_def USE_TIRTOS_ROM=1) for TI-RTOS applications whose appBLE.cfg file
// specifies to use the ROM.
//
if (isdefinedsymbol(USE_TIRTOS_ROM)) {
include "TIRTOS_ROM.icf";
}
// The following is used to Enable IAR to display RTOS ROV when auto-size heap
// is being used. it will disable the check on teh validity of the RAM address.
// if One wants to keep this check, and does not whish to use auto-size heap,
// he may comments the lines bellow.
define exported symbol __ROV_VALID_RANGE0_end__ = RAM_BASE + RAM_SIZE;
define exported symbol __ROV_VALID_RANGE0_start__ = RAM_BASE;
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