首页 > 应用与分享 > 基于R7FA2E1芯片的血氧仪设计#第八届立创电赛#
  • 基于R7FA2E1芯片的血氧仪设计#第八届立创电赛#

  • 2024-01-18 15:58:41 阅读量:1353

瑞萨MCU杯第八届立创电子设计开源大赛正式落幕,最终获奖项目正式出炉!本文介绍的是:本届大赛三等奖项目基于R7FA2E1芯片的血氧仪设计

本文作者:瑞萨MCU杯第八届立创电子设计开源大赛选手@子牧禁止商用,未经许可禁止转载


1、项目功能介绍

  

1、血氧、脉搏和温度的实时检测

2、电压的实时检测

3、屏幕实时显示

4、支持电池充电

5、稳定的电源

6、按键控制交互

 

除此之外,它还可以当作R7FA2E1A72DFL的开发板使用,它支持以下功能:

1、SWD调试下载

2、串口调试下载

3、一路IIC与MAX30102连接,可学习IIC和MAX30102相关知识

4、一路SPI与0.96LCD彩幕连接,可学习IIC与屏幕相关知识

5、一个可编程LED

6、两个可编程按键

7、一路ADC检测电池电压

 

 

2、硬件部分

图4-1 硬件结构图 


本项目的硬件结构图如上图4-1所示,该图包含了硬件部分全部内容,但在这里只针对血氧仪相关功能进行详细说明。


1、R7FA2E1A72DFL主控

图4-2 主控原理图


本项目的主控原理图如上图4-2所示,与官方例程相比,本项目增加了16MHz晶振和SPI与IIC的串联电阻,并启用了ADC功能。

 

2、血氧、脉搏和温度的实时检测


本项目的血氧、脉搏和温度的实时检测功能,是通过ADI公司的MAX30102采用反射式方案进行检测实现。MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了多个LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路,能够实现PRG(photoplethysmographic,光电描记法)脉搏波信号的高精度检测。图4-3是通过本设备检测出的波形,该波形由检测到的原始数据减去数据最小值得到,使用的上位机软件是匿名助手。


 图4-3 检测到的波形


在本项目确立之初,是打算直接使用MAX30102芯片进行检测,原方案是通过拆卸MAX30102模块上的芯片再焊接但本项目的板子上进行使用(模块比芯片要便宜很多,且芯片不好买),但可能是由于在使用热风枪拆卸的过程中破坏了芯片,导致焊接到本项目的板子上后无法正常使用,最终选择直接使用MAX30102模块完成信号的检测,通信方式使用IIC,MAX30102模块如图4-4所示。


图4-4 MAX30102模块图片

 

3、电压的实时检测

电压的实时检测通过R7FA2E1A72DFL的ADC功能进行实现,该功能的原理图如图4-5所示。

 

 图4-5 电池电压检测原理图


        本项目中的主控R7FA2E1A72DFL是采用3.3V供电,待检测电压最高可达5V,直接接入芯片会损害芯片,不能直接接入,需要分压后再接入,并通过分压公式实现电压的检测。在本设计中将待检测电压设定为输入电压,当输入电压大于4.2V时(3.7电池的电压一般最高可达4.2V),可判定为正在充电,当小于4.2V时,则是电池正常供电(这个判断阈值不能设置为4.2V,应比4.2V稍大,设置在4.4V左右即可)。这样就可以在实现电池电压检测同时实现充电状态的检测。

 

4、屏幕实时显示

       本项目中使用是屏幕是0.96寸LCD液晶屏,屏幕分辨率为160*80,通信接口为SPI,该部分原理图如图4-6所示。


 图4-6 屏幕部分原理图

 

5、支持电池充电

本项目的充放电电路参考官方示例项目的充放电电路,引出充电指示灯,限流电阻设置为3kΩ,根据图4-7公式可得充电电流大小约为400mA。


图4-7 充电电流计算公式

本项目充放电电路原理图如图4-8所示。

图4-8 充放电原理图

 

6、稳定的电源

本项目的电源电路参考官方示例项目的电源电路,提供TYPE-C供电和电池供电两种方式,当TYPE-C接入时,由TYPE-C供电,同时为电池充电,当TYPE-C未接入时,由电池为血氧仪供电。当电源接入后,使用6206线性稳压器得到3.3V稳压,为主控和屏幕提供稳定的电源。电源部分原理图如图4-9所示。


图4-9 电源部分原理图

 

7、按键控制交互

本项目中通过两个按键实现按键控制交互,按键一代表开始检测,按键二代表切换模式。按键控制电路如下图4-10所示。


图4-10 按键控制电路原理图

 

8、面板说明

在PCB板外有外壳和面板,由于按键设计在正面,且面板在外壳的上面,所以最终是通过面板实现按键的按压,如图4-11所示。由图4-12面板3D效果图可知,通过按压图中红框部分即可实现按键的按压。


图4-11 面板图片

图4-12 面板3D效果图

 

9、外壳说明

当外壳厚度过厚时,TYPE-C接口可能无法很方面的接入,这时候就只能挖一个大孔或者将外壳厚度改薄(这是我原本的做法),而在本项目中我采用的方式是在原本TYPE-C的通孔轮廓外再打一个孔,但不打通,如图4-13所示,3D效果如图4-14所示,实际效果也很不错,如图4-15所示。


图4-13 外壳通孔PCB图片

图4-14 外壳通孔3D图片

图4-15 外壳效果图


 

3、软件部分


本项目的软件部分共分为两方面,分别是程序开发和上位机调试,下面将分别对这两个部分进行说明。


1、程序开发

程序开发部分我使用的软件是RASC+keil联合开发,RASC负责芯片的初始化配置,keil负责编写逻辑程序。在本项目的开发过程中,既是学习编程的过程也是学习新软件的过程。我学习这两个软件的途径主要有两个,一个是本期训练营的官方课程,链接如下: 基于RASC的keil电子时钟制作 ,另一个是野火关于瑞萨的教学课程,目前视频文件不多,主要还是文档类,链接如下:[野火]瑞萨RA系列FSP库开发实战指南 。这些软件的配置参考以上链接的教学内容即可,我在此就不再赘述。


1.1、RASC的初始化配置

关于RASC的初始化配置,首先是堆栈配置,本项目的堆栈配置如下图5-1所示。

图5-1 堆栈配置


本项目使用的堆栈主要有GPIO、RTC、UART和ADC,其中GPIO、RTC和UART由于篇幅有限而且上面的教程中都有讲解,这里就不再说明,只看一下ADC的配置。ADC配置如下图5-2所示。


图5-2 ADC_RASC配置图

       

然后是RASC关于芯片的引脚配置,在这一部分,主要有IIC、SPI、ADC、LED和按键的相关引脚需要配置。IIC是MAX30102使用,IIC引脚使用的软件IIC,引脚是P400和P401,分别是SCL和SDA,引脚分别配置为推挽输出和开漏输出,SDA配置为开漏是因为需要在输出同时可以读取数据,该部分除了IIC引脚,还有一个中断引脚INT,配置为上拉输入;SPI是屏幕使用,该部分除了SPI引脚还有屏幕的相关引脚,引脚分别包括片选引脚CS-P015,复位引脚RES-P500,数据/命令控制引脚DC-P100,数据引脚SDA-P101,时钟引脚SCL-P102,背光控制引脚BLK-P103,除SDA配置为开漏输出外,其他都配置为推挽输出;LED引脚是P001,设置为推挽输出;按键引脚分别为P914和P013,均配置为上拉输入。ADC的引脚配置与其他不一样,所以放到后面说。这些引脚数量较多,所以不全部贴图,仅将各种引脚配置类型各贴一个图。下图5-3是推挽输出配置图,图5-4是开漏输出配置图,图5-5是上拉输入配置图。


图5-3 推挽输出

图5-4 开漏输出

图5-5 上拉输入

 

       ADC引脚是P000,配置图如下图5-6所示。

图5-6 ADC引脚配置图

RASC的初始化配置就此结束。

 

1.2、keil的逻辑程序

1.2.1 程序说明

  keil程序的主流程图如下图5-7所示。

图5-7 主程序流程图

 

从主程序流程图中可以看出,程序主要是按键扫描、传感器检测和屏幕显示三个函数在循环执行,下面对三个函数进行说明。

首先是按键扫描程序,该程序扫描按键引脚,当按键一被按下时,输出1,当按键2被按下时,输出2,无按键按下时,输出0,不支持连续按。该函数参考正点原子按键程序,链接如下:正点原子按键教学 。代码如下:


//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//2,KEY1按下
//3,KEY2按下 
//注意此函数有响应优先级,KEY1>KEY2
uint8_t Key_Scan(uint8_t mode)
{  
static uint8_t key_up=1;//按键按松开标志
if(mode)key_up=1;  //支持连按  
if(key_up&&(KEY1_READ==0||KEY2_READ==0))
{
delay_ms(10);//去抖动 
key_up=0;
if(KEY1_READ==0)return 1;
else if(KEY2_READ==0)return 2;
}else if(KEY1_READ==1&&KEY2_READ==1)key_up=1;     
  return 0;// 无按键按下
}

        第二个是传感器检测函数,该函数流程图如下图5-8所示。

 图5-8 传感器检测函数流程图

 

由上图可知该函数的主要逻辑是根据按键值和屏幕状态值进行一系列处理,总结来说就是:当按键1按下后,开始检测,但检测什么由屏幕状态值决定,当为血氧-心率状态时,检测心率和血氧,当为温度-电压状态时,检测温度和电压;当按键2按下后,会切换屏幕状态值。而检测数据则是由我定义的一个结构体变量来存储,该结构体定义代码如下:

struct MAX30102{
    uint32_t spo2_value;           //血氧值
    uint32_t hr_value;             //心率值
    int8_t vaild_value;            //血氧-心率检测有效值
    uint32_t n_ir_buffer_length;   //缓冲区长度
    uint8_t temp_refresh_state;    //温度检测是否刷新状态值
    float temp;                    //温度值
    float volt;                    //电压值
};

       传感器检测函数代码如下:

void chedk_fuc(struct MAX30102 *max30102_data_ori, uint8_t key, uint8_t *lcd_state)
{
struct MAX30102 max30102_data = *max30102_data_ori;
if(key == 1 & *lcd_state == 0)               //当KEY1按下且LCD状态为0时,开始检测血氧和心率
{
max30102_read_sp02_PR(&max30102_data);
if(max30102_data.vaild_value != 1)
{
LCD_Fill(0,22,LCD_W,LCD_H,BLACK);
LCD_ShowString(40,26,"error",RED,BLACK,32,0);
}
}
else if(key == 1 & *lcd_state == 1) //当KEY1按下且LCD状态为1时,检测一次电压和温度
{
max30102_read_temp_volt(&max30102_data);
}
else if(key == 2)        //KEY2按下,切换显示状态
{
if(*lcd_state)
*lcd_state = 0;
else
*lcd_state =1;
 
LCD_Fill(0,0,LCD_W,LCD_H,BLACK);                  //直接刷屏,显示新内容
 
if(*lcd_state)
{
LCD_ShowString(30,6,"temp",WHITE,BLACK,12,0);
LCD_ShowString(100,6,"volt",WHITE,BLACK,12,0);
LCD_ShowString(28,26,"--",WHITE,BLACK,32,0);
LCD_ShowString(100,26,"--",WHITE,BLACK,32,0);
max30102_read_temp_volt(&max30102_data);
}
else
{
LCD_ShowString(30,6,"%sp02",WHITE,BLACK,12,0);
LCD_ShowString(100,6,"PR bpm",WHITE,BLACK,12,0);
LCD_ShowString(28,26,"--",WHITE,BLACK,32,0);
LCD_ShowString(100,26,"--",WHITE,BLACK,32,0);
}
}
*max30102_data_ori = max30102_data;
}


第三个是屏幕显示函数,该函数流程图如下图5-9所示。

 

 图5-9 屏幕显示函数流程图

 

      由上图可知该函数的主要逻辑是根据检测有效值和屏幕状态值进行一系列处理,总结来说就是:当血氧-心率检测有效值为1且屏幕状态是血氧-心率状态时,显示血氧-心率数据;当温度-电压检测有效值为1且屏幕状态是温度-电压状态时,显示温度-电压数据。同时进行一个计数,当计数5次后,检测一次电压状态,当电压值大于4.35时,在屏幕上显示正在充电的标志“C”,否则不显示任何状态,最后再将有效值都置0,使他们都失效,防止反复显示。屏幕显示函数代码如下:


void LED_show_main(struct MAX30102 *max30102_data_ori, uint8_t lcd_state)
{
float volt;
uint8_t count = 1;
if(max30102_data_ori->vaild_value && lcd_state == 0)
{
LCD_Fill(0,22,LCD_W,LCD_H,BLACK);
LCD_ShowIntNum(29,26,max30102_data_ori->spo2_value,2,WHITE,BLACK,32);
if(max30102_data_ori->hr_value < 100)
{
LCD_ShowIntNum(101,26,max30102_data_ori->hr_value,2,WHITE,BLACK,32);
}
else if(max30102_data_ori->hr_value >= 100)
LCD_ShowIntNum(93,26,max30102_data_ori->hr_value,3,WHITE,BLACK,32);
if(max30102_data_ori->temp_refresh_state && lcd_state == 1)
{
LCD_Fill(0,22,LCD_W,LCD_H,BLACK);
LCD_ShowFloatNum1(13,26,max30102_data_ori->temp,4,WHITE,BLACK,24);
LCD_ShowFloatNum1(90,26,max30102_data_ori->volt,3,WHITE,BLACK,24);
}
   
if(count%5 == 1)                 //相当于循环5检测一次充电状态
{
volt = 2 * Read_ADC_Voltage_Value();
if(volt > 4.35)
LCD_ShowString(150,6,"C",GREEN,BLACK,12,0);
else
LCD_Fill(150,6,160,20,BLACK);  
count = 1;
}
count++;
max30102_data_ori->vaild_value = 0;         //防止屏幕多次刷新
max30102_data_ori->temp_refresh_state = 0;  
}


1.2.2 补充说明

除了以上内容,本项目的程序还有一些地方要说明一下。

首先是MAX30102的处理算法,本项目是基于淘宝店家给的源码设计完成的,该源码一次计算需要处理500个数据,在本项目中,无法满足该条件,所以缩减为100个数据。将数据缩减后检测精度可能受到影响,可以进行多次检测,进行均值滤波以提高检测精度。在本项目的代码中,已实现该功能,可以修改max30102.c文件中max30102_read_sp02_PR函数中的MAX_effective_detection_number变量,该变量代表最大有效检测次数,在函数中意味着只要检测成功次数达到MAX_effective_detection_number,即代表检测检测有效,同时会在结尾将数据根据MAX_effective_detection_number取均值。但注意max30102_read_sp02_PR函数中还有MAX_detection_number变量,该变量代表这最大检测次数,MAX_effective_detection_number不能大于MAX_detection_number。这两个变量的位置如图5-10所示。


图5-10 均值滤波相关代码图

 

 然后是接近模式设置,接近模式指的是检测手指是否有放在传感器上的模式,开启它可以大大降低误检率。接近模式本质上是通过检测LED反射回来的值是否满足手指放在传感器上的条件,那么根据这个思路我们可以设置一个阈值,当100个采样值的均值大于该值时,说明手指正常放在传感器上,当小于该值时,则代表有误,数据无效。该代码位置在algorithm.c文件的maxim_heart_rate_and_oxygen_saturation函数中,图5-11是接近模式程序图。


 图5-11 接近模式相关代码

 

以上就是程序开发的全部内容。

 

2、上位机调试

为了检测MAX30102是否成功检测出我们需要的PRG脉搏波信号,本项目使用匿名上位机将MAX30102采集的数据通过波形的方式进行显示。

void AnoPTv8TxFrameF1_assistant(int32_t _ir, int32_t _red)
{
uint8_t i;
uint8_t sumcheck = 0;
uint8_t addcheck = 0;
uint8_t _cnt=0;
 
//默认即可
databuf[_cnt++] = 0xAB;
databuf[_cnt++] = 0x01;
databuf[_cnt++] = 0xFE;
databuf[_cnt++] = 0xF1;
 
//用这两位来代表数据的字节数
databuf[_cnt++] = 8;   
databuf[_cnt++] = 0;
 
databuf[_cnt++] = BYTE0(_ir);
databuf[_cnt++] = BYTE1(_ir);
databuf[_cnt++] = BYTE2(_ir);
databuf[_cnt++] = BYTE3(_ir);
 
databuf[_cnt++] = BYTE0(_red);
databuf[_cnt++] = BYTE1(_red);
databuf[_cnt++] = BYTE2(_red);
databuf[_cnt++] = BYTE3(_red);
 
 
for(i=0;i<14;i++) 
{
sumcheck+=databuf[i];
addcheck+=sumcheck;
}
databuf[_cnt++]=sumcheck;
databuf[_cnt++]=addcheck;
 
R_SCI_UART_Write(&g_uart9_ctrl, databuf, _cnt);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
}

上述代码是根据匿名通信协议中的灵活格式帧编写的代码,可以将发送的数据通过波形的方式显示出来。需要注意的是这段代码最后的串口发送数据的函数不能更改为printf函数,因为printf函数发送的数据的会变换格式,导致串口接收端接收到的数据并不是我们想发送的数据,而使用R_SCI_UART_Write函数发送数据会将数据原封不动的发送到接收端,这样才能正常通信。匿名通信协议pdf文件我以添加到附件中,有需要的可以下载查看。

匿名助手的软件相关信息介绍可以参考匿名官方发布的视频,链接如下:匿名助手上位机公测版本入门介绍视频 ,虽然好像公测没多久,但是这个匿名助手异常好用。

 

以上就是软件部分的全部内容。

 

4、BOM清单

 

5、大赛LOGO验证


 

6、注意事项

1、由于使用的是MAX30102模块,电路是直接裸露在外面的,所以使用时一定注意保持手部干燥,小心短路。

2、由于设备限制,本项目血氧仪未经实验校准,精度较差,仅适于测试或大概检测,不适用于医疗应用。


7、参考链接

(1)、MAX30102 用于可穿戴健康设备的高灵敏度脉搏血氧仪和心率传感器

2)、【立创电赛】基于瑞萨的桌面电子时钟设计

3)、基于RASC的keil电子时钟制作

(4)、[野火]瑞萨RA系列FSP库开发实战指南

 (5)、正点原子按键教学 

(6)、匿名助手上位机公测版本入门介绍视频


您的浏览器版本过低(IE8及IE8以下的浏览器或者其他浏览器的兼容模式),存在严重安全漏洞,请切换浏览器为极速模式或者将IE浏览器升级到更高版本。 【查看详情】
推荐您下载并使用 立创商城APP 或者最新版 谷歌浏览器火狐浏览器360浏览器搜狗浏览器QQ浏览器 的极(高)速模式进行访问。
© 2022 深圳市立创电子商务有限公司 版权所有

提示

您确定删除此收货地址吗?

提示

您确定删除此收货地址吗?

成功提示

content

失败提示

content

微信咨询

关注公众号咨询客服

咨询客服
  • 在线客服热线

    0755-83865666

  • 服务时间

    工作日  8:30~20:30

    节假日  8:30~18:00

  • 服务投诉

QQ咨询
优惠券
芯媒体

立创商城旗下芯媒体

微信号:icsight

建议反馈
填问卷 立创用户体验问卷调查 立即参与
活动规则
活动规则
展开客服