BabyOS设计和使用手册

V0.2.3

BabyOS V7.4.6

修订记录:

日期 记录 修订人
2022.03.18 编写初稿 notrynohigh
2022.06.05 增加功能模块详细介绍 notrynohigh
2022.06.06 修改按键模块的描述,文档对应代码的版本号 notrynohigh

目录

1. 项目介绍

BabyOS构想是搭建一个货架存放软件CBB(CommonBuildingBlock)。

不同产品、系统之间有许多共用的模块,这些模块调试稳定后放入货架作为积累。久而久之,货架上便有许多成熟的CBB供开发人员使用。减少大量重复劳动或者研发已经存在的成果。另一方面,如果产品是基于这些成熟的CBB搭建而成,产品的质量、进度都会得到更好的控制和保证。

货架的搭建和CBB积累并不是一个人可以完成的,需要集合众人的力量。于是2019年底发起了BabyOS开源项目。开发人员以兴趣为动力,无任何薪资报酬。坚持开源互助,共同进步。

2. 设计思路

BabyOS是想搭建一个货架,那么货架上是怎么存放东西的呢?这便决定了代码的结构。

BabyOS设计和使用手册 - 图1

3. 快速体验

以STM32F107进行说明。相关的例子在 https://gitee.com/notrynohigh/BabyOS_Example

3.1 准备基础工程

基础功能需要做到以下几点:

①MCU时钟及片内外的初始化:

初始化时钟、GPIO、滴答定时器和串口1。

②实现用于心跳的定时器:

将滴答定时器作为心跳时钟。

  1. static void _ClockInit()
  2. {
  3. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  4. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO, ENABLE);
  5. }
  6. static void _GpioInit()
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  10. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  11. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  12. GPIO_Init(GPIOA, &GPIO_InitStructure);
  13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  15. GPIO_Init(GPIOA, &GPIO_InitStructure);
  16. }
  17. static void _UartInit()
  18. {
  19. NVIC_InitTypeDef NVIC_InitStruct;
  20. USART_InitTypeDef USART_InitStructure;
  21. USART_InitStructure.USART_BaudRate = 115200;
  22. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  23. USART_InitStructure.USART_StopBits = USART_StopBits_1;
  24. USART_InitStructure.USART_Parity = USART_Parity_No;
  25. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  26. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  27. USART_Init(USART1, &USART_InitStructure);
  28. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  29. NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
  30. NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  31. NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
  32. NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
  33. NVIC_Init(&NVIC_InitStruct);
  34. USART_Cmd(USART1, ENABLE);
  35. }
  36. void BoardInit()
  37. {
  38. _ClockInit();
  39. _GpioInit();
  40. _UartInit();
  41. }
  1. //滴答定时器
  2. SysTick_Config(SystemCoreClock / TICK_FRQ_HZ);
  3. NVIC_SetPriority(SysTick_IRQn, 0x0);

3.2 添加BabyOS代码

路径 部分/全部 用于快速体验
bos/algorithm 根据需要添加 暂时不添加其中文件
bos/core 全部添加 全部添加
bos/drivers 根据需要添加 暂时不添加其中文件
bos/hal 全部添加 全部添加
bos/mcu 根据需要添加 添加bos/mcu/st/stm32f10x/路径代码
bos/modules 全部添加 全部添加
bos/thirdparty 根据需要添加 添加bos/thirdparty/nr_micro_shell/路径代码
bos/utils 全部添加 全部添加
bos/_config b_config.h 全局 配置文件
b_device_list.h 注册设备的文件
b_hal_if.h 驱动接口文件

编译器添加两个路径即可:

bos/

_config/ 如果配置文件拷贝到其他路径了,则添加相应路径即可。

3.3 修改配置

配置项 说明 用于快速体验
Version Configuration 版本配置项,硬件和固件版本 无改动
Platform Configuration 平台配置项,指定心跳频率和MCU平台 MCU平台选择STM32F10X_CL
Hal Configuration 硬件接口配置,可配置硬件接口参数是固定还是可变的 无改动
Utils Configuration 实用软件配置,部分软件代码的配置 无改动
Modules Configuration 模块配置项,各个功能模块的配置 无改动
Thirdparty Configuration 第三方开源代码配置项 勾选 NR Micro Shell Enable/Disable

b_hal_if.h中指定DEBUG接口

  1. #ifndef __B_HAL_IF_H__
  2. #define __B_HAL_IF_H__
  3. #include "b_config.h"
  4. // debug
  5. #define HAL_LOG_UART B_HAL_UART_1
  6. #endif

3.4 调用必要的函数

包含头文件 b_os.h

①滴答定时器中断服务函数调用 bHalIncSysTick();

  1. void SysTick_Handler()
  2. {
  3. bHalIncSysTick();
  4. }

②调用bInit();bExec();`

  1. int main()
  2. {
  3. BoardInit();
  4. SysTick_Config(SystemCoreClock / TICK_FRQ_HZ);
  5. NVIC_SetPriority(SysTick_IRQn, 0x0);
  6. bInit(); //bos初始化
  7. bShellInit(); //shell初始化
  8. while (1)
  9. {
  10. bExec(); //bos执行函数
  11. }
  12. }

③由于勾选了shell功能模块,所以需要在串口接收中断服务函数里调用bShellParse,将数据喂给模块。

3.5 快速体验结果

BabyOS的shell模块默认支持查询版本的指令,输入 bos -v 便可以查询到版本。

BabyOS设计和使用手册 - 图2

4. 进阶体验

完成快速体验后,再体验设备的注册和相关操作。以SPIFlash为例进行说明。

BabyOS设计和使用手册 - 图3

4.1 补充MCU资源初始化

在快速体验工程的基础上,增加了硬件SPI,和F_CS引脚。增加SPI的初始化以及GPIO的初始化。

代码省略….

4.2 添加驱动文件

添加 bos/drivers/b_drv_spiflash.c

BabyOS里面SPIFLASH的驱动是基于sfud代码编写。因此也要添加sfud部分的代码。

添加 bos/drivers/sfud/路径的代码。

4.3 添加硬件接口

在b_hal_if.h里面添加硬件接口。

可利用BabyOS配置工具生成代码。https://gitee.com/notrynohigh/bconfig-tool/releases/V0.0.2

微信截图_20220319185454

微信截图_20220319185642

由于sfud需要知道有多少个SPIFLASH,所以在b_hal_if.h里面增加一个宏:

#define HAL_SPIFLASH_TOTAL_NUMBER 1

4.4 记录开机次数

在SPIFLASH的地址0x00000000记录开机次数,增加如下代码

  1. int fd = -1;
  2. uint32_t boot_count = 0;
  3. fd = bOpen(bSPIFLASH, BCORE_FLAG_RW);
  4. bLseek(fd, 0);
  5. bRead(fd, (uint8_t *)&boot_count, sizeof(boot_count));
  6. b_log("boot:%d\r\n", boot_count);
  7. boot_count += 1;
  8. bFlashErase_t bFlashErase;
  9. bFlashErase.addr = 0;
  10. bFlashErase.num = 1;
  11. bCtl(fd, bCMD_ERASE_SECTOR, &bFlashErase);
  12. bLseek(fd, 0);
  13. bWrite(fd, (uint8_t *)&boot_count, sizeof(boot_count));
  14. bClose(fd);

微信截图_20220319190734

5.概要介绍

5.1 添加MCU

bos/mcu/路径是存放已调试过的MCU型号,命名规则是:bos/mcu/厂商/型号/

bos/hal/目录的文件及文件内定义的接口目前并不是很全,这部分的策略是:一点点添加,上层代码有需要时再添加。

下图中黑色部分是HAL部分的内容,蓝色部分是MCU部分需要实现的,绿色部分是UTILS提供的模拟时序。

SPI和I2C接口支持模拟时序,HAL层判断是否使用模拟时序,然后调用对应接口。

因此新增MCU型号:

①新建目录,添加文件

②实现蓝色部分的接口

③修改_config/b_config.h,为MCU Platform增加一个选项

  1. //<o> MCU Platform
  2. //<1001=> STM32F10X_LD
  3. //<1002=> STM32F10X_MD
  4. //<1003=> STM32F10X_HD
  5. //<1004=> STM32F10X_CL
  6. //<1101=> STM32G0X0
  7. //<2001=> NATION_F40X
  8. //<3001=> MM32SPIN2X
  9. //<3002=> MM32SPIN0X
  10. //<4001=> HC32L13X
  11. //<7001=> CH32F103
  12. #define MCU_PLATFORM 1004

HAL

5.2 HAL层介绍

Hal层一方面是给MCU层提供统一的接口。还有如下几点作用:

①提供心跳时间的查询

②提供微秒级和毫秒级延时函数

③提供通讯接口的数据结构

5.2.1 心跳时钟

使用BabyOS,需要给予一个心跳时钟。心跳时钟的频率在_config/b_config.h里定义TICK_FRQ_HZ,使用者自行实现一个定时器,并定时调用bHalIncSysTick。应用代码中可根据bHalGetSysTick获取心跳时钟计数值。

5.2.2 延时函数

提供bHalDelayMsbHalDelayUs两个阻塞型延时函数。毫秒级延时是通过心跳计算的。微妙级函数是通过for循环阻塞。bHalInit中会计算微秒级延时所用到的参数,以此尽量保证微秒级函数的精准性。

5.2.3 通讯接口

HAL层提供通讯接口的数据结构:

  1. //GPIO
  2. typedef struct
  3. {
  4. bHalGPIOPort_t port;
  5. bHalGPIOPin_t pin;
  6. } bHalGPIOInstance_t;
  7. //I2C
  8. typedef struct
  9. {
  10. uint8_t dev_addr;
  11. uint8_t is_simulation;
  12. union
  13. {
  14. bHalI2CNumber_t i2c;
  15. struct
  16. {
  17. bHalGPIOInstance_t clk;
  18. bHalGPIOInstance_t sda;
  19. } simulating_i2c;
  20. } _if;
  21. } bHalI2CIf_t;
  22. //SPI
  23. typedef struct
  24. {
  25. uint8_t is_simulation;
  26. union
  27. {
  28. bHalSPINumber_t spi;
  29. struct
  30. {
  31. bHalGPIOInstance_t miso;
  32. bHalGPIOInstance_t mosi;
  33. bHalGPIOInstance_t clk;
  34. uint8_t CPOL;
  35. uint8_t CPHA;
  36. } simulating_spi;
  37. } _if;
  38. bHalGPIOInstance_t cs;
  39. } bHalSPIIf_t;
  40. //UART
  41. typedef enum
  42. {
  43. B_HAL_UART_1,
  44. B_HAL_UART_2,
  45. ....
  46. B_HAL_UART_NUMBER
  47. } bHalUartNumber_t;
  48. //LCD
  49. typedef struct
  50. {
  51. union
  52. {
  53. uint32_t rw_addr;
  54. struct
  55. {
  56. bHalGPIOInstance_t data;
  57. bHalGPIOInstance_t rs;
  58. bHalGPIOInstance_t rd;
  59. bHalGPIOInstance_t wr;
  60. bHalGPIOInstance_t cs;
  61. } _io;
  62. struct
  63. {
  64. bHalGPIOInstance_t rs;
  65. bHalSPIIf_t _spi;
  66. } _spi;
  67. } _if;
  68. uint8_t if_type; // 0: _io 1: rw_addr 2: _spi
  69. } bLCD_HalIf_t;

5.3 驱动层介绍

BabyOS操作设备的方式是,打开、读/写/控制,关闭。这么设计的想法是,打开(唤醒设备)然后对设备进行操作(读/写/配置),最后关闭(休眠设备)。

bos/driver/inc/b_driver.h里有一个数据结构:

  1. typedef struct bDriverIf
  2. {
  3. int status;
  4. int (*init)(void);
  5. int (*open)(struct bDriverIf *pdrv);
  6. int (*close)(struct bDriverIf *pdrv);
  7. int (*ctl)(struct bDriverIf *pdrv, uint8_t cmd, void *param);
  8. int (*write)(struct bDriverIf *pdrv, uint32_t offset, uint8_t *pbuf, uint32_t len);
  9. int (*read)(struct bDriverIf *pdrv, uint32_t offset, uint8_t *pbuf, uint32_t len);
  10. void *_hal_if;
  11. union
  12. {
  13. uint32_t v;
  14. void * _p;
  15. } _private;
  16. } 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 指向当前驱动对应的硬件接口。

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

5.3.1 硬件接口

每个驱动的硬件接口通过HAL_XXXX_IF指定,在驱动文件代码中会有如下一行代码:

  1. HALIF_KEYWORD bXXXX_HalIf_t bXXXX_HalIfTable[] = HAL_XXXX_IF;
  2. //或
  3. HALIF_KEYWORD bXXXX_HalIf_t bXXXX_HalIf = HAL_XXXX_IF;

这两种分别对应哪种情况呢,通过下图可以看出。

图片

硬件接口的两种情况:

1)存在有相同设备的情况,例如接入 MCU 的有两个 Flash 芯片

2)当前驱动对应的设备不会存在多个,例如屏,一般只会接 1 块屏

第一种情况时,可通过如下宏获取当前的硬件接口:

  1. #define bDRV_GET_HALIF(name, type, pdrv) type *name = (type *)((pdrv)->_hal_if)
  2. //例如:bDRV_GET_HALIF(_if, bSPIFLASH_HalIf_t, pdrv);
  3. //_if便是指向硬件接口的指针

5.3.2 注册设备

操作设备是通过设备号进行,那么注册设备便是将设备号与驱动实例进行绑定。bos/driver/inc/b_driver.h列出了已有的驱动实例。

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

  1. B_DEVICE_REG(bSPIFLASH, bSPIFLASH_Driver[0], "spiflash")
  2. B_DEVICE_REG(bILI9341, bILI9341_Driver, "ili9341")

设备管理涉及到以下几个数据结构:

  1. #define B_DEVICE_REG(dev, driver, desc)
  2. #include "b_device_list.h"
  3. typedef enum
  4. {
  5. #define B_DEVICE_REG(dev, driver, desc) dev,
  6. #include "b_device_list.h"
  7. bDEV_NULL,
  8. bDEV_MAX_NUM
  9. } bDeviceName_t;
  10. static bDriverInterface_t bNullDriver;
  11. static bDriverInterface_t *bDriverTable[bDEV_MAX_NUM] = {
  12. #define B_DEVICE_REG(dev, driver, desc) &driver,
  13. #include "b_device_list.h"
  14. &bNullDriver,
  15. };
  16. static const char *bDeviceDescTable[bDEV_MAX_NUM] = {
  17. #define B_DEVICE_REG(dev, driver, desc) desc,
  18. #include "b_device_list.h"
  19. "null",
  20. };

设备注册便是填充了bDeviceName_t bDriverTable bDeviceDescTable ,以设备号为索引,可以从bDriverTable找到对应的驱动实例,从bDeviceDescTable中找到设备的描述。

5.3.3 操作设备

  1. int bReinit(uint8_t dev_no);
  2. int bOpen(uint8_t dev_no, uint8_t flag);
  3. int bRead(int fd, uint8_t *pdata, uint16_t len);
  4. int bWrite(int fd, uint8_t *pdata, uint16_t len);
  5. int bCtl(int fd, uint8_t cmd, void *param);
  6. int bLseek(int fd, uint32_t off);
  7. int bClose(int fd);
  8. int bModifyHalIf(uint8_t dev_no, uint32_t type_size, uint32_t off, const uint8_t *pval,
  9. uint8_t len);

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

fd 打开设备后返回的句柄,最多同时打开10(BCORE_FD_MAX)个设备。

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

  1. #if _HALIF_VARIABLE_ENABLE
  2. #define HALIF_KEYWORD static
  3. #else
  4. #define HALIF_KEYWORD const static
  5. #endif

bModifyHalIf使用例子:

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

5.4 SECTION介绍

b_section.h定义段的操作。现有的段有如下几个:

  1. bSECTION_DEF_FLASH(bos_polling, pbPoling_t);
  2. #define BOS_REG_POLLING_FUNC(func) //将func放入bos_polling段
  3. bSECTION_DEF_FLASH(driver_init_0, pbDriverInit_t);
  4. bSECTION_DEF_FLASH(driver_init, pbDriverInit_t);
  5. #define bDRIVER_REG_INIT_0(func) //将func放入driver_init_0段
  6. #define bDRIVER_REG_INIT(func) //将func放入driver_init段
  7. bSECTION_DEF_FLASH(b_mod_shell, static_cmd_st);
  8. #define bSHELL_REG_INSTANCE(cmd_name, cmd_handler) //将cmd信息放入b_mod_shell段
  9. bSECTION_DEF_FLASH(b_mod_param, bParamInstance_t);
  10. #define bPARAM_REG_INSTANCE(param, param_size) //将参数信息放入b_mod_param段

驱动文件最后会有一行这样的代码:bDRIVER_REG_INIT(bXXXX_Init);将初始化函数放入driver_init段。

  1. //设备初始化时,将遍历driver_init_0和driver_init内的函数,并执行。
  2. int bDeviceInit()
  3. {
  4. memset(&bNullDriver, 0, sizeof(bNullDriver));
  5. bSECTION_FOR_EACH(driver_init_0, pbDriverInit_t, pdriver_init_0)
  6. {
  7. (*pdriver_init_0)();
  8. }
  9. bSECTION_FOR_EACH(driver_init, pbDriverInit_t, pdriver_init)
  10. {
  11. (*pdriver_init)();
  12. }
  13. return 0;
  14. }
  1. int bExec()
  2. {
  3. //BabyOS的执行函数遍历需要轮询的函数即在bos_polling段的函数。
  4. bSECTION_FOR_EACH(bos_polling, pbPoling_t, polling)
  5. {
  6. (*polling)();
  7. }
  8. return 0;
  9. }

当使用gcc编译时,需要编辑链接文件,在链接文件中补充这几个段,例如:

  1. /* Define output sections */
  2. SECTIONS
  3. {
  4. ......
  5. /* BabyOS Section -------------*/
  6. .driver_init :
  7. {
  8. . = ALIGN(4);
  9. PROVIDE(__start_driver_init = .);
  10. KEEP(*(SORT(.driver_init*)))
  11. PROVIDE(__stop_driver_init = .);
  12. . = ALIGN(4);
  13. } > FLASH
  14. .driver_init_0 :
  15. {
  16. . = ALIGN(4);
  17. PROVIDE(__start_driver_init_0 = .);
  18. KEEP(*(SORT(.driver_init_0*)))
  19. PROVIDE(__stop_driver_init_0 = .);
  20. . = ALIGN(4);
  21. } > FLASH
  22. .bos_polling :
  23. {
  24. . = ALIGN(4);
  25. PROVIDE(__start_bos_polling = .);
  26. KEEP(*(SORT(.bos_polling*)))
  27. PROVIDE(__stop_bos_polling = .);
  28. . = ALIGN(4);
  29. } > FLASH
  30. .b_mod_shell :
  31. {
  32. . = ALIGN(4);
  33. PROVIDE(__start_b_mod_shell = .);
  34. KEEP(*(SORT(.b_mod_shell*)))
  35. PROVIDE(__stop_b_mod_shell = .);
  36. . = ALIGN(4);
  37. } > FLASH
  38. /* BabyOS Section ---------end----*/
  39. ......
  40. }

5.5 功能组件

功能组件包括: 功能模块、第三方开源代码,算法模块和工具模块。

组件 描述 代码
功能模块 收集BabyOS开发者编写的通用软件模块 b_mod_adchub
b_mod_button
b_mod_error
b_mod_fs
b_mod_gui
b_mod_kv
b_mod_menu
b_mod_modbus
b_mod_param
b_mod_protocol
b_mod_pwm
b_mod_shell
b_mod_timer
b_mod_trace
b_mod_xm128
b_mod_ymodem
第三方开源 收集第三方实用的开源代码 cjson
cm_backtrace
fatfs
flexiblebutton
littlefs
nr_micro_shell
ugui
sfud
算法模块 收集常用的算法。目前这部分处于空白状态
工具模块 支持其他各模块的通用代码 b_util_at
b_util_fifo
b_util_i2c
b_util_log
b_util_lunar
b_util_memp
b_util_spi
b_util_uart
b_util_utc

组件的每个部分都可以通过全局配置文件使能以及配置参数。组件中的代码,操作MCU资源只能调用HAL层接口,操作设备只能基于设备号进行操作。

组件中每个c文件功能单一,提供的功能接口放在对应的h文件。尽量做到,根据h文件的函数名便知道如何使用。

6. 功能模块

6.1 b_mod_adchub

6.1.1 数据结构

  1. //回调 ad_val:ADC值 arg:用户指定传入的参数
  2. typedef void (*pAdchubCb_t)(uint32_t ad_val, uint32_t arg);
  3. typedef struct _AdcInfo
  4. {
  5. uint8_t seq; //序号,每个实例中序号不能一样
  6. uint8_t filter; //是否进行默认滤波处理
  7. uint8_t flag; //buf是否填充满
  8. uint8_t index; //当前喂入的数据放入buf的索引
  9. pAdchubCb_t callback; //回调函数
  10. uint32_t arg; //指定回调传入的参数
  11. uint32_t buf[FILTER_BUF_SIZE];
  12. struct _AdcInfo *next;
  13. struct _AdcInfo *prev;
  14. } bAdcInfo_t;
  15. typedef bAdcInfo_t bAdcInstance_t;
  16. //快速创建实例的宏,name:实例名 ad_seq:序号 filter_en:是否需要滤波 cb:回调 cb_arg:回调参数
  17. #define bADC_INSTANCE(name, ad_seq, filter_en, cb, cb_arg) \
  18. bAdcInstance_t name = { \
  19. .seq = ad_seq, \
  20. .filter = filter_en, \
  21. .callback = cb, \
  22. .arg = cb_arg, \
  23. }

6.1.2 接口介绍

  1. //注册ADCHUB实例,所有注册的实例将组成列表
  2. int bAdchubRegist(bAdcInstance_t *pinstance);
  3. //喂ADC数据,ad_seq:ADC的序号 ad_val:ADC的值
  4. int bAdchubFeedValue(uint8_t ad_seq, uint32_t ad_val);

6.1.3 使用例子

  1. //回调函数
  2. void _AdcCallback(uint32_t ad_val, uint32_t arg)
  3. {
  4. b_log("%d:%d\r\n", arg, ad_val);
  5. if (arg == 2) //可以根据arg来判断是哪个实例的回调
  6. {
  7. //.....
  8. }
  9. }
  10. //此处定义实例,序号分别填的是10和16,在喂数据时候要对应
  11. //由于使用同一个回调函数,那么回调带入的参数要区分,分别是1 和 2
  12. bADC_INSTANCE(ADTest, 10, 1, _AdcCallback, 1);
  13. bADC_INSTANCE(ADTemp, 16, 1, _AdcCallback, 2);
  14. int main()
  15. {
  16. ...
  17. bInit();
  18. //注册实例
  19. bAdchubRegist(&ADTest);
  20. bAdchubRegist(&ADTemp);
  21. ...
  22. }
  23. //喂数据,中断里获取ADC值,然后喂数据
  24. void ADC1_2_IRQHandler()
  25. {
  26. uint32_t tmp = 0;
  27. if (ADC_GetITStatus(ADC1, ADC_IT_JEOC) == SET)
  28. {
  29. ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);
  30. tmp = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
  31. bAdchubFeedValue(10, tmp);
  32. tmp = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_2);
  33. bAdchubFeedValue(16, tmp);
  34. }
  35. }

6.2 b_mod_button

此功能模块是对第三方代码FlexibleButton的封装。支持独立按键和矩阵按键。

6.2.1 数据结构

  1. //按键事件回调的数据类型
  2. typedef void (*pBtnEventHandler_t)(uint16_t event, uint8_t param);
  3. //按键事件,1个按键可以同时注册多个事件
  4. #define BTN_EVENT_DOWN (0x001)
  5. #define BTN_EVENT_CLICK (0x002)
  6. #define BTN_EVENT_DOUBLE_CLICK (0x004)
  7. #define BTN_EVENT_REPEAT_CLICK (0x008)
  8. #define BTN_EVENT_SHORT (0x010)
  9. #define BTN_EVENT_SHORT_UP (0x020)
  10. #define BTN_EVENT_LONG (0x040)
  11. #define BTN_EVENT_LONG_UP (0x080)
  12. #define BTN_EVENT_LONGLONG (0x100)
  13. #define BTN_EVENT_LONGLONG_UP (0x200)

6.2.2 接口介绍

  1. //初始化,指定short long longlong的时长,单位ms
  2. int bButtonInit(uint16_t short_xms, uint16_t long_xms, uint16_t llong_xms);
  3. //注册事件,id:按键的id event:事件 handler:处理事件的回调函数
  4. void bButtonRegEvent(uint8_t id, uint16_t event, pBtnEventHandler_t handler);

6.2.3 使用例子

b_config.h 配置独立按键的数量配置矩阵按键的行和列

b_hal_if定义按键的硬件接口,顺序决定了按键的ID

  1. // Button
  2. //独立按键:{PORT, PIN, 按键按下时IO电平}
  3. #define HAL_B_BUTTON_GPIO \
  4. { \
  5. {B_HAL_GPIOC, B_HAL_PIN4, 0}, {B_HAL_GPIOB, B_HAL_PIN10, 0}, \
  6. {B_HAL_GPIOC, B_HAL_PIN13, 0}, {B_HAL_GPIOA, B_HAL_PIN0, 0}, \
  7. }
  8. //矩阵按键,{{行对应的GPIO},{列对应的GPIO}}
  9. #define HAL_B_MATRIXKEY_GPIO {{{B_HAL_GPIOE, B_HAL_PIN8}, {B_HAL_GPIOE, B_HAL_PIN9}}, {{B_HAL_GPIOE, B_HAL_PIN10}, {B_HAL_GPIOE, B_HAL_PIN11}}}
  10. void BtnEventHandler(uint16_t event, uint8_t param)
  11. {
  12. if (event == BTN_EVENT_CLICK)
  13. {
  14. b_log("BTN_EVENT_CLICK\r\n");
  15. }
  16. if (event == BTN_EVENT_DOUBLE_CLICK)
  17. {
  18. b_log("BTN_EVENT_DOUBLE_CLICK\r\n");
  19. }
  20. if (event == BTN_EVENT_SHORT)
  21. {
  22. b_log("BTN_EVENT_SHORT\r\n");
  23. }
  24. if (event == BTN_EVENT_LONG)
  25. {
  26. b_log("BTN_EVENT_LONG\r\n");
  27. }
  28. if (event == BTN_EVENT_LONGLONG)
  29. {
  30. b_log("BTN_EVENT_LONGLONG\r\n");
  31. }
  32. }
  33. int main()
  34. {
  35. ...
  36. bInit();
  37. bButtonInit(3000, 5000, 8000);
  38. //ID 0 对应PC4口接的按键
  39. bButtonRegEvent(0, BTN_EVENT_CLICK | BTN_EVENT_DOUBLE_CLICK, BtnEventHandler);
  40. //ID 1 对应PB10口接的按键
  41. bButtonRegEvent(1, BTN_EVENT_SHORT, BtnEventHandler);
  42. //ID 2 对应PC13口接的按键
  43. bButtonRegEvent(2, BTN_EVENT_LONG, BtnEventHandler);
  44. //ID 3 对应PA0口接的按键
  45. bButtonRegEvent(3, BTN_EVENT_LONGLONG, BtnEventHandler);
  46. //矩阵按键的ID在独立按键之后。矩阵按键的ID
  47. /* --------------->行
  48. key(4) key(5)
  49. key(6) key(7)
  50. */
  51. #if _MATRIXKEY_ENABLE
  52. bButtonRegEvent(4, BTN_EVENT_CLICK | BTN_EVENT_DOUBLE_CLICK, BtnEventHandler);
  53. bButtonRegEvent(5, BTN_EVENT_SHORT, BtnEventHandler);
  54. bButtonRegEvent(6, BTN_EVENT_LONG, BtnEventHandler);
  55. bButtonRegEvent(7, BTN_EVENT_LONGLONG, BtnEventHandler);
  56. #endif
  57. ...
  58. }

6.3 b_mod_error

6.3.1 数据结构

  1. typedef void (*pecb)(uint8_t err); //错误发生后的回调
  2. #define INVALID_ERR ((uint8_t)0xFF)
  3. #define BERROR_LEVEL_0 0X00 //错误等级0,调用回调后自动从队列中移除
  4. #define BERROR_LEVEL_1 0X01 //错误等级1,需要手动从队列移除

6.3.2 接口介绍

  1. //初始化并传入回调函数
  2. int bErrorInit(pecb cb);
  3. //注册错误,err:错误号 level:错误等级
  4. //interval_ms:间隔时间,level为BERROR_LEVEL_1时有效。
  5. //当错误发生后执行一次回调,如果错误没有被清除,则interval_ms时间后再次执行回调
  6. int bErrorRegist(uint8_t err, uint32_t interval_ms, uint32_t level);
  7. //清除指定的错误
  8. int bErrorClear(uint8_t e_no);
  9. //查询错误是否存在
  10. int bErrorIsExist(uint8_t e_no);
  11. //查询错误队列是否为空,即没有错误发生或者发生的错误都已经被处理
  12. int bErrorIsEmpty(void);

6.3.3 使用例子

  1. #define BAT_LOW (0)
  2. #define MEM_ERR (1)
  3. void SystemErrCallback(uint8_t err)
  4. {
  5. b_log_e("err:%d\r\n", err);
  6. }
  7. int main()
  8. {
  9. ...
  10. bInit();
  11. bErrorInit(SystemErrCallback);
  12. bErrorRegist(BAT_LOW, 3000, BERROR_LEVEL_1); //当错误发生时调用
  13. bErrorRegist(MEM_ERR, 0, BERROR_LEVEL_0); //当错误发生时调用
  14. ...
  15. }

6.4 b_mod_fs

6.4.1 数据结构

  1. //定义了两个物理盘,SPIFLASH和SDCARD
  2. typedef enum
  3. {
  4. #if _SPIFLASH_ENABLE
  5. E_DEV_SPIFLASH, /* Map SPIFLASH to physical drive*/
  6. #endif
  7. #if _SD_ENABLE
  8. E_DEV_SDCARD, /* Map MMC/SD card to physical drive*/
  9. #endif
  10. E_DEV_NUMBER,
  11. } FS_DEV_Enum_t;

6.4.2 接口介绍

  1. //b_mod_fs是对接fatfs和littlefs
  2. //b_mod_fs主要是提供初始化函数,其他文件级操作使用fatfs或者littlefs提供的接口。
  3. //初始化函数
  4. int bFS_Init(void);
  5. //提供的测试函数,主要是通过文件的方式记录开机次数
  6. int bFS_Test(void);

6.4.3 使用例子

  1. int main()
  2. {
  3. ...
  4. bInit();
  5. bFS_Init();
  6. bFS_Test();
  7. ...
  8. }

6.5 b_mod_gui

6.5.1数据结构

  1. #define TOUCH_TYPE_RES (0)
  2. #define TOUCH_TYPE_CAP (1)
  3. #define LCD_DISP_H (0)
  4. #define LCD_DISP_V (1)
  5. //创建实例,实例名、LCD设备号、触摸设备号、物理尺寸、触摸类型
  6. #define bGUI_INSTANCE(name, _lcd, _touch, _x_size, _y_size, _touch_type) \
  7. bGUIInstance_t name = { \
  8. .lcd_dev_no = _lcd, \
  9. .touch_dev_no = _touch, \
  10. .touch_type = _touch_type, \
  11. .lcd_x_size = _x_size, \
  12. .lcd_y_size = _y_size, \
  13. };

6.5.2 接口介绍

此模块支持多个屏使用uGUI。UI设计使用ugui.h文件提供的接口。

  1. // 注册GUI实例,返回GUI ID
  2. int bGUIRegist(bGUIInstance_t *pInstance);
  3. // 选择当前操作的目标,传入GUI ID
  4. int bGUISelect(uint8_t id);
  5. // 设置电阻屏触摸的AD值范围
  6. int bGUITouchRange(uint8_t id, uint16_t x_ad_min, uint16_t x_ad_max, uint16_t y_ad_min,
  7. uint16_t y_ad_max);
  8. // 设置屏幕显示方向,默认是LCD_DISP_V
  9. int bGUIDispDir(uint8_t id, uint8_t dir);

6.5.3 使用例子

  1. //定义两个实例,tft屏和oled屏
  2. bGUI_INSTANCE(tft, bSSD1289, bXPT2046, 240, 320, TOUCH_TYPE_RES);
  3. bGUI_INSTANCE(oled, bOLED, NULL, 128, 64, TOUCH_TYPE_RES);
  4. void TouchTest()
  5. {
  6. if(tft.gui_handle.touch.state == TOUCH_STATE_PRESSED)
  7. {
  8. b_log("x:%d y:%d \r\n", tft.gui_handle.touch.xp, tft.gui_handle.touch.yp);
  9. }
  10. }
  11. int main()
  12. {
  13. ......
  14. bInit();
  15. bGUIRegist(&tft);
  16. bGUIRegist(&oled);
  17. //设定电阻屏触摸的AD值范围
  18. bGUITouchRange(0, 476, 3952, 338, 3592);
  19. //选择ID 0的屏 即tft屏
  20. bGUISelect(0);
  21. UG_FillScreen(C_BLACK);
  22. UG_PutString(0, 0, "hello world");
  23. UG_PutString(0, 100, "babyos ssd1289");
  24. //选择ID 1的屏 即oled屏
  25. bGUISelect(1);
  26. UG_FillScreen(0);
  27. UG_PutString(0, 0, "hello world");
  28. UG_PutString(0, 20, "babyos oled");
  29. //再选择tft屏,并设置为横屏
  30. bGUISelect(0);
  31. bGUIDispDir(0, LCD_DISP_H);
  32. UG_PutString(0, 20, "babyos oled tft");
  33. while (1)
  34. {
  35. bExec();
  36. //测试触摸
  37. BOS_PERIODIC_TASK(TouchTest, 500);
  38. }
  39. }

6.6 b_mod_kv

6.6.1 数据结构

  1. //bKV的状态
  2. #define bKV_IDLE 0
  3. #define bKV_BUSY 1
  4. #define bKV_ERROR 2
  5. //bKV区域至少是有4个最小可擦除单位。
  6. //【数据索引1】【数据1】 【数据索引2】【数据2】
  7. #define bKV_SECTOR_T1 0X01
  8. #define bKV_SECTOR_T2 0X02
  9. #define bKV_SECTOR_D1 0X04
  10. #define bKV_SECTOR_D2 0X08
  11. #define bKV_SECTOR_ALL 0X0F
  12. //KV区域的标志字符串
  13. #define bKV_HEAD_STR "B_KV"
  14. #define bKV_ALIGN_4BYTES(n) (((n) + 3) / 4 * 4)

6.6.2 接口介绍

  1. //初始化,dev_no:存储数据的设备号 s_addr:起始地址 size:存储区域尺寸 e_size:最小擦除单位大小
  2. int bKV_Init(int dev_no, uint32_t s_addr, uint32_t size, uint32_t e_size);
  3. //设置KV的数据
  4. int bKV_Set(const char *key, uint8_t *pvalue, uint16_t len);
  5. //读取KV数据
  6. int bKV_Get(const char *key, uint8_t *pvalue);
  7. //删除KV的KEY
  8. int bKV_Delete(const char *key);

6.6.3 使用例子

  1. int main()
  2. {
  3. ...
  4. bInit();
  5. bKV_Init(bSPIFLASH, 0x0, 40960, 4096);
  6. if(0 > bKV_Get("boot", (uint8_t *)&boot_count))
  7. {
  8. boot_count = 0;
  9. }
  10. b_log("boot : %d\r\n", boot_count);
  11. boot_count += 1;
  12. bKV_Set("boot", (uint8_t *)&boot_count, sizeof(boot_count));
  13. ...
  14. }

6.7 b_mod_menu

6.7.1 数据结构

  1. //更新UI的函数,pre_id:当前界面是从pre_id的界面切换过来
  2. typedef void (*pCreateUI)(uint32_t pre_id);
  3. //切换菜单的操作
  4. #define MENU_UP 1
  5. #define MENU_DOWN 2
  6. #define MENU_BACK 3
  7. #define MENU_ENTER 4

6.7.2 接口介绍

  1. //增加同等级的菜单。创建第一个节点时,参考ID和界面ID值相同。
  2. int bMenuAddSibling(uint32_t ref_id, uint32_t id, pCreateUI f);
  3. //增加子级菜单
  4. int bMenuAddChild(uint32_t ref_id, uint32_t id, pCreateUI f);
  5. //菜单切换操作
  6. void bMenuAction(uint8_t cmd);
  7. //直接跳转到ID界面
  8. void bMenuJump(uint32_t id);
  9. //获取当前显示界面的ID
  10. uint32_t bMenuCurrentID(void);
  11. //设置ID界面的可视化状态,用于隐藏和显示界面
  12. int bMenuSetVisible(uint32_t id, uint8_t s);

6.7.3 使用例子

  1. //定义4个按键进行菜单切换操作
  2. void BtnEventHandler0(uint16_t event, uint8_t param)
  3. {
  4. bMenuAction(MENU_UP);
  5. }
  6. void BtnEventHandler1(uint16_t event, uint8_t param)
  7. {
  8. bMenuAction(MENU_DOWN);
  9. }
  10. void BtnEventHandler2(uint16_t event, uint8_t param)
  11. {
  12. bMenuAction(MENU_BACK);
  13. }
  14. void BtnEventHandler3(uint16_t event, uint8_t param)
  15. {
  16. bMenuAction(MENU_ENTER);
  17. }
  18. //创建菜单。更多的代码,参考example仓库的例程。
  19. int bMenuInit()
  20. {
  21. bMenuAddSibling(LEVEL0_MENU0_ID, LEVEL0_MENU0_ID, Level0Menu0F);
  22. bMenuAddSibling(LEVEL0_MENU0_ID, LEVEL0_MENU1_ID, Level0Menu1F);
  23. bMenuAddSibling(LEVEL0_MENU1_ID, LEVEL0_MENU2_ID, Level0Menu2F);
  24. bMenuAddChild(LEVEL0_MENU0_ID, LEVEL1_MENU0_ID, Level1Menu0F);
  25. bMenuAddChild(LEVEL0_MENU1_ID, LEVEL1_MENU1_ID, Level1Menu1F);
  26. bMenuAddChild(LEVEL0_MENU2_ID, LEVEL1_MENU2_ID, Level1Menu2F);
  27. return 0;
  28. }

6.8 b_mod_modbus

6.8.1 数据结构

  1. //这部分代码主要是提供RTU模式的主机读写功能
  2. //从机返回读数据结果的数据结构
  3. typedef struct
  4. {
  5. uint8_t func;
  6. uint8_t reg_num;
  7. uint16_t *reg_value;
  8. } bMB_ReadResult_t;
  9. //从机返回写数据结果的数据结构
  10. typedef struct
  11. {
  12. uint8_t func;
  13. uint16_t reg;
  14. uint16_t reg_num;
  15. } bMB_WriteResult_t;
  16. //传入回调函数的数据结构
  17. typedef struct
  18. {
  19. uint8_t type; // 0: read 1:write
  20. union
  21. {
  22. bMB_ReadResult_t r_result;
  23. bMB_WriteResult_t w_result;
  24. } result;
  25. } bMB_SlaveDeviceData_t;
  26. typedef void (*pMB_Send_t)(uint8_t *pbuf, uint16_t len);
  27. typedef void (*pMB_Callback_t)(bMB_SlaveDeviceData_t *pdata);
  28. //指定发送函数和回调函数
  29. typedef struct
  30. {
  31. pMB_Send_t f;
  32. pMB_Callback_t cb;
  33. } bMB_Info_t;
  34. typedef bMB_Info_t bModbusInstance_t;
  35. //可以通过这个宏快速创建实例,创建实例的时候指定发送和回调函数
  36. #define bMODBUS_INSTANCE(name, pSendData, pCallback) \
  37. bModbusInstance_t name = {.f = pSendData, .cb = pCallback};

6.8.2 接口介绍

  1. //读取寄存器的值
  2. int bMB_ReadRegs(bModbusInstance_t *pModbusInstance, uint8_t addr, uint8_t func, uint16_t reg,uint16_t num);
  3. //写寄存器的值
  4. int bMB_WriteRegs(bModbusInstance_t *pModbusInstance, uint8_t addr, uint8_t func, uint16_t reg, uint16_t num, uint16_t *reg_value);
  5. //将接收到的数据喂给模块,让模块进行解析。解析正确后执行回调
  6. int bMB_FeedReceivedData(bModbusInstance_t *pModbusInstance, uint8_t *pbuf, uint16_t len);

6.8.3 使用例子

  1. //...待添加

6.9 b_mod_param

6.9.1 数据结构

注意:使用此功能模块,需要同时使能shell功能模块

  1. //size:变量的大小Byte name:变量名 addr:变量地址
  2. typedef struct
  3. {
  4. uint8_t size;
  5. char* name;
  6. void* addr;
  7. } bParamStruct_t;
  8. typedef bParamStruct_t bParamInstance_t;
  9. #define _PARAM2STR(n) (#n)
  10. //注册实例,指定需要调整的变量名和变量大小
  11. #define bPARAM_REG_INSTANCE(param, param_size) \
  12. bSECTION_ITEM_REGISTER_FLASH(b_mod_param, bParamInstance_t, CONCAT_2(do_, param)) = { \
  13. .size = param_size, .name = _PARAM2STR(param), .addr = &(param)};

6.9.2 接口介绍

  1. //通过 #define bPARAM_REG_INSTANCE(param, param_size) 注册实例后,通过shell操作
  2. //例如变量名 i
  3. //param i 查询变量值
  4. //param i 8 设置变量名值为8

6.9.3 使用例子

  1. static uint32_t TestTick = 0;
  2. //为了测试,变量值每秒增加1
  3. void TestParamF()
  4. {
  5. TestTick += 1;
  6. }
  7. //通过指令查询和调整TestTick的值
  8. bPARAM_REG_INSTANCE(TestTick, 4);
  9. int main()
  10. {
  11. ...
  12. bInit();
  13. bShellInit();
  14. ...
  15. while (1)
  16. {
  17. bExec();
  18. BOS_PERIODIC_TASK(TestParamF, 1000);
  19. }
  20. }
  21. /*
  22. nr@bos:bos -v
  23. Version:7.4.0
  24. nr@bos:param TestTick //查询变量值
  25. TestTick:19
  26. nr@bosparam TestTick //查询变量值
  27. TestTick:23
  28. nr@bosparam TestTick 0 //设置变量值为0
  29. nr@bosparam TestTick //再次查询
  30. TestTick:4
  31. nr@bos:*/

6.10 b_mod_protocol

此模块提供通用协议格式,测试软件(https://gitee.com/notrynohigh/BabyOS_Protocol/tree/master):

  1. /**
  2. | | | | | | |
  3. | :--- | ------------------ | ------------------- | ----- | -------- | ----- |
  4. | Head | Device ID | Len(cmd+param) | Cmd | Param | Check |
  5. | 0xFE | sizeof(bProtoID_t) | sizeof(bProtoLen_t) | 1Byte | 0~nBytes | 1Byte |
  6. */

设备ID的长度以及len字段的长度可以在b_config文件进行配置。

设备ID:发送数据时,该字段是目标设备的ID , 如果设备ID为0xFFFFFFFF表示广播。

​ 接收数据时,判断ID字段与自身的ID是否匹配。或者ID是否为0xFFFFFFFF。

6.10.1 数据结构

  1. #if PROTO_FID_SIZE == 1
  2. typedef uint8_t bProtoID_t;
  3. #define INVALID_ID 0XFF
  4. #elif PROTO_FID_SIZE == 2
  5. typedef uint16_t bProtoID_t;
  6. #define INVALID_ID 0XFFFF
  7. #else
  8. typedef uint32_t bProtoID_t;
  9. #define INVALID_ID 0XFFFFFFFF
  10. #endif
  11. #if PROTO_FLEN_SIZE == 1
  12. typedef uint8_t bProtoLen_t;
  13. #else
  14. typedef uint16_t bProtoLen_t;
  15. #endif
  16. #pragma pack(1)
  17. typedef struct
  18. {
  19. uint8_t head;
  20. bProtoID_t device_id;
  21. bProtoLen_t len;
  22. uint8_t cmd;
  23. } bProtocolHead_t;
  24. #pragma pack()
  25. //分发函数,当接收的数据按照协议解析成功,则调用分发函数
  26. typedef int (*pdispatch)(uint8_t cmd, uint8_t *param, bProtoLen_t param_len);
  27. #define PROTOCOL_HEAD 0xFE

6.10.2 接口介绍

  1. //初始化,指定设备自身的ID和分发函数
  2. int bProtocolInit(bProtoID_t id, pdispatch f);
  3. //修改设备ID
  4. int bProtocolSetID(bProtoID_t id);
  5. //将接收到的数据喂给模块进行解析
  6. int bProtocolParse(uint8_t *pbuf, bProtoLen_t len);
  7. //将数据根据协议打包。打包完成的数据放在pbuf,同时返回数据长度
  8. int bProtocolPack(uint8_t cmd, uint8_t *param, bProtoLen_t param_size, uint8_t *pbuf);

6.10.3 使用例子

  1. //协议分发函数 cmd:指令 param:参数 param_len:参数长度
  2. int ProtocolDispatch(uint8_t cmd, uint8_t *param, bProtoLen_t param_len)
  3. {
  4. b_log("cmd:%d param_len: %d\r\n", cmd, param_len);
  5. // 添加指令对应的执行代码
  6. return 0;
  7. }
  8. //接收空闲
  9. int ProtocolRecCallback(uint8_t *pbuf, uint16_t len)
  10. {
  11. //接收完一段数据后,将数据给模块进行解析
  12. bProtocolParse(pbuf, len);
  13. return 0;
  14. }
  15. bUTIL_UART_INSTANCE(protocol, 128, 100, ProtocolRecCallback);
  16. int main()
  17. {
  18. ...
  19. bInit();
  20. bProtocolInit(0x520, ProtocolDispatch);
  21. ...
  22. }

6.11 b_mod_pwm

6.11.1 数据结构

  1. #define PWM_HANDLER_CCR (0)
  2. #define PWM_HANDLER_PERIOD (1)
  3. //PWM回调函数,type: PWM_HANDLER_CCR or PWM_HANDLER_PERIOD
  4. typedef void (*pPwmHandler)(uint8_t type);
  5. typedef struct bSoftPwmStruct
  6. {
  7. uint32_t repeat; //指定重复次数,为0则一直重复
  8. uint32_t tick; //用于计时
  9. uint32_t period; //周期,单位ms
  10. uint32_t ccr; //CCR,单位ms
  11. pPwmHandler handler; //回调执行函数
  12. uint32_t flag; //执行回调标志
  13. struct bSoftPwmStruct *next;
  14. } bSoftPwmStruct_t;
  15. typedef bSoftPwmStruct_t bSoftPwmInstance_t;
  16. // 创建PWM实例,指定PWM的参数
  17. #define bPWM_INSTANCE(name, _period, _ccr, _repeat) \
  18. bSoftPwmInstance_t name = {.period = _period, .ccr = _ccr, .repeat = _repeat};

6.11.2 接口介绍

  1. //启动PWM,并指定回调
  2. int bSoftPwmStart(bSoftPwmInstance_t *pPwmInstance, pPwmHandler handler);
  3. int bSoftPwmStop(bSoftPwmInstance_t *pPwmInstance);
  4. int bSoftPwmReset(bSoftPwmInstance_t *pPwmInstance);
  5. int bSoftPwmSetPeriod(bSoftPwmInstance_t *pPwmInstance, uint32_t ms);
  6. int bSoftPwmSetCcr(bSoftPwmInstance_t *pPwmInstance, uint32_t ms);

6.11.3 使用例子

  1. bPWM_INSTANCE(led1_pwm, 20, 5, 0);
  2. bPWM_INSTANCE(led2_pwm, 20, 18, 0);
  3. void PwmHandler1(uint8_t type)
  4. {
  5. if(type == PWM_HANDLER_CCR)
  6. {
  7. bHalGpioWritePin(B_HAL_GPIOD, B_HAL_PIN7, 0);
  8. }
  9. else
  10. {
  11. bHalGpioWritePin(B_HAL_GPIOD, B_HAL_PIN7, 1);
  12. }
  13. }
  14. void PwmHandler2(uint8_t type)
  15. {
  16. if(type == PWM_HANDLER_CCR)
  17. {
  18. bHalGpioWritePin(B_HAL_GPIOD, B_HAL_PIN3, 0);
  19. }
  20. else
  21. {
  22. bHalGpioWritePin(B_HAL_GPIOD, B_HAL_PIN3, 1);
  23. }
  24. }
  25. int main()
  26. {
  27. ...
  28. bInit();
  29. bSoftPwmStart(&led1_pwm, PwmHandler1);
  30. bSoftPwmStart(&led2_pwm, PwmHandler2);
  31. ...
  32. }

6.12 b_mod_shell

此软件模块对接nr_micro_shell

6.12.1 数据结构

  1. typedef void (*pCmdHandler)(char argc, char *argv);
  2. //注册指令和指令的执行函数
  3. #define bSHELL_REG_INSTANCE(cmd_name, cmd_handler)

6.12.2 接口介绍

  1. //shell模块初始化
  2. //初始化后,添加了默认指令,bos -v 查询版本
  3. void bShellInit(void);
  4. //解析函数,接收的数据放入此处解析
  5. int bShellParse(uint8_t *pbuf, uint16_t len);

6.12.3 使用例子

  1. int main()
  2. {
  3. ...
  4. bInit();
  5. bShellInit();
  6. ...
  7. }
  8. void USART1_IRQHandler()
  9. {
  10. uint8_t uart_dat = 0;
  11. if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  12. {
  13. USART_ClearITPendingBit(USART1, USART_IT_RXNE);
  14. uart_dat = USART_ReceiveData(USART1);
  15. bShellParse(&uart_dat, 1); //shell 解析
  16. }
  17. }

6.13 b_mod_timer

6.13.1 数据结构

  1. //定时器回调
  2. typedef void (*pTimerHandler)(void);
  3. typedef struct bSoftTimerStruct
  4. {
  5. uint8_t repeat; //单次定时还是重复,0:单次 1:重复
  6. uint32_t tick;
  7. uint32_t period;
  8. pTimerHandler handler;
  9. struct bSoftTimerStruct *next;
  10. } bSoftTimerStruct_t;
  11. typedef bSoftTimerStruct_t bSoftTimerInstance_t;
  12. //创建实例的宏
  13. #define bTIMER_INSTANCE(name, _period, _repeat) \
  14. bSoftTimerInstance_t name = {.period = _period, .repeat = _repeat};

6.13.2 接口介绍

  1. int bSoftTimerStart(bSoftTimerInstance_t *pTimerInstance, pTimerHandler handler);
  2. int bSoftTimerStop(bSoftTimerInstance_t *pTimerInstance);
  3. int bSoftTimerReset(bSoftTimerInstance_t *pTimerInstance);
  4. int bSoftTimerSetPeriod(bSoftTimerInstance_t *pTimerInstance, uint32_t ms);

6.13.3 使用例子

  1. bTIMER_INSTANCE(timer1, 1000, 1);
  2. bTIMER_INSTANCE(timer2, 2000, 1);
  3. void Timer1Handler()
  4. {
  5. b_log("babyos\r\n");
  6. }
  7. void Timer2Handler()
  8. {
  9. b_log("hello \r\n");
  10. }
  11. int main()
  12. {
  13. ...
  14. bInit();
  15. bSoftTimerStart(&timer1, Timer1Handler);
  16. bSoftTimerStart(&timer2, Timer2Handler);
  17. ...
  18. }

6.14 b_mod_trace

当前软件模块对接的是CmBacktrace

6.14.1 数据结构

  1. //...

6.14.2 接口介绍

  1. int bTraceInit(const char *pfw_name); //初始化并指定固件名

6.14.3 使用例子

参考https://gitee.com/Armink/CmBacktrace/tree/master

6.15 b_mod_xm128

6.15.1 数据结构

  1. //XMODEM回调,number是序号,pbuf是指向数据的指针,当pbuf为NULL时,表示接收完毕
  2. typedef void (*pcb_t)(uint16_t number, uint8_t *pbuf);
  3. //发送函数,用于发送指令
  4. typedef void (*psend)(uint8_t cmd);

6.15.2 接口介绍

  1. //初始化,指定回调和发送函数
  2. int bXmodem128Init(pcb_t fcb, psend fs);
  3. //将接收的数据喂给模块进行解析
  4. int bXmodem128Parse(uint8_t *pbuf, uint8_t len);
  5. //XModem开始和停止
  6. int bXmodem128Start(void);
  7. int bXmodem128Stop(void);

6.15.3 使用例子

  1. uint8_t FileBuf[1024];
  2. uint16_t FileLen = 0;
  3. //XModem回调
  4. void XModemCallback(uint16_t number, uint8_t *pbuf)
  5. {
  6. if(pbuf != NULL)
  7. {
  8. memcpy(&FileBuf[FileLen], pbuf, 128);
  9. FileLen += 128;
  10. }
  11. }
  12. //XModem 发送接口
  13. void XmodemSend(uint8_t cmd)
  14. {
  15. bHalUartSend(HAL_LOG_UART, &cmd, 1);
  16. }
  17. //串口接收空闲,需要接收空闲后喂数据
  18. int UartIdleCallback(uint8_t *pbuf, uint16_t len)
  19. {
  20. bXmodem128Parse(pbuf, len);
  21. return 0;
  22. }
  23. //建立串口接收实例
  24. bUTIL_UART_INSTANCE(XmodemRec, 200, 50, UartIdleCallback);
  25. int main()
  26. {
  27. ...
  28. bInit();
  29. bXmodem128Init(XModemCallback, XmodemSend);
  30. //开始传输
  31. bXmodem128Start();
  32. ...
  33. }
  34. void USART1_IRQHandler()
  35. {
  36. uint8_t uart_dat = 0;
  37. if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  38. {
  39. USART_ClearITPendingBit(USART1, USART_IT_RXNE);
  40. uart_dat = USART_ReceiveData(USART1);
  41. bUtilUartRxHandler(&XmodemRec, uart_dat);
  42. }
  43. }

6.16 b_mod_ymodem

6.16.1 数据结构

  1. //ymodem回调。t:标题或者数据 pbuf:数据 len:数据长度
  2. typedef void (*pymcb_t)(uint8_t t, uint8_t *pbuf, uint16_t len);
  3. //发送接口
  4. typedef void (*pymsend)(uint8_t cmd);

6.16.2 接口介绍

  1. //初始化,提供回调和发送接口
  2. int bYmodemInit(pymcb_t fcb, pymsend fs);
  3. //解析函数,收到的数据喂入进行解析
  4. int bYmodemParse(uint8_t *pbuf, uint16_t len);
  5. //YModem的开始和停止
  6. int bYmodemStart(void);
  7. int bYmodemStop(void);

6.16.3 使用例子

  1. uint8_t FileBuf[1024];
  2. uint16_t FileLen = 0;
  3. //回调函数,t可以为文件名也可以是文件数据 pbuf是数据,当pbuf为NULL时结束 len是数据的长度
  4. void YModemCallback(uint8_t t, uint8_t *pbuf, uint16_t len)
  5. {
  6. if(pbuf != NULL && (t == YMODEM_FILEDATA))
  7. {
  8. memcpy(&FileBuf[FileLen], pbuf, len);
  9. FileLen += len;
  10. }
  11. }
  12. //YModem发送接口
  13. void YmodemSend(uint8_t cmd)
  14. {
  15. bHalUartSend(HAL_LOG_UART, &cmd, 1);
  16. }
  17. //串口接收空闲
  18. int UartIdleCallback(uint8_t *pbuf, uint16_t len)
  19. {
  20. bYmodemParse(pbuf, len);
  21. return 0;
  22. }
  23. //串口接收实例
  24. bUTIL_UART_INSTANCE(YmodemRec, 1128, 50, UartIdleCallback);
  25. int main()
  26. {
  27. ...
  28. bInit();
  29. bYmodemInit(YModemCallback, YmodemSend);
  30. //启动传输
  31. bYmodemStart();
  32. ...
  33. }
  34. void USART1_IRQHandler()
  35. {
  36. uint8_t uart_dat = 0;
  37. if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  38. {
  39. USART_ClearITPendingBit(USART1, USART_IT_RXNE);
  40. uart_dat = USART_ReceiveData(USART1);
  41. bUtilUartRxHandler(&YmodemRec, uart_dat);
  42. }
  43. }

6.17 b_mod_iap

详细介绍:

https://gitee.com/notrynohigh/BabyOS/wikis/BabyOS固件升级功能

6.17.1 数据结构

  1. /**
  2. * IAP状态,IAP介绍文档中有状态的切换路径
  3. */
  4. #define B_IAP_STA_NULL (0)
  5. #define B_IAP_STA_START (1)
  6. #define B_IAP_STA_READY (2)
  7. #define B_IAP_STA_FINISHED (3)
  8. #define IS_IAP_STA(s) \
  9. (((s) == B_IAP_STA_NULL) || ((s) == B_IAP_STA_START) || ((s) == B_IAP_STA_READY) || \
  10. ((s) == B_IAP_STA_FINISHED))
  11. #define B_IAP_FILENAME_LEN (64) // 固件名的长度限制
  12. #define B_IAP_FAIL_COUNT (3) // 固件失败的次数限制
  13. #define B_IAP_BACKUP_EN (0x55) // 备份固件功能启用的标志
  14. #define B_IAP_BACKUP_VALID (0xAA) // 存在有效备份固件的标志
  15. typedef struct
  16. {
  17. uint8_t dev_no; //暂存新固件的设备号,不需要暂存可以忽略
  18. char name[B_IAP_FILENAME_LEN]; //固件名,限制在64个字符
  19. uint32_t len; //固件长度
  20. uint32_t c_crc32; //固件数据CRC32校验值
  21. } bIapFwInfo_t;
  22. typedef struct
  23. {
  24. uint8_t dev_no; //备份区额设备号
  25. uint8_t flag; //备份标志,0xAA表示存在有效备份
  26. uint32_t fcrc; //备份区固件的crc32校验值
  27. uint32_t second; //运行多少秒后进行备份
  28. } bIapBackupInof_t;
  29. typedef struct
  30. {
  31. int stat;
  32. int fail_count;
  33. bIapFwInfo_t info;
  34. bIapBackupInof_t backup;
  35. uint32_t fcrc;
  36. } bIapFlag_t;

6.17.2 接口介绍

  1. /**
  2. * 跳转是弱函数,用户可自己实现
  3. */
  4. void bIapJump2Boot(void);
  5. void bIapJump2App(void);
  6. /**
  7. * boot和app都先调用bIapInit
  8. * 紧接着,按照不同的代码,调用bIapXXXCheckFlag()
  9. * XXX: Boot or App
  10. * 主要用于判断,进入启动程序和进入应用程序时,当前状态是否合法
  11. */
  12. /**
  13. * \param dev_no:固件暂存区的设备号
  14. * 注:暂存于内部FLASH 或 没有暂存区,dev_no = 0
  15. */
  16. int bIapInit(uint8_t dev_no);
  17. /**
  18. * \return int 0:没有升级流程 1:升级流程正常运行中 -1:升级流程异常
  19. */
  20. int bIapAppCheckFlag(void);
  21. int bIapBootCheckFlag(void);
  22. /**
  23. * 应用程序调用,表示升级流程开始。传入新固件的信息。
  24. */
  25. int bIapStart(bIapFwInfo_t *pinfo);
  26. /**
  27. * 固件备份位置的设备号 dev_no
  28. * 注:备份到内部FLASH 则 dev_no = 0
  29. * 不需要固件备份,便不需要调用此函数。
  30. * s: 正常工作s秒后,进行固件备份
  31. */
  32. int bIapBackupFwInit(uint8_t dev_no, uint32_t s);
  33. /**
  34. * \brief 传入新固件的数据用于写入存储区域
  35. * \param index 新固件数据的索引,即相对文件起始的偏移
  36. * \return int 0:正常存储 -1:存储异常 -2:校验失败,重新接收
  37. */
  38. int bIapUpdateFwData(uint32_t index, uint8_t *pbuf, uint32_t len);
  39. /**
  40. * 查询当前IAP的状态
  41. * 应用程序,查询到是B_IAP_STA_READY状态,则跳转至启动程序
  42. * 启动程序,查询到是B_IAP_STA_NULL或者B_IAP_STA_FINISHED状态,则跳转至应用程序
  43. */
  44. uint8_t bIapGetStatus(void);
  45. /**
  46. * 查询备份固件是否有效
  47. */
  48. uint8_t bIapBackupIsValid(void);

6.17.3 使用例子

https://gitee.com/notrynohigh/BabyOS_Example/tree/BearPi/

例程仓库小熊派分支,利用BabyOS通用协议专用上位机进行固件升级

https://gitee.com/notrynohigh/BabyOS_Protocol

7.工具模块

7.1 b_util_at

7.1.1 数据结构

  1. //at的回调,id:调用AT发送后返回的id result:运行的结果
  2. typedef void (*bAtCallback_t)(uint8_t id, uint8_t result);
  3. #define AT_INVALID_ID (0XFF)
  4. #define AT_STA_NULL (0)
  5. #define AT_STA_OK (1)
  6. #define AT_STA_ERR (2)
  7. #define AT_STA_ID_INVALID (3)

7.1.2 接口介绍

  1. int bAtGetStat(uint8_t id);
  2. int bAtRegistCallback(bAtCallback_t cb);
  3. //将接收的数据喂给模块
  4. int bAtFeedRespData(uint8_t *pbuf, uint16_t len);
  5. //AT发送指令,发送的指令会放入队列,并返回id。
  6. //pcmd:at指令 cmd_len:指令长度 presp:期待的回复内容 resp_len:回复内容的长度
  7. //uart:串口号 timeout:允许的超时时间
  8. int bAtCmdSend(const char *pcmd, uint16_t cmd_len, const char *presp, uint16_t resp_len, uint8_t uart, uint32_t timeout);

7.2 b_util_fifo

7.2.1 数据结构

  1. typedef struct
  2. {
  3. uint8_t * pbuf;
  4. uint16_t size;
  5. volatile uint16_t r_index;
  6. volatile uint16_t w_index;
  7. } bFIFO_Info_t;
  8. typedef bFIFO_Info_t bFIFO_Instance_t;
  9. //创建fifo实例
  10. #define bFIFO_INSTANCE(name, _fifo_size) \
  11. static uint8_t fifo##name[_fifo_size]; \
  12. bFIFO_Instance_t name = {.pbuf = fifo##name, .size = _fifo_size, .r_index = 0, .w_index = 0};

7.2.2 接口介绍

  1. //FIFO的常用操作
  2. int bFIFO_Length(bFIFO_Instance_t *pFIFO_Instance, uint16_t *plen);
  3. int bFIFO_Flush(bFIFO_Instance_t *pFIFO_Instance);
  4. int bFIFO_Write(bFIFO_Instance_t *pFIFO_Instance, uint8_t *pbuf, uint16_t size);
  5. int bFIFO_Read(bFIFO_Instance_t *pFIFO_Instance, uint8_t *pbuf, uint16_t size);

7.3 b_util_i2c

7.3.1 数据结构

  1. //模拟I2C的GPIO定义
  2. typedef struct
  3. {
  4. bHalGPIOInstance_t sda;
  5. bHalGPIOInstance_t clk;
  6. } bUtilI2C_t;

7.3.2 接口介绍

  1. //模拟I2C的常用操作
  2. void bUtilI2C_Start(bUtilI2C_t i2c);
  3. void bUtilI2C_Stop(bUtilI2C_t i2c);
  4. int bUtilI2C_ACK(bUtilI2C_t i2c);
  5. void bUtilI2C_mACK(bUtilI2C_t i2c);
  6. void bUtilI2C_WriteByte(bUtilI2C_t i2c, uint8_t dat);
  7. uint8_t bUtilI2C_ReadByte(bUtilI2C_t i2c);
  8. int bUtilI2C_WriteData(bUtilI2C_t i2c, uint8_t dev, uint8_t dat);
  9. uint8_t bUtilI2C_ReadData(bUtilI2C_t i2c, uint8_t dev);
  10. int bUtilI2C_ReadBuff(bUtilI2C_t i2c, uint8_t dev, uint8_t addr, uint8_t *pdat, uint8_t len);
  11. int bUtilI2C_WriteBuff(bUtilI2C_t i2c, uint8_t dev, uint8_t addr, const uint8_t *pdat, uint8_t len);

7.4 b_util_spi

7.4.1 数据结构

  1. //模拟SPI的GPIO定义和SPI参数
  2. typedef struct
  3. {
  4. bHalGPIOInstance_t miso;
  5. bHalGPIOInstance_t mosi;
  6. bHalGPIOInstance_t clk;
  7. uint8_t CPOL;
  8. uint8_t CPHA;
  9. } bUtilSPI_t;

7.4.2 接口介绍

  1. //模拟SPI的读写操作
  2. uint8_t bUtilSPI_WriteRead(bUtilSPI_t spi, uint8_t dat);

7.5 b_util_log

在b_hal_if定义log输出的串口号。

7.5.1 接口介绍

  1. #define b_log_i(...)
  2. #define b_log_w(...)
  3. #define b_log_e(...)
  4. #define b_log(...)

7.6 b_util_lunar

7.6.1 数据结构

  1. //阴历数据结构
  2. typedef struct
  3. {
  4. uint16_t year;
  5. uint8_t month;
  6. uint8_t day;
  7. } bLunarInfo_t;

7.6.2 接口介绍

  1. //阳历转阴历
  2. int bSolar2Lunar(uint16_t syear, uint8_t smonth, uint8_t sday, bLunarInfo_t *plunar);

7.7 b_util_memp

7.7.1 数据结构

  1. //需要监控的信息,unused_unit 统计最小未使用量
  2. typedef struct
  3. {
  4. uint16_t unused_unit;
  5. } bMempMonitorInfo_t;
  6. //内存链表
  7. typedef struct bMempList
  8. {
  9. uint8_t *p;
  10. uint32_t total_size;
  11. uint32_t size;
  12. struct bMempList *next;
  13. struct bMempList *prev;
  14. } bMempList_t;

7.7.2 接口介绍

  1. //申请和释放空间
  2. void *bMalloc(uint32_t size);
  3. void bFree(void *paddr);
  4. #if _MEMP_MONITOR_ENABLE
  5. void bMempGetMonitorInfo(bMempMonitorInfo_t *pinfo);
  6. #endif
  7. //内存链表初始化
  8. int bMempListInit(bMempList_t *phead);
  9. //申请空间存p指向的数据,再将此次申请的空间放入链表
  10. int bMempListAdd(bMempList_t *phead, uint8_t *p, uint32_t len);
  11. //释放链表中所有动态申请的内存
  12. int bMempListFree(bMempList_t *phead);
  13. //内存链表里存储的数据转为连续内存存储
  14. uint8_t * bMempList2Array(const bMempList_t *phead);

7.8 b_util_uart

7.8.1 数据结构

  1. //串口接收空闲的回调
  2. typedef int (*pbUartIdleCallback_t)(uint8_t *pbuf, uint16_t len);
  3. typedef struct UtilUart
  4. {
  5. uint8_t *pbuf; //用于接收数据的存储区
  6. uint16_t buf_size; //存储区的大小
  7. volatile uint16_t index; //存储数据的索引
  8. uint32_t idle_thd_ms; //idle_thd_ms无新数据则判断空闲
  9. pbUartIdleCallback_t callback; //空闲回调
  10. uint32_t l_tick; //接收最后一个数据时的tick值
  11. uint32_t l_index; //接收最后一个数据时的索引
  12. struct UtilUart *next;
  13. struct UtilUart *prev;
  14. } bUitlUart_t;
  15. typedef bUitlUart_t bUitlUartInstance_t;
  16. //用于创建串口接收实例
  17. #define bUTIL_UART_INSTANCE(name, buf_len, idle_ms, cb) \
  18. static uint8_t Buf##name[buf_len]; \
  19. bUitlUartInstance_t name = { \
  20. .pbuf = Buf##name, \
  21. .buf_size = buf_len, \
  22. .idle_thd_ms = idle_ms, \
  23. .callback = cb, \
  24. .index = 0, \
  25. .l_tick = 0, \
  26. .l_index = 0, \
  27. .prev = NULL, \
  28. .next = NULL, \
  29. }

7.8.2 接口介绍

  1. //将实例与串口号绑定
  2. void bUtilUartBind(uint8_t uart_no, bUitlUartInstance_t *pinstance);
  3. // bUtilUartRxHandler 和 bUtilUartRxHandler2 效果是一样
  4. // 但是,只有通过bUtilUartBind绑定串口号,才能调用bUtilUartRxHandler2
  5. void bUtilUartRxHandler(bUitlUartInstance_t *pinstance, uint8_t dat);
  6. void bUtilUartRxHandler2(uint8_t uart_no, uint8_t dat);
  7. // 获取当前BUF中已经收到的数据长度
  8. uint16_t bUtilUartReceivedSize(bUitlUartInstance_t *pinstance);
  9. uint16_t bUtilUartReceivedSize2(uint8_t uart_no);

7.9 b_util_utc

7.9.1 数据结构

  1. typedef struct
  2. {
  3. uint16_t year;
  4. uint8_t month;
  5. uint8_t day;
  6. uint8_t week;
  7. uint8_t hour;
  8. uint8_t minute;
  9. uint8_t second;
  10. } bUTC_DateTime_t;
  11. typedef uint32_t bUTC_t;

7.9.2 接口介绍

  1. //UTC与时间结构相互转换
  2. //UTC的起始时间是2000年1月1日0时0分0秒
  3. void bUTC2Struct(bUTC_DateTime_t *tm, bUTC_t utc);
  4. bUTC_t bStruct2UTC(bUTC_DateTime_t tm);

8. 参与开发

目前还需要广大开源爱好者的加入,将货架做稳固,再填充高质量的货物。

https://gitee.com/notrynohigh/BabyOS (主仓库)

https://github.com/notrynohigh/BabyOS (自动同步)

管理员邮箱:notrynohigh@outlook.com

开发者基于https://gitee.com/notrynohigh/BabyOS仓库dev分支进行。

有贡献的开发者(不局限于提交代码),记录到 http://babyos.cn/ 网站Team页面。

有意者随时私信联系!