Pulse Sensor 心率传感器CC2530源码

** ZigBee、NBIOT等无线通信技术交流学习,可以加入QQ技术交流群:575036716

可以到CSDN的下载版块下载该源码包: 点击打开链接


     最近有朋友需要通过Pulse Sensor也就是心率传感器结合ZigBee进行开发,ZigBee是Ti的方案,主控是CC2530,因此写了这款传感器的驱动程序!

     Pulse Sensor是一款用来检测心率的传感器,使用方法还是比较方便的,用户只需要用手指按住传感器就可以,传感器的实物图如下:

            正面                            背面

                  


    Pulse Sensor检测原理是通过发射光源,然后根据光源返回的效果输出ADC数据。由于手指上布满毛细血管,而毛细血管会随着心率而跳动,这就导致传感器发射和接收光的效果不同,从而达到ADC输出效果不同,比如下图就是ADC的输出图:

    

    因此检测心率的方式很简单:测出两个ADC峰值间的时长,假设这个时长为T,单位是毫秒,那么1分钟的心率假设为S,则:

    S = 60000/T 次/秒

    程序设计需要考虑的地方:

    1. 传感器检测时,必须手指按在传感器上,否则检测的数据是不对的,所以首先要判断传感器是否有手指接触。

    2. 如何正确的获取两次峰值数据?

  程序逻辑:    

  1. if(halPulseSensorTouch())  
  2. {  
  3.   if( (refPeak = halPulseSensorPeakFitting()) == 0 )  
  4.     goto RET;  
  5.     
  6.   // First Beat  
  7.   if(halPulseSensorGetNextBeat(refPeak) == 0)  
  8.     goto RET;  
  9.       
  10.   // Clear and Start Timer  
  11.   HAL_PULSE_SENSOR_TIMER_CLEAR();  
  12.   HAL_PULSE_SENSOR_TIMER_START();  
  13.     
  14.   // Skip the peak value  
  15.   HAL_PULSE_SENSOR_DELAY_MS(200);  
  16.     
  17.   // Second Beat  
  18.   secondBeat = halPulseSensorGetNextBeat(refPeak);  
  19.     
  20.   // Stop Timer and get the delay time      
  21.   HAL_PULSE_SENSOR_TIMER_STOP();  
  22.     
  23.   if(secondBeat == 0)  
  24.     goto RET;  
  25.       
  26.   if( (beatTime = (uint16)HAL_PULSE_SENSOR_TIMER_GET_TIME()) == 0 )  
  27.     goto RET;  
  28.     
  29.   if( (dat.heartRate = (uint16)60000 / beatTime) != 0 )  
  30.     dat.ok = TRUE;  
  31. }  

        首先我们通过函数halPulseSensorTouch()检测是否手指是触摸在传感器上的,如果是我们才进行数据读取!

    然后函数halPulseSensorPeakFitting()是用来获取ADC峰值的数据,这个函数会拟合出一个峰值的数值作为参考数值,

具体的做法是不断地获取峰值数据,然后筛选出相近的一组数值求取平均值,这个平均值将作为峰值参考数值!

    然后确定一个峰值点,作为时间的起始点,这时候开启定时器,定时时间直到获取到下一个峰值点,这段时间就是一次心跳

的时长:

  1.   // First Beat  
  2.   if(halPulseSensorGetNextBeat(refPeak) == 0)  
  3.     goto RET;  

    开启定时器并尝试获取第二个峰值:

  1.  // Clear and Start Timer  
  2.   HAL_PULSE_SENSOR_TIMER_CLEAR();  
  3.   HAL_PULSE_SENSOR_TIMER_START();  
  4.     
  5.   // Skip the peak value  
  6.   HAL_PULSE_SENSOR_DELAY_MS(200);  // <-- 获取一个峰值后,如果直接读取数据,可能瞬间读到的数据还是峰值数据,所以通过延时跳过峰值阶段
  7.     
  8.   // Second Beat  
  9.   secondBeat = halPulseSensorGetNextBeat(refPeak);  // <---------------- 获取第二个峰值
  10.     
  11.   // Stop Timer and get the delay time      
  12.   HAL_PULSE_SENSOR_TIMER_STOP();  

    剩下的数据就是读取这段时间定时器时长,然后计算出心率就OK了:

  1. if( (beatTime = (uint16)HAL_PULSE_SENSOR_TIMER_GET_TIME()) == 0 )  
  2.     goto RET;  
  3.     
  4.   if( (dat.heartRate = (uint16)60000 / beatTime) != 0 )  // <------- 计算出心率
  5.     dat.ok = TRUE;  


    判断手指是否按下的方法: 先获取一个ADC的数据作为参考点,然后连续再获取20个数据(数量可调整)作为采样点,如果说这些采样点有一半以上和参考点是相近的,可以认为是没有手指按下!

  1. static BOOL halPulseSensorTouch(void)  
  2. {  
  3.   uint8 i;  
  4.   uint16 timeout = 0;  
  5.     
  6.   uint8 cnt = 0;  
  7.   uint8 adc8Next;  
  8.   uint8 adc8;  
  9.     
  10.   /* First touch or Realease */  
  11.   while( (adc8 = HAL_PULSE_SENSOR_ADC8()) < HAL_PULSE_SENSOR_TOUCH_ADC8 ||   
  12.           adc8 > HAL_PULSE_SENSOR_REALEASE_ADC8)  
  13.   {  
  14.     HAL_PULSE_SENSOR_DELAY_MS(10);  
  15.       
  16.     if(++timeout >= 100)  
  17.       goto ERROR;  
  18.   }  
  19.   
  20.   /* Touching */  
  21.   adc8 = HAL_PULSE_SENSOR_ADC8();  
  22.     
  23.   for(i = 0; i < HAL_PULSE_SENSOR_SPACING_CMP_TIMES; i++)  
  24.   {  
  25.     HAL_PULSE_SENSOR_DELAY_MS(20);  
  26.     adc8Next = HAL_PULSE_SENSOR_ADC8();  
  27.       
  28.     if( ( adc8Next >= adc8 && adc8Next < (adc8 + HAL_PULSE_SENSOR_SPACING) ) ||  
  29.         ( adc8Next < adc8 && (adc8Next + HAL_PULSE_SENSOR_SPACING) > adc8 )  )  
  30.     {  
  31.       continue;  
  32.     }  
  33.       
  34.     cnt++;  
  35.   }  
  36.     
  37.   if(cnt >= HAL_PULSE_SENSOR_SPACING_CMP_TIMES/2)  
  38.     return TRUE;  
  39.     
  40. ERROR:  
  41.   return FALSE;  
  42. }  

    获取峰值的方法: 由于人的心率是有范围的,正常人是60~100次/分钟,也有可能50、120这样,如果我们取一个极限值30次/Min,也就是说两个峰值的时长是2秒,再极端点算3秒,时长可自行调整,也就是说3秒内一定最少有一个峰值数据出现,我们只需要在这段时间内不断的获取ADC数据然后取最大值即可!当然这个过程还需要考虑其他情况,比如手指突然松开等等。

  1. static uint8 halPulseSensorGetPeak(void)  
  2. {  
  3.   uint16 i;  
  4.   uint8  adc8;  
  5.   uint8  adc8Peak = 0;  
  6.     
  7.   /* Get and compare the ADC value in 3 Second */  
  8.   for(i = 0; i < 300; i++)  
  9.   {  
  10.     HAL_PULSE_SENSOR_DELAY_MS(10);  
  11.       
  12.     adc8 = HAL_PULSE_SENSOR_ADC8();  
  13.     if(adc8 >= adc8Peak)  
  14.     {  
  15.       adc8Peak = adc8;  
  16.     }  
  17.   }  
  18.     
  19.   if(adc8Peak <= HAL_PULSE_SENSOR_TOUCH_ADC8 ||  
  20.      adc8Peak >= HAL_PULSE_SENSOR_REALEASE_ADC8)  
  21.   {  
  22.     adc8Peak = 0;  
  23.   }  
  24.     
  25.   return adc8Peak;  
  26. }  

    

      拟合峰值数据的方法:可以在每次获取心率时,由于每次都需要获取峰值来作为参考峰值数据,所以我们可以保存起来,比如保存10个数据,然后把这10个数据中相近的数值保留起来,其他删除等到下次更新,直到10个数据都是相近数据,然后我们可以算出平均值,这个平均值就会一直作为我们的参考峰值数据了!

  1. static uint8 halPulseSensorPeakFitting(void)  
  2. {  
  3.   uint8 peak;  
  4.   uint8 index;  
  5.   
  6.   for(index = 0; index < HAL_PULSE_SENSOR_PEAK_LEN; index++)  
  7.   {  
  8.     if(halPulseSensorPeak_g[index] == 0)  
  9.       break;  
  10.   }  
  11.     
  12.   if(index < HAL_PULSE_SENSOR_PEAK_LEN)  
  13.   {  
  14.     peak = halPulseSensorGetPeak();  
  15.     halPulseSensorPeak_g[index] = peak;  
  16.   }  
  17.   else  
  18.   {  
  19.     for(uint8 i = 0; i < HAL_PULSE_SENSOR_PEAK_LEN; i++)  
  20.     {  
  21.       uint8 cnt = 0;  
  22.         
  23.       if( halPulseSensorPeak_g[i] == 0 )  
  24.         continue;  
  25.         
  26.       for(uint8 j = 0; j < HAL_PULSE_SENSOR_PEAK_LEN; j++)  
  27.       {  
  28.         if( halPulseSensorPeak_g[j] == 0 )  
  29.           continue;  
  30.           
  31.         if( halPulseSensorPeak_g[i] < (halPulseSensorPeak_g[j] - HAL_PULSE_SENSOR_FITTING_ERR)  ||  
  32.             halPulseSensorPeak_g[i] > (halPulseSensorPeak_g[j] + HAL_PULSE_SENSOR_FITTING_ERR) )  
  33.         {  
  34.           cnt++;  
  35.         }  
  36.       }  
  37.         
  38.       if(cnt >= (HAL_PULSE_SENSOR_PEAK_LEN/2))  
  39.         halPulseSensorPeak_g[i] = 0;  
  40.     }  
  41.       
  42.     uint16 tmp = 0;  
  43.     uint8  tmpCnt = 0;  
  44.     for(uint8 i = 0; i < HAL_PULSE_SENSOR_PEAK_LEN; i++)  
  45.     {  
  46.       if( halPulseSensorPeak_g[i] == 0 )  
  47.         continue;  
  48.         
  49.       tmp += halPulseSensorPeak_g[i];  
  50.       tmpCnt++;  
  51.     }  
  52.       
  53.     peak = (uint8)(tmp/tmpCnt);  
  54.   }  
  55.     
  56.   return peak;  
  57. }  

    根据参考峰值数据获取心跳峰值点方法:我们会设置一段时间,比如3秒,然后在这段时间内不断的获取数据,如果获取的数据和参考峰值数据相近,我们会标记为峰值点记为A,然后继续获取,如果数据比A大,那么更新A,直到数据比A小,也就是呈现出下降趋势,说明已经到峰值点了,那么我们就会停止获取数据的动作并立即返回!

  1. static uint8 halPulseSensorGetNextBeat(uint8 refPeak)  
  2. {  
  3.   uint16 i;  
  4.   uint8 adc8, peak = 0;  
  5.     
  6.   for(i = 0; i < 300; i++)  
  7.   {  
  8.     HAL_PULSE_SENSOR_DELAY_MS(10);  
  9.       
  10.     adc8 = HAL_PULSE_SENSOR_ADC8();  
  11.     if( peak == 0 &&  
  12.         ( adc8 < (refPeak - HAL_PULSE_SENSOR_HEARTBEAT_ERR)  ||  
  13.           adc8 > (refPeak + HAL_PULSE_SENSOR_HEARTBEAT_ERR) ) )  
  14.     {  
  15.       continue;  
  16.     }  
  17.       
  18.     if(adc8 >= peak)  
  19.     {        
  20.       peak = adc8;  
  21.       continue;  
  22.     }  
  23.       
  24.     return peak;  
  25.   }  
  26.     
  27.   return 0;  
  28. }  

猜你喜欢

转载自blog.csdn.net/IoT_College/article/details/80013154