【STM32+HAL库】---- 驱动MAX30102心率血氧传感器
硬件开发板:STM32F407VET6
软件平台:cubemax+keil+VScode
1 MAX30102心率血氧传感器工作原理
MAX30102传感器是一种集成了红外光源、光电检测器和信号处理电路的高度集成传感器,主要用于心率和血氧饱和度的测量。以下是MAX30102传感器的主要特点和工作原理:
-
红外光源:MAX30102传感器内部集成了红外LED光源,用于照射到皮肤表面。红外光在血液中的反射特性可用于测量心率和血氧饱和度。
-
光电检测器:传感器还集成了光电检测器,用于接收皮肤表面反射的光信号。这些光信号中包含了经过血液的红外光的吸收情况,根据吸收的程度可以推断出血液中的血氧饱和度。
-
信号处理电路:MAX30102传感器内部还集成了一系列的信号处理电路,用于对接收到的光信号进行滤波、放大、数字化等处理。经过处理后的信号可以直接输出给微控制器进行进一步的处理和分析。
-
工作原理:MAX30102传感器的工作原理基于红外光在血液中的吸收特性。红外光能够穿透皮肤并被血液吸收,血液中的氧合血和脱氧血对红外光的吸收程度不同,因此可以通过测量红外光的吸收情况来推断血液的氧合状态。传感器利用LED发出的光照射到皮肤表面,然后通过光电检测器接收经过皮肤反射的光信号,并根据光信号的变化来计算心率和血氧饱和度。
综上所述,MAX30102传感器通过红外光源和光电检测器实现了对心率和血氧饱和度的测量,具有高度集成、精准度高、成本低廉等特点,在医疗监护、健康监测等领域有着广泛的应用。
2 程序
2.1 MAX30102.h
/*** ************************************************************************* * @file MAX30102.h* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#ifndef _MAX30102_H
#define _MAX30102_H#include "main.h" // Device header
#include "i2c.h"
#include "stdbool.h"#define MAX30102_Device_address 0xAE//register addresses
#define REG_INTR_STATUS_1 0x00
#define REG_INTR_STATUS_2 0x01
#define REG_INTR_ENABLE_1 0x02
#define REG_INTR_ENABLE_2 0x03
#define REG_FIFO_WR_PTR 0x04
#define REG_OVF_COUNTER 0x05
#define REG_FIFO_RD_PTR 0x06
#define REG_FIFO_DATA 0x07
#define REG_FIFO_CONFIG 0x08
#define REG_MODE_CONFIG 0x09
#define REG_SPO2_CONFIG 0x0A
#define REG_LED1_PA 0x0C
#define REG_LED2_PA 0x0D
#define REG_PILOT_PA 0x10
#define REG_MULTI_LED_CTRL1 0x11
#define REG_MULTI_LED_CTRL2 0x12
#define REG_TEMP_INTR 0x1F
#define REG_TEMP_FRAC 0x20
#define REG_TEMP_CONFIG 0x21
#define REG_PROX_INT_THRESH 0x30
#define REG_REV_ID 0xFE
#define REG_PART_ID 0xFF#define SAMPLES_PER_SECOND 100 //检测频率uint8_t Max30102_reset(void);
void MAX30102_Config(void);
void max30102_read_fifo(void);uint8_t max30102_write_reg(uint8_t addr, uint8_t data);
uint8_t max30102_read_reg(uint8_t addr );#endif
2.2 MAX30102.c
/*** ************************************************************************* * @file MAX30102.c* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#include "MAX30102.h"uint16_t fifo_red; //定义FIFO中的红光数据
uint16_t fifo_ir; //定义FIFO中的红外光数据/*** ************************************************************************* @brief 向MAX30102寄存器写入一个值* * @param[in] addr 寄存器地址* @param[in] data 传输数据* * @return * *************************************************************************/
uint8_t max30102_write_reg(uint8_t addr, uint8_t data)
{HAL_I2C_Mem_Write(&hi2c1, MAX30102_Device_address, addr, 1, &data,1,HAL_MAX_DELAY);return 1;
}/*** ************************************************************************* @brief 读取MAX30102寄存器的一个值* * @param[in] addr 寄存器地址* * @return * *************************************************************************/
uint8_t max30102_read_reg(uint8_t addr )
{uint8_t data=0;HAL_I2C_Mem_Read(&hi2c1, MAX30102_Device_address, addr, 1, &data, 1, HAL_MAX_DELAY);return data;
}/*** ************************************************************************* @brief MAX30102传感器复位* * * @return * *************************************************************************/
uint8_t Max30102_reset(void)
{if(max30102_write_reg(REG_MODE_CONFIG, 0x40))return 1;elsereturn 0;
}/*** ************************************************************************* @brief MAX30102传感器模式配置* * * *************************************************************************/
void MAX30102_Config(void)
{max30102_write_reg(REG_INTR_ENABLE_1,0xc0); INTR settingmax30102_write_reg(REG_INTR_ENABLE_2,0x00);//max30102_write_reg(REG_FIFO_WR_PTR,0x00);//FIFO_WR_PTR[4:0]max30102_write_reg(REG_OVF_COUNTER,0x00);//OVF_COUNTER[4:0]max30102_write_reg(REG_FIFO_RD_PTR,0x00);//FIFO_RD_PTR[4:0]max30102_write_reg(REG_FIFO_CONFIG,0x0f);//sample avg = 1, fifo rollover=false, fifo almost full = 17max30102_write_reg(REG_MODE_CONFIG,0x03);//0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LEDmax30102_write_reg(REG_SPO2_CONFIG,0x27); // SPO2_ADC range = 4096nA, SPO2 sample rate (50 Hz), LED pulseWidth (400uS) max30102_write_reg(REG_LED1_PA,0x32);//Choose value for ~ 10mA for LED1max30102_write_reg(REG_LED2_PA,0x32);// Choose value for ~ 10mA for LED2max30102_write_reg(REG_PILOT_PA,0x7f);// Choose value for ~ 25mA for Pilot LED
}/*** ************************************************************************* @brief 读取FIFO寄存器的数据* * * *************************************************************************/
void max30102_read_fifo(void)
{uint16_t un_temp;fifo_red=0;fifo_ir=0;uint8_t ach_i2c_data[6];//read and clear status registermax30102_read_reg(REG_INTR_STATUS_1);max30102_read_reg(REG_INTR_STATUS_2);ach_i2c_data[0]=REG_FIFO_DATA;HAL_I2C_Mem_Read(&hi2c1,MAX30102_Device_address,REG_FIFO_DATA,1,ach_i2c_data,6,HAL_MAX_DELAY);un_temp=ach_i2c_data[0];un_temp<<=14;fifo_red+=un_temp;un_temp=ach_i2c_data[1];un_temp<<=6;fifo_red+=un_temp;un_temp=ach_i2c_data[2];un_temp>>=2;fifo_red+=un_temp;un_temp=ach_i2c_data[3];un_temp<<=14;fifo_ir+=un_temp;un_temp=ach_i2c_data[4];un_temp<<=6;fifo_ir+=un_temp;un_temp=ach_i2c_data[5];un_temp>>=2;fifo_ir+=un_temp;if(fifo_ir<=10000){fifo_ir=0;}if(fifo_red<=10000){fifo_red=0;}
}
2.3 algorithm.h
/*** ************************************************************************* * @file algorithm.h* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#ifndef __ALGORITHM_H
#define __ALGORITHM_H#define FFT_N 1024 //定义傅里叶变换的点数
#define START_INDEX 8 //低频过滤阈值struct compx //定义一个复数结构
{float real;float imag;
}; typedef struct //定义一个直流滤波器结构体
{float w;int init;float a;
}DC_FilterData; //用于存储直流滤波器的参数typedef struct //定义一个带宽滤波器结构体
{float v0;float v1;
}BW_FilterData; //用于存储带宽滤波器的参数double my_floor(double x);double my_fmod(double x, double y);double XSin( double x );double XCos( double x );int qsqrt(int a);struct compx EE(struct compx a,struct compx b);void FFT(struct compx *xin);int find_max_num_index(struct compx *data,int count);
int dc_filter(int input,DC_FilterData * df);
int bw_filter(int input,BW_FilterData * bw);#endif
2.4 algorithm.c
/*** ************************************************************************* * @file algorithm.c* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#include "algorithm.h"
#include "stm32f4xx.h" #define XPI (3.1415926535897932384626433832795) //定义圆周率值,保留31位
#define XENTRY (100)
#define XINCL (XPI/2/XENTRY) //用于正弦函数的精度控制
#define PI 3.1415926535897932384626433832795028841971 //定义圆周率值,保留40位/*** ************************************************************************* @brief 静态正弦值对应表* * 使用预先计算好的数值可以节省计算时间* *************************************************************************/
static const double XSinTbl[] =
{0.00000000000000000 , 0.015707317311820675 , 0.031410759078128292 , 0.047106450709642665 , 0.062790519529313374 , 0.078459095727844944 , 0.094108313318514325 , 0.10973431109104528 , 0.12533323356430426 , 0.14090123193758267 ,0.15643446504023087 , 0.17192910027940955 , 0.18738131458572463 , 0.20278729535651249 , 0.21814324139654256 , 0.23344536385590542 , 0.24868988716485479 , 0.26387304996537292 , 0.27899110603922928 , 0.29404032523230400 ,0.30901699437494740 , 0.32391741819814940 , 0.33873792024529142 , 0.35347484377925714 , 0.36812455268467797 , 0.38268343236508978 , 0.39714789063478062 , 0.41151435860510882 , 0.42577929156507272 , 0.43993916985591514 ,0.45399049973954680 , 0.46792981426057340 , 0.48175367410171532 , 0.49545866843240760 , 0.50904141575037132 , 0.52249856471594880 , 0.53582679497899666 , 0.54902281799813180 , 0.56208337785213058 , 0.57500525204327857 ,0.58778525229247314 , 0.60042022532588402 , 0.61290705365297649 , 0.62524265633570519 , 0.63742398974868975 , 0.64944804833018377 , 0.66131186532365183 , 0.67301251350977331 , 0.68454710592868873 , 0.69591279659231442 ,0.70710678118654757 , 0.71812629776318881 , 0.72896862742141155 , 0.73963109497860968 , 0.75011106963045959 , 0.76040596560003104 , 0.77051324277578925 , 0.78043040733832969 , 0.79015501237569041 , 0.79968465848709058 ,0.80901699437494745 , 0.81814971742502351 , 0.82708057427456183 , 0.83580736136827027 , 0.84432792550201508 , 0.85264016435409218 , 0.86074202700394364 , 0.86863151443819120 , 0.87630668004386369 , 0.88376563008869347 ,0.89100652418836779 , 0.89802757576061565 , 0.90482705246601958 , 0.91140327663544529 , 0.91775462568398114 , 0.92387953251128674 , 0.92977648588825146 , 0.93544403082986738 , 0.94088076895422557 , 0.94608535882754530 ,0.95105651629515353 , 0.95579301479833012 , 0.96029368567694307 , 0.96455741845779808 , 0.96858316112863108 , 0.97236992039767667 , 0.97591676193874743 , 0.97922281062176575 , 0.98228725072868872 , 0.98510932615477398 ,0.98768834059513777 , 0.99002365771655754 , 0.99211470131447788 , 0.99396095545517971 , 0.99556196460308000 , 0.99691733373312796 , 0.99802672842827156 , 0.99888987496197001 , 0.99950656036573160 , 0.99987663248166059 ,1.00000000000000000
};/*** ************************************************************************* @brief 向下取整函数* * @param[in] x 需要取整的浮点数参数* * @return * *************************************************************************/
double my_floor(double x)
{double y=x;if( (*( ( (int *) &y)+1) & 0x80000000) != 0) //或者if(x<0)return (float)((int)x)-1;elsereturn (float)((int)x);
}/*** ************************************************************************* @brief 取余函数* * @param[in] x 参数1* @param[in] y 参数2* @note 避免了对浮点数的直接除法运算,从而提高了效率* @return * *************************************************************************/
double my_fmod(double x, double y)
{double temp, ret;if (y == 0.0)return 0.0;temp = my_floor(x/y);ret = x - temp * y;if ((x < 0.0) != (y < 0.0))ret = ret - y;return ret;
}/*** ************************************************************************* @brief 正弦函数* * @param[in] x 角度值* * @return * * @note 通过查表和泰勒展开式计算正弦值,相对于直接调用标准库函数,* 可能会牺牲一些精度,但在某些嵌入式系统中可能更加高效* *************************************************************************/
double XSin( double x )
{int s = 0 , n;double dx , sx , cx;if( x < 0 )s = 1 , x = -x;x = my_fmod( x , 2 * XPI );if( x > XPI )s = !s , x -= XPI;if( x > XPI / 2 )x = XPI - x;n = (int)( x / XINCL );dx = x - n * XINCL;if( dx > XINCL / 2 )++n , dx -= XINCL;sx = XSinTbl[n];cx = XSinTbl[XENTRY-n];x = sx + dx*cx - (dx*dx)*sx/2- (dx*dx*dx)*cx/6 + (dx*dx*dx*dx)*sx/24;return s ? -x : x;
}/*** ************************************************************************* @brief 余弦函数* * @param[in] x 角度值* * @return * *************************************************************************/
double XCos( double x )
{return XSin( x + XPI/2 );
}/*** ************************************************************************* @brief 开平方* * @param[in] a 参数* * @return * *************************************************************************/
int qsqrt(int a)
{uint32_t rem = 0, root = 0, divisor = 0;uint16_t i;for(i=0; i<16; i++){root <<= 1;rem = ((rem << 2) + (a>>30));a <<= 2;divisor = (root << 1) + 1;if(divisor <= rem){rem -= divisor;root++;}}return root;
}/*** ************************************************************************* @brief 两个复数相乘* * @param[in] a 复数1* @param[in] b 复数2* @note 乘积的实部为两个复数实部的乘积减去虚部的乘积* 乘积的虚部为两个复数实部的乘积加上虚部的乘积* @return * *************************************************************************/
struct compx EE(struct compx a,struct compx b)
{struct compx c;c.real=a.real*b.real-a.imag*b.imag;c.imag=a.real*b.imag+a.imag*b.real;return(c);
}/*** ************************************************************************* @brief 对输入的复数组进行快速傅里叶变换(FFT)* * @param[in] xin 复数组* * *************************************************************************/
void FFT(struct compx *xin)
{int f,m,nv2,nm1,i,k,l,j=0;struct compx u,w,t;nv2=FFT_N/2; //变址运算,即把自然顺序变成倒位序,采用雷德算法nm1=FFT_N-1; for(i=0;i<nm1;i++) {if(i<j) //如果i<j,即进行变址{t=xin[j]; xin[j]=xin[i];xin[i]=t;}k=nv2; //求j的下一个倒位序while(k<=j) //如果k<=j,表示j的最高位为1 { j=j-k; //把最高位变成0k=k/2; //k/2,比较次高位,依次类推,逐个比较,直到某个位为0}j=j+k; //把0改为1}{ //FFT运算核,使用蝶形运算完成FFT运算int le,lei,ip; f=FFT_N;for(l=1;(f=f/2)!=1;l++) //计算l的值,即计算蝶形级数;for(m=1;m<=l;m++) // 控制蝶形结级数{ //m表示第m级蝶形,l为蝶形级总数l=log(2)Nle=2<<(m-1); //le蝶形结距离,即第m级蝶形的蝶形结相距le点lei=le/2; //同一蝶形结中参加运算的两点的距离u.real=1.0; //u为蝶形结运算系数,初始值为1u.imag=0.0;w.real=XCos(PI/lei); //w为系数商,即当前系数与前一个系数的商w.imag=-XSin(PI/lei);for(j=0;j<=lei-1;j++) //控制计算不同种蝶形结,即计算系数不同的蝶形结{for(i=j;i<=FFT_N-1;i=i+le) //控制同一蝶形结运算,即计算系数相同蝶形结{ip=i+lei; //i,ip分别表示参加蝶形运算的两个节点t=EE(xin[ip],u); //蝶形运算,详见公式xin[ip].real=xin[i].real-t.real;xin[ip].imag=xin[i].imag-t.imag;xin[i].real=xin[i].real+t.real;xin[i].imag=xin[i].imag+t.imag;}u=EE(u,w); //改变系数,进行下一个蝶形运算}}}
}/*** ************************************************************************* @brief 找到具有最大实部的元素的索引* * @param[in] data Comment* @param[in] count Comment* * @return * *************************************************************************/
int find_max_num_index(struct compx *data,int count)
{int i=START_INDEX;int max_num_index = i;float temp = data[i].real;for(i=START_INDEX;i<count;i++){if(temp < data[i].real){temp = data[i].real;max_num_index = i;}}return max_num_index;
}/*** ************************************************************************* @brief 直流滤波器,去除信号中的直流成分,并输出滤波后的结果* * @param[in] input 输入信号* @param[in] df Comment* * @return * *************************************************************************/
int dc_filter(int input,DC_FilterData * df)
{float new_w = input + df->w * df->a;int16_t result = 5*(new_w - df->w);df->w = new_w;return result;
}/*** ************************************************************************* @brief 对输入信号进行带宽限制滤波,限制输入信号的频率范围,并输出滤波后的结果* * @param[in] input 输入信号* @param[in] bw Comment* * @return * *************************************************************************/
int bw_filter(int input,BW_FilterData * bw)
{bw->v0 = bw->v1;bw->v1 = (1.241106190967544882e-2*input)+(0.97517787618064910582 * bw->v0);return bw->v0 + bw->v1;
}
2.5 blood.h
/*** ************************************************************************* * @file blood.h* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#ifndef _BLOOD_H
#define _BLOOD_H#include "main.h"
#include "MAX30102.h"
#include "algorithm.h"
#include "math.h"void blood_data_translate(void);
void blood_data_update(void);
void blood_Loop(void);#endif
2.6 blood.c
/*** ************************************************************************* * @file blood.c* @author zxr* @brief * * ************************************************************************* @copyright Copyright (c) 2024 zxr * *************************************************************************/
#include "blood.h"
#include "usart.h"int heart; //定义心率
float SpO2; //定义血氧饱和度//调用外部变量
extern uint16_t fifo_red; //定义FIFO中的红光数据
extern uint16_t fifo_ir; //定义FIFO中的红外光数据uint16_t g_fft_index = 0; //fft输入输出下标
struct compx s1[FFT_N+16]; //FFT输入和输出:从S[1]开始存放,根据大小自己定义
struct compx s2[FFT_N+16]; //FFT输入和输出:从S[1]开始存放,根据大小自己定义#define CORRECTED_VALUE 47 //标定血液氧气含量/*** ************************************************************************* @brief 更新血氧数据* @note 从 MAX30102 的 FIFO 中读取红光和红外数据,并将它们存储到两个复数数组s1和s2中,* 这些数据随后可以用于进行傅里叶变换等后续处理* * *************************************************************************/
void blood_data_update(void)
{//标志位被使能时 读取FIFOg_fft_index=0;while(g_fft_index < FFT_N){while(max30102_read_reg(REG_INTR_STATUS_1)&0x40 ){//读取FIFOmax30102_read_fifo(); //read from MAX30102 FIFO2//将数据写入fft输入并清除输出if(g_fft_index < FFT_N){//将数据写入fft输入并清除输出s1[g_fft_index].real = fifo_red;s1[g_fft_index].imag= 0;s2[g_fft_index].real = fifo_ir;s2[g_fft_index].imag= 0;g_fft_index++;}}}
}/*** ************************************************************************* @brief 血液信息转换* * * *************************************************************************/
void blood_data_translate(void)
{ float n_denom;uint16_t i;//直流滤波float dc_red =0; float dc_ir =0;float ac_red =0; float ac_ir =0;for (i=0 ; i<FFT_N ; i++ ) {dc_red += s1[i].real ;dc_ir += s2[i].real ;}dc_red =dc_red/FFT_N ;dc_ir =dc_ir/FFT_N ;for (i=0 ; i<FFT_N ; i++ ) {s1[i].real = s1[i].real - dc_red ; s2[i].real = s2[i].real - dc_ir ; }//移动平均滤波for(i = 1;i < FFT_N-1;i++) {n_denom= ( s1[i-1].real + 2*s1[i].real + s1[i+1].real);s1[i].real= n_denom/4.00; n_denom= ( s2[i-1].real + 2*s2[i].real + s2[i+1].real);s2[i].real= n_denom/4.00; }//八点平均滤波for(i = 0;i < FFT_N-8;i++) {n_denom= ( s1[i].real+s1[i+1].real+ s1[i+2].real+ s1[i+3].real+ s1[i+4].real+ s1[i+5].real+ s1[i+6].real+ s1[i+7].real);s1[i].real= n_denom/8.00; n_denom= ( s2[i].real+s2[i+1].real+ s2[i+2].real+ s2[i+3].real+ s2[i+4].real+ s2[i+5].real+ s2[i+6].real+ s2[i+7].real);s2[i].real= n_denom/8.00; }//开始变换显示 g_fft_index = 0; //快速傅里叶变换FFT(s1);FFT(s2);for(i = 0;i < FFT_N;i++) {s1[i].real=sqrtf(s1[i].real*s1[i].real+s1[i].imag*s1[i].imag);s1[i].real=sqrtf(s2[i].real*s2[i].real+s2[i].imag*s2[i].imag);}//计算交流分量for (i=1 ; i<FFT_N ; i++ ) {ac_red += s1[i].real ;ac_ir += s2[i].real ;}for(i = 0;i < 50;i++) {if(s1[i].real<=10)break;}//读取峰值点的横坐标 结果的物理意义为 int s1_max_index = find_max_num_index(s1, 60);int s2_max_index = find_max_num_index(s2, 60);//检查HbO2和Hb的变化频率是否一致if(i>=45){//心率计算uint16_t Heart_Rate = 60.00 * SAMPLES_PER_SECOND * s1_max_index / FFT_N;heart = Heart_Rate;//血氧含量计算float R = (ac_ir*dc_red)/(ac_red*dc_ir);float sp02_num = -45.060*R*R+ 30.354 *R + 94.845;SpO2 = sp02_num;//状态正常}else //数据发生异常{heart = 0;SpO2 = 0;}//结束变换显示
}/*** ************************************************************************* @brief 心率血氧循环函数* * * *************************************************************************/
void blood_Loop(void)
{//血液信息获取blood_data_update();//血液信息转换blood_data_translate();SpO2 = (SpO2 > 99.99) ? 99.99:SpO2; printf("心率%3d/min; 血氧%2d%%\n", heart, (int)SpO2);}
2.7 main.c
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "MAX30102.h"
#include "algorithm.h"
#include "blood.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
//调用外部变量
extern int heart;
extern float SpO2;
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_I2C1_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */Max30102_reset(); //重启max30102MAX30102_Config(); //配置max30102printf("MAX30102初始化完成\n");/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){blood_Loop();// printf("display ok\n");/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
3 实验现象
10s完成一次采样及信号处理
相关文章:

【STM32+HAL库】---- 驱动MAX30102心率血氧传感器
硬件开发板:STM32F407VET6 软件平台:cubemaxkeilVScode1 MAX30102心率血氧传感器工作原理 MAX30102传感器是一种集成了红外光源、光电检测器和信号处理电路的高度集成传感器,主要用于心率和血氧饱和度的测量。以下是MAX30102传感器的主要特点…...

InstantX团队新作!基于端到端训练的风格转换模型CSGO
由InstantX团队、南京理工大学、北京航空航天大学以及北京大学联合提出了一种基于端到端训练的风格转换模型 CSGO,它采用独立的特征注入明确地解耦内容和风格特征。统一的 CSGO 实现了图像驱动的风格转换、文本驱动的风格化合成和文本编辑驱动的风格化合成。大量实验…...

Nginx安全性配置
文章目录 引言I Nginx简单的安全性配置禁止特定的HTTP方法限制URL长度禁止某些用户代理限制请求速率连接限制禁止访问某些文件类型II 常见的安全规则防御CC攻击User-Agent过滤GET-URL过滤GET-参数过滤POST过滤(sql注入、xss攻击 )引言 Nginx本身并不具备复杂的防火墙规则定制…...

k8s单master多node环境搭建-k8s版本低于1.24,容器运行时为docker
k8s 1.20.6单master多node环境搭建 1.环境规划2.初始化服务器1)配置主机名2)设置IP为静态IP3)关闭selinux4)配置主机hosts文件5)配置三台主机之间免密登录6)关闭交换分区swap,提升性能7…...

taro ui 小程序at-calendar日历组件自定义样式+选择范围日历崩溃处理
taro ui 日历文档 目录 单选标记时间: 效果: template: data: methods: 日历--范围选择: 效果: template: data: methods: 日历--间隔多选:利用标…...

ARM发布新一代高性能处理器N3
简介 就在2月21日,ARM发布了新一代面向服务器的高性能处理器N3和V3,N系列平衡性能和功耗,而V系列则注重更高的性能。此次发布的N3,单个die最高32核(并加入到CCS,Compute Subsystems,包含Core&a…...

基于Pytorch框架的深度学习U2Net网络天空语义精细分割系统源码
第一步:准备数据 头发分割数据,总共有10276张图片,里面的像素值为0和1,所以看起来全部是黑的,不影响使用 第二步:搭建模型 级联模式 通常多个类似U-Net按顺序堆叠,以建立级联模型,…...

50ETF期权和股指期权有什么区别?ETF期权应该怎么做?
今天期权懂带你了解50ETF期权和股指期权有什么区别?ETF期权应该怎么做?ETF是对个股期权,股指期权是对应该股指期货的,那么股指期权和etf期权有什么区别? 股指期权怎么交易 股指期权交易要开通股指期货账户࿰…...

JS设计模式之“神奇的魔术师” - 简单工厂模式
引言 在JavaScript开发中,我们经常需要创建和管理各种对象,而简单工厂模式就是一种最简单的用来创建对象的设计模式。 简单工厂模式通过一个工厂类来创建相似的对象,而无需直接使用具体类来实例化对象。这样可以将对象的创建过程与使用过程…...

【河北航空-注册安全分析报告-无验证方式导致安全隐患】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 1. 暴力破解密码,造成用户信息泄露 2. 短信盗刷的安全问题,影响业务及导致用户投诉 3. 带来经济损失,尤其是后付费客户,风险巨大,造…...
亚信安慧AntDB-T数据库内核之MVCC机制
本文主要介绍AntDB数据库内核中的一个很重要的机制——MVCC机制。 MVCC简介 MVCC(多版本并发控制)是AntDB数据库中实现事务隔离级别的一种机制。它允许多个事务同时对数据进行读写和修改操作,而不会相互干扰。在MVCC中,每个数据…...

【python】socket 入门以及多线程tcp链接
Socket 入门 及 多线程tcp链接 网络基础知识三要素 Socket是套接字的意思,是网络编程的核心对象,通信两端都独有自己的Socket对象, 数据在两个Socket之间通过 字节流(TCP协议) 或者 数据报包(UDP协议)的形式进行传输. 本文主要针对tcp流程进行讲解 socket-tcp流程图 1.创建服…...
【ZYNQ MPSoC开发】lwIP TCP发送用于数据缓存的软件FIFO设计
设计背景 任务是在ZYNQ的PS上使用裸机运行lwIP协议栈使用TCP把PL端通过AXI DMA传来的将近100K采样率的ADC数据发送出去,但由于数据带宽很大,有853.3mbps,所以在每一次AXI DMA简单传输结束后,lwIP未必有足够的发送buffer立即把数据…...
【TVM 教程】在 Relay 中使用外部库
Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 → https://tvm.hyper.ai/ 作者:Masahiro Masuda,Truman Tian 本文介绍如何将 cuDNN 或 cuBLAS 等外部库与 Relay 一起使用。…...
2024最新大厂面试:汇川嵌入式面试题及参考答案
目录 结合汇川业务,谈谈你对嵌入式开发的理解。 你使用过哪些芯片?请介绍它们的架构,例如 CORTEX-M3。 请描述项目的软件架构及其难点。 请介绍 SPI 的驱动和时序,包括 CS 拉低后到 CLK 第一个跳变沿的时间。同时,也请简要介绍数据链路层的相关知识。 栈溢出的原理是…...
tcp 流量控制
TCP流量控制是TCP/IP协议中用于控制发送方和接收方之间数据传输速率的一种机制,以防止网络拥塞和确保网络资源的有效利用。流量控制主要通过调整TCP窗口大小来实现,确保发送方不会发送超出接收方处理能力的数据量。以下是TCP流量控制的关键概念和工作原理…...

linux离线安装nacos
1、打开 Nacos-GitHub ,点击 Release 可以看到 Nacos 的各版本跟新信息和安装包之类的 点击下载nacos-server-2.4.1.tar.gz,在linux创建nacos文件夹,把下载好的文件上传到nacos文件夹,并通过命令解压:tar -zxvf nacos-server-2.4.…...

云原生 | 在 Kubernetes 中使用 Cilium 替代 Calico 网络插件实践指南!
[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ] 0x00 简述介绍 什么是 Cilium? Cilium 是一款开源软件,它基于一种名为eBPF的新的Linux内核技术提供动力,用于透明地保护使用 Docker 和 Kubernetes 等Linux 容器管理平台中部署的应用程序服务之间的网络连接,Ciliu…...

【重学 MySQL】十一、SQL 概述
【重学 MySQL】十一、SQL 概述 SQL 背景知识产生与发展主要特点主要应用SQL语言的发展趋势 SQL 语言排行榜SQL 分类数据查询语言(DQL, Data Query Language)数据操纵语言(DML, Data Manipulation Language)数据定义语言࿰…...

(一)模式识别——基于SVM的道路分割实验(附资源)
写在前面:本报告所有代码公开在附带资源中,无法下载代码资源的伙伴私信留下邮箱,小编24小时内回复 一、实验目的 1、实验目标 学习掌握SVM(Support Vector Machine)算法思想,利用MATLAB的特定工具箱和库函…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...