1. 首页 > 快讯

STM32表驱动法实例解析

大家好,今天小编来为大家解答以下的问题,关于STM32表驱动法实例解析,这个很多人还不知道,现在让我们一起来看看吧!

概念

所谓表驱动法(Table-Driven Approach)简而言之就是用查表的方法获取数据。此处的“表”通常为数组,但可视为数据库的一种体现。

根据字典中的部首检字表查找读音未知的汉字就是典型的表驱动法,即以每个字的字形为依据,计算出一个索引值,并映射到对应的页数。相比一页一页地顺序翻字典查字,部首检字法效率极高。

具体到编程方面,在数据不多时可用逻辑判断语句(if…else或switch…case)来获取值;但随着数据的增多,逻辑语句会越来越长,此时表驱动法的优势就开始显现。

简单示例

上面讲概念总是枯燥的,我们简单写一个C语言的例子。下面例子功能:传入不同的数字打印不同字符串。

使用if…else逐级判断的写法如下:

void fun(intday)
{
if(day1)
{
printf("Monday\n");
}
else if(day2)
{
printf("Tuesday\n");
}
else if(day3)
{
printf("Wednesday\n");
}
else if(day4)
{
printf("Thursday\n");
}
else if(day5)
{
printf("Friday\n");
}
else if(day6)
{
printf("Saturday\n");
}
else if(day7)
{
printf("Sunday\n");
}
}

使用switch…case的方法写。

void fun(intday)
{
switch(day)
{
case1:
printf("Monday\n");
break;
case2:
printf("Tuesday\n");
break;
case3:
printf("Wednesday\n");
break;
case4;
printf("Thursday\n");
break;
case5:
printf("Friday\n");
break;
case6:
printf("Saturday\n");
break;
case7:printf("Sunday\n");
break;
default:
break;
}
}

使用表驱动法实现。

charweekDay[]{Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday};
void fun(intday)
{
printf("%s\n",weekDay[day]);
}

看完示例,可能“恍然大悟”,一拍大腿,原来表驱动法就是这么简单啊。是的,它的核心原理就是这个简单,如上面例子一样。

如果上面的例子还没get这种用法的好处,那么再举一个栗子。

统计用户输入的一串数字中每个数字出现的次数。

常规写法

int32_t aDigitCharNum[10]{0};/* 输入字符串中各数字字符出现的次数 */
int32_t dwStrLenstrlen(szDigits);

int32_t dwStrIdx0;
for(;dwStrIdx<dwStrLen;dwStrIdx++)
{
switch(szDigits[dwStrIdx])
{
case'1':
aDigitCharNum[0]++;
break;
case'2':
aDigitCharNum[1]++;
break;
//... ...
case'9':
aDigitCharNum[8]++;
break;
}
}

表驱动法


for(;dwStrIdx<dwStrLen;dwStrIdx++)
{
aDigitCharNum[szDigits[dwStrIdx]'0']++;
}

偶尔在一些开源项目中看到类似的操作,惊呼“骚操作”,其实他们有规范的叫法:表驱动法。

在MCU中应用

在MCU中的应用示例,怎么少的了点灯大师操作呢?首先来点一下流水LED灯吧。

常规写法

void LED_Ctrl(void)
{
static uint32_t sta0;


if(0sta)
{
LED1_On();
}
else
{
LED1_Off();
}


if(1sta)
{
LED2_On();
}
else
{
LED2_Off();
}


/* 两个灯,最大不超过2 */
sta(sta+1)%2;
}


/* 主函数运行 */
intmain(void)
{
while(1)
{
LED_Ctrl();
os_delay(200);
}
}

表驱动法

extern void LED1_On(void);
extern void LED1_Off(void);
extern void LED2_On(void);
extern void LED2_Off(void);


struct tagLEDFuncCB
{
void(LedOn)(void);
void(LedOff)(void);
};


const static struct tagLEDFuncCB LedOpTable[]
{
{LED1_On,LED1_Off},
{LED2_On,LED2_Off},
};


void LED_Ctrl(void)
{
static uint32_t sta0;
uint8_t i;


for(i0;i<sizeof(LedOpTable)/sizeof(LedOpTable[0]);i++)
{
(stai)?(LedOpTable[i].LED_On()):(LedOpTable[i].LED_Off());
}


sta(sta+1)%(sizeof(LedOpTable)/sizeof(LedOpTable[0]));
}


intmain(void)
{
while(1)
{
LED_Ctrl();
os_delay(200);
}
}

这样的代码结构紧凑,因为和结构体结合起来了,方便添加下一个LED灯到流水灯序列中,这其中涉及到函数指针,详细请看《回调函数》,只需要修改LedOpTable如下:

const static struct tagLEDFuncCB LedOpTable[]
{
{LED1_On,LED1_Off},
{LED2_On,LED2_Off},
{LED3_On,LED3_Off},
};

这年头谁还把流水灯搞的这么花里胡哨的啊,那么就举例在串口解析中的应用,之前的文章推送过《回调函数在命令解析中的应用》,下面只贴一下代码:


typedef struct
{
rt_uint8_t CMD;
rt_uint8_t(callback_func)(rt_uint8_t cmd,rt_uint8_tmsg,uint8_t len);
}_FUNCCALLBACK;

_FUNCCALLBACK callback_list[]
{
{cmd1,func_callback1},
{cmd2,func_callback2},
{cmd3,func_callback3},
{cmd4,func_callback41},
...
};

void poll_task(rt_uint8_t cmd,rt_uint8_tmsg,uint8_t len)
{
intcmd_indexmaxsizeof(callback_list)/sizeof(_FUNCCALLBACK);
intcmd_index0;

for(cmd_index0;cmd_index<cmd_indexmax;cmd_index++)
{
if(callback_list[cmd_index].CMDcmd)
{
if(callback_list[cmd_index])
{
/* 处理逻辑 */
callback_list[cmd_index].callback_func(cmd,msg,len);
}
}
}
}

除上述例子,表驱动法在UI界面中也有良好的应用,如下:

结构体封装

typedefenum
{
stage10,
stage2,
stage3,
stage4,
stage5,
stage6,
stage7,
stage8,
stage9,
}SCENE;
typedef struct
{
void(current_operate)();//当前场景的处理函数
SCENE Index;//当前场景的标签
SCENE Up;//按下Up键跳转的场景
SCENE Down;//按下Down键跳转的场景
SCENE Right;//按下Left键跳转的场景
SCENE Left;//按下Right键跳转的场景
}STAGE_TAB;

函数映射表

STAGE_TAB stage_tab[] = {
//operate Index Up Down Left Right
{Stage1_Handler, stage1, stage4, stage7, stage3, stage2},
{Stage2_Handler, stage2, stage5, stage8, stage1, stage3},
{Stage3_Handler, stage3, stage6, stage9, stage2, stage1},
{Stage4_Handler, stage4, stage7, stage1, stage6, stage5},
{Stage5_Handler, stage5, stage8, stage2, stage4, stage6},
{Stage6_Handler, stage6, stage9, stage3, stage5, stage4},
{Stage7_Handler, stage7, stage1, stage4, stage9, stage8},
{Stage8_Handler, stage8, stage2, stage5, stage7, stage9},
{Stage9_Handler, stage9, stage3, stage6, stage8, stage7},
};

定义两个变量保存当前场景和上一个场景。

charcurrent_stagestage1;
charprev_stagecurrent_stage;

按下Up按键 跳转到指定场景current_stage的值根据映射表改变。

current_stagestage_tab[current_stage].Up;

场景改变后 根据映射表执行相应的函数Handler。

if(current_stage!=prev_stage)
{
stage_tab[current_stage].current_operate();
prev_stagecurrent_stage;
}

后记

文章到此结束,如果本次分享的STM32表驱动法实例解析和的问题解决了您的问题,那么我们由衷的感到高兴!

用户评论

熏染

STM32 真的是个好芯片啊,各种学习资料都很丰富!

    有16位网友表示赞同!

寂莫

以前没怎么接触过表驱动法,感觉这篇文章很有用,可以让我更好地理解它。

    有12位网友表示赞同!

七级床震

代码实现的清晰度很重要,这种方法是不是能提高代码的可读性呢?

    有19位网友表示赞同!

情如薄纱

表驱动法的应用场景还挺广的啊,在STM32上用的也很普遍。

    有14位网友表示赞同!

莫阑珊

学习一下表驱动法,以后做项目的时候也许就能派用场!

    有5位网友表示赞同!

古巷青灯

希望能看到一些更具体的案例,这样更容易理解。

    有8位网友表示赞同!

虚伪了的真心

表驱动法是不是能提高STM32的开发效率呢?

    有19位网友表示赞同!

娇眉恨

这篇文章写的很有深度啊,让我对表驱动法有了更全面的认识。

    有11位网友表示赞同!

抓不住i

看完这篇文章后,我觉得我可以尝试用表驱动法做个简单的项目了!

    有12位网友表示赞同!

厌归人

学习新的编程技巧总是很兴奋的,表驱动法是个很好的例子。

    有8位网友表示赞同!

青衫故人

表驱动法的原理看起来挺简单粗暴的,但实际应用起来还是很有用的。

    有14位网友表示赞同!

相知相惜

这篇文章有没有附带一些代码注释,可以更好地理解代码逻辑?

    有9位网友表示赞同!

堕落爱人!

如果能提供一些不同的表驱动法实现方式对比,会更好理解它的优缺点。

    有16位网友表示赞同!

搞搞嗎妹妹

表驱动法在其他嵌入式平台上也可以应用吗?

    有16位网友表示赞同!

何必锁我心

STM32 的资源配置可以通过表驱动法进行管理吗?

    有8位网友表示赞同!

愁杀

文章中提到的具体项目案例是什么样的呢?

    有11位网友表示赞同!

单身i

表驱动法的学习难度怎么样?需要掌握哪些基础知识?

    有17位网友表示赞同!

不识爱人心

这篇文章能让我更深入地了解STM32硬件的驱动机制吗?

    有20位网友表示赞同!

仰望幸福

我想尝试自己动手写个基于表驱动的代码,这篇文章可以作为参考指南吗?

    有6位网友表示赞同!

本文采摘于网络,不代表本站立场,转载联系作者并注明出处:https://www.iotsj.com//kuaixun/7030.html

联系我们

在线咨询:点击这里给我发消息

微信号:666666