Friday, June 7, 2013

I could use some help on my #arduino project

I intended for my son and I to spend some quality time last night getting the LCD display working that I bought on eBay. But, my lack of knowledge and experience got in the way. I mentioned that the code that I used, I found online. It had 2 arrays that stored decimal values for one led to start at the left and scan to the right 7 positions in one shit register and the other did the opposite in the other shift register. The final result was 2 LEDs appeared to start from both ends of the line and move toward the center, cross and continue to the opposite end. When, in fact, neither crossed the center, each register was simply panning back and forth.

For my "catch the light" game idea, I needed to have a single LED scanning back and forth. I've been a programmer in the medical industry for more than 20 years, but never had to deal much with binary data and never hardware beyond RS232 communications.

This is where my in-experience showed through.

I didn't want to use the arrays. Maybe that was a mistake. I knew easily enough that to count in binary I simply needed to start at one and multiply by 2:
     1 = 1
     2 = 10
     4 = 100
     8 = 1000
     16 = 10000
     32 = 100000
and so on. I thought that I understood that the binary data flowing to the shift registers would cascade pushing (shifting) the bits to the next shift register. I thought that I could simply keep shifting to the left until I got to 32768, then start shifting to the right until I got back to 1. The program that I started with had 2 shift outs to work with the 2 arrays:
        shiftOut(dataPin, clockPin, MSBFIRST, seq1[x]);         //Send the data byte 1
        shiftOut(dataPin, clockPin, MSBFIRST, seq2[x]);         //Send the data byte 2
I thought that I would only need 1 shift out and could, in effect, send out 16 bits at one time. So this is what I ended up with:

     digitalWrite(latchPin, LOW);      //Pull latch LOW to start sending data
     shiftOut(dataPin,clockPin,MSBFIRST,i);
     digitalWrite(latchPin, HIGH);    //Pull latch HIGH to stop sending data
     if (left ){
        i = i << 1;             //shift bit to the left while left=true
     }
     else
     {
        i = i >> 1;             //otherwise, shift right
     }
     if (i == 32768 || i == 1){
     left = !left;                    //If we are at either end of the line toggle left
     }
Needless to say this failed. My new theory was that I could only write out 1 (8 bit) byte at a time. Now, how do I do this without going back to an array? Believe it or not, I turned to Open Office Calc, sorry I can't afford Microsoft's version of Office which, according to Amazon is $139 right now and that is for 1 user on 3 PCs, however that works. We have 2 aging PCs (one of which is homemade and the other a cheap eMachine) and a laptop. Sorry, off on a tangent. The dec2bin() function failed after 9 bits:
1 1
2 10
4 100
8 1000
16 10000
32 100000
64 1000000
128 10000000
256 100000000
512 #NUM!
1024 #NUM!
2048 #NUM!
4096 #NUM!
8192 #NUM!
16384 #NUM!
32768 #NUM!
So, I figured that if I'm under 255, I can send a zero in one shiftOut() and the value of "i" in the other. I tested this in my spreadsheet in the next column with a simple if() statement:
    =if(a1>255;a1/256;a1)
This resulted in the new column counting to 128 twice. So, while 255 or less I shiftOut() my value for "i" then a zero and when greater than 255, I shift out a zero then the value of "i/256." This is what I ended up with:

void displayLed(){
  digitalWrite(latchPin, LOW);            //Pull latch LOW to start sending data
  if (i > 255){
    shiftOut(dataPin,clockPin,MSBFIRST,0);
    shiftOut(dataPin,clockPin,MSBFIRST,i/256);
  }
  else
  {
    shiftOut(dataPin,clockPin,MSBFIRST,i);
    shiftOut(dataPin,clockPin,MSBFIRST,0);
  }  
  digitalWrite(latchPin, HIGH);           //Pull latch HIGH to stop sending data
}

Next, I replaced the 9th digit (i=256) with a blue LED, which will be our target. Then decided to replace the ones on either side (LEDs 8 and 10) with yellow LEDs as warning, thinking that in game play the blue one would be worth the max score and the yellow ones half that. I modified my code to make the delay() a variable, initially set to 100. Then modified the interrupt handler to reduce the delay by 2 milliseconds every time the button was pushed. Testing this was entertaining, especially, when the scanning speed got to the point that it appeared that all of the LEDs were on at the same time. My son kept doing this over and over giggling.

Now I wanted to see which LED was lit when the button was pushed. I thought I would simply blink the "caught" led 10 times before resuming scanning. I put this code in my interrupt handler:

void pushit(){
  long holdIt=0;
  time=millis();
  if (time-lasttime>50){
    digitalWrite(12,!state);
    state=!state;
    lasttime = time;
    pauseTime = pauseTime - 2;
    holdIt = i;
    for(int loopVar=0;loopVar<10;loopVar++){
      i=0;
      displayLed();
      delay(500);
      i=holdIt;
      displayLed();
      delay(500);
    }
  }
}

I hold the current value of "i" in "holdIt," then in a loop I set "i" to zero, display it, set "i" back to "holdIt" and display that, 10 times. I tried a 50 ms delay() between the on and off, but couldn't see it blink, even 500 ms wasn't noticable.

It got late, and my boy needed sleep, even though school is finished for the summer. So we stopped there. It was amazing how he caught on the shifting left and right, and even offered intelligent suggestions when something wouldn't compile.

This is where I'm stuck

  • Why isn't it blinking? 
  • Is it because "i" isn't declared as volatile? 
  • Is it because the interrupt handler doesn't keep running long enough? 
  • Maybe, I should pass "i" into the displayLed() function? 
  • Is it because the interrupt is tripping again and starting the handler again before it finishes? 
  • Should I detachInterrupt() when I get into the handler?
Any suggestions would be helpful!

Here's the whole program:

int dataPin = 10;        //Define which pins will be used for the Shift Register control
int latchPin = 9;
int clockPin = 8;

long i = 1;
int pauseTime = 100;      // byte #2 value
bool left = true;
volatile int state=LOW;
unsigned long time;
unsigned long lasttime;
void setup()
{
    attachInterrupt(0,pushit,LOW);
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
    pinMode(12,OUTPUT);
    digitalWrite(12,state);
    lasttime==millis();
}
void loop()
{
  displayLed();
  if (left ){
    i = i << 1;
  }
  else
  {
    i = i >> 1;
  }
  if (i == 32768 || i == 1){
    left = !left;
  }
  delay(pauseTime);
  if (pauseTime <= 0){
    pauseTime = 100;
  }
}
void pushit(){
  long holdIt=0;
  time=millis();
  if (time-lasttime>50){
    digitalWrite(12,!state);
    state=!state;
    lasttime = time;
    pauseTime = pauseTime - 2;
    holdIt = i;
    for(int loopVar=1;loopVar<10;loopVar++){
      i=0;
      displayLed();
      delay(500);
      i=holdIt;
      displayLed();
      delay(500);
    }
  }
}
void displayLed(){
  digitalWrite(latchPin, LOW);            //Pull latch LOW to start sending data
  if (i > 255){
    shiftOut(dataPin,clockPin,MSBFIRST,0);
    shiftOut(dataPin,clockPin,MSBFIRST,i/256);
  }
  else
  {
    shiftOut(dataPin,clockPin,MSBFIRST,i);
    shiftOut(dataPin,clockPin,MSBFIRST,0);
  }  
  digitalWrite(latchPin, HIGH);           //Pull latch HIGH to stop sending data
}
Leave a comment below or .

2 comments :

  1. Looks like a fun blog / story you have here! Anyway, I didn't look at this in tremendous depth but I'm not sure you can successfully call delay() from within an interrupt handler as delay() itself relies on interrupts. Check out this Arduino forum post. http://forum.arduino.cc/index.php/topic,46201.0.html

    ReplyDelete
    Replies
    1. Good to know about the delay(). Couldn't find anything as far as guidlines for interrupt handlers. Thanks for taking the time to read/comment.

      Delete