2013-09-30
Olimex EKG/EMG shield breakout board for Arduino, Raspberry Pi, and NXP Cortex M0 boards
Now I am making an Arduino shield breakout board for the Olimex EKG/EMG shield. Then I can use this breakout board to route signals to non Arduino hardware, such as Raspberry Pi and NXP Cortex M0 boards.
.END
From Bulgaria with toy
From Bulgaria with toy!
.END
EESTEC Workshop 2012 Day 4. EMG
http://cs.curs.pub.ro/wiki/pm/eestec/4
http://cs.curs.pub.ro/wiki/pm/eestec/1
http://cs.curs.pub.ro/wiki/pm/eestec/2
http://cs.curs.pub.ro/wiki/pm/eestec/3
.END2
ECG toy arrives just in time!
I already switched off the scope, unplugged power, and thinking of moving it off the desktop, but at the same time the UPS guy knocked at my door, and gave me the olimex ECG toy!
I thought the toy might come a couple of days later, when I have started to forget the ECG things I have been playing. But it arrives an hour too early, because I am a big tired and need to do some yoga or taichi to refresh my memory, ...
.END
Packing up ECG experimental hardware
It is a bit disappointed to see that both my high pass and low pass filters not working. But I am not too upset, because I knew I was doing a quick and dirty project, using the simplest hardware, without much serious design. I guess the main problem is that the resistor value of the RC low pass filter is too high, therefore picking up too much noise. One thing I should do is to use OP AMPs which should pick up much less noise and better performance. But I saw that the opamp circuit is a bit too complicated, so I was too lazy to try my luck that way.
But I am still happy to have revised the RC network which I learned ages ago in technical college, and had almost forgotten the details. All these years, I still found the idea of using imaginary numbers to represent the phase a bit too abstract, at least comparing to the digital logic circuits which I have always been comfortable to play with.
Anyway, so much for the preliminary test of ECG for now.
I was thinking of shifting also the scope from the desktop, to get more desktop space to switch in my other suspended project, the NXP LPC1114 barebone thing, ...
But, wait a moment, I heard someone knocking at my door, ...
.END
Low pass filter for ECG signal a total failure!
I increased the resistor value from 10K to 470K, but still the input and output waveform does not look right. The input seems to have picked up a heavy line signal of 4Vpp, and though the 470K/2.2uF seems to have filter away the 50Hz line noise, but the ouput is still too noisy with the order of 50mVpp, which over shadows the ECG signal, even existing. I vaguely remember that high value carbon resistor pick high noise. But I have no metal film resistor to do more trials and errors. So I gave up for now, and hoping to get the Olimex thing in a couple of days.
.END
Low pass filter first trial not OK
Low pass filter for ECG signals design notes
On second thought, if the ECG signal frequency is only 10Hz, which is much lower than the line frequency of 50Hz, then actually I am having luck, because I can use a low pass filter of say 10Hz or 20Hz, to filter all high frequency noises including 50Hz line noise.
So I used the muzique calculator again, and got the RC pair of 2.2uF and 7K to start my low pass filter trials and errors design.
.END
Simple R-C Filter Cutoff Calculator - Muzique.com Lab Notebook
http://www.muzique.com/schem/filter.htm
The simple R-C filter rolls off the frequency response at 6 dB per octave above the cutoff frequency. The position of the resistor and capacitor are switched to change from low pass to high pass but the same calculation applies to both filters. This calculator assumes a low source impedance, which usually is small enough that it does not change the corner frequency.
Input any two values in the form boxes and hit the Calculate button to find the remaining value.
Capacitor (uF) = 2.2 uF, Corner Frequency (Hz) = 10 => Resistor (ohms) - 7.238K
.END2
So I used the muzique calculator again, and got the RC pair of 2.2uF and 7K to start my low pass filter trials and errors design.
.END
Simple R-C Filter Cutoff Calculator - Muzique.com Lab Notebook
http://www.muzique.com/schem/filter.htm
The simple R-C filter rolls off the frequency response at 6 dB per octave above the cutoff frequency. The position of the resistor and capacitor are switched to change from low pass to high pass but the same calculation applies to both filters. This calculator assumes a low source impedance, which usually is small enough that it does not change the corner frequency.
Input any two values in the form boxes and hit the Calculate button to find the remaining value.
Capacitor (uF) = 2.2 uF, Corner Frequency (Hz) = 10 => Resistor (ohms) - 7.238K
.END2
High pass filter problem
So I am designing a low pass filter to filter out the high frequency noise from the ECG wave signal. But I have no idea of the frequency of the ECG wave. I think I might make a rough guess to start trials and errors.
I looked at the the MIT Open Course ECG slide again and found that there are 6 wave signals in a period of about 17mm or (17mm * 0.04 second/mm = 17 * 40 mS = 700mS).
In other words, the average frequency of the 7 waves would be roughly 7 / 700mS = (7 * 1000) / 700
= 10Hz!
Now I seem to be in big trouble, because if the ECG signal is only 10Hz, then the high pass filter I made earlier must also have filter away the ECG signal, together with the 50Hz line frequency.
.END
High pass filter testing notes
So after a couple of trials and errors, I found using 0.1uF and 1K a good pair of RC values to filter the 50Hz line frequency nose. (The blue channel is input, yellow trace output of high pass filter.) I also found that a 200VAC LED lamp creates a lot of high frequency noise.
Anyway, the simple RC based high pass filter is more or less working. Now I think I need to make a high low pass filter to filter the high frequency noise, and hopefully get the ECG signal I want.
.END
High pass 50Hz line frequency filter design notes
I found the ECG signal picking up a heavy 50Hz noise. So I googled a simple RC 50Hz high pass filter circuit to try to get rid of the annoying line noise.
.END
Maxim Tutorial 733 A Filter Primer Oct 06, 2008
http://www.maximintegrated.com/app-notes/index.mvp/id/733
http://pdfserv.maximintegrated.com/en/an/AN733.pdf
Abstract: This comprehensive article covers all aspects of analog filters. It first addresses the basic types: first- and second-order filters, highpass and lowpass filters, notch and all-pass filters, and high-order filters. The tutorial then explains the characteristics of the different implementations, such as Butterworth filters, Chebychev filters, Bessel filters, elliptic filters, state-variable filters, and switched-capacitor filters.
Introduction
Ease of use makes integrated, switched-capacitor filters attractive for many applications. This article helps you prepare for such designs by describing the filter products and explaining the concepts that govern their operation.
Starting with a simple integrator, we first develop an intuitive approach to active filters in general. We then introduce practical realizations such as the state-variable filter and its implementation in switched-capacitor form. Specific integrated filters described here include Maxim's MAX7400 family of higher-order switched-capacitor filters.
.END2
Normal ECG wave period and amplitude
ECG Wave timing
Biomedical Signal and Image Processing
http://ocw.mit.edu/courses/health-sciences-and-technology/hst-582j-biomedical-signal-and-image-processing-spring-2007/index.htm
Slides: Introduction to Clinical Electrocardiography (Courtesy of Andrew Reisner, MD. Used with permission.)
http://ocw.mit.edu/courses/health-sciences-and-technology/hst-582j-biomedical-signal-and-image-processing-spring-2007/lecture-notes/l3_ecg_reisner.pdf
Cite as: Andrew Reisner. Course materials for HST.582J / 6.555J / 16.456J, Biomedical Signal and Image Processing, Spring 2007. MIT OpenCourseWare
MIT OpenCourseWare http://ocw.mit.edu HST.582J / 6.555J / 16.456J Biomedical Signal and Image Processing Spring 2007
For information about citing these materials or our Terms of Use, visit: http://ocw.mit.edu/terms.
.END
ECG noise removing methods learning notes
Biomedical Signal and Image Processing
http://ocw.mit.edu/courses/health-sciences-and-technology/hst-582j-biomedical-signal-and-image-processing-spring-2007/syllabus/
Overview
This course presents the fundamentals of digital signal processing with particular emphasis on problems in biomedical research and clinical medicine. It covers principles and algorithms for processing both deterministic and random signals. Topics include data acquisition, imaging, filtering, coding, feature extraction, and modeling. The focus of the course is a series of labs that provide practical experience in processing physiological data, with examples from cardiology, speech processing, and medical imaging. The labs are done on the MIT Server in MATLAB® during weekly lab sessions that take place in an electronic classroom.
Lecture Topics
Biomedical Signals and Images
ECG: Cardiac electrophysiology, relation of electrocardiogram (ECG) components to cardiac events, clinical applications. Guest lecture.
...
Imaging Modalities: Survey of major modalities for medical imaging: ultrasound, X-ray, CT, MRI, PET, and SPECT.
...
MRI: Physics and signal processing for magnetic resonance imaging. Guest lecture.
...
DFT: The discrete Fourier transform and its properties, the fast Fourier transform (FFT), the overlap-save algorithm, digital filtering of continuous-time signals.
...
PDFs: Introduction to random variables and probability density functions (PDFs).
Classification: Bayes' rule, detection, statistical classification.
...
ECG Filtering and Frequency Analysis of the Electrogram Design filters to remove noise from electrocardiogram (ECG) signals and then design a system to detect life-threatening ventricular arrhythmias. The detector is tested on normal and abnormal ECG signals. (3 weeks - Greenberg)
...
.END
http://ocw.mit.edu/courses/health-sciences-and-technology/hst-582j-biomedical-signal-and-image-processing-spring-2007/syllabus/
Overview
This course presents the fundamentals of digital signal processing with particular emphasis on problems in biomedical research and clinical medicine. It covers principles and algorithms for processing both deterministic and random signals. Topics include data acquisition, imaging, filtering, coding, feature extraction, and modeling. The focus of the course is a series of labs that provide practical experience in processing physiological data, with examples from cardiology, speech processing, and medical imaging. The labs are done on the MIT Server in MATLAB® during weekly lab sessions that take place in an electronic classroom.
Lecture Topics
Biomedical Signals and Images
ECG: Cardiac electrophysiology, relation of electrocardiogram (ECG) components to cardiac events, clinical applications. Guest lecture.
...
Imaging Modalities: Survey of major modalities for medical imaging: ultrasound, X-ray, CT, MRI, PET, and SPECT.
...
MRI: Physics and signal processing for magnetic resonance imaging. Guest lecture.
...
DFT: The discrete Fourier transform and its properties, the fast Fourier transform (FFT), the overlap-save algorithm, digital filtering of continuous-time signals.
...
PDFs: Introduction to random variables and probability density functions (PDFs).
Classification: Bayes' rule, detection, statistical classification.
...
ECG Filtering and Frequency Analysis of the Electrogram Design filters to remove noise from electrocardiogram (ECG) signals and then design a system to detect life-threatening ventricular arrhythmias. The detector is tested on normal and abnormal ECG signals. (3 weeks - Greenberg)
...
.END
2013-09-29
ECG waving timing
I wikied to find that the PQRST signal is roughly 500mS. That signal I captured earlier was 500 uS long, 2 orders of magnitude too short. The amplitude is also very wrong. Obviously I made a very wrong guess.
Summary Of Normal Durations And Amplitudes Of The EKG - EKG Basics # 2
http://faculty.etsu.edu/arnall/www/public_html/heartlung/lectures/ekg2.html
1mm = 40ms duration or 0.1mV amplitude
P Waves
Normal Duration : 2.5 mm ( * 40 ms = 100 ms)
Normal Amplitude : 2.3 mm (* 0.1 mV = 0.23 mv)
PR Intervals
Normal Duration : 3-5 mm
Q Waves
Normal Duration : .75 mm - 1.0 mm
Normal Amplitude : <25% of the R wave amplitude
QRS Complex
Normal Duration : <3.0 mm
Normal Amplitude : Variable
ST Segment
Normal Amplitude : 1-2 mm
Normal Duration : 2-3 mm
T Wave
Normal Duration : 2 mm
Normal Amplitude : < 5 mm in Limb Leads & < 10 mm in Precordial Leads
.END
ECG Lead II measurement notes
Now I am using the ATTEN ADS1102CAL+ (100MHz, 1G sample, 7 inch wide screen, RMB1,850 2013jun) to measure the Lead II voltage.
By a couple of trials and errors, I seemed to have captured my own heart signal at the following setting.
Ch 1, Ch 2, 50 mV,
M 10 uS.
I need to check the timing to make sure if what I am seeing is indeed the P, QRS, and T wave.
Very roughly, R wave is 100 mV, and P,QRS, and T total time is 50 uS. So I need to google again.
. END
ECG 10 electrode / 12 lead definition
ECG 10 electrode / 12 lead definition
References
1. Electrocardiographic Leads, I Introduction, Mcfee and Johnson, 1953
[Just read Page 557]
http://circ.ahajournals.org/content/8/4/554
http://circ.ahajournals.org/content/8/4/554.full.pdf+html
2. Principles and Practice of Bioelectric and Biomagnet Fields by Malmivuo and Plonsey, 1995
[Just read Chapter 15 The 12-Lead ECG System]
http://www.bem.fi/book/15/15.htm
.END
Open source ECG hardware turorial and example
Olimex Arduino compatible ECG board tutorial
http://cs.curs.pub.ro/wiki/pm/eestec/3
license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Open Source Prosthetic Arm using Olimex ECG/EMG board - 2013jun16 (Open source project)
http://www.youtube.com/watch?v=ZUIqR2JodlY
.END
http://cs.curs.pub.ro/wiki/pm/eestec/3
license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Open Source Prosthetic Arm using Olimex ECG/EMG board - 2013jun16 (Open source project)
http://www.youtube.com/watch?v=ZUIqR2JodlY
.END
My Olimex EKG Board Order
My Olimex EKG Board Order
From: ...@element14.com To: tlfong01@... Subject: ... order Date: Fri, 27 Sep 2013
Element14 Ref: 15266320 Customer Order: 27/09/2013 13:23 Date of order: 9/27/13 1:27 PM
What you ordered:
2144343 ... BOARD, ECG / EMG ARDUINO SHIELD SHIELD-EKG-EMG OLIMEX [HKD248.58]
http://hk.element14.com/olimex/shield-ekg-emg/board-ecg-emg-arduino-shield/dp/2144343
2144344 ... ELECTRODES, PASSIVE, ECG/EMG SHIELD SHIELD-EKG-EMG-PA OLIMEX [HKD126.69]
http://hk.element14.com/olimex/shield-ekg-emg-pa/electrodes-passive-ecg-emg-shield/dp/2144344
...
.END
From: ...@element14.com To: tlfong01@... Subject: ... order Date: Fri, 27 Sep 2013
Element14 Ref: 15266320 Customer Order: 27/09/2013 13:23 Date of order: 9/27/13 1:27 PM
What you ordered:
2144343 ... BOARD, ECG / EMG ARDUINO SHIELD SHIELD-EKG-EMG OLIMEX [HKD248.58]
http://hk.element14.com/olimex/shield-ekg-emg/board-ecg-emg-arduino-shield/dp/2144343
2144344 ... ELECTRODES, PASSIVE, ECG/EMG SHIELD SHIELD-EKG-EMG-PA OLIMEX [HKD126.69]
http://hk.element14.com/olimex/shield-ekg-emg-pa/electrodes-passive-ecg-emg-shield/dp/2144344
...
.END
Olimex Open Source Hardware Arduino compatible EKG shield learning notes
SHIELD-EKG-EMG - OPEN SOURCE HARDWARE ELECTROCARDIOGRAPHY ELECTROMYOGRAPHY SHIELD FOR DUINOMITE, PINGUINO, MAPLE, ARDUINO LIKE DEVELOPMENT BOARDS
https://www.olimex.com/Products/Duino/Shields/SHIELD-EKG-EMG/
This is an EKG/EMG shield which allows Arduino like boards to capture Electrocardiography Electromiography signals. The shield opens new possibilities to experiment with bio feedback. You can monitor your heartbeat and log your pulse, recognize gestures by monitoring and analize the muscule activity as it is done in this project.
FEATURES
Stackable headers up to 6 channels may be stacked and wired to A0-A6 analogue inputs
Calibration signal generation by D4/D9 digital output
Precise Trimmer potentiometer for calibration
Input connector for normal or Active electrodes
Works with both 3.3V and 5V Arduino boards
DOCUMENTS
SHIELD-EKG-EMG user's manual revision E
HARDWARE
SHIELD-EKG-EMG schematic in PDF format released under Creative Commons Attribution-Share Alike 3.0 United States License
SHIELD-EKG-EMG schematic and board in Eagle format released under Creative Commons Attribution-Share Alike 3.0 United States License
SOFTWARE
//********************************************************************/
//* Demo program for: */
//* Board: SHIELD-EKG/EMG + PIC32-PINGUINO-OTG/MX220 */
//* Manufacture: OLIMEX */
//* COPYRIGHT (C) 2012 */
//* Designed by: Penko Todorov Bozhkov */
//* Module Name: Example */
//* File Name: ShieldEkgEmgDemoWithPIC32-Pinguino-OTG_MX220.pde */
//* Revision: Rev.A */
//* History: Added support for PIC32-PINGUINO-MX220 */
//* Date: 09.05.2012 */
//* Built with PinguinoX.3 rev399 */
//*********************************************************************/
/*************************************************************************************
Purpose of this programme is to give you an easy way to
connect PIC32-PINGUINO-OTG to ElectricGuru(TM), see:
http://www.realization.org/page/topics/electric_guru.htm
where you'll be able to observe yours own EKG or EMG signal.
It is based on:
**************************************************************************************
* ModularEEG firmware for one-way transmission, v0.5.4-p2
* Copyright (c) 2002-2003, Joerg Hansmann, Jim Peters, Andreas Robinson
* License: GNU General Public License (GPL) v2
**************************************************************************************
For proper communication packet format given below have to be supported:
/////////////////////////////////////////////////////
////////// Packet Format Version 2 ////////////
/////////////////////////////////////////////////////
// 17-byte packets are transmitted from PIC32-PINGUINO-OTG at 256Hz,
// using 1 start bit, 8 data bits, 1 stop bit, no parity, 57600 bits per second.
// Minimial transmission speed is 256Hz * sizeof(PIC32-PINGUINO-OTG_packet) * 10 = 43520 bps.
struct PIC32-PINGUINO-OTG_packet
{
uint8_t sync0; // = 0xa5
uint8_t sync1; // = 0x5a
uint8_t version; // = 2 (packet version)
uint8_t count; // packet counter. Increases by 1 each packet.
uint16_t data[6]; // 10-bit sample (= 0 - 1023) in big endian (Motorola) format.
uint8_t switches; // State of PD5 to PD2, in bits 3 to 0. -> This is not used in current application. The value is set to 0x01
};
*/
/***************************************************************************************/
// In the source below was used the example given by:
// Jean-Pierre MANDON 2011
// Using interrupt with Pinguino32
// http://blog.pinguino.cc/
#include <__cdc.c>
#include <interrupt.c>
extern void putUSBUSART(char *data, char Length);
// All definitions
#if defined(PIC32_PINGUINO_220)
#define SDCD 0
#define SDWP 1
#elif defined(PIC32_PINGUINO_OTG)
#define LED2_ON TRISD &= (~0x0002); LATD |= 0x0002;
#define LED2_OFF TRISD &= (~0x0002); LATD &= (~0x0002);
#define LED2_TOGG TRISD &= (~0x0002); LATD ^= 0x0002;
#else
#error: "The selected board is not supported!!!"
#endif
#define LED1 13
#define CAL_SIG 9
#define NUMCHANNELS 6
#define HEADERLEN 4
#define PACKETLEN (NUMCHANNELS * 2 + HEADERLEN + 1)
// Global constants and variables
volatile unsigned char TXBuf[PACKETLEN]; //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
/****************************************************/
/* 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); }
}
void ISR_wrapper_vector_4(void) __attribute__ ((section (".vector_4")));
// Put the ISR_wrapper in the good place
void ISR_wrapper_vector_4(void) { Tmr1Interrupt(); }
// ISR_wrapper will call the Tmr1Interrupt()
void Tmr1Interrupt(void) __attribute__ ((interrupt));
// Tmr1Interrupt is declared as an interrupt routine
/*********************************************************/
/* Function name: Tmr1Interrupt */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: All actions are done here. */
/*********************************************************/
void Tmr1Interrupt(void)
{
if (IFS0bits.T1IF) // Timer Interrupt flag
{
TMR1=0; // reset the timer register
IFS0CLR=0x10; // Clear the timer interrupt flag
toggle_LED1(); // Toggle LED1 with frequency 250/2 = 125Hz. LED1 can be used to monitor Timer1 interrupt frequency!
//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
putUSBUSART(TXBuf,17);
TXBuf[3]++; // increment packet counter
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
#if defined(PIC32_PINGUINO_OTG)
LED2_TOGG; // Show CAL_SIG frequency via LED2 blinking
#endif
}
}
}
/*********************************************************/
/* Function name: init_timer1 */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: configure timer1 operation. */
/*********************************************************/
void init_timer1(void)
{
IntConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR); // interrupt mode (interrupt.c)
T1CON=0; // reset timer 1 configuration
TMR1=0; // reset timer 1 counter register
#if defined(PIC32_PINGUINO_OTG)
PR1=20000; // define the preload register
//PIC32_PINGUINO_OTG: 40000000(Peripheral Clk)/8(Prescaller)/20000(PR1) = 250Hz
#elif defined(PIC32_PINGUINO_220)
PR1=10000; // define the preload register
//PIC32_PINGUINO_220: 20000000(Peripheral Clk)/8(Prescaller)/10000(PR1) = 250Hz
#endif
IPC1SET=0x7; // select interrupt priority and sub-priority
IFS0CLR=0x10; // clear interrupt flag
IEC0SET=0x10; // enable timer 1 interrupt
T1CONSET=0x8010; // start timer 1 and set prescaler
}
/*********************************************************/
/* Function name: setup */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: Initializes all peripherals */
/*********************************************************/
void setup()
{
init_timer1();
pinMode(LED1, OUTPUT);
pinMode(CAL_SIG, OUTPUT);
//Write init whole packet
TXBuf[0] = 0xa5; //Sync 0
TXBuf[1] = 0x5a; //Sync 1
TXBuf[2] = 0x02; //Protocol version
TXBuf[3] = 0x00; //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
}
/*********************************************************/
/* Function name: loop */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: Do nothing. */
/*********************************************************/
void loop()
{
asm("NOP");
}
.END
https://www.olimex.com/Products/Duino/Shields/SHIELD-EKG-EMG/
This is an EKG/EMG shield which allows Arduino like boards to capture Electrocardiography Electromiography signals. The shield opens new possibilities to experiment with bio feedback. You can monitor your heartbeat and log your pulse, recognize gestures by monitoring and analize the muscule activity as it is done in this project.
FEATURES
Stackable headers up to 6 channels may be stacked and wired to A0-A6 analogue inputs
Calibration signal generation by D4/D9 digital output
Precise Trimmer potentiometer for calibration
Input connector for normal or Active electrodes
Works with both 3.3V and 5V Arduino boards
DOCUMENTS
SHIELD-EKG-EMG user's manual revision E
HARDWARE
SHIELD-EKG-EMG schematic in PDF format released under Creative Commons Attribution-Share Alike 3.0 United States License
SHIELD-EKG-EMG schematic and board in Eagle format released under Creative Commons Attribution-Share Alike 3.0 United States License
SOFTWARE
//********************************************************************/
//* Demo program for: */
//* Board: SHIELD-EKG/EMG + PIC32-PINGUINO-OTG/MX220 */
//* Manufacture: OLIMEX */
//* COPYRIGHT (C) 2012 */
//* Designed by: Penko Todorov Bozhkov */
//* Module Name: Example */
//* File Name: ShieldEkgEmgDemoWithPIC32-Pinguino-OTG_MX220.pde */
//* Revision: Rev.A */
//* History: Added support for PIC32-PINGUINO-MX220 */
//* Date: 09.05.2012 */
//* Built with PinguinoX.3 rev399 */
//*********************************************************************/
/*************************************************************************************
Purpose of this programme is to give you an easy way to
connect PIC32-PINGUINO-OTG to ElectricGuru(TM), see:
http://www.realization.org/page/topics/electric_guru.htm
where you'll be able to observe yours own EKG or EMG signal.
It is based on:
**************************************************************************************
* ModularEEG firmware for one-way transmission, v0.5.4-p2
* Copyright (c) 2002-2003, Joerg Hansmann, Jim Peters, Andreas Robinson
* License: GNU General Public License (GPL) v2
**************************************************************************************
For proper communication packet format given below have to be supported:
/////////////////////////////////////////////////////
////////// Packet Format Version 2 ////////////
/////////////////////////////////////////////////////
// 17-byte packets are transmitted from PIC32-PINGUINO-OTG at 256Hz,
// using 1 start bit, 8 data bits, 1 stop bit, no parity, 57600 bits per second.
// Minimial transmission speed is 256Hz * sizeof(PIC32-PINGUINO-OTG_packet) * 10 = 43520 bps.
struct PIC32-PINGUINO-OTG_packet
{
uint8_t sync0; // = 0xa5
uint8_t sync1; // = 0x5a
uint8_t version; // = 2 (packet version)
uint8_t count; // packet counter. Increases by 1 each packet.
uint16_t data[6]; // 10-bit sample (= 0 - 1023) in big endian (Motorola) format.
uint8_t switches; // State of PD5 to PD2, in bits 3 to 0. -> This is not used in current application. The value is set to 0x01
};
*/
/***************************************************************************************/
// In the source below was used the example given by:
// Jean-Pierre MANDON 2011
// Using interrupt with Pinguino32
// http://blog.pinguino.cc/
#include <__cdc.c>
#include <interrupt.c>
extern void putUSBUSART(char *data, char Length);
// All definitions
#if defined(PIC32_PINGUINO_220)
#define SDCD 0
#define SDWP 1
#elif defined(PIC32_PINGUINO_OTG)
#define LED2_ON TRISD &= (~0x0002); LATD |= 0x0002;
#define LED2_OFF TRISD &= (~0x0002); LATD &= (~0x0002);
#define LED2_TOGG TRISD &= (~0x0002); LATD ^= 0x0002;
#else
#error: "The selected board is not supported!!!"
#endif
#define LED1 13
#define CAL_SIG 9
#define NUMCHANNELS 6
#define HEADERLEN 4
#define PACKETLEN (NUMCHANNELS * 2 + HEADERLEN + 1)
// Global constants and variables
volatile unsigned char TXBuf[PACKETLEN]; //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
/****************************************************/
/* 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); }
}
void ISR_wrapper_vector_4(void) __attribute__ ((section (".vector_4")));
// Put the ISR_wrapper in the good place
void ISR_wrapper_vector_4(void) { Tmr1Interrupt(); }
// ISR_wrapper will call the Tmr1Interrupt()
void Tmr1Interrupt(void) __attribute__ ((interrupt));
// Tmr1Interrupt is declared as an interrupt routine
/*********************************************************/
/* Function name: Tmr1Interrupt */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: All actions are done here. */
/*********************************************************/
void Tmr1Interrupt(void)
{
if (IFS0bits.T1IF) // Timer Interrupt flag
{
TMR1=0; // reset the timer register
IFS0CLR=0x10; // Clear the timer interrupt flag
toggle_LED1(); // Toggle LED1 with frequency 250/2 = 125Hz. LED1 can be used to monitor Timer1 interrupt frequency!
//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
putUSBUSART(TXBuf,17);
TXBuf[3]++; // increment packet counter
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
#if defined(PIC32_PINGUINO_OTG)
LED2_TOGG; // Show CAL_SIG frequency via LED2 blinking
#endif
}
}
}
/*********************************************************/
/* Function name: init_timer1 */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: configure timer1 operation. */
/*********************************************************/
void init_timer1(void)
{
IntConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR); // interrupt mode (interrupt.c)
T1CON=0; // reset timer 1 configuration
TMR1=0; // reset timer 1 counter register
#if defined(PIC32_PINGUINO_OTG)
PR1=20000; // define the preload register
//PIC32_PINGUINO_OTG: 40000000(Peripheral Clk)/8(Prescaller)/20000(PR1) = 250Hz
#elif defined(PIC32_PINGUINO_220)
PR1=10000; // define the preload register
//PIC32_PINGUINO_220: 20000000(Peripheral Clk)/8(Prescaller)/10000(PR1) = 250Hz
#endif
IPC1SET=0x7; // select interrupt priority and sub-priority
IFS0CLR=0x10; // clear interrupt flag
IEC0SET=0x10; // enable timer 1 interrupt
T1CONSET=0x8010; // start timer 1 and set prescaler
}
/*********************************************************/
/* Function name: setup */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: Initializes all peripherals */
/*********************************************************/
void setup()
{
init_timer1();
pinMode(LED1, OUTPUT);
pinMode(CAL_SIG, OUTPUT);
//Write init whole packet
TXBuf[0] = 0xa5; //Sync 0
TXBuf[1] = 0x5a; //Sync 1
TXBuf[2] = 0x02; //Protocol version
TXBuf[3] = 0x00; //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
}
/*********************************************************/
/* Function name: loop */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: Do nothing. */
/*********************************************************/
void loop()
{
asm("NOP");
}
.END
Subscribe to:
Posts (Atom)