IOT-OS之RT-Thread(十二)--- 驱动分层与主从分离思想

本文详细介绍了RT-Thread操作系统中驱动分层和主从分离的思想,通过UART、I2C、SPI设备驱动的实例,阐述了驱动分层的好处和实现方式,以及主从分离在SPI设备中的应用。驱动分层有助于各层专注于各自的任务,而主从分离思想在非对等总线如I2C和SPI中,通过总线控制器管理多个从设备,提高了驱动的可移植性和管理效率。
摘要由CSDN通过智能技术生成

一、驱动分层思想

通过前面对RT-Thread设备模型框架,以及UART、IIC、SPI 等设备驱动实现过程的介绍,我们应该对驱动分层思想并不陌生了。操作系统为什么对设备驱动采用分层管理呢?驱动分层有什么好处呢?

在介绍驱动分层的好处前,我们先看看著名的TCP/IP协议栈分层模型
TCP/IP协议栈分层模型
TCP/IP协议栈每层都有自己独特的作用:

  • 网络接口层负责对网卡硬件设备的访问,在进行网卡驱动开发时,需要将网卡硬件的属性、接口等信息注册到网络接口层;
  • 网络层则负责网际寻址与路由,负责两个主机之间的寻址与连接;
  • 传输层则负责两个应用程序端口之间的连接与数据传输;
  • 应用层则负责网络数据格式的定义,并向上层APP提供网络访问的接口等。

类比TCP/IP协议的分层思想,不难得知操作系统驱动分层的好处,再参照RT-Thread I/O 设备模型框架:
I/O设备模型框架
RT-Thread I/O设备模型框架位于硬件和应用程序之间,共分成三层,每一层也都有自己的作用:

  • 设备驱动层负责对各种硬件设备的访问,在进行设备驱动开发时,需要将硬件设备的属性配置、访问接口等信息注册到上面的设备驱动框架层;
  • 设备驱动框架层负责定义主机控制器(比如CPU、MCU)与外设之间传输数据的格式和规则(比如总线通信协议中与硬件无关的上层协议),并按照上面 I/O 设备管理层要求的接口形式,向上层注册设备对象与访问接口;
  • I/O 设备管理层则将不同总线设备的访问接口封装为统一的标准接口,让上面的应用程序可以通过该层提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。

协议和驱动虽然分层管理,每一层专注完成自己的事情,但层与层之间想要协同工作,还需要留出各自的接口用来交互资源信息等,最常见的接口就是设备对象的注册或初始化,当然也包括设备访问接口的注册(包含在设备对象的注册过程中)。RT-Thread创建设备对象、向上面的 I/O 设备管理器注册设备对象和访问接口、应用程序通过设备对象和接口访问设备的一般过程如下所示:
应用程序访问设备的过程
创建设备实际上就是在设备驱动层创建一个设备对象,并将该硬件设备的属性信息(比如基地址、中断号、时钟、DMA等)与访问接口保存到该设备对象结构体或类中,将初始化后的设备对象向上注册到 I/O 设备管理器中。

既然可以将设备对象向上注册,又可以从上层访问该设备对象,这就要求不同层级描述设备的结构体或类相互兼容,存在自上而下的继承关系。上层保存同类设备的通用属性和访问接口,下层增加硬件设备的专有属性并完成硬件访问接口的实现,就像下面这样:
设备描述结构体间的继承关系
下面分别用前面介绍过的串口设备、I2C设备、SPI设备为例,回顾下具体的设备驱动是如何将驱动分层思想实现在代码中的。

1.1 UART设备驱动分层

每一层都有自己的设备描述结构和接口函数集合,每一层的接口函数集合由低一层实现并注册。在当前层直接调用,用来实现更上层的接口函数集合。

1.1.1 串口设备驱动框架层

串口设备的驱动框架层提供的设备描述结构和接口函数集合如下:

// .\rt-thread-4.0.1\components\drivers\include\drivers\serial.h

struct rt_serial_device
{
   
    struct rt_device          parent;

    const struct rt_uart_ops *ops;
    struct serial_configure   config;

    void *serial_rx;
    void *serial_tx;
};
typedef struct rt_serial_device rt_serial_t;

struct rt_uart_ops
{
   
    rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
    rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);

    int (*putc)(struct rt_serial_device *serial, char c);
    int (*getc)(struct rt_serial_device *serial);

    rt_size_t (*dma_transmit)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction);
};

串口设备框架层使用rt_uart_ops接口函数实现 I / O 设备管理层的接口函数集合rt_device_ops,并将实现的接口函数集合serial_ops注册到上层的过程如下:

// .\rt-thread-4.0.1\components\drivers\serial\serial.c

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops serial_ops = 
{
   
    rt_serial_init,
    rt_serial_open,
    rt_serial_close,
    rt_serial_read,
    rt_serial_write,
    rt_serial_control
};
#endif

/*
 * serial register
 */
rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
                               const char              *name,
                               rt_uint32_t              flag,
                               void                    *data)
{
   
    rt_err_t ret;
    struct rt_device *device;
    RT_ASSERT(serial != RT_NULL);

    device = &(serial->parent);

    device->type        = RT_Device_Class_Char;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;

#ifdef RT_USING_DEVICE_OPS
    device->ops         = &serial_ops;
#else
    ......
#endif
    device->user_data   = data;

    /* register a character device */
    ret = rt_device_register(device, name, flag);

#if defined(RT_USING_POSIX)
    /* set fops */
    device->fops        = &_serial_fops;
#endif

    return ret;
}

接口函数集合serial_ops中的函数实现最终都是通过调用rt_uart_ops接口函数实现的,rt_uart_ops接口函数则由更底层的UART设备驱动层来实现并注册。

1.1.2 串口设备驱动层

串口设备驱动层提供的设备描述结构如下:

// .\libraries\HAL_Drivers\drv_usart.h

/* stm32 uart dirver class */
struct stm32_uart
{
   
    UART_HandleTypeDef handle;
    struct stm32_uart_config *config;
    
#ifdef RT_SERIAL_USING_DMA
    struct
    {
   
        DMA_HandleTypeDef handle;
        rt_size_t last_index;
    } dma;
#endif
    rt_uint8_t uart_dma_flag;
    struct rt_serial_device serial;
};

// .\libraries\HAL_Drivers\drv_usart.c

static struct stm32_uart uart_obj[sizeof(uart_config) / sizeof(uart_config[0])] = {
   0};

static struct stm32_uart_config uart_config[] =
{
   
#ifdef BSP_USING_UART1
    {
                                                              
    	.name = "uart1",                                            
    	.Instance = USART1,                                        
    	.irq_type = USART1_IRQn,                                   
    }
#endif
......
};

stm32_uart结构中包含的UART_HandleTypeDef由芯片厂商ST提供的标准库HAL提供,UART驱动层用以实现上层rt_uart_ops接口函数的基础函数也由HAL库提供,可能用到的HAL库函数如下:

// .\libraries\STM32L4xx_HAL\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h

HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DeInit(UART_HandleTypeDef *huart);

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);

HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart);
uint32_t              HAL_UART_GetError(UART_HandleTypeDef *huart);

串口设备驱动层使用HAL库函数实现串口设备框架层的接口函数集合rt_uart_ops,并将实现的接口函数集合stm32_uart_ops注册到上层的过程如下:

// .\libraries\HAL_Drivers\drv_usart.c

static const struct rt_uart_ops stm32_uart_ops =
{
   
    .configure = stm32_configure,
    .control = stm32_control,
    .putc = stm32_putc,
    .getc = stm32_getc,
};

int rt_hw_usart_init(void)
{
   
    rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct stm32_uart);
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    rt_err_t result = 0;

    stm32_uart_get_dma_config();
    
    for (int i = 0; i < obj_num; i++)
    {
   
        uart_obj[i].config = &uart_config[i];
        uart_obj[i].serial.ops    = &stm32_uart_ops;
        uart_obj[i].serial.config = config;

#if defined(RT_SERIAL_USING_DMA)
        if(uart_obj[i].uart_dma_flag)
        {
   
            /* register UART device */
            result = rt_hw_serial_register(&uart_obj[i].serial,uart_obj[i].config->name,
                                           RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX| RT_DEVICE_FLAG_DMA_RX 
                                           ,&uart_obj[i]);
        }
        else
#endif
        {
   
            /* register UART device */
            result = rt_hw_serial_register(&uart_obj[i].serial,uart_obj[i].config->name,
                                           RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX
                                           ,&uart_obj[i]);
        }
        RT_ASSERT(result == RT_EOK);
    }

    return result;
}

函数rt_hw_usart_init内部有一个for循环,可以将所有启用的串口设备全部初始化并注册到 I/O 设备管理层。启用串口设备也比较简单,先使用CubeMX配置好要启用的串口设备引脚,再在Kconfig和menuconfig中配置并使能相关的宏定义就可以了。rt_hw_usart_init函数已经在board.c文件中的rt_hw_board_init()函数内部被调用(需要定义宏RT_USING_SERIAL),在博客系统启动与初始化过程中有介绍。

1.1.3 串口设备中断回调支持

到这里就可以通过 I/O 设备管理接口rt_device_ops来访问uart串口设备了,还记得rt_device设备描述结构中有两个中断回调函数,I/O设备管理层也有两个函数接口分别用来设置这两个可由用户自定义的中断回调函数吗?

// .\rt-thread-4.0.1\include\rtdef.h

/* Device structure */
struct rt_device
{
   
......
    /* device call back */
    rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
    rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);

#ifdef RT_USING_DEVICE_OPS
    const struct rt_device_ops *ops;
......
};

// .\rt-thread-4.0.1\include\rtthread.h

rt_err_t
<
  • 11
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
奇特物联开源 IoT-mp-Home是一个开源的物联网管理平台。物联网(Internet of Things,IoT)即将成为现实中的一个巨大网络,物联技术的发展正在极大地改变人们的生活方式和工作方式。奇特物联开源的目的是为了促进物联网技术的发展和推广,提供一个开放、灵活的平台供开发人员和研究者使用。 IoT-mp-Home提供了一套完整的物联网管理系统,包括设备管理、数据管理、远程控制等功能。它可以连接多种类型的设备,如传感器、执行器、智能家居设备等,并通过互联网进行通信和交互。开发者可以利用该平台来构建智能家居系统、智能城市解决方案等。 使用IoT-mp-Home,用户可以方便地监控和控制各种设备。通过平台提供的web界面,用户可以实时查看设备的状态、传感器数据等。同时,用户还可以远程控制设备,根据需要进行操作和调节。这使得用户能够更加方便地管理并优化自己的家居环境。 此外,IoT-mp-Home还支持数据的收集和分析。它可以自动收集来自各个设备的数据,并将其存储在云端。用户可以利用这些数据来进行分析,发现隐藏的规律和趋势,并根据这些分析结果来进行决策和优化。 总之,奇特物联开源的IoT-mp-Home是一个功能强大且开放的物联网管理平台,为开发者和研究者提供了一个极好的工具,促进了物联技术的发展和应用。它的出现将进一步推动物联网领域的创新,并为我们的生活带来更多的便利和智能化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流云IoT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值