项目背景

心脏运作可以揭露人体许多极具价值的信息,包括其健康状态、生活方式,甚至是情绪状态及心脏疾病的早期发病等。传统的医疗设备中,监测心跳速率和心脏活动是经由测量电生理讯号与心电图 (ECG) 来完成的,需要将电极连接到身体来量测心脏组织中所引发电气活动的信号。

整体方案

本项目系统上位机使用 LabVIEW VI,可以形象的看到6路心电信号,下位机则使用arduino开发板,连接我们的Olimex ECG/EMG扩展板,Arduino ADC 6路读取模拟输入, LabVIEW上位机以图形方式呈现读数, 并可以保存到文件中。

下位机设计

下位机则使用arduino开发板,arduino ARDUINO UNO 中介绍 ADC(模数转换)的概念。Arduino 板有六个 ADC 通道,如下图所示。其中任何一个或全部都可以用作模拟电压的输入。Arduino Uno ADC具有10 位分辨率(因此整数值来自 (0-(2^10) 1023))。这意味着它将 0 到 5 伏之间的输入电压映射为 0 到 1023 之间的整数值。 SHIELD-EKG-EMG是一个扩展模块, 用于ARDUINO兼容电路板, 如OLIMEXINO-328, OLIMEXINO-STM32和PIC32-PINGUINO及其它. 此子板也可兼容ARDUINO电路板, 包括ARDUINO UNO. 电路板配有安装用连接器. 此产品为EKG/EMG子板, 允许Arduino类的电路板捕捉心电图肌电图信号. 此子板添加了进行生物实验反馈的新途径. 此程序可使用户可监视心跳并记录脉搏, 通过监视和分析肌肉动作来识别姿势动作. 可堆叠子板(经针座) 开放硬件,开放软件项目(用户可访问所有的设计文件) D4/D9数字输出生成校准信号 精确的微调电位器用于校准 输入连接器, 用于无源或有源电极. 可运行3.3V和5V Arduino电路板 从Olimex ECG/EMG心电图/肌电图扩展板读取Arduino模拟输入,以图形方式呈现读数,并保存到“记录的示例”文件夹中的文件。 将工程中的库文件夹中的文件复制到你自己的Arduino库中。一般为“C:\用户\您的名称\文档\Arduino\库”。

#include

#include

//http://www.arduino.cc/playground/Main/FlexiTimer2

// All definitions

#define NUMCHANNELS 6

#define HEADERLEN 4

#define PACKETLEN (NUMCHANNELS * 2 + HEADERLEN + 1)

#define SAMPFREQ 256 // ADC sampling rate 256

#define TIMER2VAL (1024/(SAMPFREQ)) // Set 256Hz sampling frequency

#define LED1 13

#define CAL_SIG 9

// Global constants and variables

volatile unsigned char TXBuf[PACKETLEN]; //The transmission packet

volatile unsigned char TXIndex; //Next byte to write in the transmission packet.

volatile unsigned char CurrentCh; //Current channel being sampled.

volatile unsigned char counter = 0; //Additional divider used to generate CAL_SIG

volatile unsigned int ADC_Value = 0; //ADC current value

//~~~~~~~~~~

// Functions

//~~~~~~~~~~

/****************************************************/

/* Function name: Toggle_LED1 */

/* Parameters */

/* Input : No */

/* Output : No */

/* Action: Switches-over LED1. */

/****************************************************/

void Toggle_LED1(void){

if((digitalRead(LED1))==HIGH){ digitalWrite(LED1,LOW); }

else{ digitalWrite(LED1,HIGH); }

}

/****************************************************/

/* Function name: toggle_GAL_SIG */

/* Parameters */

/* Input : No */

/* Output : No */

/* Action: Switches-over GAL_SIG. */

/****************************************************/

void toggle_GAL_SIG(void){

if(digitalRead(CAL_SIG) == HIGH){ digitalWrite(CAL_SIG, LOW); }

else{ digitalWrite(CAL_SIG, HIGH); }

}

/****************************************************/

/* Function name: setup */

/* Parameters */

/* Input : No */

/* Output : No */

/* Action: Initializes all peripherals */

/****************************************************/

void setup() {

noInterrupts(); // Disable all interrupts before initialization

// LED1

pinMode(LED1, OUTPUT); //Setup LED1 direction

digitalWrite(LED1,LOW); //Setup LED1 state

pinMode(CAL_SIG, OUTPUT);

//Write packet header and footer

TXBuf[0] = 0xa5; //Sync 0

TXBuf[1] = 0x5a; //Sync 1

TXBuf[2] = 2; //Protocol version

TXBuf[3] = 0; //Packet counter

TXBuf[4] = 0x02; //CH1 High Byte

TXBuf[5] = 0x00; //CH1 Low Byte

TXBuf[6] = 0x02; //CH2 High Byte

TXBuf[7] = 0x00; //CH2 Low Byte

TXBuf[8] = 0x02; //CH3 High Byte

TXBuf[9] = 0x00; //CH3 Low Byte

TXBuf[10] = 0x02; //CH4 High Byte

TXBuf[11] = 0x00; //CH4 Low Byte

TXBuf[12] = 0x02; //CH5 High Byte

TXBuf[13] = 0x00; //CH5 Low Byte

TXBuf[14] = 0x02; //CH6 High Byte

TXBuf[15] = 0x00; //CH6 Low Byte

TXBuf[2 * NUMCHANNELS + HEADERLEN] = 0x01; // Switches state

// Timer2

// Timer2 is used to setup the analag channels sampling frequency and packet update.

// Whenever interrupt occures, the current read packet is sent to the PC

// In addition the CAL_SIG is generated as well, so Timer1 is not required in this case!

FlexiTimer2::set(TIMER2VAL, Timer2_Overflow_ISR);

FlexiTimer2::start();

// Serial Port

Serial.begin(57600);

//Set speed to 57600 bps

// MCU sleep mode = idle.

//outb(MCUCR,(inp(MCUCR) | (1<

interrupts(); // Enable all interrupts after initialization has been completed

}

/****************************************************/

/* Function name: Timer2_Overflow_ISR */

/* Parameters */

/* Input : No */

/* Output : No */

/* Action: Determines ADC sampling frequency. */

/****************************************************/

void Timer2_Overflow_ISR()

{

// Toggle LED1 with ADC sampling frequency /2

Toggle_LED1();

//Read the 6 ADC inputs and store current values in Packet

for(CurrentCh=0;CurrentCh<6;CurrentCh++){

ADC_Value = analogRead(CurrentCh);

TXBuf[((2*CurrentCh) + HEADERLEN)] = ((unsigned char)((ADC_Value & 0xFF00) >> 8)); // Write High Byte

TXBuf[((2*CurrentCh) + HEADERLEN + 1)] = ((unsigned char)(ADC_Value & 0x00FF)); // Write Low Byte

}

// Send Packet

for(TXIndex=0;TXIndex<17;TXIndex++){

Serial.write(TXBuf[TXIndex]);

}

// Increment the packet counter

TXBuf[3]++;

// Generate the CAL_SIGnal

counter++; // increment the devider counter

if(counter == 12){ // 250/12/2 = 10.4Hz ->Toggle frequency

counter = 0;

toggle_GAL_SIG(); // Generate CAL signal with frequ ~10Hz

}

}

/****************************************************/

/* Function name: loop */

/* Parameters */

/* Input : No */

/* Output : No */

/* Action: Puts MCU into sleep mode. */

/****************************************************/

void loop() {

__asm__ __volatile__ ("sleep");

}`

上位机设计

设计一个 LabVIEW VI, VI需要一些时间才能开始工作, 直到 LabVIEW 与 Arduino的串口输出同步。此时“当前状态”将从“初始状态”更改为“已校正偏移”。可以更改保存文件的名称。如果您想加入自己的信号处理算法,可以在“消费者循环”中执行此操作。 最后效果图:

总结

下位机程序已经在文章中了,需要下位机库文件和上位机labview程序的可以在评论区留下邮箱,如果这篇文章帮助了你,请好评三连呀!

推荐链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: