• 2
    回复
  • 收藏
  • 点赞
  • 分享
  • 发新帖

单片机软件进阶——分层设计与面向对象。

      好久没更帖子了,写这篇帖子有点底气不足,毕竟没有太深厚的内力(功底),算是借鉴心法总纲出来抛砖引玉吧,希望给道友们打开一片新的世界。

      第一个疑惑点:单片机大部分情况下都是使用C语言进行开发,虽然IDE也支持C++,但是真正要用到C++的几乎微乎其微,提起面向对象,脑子里第一个词可能就是:C++。其实告诉你一个小秘密,linux操作系统绝大部分都是用C语言开发的,同样应用了大量的面向对象的设计,也就是说,C语言的威力可能超乎我们的想象。

      本篇帖子是以RT-thread中设备IO驱动框架的实现为例子(心法总纲)来聊一聊单片机的中分层技术和面向对象:

上图是设备管理层的构架图(分层思想一目了然),面向对象的类与继承应用最多,这里我们只关注设备驱动框架层以下的三层,因为用到的是pin设备,比较特殊,她并没有应用I/O设备管理层。

其实设备驱动的核心就是抽象出了一个设备对象,一切都围绕着设备驱动对象展开,pin设备抽象类(结构体实现)如下:

Pin设备对象的定义如下:static struct rt_device_pin _hw_pin;

这里定义了一个设备实例,但是没有对实例对象初始化,还无法具体的操作,因为其内部结构体成员为空。

接下来需要进行关键的一步,就是设备实例的初始化。

以下函数中进行hw_pin设备实例的初始化:

int rt_hw_pin_init(void)
{
#if defined(__HAL_RCC_GPIOA_CLK_ENABLE)
    __HAL_RCC_GPIOA_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOB_CLK_ENABLE)
    __HAL_RCC_GPIOB_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOC_CLK_ENABLE)
    __HAL_RCC_GPIOC_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOD_CLK_ENABLE)
    __HAL_RCC_GPIOD_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOE_CLK_ENABLE)
    __HAL_RCC_GPIOE_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOF_CLK_ENABLE)
    __HAL_RCC_GPIOF_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOG_CLK_ENABLE)
    #ifdef SOC_SERIES_STM32L4
        HAL_PWREx_EnableVddIO2();
    #endif
    __HAL_RCC_GPIOG_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOH_CLK_ENABLE)
    __HAL_RCC_GPIOH_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOI_CLK_ENABLE)
    __HAL_RCC_GPIOI_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOJ_CLK_ENABLE)
    __HAL_RCC_GPIOJ_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOK_CLK_ENABLE)
    __HAL_RCC_GPIOK_CLK_ENABLE();
#endif

    return rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);
}

关键的在于调用rt_device_pin_register函数,这个函数来自于设备驱动框架层,其中hw_pin对象也来自于设备驱动框架层。

输入参数有注册设备名称”pin”,设备操作函数结构体指针_stm32_pin_ops,这两个参数,通过上层注册函数将hw_pin对象与底层的设备的操作函数建立联系。

关于_stm32_pin_ops的结构体(其实C语言中的结构体,在面向对象中可以看作是类,结构体与实例,对应于类与对象的概念)原型如下:

接下来看一下rt_device_pin_register函数:

_hw_pin的parent成员是device类型对象,pin设备并没有用到设备管理层API ,而是直接用的设备驱动框架层提供的API函数:

主要有以下几个:

void rt_pin_mode(rt_base_t pin, rt_base_t mode);  //设置引脚模式

void rt_pin_write(rt_base_t pin, rt_base_t value);   //引脚电平输出设置

int  rt_pin_read(rt_base_t pin); //引脚电平输入读取

设备对象注册函数rt_device_register(&_hw_pin.parent,name,RT_DEVICE_FLAG_RDWR);完成了对象管理器相关的注册(实际是链表相关的操作):

执行之后,当系统运行起来以后,我们可以在shell命令窗口输入list_device命令,查看系统当前所有的设备,会发现我们的设备成功注册:

到这里,扔出来的砖就结束了,其实很想能够将自己想表达的东西传递给大家,但是读完写的帖子又总觉得差强人意,如果筒子们对嵌入式软件感兴趣,一定要尝试着去玩玩RTT,她会带给你很多意想不到的收获,底子扎实的又有机会的可以直接晋级嵌入式linux领域(其实多数RTOS都是linux的皮毛,学术角度,当然各有所长),她不止能带给你高大上的赶脚:

全部回复(2)
正序查看
倒序查看
ruohan
LV.9
2
2022-12-19 08:30

我们现在用的架构和你讲解的差不多,看程序看的脑袋疼,,

 

0
回复
2023-01-03 09:24
@ruohan
我们现在用的架构和你讲解的差不多,看程序看的脑袋疼,, 

习惯套路就好了

0
回复