• 单片机代码分层


    1.在代码中我会将整个工程分为三层:硬件层,中间层,应用层:

    硬件层:

            1.主要是硬件的初始化,以及从硬件中采集数据。初始化没什么好讲的,我们主要讲如何接受采集的数据。

    为了提高程序可读性和修改硬件接口简单,我们定义一些硬件接口时,使用宏定义来替换硬件接口:

    #define NFC_12M_CLK_PORT  GPIOC
    #define NFC_12M_CLK_PIN   GPIO_Pin_10

    #define NFC_RST_PORT  GPIOA
    #define NFC_RST_PIN   GPIO_Pin_4

             2.硬件层就是我们的传感器,我们需要将传感器中的数据读出来。我们知道,读数据其实就是将DR(data resigster)中的数据放到一个变量中。

            3.一般采集数据有两种方式:一种通过中断触发后然后读数据(例如串口数据在中断中接收),另一种通过主动轮询读数据(例如ADC数据采集(非中断))。

            4.为了达到分层效果,意味着我的bsp.c和bsp.h这两个文件不能包括任何外部的头文件(由开发人员自己写的),只要保证这种效果,分层自然开始了。

            5.当然我们这里的数据要如何发送给中间层去简单的处理,这里还是分中断方式和非中断方式:中断方式采用函数回调的方法,非中断方式采用返回值的方法

    中间层:

            1.这一层是对采集到的原始数据做处理的,将采集到的冰冷的数据进行处理,得到不同的信号,状态。

            2.同理这一层我们也不包含任何自己写的外部头文件,只要参杂了任意自定义的头文件那么就表示分层失败。(这一点只能说尽量实现,我有尝试过,如果强制的不包含硬件层的.h文件,会出现很多带参函数,降低程序效率,以及一些可读性,中断回调可以做到不去和硬件层勾搭在一起,而比如IO状态,ADC采集,我们还是需要将数据传到中间层,这时候还是用宏定义来替换硬件层接口)

            3.这一层一个入口(硬件层采集到的数据),一个出口(信号,状态等等)。

            4.如果应用层有多个应用需要去读中间层返回值,那么我们这时候就不能用单一函数去返回处理出来的状态,而要用一个全局变量去接处理出来的状态,然后大家可以通过访问这个全局变量(为了提高代码可读性,这个全局变量使用枚举赋值)知道自己相应的要做什么(比如按键按下,长按开灯,短按电机转起来)。当如果只要一个应用层去读中间层的状态,那么我们直接让中间层处理完毕的数据返回即可。

            如果我们当前中间层需要反馈出不同的状态量(比如多个按键的长按短按),如果用多个u8类型也是可以的,其实这时候我们完全可以用一个u8类型的位域,处理4个按键,每个按键占用两个bit(可以表示4种状态)。并且像位域,结构体他们都让程序中的数据看起来更简洁。

            5.中间层一般涉及到的难点应该也能叫做算法:比如ADC滤波,串口数据队列

    应用层:

            1.这一层可以说是最难写的,需要将所有的中间层或者硬件层的数据组织起来,如果组织不好将会导致写出来的应用到处是bug,或者后面开发时需求一改,整个应用层逻辑全部打乱。

            2.当然这一层我们一般都是调用中间层,然后将中间层返回的状态做最终的处理。比如就点亮一个普通的灯,这个灯可能会读很多状态,按键按下开灯,再按下关灯,ADC采集到低电压灯闪烁,档位调节改变亮度等等。

            3.例如我需要处理串口接收到的数据(TDE模块),我需要获取串口中断发生的情况,需要用到定时器计数,需要用到串口中间层处理完毕后的数据。 当然我还需要一些功能函数

            4.如果遇到更改IC的情况,只要我们硬件层和中间层做好的分离,我们只要稍微改动硬件层就可以,因为从目前逻辑来看,中间层实际也不和硬件直接相关。

            之前看到过一句这样的话:如果想让各个层级之间分离,只需要在两个层之间加一个层就行,如果还不行就再加一层。这句话意思就是为了解除代码层之间的关联,我们需要适当添加代码层。(之前看到国际空间站为了能够停靠各个国家的飞船,他们就搞了一些接口来连接飞船。我们假设国际空间站的接口是圆形的,而其他国家飞船是各种形状,各种口径的接口,为了能够连接,就需要中间搞一个接口)

            5.如果我们在写应用层的时候,发现了统一的处理应用,我们也可以打包,让应用层也具有可移植性

    最后:

            1.最后我们初始化硬件,调用应用层就可以了,中间层已经被应用层调用过一遍了。

    最主要的我们还是要想好如何将硬件层能尽快的和中间层分开,在linux中用用统一的接口去屏蔽底层操作如我这篇文章写的(嵌入式驱动分层_他日仙界再相逢,一声道友尽沧桑的博客-CSDN博客)。当然这种方法并不好,我对硬件和驱动分离非常痴狂正在摸索最优的解决方案。

    做法其实有很多种,我之前的办法是传参,但是传多了会感觉很烦。为了达到和linux类似的效果。我这里想到了一个不错的方法,那就是用宏来代替底层的一些函数,这样节约空间,执行效率也变高了。

    IO类使用宏替换

    1. void led_pin(u8 value)
    2. {
    3. P1^1 = value;//51输出高低电平
    4. GPIO_SetBits(GPIOA, GPIO_Pin_1, value);//stm32输出高低电平
    5. }
    6. //我们把上面这个函数设置在BSP层中,当然我们可以调用
    7. //但是为了屏蔽,我们就要再套一层,而再套一层增加消耗,降低效率
    8. //这时候我们可以使用宏来屏蔽,当然宏是写在中间层的
    9. #define LED1(value) led_pin(value)
    10. //同理按键也用类似的方法
    11. //按键屏蔽某个脚可以更加直接
    12. #define KEY() P1^1
    13. #define KEY() GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)

    也许有人会说,这样和调用底层函数没啥区别,确实,但是有天我要移植到其他工程,那我只需要把中间层的头文件宏改一下就可以了,换成其他硬件的函数就可以了。 

     数据类

    中断采集的数据,例如串口,使用回调,有返回值我们也可以用宏替换(例如ADC数据采集)

    分层的重要性

    目前遇到最多的事情就是各种替换IC,器件,设备,有些东西是完全兼容的,但是往往很多都是不兼容的。

            比如IC替换,所有底层都要重新,如果本来就是自己写的代码还好,要是别人写的(一般这个人都离职了),代码没有分层,那就只能重写。

            外围器件,设备替换,基本都是完全兼容(当然这种一般都是会完全兼容,因为不可能重写画板,评估,仅仅是找到更便宜的器件了,或停产);但是不排除看似兼容实际不同的,这就非常坑了,如果代码写得乱,根本不敢大改,因为根本没时间。

  • 相关阅读:
    js promise从入门到精通
    Prometheus邮件告警
    电脑技巧:27个Office使用小技巧,值得收藏
    [计算机通信网络]子网掩码
    全新Storm Core API管理系统源码 免授权版
    万宾科技内涝积水监测仪使用效果一览
    如何像我这样创建一个酷炫且能赚钱的网站(使用宝塔安装WordPress搭建子比主题)
    【MySQL】sql调优实战教学
    三西格玛和六西格玛区别是什么?优思学院用一幅图告诉你
    操作系统原理实验四:管道通信、消息通信程序
  • 原文地址:https://blog.csdn.net/qq_38591801/article/details/127662823