STM32F4系列开发家庭医疗系统(esp8266+MQTT上阿里云)

开发要求

1、通过温湿度、烟雾、光敏电阻传感器采集家庭环境中的温湿度、烟雾浓度、光照强度,数据超        过阈值产生报警提示;
2、通过心率模块检测人体心率,通过温度传感器检测人体体温,数据超过阈值时产生报警提醒;
3、通过OLED显示屏显示人体每半小时的平均心率和平均体温;
4、单片机将数据通过MQTT协议和阿里云物联网平台交互,手机端APP查看数据,实现远程监控、        数据内容显示;

硬件选材

 芯片:  stm32 F407(我用的是集成开发板内部集成stm32 F407芯片)

 温湿度模块:  DHT11(老朋友了  数字信号嘎嘎友好)

                    两块 测体温和测家庭环境的 (测体温可以用别的体温模块我图方便就暂时先用这个)

烟雾传感器: MQ-2 

光照强度:GL55光敏电阻 (开发板内置)

心率模块:max30102

显示:OLED(老朋友了)

wifi模块:esp8266(正点原子)别的也可以

下面正式开始  

第一步 新建工程 

        好的目录结构更容易 为后续开发工作提供便利

1.headwear:存放开发的头文件

2.project:工程文件

3.system:存放滴答计时器的用于延时

4.user:用户目录 存放主函数

  工程创建很基础这里就先不演示了详见下面链接用keil5新建STM32工程(超详细的图解操作)_keil5新建stm32工程步骤-CSDN博客

第二步 规划资源分配

 就是怎么合理运用单片机内部的资源,好的开始能让项目更顺利。

1.资源分配

        我们需要一个定时中断在获取模块的数值    我们选用基本定时器 f407有两个基本定时器

  TIM6 和 TIM7  我们选用一个即可  。

       上文提到半小时平均值  我们在定时器中断加一个累加即可 简单方便。一分钟采样一次

第三步 开发OLED模块

       先开发OLED模块易于后期调试

   1.引脚部分

GNDGND
VDD3.3V
SCKPB8
SDAPB9

iic协议不会的看这里【通信协议】一文搞懂I2C(IIC)_iic?!,孞↖。↖∴:i…-CSDN博客

2.代码部分

源码如下

i2c.h

#ifndef __I2C_H__
#define __I2C_H__


#include "stm32f4xx.h"
#include "systick.h"


#define SCL_CLK RCC_AHB1Periph_GPIOB
#define SCL_PIN GPIO_Pin_8
#define SDA_PORT GPIOB

#define SDA_CLK RCC_AHB1Periph_GPIOB
#define SDA_PIN GPIO_Pin_9
#define SCL_PORT GPIOB

#define IIC_DELAY_US(nus) Delay_us(nus) 
#define IIC_DELAY_MS(nms) Delay_ms(nms)

#define IIC_SCL_SET(n) (n) ? (GPIO_SetBits(SCL_PORT, SCL_PIN)) : (GPIO_ResetBits(SCL_PORT, SCL_PIN))


#define IIC_SDA_SET(n) (n) ? (GPIO_SetBits(SDA_PORT, SDA_PIN)) : (GPIO_ResetBits(SDA_PORT, SDA_PIN))

#define IIC_SDA_READ GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN)

void I2C_SIM_Config(void);
void IIC_SetSDAOutputMode(void);
void IIC_SetSDAInputMode(void);
void IIC_Start(void);
void IIC_Stop(void);
void IIC_SendByte(uint8_t data);
uint8_t IIC_WaitSlaveAck(void);
void IIC_MasterAck(uint8_t ack);
uint8_t IIC_ReadByte(void);

#endif

i2c.c

#include "i2c.h"

/*
   	模拟IIC接口初始化函数
 */
void I2C_SIM_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

	GPIO_InitStructure.GPIO_Pin = SCL_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;	  
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	  
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;   
	GPIO_Init(SCL_PORT, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = SDA_PIN;
	GPIO_Init(SDA_PORT, &GPIO_InitStructure);

	IIC_SDA_SET(1);
	IIC_SCL_SET(1);
}

/*
		配置SDA引脚为输入模式函数
 */
void IIC_SetSDAInputMode(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(SDA_CLK, ENABLE);

	GPIO_InitStructure.GPIO_Pin = SDA_PIN;		 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
		配置SDA引脚为输出模式函数
 */
void IIC_SetSDAOutputMode(void)
{

	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(SDA_CLK, ENABLE);

	GPIO_InitStructure.GPIO_Pin = SDA_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;	   
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	   
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
		IIC协议 起始信号函数
 */
void IIC_Start(void)
{
	
	IIC_SetSDAOutputMode();

	
	IIC_SDA_SET(1);
	IIC_SCL_SET(1);
	delay_us(5);


	IIC_SDA_SET(0);
	delay_us(5);

	IIC_SCL_SET(0);
}

/*
 IIC协议 停止信号函数
 */
void IIC_Stop(void)
{

	IIC_SetSDAOutputMode();
	IIC_SDA_SET(0);
	IIC_SCL_SET(0);
	delay_us(5);

	IIC_SCL_SET(1);
	delay_us(5);

	
	IIC_SDA_SET(1);
	delay_us(5);
}

/*
		IIC协议 发送数据或者地址函数
 */
void IIC_SendByte(uint8_t data)
{
	uint8_t i = 0;

	
	IIC_SetSDAOutputMode();

	IIC_SDA_SET(0);
	IIC_SCL_SET(0);


	for (i = 0; i < 8; i++)
	{

		if (data & 0x80)
			IIC_SDA_SET(1);
		else
			IIC_SDA_SET(0); 
		
		delay_us(5);
		data <<= 1;
		
		IIC_SCL_SET(1);
		delay_us(5);

		IIC_SCL_SET(0);
		delay_us(5);
	}
}

/*
		IC协议 主机等待从机应答函数
		返回值为:0 表示应答,1 表示无应答
 */
uint8_t IIC_WaitSlaveAck(void)
{
	uint8_t ack = 0;

	
	IIC_SetSDAInputMode();

	IIC_SCL_SET(0);
	delay_us(5);

	IIC_SCL_SET(1);
	delay_us(5);

	if (IIC_SDA_READ == 0)
		ack = 0; 
	else
		ack = 1; 

	IIC_SCL_SET(0);
	delay_us(5);

	return ack;
}

/*
 	IIC协议 主机应答函数
 	ack:0表示应答,1表示不应答。

 */
void IIC_MasterAck(uint8_t ack)
{
	
	IIC_SetSDAOutputMode();

	IIC_SDA_SET(0);
	IIC_SCL_SET(0);

	if (ack == 0)
		IIC_SDA_SET(0); 
	else
		IIC_SDA_SET(1); 

	delay_us(5);

	IIC_SCL_SET(1);
	delay_us(5);

	IIC_SCL_SET(0);
	delay_us(5);
}

/*
   	IIC协议 主机读取数据函数

 */
uint8_t IIC_ReadByte(void)
{
	uint8_t i = 0;
	uint8_t data = 0;


	IIC_SetSDAInputMode();

	IIC_SCL_SET(0);
	delay_us(5);

	for (i = 0; i < 8; i++)
	{
		IIC_SCL_SET(1);
		delay_us(5);


		data <<= 1;

	
		if (IIC_SDA_READ == 1)
		{
			data |= 0x01;
		}

		IIC_SCL_SET(0);
		delay_us(5);
	}

	return data;
}

接下来就是OLED的驱动代码  是建立在上面I2C代码基础之上的

OLED.h

#ifndef __OLED_H
#define __OLED_H
#include "stm32f4xx.h"                  // Device header



void OLED_Init(void);
void OLED_WriteCommand(uint8_t command);
void OLED_WriteData(uint8_t Data);
void OLED_SetCursor(uint8_t Page, uint8_t X);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

#endif

OLED.c

#include "OLED.h"
#include "i2c.h" 
#include "systick.h"
#include "OLED_Font.h"


void OLED_Clear(void);

/*oled写命令*/
void OLED_WriteCommand(uint8_t command)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_WaitSlaveAck();
	IIC_SendByte(0x00);
	IIC_WaitSlaveAck();
	IIC_SendByte(command);
	IIC_WaitSlaveAck();
	IIC_Stop();
}

/*oled初始化*/
void OLED_Init(void)
{
	I2C_SIM_Config();
	delay_ms(100);
	OLED_WriteCommand(0xAE);	//设置显示开启/关闭,0xAE关闭,0xAF开启
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);	//0x00~0xFF
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);	//0x0E~0x3F
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);	//0x00~0x7F
	
	OLED_WriteCommand(0x40);	//设置显示开始行,0x40~0x7F
	
	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常,0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常,0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//设置对比度
	OLED_WriteCommand(0xCF);	//0x00~0xFF

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/反色显示,0xA6正常,0xA7反色

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示

  OLED_Clear();
	
	delay_ms(100);

}



/*oled写数据*/
void OLED_WriteData(uint8_t Data)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_WaitSlaveAck();
	IIC_SendByte(0x40);
	IIC_WaitSlaveAck();
	IIC_SendByte(Data);
	IIC_WaitSlaveAck();
	IIC_Stop();
}


/*oled设置光标*/
void OLED_SetCursor(uint8_t Page, uint8_t X)
{
	OLED_WriteCommand(0x00 | (X & 0x0F));			
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	
	OLED_WriteCommand(0xB0 | Page);					
}

void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}


/*OLED显示一个字符*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		
	}
}




/* OLED显示字符串*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/*次方函数*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;	//结果默认为1
	while (Y --)			//累乘Y次
	{
		Result *= X;		//每次把X累乘到结果上
	}
	return Result;
}


/* OLED显示十进制数字*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

第四步 温度传感器的配置

前面OLED部分配置好了可以直接配置dht11并调试, 会方便很多。

dht11有单总线驱动 时序图在这里  详见DHT11详细介绍(内含51和STM32代码)-CSDN博客

1.引脚部分

                                                          室内的温湿度

vcc3.3v
DATAPG9
GNDGND

                                                                   体温(先凑合一下)

vcc3.3v
DATAPB6
GNDGND

2.代码部分

dht11.h

#ifndef __DHT11_H__
#define __DHT11_H__

#include "stm32f4xx.h"
#include "systick.h"


#define DHT11_PIN GPIO_Pin_9    //室内温湿度
#define DHT11_PIN_1 GPIO_Pin_6  //体温

void DHT11_Read_Data(void);  //获取室内温湿度

void DHT11_2_Read_Data(void); //获取体温

#endif

dht11.c

#include "dht11.h"


/* PG9配置为推挽输出模式 */
void DHT11_Output_Mode(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);  
	
	GPIO_InitStructure.GPIO_Pin = DHT11_PIN;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; 
	
	GPIO_Init(GPIOG, &GPIO_InitStructure);  
}

/* 引脚输出高低电平 */
void DHT11_GPIO_OUT(uint8_t x)
{
	if( x == 1 ) 
		GPIO_SetBits(GPIOG, DHT11_PIN);
	else 
		GPIO_ResetBits(GPIOG, DHT11_PIN);
}

/* PG9配置成浮空输入模式 */
void DHT11_Input_Moda(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	GPIO_InitStructure.GPIO_Pin = DHT11_PIN;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
	GPIO_Init(GPIOG, &GPIO_InitStructure);
	
}

/* 读取外部引脚的高低电平 */
uint8_t DHT11_GPIO_IN(void)
{
	return GPIO_ReadInputDataBit(GPIOG, DHT11_PIN);
}

/* STM32输出起始信号 */
void DHT11_Start(void)
{
	
	DHT11_Output_Mode();
	
	DHT11_GPIO_OUT(0);
	delay_ms(20);
	DHT11_GPIO_OUT(1);
	delay_us(30);
	
}

/* DHT11的应答信号 */
/* 成功返回0,失败返回1 */
uint8_t DHT11_ACK(void)
{
	uint16_t count;
	DHT11_Input_Moda();
	if(DHT11_GPIO_IN() == Bit_RESET)
	{
		count = 0;
		while(DHT11_GPIO_IN() == Bit_RESET)
		{
			count++;
			if(count > 10)
			{
				return 1;
			}
			delay_us(10);
		}
		count = 0;
		while(DHT11_GPIO_IN() == Bit_SET)
		{
			count ++;
			if(count > 9)
			{
				return 1;
			}
			delay_us(10);
		}
		return 0;
	}
	return 1;
}


/* 读取传输每比特的数据 */
/* 返回对应的数据 */
uint8_t Read_Byte(void)
{
	
	uint8_t i, temp =0;

	for (i = 0; i < 8; i++)
	{
		while (DHT11_GPIO_IN() == Bit_RESET)
			;

		delay_us(35);

		if (DHT11_GPIO_IN() == Bit_SET)
		{
			while (DHT11_GPIO_IN() == Bit_SET)
				;

			temp |= (uint8_t)(0x01 << (7 - i));
		}
		else
		{
			temp &= (uint8_t) ~(0x01 << (7 - i));
		}
	}
	return temp;
}

uint8_t humi_int = 0;
uint8_t humi_deci = 0;
uint8_t temp_int = 0;
uint8_t temp_deci = 0;
uint8_t check_sum = 0;

void DHT11_Read_Data(void)
{
	DHT11_Start();
	if(DHT11_ACK() == 0)
	{
		humi_int = Read_Byte();	 // 8bit湿度整数数据
		humi_deci = Read_Byte(); // 8bit湿度小数数据
		temp_int = Read_Byte();	 // 8bit温度整数数据
		temp_deci = Read_Byte(); // 8bit温度小数数据
		check_sum = Read_Byte(); // 8bit校验和
		
		DHT11_Output_Mode();
		DHT11_GPIO_OUT(1);
		

	delay_ms(200);
}

}


/*  下面是体温的函数 */


/* PC7,配置为推挽输出模式 */
void DHT11_2_Output_Mode(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  
	
	GPIO_InitStructure.GPIO_Pin = DHT11_PIN_1;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; 
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);  
}

/* 引脚输出高低电平 */
void DHT11_2_GPIO_OUT(uint8_t x)
{
	if( x == 1 ) 
		GPIO_SetBits(GPIOB, DHT11_PIN_1);
	else 
		GPIO_ResetBits(GPIOB, DHT11_PIN_1);
}

/* 配置成浮空输入模式 */
void DHT11_2_Input_Moda(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	GPIO_InitStructure.GPIO_Pin = DHT11_PIN_1;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
}

/* 读取外部引脚的高低电平 */
uint8_t DHT11_2_GPIO_IN(void)
{
	return GPIO_ReadInputDataBit(GPIOB, DHT11_PIN_1);
}

/* STM32输出起始信号 */
void DHT11_2_Start(void)
{
	
	DHT11_2_Output_Mode();
	
	DHT11_2_GPIO_OUT(0);
	delay_ms(20);
	DHT11_2_GPIO_OUT(1);
	delay_us(30);
	
}

/* DHT11的应答信号 */
/* 成功返回0,失败返回1 */
uint8_t DHT11_2_ACK(void)
{
	uint16_t count;
	DHT11_2_Input_Moda();
	if(DHT11_2_GPIO_IN() == Bit_RESET)
	{
		count = 0;
		while(DHT11_2_GPIO_IN() == Bit_RESET)
		{
			count++;
			if(count > 10)
			{
				return 1;
			}
			delay_us(10);
		}
		count = 0;
		while(DHT11_2_GPIO_IN() == Bit_SET)
		{
			count ++;
			if(count > 9)
			{
				return 1;
			}
			delay_us(10);
		}
		return 0;
	}
	return 1;
}


/* 读取传输每比特的数据 */
/* 返回对应的数据 */
uint8_t Read_2_Byte(void)
{
	
	uint8_t i, temp =0;

	for (i = 0; i < 8; i++)
	{
		while (DHT11_2_GPIO_IN() == Bit_RESET)
			;

		delay_us(35);

		if (DHT11_2_GPIO_IN() == Bit_SET)
		{
			while (DHT11_2_GPIO_IN() == Bit_SET)
				;

			temp |= (uint8_t)(0x01 << (7 - i));
		}
		else
		{
			temp &= (uint8_t) ~(0x01 << (7 - i));
		}
	}
	return temp;
}

uint8_t humi_2_int = 0;
uint8_t humi_2_deci = 0;
uint8_t temp_2_int = 0;
uint8_t temp_2_deci = 0;
uint8_t check_2_sum = 0;

void DHT11_2_Read_Data(void)
{
	DHT11_2_Start();
	if(DHT11_2_ACK() == 0)
	{
		humi_2_int = Read_2_Byte();	 // 8bit湿度整数数据
		humi_2_deci = Read_2_Byte(); // 8bit湿度小数数据
		temp_2_int = Read_2_Byte();	 // 8bit温度整数数据
		temp_2_deci = Read_2_Byte(); // 8bit温度小数数据
		check_2_sum = Read_2_Byte(); // 8bit校验和
		
		DHT11_2_Output_Mode();
		DHT11_2_GPIO_OUT(1);
		

	delay_ms(200);
}

}





调试代码 main.c

#include "stm32f4xx.h"                  // Device header
#include "OLED.h"   
#include "systick.h"
#include "dht11.h"


extern uint8_t temp_2_int;
extern uint8_t temp_2_deci;



void init(void)
{
	OLED_Init();
}

void show_oled(void)
{
   
  DHT11_2_Read_Data(); //获取体温
	OLED_ShowString(1, 1, "average:");
  OLED_ShowString(2, 1, "temp:");
	OLED_ShowNum(2 , 8 ,temp_2_int , 2 );
	OLED_ShowString(2, 10, ".");
	OLED_ShowNum(2 , 11 ,temp_2_deci , 2 );
	OLED_ShowString(2, 14, "C");
	OLED_ShowString(3, 1, "HR:");
	    
}


int main(void)
{
	
	 init();
   
		while(1)
		{
			
			show_oled();
		}
		
}

效果如下

  第五步 烟雾模块

1.引脚部分

VCC5V
AOPA4
DO
GNDGND

这个模块涉及到ADC转换  需要有adc功能的引脚

2.代码部分

MQ2.h

#ifndef _MQ2_H
#define _MQ2_H

#include "stm32f4xx.h"                  // Device header
#include "systick.h"
#include "math.h"

#define SMOG_READ_TIMES	10				//定义烟雾传感器读取次数,读这么多次,然后取平均值


void MQ2_Init(void);
float Smog_Get_Vol(void);	//读取MQ7传感器的电压值
float Smog_GetPPM(void);

#endif

MQ2.c

#include "MQ_adc.h"

#define CAL_PPM  10  // 校准环境中PPM值
#define RL	     10  // RL阻值
#define R0	     26  // R0阻值


void MQ2_Init(void)
{
	GPIO_InitTypeDef      GPIO_InitStructure;
	ADC_CommonInitTypeDef ADC_CommonInitStructure;
	ADC_InitTypeDef       ADC_InitStructure;
	
	/* 引脚和ADC的时钟使能 */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

	/* 配置引脚为模拟功能模式 */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;		//模拟功能模式
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* ADC的常规配置 */
	ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;		//独立模式
	ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;		//84MHz/2 = 42MHz
	ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;	//禁止MDA
	ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;	//ADC通道采用间隔
	ADC_CommonInit(&ADC_CommonInitStructure);

	
	ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;		//分辨率
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;				//禁止扫描			
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;			//连续转换模式
	ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//不需要外部触发
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;		//数据右对齐
	ADC_InitStructure.ADC_NbrOfConversion = 1;					//一次转换
	ADC_Init(ADC1, &ADC_InitStructure);
	
	
	ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_3Cycles);

	/* 使能 ADC1 */
	ADC_Cmd(ADC1, ENABLE);
}

uint32_t MQ2_ADC_Read(void)
{ 
	ADC_SoftwareStartConv(ADC1);
	
	while( ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
	
	return ADC_GetConversionValue(ADC1);
}


u16 ADC1_Average_Data(u8 ADC_Channel)
{
	u16 temp_val=0;
	u8 t;
	for(t=0;t<SMOG_READ_TIMES;t++)	

	{
		temp_val+=MQ2_ADC_Read();	
		delay_ms(5);
	}
	temp_val/=SMOG_READ_TIMES;
    return (u16)temp_val;
}

//读取MQ7传感器的电压值
float Smog_Get_Vol(void)
{
	u16 adc_value = 0;
	float voltage = 0;
	
	adc_value = ADC1_Average_Data(ADC_Channel_4);
	delay_ms(5);
	
    voltage  = (3.3/4096.0)*(adc_value);
	
	return voltage;
}


// 计算Smog_ppm
float Smog_GetPPM()
{
	float RS = (3.3f - Smog_Get_Vol()) / Smog_Get_Vol() * RL;
	float ppm = 98.322f * pow(RS/R0, -1.458f);
	return  ppm;
}

测试用例

#include "stm32f4xx.h"                  // Device header
#include "OLED.h"   
#include "systick.h"
#include "dht11.h"
#include "light_adc.h"
#include "ADC.h"
#include "MQ_adc.h"


extern uint8_t temp_2_int;
extern uint8_t temp_2_deci;

float GL5516_data = 0; 
float PhotoResistor;  


float Smog_ppm = 0; 

void init(void)
{
	OLED_Init(); 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	ADC3_Config();
	MQ2_Init();
}

void show_oled(void)
{
   
  DHT11_2_Read_Data(); //获取体温
	OLED_ShowString(1, 1, "average:");
  OLED_ShowString(2, 1, "temp:");
	OLED_ShowNum(2 , 8 ,temp_2_int , 2 );
	OLED_ShowString(2, 10, ".");
	OLED_ShowNum(2 , 11 ,temp_2_deci , 2 );
	OLED_ShowString(2, 14, "C");
	OLED_ShowString(3, 1, "HR:");
	    
}

void light()
{
	   GL5516_data = Get_Conversion_value(ADC3, ADC_Channel_5, 20);

		PhotoResistor = (10000*GL5516_data)/(5-GL5516_data);
		LUX = GetLux(PhotoResistor);
}


int main(void)
{
	
	 init();
  
		while(1)
		{
		Smog_ppm = Smog_GetPPM();
		
		OLED_ShowNum(1,1,Smog_ppm,3);
		}
		
}

第六步 光感模块

1.引脚部分

VCC3V
AOPF7
GNDGND

该模块涉及ADC  模拟信号输入输入即可  另外需要一些参考量  如下代码

电阻与光强关系

/* GL5516光敏电阻的阻值与流明对应表 */
const PhotoRes_TypeDef GL5516[281] =
	{
		{40000, 1},
		{26350, 2},
		{20640, 3},
		{17360, 4},
		{15170, 5},
		{13590, 6},
		{12390, 7},
		{11430, 8},
		{10650, 9},
		{9990, 10},
		{9440, 11},
		{8950, 12},
		{8530, 13},
		{8160, 14},
		{7830, 15},
		{7530, 16},
		{7260, 17},
		{7010, 18},
		{6790, 19},
		{6580, 20},
		{6390, 21},
		{6210, 22},
		{6050, 23},
		{5900, 24},
		{5750, 25},
		{5620, 26},
		{5490, 27},
		{5370, 28},
		{5260, 29},
		{5160, 30},
		{5050, 31},
		{4960, 32},
		{4870, 33},
		{4780, 34},
		{4700, 35},
		{4620, 36},
		{4540, 37},
		{4470, 38},
		{4400, 39},
		{4330, 40},
		{4270, 41},
		{4210, 42},
		{4150, 43},
		{4090, 44},
		{4040, 45},
		{3980, 46},
		{3930, 47},
		{3880, 48},
		{3840, 49},
		{3790, 50},
		{3740, 51},
		{3700, 52},
		{3660, 53},
		{3620, 54},
		{3580, 55},
		{3540, 56},
		{3500, 57},
		{3460, 58},
		{3430, 59},
		{3390, 60},
		{3360, 61},
		{3330, 62},
		{3300, 63},
		{3270, 64},
		{3230, 65},
		{3210, 66},
		{3180, 67},
		{3150, 68},
		{3120, 69},
		{3090, 70},
		{3070, 71},
		{3040, 72},
		{3020, 73},
		{2990, 74},
		{2970, 75},
		{2940, 76},
		{2920, 77},
		{2900, 78},
		{2880, 79},
		{2850, 80},
		{2830, 81},
		{2810, 82},
		{2790, 83},
		{2770, 84},
		{2750, 85},
		{2730, 86},
		{2710, 87},
		{2690, 88},
		{2680, 89},
		{2660, 90},
		{2640, 91},
		{2620, 92},
		{2610, 93},
		{2590, 94},
		{2570, 95},
		{2560, 96},
		{2540, 97},
		{2530, 98},
		{2510, 99},
		{2490, 100},
		{2480, 101},
		{2460, 102},
		{2450, 103},
		{2440, 104},
		{2420, 105},
		{2410, 106},
		{2390, 107},
		{2380, 108},
		{2370, 109},
		{2360, 110},
		{2340, 111},
		{2330, 112},
		{2320, 113},
		{2300, 114},
		{2290, 115},
		{2280, 116},
		{2270, 117},
		{2260, 118},
		{2250, 119},
		{2230, 120},
		{2220, 121},
		{2210, 122},
		{2200, 123},
		{2190, 124},
		{2180, 125},
		{2170, 126},
		{2160, 127},
		{2150, 128},
		{2140, 129},
		{2130, 130},
		{2120, 131},
		{2110, 132},
		{2100, 133},
		{2090, 134},
		{2080, 135},
		{2070, 136},
		{2060, 137},
		{2050, 138},
		{2040, 139},
		{2030, 141},
		{2020, 142},
		{2010, 143},
		{2000, 144},
		{1990, 145},
		{1980, 147},
		{1970, 148},
		{1960, 149},
		{1950, 150},
		{1940, 152},
		{1930, 153},
		{1920, 154},
		{1910, 155},
		{1900, 157},
		{1890, 158},
		{1880, 160},
		{1870, 161},
		{1860, 162},
		{1850, 164},
		{1840, 165},
		{1830, 167},
		{1820, 168},
		{1810, 170},
		{1800, 171},
		{1790, 173},
		{1780, 175},
		{1770, 176},
		{1760, 178},
		{1750, 180},
		{1740, 181},
		{1730, 183},
		{1720, 185},
		{1710, 187},
		{1700, 188},
		{1690, 190},
		{1680, 192},
		{1670, 194},
		{1660, 196},
		{1650, 198},
		{1640, 200},
		{1630, 202},
		{1620, 204},
		{1610, 206},
		{1600, 208},
		{1590, 210},
		{1580, 212},
		{1570, 215},
		{1560, 217},
		{1550, 219},
		{1540, 222},
		{1530, 224},
		{1520, 226},
		{1510, 229},
		{1500, 231},
		{1490, 234},
		{1480, 237},
		{1470, 239},
		{1460, 242},
		{1450, 245},
		{1440, 248},
		{1430, 250},
		{1420, 253},
		{1410, 256},
		{1400, 259},
		{1390, 262},
		{1380, 266},
		{1370, 269},
		{1360, 272},
		{1350, 275},
		{1340, 279},
		{1330, 282},
		{1320, 286},
		{1310, 289},
		{1300, 293},
		{1290, 297},
		{1280, 300},
		{1270, 304},
		{1260, 308},
		{1250, 312},
		{1240, 317},
		{1230, 321},
		{1220, 325},
		{1210, 330},
		{1200, 334},
		{1190, 339},
		{1180, 344},
		{1170, 348},
		{1160, 353},
		{1150, 358},
		{1140, 364},
		{1130, 369},
		{1120, 374},
		{1110, 380},
		{1100, 386},
		{1090, 391},
		{1080, 397},
		{1070, 403},
		{1060, 410},
		{1050, 416},
		{1040, 423},
		{1030, 430},
		{1020, 436},
		{1010, 444},
		{1000, 451},
		{990, 458},
		{980, 466},
		{970, 474},
		{960, 482},
		{950, 491},
		{940, 499},
		{930, 508},
		{920, 517},
		{910, 526},
		{900, 536},
		{890, 546},
		{880, 556},
		{870, 567},
		{860, 578},
		{850, 589},
		{840, 600},
		{830, 612},
		{820, 625},
		{810, 637},
		{800, 650},
		{790, 664},
		{780, 678},
		{770, 692},
		{760, 707},
		{750, 723},
		{740, 739},
		{730, 756},
		{720, 773},
		{710, 791},
		{700, 809},
		{690, 829},
		{680, 849},
		{670, 869},
		{660, 891},
		{650, 914},
		{640, 937},
		{630, 961},
		{620, 987},
};

2.代码部分

ADC.h

#ifndef __ADC_H__
#define __ADC_H__

/* 包含官方头文件 */
#include "stm32f4xx.h"

/* 包含其它相关头文件 */
#include "light_adc.h"
#include "systick.h"



/* 外部接口函数声明 */
void ADC3_Config(void);
uint16_t Get_Adc(ADC_TypeDef *ADCx, uint8_t ADC_Channel);
uint16_t Get_Adc_Average(ADC_TypeDef *ADCx, uint8_t ADC_Channel, uint8_t times);
float Get_Conversion_value(ADC_TypeDef *ADCx, uint8_t ADC_Channel, uint8_t times);

#endif

adc.c

#include "adc.h"


void ADC3_Config(void)
{
	
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_CommonInitTypeDef ADC_CommonInitStructure;
	ADC_InitTypeDef ADC_InitStructure;


	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);		


	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;		
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;	 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
	GPIO_Init(GPIOF, &GPIO_InitStructure); 

	ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;						
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;								
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;							
	
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;						
	ADC_InitStructure.ADC_NbrOfConversion = 1;									
	ADC_Init(ADC3, &ADC_InitStructure);										


	ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;					 
	ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; 
	ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;		 
	ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;					 
	ADC_CommonInit(&ADC_CommonInitStructure);									

	
	ADC_Cmd(ADC3, ENABLE);
}


uint16_t Get_Adc(ADC_TypeDef *ADCx, uint8_t ADC_Channel)
{
	
	ADC_RegularChannelConfig(ADCx, ADC_Channel, 1, ADC_SampleTime_480Cycles);

	ADC_DiscModeChannelCountConfig(ADC3, 1);
	
	ADC_DiscModeCmd(ADC3,ENABLE);
	ADC_SoftwareStartConv(ADCx);
	while (!ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC))
		;

	return ADC_GetConversionValue(ADCx);
}


uint16_t Get_Adc_Average(ADC_TypeDef *ADCx, uint8_t ADC_Channel, uint8_t times)
{
	uint8_t i;
	uint32_t temp_val = 0;

	for (i = 0; i < times; i++)
	{
		temp_val += Get_Adc(ADCx, ADC_Channel);
		delay_ms(5);
	}

	return temp_val / times;
}


float Get_Conversion_value(ADC_TypeDef *ADCx, uint8_t ADC_Channel, uint8_t times)
{
	uint16_t adc_value;
	float adc_vol;

	adc_value = Get_Adc_Average(ADCx, ADC_Channel, times);

	adc_vol = (float)adc_value * (3.3 / 4096); 

	return adc_vol;
}

这样我们便获取到了模拟电压值  通过模块给的公式或自己电路的特性计算光强即可

    GL5516_data = Get_Conversion_value(ADC3, ADC_Channel_5, 20);  //获取电压值

    PhotoResistor = (10000*GL5516_data)/(5-GL5516_data);   //获取电阻值

接下来查表即可

light_adc.h

#ifndef __LIGHT_ADC_H__
#define __LIGHT_ADC_H__

/* 包含官方头文件 */
#include "stm32f4xx.h"


typedef struct
{
	unsigned short ohm;

	unsigned short lux; 
} PhotoRes_TypeDef;

extern uint16_t LUX; 

uint16_t GetLux(float PhotoResistor);

#endif

light_adc.c

#include "light_adc.h"
/* 光照度 */
uint16_t LUX;

/* GL5516光敏电阻的阻值与流明对应表 */
const PhotoRes_TypeDef GL5516[281] =
	{
		{40000, 1},
		{26350, 2},
		{20640, 3},
		{17360, 4},
		{15170, 5},
		{13590, 6},
		{12390, 7},
		{11430, 8},
		{10650, 9},
		{9990, 10},
		{9440, 11},
		{8950, 12},
		{8530, 13},
		{8160, 14},
		{7830, 15},
		{7530, 16},
		{7260, 17},
		{7010, 18},
		{6790, 19},
		{6580, 20},
		{6390, 21},
		{6210, 22},
		{6050, 23},
		{5900, 24},
		{5750, 25},
		{5620, 26},
		{5490, 27},
		{5370, 28},
		{5260, 29},
		{5160, 30},
		{5050, 31},
		{4960, 32},
		{4870, 33},
		{4780, 34},
		{4700, 35},
		{4620, 36},
		{4540, 37},
		{4470, 38},
		{4400, 39},
		{4330, 40},
		{4270, 41},
		{4210, 42},
		{4150, 43},
		{4090, 44},
		{4040, 45},
		{3980, 46},
		{3930, 47},
		{3880, 48},
		{3840, 49},
		{3790, 50},
		{3740, 51},
		{3700, 52},
		{3660, 53},
		{3620, 54},
		{3580, 55},
		{3540, 56},
		{3500, 57},
		{3460, 58},
		{3430, 59},
		{3390, 60},
		{3360, 61},
		{3330, 62},
		{3300, 63},
		{3270, 64},
		{3230, 65},
		{3210, 66},
		{3180, 67},
		{3150, 68},
		{3120, 69},
		{3090, 70},
		{3070, 71},
		{3040, 72},
		{3020, 73},
		{2990, 74},
		{2970, 75},
		{2940, 76},
		{2920, 77},
		{2900, 78},
		{2880, 79},
		{2850, 80},
		{2830, 81},
		{2810, 82},
		{2790, 83},
		{2770, 84},
		{2750, 85},
		{2730, 86},
		{2710, 87},
		{2690, 88},
		{2680, 89},
		{2660, 90},
		{2640, 91},
		{2620, 92},
		{2610, 93},
		{2590, 94},
		{2570, 95},
		{2560, 96},
		{2540, 97},
		{2530, 98},
		{2510, 99},
		{2490, 100},
		{2480, 101},
		{2460, 102},
		{2450, 103},
		{2440, 104},
		{2420, 105},
		{2410, 106},
		{2390, 107},
		{2380, 108},
		{2370, 109},
		{2360, 110},
		{2340, 111},
		{2330, 112},
		{2320, 113},
		{2300, 114},
		{2290, 115},
		{2280, 116},
		{2270, 117},
		{2260, 118},
		{2250, 119},
		{2230, 120},
		{2220, 121},
		{2210, 122},
		{2200, 123},
		{2190, 124},
		{2180, 125},
		{2170, 126},
		{2160, 127},
		{2150, 128},
		{2140, 129},
		{2130, 130},
		{2120, 131},
		{2110, 132},
		{2100, 133},
		{2090, 134},
		{2080, 135},
		{2070, 136},
		{2060, 137},
		{2050, 138},
		{2040, 139},
		{2030, 141},
		{2020, 142},
		{2010, 143},
		{2000, 144},
		{1990, 145},
		{1980, 147},
		{1970, 148},
		{1960, 149},
		{1950, 150},
		{1940, 152},
		{1930, 153},
		{1920, 154},
		{1910, 155},
		{1900, 157},
		{1890, 158},
		{1880, 160},
		{1870, 161},
		{1860, 162},
		{1850, 164},
		{1840, 165},
		{1830, 167},
		{1820, 168},
		{1810, 170},
		{1800, 171},
		{1790, 173},
		{1780, 175},
		{1770, 176},
		{1760, 178},
		{1750, 180},
		{1740, 181},
		{1730, 183},
		{1720, 185},
		{1710, 187},
		{1700, 188},
		{1690, 190},
		{1680, 192},
		{1670, 194},
		{1660, 196},
		{1650, 198},
		{1640, 200},
		{1630, 202},
		{1620, 204},
		{1610, 206},
		{1600, 208},
		{1590, 210},
		{1580, 212},
		{1570, 215},
		{1560, 217},
		{1550, 219},
		{1540, 222},
		{1530, 224},
		{1520, 226},
		{1510, 229},
		{1500, 231},
		{1490, 234},
		{1480, 237},
		{1470, 239},
		{1460, 242},
		{1450, 245},
		{1440, 248},
		{1430, 250},
		{1420, 253},
		{1410, 256},
		{1400, 259},
		{1390, 262},
		{1380, 266},
		{1370, 269},
		{1360, 272},
		{1350, 275},
		{1340, 279},
		{1330, 282},
		{1320, 286},
		{1310, 289},
		{1300, 293},
		{1290, 297},
		{1280, 300},
		{1270, 304},
		{1260, 308},
		{1250, 312},
		{1240, 317},
		{1230, 321},
		{1220, 325},
		{1210, 330},
		{1200, 334},
		{1190, 339},
		{1180, 344},
		{1170, 348},
		{1160, 353},
		{1150, 358},
		{1140, 364},
		{1130, 369},
		{1120, 374},
		{1110, 380},
		{1100, 386},
		{1090, 391},
		{1080, 397},
		{1070, 403},
		{1060, 410},
		{1050, 416},
		{1040, 423},
		{1030, 430},
		{1020, 436},
		{1010, 444},
		{1000, 451},
		{990, 458},
		{980, 466},
		{970, 474},
		{960, 482},
		{950, 491},
		{940, 499},
		{930, 508},
		{920, 517},
		{910, 526},
		{900, 536},
		{890, 546},
		{880, 556},
		{870, 567},
		{860, 578},
		{850, 589},
		{840, 600},
		{830, 612},
		{820, 625},
		{810, 637},
		{800, 650},
		{790, 664},
		{780, 678},
		{770, 692},
		{760, 707},
		{750, 723},
		{740, 739},
		{730, 756},
		{720, 773},
		{710, 791},
		{700, 809},
		{690, 829},
		{680, 849},
		{670, 869},
		{660, 891},
		{650, 914},
		{640, 937},
		{630, 961},
		{620, 987},
};

/**
 * @brief  获取光照度函数
 * @param  PhotoResistor:光敏电阻电阻值
 * @retval 获取到的光照度
 */
uint16_t GetLux(float PhotoResistor)
{
	int i = 0;
	uint16_t lux = 0;

	for (i = 0; i < 281; i++)
	{

		if (PhotoResistor < 620)
		{
			lux = 1000;
			break;
		}
		else if (PhotoResistor > GL5516[i].ohm)
		{
			lux = GL5516[i].lux;
			break;
		}
	}
	return lux;
}

下面是我的测试用例

main.c

#include "stm32f4xx.h"                  // Device header
#include "OLED.h"   
#include "systick.h"
#include "dht11.h"
#include "light_adc.h"
#include "ADC.h"


extern uint8_t temp_2_int;
extern uint8_t temp_2_deci;

float GL5516_data = 0; 
float PhotoResistor;  

void init(void)
{
	OLED_Init(); 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	ADC3_Config();
}

void show_oled(void)
{
   
  DHT11_2_Read_Data(); //获取体温
	OLED_ShowString(1, 1, "average:");
  OLED_ShowString(2, 1, "temp:");
	OLED_ShowNum(2 , 8 ,temp_2_int , 2 );
	OLED_ShowString(2, 10, ".");
	OLED_ShowNum(2 , 11 ,temp_2_deci , 2 );
	OLED_ShowString(2, 14, "C");
	OLED_ShowString(3, 1, "HR:");
	    
}


int main(void)
{
	
	 init();
  
		while(1)
		{
			
		GL5516_data = Get_Conversion_value(ADC3, ADC_Channel_5, 20);

		PhotoResistor = (10000*GL5516_data)/(5-GL5516_data);
		LUX = GetLux(PhotoResistor);
		OLED_ShowNum(1,1,LUX,3);
		delay_s(1);
		}
		
}

第七步 血氧模块

1.引脚部分

这里面涉及很多寄存器操作   用的是iic的协议   可以自己去学习一下

vin3.3v
sclPB8
sdaPB9
GNDGND
INTPE15

与oled一个引脚  设备地址不同 可以挂载多个设备

该代码涉及iic协议 通过iic协议控制内部寄存器 调节模式及其读取数据 iic代码和上面OLED是一个代码无改动  控制寄存器规则及其读取到的数据处理参考如下链接

STM32通过IIC驱动MAX30102心率血氧传感器_srm32驱动max30102传感器与-CSDN博客

MAX30102脉搏血氧仪和心率传感器(含寄存器介绍)_max30102心率算法详解-CSDN博客

建议自己看手册配置寄存器锻炼动手和理解能力  (我的老师告诉我不要抄代码   0.0)

2.代码部分

max30102.h

#ifndef __MYIIC_H
#define __MYIIC_H
#include "systick.h"
#include "stm32f4xx.h"                  // Device header
#include "i2c.h"
#include "stdbool.h"

#define max30102_WR_address_w 0xAE //写
#define max30102_WR_address_r 0xAF //读

#define INT_pin GPIO_Pin_15

//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

bool max30102_write_reg(uint8_t uch_addr, uint8_t uch_data);
bool max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data);
bool maxim_max30102_init(void);
bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led);
bool max30102_reset();


#endif

max30102.c

#include "max30102.h"

 


bool max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
{
    
    IIC_Start();
    IIC_SendByte(max30102_WR_address_w);

    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;	
    }
    IIC_SendByte(uch_addr);
    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;	
    }
    IIC_SendByte(uch_data);

    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;	
    }

    IIC_Stop();
    return true;	

cmd_fail: 
    IIC_Stop();
    return false;
}

bool max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
{

    IIC_Start();
    IIC_SendByte(max30102_WR_address_w);	
    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;	
    }
    IIC_SendByte((uint8_t)uch_addr);
    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;	
    }
    IIC_Start();

    IIC_SendByte(max30102_WR_address_r);	

    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;
    }

    {
        *puch_data = IIC_ReadByte();	

        IIC_MasterAck(1);	
    }
    IIC_Stop();
    return true;	

cmd_fail: 
    IIC_Stop();
    return false;
}

bool maxim_max30102_init(void)

{
	
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);

	GPIO_InitStructure.GPIO_Pin = INT_pin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;	    
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;   
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	
    if(!max30102_write_reg(REG_INTR_ENABLE_1, 0xc0)) // INTR setting
        return false;
    if(!max30102_write_reg(REG_INTR_ENABLE_2, 0x00))
        return false;
    if(!max30102_write_reg(REG_FIFO_WR_PTR, 0x00)) 
        return false;
    if(!max30102_write_reg(REG_OVF_COUNTER, 0x00))
        return false;
    if(!max30102_write_reg(REG_FIFO_RD_PTR, 0x00)) 
        return false;
    if(!max30102_write_reg(REG_FIFO_CONFIG, 0x6f)) 
        return false;
    if(!max30102_write_reg(REG_MODE_CONFIG, 0x03))  
        return false;
    if(!max30102_write_reg(REG_SPO2_CONFIG, 0x2F)) 
        return false;

    if(!max30102_write_reg(REG_LED1_PA, 0x17))  
        return false;
    if(!max30102_write_reg(REG_LED2_PA, 0x17))  
        return false;
    if(!max30102_write_reg(REG_PILOT_PA, 0x7f)) 
        return false;
    return true;
}

bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)


{
    uint32_t un_temp;
    uint8_t uch_temp;
    *pun_ir_led = 0;
    *pun_red_led = 0;
    max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);
    max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);


    IIC_Start();

    IIC_SendByte(max30102_WR_address_w);	
    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;	
    }
    IIC_SendByte((uint8_t)REG_FIFO_DATA);
    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;	
    }

    IIC_Start();
    IIC_SendByte(max30102_WR_address_r);	

    if (IIC_WaitSlaveAck() != 0)
    {
        goto cmd_fail;
    }

    un_temp = IIC_ReadByte();
    IIC_MasterAck(0);
    un_temp <<= 16;
    *pun_red_led += un_temp;
    un_temp = IIC_ReadByte();
    IIC_MasterAck(0);
    un_temp <<= 8;
    *pun_red_led += un_temp;
    un_temp = IIC_ReadByte();
    IIC_MasterAck(0);
    *pun_red_led += un_temp;

    un_temp = IIC_ReadByte();
    IIC_MasterAck(0);
    un_temp <<= 16;
    *pun_ir_led += un_temp;
    un_temp = IIC_ReadByte();
    IIC_MasterAck(0);
    un_temp <<= 8;
    *pun_ir_led += un_temp;
    un_temp = IIC_ReadByte();
    IIC_MasterAck(0);
    *pun_ir_led += un_temp;
    *pun_red_led &= 0x03FFFF; 
    *pun_ir_led &= 0x03FFFF; 

    IIC_Stop();
    return true;
cmd_fail:

    IIC_Stop();
    return false;
}

bool max30102_reset()

{
    if(!max30102_write_reg(REG_MODE_CONFIG, 0x40))
        return false;
    else
        return true;
}


hr.h     数据处理   有个参照



#include "hr.h"

const uint8_t uch_spo2_table[184] = { 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99,
                                      99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
                                      100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97,
                                      97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91,
                                      90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81,
                                      80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67,
                                      66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50,
                                      49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29,
                                      28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5,
                                      3, 2, 1
                                    } ;


void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid,
        int32_t *pn_heart_rate, int8_t *pch_hr_valid)
/**
* \brief        Calculate the heart rate and SpO2 level
* \par          Details
*               By detecting  peaks of PPG cycle and corresponding AC/DC of red/infra-red signal, the an_ratio for the SPO2 is computed.
*               Since this algorithm is aiming for Arm M0/M3. formaula for SPO2 did not achieve the accuracy due to register overflow.
*               Thus, accurate SPO2 is precalculated and save longo uch_spo2_table[] per each an_ratio.
*
* \param[in]    *pun_ir_buffer           - IR sensor data buffer
* \param[in]    n_ir_buffer_length      - IR sensor data buffer length
* \param[in]    *pun_red_buffer          - Red sensor data buffer
* \param[out]    *pn_spo2                - Calculated SpO2 value
* \param[out]    *pch_spo2_valid         - 1 if the calculated SpO2 value is valid
* \param[out]    *pn_heart_rate          - Calculated heart rate value
* \param[out]    *pch_hr_valid           - 1 if the calculated heart rate value is valid
*
* \retval       None
*/
{
    uint32_t un_ir_mean;
    int32_t k, n_i_ratio_count;
    int32_t i, n_exact_ir_valley_locs_count, n_middle_idx;
    int32_t n_th1, n_npks;
    int32_t an_ir_valley_locs[15] ;
    int32_t n_peak_interval_sum;

    int32_t n_y_ac, n_x_ac;
    int32_t n_spo2_calc;
    int32_t n_y_dc_max, n_x_dc_max;
    int32_t n_y_dc_max_idx, n_x_dc_max_idx;
    int32_t an_ratio[5], n_ratio_average;
    int32_t n_nume, n_denom ;

    // calculates DC mean and subtract DC from ir
    un_ir_mean = 0;
    for (k = 0 ; k < n_ir_buffer_length ; k++ ) un_ir_mean += pun_ir_buffer[k] ;
    un_ir_mean = un_ir_mean / n_ir_buffer_length ;

    // remove DC and invert signal so that we can use peak detector as valley detector
    for (k = 0 ; k < n_ir_buffer_length ; k++ )
        an_x[k] = -1 * (pun_ir_buffer[k] - un_ir_mean) ;

    // 4 pt Moving Average
    for(k = 0; k < BUFFER_SIZE - MA4_SIZE; k++)
    {
        an_x[k] = ( an_x[k] + an_x[k + 1] + an_x[k + 2] + an_x[k + 3]) / (int)4;
    }
    // calculate threshold
    n_th1 = 0;
    for ( k = 0 ; k < BUFFER_SIZE ; k++)
    {
        n_th1 +=  an_x[k];
    }
    n_th1 =  n_th1 / ( BUFFER_SIZE);
    if( n_th1 < 30) n_th1 = 30; // min allowed
    if( n_th1 > 60) n_th1 = 60; // max allowed

    for ( k = 0 ; k < 15; k++) an_ir_valley_locs[k] = 0;
    // since we flipped signal, we use peak detector as vSalley detector
    maxim_find_peaks( an_ir_valley_locs, &n_npks, an_x, BUFFER_SIZE, n_th1, 4, 15 );//peak_height, peak_distance, max_num_peaks
    n_peak_interval_sum = 0;
    if (n_npks >= 2)
    {
        for (k = 1; k < n_npks; k++) n_peak_interval_sum += (an_ir_valley_locs[k] - an_ir_valley_locs[k - 1] ) ;
        n_peak_interval_sum = n_peak_interval_sum / (n_npks - 1);
        *pn_heart_rate = (int32_t)( (FS * 60) / n_peak_interval_sum );
        *pch_hr_valid  = 1;
    }
    else
    {
        *pn_heart_rate = -999; // unable to calculate because # of peaks are too small
        *pch_hr_valid  = 0;
    }

    //  load raw value again for SPO2 calculation : RED(=y) and IR(=X)
    for (k = 0 ; k < n_ir_buffer_length ; k++ )
    {
        an_x[k] =  pun_ir_buffer[k] ;
        an_y[k] =  pun_red_buffer[k] ;
    }

    // find precise min near an_ir_valley_locs
    n_exact_ir_valley_locs_count = n_npks;

    //using exact_ir_valley_locs , find ir-red DC andir-red AC for SPO2 calibration an_ratio
    //finding AC/DC maximum of raw

    n_ratio_average = 0;
    n_i_ratio_count = 0;
    for(k = 0; k < 5; k++) an_ratio[k] = 0;
    for (k = 0; k < n_exact_ir_valley_locs_count; k++)
    {
        if (an_ir_valley_locs[k] > BUFFER_SIZE )
        {
            *pn_spo2 =  -999 ; // do not use SPO2 since valley loc is out of range
            *pch_spo2_valid  = 0;
            return;
        }
    }
    // find max between two valley locations
    // and use an_ratio betwen AC compoent of Ir & Red and DC compoent of Ir & Red for SPO2
    for (k = 0; k < n_exact_ir_valley_locs_count - 1; k++)
    {
        n_y_dc_max = -16777216 ;
        n_x_dc_max = -16777216;
        if (an_ir_valley_locs[k + 1] - an_ir_valley_locs[k] > 3)
        {
            for (i = an_ir_valley_locs[k]; i < an_ir_valley_locs[k + 1]; i++)
            {
                if (an_x[i] > n_x_dc_max)
                {
                    n_x_dc_max = an_x[i];
                    n_x_dc_max_idx = i;
                }
                if (an_y[i] > n_y_dc_max)
                {
                    n_y_dc_max = an_y[i];
                    n_y_dc_max_idx = i;
                }
            }
            n_y_ac = (an_y[an_ir_valley_locs[k + 1]] - an_y[an_ir_valley_locs[k] ] ) * (n_y_dc_max_idx - an_ir_valley_locs[k]); //red
            n_y_ac =  an_y[an_ir_valley_locs[k]] + n_y_ac / (an_ir_valley_locs[k + 1] - an_ir_valley_locs[k])  ;
            n_y_ac =  an_y[n_y_dc_max_idx] - n_y_ac;   // subracting linear DC compoenents from raw
            n_x_ac = (an_x[an_ir_valley_locs[k + 1]] - an_x[an_ir_valley_locs[k] ] ) * (n_x_dc_max_idx - an_ir_valley_locs[k]); // ir
            n_x_ac =  an_x[an_ir_valley_locs[k]] + n_x_ac / (an_ir_valley_locs[k + 1] - an_ir_valley_locs[k]);
            n_x_ac =  an_x[n_y_dc_max_idx] - n_x_ac;     // subracting linear DC compoenents from raw
            n_nume = ( n_y_ac * n_x_dc_max) >> 7 ; //prepare X100 to preserve floating value
            n_denom = ( n_x_ac * n_y_dc_max) >> 7;
            if (n_denom > 0  && n_i_ratio_count < 5 &&  n_nume != 0)
            {
                an_ratio[n_i_ratio_count] = (n_nume * 100) / n_denom ; //formular is ( n_y_ac *n_x_dc_max) / ( n_x_ac *n_y_dc_max) ;
                n_i_ratio_count++;
            }
        }
    }
    // choose median value since PPG signal may varies from beat to beat
    maxim_sort_ascend(an_ratio, n_i_ratio_count);
    n_middle_idx = n_i_ratio_count / 2;

    if (n_middle_idx > 1)
        n_ratio_average = ( an_ratio[n_middle_idx - 1] + an_ratio[n_middle_idx]) / 2; // use median
    else
        n_ratio_average = an_ratio[n_middle_idx ];

    if( n_ratio_average > 2 && n_ratio_average < 184)
    {
        n_spo2_calc = uch_spo2_table[n_ratio_average] ;
        *pn_spo2 = n_spo2_calc ;
        *pch_spo2_valid  = 1;//  float_SPO2 =  -45.060*n_ratio_average* n_ratio_average/10000 + 30.354 *n_ratio_average/100 + 94.845 ;  // for comparison with table
    }
    else
    {
        *pn_spo2 =  -999 ; // do not use SPO2 since signal an_ratio is out of range
        *pch_spo2_valid  = 0;
    }
}


void maxim_find_peaks( int32_t *pn_locs, int32_t *n_npks,  int32_t  *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num )
/**
* \brief        Find peaks
* \par          Details
*               Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
*
* \retval       None
*/
{
    maxim_peaks_above_min_height( pn_locs, n_npks, pn_x, n_size, n_min_height );
    maxim_remove_close_peaks( pn_locs, n_npks, pn_x, n_min_distance );
    *n_npks = min( *n_npks, n_max_num );
}

void maxim_peaks_above_min_height( int32_t *pn_locs, int32_t *n_npks,  int32_t  *pn_x, int32_t n_size, int32_t n_min_height )
/**
* \brief        Find peaks above n_min_height
* \par          Details
*               Find all peaks above MIN_HEIGHT
*
* \retval       None
*/
{
    int32_t i = 1, riseFound = 0, holdOff1 = 0, holdOff2 = 0, holdOffThresh = 4;
    *n_npks = 0;

    while (i < n_size - 1)
    {
        if (holdOff2 == 0)
        {
            if (pn_x[i] > n_min_height && pn_x[i] > pn_x[i - 1])     // find left edge of potential peaks
            {
                riseFound = 1;
            }
            if (riseFound == 1)
            {
                if ((pn_x[i] < n_min_height) && (holdOff1 < holdOffThresh))     // if false edge
                {
                    riseFound = 0;
                    holdOff1 = 0;
                }
                else
                {
                    if (holdOff1 == holdOffThresh)
                    {
                        if ((pn_x[i] < n_min_height) && (pn_x[i - 1] >= n_min_height))
                        {
                            if ((*n_npks) < 15 )
                            {
                                pn_locs[(*n_npks)++] = i;   // peak is right edge
                            }
                            holdOff1 = 0;
                            riseFound = 0;
                            holdOff2 = 8;
                        }
                    }
                    else
                    {
                        holdOff1 = holdOff1 + 1;
                    }
                }
            }
        }
        else
        {
            holdOff2 = holdOff2 - 1;
        }
        i++;
    }
}

void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance)
/**
* \brief        Remove peaks
* \par          Details
*               Remove peaks separated by less than MIN_DISTANCE
*
* \retval       None
*/
{

    int32_t i, j, n_old_npks, n_dist;

    /* Order peaks from large to small */
    maxim_sort_indices_descend( pn_x, pn_locs, *pn_npks );

    for ( i = -1; i < *pn_npks; i++ )
    {
        n_old_npks = *pn_npks;
        *pn_npks = i + 1;
        for ( j = i + 1; j < n_old_npks; j++ )
        {
            n_dist =  pn_locs[j] - ( i == -1 ? -1 : pn_locs[i] ); // lag-zero peak of autocorr is at index -1
            if ( n_dist > n_min_distance || n_dist < -n_min_distance )
                pn_locs[(*pn_npks)++] = pn_locs[j];
        }
    }

    // Resort indices int32_to ascending order
    maxim_sort_ascend( pn_locs, *pn_npks );
}

void maxim_sort_ascend(int32_t  *pn_x, int32_t n_size)
/**
* \brief        Sort array
* \par          Details
*               Sort array in ascending order (insertion sort algorithm)
*
* \retval       None
*/
{
    int32_t i, j, n_temp;
    for (i = 1; i < n_size; i++)
    {
        n_temp = pn_x[i];
        for (j = i; j > 0 && n_temp < pn_x[j - 1]; j--)
            pn_x[j] = pn_x[j - 1];
        pn_x[j] = n_temp;
    }
}

void maxim_sort_indices_descend(  int32_t  *pn_x, int32_t *pn_indx, int32_t n_size)
/**
* \brief        Sort indices
* \par          Details
*               Sort indices according to descending order (insertion sort algorithm)
*
* \retval       None
*/
{
    int32_t i, j, n_temp;
    for (i = 1; i < n_size; i++)
    {
        n_temp = pn_indx[i];
        for (j = i; j > 0 && pn_x[n_temp] > pn_x[pn_indx[j - 1]]; j--)
            pn_indx[j] = pn_indx[j - 1];
        pn_indx[j] = n_temp;
    }
}





hr.h


#ifndef HR_H_
#define HR_H_
#include "stm32f4xx.h"                  // Device header
#include "stdbool.h"
#include "max30102.h"
#include "i2c.h"

#define true 1
#define false 0
#define FS 50    
#define BUFFER_SIZE  (FS* 3) 
#define MA4_SIZE  4 
#define min(x,y) ((x) < (y) ? (x) : (y))
#define MAX_BRIGHTNESS 255
#define START 100
#define DATA_LENGTH 500


static  int32_t an_x[ BUFFER_SIZE]; 
static  int32_t an_y[ BUFFER_SIZE]; 

void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid, int32_t *pn_heart_rate, int8_t *pch_hr_valid);
void maxim_find_peaks(int32_t *pn_locs, int32_t *n_npks,  int32_t  *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num);
void maxim_peaks_above_min_height(int32_t *pn_locs, int32_t *n_npks,  int32_t  *pn_x, int32_t n_size, int32_t n_min_height);
void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance);
void maxim_sort_ascend(int32_t  *pn_x, int32_t n_size);
void maxim_sort_indices_descend(int32_t  *pn_x, int32_t *pn_indx, int32_t n_size);

#endif 


接口都准备好了  接下来在main.c添加获取数值的函数   下面是我的用例

#include "stm32f4xx.h"                  // Device header
#include "OLED.h"   
#include "systick.h"
#include "dht11.h"
#include "light_adc.h"
#include "ADC.h"
#include "MQ_adc.h"
#include "i2c.h"
#include "hr.h"   
#include "max30102.h"  

extern uint8_t temp_2_int;
extern uint8_t temp_2_deci;

float GL5516_data = 0; 
float PhotoResistor;  


float Smog_ppm = 0; 

uint32_t aun_ir_buffer[DATA_LENGTH]; //IR LED sensor data
int32_t n_ir_buffer_length;    //data length
uint32_t aun_red_buffer[DATA_LENGTH];    //Red LED sensor data
int32_t n_sp02; //SPO2 value
int8_t ch_spo2_valid;   //indicator to show if the SP02 calculation is valid
int32_t n_heart_rate;   //heart rate value
int8_t  ch_hr_valid;    //indicator to show if the heart rate calculation is valid
uint8_t uch_dummy;


void get_hr(void)
 { 
	uint32_t un_min, un_max, un_prev_data;  //variables to calculate the on-board LED brightness that reflects the heartbeats
	int i;
	int32_t n_brightness;
	float f_temp;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
 	I2C_SIM_Config();		 	
	
	max30102_reset(); //resets the MAX30102
	// initialize serial communication at 115200 bits per second:
	//read and clear status register
	max30102_read_reg(0,&uch_dummy);
	maxim_max30102_init();  //initializes the MAX30102

	n_brightness=0;
	un_min=0x3FFFF;
	un_max=0;
	n_ir_buffer_length=DATA_LENGTH; //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(GPIO_ReadInputDataBit(GPIOE , INT_pin)==1);   //wait until the interrupt pin asserts
			maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));  //read from MAX30102 FIFO	
			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=START;i<DATA_LENGTH;i++)
		{
				aun_red_buffer[i-START]=aun_red_buffer[i];
				aun_ir_buffer[i-START]=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<DATA_LENGTH;i++)
		{
				un_prev_data=aun_red_buffer[i-1];
				while(GPIO_ReadInputDataBit(GPIOE , INT_pin)== 1);
				maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));
		
				if(aun_red_buffer[i]>un_prev_data)//just to determine the brightness of LED according to the deviation of adjacent two AD 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;
				}
		}
		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); 
//		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);
		OLED_ShowNum(1 , 1 ,n_heart_rate , 2 );
		OLED_ShowNum(2 , 1 ,ch_hr_valid , 2 );
		OLED_ShowNum(3 , 1 ,n_sp02 , 2 );
		OLED_ShowNum(4 , 1 ,ch_spo2_valid , 2 );
		delay_ms(10);
	}
}






void init(void)
{
	OLED_Init(); 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	ADC3_Config();
	MQ2_Init();
}

void show_oled(void)
{
   
  DHT11_2_Read_Data(); //获取体温
	OLED_ShowString(1, 1, "average:");
  OLED_ShowString(2, 1, "temp:");
	OLED_ShowNum(2 , 8 ,temp_2_int , 2 );
	OLED_ShowString(2, 10, ".");
	OLED_ShowNum(2 , 11 ,temp_2_deci , 2 );
	OLED_ShowString(2, 14, "C");
	OLED_ShowString(3, 1, "HR:");
	    
}

void light()
{
	   GL5516_data = Get_Conversion_value(ADC3, ADC_Channel_5, 20);

		PhotoResistor = (10000*GL5516_data)/(5-GL5516_data);
		LUX = GetLux(PhotoResistor);
}




int main(void)
{
	
	 init();
	
		while(1)
		{
		get_hr();
		}
		
}













第八步 调节ESP8266模块上阿里云

1.步骤

原理:说白了就是通过串口发送at命令连接到阿里云的接口上去

1.首先就是烧录固件教程如下链接  

乐鑫ESP8266烧录固件、升级最新固件、刷MQTT固件_at version:1.7.4.0-CSDN博客

烧录固件完成后  去阿里云注册账号  和创建设备  入口在下面

物联网平台_设备接入_设备管理_监控运维_数据服务-阿里云 (aliyun.com)

注册就不多说了    点击“管理控制台”   开通一下公共实例

  • 31
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值