Pages: 1 [2]   Go Down
Author Topic: Hack from SPI display to Arduino  (Read 2136 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 27
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, thanks, guys, for sticking with me.  I thought this was going to be a one or two evening project, but...  now my wife says I'm obsessed.  And in a sense I guess I am.  If you lose interest I'll get by somehow.  I ordered a Maple supposedly Arduino compatible that uses a different processor that runs a 5 times the speed.  That should give me time to complete the analysis within the ISR.

I tried just stuffing the data into a buffer like you suggested in #8 and couldn't get that to work.  I couldn't find a way to stop the buffer from overflowing when data comes in too rapidly.  I have to analyze something before I tell it to stop.

Data bursts are at about 250,000 bytes per second. "Normal mode" is a couple of data bursts per second to display a clock and whatnot. These bursts range from 66 bytes each to occasionally 1062 bytes. (I might get a few dozen bytes inside these "normal" bursts that contain the "unique bytes" of the 10 numerals)

Other data: Of the 256 byte values only about 100 appear to ever get used as digits, letters or commands.  (I looked at a string of 40,000 bytes as a representative sample.  As long as I don't crash the system, all this normal mode data is junk.  I'll don't think I'll ever get a false positive from it.)

A good finger swipe results in 4 bursts within two seconds, followed by 2 seconds of silence.  They are 1024 bytes, 704 bytes, 1502 bytes (or 1216 bytes depending on how it clears the screen) and 66 bytes long. The first two bursts tell the user to remove their finger, the third provides information including the userID. The last 176 bytes of the 1502 byte burst are four zeroes and four digit drawings of the User ID.

(Each digit drawing is a 3 byte location command, 8 bytes of upper bitmap data, 3 more bytes location and 8 more bytes of lower bitmap data.) 

The last 66 byte burst contains a colon and could be the cue to process the raw_digit string in the buffer.  I poked around some more and just before the User ID string is the word "OK!" and the "K" contains a useful unique byte that can be used as a start collecting data signal.

Here is the code I'm using now:

All counters are volatile int and all data are volatile byte data types.

void setup (void)
{
  Serial.begin (115200);
  Serial.println (sketch); // Rick - always start by identifying what's actually in the Arduino

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);   

  // turn on SPI in slave mode
  SPCR |= _BV(SPE);

  // turn on interrupts
  SPCR |= _BV(SPIE);
 
   SPI.setDataMode(SPI_MODE2);  // Rick - because of inverted clock in T&A - not used in testing between Arduinos

}  // end of setup


// SPI interrupt routine
ISR (SPI_STC_vect)
{
  if (SPDR==13) {start = 1;} // Rick - unique part of "OK" before User ID
  if (SPDR == 198) {process = 1;} // Rick - unique inverse colon after UserID
  if ((start==1) && (r < rmax)) {raw_digit[r++] = SPDR; } // Rick - only collect when within range
  //     Rick - I still need rmax in case I miss a 198 - may be overkill

}  // end of interrupt service routine (ISR) SPI_STC_vect


void loop (void)
{
  if ((r >= rmax) || (process == 1))// Rick - ISR got an inverse colon - rmax if I missed it
  {
    // Rick - caution: in a millisec ISR will start overwriting raw_digit[0]
    rtemp = r;
    r=0; // Rick - let interrupt go back to filling raw_digit   
    process = 0; start = 0; // Rick - wait for new start/stop from ISR 
    for (int i=0; i <= rtemp; i++){ work_digit = raw_digit;  }  // Rick - get these moved in a hurry
   
    for (m=0; m <=rtemp; m++) { getDigit(); } // Rick - m is global
      // Rick - this for loop and getDigit returns with an array good_digit[ ] with g valid digits in it       

    get_user_id(); // Rick - convert all the good_digits to any valid out_digit (or userID)
    g=0;
  } // end if process  NOTE: get_user_id function now has a couple of seconds while ISR refills raw_digit

}  // end of loop

My test userIDs are 00000001, 00000002 and 00001945.  A recent run of results was 00000002 00000200 00002520
00000100 00002010 00000220 00000210 00000100 00000001 00000001 00000021.  I rarely get a successful output on a 00001945, I think because at least one of the zeroes gets lost.  On the LCD you can see typically "OK! ID 00000002  1:1"   When I put debug timers and counters in there I see even worse gaps, but then the main loop is doing Serial.print and timers, etc. which might be screwing it up.  Again the data on the logic analyzer is good.  Somehow the ISR is simply missing one or more of the zeros (and likely other bytes as well).

Until the Maple comes in, I'm going to study table lookups.  Unless you have another suggestion.  By the way, what is naked_ISR?  Is that a function someone wrote?

Thanks.

Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 485
Posts: 18816
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

See how half your code is in italics? If you put it into [ code ] tags it won't do that. Select the code and hit the "#" button above the input box.
Logged


ottawa, canada
Offline Offline
God Member
*****
Karma: 6
Posts: 991
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
By the way, what is naked_ISR?  Is that a function someone wrote?

it's packaged with the avr gcc compiler i believe.  It lets you pick and choose what regs to save.  In the one place I used it it cut the isr entry time from 3.5us to 1 and a bit.  If you bound your "r" variable to a register i think you would only need to save and restore the status register.  I actually learned about naked_isr and binding to registerd from a post here (maybe westfw's) but I can't find it any more..

The code I see in your post uses the ISR to stuff a buffer and the loop to analyze it - right? Where is the overrun or dropped data happening do you think?  Does it just not make it into the buffer?

The loop code looks iffy to me.  Let's say you missed a stop and you're over rmax: i think the isr will start overwriting the data that you're analyzing.  You need some rock-solid way of keeping out of your own way:
e.g.in the loop, disable interrupts, copy all the data, reset the pointer and enable interrupts. If you miss a second swipe that starts that soon, too bad but at least your data is solid.

Code:
rtemp = r;
    r=0; // Rick - let interrupt go back to filling raw_digit   
    process = 0; start = 0; // Rick - wait for new start/stop from ISR 
    for (int i=0; i <= rtemp; i++){ work_digit = raw_digit;  }  // Rick - get these moved in a hurry
   
    for (m=0; m <=rtemp; m++) { getDigit(); } // Rick - m is global
      // Rick - this for loop and getDigit returns with an array good_digit[ ] with g valid digits in it       

    get_user_id(); // Rick - convert all the good_digits to any valid out_digit (or userID)
    g=0;

I'll be interested in the maple - have you looked at its interrupt response times?
Logged

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

Nick, losing a byte or two every 1000 isn't a disaster, it will just require the patients to reswipe 8 or 16 times out of a 1000.  The only time this should happen is when I hit rmax, currently set at 1229.  I figure that when I hit rmax, after two moves r=0 so the ISR can collect data again, and two more moves plus setting up a "for loop" then moving the first few bytes out of raw_data and I'm safe again.  Is my reasoning correct?  But in the real world, using the 7 zeroes in a row as a good example I'm losing one or more of those zeroes at least half the time, not 8 or 16 in 1000.  (That's why I set rmax to 1229, so it wasn't likely to keep hitting the same seven zeroes.)

I'm now playing with a 256 byte lookup table for each possible SPDR bytes.  (I tried a boolean table but I need more than 2 responses because of the 1 and 8 not having a unique byte.)  The best I can do in the ISR still requires 3 "if" statements, one to decide if the byte is "potentially unique", a second to handle the 1 and 8 situation (which became 1,3 and 8 after I discovered a bug) and a third to distinguish a legitimate zero from an invalid/non-digit byte.  Plus moving a few bytes around and I'm back where I started.  It does have a lot of potential to eliminate downstream analysis, but it won't matter if I can't capture the raw data.

I mistakenly used the insert quote instead of insert code and all my [] bracket things disappeared.  Rather than deal with it I just copied the code to get the mail out.

I think the only way to find out about the Maple is to try it when it arrives.

I'll look around for the naked_ISR references.  Saving 1 or 2 microseconds might be enough.

Logged

ottawa, canada
Offline Offline
God Member
*****
Karma: 6
Posts: 991
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I seriously think you need a rock solid protocol between the ISR and the loop.

I found the ISR_NAKED ref that I used myself: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1261537186
In my case I only needed a single variable and was able to copy westfw's code closely.  You'd have to think hard about your flag variables but this would easily let you copy to the buffer much more quickly. Maybe you define one more register variable "isrstate" and use that to subsume start, process, and some sort of blocker flag from the loop to the isr.

Logged

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

Bill, thanks for the thought, but I don't think I'm that far down the road yet.

I just looked at my bit-byte to numeric digit map again and came up with a good way to both streamline the ISR using an improved lookup table and reduce the downstream analysis.  The ISR only has a single if... move... increment statement, (and 9 out of 10 times the if is not taken) so that ought to be quick enough.  The downstream processing just resets the ISR index and immediately starts moving bytes to a safe place. 

Here is the entire code:
Code:
// Written by Nick Gammon
// April 2011

char sketch[] = "Nick_Gammon_Slave2_dec31x";  // Revisions by Rick Rantilla

// rev x - new raw2work chart - avoids copying strings of bytes for valid digits. This may help ISR timing       
// rev w - using 256 member raw2work lookup table
// rev s - master counter "count" added for debugging
// rev p - just capture byte is ISR - everything else outside
// rev n - Added timing counters for debug - Why am I losing about 5% of the valid digits in the ISR?
//         Since a valid userID has 8 digits, I'm losing or distorting 40% of them!!!
//         Per timing ISR takes only 4 or 8 microsec, however, LCD gets 1 byte every 4 microsec.
// rev m - Ugh! - Serial.print takes hundreds of microseconds per character even at 115200
// rev k - Mega: now using wire 15 from T&A as SS (unfortunately this picks up commands as well)
//         Mega SPI pins: 53 SS, 51 MOSI (signal), 52 clock
//         Also SPI.setDataMode for inverted clock from the T&A LCD

#include "pins_arduino.h"
#include "SPI.h"  // rick k

volatile byte c = 0; // Rick m
volatile byte cp = 0; // Rick n - previous byte needed to distinguish some digits
volatile int g = 0; // Rick d - counter for storing bytes into good_digit array to examine at leisure
volatile int r = 0; // Rick p - counter for raw digits
volatile int rmax = 50; // Rick r
volatile int i = 0; // Rick p
volatile int j = 0; // Rick p
volatile int k = 0; // Rick p
volatile int p = 0; // Rick x - I've made all counters unique and global
volatile int count = 0; //  Rick s master counter

byte raw_digit [500]; // Rick p
byte work_digit[500]= "a"; // Rick n
byte good_digit [400];  // Rick d
byte out_digit[100]= "a"; // Rick n

volatile boolean raw2work[] = {0,1,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,1,0,0,0,0,  0,0,0,0,0,0,0,0,   // 0-31  Rick w
                               0,1,0,1,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,1,0,1,0,   // 32-63
                               0,0,0,1,0,0,0,1,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,   // 64-95
                               1,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,1,1,   // 96-127
                               0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,   // 128-159
                               0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,   // 160-191
                               1,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,   // 192-223
                               0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0  };// 224-255
                // Rick x - only bytes in this table pass thru to the next step of decoding


void setup (void)
{
  Serial.begin (115200);
  Serial.println (sketch); // Rick c - always start by identifying what's actually in the Arduino

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);   

  // turn on SPI in slave mode
  SPCR |= _BV(SPE);

  // turn on interrupts
  SPCR |= _BV(SPIE);
 
   SPI.setDataMode(SPI_MODE2);  // Rick k - because of inverted clock in T&A - not used in testing between Arduinos
}  // end of setup


ISR (SPI_STC_vect)  // SPI interrupt routine
{
  //count++;
  if (raw2work[SPDR]) {raw_digit[r++] = SPDR; // Rick x - if it is in the table, store it
    // Rick w - it is a potential unique digit and is worth storing for further analysis
    //Serial.println (raw_digit[r-1]); //Serial.print (" "); Serial.println (r,DEC);// debug
  }
}  // end of interrupt service routine (ISR) SPI_STC_vect


void loop (void)
{
  if (r >= rmax) // Rick w - just be patient, don't get too fancy
  { // Rick m - caution: in a millisec ISR will start overwriting raw_digit[0]
    r=0; // Rick n - let interrupt go back to filling raw_digit   
   
    for (i=0; i <= rmax; i++){ work_digit[i] = raw_digit[i];  }  // Rick n - get these moved in a hurry, most of these are already good_digit
    //for (int i=0; i <= rmax; i++){Serial.println(raw_digit[i]); } // Rick v - debug
   
    for (j=0; j <=rmax; j++) { getDigit(); } // Rick x - get only valid digits, requires valid predecessors for 1, 3 and 8
      // Rick p - this for loop ends with an array good_digit[ ] with g valid digits 

    for (k=0; k < g; k++){ // Rick n - process all g good_digits
      // A valid userID is 4 zeroes followed by 4 digits.  I've never seen a string of 4 zeroes in the command fields
      if ( (good_digit[k] == 0) && (good_digit[k+1]==0) && (good_digit[k+2]==0) && (good_digit[k+3]==0) )
        { // got a valid userID - print the 4 least significant digits
          for (p=0; p <= 7; p++) { // Rick n - again i is a local variable
            out_digit[p] = good_digit[k+p];  // Rick n - userID is the 4 digits after the four zeroes
            Serial.print (out_digit[p], DEC);  // debug
          } // end for p
          Serial.println(" ");//Serial.println(count);  // debug
         
          // Rick x - here I will insert my cabinet door operation application code using out_digit
         
          k = k+7; // k++ already incremented once - skip past the 8 digits of the UserID
        } // end if good_digit - what happens if at the end of work_digit??? - adds trailing zeroes - I need to clean this up later
    } // end for k
    g=0; // set up for next batch
  } // end if r - most of the time loop does nothing but check r
} // end loop


void getDigit()  // Rick p
{
  cp = c; // Rick n - store previous digit needed to distinguish a 1, 3 or 8
  c = work_digit[j]; // still the raw byte as SPI found it, but only those in raw2work lookup table
     
  switch (c)  // Rick n - each graphic bitmap has 16 bytes per digit displayed (plus 6 command digits)
              // Among the digits, each digit has a unique byte (or byte pair)
              //   96, 192, 62, 126 and 127 may also appear in other digits, but unique pairings finalize the selection
              // So whenever I find this unique byte (pair) in a field of digits, I've got a good digit
              //   regardless of what else is in that digit
              // I might also get what appears to be a digit in a field of commands or letters or the clock,
              //   but I will later throw them out unless they are immediately following 4 zeroes in a row
              // Increment g only if a valid digit

  {
  case 71:
    good_digit [g++] = 0;  // Rick e
    break;
  case 96:  // original 127 remapped to 11 if cp was 0
    if (cp == 192) {good_digit [g++] = 1; }// Rick t
    break; 
  case 67:
    good_digit [g++] = 2;  // Rick d
    break;
  case 62:  // original 32 remapped to 5 if cp was 0
    if (cp == 126) {good_digit [g++] = 3; } // Rick t
    if (cp == 127) {good_digit [g++] = 8; }// Rick t
    // Note: there is a bug where sequence "48" in a valid user ID would register as "488"
    //       since a "4" ends in "127" and the "8" starts with "62"
    //       although these bytes are not part of their "unique" byte pairs
    break; 
  case 19:
    good_digit [g++] = 4;  // Rick d
    break;
  case 33:
    good_digit [g++] = 5;  // Rick e
    break;
  case 60:
    good_digit [g++] = 6;  // Rick d
    break;
  case 1:
    good_digit [g++] = 7;  // Rick d
    break;
  case 35:
    good_digit [g++] = 9;  // Rick d
    break;
  default:
  //time = micros() - time; Serial.println (time, DEC); delay(100); // debug
    break;
  } // end of switch
}  // end of getDigit

Here are the results of what should have been 1,1,1,2,2,2,1945,1945,1945,3,3,3 (all with leading 0s):

00000001
00000001
00000008 (could have missed a 0)
00000002
00002114 (could have missed 3 0s)
00000217 (could have missed 2 0s)
(nothing - could have missed a 0)
(nothing - could have missed a 0)
00001945
00000351 (could have missed 2 0s
00000003
00004444 (could have missed 3 0s and the 3)

In this run rmax was set at 50 bytes, so I should have expected "end of buffer" effects 8/50 or about 2/12, not 7/12.  If I set rmax at a big number (like 1000) I'd have to wait minutes for a response.

I'm going to see if I can find another Arduino laying around (maybe a newer Uno) before starting on a new venture to clean up the buffer ends or study naked_ISR.  What I could do is set my buffer raw_digits to 1000, but only analyze 50 byte segments into work_digit, etc. Hmmm....

Thanks
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 485
Posts: 18816
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Some suggestions:

* Get rid of the volatile keyword except for anything shared between the ISR and the main loop. That actually slows things down because the compiler is forced to reload registers. Basically raw_digit and r. (raw2work doesn't change and clearly isn't volatile).

* Make rmax a const, so the compiler can optimize it into a literal load.

* Change everything you can from int to byte. After all it is an 8-bit machine. Specifying int requires more cycles than byte.

* Take the "if" out of the ISR:

Code:
if (raw2work[SPDR]) {
    raw_digit[r++] = SPDR; // Rick x - if it is in the table, store it

(Do tests later.)

* I would use a circular buffer - that handles bursts quite well. The ISR just stuffs things into the buffer (and if you make it 256 bytes a "byte" counter will wrap around without you having to tell it to).

* Then in "loop" use a "remove" pointer to pull stuff out of the buffer (up to the insert point) - that lets you decode more slowly.

There is an example of inserting/removing from a circular buffer in the code for a debugging slave here:

http://www.gammon.com.au/forum/?id=11329
Logged


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

Nick, great!  Thanks!  I'm happy!

I got rid of "volatile" and changed everything I could to single bytes.   I figured out the automatic circular buffers which are really neat.  I now get a quick response and miss something occasionally, but not horrendously.  I just ran 40 trials and missed a zero byte on 3 trials and garbled up enough to get no response on 2 others.  That's 12% which is no worse than the finger sensor reject rate itself.  The 40 trials made the program go through all the 256 byte buffers a couple of times, and I don't see any issues there and don't think they are the cause of the occasional missed bytes.  

(By the way, the fingerprint reader is on my left side and I've been ackwardly reaching around with my right hand to get test readings for several days now.  It just occurred to me to "register" the middle finger of my left hand and now I get satisfaction from giving the reader my left finger several times in a row.)

I'm reluctant to remove the "if" statement from the ISR because it eliminates over 90% of the junk data that I don't have to process at all downstream.  Also rather than checking and stop reading if "inpoint" ever catches "outpoint" I'll just live with what happens... that is possibly what happened in the 2 garbled responses.

Now that I've got some decent data I've noticed that when I drop a zero, if UserID was say 00000067 it becomes something like 00000671, which might be a false positive.  If I can't overcome this I'll put some restrictions on valid user IDs to compensate.

Other than that I think I'm good to go.  Good thing, because I just got to revision z and hate to go to capital letters.  Also the Maple just arrived in the mail.  Don't know if I'll open it this month.

Thanks again, everyone.  I couldn't have done it without you.  Let me know if there is anything I can do for you all.

PS. Happy New Year!


Logged

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

Here is the code if anyone wants to use it as a (poor) example.

Code:
// Written by Nick Gammon
// April 2011

char sketch[] = "Nick_Gammon_Slave2_dec31z";  // Revisions by Rick Rantilla

// rev z - automatic 256 byte buffers
//         initialize good_digit with something besides zeroes
// rev y - Nick's advice on bytes and volatile
// rev x - new raw2work chart - avoids copying strings of bytes for valid digits. This may help ISR timing       
// rev w - using 256 member raw2work lookup table
// rev s - master counter "count" added for debugging
// rev p - just capture byte is ISR - everything else outside
// rev n - Added timing counters for debug - Why am I losing about 5% of the valid digits in the ISR?
//         Since a valid userID has 8 digits, I'm losing or distorting 40% of them!!!
//         Per timing ISR takes only 4 or 8 microsec, however, LCD gets 1 byte every 4 microsec.
// rev m - Ugh! - Serial.print takes hundreds of microseconds per character even at 115200
// rev k - Mega: now using wire 15 from T&A as SS (unfortunately this picks up commands as well)
//         Mega SPI pins: 53 SS, 51 MOSI (signal), 52 clock
//         Also SPI.setDataMode for inverted clock from the T&A LCD
//
//  This program is a hack of a Time and Attendance system with a fingerprint reader.  My application needs
//      the userID in "real time" but the system is designed to only output in batch. 
//      The system does display the userID immediately after each successful read on an attached LCD,
//       but only in bit mapped graphics form.  So I've figured out what bits & bytes represent which numbers
//       and am tapping the motherboard to LCD lines using an Arduino as an SPI slave.

#include "pins_arduino.h"
#include "SPI.h"  // rick k

byte c = 0; // Rick m
byte cp = 0; // Rick n - previous byte needed to distinguish some digits
byte g = 0; // Rick d - counter for storing bytes into good_digit array to examine at leisure
//volatile byte r = 0; // Rick p - counter for raw digits

volatile byte inpoint = 0;  // Rick y
volatile byte outpoint = 0;  // Rick y

//byte rmax = 255; // Rick r - no longer needed, wrap at end of byte
byte i = 0; // Rick p
byte j = 0; // Rick p
byte k = 0; // Rick p
byte p = 0; // Rick x - I've made all counters unique and global
volatile int count = 0; //  Rick s master counter

byte raw_digit [255]; // Rick p
byte work_digit[255]= "a"; // Rick n
byte good_digit [255];  // Rick d
byte out_digit[100]= "a"; // Rick n

volatile boolean raw2work[] = {0,1,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,1,0,0,0,0,  0,0,0,0,0,0,0,0,   // 0-31  Rick w
                               0,1,0,1,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,1,0,1,0,   // 32-63
                               0,0,0,1,0,0,0,1,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,   // 64-95
                               1,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,1,1,   // 96-127
                               0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,   // 128-159
                               0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,   // 160-191
                               1,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,   // 192-223
                               0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0  };// 224-255
                // Rick x - only bytes in this table pass thru to the next step of decoding


void setup (void)
{
  Serial.begin (115200);
  Serial.println (sketch); // Rick c - always start by identifying what's actually in the Arduino

  for (i=0; i<=254; i++) {good_digit[i] = 9;}  // Rick z - initialize so stray zeroes don't trigger output
 
  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);   

  // turn on SPI in slave mode
  SPCR |= _BV(SPE);

  // turn on interrupts
  SPCR |= _BV(SPIE);
 
   SPI.setDataMode(SPI_MODE2);  // Rick k - because of inverted clock in T&A - not used in testing between Arduinos
}  // end of setup


ISR (SPI_STC_vect)  // SPI interrupt routine
{
  //count++;  // debug
  if (raw2work[SPDR]) {raw_digit[inpoint++] = SPDR; // Rick x - store it
    // Rick w - it is a potential unique digit and is worth storing for further analysis
    //Serial.println (raw_digit[r-1]); //Serial.print (" "); Serial.println (inpoint-1,DEC);// debug
  }
}  // end of interrupt service routine (ISR) SPI_STC_vect


void loop (void)
{
  if (outpoint != inpoint) // Rick w - just be patient, don't get too fancy - assume it is behind
  {           // Rick m - caution: in a millisec ISR will start overwriting raw_digit[0]   
    work_digit[outpoint] = raw_digit[outpoint++];    // Rick n - get these moved in a hurry, most of these are already good_digit
     
    getDigit();  // Rick x - converts only valid digits, requires valid predecessors for 1, 3 and 8
      // Rick p - this function returns with the next good_digit[ ] at g 
      // Rick y - run through the rest of the loop even if no new g?
      // Rick y - is this digit the end of an 8 digit userID?
      // A valid userID is 4 zeroes followed by 4 digits.  I've never seen a string of 4 zeroes in the command fields
    if ( (good_digit[g-8] == 0) && (good_digit[g-7]==0) && (good_digit[g-6]==0) && (good_digit[g-5]==0) )
      { // got a valid userID - print the 4 least significant digits
          Serial.print("User ID = ");  // debug
          for (p=0; p <= 7; p++) { // Rick n
            out_digit[p] = good_digit[g-8+p];  // Rick n - userID is the 4 digits after the four zeroes
            Serial.print (out_digit[p], DEC);  // debug
            good_digit[g-8+p] = 99; // Rick y - so we don't try to use these digits again
          } // end for p
          Serial.println(" ");//Serial.println(count);  // debug
         
          // Rick x - here I will insert my cabinet door operation application code using out_digit
         
      } // end if good_digit - what happens if at the end of work_digit??? - adds trailing zeroes - I need to clean this up later   
  } // end if outpoint - most of the time loop does nothing but check inpoint/outpoint
} // end loop


void getDigit()  // Rick p
{
  cp = c; // Rick n - store previous digit needed to distinguish a 1, 3 or 8
  c = work_digit[outpoint-1]; // still the raw byte as SPI found it, but only those in raw2work lookup table
     
  switch (c)  // Rick n - each graphic bitmap has 16 bytes per digit displayed (plus 6 command digits)
              // Among the digits, each digit has a unique byte (or byte pair)
              //   96, 192, 62, 126 and 127 may also appear in other digits, but unique pairings finalize the selection
              // So whenever I find this unique byte (pair) in a field of digits, I've got a good digit
              //   regardless of what else is in that digit
              // I might also get what appears to be a digit in a field of commands or letters or the clock,
              //   but I will later throw them out unless they are immediately following 4 zeroes in a row
              // Increment g only if a valid digit found

  {
  case 71:
    good_digit [g++] = 0;  // Rick e
    break;
  case 96:  // original 127 remapped to 11 if cp was 0
    if (cp == 192) {good_digit [g++] = 1; }// Rick t
    break; 
  case 67:
    good_digit [g++] = 2;  // Rick d
    break;
  case 62:  // original 32 remapped to 5 if cp was 0
    if (cp == 126) {good_digit [g++] = 3; } // Rick t
    if (cp == 127) {good_digit [g++] = 8; }// Rick t
    // Note: there is a bug where sequence "48" in a valid user ID would register as "488"
    //       since a "4" ends in "127" and the "8" starts with "62"
    //       although these bytes are not part of their "unique" byte pairs
    break; 
  case 19:
    good_digit [g++] = 4;  // Rick d
    break;
  case 33:
    good_digit [g++] = 5;  // Rick e
    break;
  case 60:
    good_digit [g++] = 6;  // Rick d
    break;
  case 1:
    good_digit [g++] = 7;  // Rick d
    break;
  case 35:
    good_digit [g++] = 9;  // Rick d
    break;
  default:
  //time = micros() - time; Serial.println (time, DEC); delay(100); // debug
    break;
  } // end of switch
}  // end of getDigit
Logged

Pages: 1 [2]   Go Up
Jump to: