0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式软件设计之PPG采集系统

云深之无迹 来源:云深之无迹 2023-08-03 10:18 次阅读

500d6ae2-318e-11ee-9e74-dac502259ad0.png

嵌入式系统是分层级的,分模块的。

使用的硬件资源有:

IMU-IIC

采集-ADC

电源-ADC

外置接口-串口

主控部分使用ESP32-IDF进行开发,因为芯片寄存器较多,而且采集对的实时性有要求,所以选用freeRTOS,在满足实时性的要求上程序的设计也会更简单。

FreeRTOS任务设计

MAX30102采集任务:初始化IIC和MAX30102,在一个死循环里面以50Hz的频率读取红外线和红光传感器的数据,进行简单滤波后存入队列。

MPU6050任务:初始化IIC和MPU6050,连续读取Euler角,以20Hz的频率进行更新,数据处理后存入队列。

ADC采集任务:初始化ADC,以适当采样频率(例如100Hz)采集模拟通道电压,发送到队列。

串口发送任务:优先级最低,从队列中读取数据并打包发送。可以设置一定的数据缓存。

空闲任务:优先级最低,MCU睡眠时运行,用于切换低功耗模式。

数据同步

采用FreeRTOS的队列和信号量机制进行任务间同步。信号量可用于指示队列已满或空。

给每个数据包添加采集时间戳,上位机可以根据时间戳重新同步。

也可以仅在串口发送任务中合并时间戳,不在各个采集任务中添加。

低功耗设计

利用调度器suspend/resume接口暂停任务实现睡眠唤醒。

DMA采集ADC数据,避免CPU占用。

使用内部PERIPH FIFO buffer,减少IIC任务调用。

串口使用DMA传输,CPU仅在发送完一个包后进行复位。

关闭不需要的外设时钟。利用IDLE调度钩子函数实现自动降频。

模块化设计

独立通信模块,内部封装串口通信的复杂度。

采集核心模块只输出统一格式的采集数据。

模块间使用统一的队列/缓存接口进行数据交换。

502d2292-318e-11ee-9e74-dac502259ad0.png504c494c-318e-11ee-9e74-dac502259ad0.png

这里给出采集的样板任务

针对MAX30102的芯片,更多的技术细节是:首先配置传感器工作在FIFO模式下然后周期性读取FIFO,通过1024点的FFT变换得到频域数据,然后选择频带内的最高幅值为心率,通过对比两个幅值的幅度计算出血氧饱和度。通过平均其他频点的差值来标定两个波长数据。

struct compx FFTBUF1[FFT_N + 16];
struct compx FFTBUF2[FFT_N + 16];
uint16_t g_fft_index = 0;
BloodData g_blooddata = {0};


void test(float data1, float data2)
{
    static uint8_t str[50];
    sprintf((char *)str, "%f,%f
", data1, data2);
    HAL_UART_Transmit_DMA(&huart1, str, sizeof(str));
}


// 血液检测信息更新
void blood_data_update(void)
{
    static DC_FilterData dc1 = {.w = 0, .init = 0, .a = 0.8};
    static DC_FilterData dc2 = {.w = 0, .init = 0, .a = 0.8};
    static float data1buf[20];
    static uint8_t data1cur = 0;
    static float data2buf[20];
    static uint8_t data2cur = 0;
    uint16_t temp_num = 0;
    uint16_t fifo_word_buff[1][2];
    temp_num = max30100_Bus_Read(INTERRUPT_REG);
    if (INTERRUPT_REG_A_FULL & temp_num)
    {
        max30100_FIFO_Read(0x05, fifo_word_buff, 1); // read the hr and spo2 data form fifo in reg=0x05
        float data1 = dc_filter(fifo_word_buff[0][0], &dc1) + 100.0;
        float data2 = dc_filter(fifo_word_buff[0][1], &dc2) + 100.0;
        data1buf[data1cur] = data1;
        data2buf[data2cur] = data2;
        data1 = 0;
        data2 = 0;
        for (int i = 0; i < 20; i++)
        {
            data1 += data1buf[i];
            data2 += data2buf[i];
        }
        data1 /= 20;
        data2 /= 20;
        data1cur = (data1cur < 19) ? data1cur + 1 : 0;
        data2cur = (data2cur < 19) ? data2cur + 1 : 0;
        g_blooddata.hb = data1 + 50;
        g_blooddata.hbo2 = data2 + 50;
        // 将数据写入fft输入并清除输出
        for (int i = 0; i < 1; i++)
        {
            if (g_fft_index < FFT_N)
            {
                FFTBUF1[g_fft_index].real = fifo_word_buff[i][0];
                FFTBUF1[g_fft_index].imag = 0;
                FFTBUF2[g_fft_index].real = fifo_word_buff[i][1];
                FFTBUF2[g_fft_index].imag = 0;
                g_fft_index++;
            }
        }
        // 信息更新标志位
        g_blooddata.update++;
    }
}
// 血液信息转换
void blood_data_translate(void)
{
    // 缓冲区写入结束
    if (g_fft_index >= FFT_N)
    {
        // 快速傅里叶变换
        FFT(FFTBUF1);
        FFT(FFTBUF2);
        // 解平方
        for (int i = 0; i < FFT_N; i++)
        {
            FFTBUF1[i].real = sqrtf(FFTBUF1[i].real * FFTBUF1[i].real + FFTBUF1[i].imag * FFTBUF1[i].imag);
            FFTBUF2[i].real = sqrtf(FFTBUF2[i].real * FFTBUF2[i].real + FFTBUF2[i].imag * FFTBUF2[i].imag);
        }
        // 读取峰值点 10-100带通 频率范围30-292次/分钟
        uint16_t s1_max_index = find_max_num_index(FFTBUF1, 100);
        uint16_t s2_max_index = find_max_num_index(FFTBUF2, 100);


        // 检查HbO2和Hb的变化频率是否一致
        if (s1_max_index == s2_max_index)
        {
            // 心率计算
            uint16_t Heart_Rate = 60 * SAMPLES_PER_SECOND *
                                  s2_max_index / FFT_N;
            g_blooddata.heart = Heart_Rate;
            // 血氧含量计算
            float sp02_num = (FFTBUF1[s1_max_index].real * FFTBUF1[0].real) / (FFTBUF2[s1_max_index].real * FFTBUF2[0].real);
            sp02_num = sp02_num * SAMPLES_PER_SECOND + CORRECTED_VALUE;
            g_blooddata.SpO2 = sp02_num;
            // 状态正常
            g_blooddata.state = BLD_NORMAL;
        }
        else // 数据发生异常
        {
            g_blooddata.heart = 0;
            g_blooddata.SpO2 = 0;
            g_blooddata.state = BLD_ERROR;
        }


        g_fft_index = 0;
    }
}

因为PPG的数据处理是难点,以上给出一段处理代码,但是还有优化的空间。

将采集数据和处理算法分离开来,降低耦合

可以创建独立的采集模块和处理模块,采集模块专注获取传感器数据,处理模块实现算法逻辑。两者通过统一的数据结构进行交互。这可以提高代码的模块化和可维护性。

优化数据滤波方式

当前的平均滤波可以考虑改为滚动平均滤波,这样可以加快数据更新的响应速度。同时可以引入一阶IIR滤波来平滑数据。

优化FFT实现

可以考虑使用更优化的FFT库,或者直接调用DSP库的FFT函数,提高运算效率。当前的FFTBUFFER可以改为复数数组,简化运算。

血氧算法可进一步优化

血氧计算中使用了简单的比值法,可以参考更复杂的算法来提高精度,比如考虑LED功率补偿等。

添加参数配置接口

例如采样率、FFT长度、滤波参数等可以设计成可配置的,而不是硬编码的数字。这样可以更灵活地调整参数。

优化数据包发送流程

可以考虑使用FreeRTOS队列来缓存要发送的数据,发送任务从队列中获取数据。这可以避免直接在中断中发送造成的阻塞。

增加状态机管理

可以设计一个状态机来管理整个采集和处理的流程,例如初始化状态,检测状态,发送状态等。这可以使代码流程更清晰。

50769b8e-318e-11ee-9e74-dac502259ad0.png

50ac82ee-318e-11ee-9e74-dac502259ad0.png

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    4983

    文章

    18286

    浏览量

    288517
  • adc
    adc
    +关注

    关注

    95

    文章

    5652

    浏览量

    539490
  • 采集系统
    +关注

    关注

    0

    文章

    152

    浏览量

    20414
  • PPG
    PPG
    +关注

    关注

    2

    文章

    62

    浏览量

    17980
  • FreeRTOS
    +关注

    关注

    12

    文章

    473

    浏览量

    61351

原文标题:PPG采集系统-嵌入式软件设计思路

文章出处:【微信号:TT1827652464,微信公众号:云深之无迹】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    诚聘嵌入式软件设计

    猎头职位:嵌入式软件设计师【成都】岗位职责:1.负责燃气类产品的嵌入式开发及调试;2.参与产品开发方案的设计;3.协助硬件设计师完成产品硬件设计;4.负责项目研发过程中的技术文档编写及归档;5.参与
    发表于 03-13 10:29

    【下载】《嵌入式系统软件设计中的数据结构》

    `编辑推荐  《嵌入式系统软件设计中的数据结构》可作为从事嵌入式系统软件设计的电子技术人员自学"数据结构"的教材,也可供高等院校电子技术类专业本科生、研究生作为
    发表于 11-30 17:46

    嵌入式软件设计思想与方法

    本帖最后由 lee_st 于 2018-2-24 17:16 编辑 嵌入式软件设计思想与方法
    发表于 02-24 17:15

    DIY分享:嵌入式导航系统硬件+软件设计教程

    特点。模块与 S3C2440A 接口电路如图 2 所示。其中,RX_GPS,RX_GPS 接入 UART 端。3 嵌入式导航系统软件设计系统软件嵌入式
    发表于 02-12 08:00

    基于QT的嵌入式linux图形应用软件设计

    嵌入式数据库或图形软件开发有兴趣,可以进一步学习嵌入式linux数据库开发或基于 QT的嵌入式linux图形应用软件设计。...
    发表于 11-05 08:11

    掌握嵌入式系统软件设计方法

    实验目的(1)掌握嵌入式系统软件设计方法,培养分析问题、解决问题、应用知识的能力和创新精神,全面提高综合素质。(2)熟悉嵌入式Linux开发环境,学会基于UP-CUP6410-II型平台的Linux
    发表于 11-09 09:05

    嵌入式环境下软件设计的特点是什么

    详见微信公众号,二进制人生。目录:嵌入式环境下软件设计的特点设计目标设计思路多进程解耦嵌入式环境下软件设计的特点要谈嵌入式
    发表于 11-09 06:31

    嵌入式软件设计开发工具有哪些呢?分别有何用途

    什么是嵌入式软件嵌入式系统的硬件包括哪些部分呢?嵌入式软件设计开发工具有哪些呢?分别有何用途?
    发表于 12-24 07:47

    嵌入式系统软件设计的原则是什么

    嵌入式系统软件设计的原则1、基本原则是“物尽其用”,嵌入式系统的硬件和软件都必须高效率地设计,去除冗杂 还应尽可能采用高效率的设计方法,标
    发表于 12-24 06:29

    如何去实现嵌入式中的硬件设计和软件设计

    什么是嵌入式?如何去实现嵌入式中的硬件设计和软件设计呢?
    发表于 01-19 07:55

    嵌入式软件设计和单片机软件设计的区别是什么?

    嵌入式软件设计和单片机软件设计目前的应用很多,这两者有什么区别? 嵌入式主要说的什么?结构是啥!
    发表于 11-10 07:46

    基于ARM的嵌入式系统软件设计

    嵌入式软件的启动代码嵌入式软件开发关键技术嵌入式实时操作系统程序的链接定位
    发表于 03-25 15:03 203次下载
    基于ARM的<b class='flag-5'>嵌入式</b><b class='flag-5'>系统软件设计</b>

    基于嵌入式的脑卒中康复仪的软件设计

    本文介绍一种新型嵌入式脑卒中康复治疗仪系统软件设计。以Qt/Embedded为核心实现治疗仪的软件设计,利用Qt/Embedded的多线程技术来实现肌电信号的
    发表于 08-14 09:30 28次下载

    ARM的嵌入式系统软件设计

    ARM的嵌入式系统软件设计
    发表于 10-27 15:00 8次下载
    ARM的<b class='flag-5'>嵌入式</b><b class='flag-5'>系统软件设计</b>

    基于RTOS的嵌入式系统软件设计

    基于RTOS的嵌入式系统软件设计说明。
    发表于 04-19 14:38 16次下载