[单片机框架][AT指令框架][从机版] 高内聚 低耦合

发布者:心灵律动最新更新时间:2022-09-20 来源: csdn关键字:高内聚 手机看文章 扫描二维码
随时随地手机看文章

核心思想

采用__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


关键字:高内聚 引用地址:[单片机框架][AT指令框架][从机版] 高内聚 低耦合

上一篇:[C语言] 16进制整数转字符串
下一篇:[单片机框架][drivers层][g_sensor][adxl34x] 加速度传感器的使用

推荐阅读最新更新时间:2024-11-08 04:44

单片机上的按键检测框架
下面是我之前写的一个按键检测的框架,适合比较多的按键操作,从信号接收、滤波、最好处理按键任务,大体上没什么问题,不过没具体测试过到时可能会有bug。 1 /****************************************************************************** 2 * @file button.c 3 * @author wule 4 * @version 5 * @date 6 * @brief 7 ***************************************************************************
[单片机]
[单片机框架][os层] RTX4 中间件 公共函数
重新封装RTX4函数,以便适配RTX5 / FREERTOS / FREERTOS_NRF_RTC。 Keil RTX是为ARM和Cortex-M设备设计的免版税,确定性的实时操作系统。它允许您创建同时执行多个功能的程序,并帮助创建更好的结构和更容易维护的应用程序。 特征 具有源代码的免版权,确定性RTOS 灵活的调度:循环,抢占和协作 具有低中断延迟的高速实时操作 为资源有限的系统提供小封装 无限数量的任务每个具有254个优先级 无限数量的邮箱,信号量,互斥量和计时器 支持多线程和线程安全操作 内核感知调试支持MDK-ARM 使用μVision配置向导的基于对话框的设置 优点 尽管无需实时操作系统(通过在超级循环中执行一
[单片机]
[<font color='red'>单片机</font><font color='red'>框架</font>][os层] RTX4 中间件 公共函数
[单片机框架][bsp层][nrf51822][nrf51422][nrf51802][bsp_key] KEY配置和使用
按键的基本原理是设置单片机IO口(PB0-PB3)为输入状态,如DDRB = 0XF0(方向寄存器,“1”为输出,“0”为输入); 单片机一直检测按键端口(PB0-PB3)的状态,当端口为低电平时(即按键按下),实行相应的动作(比如控制LED灯)。 原理就是这么回事,但是正真实现时,按键会有抖动,要进行按键去抖,下图为按键按下时的抖动图。 按键实行一个动作过程是需要一定时间的,一般为100mS-1S左右,而一个单片机执行一个机器周期的时间很短,时钟为10MH的周期为0.1μs,这样按键每一次动作程序就会多次检测按键,出现误判(一次按下,多次动作)。 /********************************
[单片机]
[单片机框架][bsp层][nrf51822][nrf51422][nrf51802][bsp_adc] ADC配置和使用
Analog to Digital Converter (ADC) The 10 bit incremental Analog to Digital Converter (ADC) enables sampling of up to 8 external signals through a front-end multiplexer. The ADC has configurable input, reference prescaling, and sample resolution (8, 9, and 10 bit). Note: The ADC module uses the same analog inputs as
[单片机]
[单片机框架][bsp层][nrf51822][nrf51422][nrf51802][bsp_gpio] GPIO配置和使用
GPIO The general purpose I/O is organized as one port with up to 32 I/Os (dependent on package) enabling access and control of up to 32 pins through one port. Each GPIO can be accessed individually with the following user configurable features: Input/output direction Output drive strength Internal pull-up and pull-d
[单片机]
单片机---HLK-W801图形框架LVGL下开发(一)
简单介绍 根据前面的知识点内容,一步一步走了过来,实现了lvgl在HLK801上的运行,还是挺让人欣慰的,没有遇到让人半途而废的坑,这就和减肥一个道理,坚持下来,以后还能多吃点。 在前面的基础上,这次来学习一下绘制一个简单的界面,做一个简单的时钟显示。其实市面上很多类似的智能wifi时钟,都是在这个基础上实现的,能够通过网络同步时间,并且能够获得天气信息并显示出来。 今天就做个简单的RTC时钟,学习一下界面的绘制。 界面绘制 代码参考自LVGL8制作简易时钟 修改了全屏的黑色背景,其他部分没有修改。因为背景太白,有点伤眼。 界面部分 void lvgl_clock_start() { static lv_
[单片机]
<font color='red'>单片机</font>---HLK-W801图形<font color='red'>框架</font>LVGL下开发(一)
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved