嵌入式按键调参:简洁接口轻松调参(ADC FLASH 按键 屏幕参数显示)

调试平台:逐飞TC387库 4路按键 3路拨码开关 IPS200屏 2电位器

按键用于页面选择/保存参数 拨码开关用于页的切换(比如说一个页面放pid 一个页面放陀螺仪)

双adc一个用于切换选择项,另一个用于参数的调整。

这个程序的核心思想是简洁的接口,仅修改几个数组就可以很轻松的添加/修改参数

比如说这是一个显示列表的页面 第一个参数是显示在名字前面的字符串 第二个是类型,第三个是地址,之后的文章中会讲到为什么要这样定义

  • 程序的整体思路

整个程序主要分三个模块,显示列表,调试列表,FLASH列表

显示列表是最基本的列表,也就是仅仅显示数据 ,没有调参

调试列表在显示列表的基础上,加入了ADC调参,一个用于选择调整的项,另一个用于选择调整多少

FLASH列表是调试列表的拓展 如果你的参数同时在FLASH列表和调试列表中,那么你的参数在调整并且按下按键后会被写入FALSH

显示列表和调试列表都有一个控制器,这个控制器除了存放用于存放各项目的数组之外,还要存放和换页相关的参数

每个控制器代表用拨码开关选择的一块,比如说2路拨码开关就可以出现4种状态,也就是有4块,每一个块对应着一个调试列表/显示列表的控制器

FLASH列表则复杂一些,因为前面说到的那些调试参数可能会很长,动辄几十多条,但是FLASH里面只存了参数,然后程序上电的时候依次赋值。如果这几十条参数放在同一个列表中,那么就无法改动列表项的顺序,增加/减少一个都会导致数据乱套。这是很危险的。

你可能会有疑问:既然嵌入式是32位的 那么地址也是32位 为什么不能相邻的两个格子,一个存放参数,另一个存放地址呢?因为变量的地址在编译的时候可能会改变,这是无法人为控制的。

综上所述,我的程序中采用了FLASH控制器+FLASH中存放控制页面的方式,这样可以尽可能小的减小因为参数顺序改变造成的问题,这个后面会细讲。

  • 显示参数的基本原理

先说说显示的基本原理,因为笔者用到的单片机是32位的英飞凌TC387,32位意味着地址总线是32位的,也就是说,我们可以用32个bit存放片上任意一个数据的地址。这就是上面这张图片为什么要取地址后转换成uint32类型的原因。

知道数据的地址之后,我们怎么把数据解析成原数据呢?比如说变量uint32 a= &b,把b的地址赋给了a。那么如果用 *a解析出b的原数据可行吗?显然是不行的,因为无法知道b的元数据类型,是无符号还是有符号?是浮点还是定点数?指针所指向的位置存放的是一串二进制,我们无法通过一串二进制把他转回需要的数据,所以我们在保存参数的时候还需要增加一个数据的类型,在后续解析的时候可以强转回这个类型的指针,再转回原数据。这样我们就可以定义出本程序中最基础的数据结构

cpp 复制代码
typedef enum data_type{
    FLOAT,
    INT
}data_type;

typedef struct debug_item{
    char name[15];
    data_type type;
    uintptr_t data;    
//以上的需要显式定义
    FI current_data;
    int is_eeprom;
    FI _default;//_default val form program
    float step;        // adc per circle step
    int is_changed; //
    int is_saved;  //

}debug_item;

其中

data_type type;

uintptr_t data;这两个参数是知道一个数据类型所必须的,其他作为一些附加的参数,在之后的程序中会用到

上文说完了如何存放数据,接下来说明如何把数据指针转回原始的值

如图所示 根据类型 转换对应类型的指针 再转换成对于类型的数据,拿int来举例

*((int*) show_list1[i].data)这句话的意思是 show_list1[i].data是一个uint32类型的数据,我们先通(int*) show_list1[i].data把他强制转为了一个int指针,再取这个指针里的值。

  • 显示/调试参数控制器

之所以把显示/调试参数控制器放在一起讲,是因为显示和调试控制器这两个的区别仅在对于ADC的响应,也就是说 显示部分不加入ADC调整数据,调试控制器加入了一个ADC调数据。

cpp 复制代码
typedef struct debug_page{
    char page_name[15];
    debug_item* items;
    int item_num;
    int total_page;
    int current_page;
}debug_headle;

typedef struct disp_page{
    char page_name[15];
    debug_item* items;
    int item_num;
    int total_page;
    int current_page;
}disp_headle;

page_name是显示在屏幕上的块的名字,比如说陀螺仪就显示GYRO_INFO这种

debug_item* items是这个块所要显示数据的数据数组的指针

INIT_DISP_PAGE是一个宏定义,因为数组退化成指针的时候会丢失长度,所以如果在运行的时候再进行长度的计算是不可行的,必须使用宏定义:

cpp 复制代码
#define INIT_DISP_PAGE(name, arr) { \
    name,           /* page_name */ \
    arr,            /* item */ \
    ARRAY_SIZE(arr),/* item_num */ \
    (ARRAY_SIZE(arr) + (PAGE_LENGTH) - 1) / (PAGE_LENGTH), /* total_page */ \
    0               /* current_page */ \
}

然后就可以显示参数了:

cpp 复制代码
void show_main(disp_headle* d){
    DISP_STRING(0, 0, d->page_name);

    int item_begin_cur_page = PAGE_LENGTH * d->current_page;
    int item_end_cur_page = func_limit_ab(PAGE_LENGTH * (d->current_page + 1) - 1, 0, d->item_num);
    for(int i = 0; i < item_end_cur_page - item_begin_cur_page; i++){
        uint16 data_y = ITEM_BEGIN + i * DATA_Y_STEP;
        DISP_STRING(0, data_y, d->items[i].name);
        if(d->items[i].type == INT)     DISP_INT  (NUM_BEGIN, data_y, *((int*)  d->items[i].data));
        if(d->items[i].type == FLOAT)   DISP_FLOAT(NUM_BEGIN, data_y, *((float*)d->items[i].data));
    }

    char page_info[20];
    sprintf(page_info,"Page-%d-of-%d-", d->current_page + 1, d->total_page);
    ips200_show_string(0, 300, page_info);

    int cur_key = get_key();
    if(cur_key == KEY2){
        ips200_clear();
        d->current_page = (d->current_page + 1) % d->total_page;
    }
    if(cur_key == KEY1){
        ips200_clear();
        d->current_page = (d->current_page - 1 + d->total_page) % d->total_page;
    }
}

这里还有一个函数是

cpp 复制代码
#define     func_limit_ab(x, a, b)  ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x)))

这个函数的意思是把x限定在a~b中 逐飞的函数还是很好用的

  • 参数保存进FLASH

前文提到,本程序采用单独的FLASH管理页面和FALSH数据存储页面,同时 代码中存放了FLASH

相关推荐
v_for_van18 小时前
STM32低频函数信号发生器(四通道纯软件生成)
驱动开发·vscode·stm32·单片机·嵌入式硬件·mcu·硬件工程
你撅嘴真丑18 小时前
求10000 以内的阶乘 与 字符串最大跨距
数据结构·c++·算法
lingzhilab18 小时前
零知IDE—— ESP8266(ESP-12F)MESH 组网实现多设备智能家居控制系统(灯光 / 传感器 / 人体感应)
c++·ide·智能家居
信创天地18 小时前
从 “替代” 到 “超越”:信创系统架构师如何筑牢自主可控技术底座
运维·安全·系统架构·开源·dubbo·risc-v
小y要自律18 小时前
11 string容器 - 子串获取
c++·算法·stl
电化学仪器白超18 小时前
③YT讨论
开发语言·python·单片机·嵌入式硬件
DevangLic18 小时前
【确认是否安装了 C++ 工具】
android·java·c++
承渊政道18 小时前
C++学习之旅【C++拓展学习之反向迭代器实现、计算器实现以及逆波兰表达式】
c语言·开发语言·c++·学习·visual studio
乡野码圣19 小时前
【RK3588 Android12】硬件中断IRQ
单片机·嵌入式硬件
happygrilclh19 小时前
数码管驱动(一):ET6226M -数据手册主要点分析
单片机·嵌入式硬件