文章目录[隐藏]
一. 实现功能
BILIBILILI链接(直接戳跳转)
上电后OLED0.96显示心率血氧参数,手靠近后显示对应值和曲线
注意:STC89C52RC移植后发现内存太小没法测试,要测试51的需要用更大内存单片机
二. 硬件清单
- OLED0.96模块
- MAX30102心率模块
- STM32F103C8T6/STC89C52RC
- SWD或JLINK仿真器(直接用CH340串口模块烧录也行,不过注意配置BOOT)
- 杜邦线若干
三. 资料清单
程序代码
文档资料
四. 模块简介
1.基本参数
2.引脚说明
五. 接线
基于STM32 +模块接线
模块--------------OLED-------------- STM32
VCC-----------------------------------5V
GND-----------------------------------GND
SCL------------------------------------PB5
SDA------------------------------------PB8
INT-------------------------------------PB9
---------------------VCC--------------3.3V
---------------------GND--------------GND
---------------------SCL-------------- GPIOB_6
---------------------SDA-------------- GPIOB_7
六.代码说明
以下以32代码为例:
1. 模块引脚配置
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//RCC->APB2ENR|=1<<4;//先使能外设IO PORTC时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
IIC_SCL=1;
IIC_SDA=1;
}
2. 模块IIC基础函数
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
3. 模块读写函数
void IIC_WriteBytes(u8 WriteAddr,u8* data,u8 dataLength)
{
u8 i;
IIC_Start();
IIC_Send_Byte(WriteAddr); //发送写命令
IIC_Wait_Ack();
for(i=0;i<dataLength;i++)
{
IIC_Send_Byte(data[i]);
IIC_Wait_Ack();
}
IIC_Stop();//产生一个停止条件
delay_ms(10);
}
void IIC_ReadBytes(u8 deviceAddr, u8 writeAddr,u8* data,u8 dataLength)
{
u8 i;
IIC_Start();
IIC_Send_Byte(deviceAddr); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(writeAddr);
IIC_Wait_Ack();
IIC_Send_Byte(deviceAddr|0X01);//进入接收模式
IIC_Wait_Ack();
for(i=0;i<dataLength-1;i++)
{
data[i] = IIC_Read_Byte(1);
}
data[dataLength-1] = IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
delay_ms(10);
}
void IIC_Read_One_Byte(u8 daddr,u8 addr,u8* data)
{
IIC_Start();
IIC_Send_Byte(daddr); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(addr);//发送地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(daddr|0X01);//进入接收模式
IIC_Wait_Ack();
*data = IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
}
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data)
{
IIC_Start();
IIC_Send_Byte(daddr); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(addr);//发送地址
IIC_Wait_Ack();
IIC_Send_Byte(data); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
delay_ms(10);
}
4. 初始化函数
void max30102_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
IIC_Init();
max30102_reset();
max30102_Bus_Write(REG_INTR_ENABLE_1,0xc0); // 设置中断使能寄存器1 0x02
max30102_Bus_Write(REG_INTR_ENABLE_2,0x00); // 设置中断使能寄存器2 0x03
max30102_Bus_Write(REG_FIFO_WR_PTR,0x00); //FIFO_WR_PTR[4:0] FIFO写指针指向下一个要写的内存地址 0x04
max30102_Bus_Write(REG_OVF_COUNTER,0x00); //OVF_COUNTER[4:0] FIFO溢出丢失数据计数 0x05
max30102_Bus_Write(REG_FIFO_RD_PTR,0x00); //FIFO_RD_PTR[4:0] FIFO读指针指向下一个要读的内存地址 0x06
max30102_Bus_Write(REG_FIFO_CONFIG,0x0f); //sample avg = 1, fifo rollover=false, fifo almost full = 17 0x07
max30102_Bus_Write(REG_MODE_CONFIG,0x03); //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED 0x09
max30102_Bus_Write(REG_SPO2_CONFIG,0x27); // SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS) 0x0A
max30102_Bus_Write(REG_LED1_PA,0x24); //Choose value for ~ 7mA for LED1 0x0C
max30102_Bus_Write(REG_LED2_PA,0x24); // Choose value for ~ 7mA for LED2 0x0D
max30102_Bus_Write(REG_PILOT_PA,0x7f); // Choose value for ~ 25mA for Pilot LED 0x10
}
5. 主函数
int main(void)
{
//variables to calculate the on-board LED brightness that reflects the heartbeats
uint32_t un_min, un_max, un_prev_data;
int i;
int32_t n_brightness;
float f_temp;
u8 temp_num=0;
u8 temp[6];
u8 str[100];
u8 dis_hr=0,dis_spo2=0;
I2C_Configuration();
NVIC_Configuration();
delay_init(); //延时函数初始化
uart_init(115200); //串口初始化为115200
//OLED
OLED_Init();
OLED_ShowString(0,0," initializing ",16);
OLED_Refresh_Gram();//更新显示到OLED
max30102_init();
printf("\r\n MAX30102 init \r\n");
un_min=0x3FFFF;
un_max=0;
n_ir_buffer_length=500; //buffer length of 100 stores 5 seconds of samples running at 100sps
//read the first 500 samples, and determine the signal range
for(i=0;i<n_ir_buffer_length;i++)
{
while(MAX30102_INT==1); //wait until the interrupt pin asserts
max30102_FIFO_ReadBytes(REG_FIFO_DATA,temp);
aun_red_buffer[i] = (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2]; // Combine values to get the actual number
aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03)<<16) |(long)temp[4]<<8 | (long)temp[5]; // Combine values to get the actual number
if(un_min>aun_red_buffer[i])
{
un_min=aun_red_buffer[i]; //update signal min
}
if(un_max<aun_red_buffer[i])
{
un_max=aun_red_buffer[i]; //update signal max
}
}
un_prev_data=aun_red_buffer[i];
//calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
while(1)
{
i=0;
un_min=0x3FFFF;
un_max=0;
//dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
for(i=100;i<500;i++)
{
aun_red_buffer[i-100]=aun_red_buffer[i];
aun_ir_buffer[i-100]=aun_ir_buffer[i];
//update the signal min and max
if(un_min>aun_red_buffer[i])
un_min=aun_red_buffer[i];
if(un_max<aun_red_buffer[i])
un_max=aun_red_buffer[i];
}
//take 100 sets of samples before calculating the heart rate.
for(i=400;i<500;i++)
{
un_prev_data=aun_red_buffer[i-1];
while(MAX30102_INT==1);
max30102_FIFO_ReadBytes(REG_FIFO_DATA,temp);
aun_red_buffer[i] = (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2]; // Combine values to get the actual number
aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03)<<16) |(long)temp[4]<<8 | (long)temp[5]; // Combine values to get the actual number
if(aun_red_buffer[i]>un_prev_data)
{
f_temp=aun_red_buffer[i]-un_prev_data;
f_temp/=(un_max-un_min);
f_temp*=MAX_BRIGHTNESS;
n_brightness-=(int)f_temp;
if(n_brightness<0)
n_brightness=0;
}
else
{
f_temp=un_prev_data-aun_red_buffer[i];
f_temp/=(un_max-un_min);
f_temp*=MAX_BRIGHTNESS;
n_brightness+=(int)f_temp;
if(n_brightness>MAX_BRIGHTNESS)
n_brightness=MAX_BRIGHTNESS;
}
//send samples and calculation result to terminal program through UART
if(ch_hr_valid == 1 && n_heart_rate<120)//**/ ch_hr_valid == 1 && ch_spo2_valid ==1 && n_heart_rate<120 && n_sp02<101
{
dis_hr = n_heart_rate;
dis_spo2 = n_sp02;
}
else
{
dis_hr = 0;
dis_spo2 = 0;
}
printf("HR=%i, ", n_heart_rate);
printf("HRvalid=%i, ", ch_hr_valid);
printf("SpO2=%i, ", n_sp02);
printf("SPO2Valid=%i\r\n", ch_spo2_valid);
}
maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
//显示刷新
if(dis_hr == 0 && dis_spo2 == 0) //**dis_hr == 0 && dis_spo2 == 0
{
sprintf((char *)str,"HR:--- SpO2:--- ");//**HR:--- SpO2:---
}
else{
sprintf((char *)str,"HR:%3d SpO2:%3d ",dis_hr,dis_spo2);//**HR:%3d SpO2:%3d
}
OLED_ShowString(0,0,str,16);
OLED_Fill(0,23,127,63,0);
//红光在上,红外在下
dis_DrawCurve(aun_red_buffer,20);
dis_DrawCurve(aun_ir_buffer,0);
OLED_Refresh_Gram();//更新显示到OLED
}
}
七.代码资料免费获取三步走
第一步:
搬砖不易,跪求三连(点赞,收藏或者说说心里话(不足之处也欢迎指点哦),关注)。
第二步:
三连后截图,上门找群主,企鹅群号:1041406448
第三步:
截图私发群主,”逼问“资料下落,不给就锤他
版权声明:本文为CSDN博主「单片机代码搬运工」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lfmnlxx79691557/article/details/121388683
暂无评论