Pages: [1] 2   Go Down
Author Topic: Hack from SPI display to Arduino  (Read 2193 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

I have a time and attendance fingerprint system with no technical documentation or support line.  It reads fingerprints, matches them up to a user ID then logs the read (or other activity) to its memory.  You can access the memory only in batch, such as downloading a day's worth of transactions to a USB card.  I need to know the user ID in "real time". 

The system does display each successfully read user ID on a 128 x 64 LCD display.  The display is similar to DOGM display but not identical.  The motherboard writes to the LCD using SPI at 2.4 mega Hz.  When writing the user ID it uses a graphics mode to draw a picture of the numbers.  Each digit is 11 bits high and 7 bits wide, so it uses two 8x8 bit blocks for each digit.  They are sent as a 3 byte location/command, followed by 16 bytes of data.

Here is my plan: I want to use an Arduino as an SPI slave to monitor the signals being sent to the LCD. I've decoded the motherboard to LCD enough that I know that after the sequence of two bytes with digital 88, 16 I have to capture about the next couple hundred bytes which I can eventually decode to my user ID.  I'll then have a couple of seconds to decode this mess and issue instructions to the rest of my system before going back to monitoring the LCD.

Before I get too invested in this plan, I'd like to know if the Arduino can read SPI at 2.4 megaHz while looking for the "88,16" trigger signal and storing a few hundred bytes of the SPI sequence in memory.  Or am I going to have to build some kind of small hardware buffer.

I'd appreciate any advice on either approach.
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 502
Posts: 19085
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Take a look at my screenshots here:

http://www.gammon.com.au/spi

According to that page, the normal SPI processing is at 4 MHz clock, so 2.4 should be fine.

I suppose you are wearing a white hat, and not a black one?
Logged


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

Thanks, this looks good and gives me a better understanding of SPI.

I'm doing a white hat.  The time and attendance fingerprint reader I'm hacking will be used in an Alzheimer's clinic art class.  Placing the patient's finger in the reader will pop open that specific patient's cabinet door.  The other patients can't accidentally access other patient's work.  The teacher's fingerprint will open any door and/or allow registration of new patients.  The cabinet doors are clear acrylic and family visitors can see the art work in progress.  We might optionally allow the family to register their fingerprint and to open their relative's cabinet.

Thanks again.  I'd be glad to post my "decoding" algorithm if you think anyone might be interested.
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 502
Posts: 19085
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Sounds interesting. smiley
Logged


nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 129
Posts: 8601
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You should be able to read the bytes just fine but I wouldn't spent much time playing with them until you have the lot.

If there's no gap between the bytes you only have about 400nS to read data from the SPDR register so interrupts are out as well.

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

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

Well, a little progress, but I'm stuck again.  To bone up on SPI I used Nick's article, thank you.  I set up an Arduino 2009 as a slave and a Mega1280 as the Master.  I put the logic analyzer pins on the slave and got them communicating the best using the second pair of Nick's programs.  I got the Master to simulate the signals and pictures that are coming from the Time and Attendance LCD and hacked enough of a program on the Slave so I could identify the graph/picture for at least the digits 0-9.  I got the interrupt routine short enough to work at 4 megaHz and since the T&A system is at 2.4 megaHz I thought it was time to move on.

Then I hooked the Slave and logic analyzer to the T&A LCD and ran into what turned out to be a big problem.  Both the clock and the Slave Select voltages on the T&A system are inverted (voltage high vs. low) from the way SPI is set up in the SPI library.  There is a clock polarity setting in SPI.setDataMode() which I think solves the former, but I can't find any way to change the Slave Select polarity. 

Any ideas in software or settings?

I'm not much with electronic hardware but I'm sure there is some easy way to invert either of these signals with a transistor or two.  Any reference to how to do that?

PS.  My son is visiting his in-laws in Florida.  They are originally from somewhere just north of Melbourne, I think Seymour?
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 502
Posts: 19085
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

  There is a clock polarity setting in SPI.setDataMode() which I think solves the former, but I can't find any way to change the Slave Select polarity. 

Any ideas in software or settings?

Well, slave select is pretty-much manual, so you just do a digitalWrite HIGH rather than LOW, and you solved that one.

Quote
PS.  My son is visiting his in-laws in Florida.  They are originally from somewhere just north of Melbourne, I think Seymour?

About two hours drive north, but yes, I know Seymour.
Logged


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

Thanks, Nick.  I got the clock mode fixed and found another line from the motherboard with a byte frame signal that I'm using as a SlaveSelect signal with the correct polarity.  This causes a minor problem in that I have to also look at and discard all the command bytes, but no big deal.

Thanks, Graynomad.  Actually there are about 10 bits per byte so I have about 4 microsec to do something with the SPDR byte before it gets overwritten.  The time and attendance to LCD clock is running at about 2.4 megaHz (400 nanosec).  4 microsec is about 64 Arduino assembly instructions.

I simulated the LCD with another Arduino at 4 megaHz SPI and got everything hobbling along.  However, when I wire up to the Time and Attendance LCD I'm losing about 5% of the bytes in the Interrupt Service Routine.  I'm getting clear bytes on the logic analyzer.  I put debug timers in the ISR and with Arduino microsec() resolution I get mostly 4 microsec but occasionally 8 microsec for the loop, which is marginal.  I don't know what causes the longer ISR or whether that's when I lose a byte.  Since each valid userID has 8 digits in it I'd have to have the patients rescan about 40% of the time, which I think is unacceptable.  Even worse might be the occasional false positive.

I've written the ISR with a switch case conditional to capture any of the 10 numerals it might be looking at.

Here is the code.  Any idea how to speed it up?  Or do I need to try a faster processor like the Maple?

Quote
// SPI interrupt routine
ISR (SPI_STC_vect)
{
  //unsigned int time = 0; time = micros(); // debug
  cp = c; //  store previous digit needed to distinguish a 1 or 8
  c = SPDR; // SPDR is the byte as SPI finds it
     
  switch (c)  // The T&A LCD displays the userID as an 8 digit graphic including 4 leading zeroes
               // each graphic bitmap has 16 bytes per digit displayed
              // Among the displayed digits, I found each digit has a unique byte (or byte pair) somewhere in it
              // So whenever I find this unique byte in a field of digits, I've identified a digit
              // I might also get these "unique bytes" in a field of commands or letters, but never with 4 leading zeroes
              // Whenever I find a likely digit I store it in the buffer array digit[] to extract the userID later
  {
  case 71:
    digit [j++] = 0;  // Rick e
    break;
  case 96:
    if (cp == 192) {digit [j++] = 1; }// Rick n
    break; 
  case 67:
    digit [j++] = 2;  // Rick d
    break;
  case 30:
    digit [j++] = 3;  // Rick d
    break; 
  case 19:
    digit [j++] = 4;  // Rick d
    break;
  case 33:
    digit [j++] = 5;  // Rick e
    break;
  case 48:
    digit [j++] = 6;  // Rick d
    break;
  case 1:
    digit [j++] = 7;  // Rick d
    break; 
  case 62:
    if (cp == 127) {digit [j++] = 8; }// Rick n
    break;
  case 35:
    digit [j++] = 9;  // Rick d
    break;
  default:
  //time = micros() - time; Serial.println (time, DEC); delay(100); // debug
    break;
  } // end of switch
}  // end of interrupt service routine (ISR) SPI_STC_vect




Thanks in advance, Rick.

Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 502
Posts: 19085
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

How have you defined all those variables?

Possibly the 5% is because of the timer interrupt kicking in. You could probably turn that off, however then micros() and millis() wouldn't work.

What would probably be faster than a switch would be a table lookup. That is, directly index into a table to find the correlation between the code you receive and the translated code. You would still have a problem with 1 and 8 though.

The other thing would be to stuff it into a buffer (eg. buf [pos++] = SPDR; ). Then worry about interpreting the buffer when you have time on your side.
Logged


nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 129
Posts: 8601
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Any idea how to speed it up?
If you can afford 72 bytes for a sparse array lookup table and use a pointer instead of indexing into an array it should be faster

Code:
// SPI interrupt routine

volatile BYTE vals [72] = {x,x,x,x,x,}; // put values in here, 0 if not used
volatile BYTE * j = digit;

ISR (SPI_STC_vect)
{
  cp = c; //  store previous digit needed to distinguish a 1 or 8
  c = SPDR; // SPDR is the byte as SPI finds it
     
if (c == 96 && cp == 192) {
*j = 1;
} else if (c == 62 && cp == 127)
*j = 8;
} else {
*j = vals[c];
}
j++;


Not tested (or even compiled) and I'm sure the first two ifs could be removed with some thought and more knowledge of the data, but that has to be faster.

Also you are losing a hell of a lot of time just servicing the ISR, polling will be faster.
______
Rob
« Last Edit: December 27, 2011, 11:53:21 pm by Graynomad » Logged

Rob Gray aka the GRAYnomad www.robgray.com

Global Moderator
Online Online
Brattain Member
*****
Karma: 502
Posts: 19085
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well he says it works 95% of the time, so a bit of speed up in array lookups might help. And if he is using int rather than byte you could save a bit there.

However I agree with Rob, I reckon it takes around 3.5 uS to enter an ISR (we looked into it before, there are about 22 54 clock cycles involved) so if time is critical, polling would probably be faster. Of course you have to stop polling and do something with the results at some stage. If you do poll, you could turn off all interrupts (thus stopping the timer interrupts) which should make it more reliable.
« Last Edit: December 28, 2011, 12:55:05 am by Nick Gammon » Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 502
Posts: 19085
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I worked it out here:

http://arduino.cc/forum/index.php?topic=70625.21

54 cycles to enter the interrupt routine (3.38 uS). And roughly the same amount to leave it.
« Last Edit: December 28, 2011, 12:55:47 am by Nick Gammon » Logged


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

naked_Isr can cut the entry time quite a bit.

Good point about the polling though.
Logged

Bill Rowe
Olduino - An Arduino for the First of Us
www.olduino.wordpress.com

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 129
Posts: 8601
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I've use a naked ISR before but in an app that had to save every reg in a known order so I used ASM pre/postambles.

How would naked work if you just had C code in the ISR? You couldn't use any variables or do anything except IO I would think.

______
Rob 
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Global Moderator
Online Online
Brattain Member
*****
Karma: 502
Posts: 19085
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You could be right there.

Anyway I await a response to reply #8. smiley
Logged


Pages: [1] 2   Go Up
Jump to: