I am trying to graph ekg on glcd using the mega 2560 and the olimex ekg emg shield (SHIELD-EKG-EMG - Open Source Hardware Board). The company provides a base code that is supposed to interface with a windows program (ElecGuru), but I am having a hard time understanding it. How would I go about graphing the data on an lcd?
Provided code by Olimex:
/**********************************************************/
#include <compat/deprecated.h>
#include <FlexiTimer2.h>
//http://www.arduino.cc/playground/Main/FlexiTimer2
#include <TimerOne.h>
//http://arduino.cc/playground/Code/Timer1
// 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 PWM_OUT 9 // Number of pin used for generating CAL_SIG
#define PWMFREQ 10
#define LED1 13
// Global constants and variables
char const channel_order[]= { 0, 1, 2, 3, 4, 5 };
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.
//~~~~~~~~~~
// 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: 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
//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
// ADC
// Timings for sampling of one 10-bit AD-value:
// XTAL = 16000000MHz
// prescaler > ((XTAL / 200kHz) = 80 =>
// prescaler = 128 (ADPS2 = 1, ADPS1 = 1, ADPS0 = 1)
// ADCYCLE = XTAL / prescaler = 125000Hz or 8 us/cycle
// 14 (single conversion) cycles = 112 us
// 26 (1st conversion) cycles = 208 us
outb(ADMUX, 0); //Select channel 0
outb(ADCSRA, ((1<<ADPS2) | (1<<ADPS1)| (1<<ADPS0))); //Prescaler = 128, free running mode = off, interrupts off.
sbi(ADCSRA, ADIF); //Reset any pending ADC interrupts
sbi(ADCSRA, ADEN); //Enable the ADC
// Serial Port
outb(UBRR0, 16); //Set speed to 57600 bps
outb(UCSR0B, (1<<TXEN0)); //Enable USART Transmitter.
// Timer1
// It's used for calibration signal generation: CAL_SIG via PWM.
// CAL_SIG is used like reference signal when setting-up SHIELD-EKG/EMG's channel gain
// During normal operation this signal is not required so it can be disabled with uncommenting te row below!
pinMode(PWM_OUT, OUTPUT); //Set PWM_OUT direction
digitalWrite(PWM_OUT,LOW); //Set PWM_OUT state
Timer1.initialize((1000000/(PWMFREQ))); // initialize timer1, and set a 1/10 second period = 10Hz
Timer1.pwm(PWM_OUT, 512); // setup pwm on pin 9, 50% duty cycle
//Timer1.disablePwm(PWM_OUT); // Uncomment if CAL_SIG is not requiered
// Timer2
// Timer2 is used for setting ADC sampling frequency.
FlexiTimer2::set(TIMER2VAL, Timer2_Overflow_ISR);
FlexiTimer2::start();
// MCU sleep mode = idle.
outb(MCUCR,(inp(MCUCR) | (1<<SE)) & (~(1<<SM0) | ~(1<<SM1) | ~(1<<SM2)));
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();
CurrentCh = 0;
// Write header and footer:
// Increase packet counter (fourth byte in header)
TXBuf[3]++;
//Get state of switches on PD2..5, if any (last byte in packet).
TXBuf[2 * NUMCHANNELS + HEADERLEN] = (inp(PIND) >> 2) &0x0F;
cbi(UCSR0B, UDRIE0); //Ensure Data Register Empty Interrupt is disabled.
sbi(ADCSRA, ADIF); //Reset any pending ADC interrupts
sbi(ADCSRA, ADIE); //Enable ADC interrupts.
sbi(ADCSRA, ADSC) ; // Start conversion!!!
//Next interrupt will be ISR(ADC_vect)
}
/****************************************************/
/* Function name: ISR(ADC_vect) */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: Reads ADC's current selected channel */
/* and stores its value into TXBuf. When */
/* TXBuf is full, it starts sending. */
/****************************************************/
ISR(ADC_vect)
{
volatile unsigned char i;
i = 2 * CurrentCh + HEADERLEN;
TXBuf[i+1] = inp(ADCL);
TXBuf[i] = inp(ADCH);
CurrentCh++;
if (CurrentCh < NUMCHANNELS)
{
outb(ADMUX, (channel_order[CurrentCh])); //Select the next channel.
sbi(ADCSRA, ADSC) ; //Start conversion!!!
}
else
{
outb(ADMUX, channel_order[0]); //Prepare next conversion, on channel 0.
cbi(ADCSRA, ADIE); //Disable ADC interrupts to prevent further calls to ISR(ADC_vect).
outb(UDR0, TXBuf[0]); //Send first Packet's byte: Sync 0
sbi(UCSR0B, UDRIE0); //USART Data Register Empty Interrupt Enable
TXIndex = 1; //Next interrupt will be ISR(USART_UDRE_vect)
}
}
/****************************************************/
/* Function name: ISR(USART_UDRE_vect) */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: Sends remaining part of the Packet. */
/****************************************************/
ISR(USART_UDRE_vect){
outb(UDR0, TXBuf[TXIndex]); //Send next byte
TXIndex++;
if (TXIndex == PACKETLEN) //See if we're done with this packet
{
cbi(UCSR0B, UDRIE0); //USART Data Register Empty Interrupt Disable
//Next interrupt will be Timer2_Overflow_ISR()
}
}
/****************************************************/
/* Function name: loop */
/* Parameters */
/* Input : No */
/* Output : No */
/* Action: Puts MCU into sleep mode. */
/****************************************************/
void loop() {
__asm__ __volatile__ ("sleep");
}