[设计过程分享] Max32630(四)——心率传感器

29447945   2017-9-14 10:52 楼主
现在越来越多的人们重视自己的健康,参加各类运动,强身健体,个人也喜欢跑步,但是最近好久都没跑步了。 运动过程中,都需要关注的一个参数就是自己的心率,心率过高过低都不行,所以检测心率是一个很重要的参数。 现在最普遍的心率传感器就是 PulseSensor 脉搏心率传感器 QQ截图20170914104627.png 可以检测心率和脉搏; QQ截图20170914104639.png 原理主要就是通过光反射,转化为电压信号,单片机通过ADC采集电压,然后在通过算法得到心率和心跳; IMG_2248.gif 其中算法部分代码都是现成,我们使用时只需要把ADC配置好,读取数据就可以得到需要的参数; 算法部分:
  1. void SensorTimer_handler(void)
  2. {
  3. float temp = 0;
  4. unsigned int runningTotal;
  5. ADC_GetData(&Signal); // read the Pulse Senso
  6. temp = Signal * 1.818;
  7. Signal = (u16)temp-1;
  8. sampleCounter += 2; // keep track of the time in mS with this variable
  9. Num = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
  10. ADC_StartConvert(ADC_CH_1_DIV_5, 0, 1); //restart ADC conversion
  11. // find the peak and trough of the pulse wave
  12. if(Signal < thresh && Num > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
  13. if (Signal < T){ // T is the trough
  14. T = Signal; // keep track of lowest point in pulse wave
  15. }
  16. }
  17. if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
  18. P = Signal; // P is the peak
  19. } // keep track of highest point in pulse wave
  20. // NOW IT'S TIME TO LOOK FOR THE HEART BEAT
  21. // signal surges up in value every time there is a pulse
  22. if (Num > 250){ // avoid high frequency noise
  23. if ( (Signal > thresh) && (Pulse == 0) && (Num > (IBI/5)*3) ){
  24. Pulse = 1; // set the Pulse flag when we think there is a pulse
  25. // HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET); // turn on pin 13 LED
  26. IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
  27. lastBeatTime = sampleCounter; // keep track of time for next pulse
  28. if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
  29. secondBeat = 0; // clear secondBeat flag
  30. for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
  31. rate[i] = IBI;
  32. }
  33. }
  34. if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE
  35. firstBeat = 0; // clear firstBeat flag
  36. secondBeat = 1; // set the second beat flag
  37. // sei(); // enable interrupts again
  38. return; // IBI value is unreliable so discard it
  39. }
  40. // keep a running total of the last 10 IBI values
  41. runningTotal = 0; // clear the runningTotal variable
  42. for(int i=0; i<=8; i++){ // shift data in the rate array
  43. rate[i] = rate[i+1]; // and drop the oldest IBI value
  44. runningTotal += rate[i]; // add up the 9 oldest IBI values
  45. }
  46. rate[9] = IBI; // add the latest IBI to the rate array
  47. runningTotal += rate[9]; // add the latest IBI to runningTotal
  48. runningTotal /= 10; // average the last 10 IBI values
  49. BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM!
  50. QS = 1; // set Quantified Self flag
  51. // QS FLAG IS NOT CLEARED INSIDE THIS ISR
  52. }
  53. }
  54. if (Signal < thresh && Pulse == 1){ // when the values are going down, the beat is over
  55. // HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET); // turn off pin 13 LED
  56. Pulse = 0; // reset the Pulse flag so we can do it again
  57. amp = P - T; // get amplitude of the pulse wave
  58. thresh = amp/2 + T; // set thresh at 50% of the amplitude
  59. P = thresh; // reset these for next time
  60. T = thresh;
  61. }
  62. if (Num > 2500){ // if 2.5 seconds go by without a beat
  63. thresh = 512; // set thresh default
  64. P = 512; // set P default
  65. T = 512; // set T default
  66. lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
  67. firstBeat = 1; // set these to avoid noise
  68. secondBeat = 0; // when we get the heartbeat back
  69. }
  70. TMR32_ClearFlag(MXC_TMR2);
  71. }
读值:
  1. int BPM; // used to hold the pulse rate
  2. u16 Signal; // holds the incoming raw data
  3. int IBI = 600; // holds the time between beats, must be seeded!
  4. unsigned char Pulse = 0; // true when pulse wave is high, false when it's low
  5. unsigned char QS = 0; // becomes true when Arduoino finds a beat.
  6. int rate[10]; // array to hold last ten IBI values
  7. unsigned long sampleCounter = 0; // used to determine pulse timing
  8. unsigned long lastBeatTime = 0; // used to find IBI
  9. int P =426; // used to find peak in pulse wave, seeded
  10. int T = 426; // used to find trough in pulse wave, seeded
  11. int thresh = 426; // used to find instant moment of heart beat, seeded
  12. int amp = 100; // used to hold amplitude of pulse waveform, seeded
  13. int Num;
  14. unsigned char firstBeat = 1; // used to seed rate array so we startup with reasonable BPM
  15. unsigned char secondBeat = 0; // used to seed rate array so we startup with reasonable BPM
  16. void sendDataToProcessing(char symbol, int dat )
  17. {
  18. putchar(symbol); // symbol prefix tells Processing what type of data is coming
  19. printf("%d\r\n",dat); // the data to send culminating in a carriage return
  20. }
  21. int Sensor_Task(void)
  22. {
  23. static u32 PointM = 0;
  24. uint16_t adc_val[4];
  25. unsigned int overflow[4];
  26. uint8_t fmtstr[40];
  27. /* Initialize ADC */
  28. if( ( OsDelayCCnt - PointM ) >= T_20MS)
  29. {
  30. PointM = OsDelayCCnt;
  31. /* Convert channel 0 */
  32. sendDataToProcessing('S', Signal); // send Processing the raw Pulse Sensor data
  33. if (QS == 1)
  34. {
  35. User.BPM = BPM;
  36. User.IBI = IBI;
  37. sendDataToProcessing('B',BPM); // send heart rate with a 'B' prefix
  38. sendDataToProcessing('Q',IBI); // send time between beats with a 'Q' prefix
  39. QS = 0; // reset the Quantified Self flag for next time
  40. }
  41. /* Delay for 1/4 second before next reading */
  42. }
  43. }
显示:
  1. void OLED_Task(void)
  2. {
  3. static u32 PointM = 0;
  4. static u8 mState = 0;
  5. char Data_Time[9] = "hh:mm:ss";
  6. char Data_Data[11] = "yyyy-mm-dd";
  7. char Data_Sensor[19] = "BMP:xxxx IBI:xxxx";
  8. LCD_P8x16Str(36,0,"MAX32630");
  9. // LCD_P8x16Str(0,2,"EEWORLD");
  10. // LCD_P8x16Str(0,4,"Smart Watch");
  11. if( ( OsDelayCCnt - PointM ) >= T_1S)
  12. {
  13. PointM = OsDelayCCnt;
  14. // LCD_CLS();
  15. sprintf(Data_Sensor,"BMP:%03d IBI:%04d",User.BPM,User.IBI);
  16. LCD_P8x16Str(0,2,Data_Sensor);
  17. sprintf(Data_Data,"%04d-%02d-%02d",User.RtcTime.Year + 2000,User.RtcTime.Mon,User.RtcTime.Day);
  18. LCD_P8x16Str(0,4,Data_Data);
  19. sprintf(Data_Time,"%02d:%02d:%02d",User.RtcTime.Hour,User.RtcTime.Min,User.RtcTime.Sec);
  20. LCD_P8x16Str(0,6,Data_Time);
  21. }
  22. }
IMG_2249.PNG 图片看不清,把视频上传上来
IMG_2248.mp4 (1.61 MB)
(下载次数: 4, 2017-9-14 10:54 上传)
IMG_2248.mp4 (1.61 MB)
(下载次数: 4, 2017-9-14 10:54 上传)
本帖最后由 29447945 于 2017-9-14 10:58 编辑

回复评论 (4)

点赞  2018-1-31 09:43
大佬使用什么单片机写的啊
点赞  2018-7-6 19:14
大佬使用什么单片机写的啊
点赞  2018-7-6 19:15
引用: 风斩冰宁霜 发表于 2018-7-6 19:15
大佬使用什么单片机写的啊

ST或者美信都可以
点赞  2018-7-9 09:26
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复