驱动层介绍

BabyOS里面有几个重要的概念:设备、驱动和硬件接口。

设备:是一个实体,与MCU通过硬件接口相连接。

驱动,是针对设备的一份软件逻辑代码

硬件接口:是设备与MCU相连的数字接口

操作硬件接口,使用HAL层的接口,操作设备,使用bOpen bClose等接口

例如:SPIFLASH是一个设备,b_drv_spiflash是驱动,SPI是硬件接口

bos/driver/inc/b_driver.h里定义了驱动的统一接口:

typedef struct bDriverIf
{
    int status;
    int (*init)(struct bDriverIf *pdrv);
    int (*open)(struct bDriverIf *pdrv);
    int (*close)(struct bDriverIf *pdrv);
    int (*ctl)(struct bDriverIf *pdrv, uint8_t cmd, void *param);
    int (*write)(struct bDriverIf *pdrv, uint32_t offset, uint8_t *pbuf, uint32_t len);
    int (*read)(struct bDriverIf *pdrv, uint32_t offset, uint8_t *pbuf, uint32_t len);
    void             *hal_if;
    const char       *pdes;
    uint32_t          drv_no;
    bQueueInstance_t *preadbuf;
    bQueueInstance_t *pwritebuf;
    union
    {
        uint32_t v;
        void    *_p;
    } _private;
} bDriverInterface_t;

每个驱动文件的目标便是实现bDriverInterface_t里面的各个元素。

status 驱动初始化异常则将 status 设为-1 反之设 为 0。 操作设备时检测此项,如果是-1 则不执行。

init 负责执行初始化,用于运行过程中再次初始化。

open 负责唤醒操作,此处可执行设备唤醒。如果设备没有休眠状态,可以赋值为 NULL 。

close 负责休眠的操作,此处可执行设备休眠。如果设备没有休眠状态,可以赋值为 NULL 。

ctl 负责对设备进行配置或者执行特定的操作,例如擦除,切换状态等。ctl 的调用需要传入指令 cmd 和对应的参数。 执 行成功返回 0,失败或者不支持指令则返回-1。

write 负责传输数据至设备,执行成功返回实际发送的数据长度,执行失败则返回-1。

read 负责从设备获取数据,获取数据的最小单元依据设备功能而定,例如,存储设备,最小可以获取 1 个字节;3 轴加速度设备,最小单元为 3 个加速度值;温湿度传感器最小单元是一组温度湿度值。读取的最小单元需 要在驱动的 h 文件进行说明让使用者能明白。

_hal_if 指向当前驱动对应的硬件接口。

pdes 指向设备的描述信息

drv_no 指同类设备中的序号,例如有3个SPIFLASH,那么它们的编号0,1,2便是存在drv_no。

preadbuf 读取缓存

pwritebuf 写入缓存

private 当驱动需要携带私有参数时,则利用这个字段。例如 flash 的 id,可以放在 _private.v。如果需要存放更多的信息,那么就利用_private.p 指向一片数据区域。

硬件接口

硬件接口通过HAL_XXXX_IF指定,具体可查看b_hal_if.h文件:

//----------------------------------------------------------------
//     驱动文件的命名规则 b_drv_<驱动名小写>.c .h
//     每个驱动文件里有宏定义  #define DRIVER_NAME 驱动名大写
//     例如: spiflash驱动
//           驱动文件为 b_drv_spiflash.c .h
//           c文件里面定义宏 #define DRIVER_NAME SPIFLASH
//     驱动需要在此文件(b_hal_if.h)定义HAL层接口
//     #define HAL_<DRIVER_NAME>_IF
//     例如spiflash #define HAL_SPIFLASH_IF {具体的数据接口查看h文件}
//--------------------------------------------------------------------
//     如果有多个spiflash:
//     #define HAL_SPIFLASH_IF {第一个SPIFLASH},{第二个SPIFLASH}
//--------------------------------------------------------------------

图片

例如2个SPIFLASH,1个24C02

//b_hal_if.h
#define HAL_24CXX_IF                                                                    
    {                                                        \                      
        .dev_addr = 0xa0,                                    \
        .is_simulation = 1,                                  \
        ._if.simulating_i2c.clk = {B_HAL_GPIOB, B_HAL_PIN6}, \
        ._if.simulating_i2c.sda = {B_HAL_GPIOB, B_HAL_PIN7}, \                   
    }  

#define HAL_SPIFLASH_IF                                 \
    {                                                   \
        .is_spi   = 1,                                  \
        ._if._spi = {                                   \
            .is_simulation = 0,                         \
            .cs            = {B_HAL_GPIOB, B_HAL_PIN9}, \
            ._if.spi       = B_HAL_SPI_1,               \
        },                                              \
    },                                                  \
    {                                                   \
        .is_spi   = 1,                                  \
        ._if._spi = {                                   \
            .is_simulation = 0,                         \
            .cs            = {B_HAL_GPIOB, B_HAL_PIN8}, \
            ._if.spi       = B_HAL_SPI_1,               \
        },                                              \
    }

注册设备

注册设备,便是设备与驱动建立联系的过程:

b_device_list.h中通过宏进行注册:

/**
typedef enum
{
    B_DRIVER_NULL = 0,
    B_DRIVER_24CXX,
    B_DRIVER_DS18B20,
    B_DRIVER_ESP12F,
    B_DRIVER_FM25CL,
    B_DRIVER_ILI9320,
    B_DRIVER_ILI9341,
    B_DRIVER_KEY,
    B_DRIVER_LIS3DH,
    B_DRIVER_MATRIXKEYS,
    B_DRIVER_MCUFLASH,
    B_DRIVER_OLED,
    B_DRIVER_PCF8574,
    B_DRIVER_SD,
    B_DRIVER_SPIFLASH,
    B_DRIVER_SSD1289,
    B_DRIVER_ST7789,
    B_DRIVER_TESTFLASH,
    B_DRIVER_XPT2046,
    B_DRIVER_NUMBER
} bDriverNumber_t;
*/
/**
    B_DEVICE_REG(dev_1, bDriverNumber_t, "description")
    .....
    B_DEVICE_REG(dev_n, bDriverNumber_t, "description")
 */

B_DEVICE_REG(bTESTFLASH, B_DRIVER_TESTFLASH, "testflash")

设备注册后,便会自动生成如下数据结构以及定义的数组:

//b_device.h
//生成设备号
typedef enum
{
#define B_DEVICE_REG(dev, driver, desc) dev,
#include "b_device_list.h"
    B_REG_DEV_NULL,
    B_REG_DEV_NUMBER
} bDeviceName_t;
//b_device.c
//驱动号数组
static bDriverNumber_t bDriverNumberTable[B_REG_DEV_NUMBER] = {
#define B_DEVICE_REG(dev, driver, desc) driver,
#include "b_device_list.h"
    B_DRIVER_NULL,
};
//设备描述信息数组
static const char *bDeviceDescTable[B_REG_DEV_NUMBER] = {
#define B_DEVICE_REG(dev, driver, desc) desc,
#include "b_device_list.h"
    "null",
};
//驱动实例数组
static bDriverInterface_t bDriverInterfaceTable[B_REG_DEV_NUMBER];

根据注册的设备,定义相应数量的驱动实例。以宏的形式来实现,避免对动态内存的依赖。

BabyOS的代码中,在尽可能避免使用动态内存。

操作设备

int bOpen(uint32_t dev_no, uint8_t flag);
int bRead(int fd, uint8_t *pdata, uint32_t len);
int bWrite(int fd, uint8_t *pdata, uint32_t len);
int bCtl(int fd, uint8_t cmd, void *param);
int bLseek(int fd, uint32_t off);
int bClose(int fd);

int bInit(void);
int bExec(void);
int bReinit(uint32_t dev_no);

int bModifyHalIf(uint32_t dev_no, uint32_t type_size, uint32_t off, const uint8_t *pval,
                 uint8_t len);

dev_no 注册设备时指定的设备号

fd 打开设备后返回的句柄。

配置项_HALIF_VARIABLE_ENABLE用于配置是否允许硬件接口可以改动。

#if _HALIF_VARIABLE_ENABLE
#define HALIF_KEYWORD static
#else
#define HALIF_KEYWORD const static
#endif

bModifyHalIf使用例子:

//oled硬件接口数据结构是 bOLED_HalIf_t
typedef struct
{
   union
  {
       bHalI2CIf_t _i2c;
       bHalSPIIf_t _spi;
  } _if;
   uint8_t is_spi;
} bOLED_HalIf_t;
// 修改IIC的设备地址 OLED是注册的设备号,dev_addr变量存放着新的指。
bModifyHalIf(OLED, sizeof(bOLED_HalIf_t),(uint8_t)(&(((bOLED_HalIf_t *)0)->_if._i2c.dev_addr)), &dev_addr, 1)