驱动层介绍
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)