前言:通过断断续续的学习MSP340FR5969,发现这款金刚狼芯片具有很多与众不同的特性,尤其是在低功耗特性方面表现突出,比较适合用于可穿戴设备及无线传感器方面。经过几天的摸索,实现利用反射式光电容积描记传感器来采集和显示人体脉搏信号并计算出心率数值。
一、硬件组成
硬件上使用MSP340FR5969 launchpad作为主控板,完成脉搏模拟量定时采集,并将计算后的心率值和数字化的脉搏值通过串口发送到上位机显示。
此外,脉搏传感器使用购买的Pulse sensor心率传感器,该传感器使用绿光反射来探测皮肤表层血液流动,从而得到脉搏波形。传感器只有三个引脚,分别为signal out,VCC,GND。电源为3.3V和5v均可,传感器将接收到的光电路信号转化为电压信号,经过滤波放大后输出,所以输出为模拟信号。
pulse sensor模块:
硬件系统组成:
连线顺序为:
puslesensor-msp430
sigout->A10
vcc->3.6V
gnd->gnd
二、编译软件Energia
一般可使用IAR ,CCS等软件对MSP430进行编程,但是在MSP430FR5969 Launchpad开发板的说明书上看到有一种免费开源的软件Energia,其使用非常类似于arduino平台,容易上手,对于想在不过多了解底层寄存器的情况下快速开发应用是非常适合的。
目前支持MSP430FR5969 Launchpad开发板的是Energia 0101E0013版。但是在开始使用该软件时会出现一个问题,就是当你准备下载程序时,会提示你升级编程器,如下图:
当时当你升级的时候,又会提示设备连接失败。通过上网寻找发现已经有网友解决了这个问题,关键在于Energia内部的MSP430.DLL版本太旧,具体操作可查看原帖:
http://bbs.ednchina.com/BLOG_ARTICLE_3024874.HTM
更新之后Energia软件就可以正常使用了。针对原来Pulse sensor在arduino下的程序进行移植,程序上主要需要一个AD口采样,一个定时器中断来实现固定间隔采样(大约500HZ),还有一个串口(波特率115200)来实现数据上传。在实验过程中发现,energia软件还是存在一些问题,相比arduino来说不够完善,定时器并没有提供接口出来使用,最后在外国专业的430h论坛上提问,有老外提供了一个RTC_B的库文件,该文件可提供一个周期性中断功能,才最终实现了整个程序,程序如下:
- /*
- >> Pulse Sensor Amped 1.2 <<
- This code is for Pulse Sensor Amped by Joel Murphy and Yury Gitman
- www.pulsesensor.com
- >>> Pulse Sensor purple wire goes to Analog Pin 10 <<<
- Pulse Sensor sample aquisition and processing happens in the background via Timer 2 interrupt. 2mS sample rate.
- PWM on pins 3 and 11 will not work when using this code, because we are using Timer 2!
- The following variables are automatically updated:
- Signal : int that holds the analog signal data straight from the sensor. updated every 2mS.
- IBI : int that holds the time interval between beats. 2mS resolution.
- BPM : int that holds the heart rate value, derived every beat, from averaging previous 10 IBI values.
- QS : boolean that is made true whenever Pulse is found and BPM is updated. User must reset.
- Pulse : boolean that is true when a heartbeat is sensed then false in time with pin13 LED going out.
- This code is designed with output serial data to Processing sketch "PulseSensorAmped_Processing-xx"
- The Processing sketch is a simple data visualizer.
- All the work to find the heartbeat and determine the heartrate happens in the code below.
- Pin 13 LED will blink with heartbeat.
- If you want to use pin 13 for something else, adjust the interrupt handler
- It will also fade an LED on pin fadePin with every beat. Put an LED and series resistor from fadePin to GND.
- Check here for detailed code walkthrough:
- http://pulsesensor.myshopify.com/pages/pulse-sensor-amped-arduino-v1dot1
- Code Version 1.2 by Joel Murphy & Yury Gitman Spring 2013
- This update fixes the firstBeat and secondBeat flag usage so that realistic BPM is reported.
- */
- #include <RTC_B.h>
- // VARIABLES
- int pulsePin = A10; // Pulse Sensor purple wire connected to analog pin A10
- int blinkPin = RED_LED; // pin to blink led at each beat,pin P4.6
- int fadePin = GREEN_LED; // pin to do fancy classy fading blink at each beat,pin P1.0
- int fadeRate = 0; // used to fade LED on with PWM on fadePin
- // these variables are volatile because they are used during the interrupt service routine!
- volatile int BPM; // used to hold the pulse rate
- volatile int Signal; // holds the incoming raw data
- volatile int IBI = 600; // holds the time between beats, must be seeded!
- volatile boolean Pulse = false; // true when pulse wave is high, false when it's low
- volatile boolean QS = false; // becomes true when Arduoino finds a beat.
- volatile int rate[10]; // array to hold last ten IBI values
- volatile unsigned long sampleCounter = 0; // used to determine pulse timing
- volatile unsigned long lastBeatTime = 0; // used to find IBI
- volatile int Peak =512; // used to find peak in pulse wave, seeded
- volatile int Trough = 512; // used to find trough in pulse wave, seeded
- volatile int thresh = 512; // used to find instant moment of heart beat, seeded
- volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded
- volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
- volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM
- void setup(){
- boolean t;
- pinMode(blinkPin,OUTPUT); // pin that will blink to your heartbeat!
- pinMode(fadePin,OUTPUT); // pin that will fade to your heartbeat!
- Serial.begin(115200); // we agree to talk fast!
- rtc.begin(); // sets up to the RTC_B
- t = rtc.attachPeriodicInterrupt(512,timer_do); // Runs RTC_ISR() every 1/512 Second
- if (!t) {
- Serial.println("ERROR: Could not register a 2ms Periodic Interrupt!");
- }
- // UN-COMMENT THE NEXT LINE IF YOU ARE POWERING The Pulse Sensor AT LOW VOLTAGE,
- // AND APPLY THAT VOLTAGE TO THE A-REF PIN
- //analogReference(EXTERNAL);
- }
- void loop(){
- sendDataToProcessing('S', Signal); // send Processing the raw Pulse Sensor data
- if (QS == true){ // Quantified Self flag is true when arduino finds a heartbeat
- fadeRate = 255; // Set 'fadeRate' Variable to 255 to fade LED with pulse
- sendDataToProcessing('B',BPM); // send heart rate with a 'B' prefix
- sendDataToProcessing('Q',IBI); // send time between beats with a 'Q' prefix
- QS = false; // reset the Quantified Self flag for next time
- }
-
- ledFadeToBeat();
-
- delay(20); // take a break
- }
- void ledFadeToBeat(){
- fadeRate -= 15; // set LED fade value
- fadeRate = constrain(fadeRate,0,255); // keep LED fade value from going into negative numbers!
- analogWrite(fadePin,fadeRate); // fade LED
- }
- void sendDataToProcessing(char symbol, int data ){
- Serial.print(symbol); // symbol prefix tells Processing what type of data is coming
- Serial.println(data); // the data to send culminating in a carriage return
- }
- // THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
- // Timer 2 makes sure that we take a reading every 2 miliseconds
- void timer_do(){ // triggered when Timer2 counts to 124
- noInterrupts(); // disable interrupts while we do this
- Signal = analogRead(pulsePin)>>2; // read the Pulse Sensor,10-bit solution
- sampleCounter += 2; // keep track of the time in mS with this variable
- int Num = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
- // find the peak and trough of the pulse wave
- if(Signal < thresh && Num > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
- if (Signal < Trough){ // T is the trough
- Trough = Signal; // keep track of lowest point in pulse wave
- }
- }
- if(Signal > thresh && Signal > Peak){ // thresh condition helps avoid noise
- Peak = Signal; // P is the peak
- } // keep track of highest point in pulse wave
- // NOW IT'S TIME TO LOOK FOR THE HEART BEAT
- // signal surges up in value every time there is a pulse
- if (Num > 250){ // avoid high frequency noise
- if ( (Signal > thresh) && (Pulse == false) && (Num > (IBI/5)*3) ){
- Pulse = true; // set the Pulse flag when we think there is a pulse
- digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
- IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
- lastBeatTime = sampleCounter; // keep track of time for next pulse
- if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
- secondBeat = false; // clear secondBeat flag
- for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
- rate[i] = IBI;
- }
- }
- if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE
- firstBeat = false; // clear firstBeat flag
- secondBeat = true; // set the second beat flag
- interrupts(); // enable interrupts again
- return; // IBI value is unreliable so discard it
- }
- // keep a running total of the last 10 IBI values
- word runningTotal = 0; // clear the runningTotal variable
- for(int i=0; i<=8; i++){ // shift data in the rate array
- rate[i] = rate[i+1]; // and drop the oldest IBI value
- runningTotal += rate[i]; // add up the 9 oldest IBI values
- }
- rate[9] = IBI; // add the latest IBI to the rate array
- runningTotal += rate[9]; // add the latest IBI to runningTotal
- runningTotal /= 10; // average the last 10 IBI values
- BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM!
- QS = true; // set Quantified Self flag
- // QS FLAG IS NOT CLEARED INSIDE THIS ISR
- }
- }
- if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over
- digitalWrite(blinkPin,LOW); // turn off pin 13 LED
- Pulse = false; // reset the Pulse flag so we can do it again
- amp = Peak - Trough; // get amplitude of the pulse wave
- thresh = amp/2 + Trough; // set thresh at 50% of the amplitude
- Peak = thresh; // reset these for next time
- Trough = thresh;
- }
- if (Num > 2500){ // if 2.5 seconds go by without a beat
- thresh = 512; // set thresh default
- Peak = 512; // set P default
- Trough = 512; // set T default
- lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
- firstBeat = true; // set these to avoid noise
- secondBeat = false; // when we get the heartbeat back
- }
- interrupts(); // enable interrupts when youre done!
- }// end isr
复制代码在开发板上使用了下方的红灯和绿灯来模拟心跳频率。
三,效果演示
利用Processing制作的上位机来显示脉搏波形和心率数值,最后附上上位机软件程序。
PulseSensorAmpd_Processing_1dot1.rar
(17.79 KB, 下载次数: 119)
PulseSensorAmpd_Processing_1dot1.rar
(17.79 KB, 下载次数: 119)