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

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

    关注

    5068

    文章

    19013

    浏览量

    303074
  • adc
    adc
    +关注

    关注

    98

    文章

    6430

    浏览量

    544046
  • 采集系统
    +关注

    关注

    0

    文章

    168

    浏览量

    20603
  • PPG
    PPG
    +关注

    关注

    2

    文章

    62

    浏览量

    18125
  • FreeRTOS
    +关注

    关注

    12

    文章

    483

    浏览量

    61993

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

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

收藏 人收藏

    评论

    相关推荐

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

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

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

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

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

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

    基于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 29次下载

    ARM嵌入式系统开发-软件设计与优化

    ARM嵌入式系统开发-软件设计与优化
    发表于 02-11 09:57 94次下载
    ARM<b class='flag-5'>嵌入式</b><b class='flag-5'>系统</b>开发-<b class='flag-5'>软件设计</b>与优化

    嵌入式USB主机设计(硬件设计和软件设计)

    嵌入式USB主机设计(硬件设计和软件设计) 嵌入式USB主机硬件设计选用廉价的51系列单片机(89C52)控制US
    发表于 11-26 13:58 1307次阅读
    <b class='flag-5'>嵌入式</b>USB主机设计(硬件设计和<b class='flag-5'>软件设计</b>)

    采用构件技术的嵌入式系统复用软件设计

    采用构件技术的嵌入式系统复用软件设计 提高软件生产率成为软件产业的当务之急;基于构件的软件复用
    发表于 03-29 15:10 769次阅读
    采用构件技术的<b class='flag-5'>嵌入式</b><b class='flag-5'>系统</b>复用<b class='flag-5'>软件设计</b>

    嵌入式系统智能键盘的软件设计

    嵌入式系统智能键盘的软件设计 引言键盘是智能化测控系统主要的信息输入方式,是实现人机对话的重要途径,因此如何有效地控制键盘并为系统服务是
    发表于 03-11 13:45 1079次阅读
    <b class='flag-5'>嵌入式</b><b class='flag-5'>系统</b>智能键盘的<b class='flag-5'>软件设计</b>

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

    基于ARM的嵌入式系统软件设计部分
    发表于 01-14 12:32 15次下载

    ARM的嵌入式系统软件设计

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

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

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

    嵌入式软件设计设计模式

    文章目录前言1.设计模式适配器模式2.设计模式单例模式3.设计模式命令模式前言在嵌入式软件设计过程中,也会用到一些设计模式,所以说设计
    发表于 10-21 11:07 9次下载
    <b class='flag-5'>嵌入式</b><b class='flag-5'>软件设计</b><b class='flag-5'>之</b>设计模式

    嵌入式系统软件设计教材资料

    嵌入式系统软件设计教材资料免费下载。
    发表于 04-12 14:44 5次下载

    嵌入式软件设计的原则分享

    嵌入式软件开发如果具有更好的阅读性、扩展性以及维护性,就需要考虑很多因素。今天给大家分享几个嵌入式软件设计的原则。
    发表于 02-25 10:54 650次阅读
    <b class='flag-5'>嵌入式</b><b class='flag-5'>软件设计</b>的原则分享