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
No comments:
Post a Comment