Pages: [1]   Go Down
Author Topic: Exiting a void loop  (Read 936 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am student working on a project to develop a patient simulator using the Arduino platform. We have developed a code to eliminate the interrupt function while using PWM in order to insure constant signal output. However, our goal is to use a button to not only exit the current loop, but also switch between arrays of data representing different physiologic signals. We are having issues with exiting the loop. We are using two simple RC filters to create a constant signal. What are we doing wrong/ what do we need to do? Note: the signal data had to be reduced to fit into the allowed length of the message

Code:
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

const int EKG_length=1001;
const int Respiration_length = 1045;
const unsigned char EKG_data[] PROGMEM = {0x14,0x13,0x13,0x13,0x13,0x12,0x12,0x13,0x13,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x13,0x13,0x13,0x14,0x14,0x15,0x15,0x15,0x15,0x16,0x16,0x16,0x15,0x15,0x15,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x14,0x14,0x14,0x14,0x13,0x13,0x13,0x13,0x13,0x14,0x14,0x15,0x15,0x16,0x16,0x16,0x16,0x16,0x16,0x15,0x15,0x14,0x14,0x14,0x14,0x14,0x14,0x15,0x15,0x15,0x16,0x16,0x16,0x16,0x16,0x15,0x15,0x15,0x15,0x15,0x15,0x16,0x16,0x17,0x17,0x17,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x19,0x19,0x1A,0x1A,0x1B,0x1B,0x1B,0x1C,0x1C,0x1B,0x1B,0x1B,0x1A,0x1A,0x19,0x19,0x19,0x19,0x1A,0x1A};
const unsigned char Respiration_data[] PROGMEM = {0xE9,0xE9,0xE9,0xE9,0xEB,0xEB,0xEC,0xEC,0xEC,0xEA,0xEA,0xEB,0xEB,0xEB,0xEB,0xEB,0xEC,0xEC,0xEC,0xEB,0xEB,0xEC,0xED,0xED,0xED,0xED,0xED,0xED,0xED,0xEF,0xEF,0xED,0xED,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xED,0xED,0xEE,0xEE,0xEE,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEE,0xEE,0xEE,0xB7,0x97,0x7B,0x65,0x52,0x46,0x3A,0x30,0x28,0x21,0x1A,0x16,0x13,0x10,0xD,0xA,0x8,0x8,0x6,0x6,0x7,0x6,0x5,0x5,0x4,0x2,0x2,0x3,0x3,0x3,0x0,0x0,0x3,0x3,0x2,0x3,0x3,0x1,0x1,0x1,0x0,0x0,0x3,0x3,0x3,0x4,0x4,0x2,0x2,0x2,0x1,0x2,0x2,0x6,0xC,0x17,0x23,0x30,0x3F,0x4E,0x5C,0x6A,0x78,0x85,0x90,0x9B,0xA4,0xAB,0xB2,0xB8,0xBE,0xC3,0xC6,0xCA,0xCD,0xD1,0xD4,0xD7};
int outputPin = 6; // (PCINT22/OC0A/AIN0)PD6, Arduino Digital Pin 6
int buttonPin = 2;
//int buttonPushCount = 0;
int buttonCurrentCount = 0;
//int buttonLastCount = 0;
volatile uint16_t sample; // This is called at SAMPLE_RATE kHz to load the next sample.
ISR(TIMER1_COMPA_vect)
{
if (sample >= EKG_length)
{
sample = -1;
}
else
{
OCR0A = pgm_read_byte(&EKG_data[sample]);
}
++sample;
}
void startPlayback(int samp_rate, int array_num)
{
pinMode(outputPin, OUTPUT);
// Set Timer 0 Fast PWM Mode (Section 14.7.3)
// WGM = 0b011 = 3 (Table 14-8)
// TOP = 0xFF, update OCR0A register at BOTTOM
TCCR0A |= _BV(WGM01) | _BV(WGM00);
TCCR0B &= ~_BV(WGM02);
// Do non-inverting PWM on pin OC0A, arduino digital pin 6
// COM0A = 0b10, clear OC0A pin on compare match,
// set 0C0A pin at BOTTOM (Table 14-3)
TCCR0A = (TCCR0A | _BV(COM0A1)) & ~_BV(COM0A0);
// COM0B = 0b00, OC0B disconnected (Table 14-6)
TCCR0A &= ~(_BV(COM0B1) | _BV(COM0B0));
// No prescaler, CS = 0b001 (Table 14-9)
TCCR0B = (TCCR0B & ~(_BV(CS02) | _BV(CS01))) | _BV(CS00);
// Set initial pulse width to the first sample.
if(array_num == 1)
{
OCR0A = pgm_read_byte(&EKG_data[0]);
}
if (array_num == 2)
{
OCR0A = pgm_read_byte(&Respiration_data[0]);
}
// Set up Timer 1 to send a sample every interrupt.
cli(); // disable interrupts
// Set CTC mode (Section 15.9.2 Clear Timer on Compare Match)
// WGM = 0b0100, TOP = OCR1A, Update 0CR1A Immediate (Table 15-4)
// Have to set OCR1A *after*, otherwise it gets reset to 0!
TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
// No prescaler, CS = 0b001 (Table 15-5)
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set the compare register (OCR1A).
// OCR1A is a 16-bit register, so we have to do this with
// interrupts disabled to be safe.
OCR1A = F_CPU / samp_rate; // 16e6 / 8000 = 2000
// Enable interrupt when TCNT1 == OCR1A (p.136)
TIMSK1 |= _BV(OCIE1A);
sample = 0;
sei(); // enable interrupts
}
void setup()
{
pinMode(buttonPin, INPUT);
Serial.begin(9600);
}
void loop()
{
// buttonCurrentCount = digitalRead(buttonPin);
// if (buttonCurrentCount != buttonLastCount)
// {
if (digitalRead(buttonPin) == HIGH)
{
buttonCurrentCount++;
}
// }
// buttonLastCount = buttonCurrentCount;

if(buttonCurrentCount == 1)
{
startPlayback(500, 1);
}

if(buttonCurrentCount == 2)
{
startPlayback(500, 2);
}
Serial.println(buttonCurrentCount);
}
Logged

0
Offline Offline
Faraday Member
**
Karma: 19
Posts: 3420
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

RC filters will smooth the signal are most probably not sufficient for debouncing. Flip flops are the way to go.

And learn how to indent your code.
Logged

Check out my experiments http://blog.blinkenlight.net

UK
Offline Offline
Shannon Member
****
Karma: 184
Posts: 11197
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It looks as if you're using a timer interrupt to schedule the sample outputs, is that right?

The code seems to assume that both sample arrays are the same length. Is that a valid assumption?

There are a few anomalies in the code. When you reach the end of the array of values, you skip a sample, Is that deliberate? It would only take minor changes to your ISR to avoid that.

In ISR you always output the values from EKG_data regardless of the value of sample. Shouldn't you use sample to select which array to take your values from?

You have code in loop() to detect button transitions commented out. You need to detect transitions, otherwise your sketch will keep responding to the button press each time round loop(). I suspect it's doubly broken at the moment, because it looks as if it would increment buttonCurrentCount past 2 and then never reset it back to a valid value.

In startPlayback() you ignore the value of array_num and always set sample to 0.

In your situation what I'd be inclined to do is use a global variable to point to the current data array, and also global variables recording the length of the array and the current index; have a function similar to startPlayback() which disables interrupts, updates the global variables and reenables interrupts.

Are you outputting these sample values at a particularly high rate, or something, that needs you to access the I/O ports directly instead of going through the Arduino API?
« Last Edit: November 16, 2012, 06:19:12 pm by PeterH » Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

We have kind of changed the path in which we hope to go now. We are hoping to press the button a given amount of times in correlation to a given array. After the buttoncount has been set we would then advance into the uninterrupted loop using the chosen array. We think this will be easier to program and implement. We would end the program using the reset button built into the board, and a new array would be chosen from there.

The arrays will not be the same length, especially as we add more physiological signals to the code. We are hoping to have the arduino completely untethered to the computer which is why we are trying to store all signals and programming on the arduino and not change them on a PC. I do not believe the skipping of the sample is deliberate, you'll have to forgive us we are fairly new at programming and are trying to get by with the available information on the internet.

What is the best way to do this new method? And how exactly do you reset the buttoncount back to a valid number if the buttoncount exceeds the maximum number of available arrays? Here is the indented code:

Code:
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
//#define SAMPLE_RATE 500 // 8 ksps

/*
* The sinewave data needs to be unsigned, 8-bit
* sinewavedata.h should look like this:
* const int sinewave_length=256;
* const unsigned char sinewave_data[] PROGMEM = {0x80,0x83, ...
*/

const int EKG_length=1001;
const int Respiration_length = 1045;

const unsigned char EKG_data[] PROGMEM = {0x14,0x13,0x13,0x13,0x13,0x12,0x12,0x13,0x13,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x13,0x13,0x13,0x14,0x14,0x15,0x15,0x15,0x15,0x16,0x16,0x16,0x15,0x15,0x15,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x14,0x14,0x14,0x14,0x13,0x13,0x13,0x13,0x13,0x14};
const unsigned char Respiration_data[] PROGMEM = {0xE9,0xE9,0xE9,0xE9,0xEB,0xEB,0xEC,0xEC,0xEC,0xEA,0xEA,0xEB,0xEB,0xEB,0xEB,0xEB,0xEC,0xEC,0xEC,0xEB,0xEB,0xEC,0xED,0xED,0xED,0xED,0xED,0xED,0xED,0xEF,0xEF,0xED,0xED,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xED,0xED,0xEE,0xEE,0xEE,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEE,0xEE,0xEE,0xB7,0x97,0x7B,0x65,0x52,0x46,0x3A,0x30,0x28,0x21,0x1A,0x16,0x13,0x10,0xD,0xA,0x8,0x8,0x6,0x6,0x7,0x6,0x5,0x5,0x4,0x2,0x2,0x3,0x3,0x3,0x0,0x0,0x3,0x3,0x2};
int outputPin = 6; // (PCINT22/OC0A/AIN0)PD6, Arduino Digital Pin 6
int buttonPin = 2;
int buttonCurrentCount = 0;

volatile uint16_t sample; // This is called at SAMPLE_RATE kHz to load the next sample.

ISR(TIMER1_COMPA_vect)
{
  if (sample >= EKG_length)
  {
    sample = -1;
  }
  else
  {
    OCR0A = pgm_read_byte(&EKG_data[sample]);
  }
  ++sample;
}
 
void startPlayback(int samp_rate, int array_num)
{
 
  pinMode(outputPin, OUTPUT);
  // Set Timer 0 Fast PWM Mode (Section 14.7.3)
  // WGM = 0b011 = 3 (Table 14-8)
  // TOP = 0xFF, update OCR0A register at BOTTOM
  TCCR0A |= _BV(WGM01) | _BV(WGM00);
  TCCR0B &= ~_BV(WGM02);
  // Do non-inverting PWM on pin OC0A, arduino digital pin 6
  // COM0A = 0b10, clear OC0A pin on compare match,
  // set 0C0A pin at BOTTOM (Table 14-3)
  TCCR0A = (TCCR0A | _BV(COM0A1)) & ~_BV(COM0A0);
  // COM0B = 0b00, OC0B disconnected (Table 14-6)
  TCCR0A &= ~(_BV(COM0B1) | _BV(COM0B0));
  // No prescaler, CS = 0b001 (Table 14-9)
  TCCR0B = (TCCR0B & ~(_BV(CS02) | _BV(CS01))) | _BV(CS00);
  // Set initial pulse width to the first sample.
  if(array_num == 1)
  {
    OCR0A = pgm_read_byte(&EKG_data[0]);
  }
  if (array_num == 2)
  {
    OCR0A = pgm_read_byte(&Respiration_data[0]);
  }
  // Set up Timer 1 to send a sample every interrupt.
  cli(); // disable interrupts
  // Set CTC mode (Section 15.9.2 Clear Timer on Compare Match)
  // WGM = 0b0100, TOP = OCR1A, Update 0CR1A Immediate (Table 15-4)
  // Have to set OCR1A *after*, otherwise it gets reset to 0!
  TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
  TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
  // No prescaler, CS = 0b001 (Table 15-5)
  TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  // Set the compare register (OCR1A).
  // OCR1A is a 16-bit register, so we have to do this with
  // interrupts disabled to be safe.
  OCR1A = F_CPU / samp_rate; // 16e6 / 8000 = 2000
  // Enable interrupt when TCNT1 == OCR1A (p.136)
  TIMSK1 |= _BV(OCIE1A);
  sample = 0;
  sei(); // enable interrupts
}

void setup()
{
  Serial.begin(9600);
  pinMode(buttonPin, INPUT);
}

void loop()
{
  if (digitalRead(buttonPin) == HIGH)
  {
    buttonCurrentCount++;
    delay(50);
  }
 
  if(buttonCurrentCount == 1)
  {
    startPlayback(500, 1);
  }
 
  if(buttonCurrentCount == 2)
  {
    startPlayback(500, 2);
  }
 
  Serial.println(buttonCurrentCount);
}
Logged

UK
Offline Offline
Shannon Member
****
Karma: 184
Posts: 11197
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You have to decide what to do if the number of button presses is more than the number of available options. There are several ways to handle it and you need to decide which behaviour you want. Do you have any means to give feedback about which option is currently selected? If so, you could just go back to 0 again. But if there's no feedback it'd be very easy to get lost, and it might be better just to say that pressing the button again just leaves the last option selected.

You could do the programming world a favour by wrapping those very long initialisers onto multiple lines.

Your loop doesn't seem to implement the control logic you described.

Firstly, you need to detect button transitions, not just the button being down. Second, you need to count the number of button presses that occur before you decide to fix that count. You don't say what the criteria are for fixing the count. If you have another button, you could use that. Otherwise you could just apply a timer and say that if there were no more button presses for (say) five seconds then the playback number will be fixed and playback will start
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

We plan on including an LCD display that has the number printed on it, but at the moment we are just using the serial monitor to see how many presses have been made. We also do have another button that we could use as a "select" button when the proper number is pressed. How do I go about detecting transitions and counting the number of button presses that occur before fixing the count?
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 551
Posts: 46240
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
How do I go about detecting transitions and counting the number of button presses that occur before fixing the count?
Isn't this fairly obvious? A transition, by definition, happens when the current state is not the same as the previous state. You read the current state. Next time around, that state is the previous state. Therefore, at the end of loop(), you need to same the current state as the previous state.

Counting is unbelievably simple:
Code:
count++;
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I thought that was what I was doing with the code:
Code:
buttonCurrentCount++
which is why I asked. I guess the better question would have been, how do I prevent the playback loop from starting before I push the "select" button, and how do I get the buttoncounter to only increase one when the button is pressed (i.e. implementing a "raising edge" function)?
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 551
Posts: 46240
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I thought that was what I was doing with the code
If you knew how, why did you ask how?

Quote
I guess the better question would have been, how do I prevent the playback loop from starting before I push the "select" button
Is this a trick question? The startPlayback() function doesn't run until you call it. So, don't call it until the "select" switch is determined to be pressed.

Quote
, and how do I get the buttoncounter to only increase one when the button is pressed (i.e. implementing a "raising edge" function)?
I've explained that.
Code:
int currState;
int prevState = LOW;

void loop()
{
   currState = digitalRead(somePin);
   if(currState != prevState)
   {
      // A transition occurred. Which one?
      if(currState == HIGH)
      {
         // To pressed
         buttonCurrentCount++; // Count it!
      }
      else
      {
          // To released
      }
   }
   prevState = currState;
}
Logged

Pages: [1]   Go Up
Jump to: