Chaos-nano协作式异步操作系统(六):`Chaos-nano` 在手持式 `VOC` 检测设备上的应用

Chaos-nano 在手持式 VOC 检测设备上的应用

Chaos-nano 在手持式 `VOC` 检测设备上的应用

产品开发背景

VOC 的危害

VOC(挥发性有机化合物)是常温下易挥发的有机化学物质,广泛存在于室内装修材料(甲醛、苯)、家具板材、汽车内饰、日用化学品(清洁剂、香水)等场景中,对人体健康具有显著危害。尤其对老人与孕妇这两类特殊群体,VOC 危害更为突出

  • 孕妇:孕期免疫力较低,长期接触 VOC 可能影响胎儿正常发育,增加胎儿畸形、早产、流产的风险,其中甲醛、苯等物质已被证实具有明确的生殖毒性;

  • 老人:身体机能衰退,呼吸系统、心血管系统及肝脏肾脏功能较弱,VOC 刺激易引发呼吸道感染、头晕胸闷、血压波动等症状,长期暴露可能加重慢性疾病(如哮喘、冠心病)。

此外,VOC 还会导致室内空气质量下降,引发眼睛干涩、皮肤瘙痒等不适,影响特殊群体的日常生活质量。

产品开发的必要性

随着居民对健康重视程度的提升,老人与孕妇的居住环境安全成为家庭核心关注点,但传统 VOC 检测存在明显痛点:

  1. 专业检测机构服务价格高昂(单次检测费用数百至数千元),且检测周期长,无法满足日常高频监测需求;
  2. 大型实验室仪器体积庞大、操作复杂,老人与孕妇难以独立使用,更无法随身携带;
  3. 普通家用检测工具体积大多数续航较短,无法支撑长时间外出检测;

针对老人与孕妇群体的使用需求,需要一款可以随身携带、可以长时间工作的 VOC 检测设备;同时需要具备操作极简、数据直观、安全无辐射 等核心特性。基于该思路开发面向老人与孕妇的手持式 VOC 检测设备,具有极强的现实必要性与社会价值。

产品介绍

该产品以"极致便携+长续航"为核心设计目标:

  • 为了能够满足便携,在外观设计上采用了这种直径 4 cm 的圆形的外观设计;
  • 为了能够长时间工作,在器件选择上选用了低功耗的 8 位单片机 STM8L151F3 ,并且在系统软件设计上采用了 Chaos-nano 操作系统------该系统是轻量级的异步协作式操作系统,可以在任务不执行的时候阻塞任务,且可以在合适的时候进入到低功耗状态------进一步降低了产品的功耗。

产品结构

手持式 VOC 检测设备基于 Chaos-nano 操作系统与 STM8L151F3 芯片构建,针对老人与孕妇的使用习惯,采用"极简设计+核心功能聚焦"的模块化结构,核心组成包括:

  1. 硬件层
    • 控制核心:STM8L151F3 微控制器(小巧封装,降低设备体积);
    • 传感模块:高精度低功耗 VOC 传感器,温湿度传感器 SHT21;
    • 显示模块:0.95 英寸 OLED 显示屏;
    • 操作模块:一键式操作按键(仅保留开机/唤醒、显示切换 2 个核心按键,简化操作);
    • 电源模块:限于产品体积,该产品采用了 150 mAh ,并采用 usb-micro 接口(支持 5V 充电,适配家庭常用充电器);
  2. 系统层 :搭载 Chaos-nano 操作系统,提供轻量化任务调度、中断处理、低功耗管理等核心功能,适配 STM8L151F3 的硬件资源限制,确保设备运行稳定、续航持久。
  3. 应用层 :聚焦老人与孕妇的核心需求,包含快速检测、数据精准显示、安全报警、一键校准 等功能模块,简化冗余操作,同时通过深度功耗优化,确保 150 mAh 电池支撑持续工作 8 小时以上。

核心优势:极致便携性设计

依托 150 mAh 紧凑锂电池与 STM8L151F3 芯片的小巧封装,以及轻量级的 Chaos-nano 操作系统,设备实现"口袋级便携":

  • 尺寸:长 40 mm × 宽 40 mm × 厚 10 mm,并配有钥匙孔,便于老人与孕妇可轻松放入口袋、手提包或随身悬挂,几乎无携带负担;
  • 材质:外壳采用 ABS 塑料表面防滑处理,手感舒适,便于握持;边角圆润无棱角,避免磕碰伤害;与铝合金铝块结合,使整体不显单调;
  • 续航适配: usb-micro 接口支持充电宝、手机充电器、电脑 USB 口等多种充电方式,外出时可随时补充电量,配合 8 小时以上持续工作能力,满足全天外出检测需求(如就医、购物、探访亲友等场景)。

核心芯片:STM8L151F3 详细介绍

STM8L151F3 是意法半导体推出的超低功耗 8 位微控制器,基于 STM8 内核架构,专为电池供电的便携式智能设备设计,其特性与"150 mAh 电池+8 小时续航"的产品定位及老人、孕妇的使用需求高度适配,是设备的理想控制核心:

  • 内核与主频 :采用 STM8 增强型内核,最高工作频率 16 MHz,支持 16 位乘法指令与硬件除法,指令执行效率高,可快速响应传感器数据采集、按键操作等实时任务,确保设备"开机即测、数据秒出",无需老人与孕妇长时间等待。

  • 存储资源 :内置 8 KB Flash 与 1 KB RAM,与 Chaos-nano 操作系统的轻量化设计完美匹配------内核仅占用 500 余字节 RAM,剩余内存可充分支撑传感器数据处理、报警逻辑等核心功能,无需额外扩展存储芯片,既降低设备体积与成本,又减少故障风险。

  • 运行模式(持续检测状态):16 MHz 主频下芯片自身功耗仅 240 μA/MHz(约 3.8 mA);

  • STM8L151F3 集成多种实用外设,无需额外扩展芯片,既简化设备结构、缩小体积,又降低功耗:

  • 芯片封装小巧(UFQFPN20 封装,尺寸 4 mm × 4 mm),可大幅缩小设备体积,为 150 mAh 电池的紧凑布局提供硬件基础。

系统整体框架


系统采用"硬件驱动-操作系统-应用层"三层架构:

  1. 底层驱动层 :由 STM8L15x_StdPeriph_Driver 驱动库与自定义设备驱动组成,实现 STM8L151F3 芯片外设(ADCI2CGPIO 等)及 VOC 传感器、显示模块、按键等硬件的底层控制,重点优化低功耗驱动逻辑与数据采集精度,对应代码中的 devices 目录及 stm8l15x_it.c/stm8l15x_it.h 中断处理文件。
  2. 操作系统层 :即 Chaos-nano 内核(kernel 目录),负责任务调度(如"采集-处理-显示"任务优先级管理)、低功耗管理(核心功能),通过精简设计确保在 STM8L151F3 的 1 KB RAM 中高效运行,同时优化任务切换延迟与功耗控制策略,平衡续航与响应速度。
  3. 应用层 :优化核心逻辑,包括:
    • 传感器快速采集(handle.c/handle.h);
    • 数据直观显示;
    • 操作逻辑简化(仅 2 个按键,开机自动检测,无操作自动休眠);

产品系统实现分析

工作流程

  • 按下开机键之后,系统进行初始化并启动开机键检测任务。当检测到开机键持续 3 s 按下后,设置电源开机并显示第一屏内容;
  • 按下切屏按键后:
    • 会触发相应的中断,在中断中启动切屏任务;
    • 当从中断返回后,调度器优先调度切屏任务;
    • 在切屏任务中会切换显示内容;
  • 上电后,系统会按照一定的延迟进行数据采集:
    • 每 1 s 启动温湿度任务采集一次温湿度,当采集到数据后刷新显示;
    • 每 1 s 启动 VOC 任务采集一次 VOC 数据,当采集到数据后刷新显示;
    • 每 2 s 启动电池任务采集一次电池的电量和充电状态,然后刷新显示;
  • 显示刷新:
    • 显示刷新没有设定单独的任务,因此显示刷新属于同步操作------这里没有采用单独的任务进行异步刷新显示;
    • 通过全局标志来判断当前处于哪个屏幕显示,当有当前屏幕的内容需要刷新时会对屏幕进行刷新,否则直接返回;
    • 电池标志在每个屏幕上都有,所以当有电池数据需要刷新时会及时刷新该部分显示;

软件实现

任务分类与优先级设定

该软件基于 IAR for STM8 开发环境,搭配 Chaos-nano 操作系统,采用"任务化"设计,将功能拆解为5个独立的任务:

  • 电源按键检测任务;
  • 切屏按键检测任务;
  • 温湿度(SHT21)检测任务;
  • VOC 检测任务;
  • 电池状态与电量检测任务;

通过基于优先级调度实现高效协同工作。

任务 ID 任务名称 核心功能 优先级 触发方式
TASK_ID_POWER 电源按键检测 检测电源按键是否在规定的时间内保持按下状态 0 中断
TASK_ID_KEY 切屏按键检测 切换显示屏幕 1 中断
TASK_ID_SHT21 温湿度检测 获取温湿度检测数据 2 定时器
TASK_ID_TVOC VOC 检测 获取 VOC 检测数据 3 定时器
TASK_ID_BAT 电池检测 检测电池的充电状态与电池电量 4 定时器

核心任务代码逻辑解析

  • 主调度逻辑

主函数通过 "获取下一个高优先级任务→执行任务→状态重置" 的循环,实现任务调度,核心逻辑如下:

c 复制代码
void start_kernel(void)
{
  board_init();
  
  task_init();
  time_init();
  
  device_on();
  
  while (1)
  {
    switch (task_getNextPriority())
    {
    case TASK_ID_POWER:
      powerOnHandle();
      break;
      
    case TASK_ID_KEY:
      keyHandle();
      break;
      
    case TASK_ID_SHT21:
      Get_TempHum();
      break;
      
    case TASK_ID_TVOC:
      Get_Tvoc();
      break;
      
    case TASK_ID_BAT:
      Check_Charge_Bat();
      break;
      
    case TASK_ID_DISP:
      //bat_pic(3,50, pow_bat[1]);
      break;
      
    default:
      task_restoreAll();
      if (IDLE_PRI == task_getNextPriority())
      {
        sleep_cpu();
      }
      break;
    }
  }
}
  • 电源键检测

当按下电源按键后,会通过中断触发 TASK_ID_POWER 任务

  • 如果在电源关闭的状态下按下:该任务中会先延迟 3 s 后再次判断该按键是否保持按下状态,如果保持按钮下状态则设置相应的引脚和标志位;
  • 如果在电源开启的状态下按下:该任务中会先延迟 5 s 后再次判断该按键是否保持按下状态,如果保持按钮下状态则设置相应的引脚和标志位;
c 复制代码
void powerOnHandle(void)
{
  static bool flag = false;
  
  if(!powerOn){
    if(!flag){
      if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)!=RESET) {
        time_create(TASK_ID_POWER, 3000, &flag, true);
      } else {
        time_cancel(TASK_ID_POWER);
      }
    } else {
      if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)!=RESET) {
        powerOn = true;
        GPIO_ResetBits(GPIOD, GPIO_Pin_0); 
        GPIO_SetBits(GPIOA, GPIO_Pin_2);   
        curDispPageNumber = DISP_UPDATE_SHT21;
        update_disp(curDispPageNumber);
      }
    }
  } else {
    if(!flag){
      if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)!=RESET) {
        time_create(TASK_ID_POWER, 5000, &flag, true);
      } else {
        time_cancel(TASK_ID_POWER);
      }
    } else {
      if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)!=RESET) {
        powerOn = false;
        GPIO_SetBits(GPIOD, GPIO_Pin_0);          
        GPIO_ResetBits(GPIOA, GPIO_Pin_2);        
        curDispPageNumber = DISP_UPDATE_POWER_OFF;
        update_disp(curDispPageNumber);
      }
    }
  }
  
  task_setBlock(TASK_ID_POWER);
  flag = false;
  
  return;
}
  • 温度检测任务

当温湿度传感器初始化时,会启动一个定时器。当定时器计时结束时会触发 TASK_ID_SHT21 任务,并在该任务中获取通过 IICSHT21 中获取温湿度数据,更新屏幕显示;且启动下一延迟。

c 复制代码
void Get_TempHum(void)
{
  if(flag){
    flag = false;
    Get_TempHum_1();
    update_disp(DISP_UPDATE_SHT21);
    time_create(TASK_ID_SHT21, DELAY_MS, &flag, true);
  }
  
  task_setBlock(TASK_ID_SHT21);
}

Chaos-nano 在开发中带来的优势

由于使用的是 8 位单片机所以产品最初是使用的是传统的软件开发方式;在 Chaos-nano 操作系统被开发后,使用 Chaos-nano 操作系统对该项目进行了重构。

新旧软件代码对比

  • 以下代码时传统模式开发中的按键处理部分:
c 复制代码
void KeyHandle()
{
  OLED_CLS();
  while(1)
  {
    if(Sleeping==0){
      if(PowerStatus==ENABLE) //表示开机
      {
        Delay(3000);
        Check_Charge_Bat();
        Dis_Bat();
        if(page_nums==0)  {//显示温度
          my_page = 3;
          Delay(1000);
          
          if(FlagChange == ENABLE) //按下S2翻页
          {
            page_nums=1;
            OLED_CLS();
            Dis_Uin();
            FlagChange=DISABLE;
          }
          Get_TempHum();
          Check_Charge_Bat();
          Dis_Bat();
          //OLED 显示
          Dis_Data(page_nums);
          
        }else if(page_nums==1)  {//显示RH
          my_page = 0;
          Delay(1000);
          if(FlagChange == ENABLE)
          {
            page_nums=2;
            OLED_CLS();
            Check_Charge_Bat();
            FlagChange=DISABLE;
          }
          Get_TempHum();
          Check_Charge_Bat();
          //OLED 显示
          Dis_Data(page_nums);
          
        }else if(page_nums==3)  {//显示TVOC
          my_page = 2;
          Delay(1000);
          if(FlagChange == ENABLE)
          {
            page_nums=0;
            OLED_CLS();
            FlagChange=DISABLE;
          }
          Get_Tvoc();
          //OLED 显示
          Dis_Data(page_nums);
          
        }else if(page_nums==2) {//显示OC2
          my_page = 1;
          Delay(1000);
          if(FlagChange == ENABLE)
          {
            page_nums=3;
            OLED_CLS();
            Check_Charge_Bat();
            FlagChange=DISABLE;
          }
          Check_Charge_Bat();
          Get_Tvoc();
          //OLED 显示
          Dis_Data(page_nums);
        }
      } else if(PowerStatus==DISABLE)//充电状态关机
      {
        Delay(3000);
        if(FlagS==ENABLE){
          FlagChange=DISABLE;
          page_nums=0;
          FlagS=DISABLE;
          //关闭OLED屏幕	
          OLED_CLS();
        }
      }
      
    }
    ScreenDispose();
    
  }
}
  • 使用 Chaos-nano 操作系统后的代码
c 复制代码
void keyHandle(void)
{
  if(!powerOn)
    return;
  
  curDispPageNumber = (curDispPageNumber == DISP_UPDATE_SHT21) ? DISP_UPDATE_TVOC : DISP_UPDATE_SHT21;
  OLED_CLS();
  update_disp(DISP_UPDATE_BAT);
  update_disp(curDispPageNumber);
  
  task_setBlock(TASK_ID_KEY);
}

void update_disp(enum disp_update_t disp_update_number)
{
  switch(disp_update_number){
  case DISP_UPDATE_SHT21:
    {
      if(disp_update_number != curDispPageNumber)
        return;
      
      //显示温湿度
      ... ...
    }
    break;
    
  case DISP_UPDATE_TVOC:
    {
      uint16_t Tvocmg_L = 0;
      uint16_t eCO2PPM = 0;
      
      if(disp_update_number != curDispPageNumber)
        return;
      
      //显示甲醛和空气质量
      ... ...
    }
    break;
    
  case DISP_UPDATE_BAT:
    {
      if( (curDispPageNumber == DISP_UPDATE_SHT21) || (curDispPageNumber == DISP_UPDATE_TVOC) ){
        Dis_Bat(true);
      } else {
        Dis_Bat(false);
      }
    }
    break;
    
  case DISP_UPDATE_POWER_OFF:
    {
      OLED_CLS();
      Dis_Bat(true);
    }
    break;
  }
}

传统开发模式带来的问题:

  • 逻辑冗余且耦合度极高
  • 代码将按键检测、页面切换、数据采集(温湿度、TVOC)、OLED 显示、电池检测等功能 "堆叠" 在一个无限循环中,各模块职责边界模糊。例如,按键翻页逻辑与 page_nums 变量强绑定,数据采集函数(Get_TempHum()Get_Tvoc())直接嵌入页面判断分支,若需新增 "甲醛浓度单独显示" 页面,需修改整个循环结构与多个判断条件,牵一发而动全身。
  • 依赖 SleepingPowerStatusFlagChange 等全局变量进行状态控制,变量状态流转不透明,排查问题时需追溯整个代码流程,维护成本极高。
  • 阻塞式设计影响用户体验
  • 代码中大量使用 Delay(1000)Delay(3000) 等阻塞延时函数,期间 CPU 无法响应其他操作。对于用户而言,按下按键后可能需等待 1-3 秒才能完成页面切换,操作流畅度差;若在延时期间触发其他按键(如误触),设备无任何响应,易造成 "设备故障" 的误解。
  • 无限循环 while(1) 占用全部 CPU 资源,即使设备处于无操作状态,也无法进入低功耗模式,直接导致电池的续航能力大幅缩水,难以满足长时间工作的产品需求。
  • 扩展性差,适配需求变更成本高
  • 页面切换逻辑通过 page_nums 的多分支 if-else 实现,若需新增检测参数(如温湿度、TVOC 之外增加 PM2.5 显示),需新增 page_nums 枚举值、扩展判断分支、修改翻页逻辑,代码量呈线性增长,且容易引入逻辑错误。
  • 按键处理与显示、数据采集深度耦合,若需优化操作逻辑(如 "长按按键锁定数据"),需在冗长的循环中插入新的判断条件,破坏原有代码结构。

使用 Chaos-nano 操作系统后的代码优势

  • 模块化拆分,职责单一清晰

    • 将原代码拆分为 "按键处理(keyHandle())" 与 "显示更新(update_disp())" 两大独立模块,按键仅负责触发页面切换,显示仅负责根据页面类型更新内容,数据采集可独立封装为任务,各模块通过 curDispPageNumber 枚举变量通信,职责边界明确。
    • 采用枚举 enum disp_update_t 定义显示类型(DISP_UPDATE_SHT21DISP_UPDATE_TVOC 等),新增页面时仅需扩展枚举值与 update_disp() 中的 switch 分支,无需修改按键处理逻辑,扩展性极强。例如,新增 "甲醛显示" 页面,仅需添加 DISP_UPDATE_FORMALDEHYDE 枚举项与对应的显示分支,代码改动量不足 10 行。
  • 非阻塞式设计,适配特殊群体操作需求

    • 彻底摒弃阻塞延时,借助 Chaos-nano 的任务调度机制实现异步处理。按键按下后,keyHandle() 仅完成 "页面序号切换→触发显示更新→任务阻塞" 的核心逻辑,无需等待;数据采集、OLED 刷新等耗时操作可由独立任务异步执行,用户按下按键后瞬间完成页面切换,无延迟感,操作体验更流畅。
    • 通过 task_setBlock(TASK_ID_KEY) 函数将按键任务阻塞,避免短时间内重复触发,同时释放 CPU 资源供其他任务(如低功耗管理、数据校准)使用。配合 Chaos-nano 的低功耗调度,设备无操作时可快速进入休眠模式,最大化延长电池的续航时间。
  • 状态管理透明,维护效率大幅提升

    • 页面状态通过 curDispPageNumber 枚举变量统一管理,取值范围明确(仅对应已定义的显示类型),避免了裸机代码中全局变量状态混乱的问题。例如,update_disp() 函数通过判断枚举值直接定位显示逻辑,无需追溯全局变量的修改记录。
    • 逻辑分支从 4 个 if-else 简化为 1 个 switch 语句,可读性极强。开发人员无需理解复杂的循环嵌套与延时逻辑,即可快速定位问题或进行功能扩展,大幅缩短产品迭代周期。
  • 低耦合设计,适配产品功能迭代

    • 按键处理与数据采集、显示逻辑完全解耦。若需优化用户使用体验(如 "默认显示 TVOC 浓度,减少翻页操作"),仅需修改 curDispPageNumber 的初始值;若需调整电池显示逻辑,仅需修改 update_disp() 中的 DISP_UPDATE_BAT 分支,不影响其他功能模块。
    • 依托 Chaos-nano 的任务管理能力,可轻松新增独立任务(如 "低电量报警""一键校准"),无需担心与原有按键、显示逻辑冲突。例如,新增 "校准任务" 时,仅需创建新任务并添加新功能的代码,原有代码无需改动。

Chaos-nano 带来的核心价值总结

对于本项目而言,Chaos-nano 不仅是 "让代码更简洁",更是通过架构革新实现了三大核心价值:

1. 提升产品易用性

非阻塞式设计消除了按键操作的延迟感,模块化逻辑保障了设备运行的稳定性,让老人与孕妇 "一键操作、即刻响应" 的核心需求得到充分满足,降低了特殊群体的使用门槛。

2. 降低开发与维护成本,加速产品迭代

简洁的代码结构、清晰的模块划分,让开发人员无需花费大量时间梳理逻辑关系,新增功能、修改需求时的代码改动量减少 60% 以上,显著缩短产品开发周期与维护成本。

3. 释放硬件潜力,保障核心产品特性

Chaos-nano 的低功耗调度机制,配合非阻塞式代码设计,同时确保设备体积小巧、重量轻盈,完美实现 "极致便携 + 长续航" 的产品定位。

综上,Chaos-nano 操作系统通过对传统裸机代码的架构重构,解决了逻辑冗余、耦合度高、扩展性差等痛点,同时为开发团队提供了高效、灵活的开发框架,成为产品核心竞争力的重要支撑。


代码位置https://gitee.com/kongrong77/Chaos-nano/tree/main/examples/nose_chaos_nano

产品视频

https://gitee.com/kongrong77/Chaos-nano/blob/main/pic/voc.gif

相关推荐
南棱笑笑生7 小时前
20251213给飞凌OK3588-C开发板适配Rockchip原厂的Buildroot【linux-6.1】系统时适配CTP触摸屏FT5X06
linux·c语言·开发语言·rockchip
XINVRY-FPGA8 小时前
XC7Z030-2SBG485I Xilinx Zynq-7000 系列 SoC FPGA
嵌入式硬件·fpga开发·硬件工程·fpga
南棱笑笑生9 小时前
20251213给飞凌OK3588-C开发板适配Rockchip原厂的Buildroot【linux-6.1】系统时适配type-C0
linux·c语言·开发语言·rockchip
xiaohai@Linux10 小时前
LVGL显示gif动图导致MCU进入HardFault_Handler问题(已解决!)
单片机·lvgl
czhaii10 小时前
并口LCD1602用DMA刷屏
单片机·嵌入式硬件·硬件工程
小猪猪屁10 小时前
顺序表与链表:头插法与尾插法详解
c语言·数据结构·c++
历程里程碑10 小时前
C++ 5:模板初阶
c语言·开发语言·数据结构·c++·算法
离凌寒10 小时前
二、在freertos中对应esp01s模块的ap模式下的通信测试。
单片机·freertos·esp01s
CFZPL10 小时前
esp32,stm32编译的不同
单片机·esp32