电子产业一站式赋能平台

PCB联盟网

搜索
查看: 271|回复: 0
收起左侧

用表驱动法软件架构应对复杂逻辑,实在太"骚"了。。。

[复制链接]

318

主题

318

帖子

3043

积分

四级会员

Rank: 4

积分
3043
发表于 2025-3-8 08:01:00 | 显示全部楼层 |阅读模式
关注公众号,回复“入门资料”获取单片机入门到高级开挂教程
开发板带你入门,我们带你飞

文 | 无际(微信:2777492857)
全文约3287字,阅读大约需要 10 分钟
雷猴啊~我是无际,一个即将秃顶的技术朋友。
刚做开发那几年,每次领导或客户提新需求,我都心慌慌,表面点头,心里已经在算要重写多少代码了。
           
那会没有程序架构的概念,写程序都是if-else无限套娃,隔一段时间再回去看,想改个地方得先花大量时间理清头绪,边改代码边骂项目经理。
           
后面像捡垃圾一样,大佬代码捡一点,网上捡一点,慢慢才有了系统的程序架构思维,写程序也有框架了。
           
今天就来介绍一个我经常用的架构技巧:表驱动法。
有幸之前看过一个大佬的代码,可惜我去的时候,他已经离职了,我接受了他的项目进行维护,看到了大量表驱动的用法,比如矩阵按键,矩阵LED控制。
我第一次看到这代码,都震惊了,啥玩意?这么复杂?
        
这玩意儿听起来像是高大上的学术名词,但其实就是个“查表解决问题”的骚操作,能让你的代码从一团乱麻变成整齐划一的艺术品。
           
别慌,我保证用最接地气的语言带你搞懂这东西,全篇3000字以上,咬碎嚼烂喂你嘴里,保管你看完能直接上手,写代码时嘴角都不自觉上扬!
           
1.你是不是也烦透了乱糟糟的代码?
先说个场景,咱们假设你在搞一个单片机项目,比如花式点灯。
           
需求很简单:按按钮A,灯亮;按按钮B,灯灭;按按钮C,灯闪烁。
           
你脑子一热,立马甩出一堆if-else或者switch-case,代码可能是这样的:   
  • if (button == 'A') {    light_on();} else if (button == 'B') {    light_off();} else if (button == 'C') {    light_blink();} else {    handle_error();}看着挺顺眼,对吧?逻辑清晰,小项目里用用也没啥毛病。但现实的需求总是变态脑洞大开的,项目经理冷不丁跑过来说:“加个按钮D,灯要渐变亮!”你咋整?硬着头皮在else if后面再塞一行?
               
    然后改完还得测半天,生怕手抖改错了把整个逻辑搞崩。更别提需求再变几次,代码里全是条件判断,维护起来简直是边骂项目经理,边想砸键盘。
               
    所以,表驱动法,其实就能解决这些问题。
               
    2.表驱动法:从“码农”到“码神”的捷径
               
    啥是表驱动法?别被名字吓尿,说白了就是把那些乱七八糟的逻辑判断塞进一张表里,程序跑的时候直接查表办事,就跟查字典一样。
               
    单片机里用这招,能把复杂的代码变得简单到飞起,想加功能?改表就行,核心代码稳如泰山不动。这不比你手动改一堆if-else爽多了?
                   
    这招的精髓在于:把容易变的东西(比如逻辑、数据)抽出来,扔到表里隔离起来,程序就只管查表干活。既优雅又实用,简直是单片机开发里的降维打击。
               
    3.啥时候使用表驱动法?
    表驱动法不是万能钥匙,但有些场景用它那是真的香。咱们列几个单片机开发里的常见情况,你看看是不是似曾相识:
    ?命令解析:比如串口收到不同指令,要执行不同操作,像“ON”开灯,“OFF”关灯。
    ?状态机:设备在“待机”“运行”“故障”之间跳来跳去,每次状态切换还得干点啥。
    ?事件处理:根据不同事件调用不同函数,比如按键触发、传感器报警。
    ?配置管理:一堆参数需要统一管理,查起来方便。
               
    这些场景有个共同点:逻辑多、容易变、改起来烦。表驱动法就像个“收纳大师”,把这些乱七八糟的东西整整齐齐塞进表里,代码瞬间清爽,维护起来也跟喝水一样简单。
               
    4.表驱动法怎么玩?
    表驱动法其实就三步:
    1.搭个表:根据需求弄个数组或者结构体数组,把逻辑和数据塞进去。
    2.查表:程序跑的时候,根据输入或者状态去表里找对应的条目。
    3.干活:找到后按表里的指示执行操作,完事。
               
    听起来是不是有点“so easy”?别光点头,咱们直接上例子,无际单片机从不整虚的,直接带你实操落地。
               
    实战1:命令解析
    还记得前面那个灯控系统吗?咱们用表驱动法给它整一波优化。先看看传统的路子:
  • void process_button(char button) {    switch (button)     {        case 'A': light_on(); break;        case 'B': light_off(); break;        case 'C': light_blink(); break;        default: handle_error(); break;    }}
    这代码小项目里凑合,但一加新功能就暴露短板。咱们换表驱动法试试:
               
    第一步:建表
    定义一个结构体数组,把按钮和对应的操作塞进去,用函数指针来把逼装起来:
  • // 定义函数指针类型typedef void (*ButtonHandler)(void);// 表结构typedef struct {    char button;          // 按钮    ButtonHandler handler; // 操作函数} ButtonCommand;// 命令表ButtonCommand commands[] = {    {'A', light_on},    // 按钮A:开灯    {'B', light_off},   // 按钮B:关灯    {'C', light_blink}, // 按钮C:闪烁    // 加新功能?在这加一行就行!};
       
    第二步:查表+执行
    写个函数,收到按钮信号就去表里找,然后执行:
  • void process_button(char button) {    int size = sizeof(commands) / sizeof(ButtonCommand);    for (int i = 0; i         if (commands.button == button) {            commands.handler(); // 找到就干活            return;        }    }    handle_error(); // 没找到就报错}
    这招有啥好?
    ?扩展无压力:加个按钮D渐变亮?表里加一行{'D', light_fade},完事!
    ?代码不乱套:核心逻辑不动,改需求只改表,维护起来跟玩儿似的。
    ?一目了然:所有命令都在表里列着,比一堆switch-case直观多了。
               
    怎么样,是不是有种“代码瞬间高级了”的感觉?
                   
    实战2:状态机也能这么丝滑
    单片机里状态机用得老多了,比如设备有“待机”“运行”“错误”三种状态,根据事件跳来跳去。传统写法是这样的:
  • switch (current_state) {    case IDLE:        if (event == START)         {            start_running();            current_state = RUNNING;        }        break;    case RUNNING:        if (event == STOP)         {            stop_running();            current_state = IDLE;        } else if (event == ERROR)         {            handle_error();            current_state = ERROR;        }        break;    // 后面还有一堆}
    这代码看着就头晕,改起来更头大,尤其是状态和事件一多,嵌套得让人想哭。咱们用表驱动法整一波:
               
    第一步:建表
    定义状态和事件的枚举,再搞个状态转移表:   
  • // 状态和事件typedef enum { IDLE, RUNNING, ERROR } State;typedef enum { START, STOP, ERROR_EVENT } Event;// 表结构typedef struct {    State current;  // 当前状态    Event event;    // 触发事件    State next;     // 下一状态    void (*action)(void); // 执行的操作} Transition;// 状态转移表Transition transitions[] = {    {IDLE, START, RUNNING, start_running},    {RUNNING, STOP, IDLE, stop_running},    {RUNNING, ERROR_EVENT, ERROR, handle_error},    // 加新状态?在这加一行};           
    第二步:查表+执行
    写个函数,根据当前状态和事件查表,更新状态并干活:
  • void process_event(Event event) {    int size = sizeof(transitions) / sizeof(Transition);    for (int i = 0; i     {        if (transitions.current == current_state && transitions.event == event)         {            current_state = transitions.next;            if (transitions.action) transitions.action();            return;        }    }    invalid_transition(); // 没找到就报个错}           
    这招有啥妙处?
    ?加状态超轻松:新状态新事件?表里加一行,逻辑自动生效。
    ?规则全在表里:一打开代码,所有状态转移一清二楚。
    ?告别嵌套地狱:再也不用盯着层层switch找bug了。
               
    这状态机一用表驱动法,丝滑得像刚抹了润滑油,调试起来都心情舒畅。
               
    以上几种我们无际单片机项目用的也非常多,比如自己任务调度、LED特效、按键检测、菜单、几种防盗报警模式等等。

    yy4d41wrffl6402385117.png

    yy4d41wrffl6402385117.png

                   

    nthueywmfsm6402385217.png

    nthueywmfsm6402385217.png

    所以项目不是关键,关键是实现的水平,在什么高度。
               
    把这些实现细节讲给面试官听,内行人能听得出来你是有水平的,对于入行来说,完全够了,哪怕做了嵌入式工程师4-5年,都不见得听过表驱动法,更别说用了。

               
    5.用表驱动法的小心机
    表驱动法这么好用,但也不是没坑,下面的坑不要踩:
    ?表得全:漏了个情况,程序就懵了,查表查不到可不会自己猜。
    ?性能得掂量:单片机那点资源,像51单片机项目就没必要用了,表太大了查找慢,必要时可以用二分查找或者哈希优化。
    ?调试别偷懒:表里的逻辑不像if-else那么直白,多写点注释,不然回头自己都看不懂。
               
    我第一次看到那些大佬写的代码,没注释,也边看边骂,写这么复杂干吊?还是if-else看起来轻松。
               
    不过这些小毛病比起它的优点,简直不值一提。只要用得顺手,表驱动法绝对是你代码里的得力助手。
               
    6. 总结下
    说到底,程序架构这东西,它不仅是一种编程技术,更是一种设计思维,同一个项目,不同段位的人去做,稳定性各方面都不同,就取决于这种思维和技术的经验积累。
               
    而表驱动法只是其中之一,在单片机开发中,无论是命令解析、状态机设计,还是配置管理,表驱动法都能帮你写出更优雅的代码
               
    接下来,我强烈建议你亲自下场,感受“改表不改码”的快感。
               
    下次领导再改需求,你先苦巴巴说,这个功能不好做呀,可能要2周。然后暗地里笑笑眯眯地改张表,几分钟搞定,其它时间,当然是用来摸鱼啦,以前我就经常干这种事。


    end

    yfmr0gzrz0h6402385317.jpg

    yfmr0gzrz0h6402385317.jpg


    下面是更多无际原创的个人成长经历、行业经验、技术干货。
    1.电子工程师是怎样的成长之路?10年5000字总结
    2.如何快速看懂别人的代码和思维
    3.单片机开发项目全局变量太多怎么管理?
    4.C语言开发单片机为什么大多数都采用全局变量的形式
    5.单片机怎么实现模块化编程?实用程度让人发指!
    6.c语言回调函数的使用及实际作用详解

    7.手把手教你c语言队列实现代码,通俗易懂超详细!

    8.c语言指针用法详解,通俗易懂超详细!
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表