目录
一、介绍
1、工作原理概述
2、具体实现步骤
二、HP6的通信及配置
1、通信接口
2、器件地址/命令
3、校验
三、程序设计
①IIC通信相关基础函数
②HP6相关基础函数
一、介绍
HP6心率血压传感器的原理主要基于光电容积脉搏波描记法(PPG),这是一种利用光测量脉搏的技术。
1、工作原理概述
HP6传感器通过绿色发光二极管(LED)发光来探测脉搏跳动的强弱变化。当LED发出的绿光照射到皮肤时,由于血管组织在脉搏跳动过程中对于光线的吸收率不同,透过皮肤组织反射回的光被光敏传感器接受并转换成电信号。这些电信号再经过数字化处理,根据血液的吸光率即可算出心率和血压值。
2、具体实现步骤
发光与探测:HP6传感器内部至少包含两个绿色LED和一个光电传感器。LED发光照射到皮肤表面,光电传感器则负责接收反射回的光信号。
信号转换:反射回的光信号被光敏传感器接收后,转换成电信号。这些电信号代表了脉搏跳动的强弱变化。
数据处理:转换后的电信号经过放大、滤波等处理,以消除噪声和干扰。然后,通过HP6传感器内部的算法对这些信号进行分析,计算出精确的心率和血压值。
数值反馈与定位:最后,通过GSM和GPS模块实现对心率血压的数值反馈和当前位置信息的定位
二、HP6的通信及配置
HP-6 适用于 IIC 接口通讯,7bite 硬件地址:0x66;IIC 通讯速率 为 100K。
1、通信接口
IIC通信方式
供电范围: >=3.3v
接口:IIC_SDA IIC_SCL VCC GND EN
硬件连接:
HP6_IIC_SCL--------PA2
HP6_IIC_SDA--------PA1
HP6_EN-------------PC13 低电平有效
获取到的数据不需要二次计算,直接就是需要的数据
2、器件地址/命令
器件地址:
手册描述是7位器件地址是 0x66 110 0110
通过位运算计算器件地址
器件地址写: 0x66<<1 110 01100
器件地址读: (0x66<<1) | 0x01 110 01101
器件命令:
说明:
对此传感器的操作是要发送24个字节的命令组,实际就是要发一个数组,发完每个命令组后,主控芯片会收到24个字节.
开启心率命令组 获取心率命令组 关闭心率命令组
开启血压命令组 获取血压命令组 关闭血压命令组
所以:
对每个操作过程定义一个24元素数组,数组的内容就是22字节命令和2个字节的校验值
要通过IIC函数封装一个发送24字节函数 和 接收24字节函数
3、校验
校验的目的:
每个命令组都是24个字节,所以,传感器为了数据准确性的检查,所以要校验数据的正确性
校验过程:
校验方式用的是CRC16校验
发送24个字节命令需要校验
要发送24个字节数据
校验数据计算是从4号字节到21号字节(18个), 用户通过校验函数自己计算校验值
把校验值给到要发送的命令数组的22 和23 号字节
把带有校验值的命令数组发送给传感器(连续发送24个字节函数)
传感器接收命令组并且自动计算校验值, 然后跟用户的的校验值比较
接收24个字节数据需要校验
传感器将要发送的数据及传感器自己计算的校验值发送给主控芯片
用户接收24个字节数据到数组中(连续接收24字节函数)
用户需要将数组中的4到21号字节传入到校验函数计算校验值
用户计算的校验值与传感器回传的校验值对比看是否数据正确
所以:
封装主控芯片与HP6发收24字节并且校验的函数
三、程序设计
所用到的宏定义:
//宏定义器件地址
#define Hp6_ADDR_W (0x66<<1)
#define Hp6_ADDR_R (0x66<<1 | 0x01)//供电引脚宏定义
#define Hp_6_PowerON (GPIOC->ODR &= ~(1 << 13))
#define Hp_6_PowerOFF (GPIOC->ODR |= (1 << 13))//时钟线宏定义
#define HP6_IIC_SCL_L (GPIOA->ODR &= ~(1<<2))
#define HP6_IIC_SCL_H (GPIOA->ODR |= (1<<2))
//数据线宏定义
//输出
#define HP6_IIC_SDA_OUT_L (GPIOA->ODR &= ~(1<<1))
#define HP6_IIC_SDA_OUT_H (GPIOA->ODR |= (1<<1))
//输入
#define HP6_IIC_SDA_IN (GPIOA->IDR & (1<<1))
①IIC通信相关基础函数
/***********************************************
*函数名 :ph6_iic_io_init
*函数功能 :IIC所用IO口初始化
*函数参数 :无
*函数返回值:无
*函数描述 :HP6_HP6_IIC_SCL------PA2HP6_IIC_SDA------PA1
************************************************/
void hp6_iic_io_init(void)
{//端口时钟使能RCC->AHB1ENR |= (1<<0);//端口模式配置GPIOA->MODER &= ~((3<<2)|(3<<4));GPIOA->MODER |= ((1<<2)|(1<<4));//输出类型配置GPIOA->OTYPER &= ~(1<<2); //SCL---推挽GPIOA->OTYPER |= (1<<1); //SDA---开漏 //输出速度配置GPIOA->OSPEEDR &= ~((3<<2)|(3<<4));//上下拉配置GPIOA->PUPDR &= ~((3<<2)|(3<<4));//空闲状态GPIOA->ODR |= (1<<2); GPIOA->ODR |= (1<<1);
}/***********************************************
*函数名 :hp6_iic_star
*函数功能 :IIC起始信号函数
*函数参数 :无
*函数返回值:无
*函数描述 :
************************************************/
void hp6_iic_star(void)
{//时钟线拉低 为了可以动数据线HP6_IIC_SCL_L;//数据线拉高 为了可以产生下降沿HP6_IIC_SDA_OUT_H;//时钟线拉高 为了产生起始信号HP6_IIC_SCL_H;TIM11_delay_us(10);//数据线拉低 产生下降沿从而产生起始信号HP6_IIC_SDA_OUT_L;TIM11_delay_us(10);//时钟线拉低 安全作用HP6_IIC_SCL_L;
}/***********************************************
*函数名 :hp6_iic_stop
*函数功能 :IIC停止信号函数
*函数参数 :无
*函数返回值:无
*函数描述 :
************************************************/
void hp6_iic_stop(void)
{HP6_IIC_SCL_L; //为了可以动数据线HP6_IIC_SDA_OUT_L; //为了可以产生上升沿HP6_IIC_SCL_H; //为了产生停止信号TIM11_delay_us(10);HP6_IIC_SDA_OUT_H; //产生了上升沿,从而停止信号TIM11_delay_us(10);}/***********************************************
*函数名 :hp6_iic_send_ack
*函数功能 :IIC发送应答/不应答信号
*函数参数 :u8 ack
*函数返回值:无
*函数描述 :ack : 0 应答ack : 1 不应答
************************************************/
void hp6_iic_send_ack(u8 ack)
{HP6_IIC_SCL_L; //为了可以动数据线TIM11_delay_us(10);if(ack==0){HP6_IIC_SDA_OUT_L; //为了保持低电平 应答}else{HP6_IIC_SDA_OUT_H; //为了保持高电平 不应答}TIM11_delay_us(10);HP6_IIC_SCL_H; //产生应答信号TIM11_delay_us(10);//安全作用HP6_IIC_SCL_L;}/***********************************************
*函数名 :hp6_iic_get_ack
*函数功能 :IIC检测应答/不应答信号
*函数参数 :无
*函数返回值:u8
*函数描述 :返回0 检测到的是应答信号返回1 检测到的是不应答信号
************************************************/
u8 hp6_iic_get_ack(void)
{u8 ack;/*将数据线切换为输入模式*/HP6_IIC_SCL_L;HP6_IIC_SDA_OUT_H; /*获取应答/不应答信号*/HP6_IIC_SCL_L; //帮助从机拉低时钟线,从机就可以改变数据线了TIM11_delay_us(10);HP6_IIC_SCL_H; //从机产生了应答/不应答信号, 主机可以读数据线TIM11_delay_us(10);if(HP6_IIC_SDA_IN){ack = 1; //不应答}else{ack = 0; //应答}//安全作用HP6_IIC_SCL_L;return ack;}/***********************************************
*函数名 :hp6_iic_send_byte
*函数功能 :IIC发送一个字节函数
*函数参数 :u8 data
*函数返回值:无
*函数描述 :
************************************************/
void hp6_iic_send_byte(u8 data)
{u8 i;for(i=0;i<8;i++){HP6_IIC_SCL_L; //可以写数据TIM11_delay_us(10);if(data & 0x80){HP6_IIC_SDA_OUT_H; //表示数据对应位是1}else{HP6_IIC_SDA_OUT_L; //表示数据对应位是0}TIM11_delay_us(10);HP6_IIC_SCL_H; //从机可以读数据TIM11_delay_us(10);//发送次高位data = data << 1;}//安全作用HP6_IIC_SCL_L; }/***********************************************
*函数名 :hp6_iic_rec_byte
*函数功能 :IIC接收一个字节函数
*函数参数 :无
*函数返回值:u8
*函数描述 :
************************************************/
u8 hp6_iic_rec_byte(void)
{u8 i;u8 data;/*将数据线切换输入模式*/HP6_IIC_SCL_L; HP6_IIC_SDA_OUT_H; /*接收一个字节数据*/for(i=0;i<8;i++){HP6_IIC_SCL_L; //帮助从机拉低时钟线,可以写数据TIM11_delay_us(10);HP6_IIC_SCL_H; //主机可以读数据TIM11_delay_us(10);data = data << 1;if(HP6_IIC_SDA_IN){data = data | 0x01;}}//安全HP6_IIC_SCL_L;return data;}
②HP6相关基础函数
HP6所用到的命令:
//开启血压测量
u8 cmd_bp_open[]=
{0xc8,0xd7,0xb6,0xa5,0x90,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//关闭血压测量
u8 cmd_bp_close[]=
{0xc8,0xd7,0xb6,0xa5,0x90,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//获取血压测量结果
u8 cmd_bp_result[]=
{0xc8,0xd7,0xb6,0xa5,0x90,0x02,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//开启心率测量
u8 cmd_rate_open[]=
{0xc8,0xd7,0xb6,0xa5,0xD0,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//关闭心率测量
u8 cmd_rate_close[]=
{0xc8,0xd7,0xb6,0xa5,0xD0,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//获取心率测量结果
u8 cmd_rate_result[]=
{0xc8,0xd7,0xb6,0xa5,0xD0,0x02,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//获取ADC 数据
u8 cmd_get_adc[]=
{0xc8,0xd7,0xb6,0xa5,0x91,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//设置低功耗
u8 cmd_set_powersaving[]=
{0xc8,0xd7,0xb6,0xa5,0x70,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//获取版本信息
u8 cmd_get_version[]=
{0xc8,0xd7,0xb6,0xa5,0xa2,0x02,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
crc校验表:
const u16 crc16_tab[256] =
{0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
PH6的初始化函数
/************************************************
*函数名 :Hp6_init
*函数功能 :HP6初始化函数
*函数参数 :无
*函数返回值:无
*函数描述 : HP6_EN管脚HP6_IIC_IO;
***************************************************/
void Hp_6_init(void)
{/*HP6_IIC_IO*/hp6_iic_io_init();/*HP6_EN管脚*/RCC->AHB1ENR |= 1 << 2;GPIOC->MODER &= ~(3 << 26);GPIOC->MODER |= 1 << 26;GPIOC->OTYPER &= ~(1 << 13);GPIOC->OSPEEDR &= ~(3 << 26);/*HP6电源使能*/Hp_6_PowerON;//延时50tim11_delay_ms(50);
}
利用IIC连续发送24字节函数
/************************************************
*函数名 :Hp_6_I2CWrite
*函数功能 :主机通过IIC协议发送24字节数据到Hp6
*函数参数 :u8 *pData
*函数返回值:无
*函数描述 :主机发送命令数组给Hp6
***************************************************/
void Hp_6_I2CWrite(u8 *pData)
{u8 i;u8 ack;hp6_iic_star();hp6_iic_send_byte(Hp6_ADDR_W); hp6_iic_get_ack();//发送24字节数据for(i=0;i<24;i++){hp6_iic_send_byte(*(pData+i)); hp6_iic_get_ack();}hp6_iic_stop();
}
利用IIC连续接收24字节函数
/************************************************
*函数名 :Hp_6_I2CRead
*函数功能 :主机通过IIC协议从hp6读24字节数据
*函数参数 :u8 *rData 接收到的数据存放地址
*函数返回值:无
*函数描述 :HP6回传的数据
***************************************************/
void Hp_6_I2CRead(u8 *rData)
{u8 i;hp6_iic_star();hp6_iic_send_byte(Hp6_ADDR_R); hp6_iic_get_ack();//读24字节数据for(i=0;i<24;i++){*(rData+i) = hp6_iic_rec_byte(); if(i == 23){hp6_iic_send_ack(1); //不再接收数据}else{hp6_iic_send_ack(0); //继续接收下一个字节数据}}hp6_iic_stop();
}
CRC16校验函数
/************************************************
*函数名 :Crc16
*函数功能 :CRC16校验函数
*函数参数 :u8 *data,u8 len
*函数返回值:无
*函数描述 :u8 *data :校验的数据的起始位置u8 len :数据的个数
***************************************************/
u16 Crc16(u8 *data,u8 len)
{u16 crc16 = 0xFFFF;u32 uIndex ; //CRC查询表索引while (len --){uIndex = (crc16&0xff) ^ ((*data) & 0xff) ; //计算CRCdata = data + 1;crc16 = ((crc16>>8) & 0xff) ^ crc16_tab[uIndex];}return crc16 ;//返回CRC校验值
}
主控芯片与HP6发\收24字节并且校验的函数
/************************************************
*函数名 :HP_6_SendCmd_CRC
*函数功能 :发送命令数组到HP6,HP6返回数组,并且校验
*函数参数 :u8 *tx_buff,u8 *rx_buff
*函数返回值:u8
*函数描述 :返回值位1表示校验正确返回值位0表示校验错误
***************************************************/
u8 HP_6_SendCmd_CRC(u8 *tx_buff,u8 *rx_buff)
{u16 crc;u16 crc_r;u8 sta = 1;/*发送命令组*///计算要发送的数据的校验值crc = Crc16(&tx_buff[4],18);//将计算后的校验值赋值到命令数组中22 23*(u16 *)&tx_buff[22] = crc;//将24个字节数据发送给HP6Hp_6_I2CWrite(tx_buff);/*接收命令组*///接收到24个字节数据Hp_6_I2CRead(rx_buff);//计算接收回来的数组校验值crc = Crc16(&rx_buff[4],18); //校验值 主机自己计算的校验//提出接收回来的校验值crc_r = *(u16 *)&rx_buff[22]; //校验值 从机计算的校验//对比计算校验值与传感器校验值if(crc != crc_r){sta=0;}return sta;
}
开启心率函数
/************************************************
*函数名 :HP_6_OpenRate
*函数功能 :开启心率测量函数
*函数参数 :无
*函数返回值:u8
*函数描述 :此函数的返回值如果是1,就证明发送的命令已经执行,开启成功如果是0 传感器5s开启失败
***************************************************/
u8 HP_6_OpenRate(void)
{u8 sta;u8 rx_buff[24]= {0};timer_buff[5] = 0; //开始计时do{//5s开启失败就跳出if(timer_buff[5] >= 5000){return 0;}sta = HP_6_SendCmd_CRC(cmd_rate_open,rx_buff);}while(!sta || !rx_buff[6]);return 1;
}
关闭心率函数
/************************************************
*函数名 :HP_6_CloseRate
*函数功能 :关闭心率测量函数
*函数参数 :无
*函数返回值:u8
*函数描述 :此函数的返回值如果是1,就证明发送的命令已经执行,关闭成功如果是0 关闭失败 传感器5s超时处理
***************************************************/
u8 HP_6_CloseRate(void)
{u8 sta ;u8 rx_buff[24];timer_buff[5] = 0; //开始计时do{//5s关闭失败就跳出if(timer_buff[5] >= 5000){return 0;}//发送心率测量命令sta = HP_6_SendCmd_CRC(cmd_rate_close,rx_buff);}while(!sta || !rx_buff[6]);return 1;
}
获取心率函数
/*********************************************
*函数名 :HP_6_GetRateResult
*函数功能 :获取心率测量结果函数
*函数参数 :u8 *result //存放心率结果地址
*函数返回值:无
*函数描述 :
**********************************************/
void HP_6_GetRateResult(u8 *result)
{u8 rx_buff[24] = {0};u8 sta;//发送心率测量命令sta = HP_6_SendCmd_CRC(cmd_rate_result,rx_buff);if(sta == 1){*result = rx_buff[7];}
}
开启血压函数
/************************************************
*函数名 :HP_6_Openbp
*函数功能 :开启血压测量函数
*函数参数 :无
*函数返回值:u8
*函数描述 :此函数的返回值如果是1,就证明发送的命令已经执行,开启成功如果是0 传感器5s开启失败
***************************************************/
u8 HP_6_Openbp(void)
{u8 sta;u8 rx_buff[24]= {0};timer_buff[5] = 0; //开始计时do{//5s开启失败就跳出if(timer_buff[5] >= 5000){return 0;}sta = HP_6_SendCmd_CRC(cmd_bp_open,rx_buff);}while(!sta || !rx_buff[6]);return 1;
}
关闭血压函数
/************************************************
*函数名 :HP_6_Closebp
*函数功能 :关闭血压测量函数
*函数参数 :无
*函数返回值:u8
*函数描述 :此函数的返回值如果是1,就证明发送的命令已经执行,关闭成功如果是0 关闭失败 传感器5s超时处理
***************************************************/
u8 HP_6_Closebp(void)
{u8 sta ;u8 rx_buff[24];timer_buff[5] = 0; //开始计时do{//5s关闭失败就跳出if(timer_buff[5] >= 5000){return 0;}//发送心率测量命令sta = HP_6_SendCmd_CRC(cmd_bp_close,rx_buff);}while(!sta || !rx_buff[6]);return 1;
}
获取血压函数
/*********************************************
*函数名 :HP_6_Get_bpResult
*函数功能 :获取血压测量结果函数
*函数参数 :u8 *result 接收到血压数据数组
*函数返回值:void
*函数描述 :
**********************************************/
void HP_6_Get_bpResult(u8 *result)
{u8 state;u8 r_buff[24] = {0};state = HP_6_SendCmd_CRC(cmd_bp_result,r_buff);if(state==1 && r_buff[7]==1) //测量成功{*result = r_buff[10];*(result+1) = r_buff[11];HP_6_Closebp();HP_6_Openbp();}else if(state==1 && r_buff[7]==2) //测量失败{*result = 0;*(result+1) = 0;HP_6_Closebp();HP_6_Openbp();}}