## 驱动层介绍 BabyOS里面有几个重要的概念:设备、驱动和硬件接口。 设备:是一个实体,与MCU通过硬件接口相连接。 驱动,是针对设备的一份软件逻辑代码 硬件接口:是设备与MCU相连的数字接口 操作硬件接口,使用HAL层的接口,操作设备,使用bOpen bClose等接口 *例如:SPIFLASH是一个设备,b_drv_spiflash是驱动,SPI是硬件接口* `bos/driver/inc/b_driver.h`里定义了驱动的统一接口: ```C 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文件: ```C //---------------------------------------------------------------- // 驱动文件的命名规则 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__IF // 例如spiflash #define HAL_SPIFLASH_IF {具体的数据接口查看h文件} //-------------------------------------------------------------------- // 如果有多个spiflash: // #define HAL_SPIFLASH_IF {第一个SPIFLASH},{第二个SPIFLASH} //-------------------------------------------------------------------- ``` ![图片](../../_static/driver.png) *例如2个SPIFLASH,1个24C02* ```C //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`中通过宏进行注册: ```C /** 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") ``` 设备注册后,便会自动生成如下数据结构以及定义的数组: ```C //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的代码中,在尽可能避免使用动态内存。 ### 操作设备 ```C 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`用于配置是否允许硬件接口可以改动。 ```C #if _HALIF_VARIABLE_ENABLE #define HALIF_KEYWORD static #else #define HALIF_KEYWORD const static #endif ``` `bModifyHalIf`使用例子: ```C //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) ```