bos框架使用
前面已经初步理解了bos框架,现在就看其关键的部分,如何实现的一个设备框架??
关于框架调度
除了在bos初识中说到的循环调度bExec();就是基于SysTIck的计时调度,可以说裸机的程序都有了。
使用时只要使用b_section.h里的BOS_REG_POLLING_FUNC()
注册一下就可以了。这个一般在组件里使用,或自己写的函数。
定时调度,使用b_os.h中的 BOS_PERIODIC_TASK()
;注册一下就可以使用了。
更新
最近,在官网上看到,代码已经迭代了,具明显的就是不再分三个仓库了,而是把配置及hal仓库合并到了主仓库里面。
再顺一遍建工程流程
新建一个空工程
添加bos 代码
执行 git init
;将工程目录变为git目录
执行git submodule add https://gitee.com/notrynohigh/BabyOS.git
;将baby 添加进工程
将os代码添加进工程
路径 | 部分/全部 | 备注 |
---|---|---|
bos/algorithm | 根据需要添加 | |
bos/core | 全部添加 | |
bos/drivers | 根据需要添加 | 不需要的驱动不要添加到工程中 |
bos/hal | 全部添加 | |
bos/mcu | 根据需要添加 | 根据平台添加对应的代码,对应b_config.h中的配置 |
bos/modules | 全部添加 | 全部添加后,可在b_config中配置 |
bos/thirdparty | 根据需要添加 | CmBacktrace 错误跟踪 FatFS 文件系统 littlefs 文件系统 FlexibleButton 使用按键模块则需要添加 nr_micro_shell 命令行 SFUD 使用SPIFLASH驱动需要添加 UGUI 简单的GUI库 |
bos/utils | 全部添加 | 通用代码,有模拟的IIC和SPI代码,模拟串口接收空闲事件 |
bos/_config | b_config.h BabyOS 配置文件 b_device_list.h 注册设备的文件 b_hal_if.h 驱动接口文件 |
首先将,需要全部添加的,都加进工程,其他根据需要添加。
添加头文件路径
添加两个路径即可:
bos/
_config/
如果配置文件拷贝到其他路径了,则添加相应路径即可。
修改配置
配置项 | 说明 | 备注 |
---|---|---|
Version Configuration | 版本配置项,硬件和固件版本 | |
Platform Configuration | 平台配置项,指定心跳频率和MCU平台 | |
Modules Configuration | 模块配置项,各个功能模块的配置 | |
Thirdparty Configuration | 第三方开源代码配置项 |
调用必要的函数
Include头文件 b_os.h
①滴答定时器中断服务函数调用 bHalIncSysTick();
②初始化代码 bInit();
③主循环调用 bExec();
新版本特点
新版本加入了mcu部分,支持的mcu还是比较少的。如对st,只支持F107,这块还必须得有,否则会编译报错。并且,还需要完善,所以还是以旧版本为参考。
设备注册到使用
例子用到的spi与qspi区别:
SPI协议其实是包括:Standard SPI、Dual SPI和Queued SPI三种协议接口,分别对应3-wire, 4-wire, 6-wire。
- Standard SPI,有4根信号线,分别为CLK、CS、MOSI和MISO。数据线工作在全双工。
- Dual SPI,它只是针对SPI Flash而言,不是针对所有SPI外设。对于SPI Flash,全双工并不常用,因此扩展了mosi和miso的用法,让它们工作在半双工,用以加倍数据传输。也就是对于Dual SPI Flash,可以发送一个命令字节进入dual mode,这样mosi变成SIO0(serial io 0),mosi变成SIO1(serial io 1),这样一个时钟周期内就能传输2个bit数据,加倍了数据传输。
- 类似的,还可以扩展,Qual SPI Flash增加了两根I/O线(SIO2,SIO3),目的是一个时钟内传输4个bit
而QSPI就是Queued SPI的简写。
注册
/b_device_list.h,在里面添加使用的外设,B_DEVICE_REG(SPIFLASH, bSPIFLASH_Driver[0], "flash")
注意:这个头文件最后有个#undef B_DEVICE_REG
,也就是说注册完了后,就取消了这个宏
随后在b_device.h中完成名称赋值,如下:
typedef enum
{
#define B_DEVICE_REG(dev, driver, desc) dev,
#include "b_device_list.h"
bDEV_MAX_NUM
} bDeviceName_t;//设备号
在b_device.c中完成驱动及描述的赋值,如下:
static bDriverInterface_t *bDriverTable[bDEV_MAX_NUM] = {
#define B_DEVICE_REG(dev, driver, desc) &driver,
#include "b_device_list.h"
};//驱动接口 在相应的设备接口数组中注册
static const char *bDeviceDescTable[bDEV_MAX_NUM] = {
#define B_DEVICE_REG(dev, driver, desc) desc,
#include "b_device_list.h"
};
注册完后的操作,设备层实现
bSECTION_DEF_FLASH(driver_init, pbDriverInit_t)
//这个就是要连接底层的中间宏
driver_init :很明显就是段名
pbDriverInit_t:在b_device.h中被定义为了指针函数。
这个其实就是要在初始化时,要初始化的设备地址的定义
映射到驱动层,完成初始化
驱动层要在硬件抽象层的上面,所以上面只是完成了要初始化接口,但具体初始化哪些设备呢?这个就要根据实际加上驱动。如要使用spi - flash ,就要加上b_drv_spiflash.c文件,这里面自然会有bDRIVER_REG_INIT(bSPIFLASH_Init);
//bSPIFLASH_Init:这个便是初始化函数
这个就在映射ROM地图里找到相应的段。
在b_drive.h中,#define bDRIVER_REG_INIT(func) \bSECTION_ITEM_REGISTER_FLASH(driver_init, pbDriverInit_t, CONCAT_2(init, func)) = func
//通过这个宏又跳到section
在b_section.h中,#define bSECTION_ITEM_REGISTER_FLASH(section_name, data_type, var_name) \BOS_SECTION_ITEM_REGISTER(section_name, const data_type var_name)
还得跳
#if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6000000))
#define BOS_SECTION_ITEM_REGISTER(section_name, section_var) \
section_var __attribute__((section(STRINGIFY(section_name)))) __attribute__((used))
当然这些底层的操作,要熟悉MDK链接。还是有难度的…通过这些操作,在main中调用bInit()就知道调用哪些设备来完成初始化了。也就相当于系统知道了初始化了。
bSPIFLASH_Init()函数代码
int bSPIFLASH_Init()
{
size_t i = 0, number = sizeof(bSPIFLASH_HalIfTable) / sizeof(bSPIFLASH_HalIf_t);
int retval = 0;
for (i = 0; i < number; i++)
{
sprintf(bSPIFlashName[i], "%03d", i);
flash_table[i].name = bSPIFlashName[i];
flash_table[i].spi._hal_if = (void *)&bSPIFLASH_HalIfTable[i];
bSPIFLASH_Driver[i]._hal_if = (void *)&bSPIFLASH_HalIfTable[i];
bSPIFLASH_Driver[i].open = _bSPIFLASH_Open;//通过这些函数调用硬件抽象层
bSPIFLASH_Driver[i].close = _bSPIFLASH_Close;
bSPIFLASH_Driver[i].ctl = _bSPIFLASH_Ctl;
bSPIFLASH_Driver[i].read = _bSPIFLASH_ReadBuf;
bSPIFLASH_Driver[i].write = _bSPIFLASH_WriteBuf;
bSPIFLASH_Driver[i].status = 0;
bSPIFLASH_Driver[i]._private._p = &flash_table[i];
}
for (i = 0; i < number; i++)
{
_bSPIFLASH_Open(&bSPIFLASH_Driver[i]); // wakeup flash
}
if (sfud_init() != SFUD_SUCCESS)//调用万能spi驱动,实现硬件的初始化
{
for (i = 0; i < number; i++)
{
bSPIFLASH_Driver[i].status = -1;
}
retval = -1;
}
for (i = 0; i < number; i++)
{
_bSPIFLASH_Close(&bSPIFLASH_Driver[i]); // powerdown flash
}
return retval;
}
bDRIVER_REG_INIT(bSPIFLASH_Init);
调用硬件抽象层
万能spi-flash 驱动库
SFUD–SFUD 全称 Serial Flash Universal Driver,是一款开源的串行 SPI Flash 通用驱动库。github项目地址,实际上,fllash首先得是 SFDP :它是 JEDEC (固态技术协会)制定的串行 Flash 功能的参数表标准,这四个字符SFDP都存放到了flash特定区域中,以此来区分是否支持sfdp.这里就不展开了,资料也很多。
驱动层如何调用的硬件抽象层??
在b_drv_spiflash.c文件中bSPIFLASH_Init()函数中,定义的open、close 等函数,便是与硬件抽象层的接口。
拿open为例:
static int _bSPIFLASH_Open(bSPIFLASH_Driver_t *pdrv)
{
bDRV_GET_HALIF(_if, bSPIFLASH_HalIf_t, pdrv);
uint8_t cmd = 0xab;
bHalGPIO_WritePin(_if->cs.port, _if->cs.pin, 0);//这里便是抽象层定义的函数
if (_if->is_spi == 0)
{
return -1;
// add qspi ...
}
else
{
bHalSPI_Send(_if->_if.spi, &cmd, 1);//这也是抽象层函数
}
bHalGPIO_WritePin(_if->cs.port, _if->cs.pin, 1);
bHalDelayUs(10);
return 0;
}
bDRV_GET_HALIF(_if, bSPIFLASH_HalIf_t, pdrv)实现了什么?
在b_drive.h中#define bDRV_GET_HALIF(name, type, pdrv) type *name = (type *)(pdrv->_hal_if)
替换一下就是 bSPIFLASH_HaIIf_t *_if = (bSPIFLASH_HaIIf_t *)(pdrv->_hal_if),这就实现了函数指针的赋值。
调用底层库
库指的是芯片的操作库,直接由驱动层里的函数来实现调用,这样就可以操作芯片了。