Monthly Archives: September 2013

Hack: Reading an EEPROM from a Nokia 5110

 Intro

I found an old Nokia 5110 mobile phone from way back in the day (1998?). Decided it was time to take it apart, and in the process get me a nice LCD screen to play around with. I’ve found them on Ebay for like $3, but it seemed a little cooler to reuse an LCD from an actual phone.

While taking it apart however, I found a little gem of a microchip, an AT24C16 16KB EEPROM:

I’ve seen many cool hacks of people taking apart commercial devices and extracting information from them. I’ve used EEPROM’s before, so decided to try and recover the data from this one, more for a little practice in the process, rather then wanting to extract any real data from it.

Given this phones popularity, i was able to pretty quickly find a schematic and service guide for the phone, and a datasheet for the AT24C16 chip. The chip is a 8x2KB (16KB) I2C bus chip. This basically means that the data is organised into 8 pages of 2KB of data, and the information is read and written using the I2C protocol.

Removing the Chip, and making it Usable

First things first, get the chip de-soldered from the board, and get it into a form i can use. The chip is a SOP-8 chip, so can be pretty tricky to work with. Though, i have recently taught myself SMD soldering using the ‘Simon’ kit from Sparkfun, so felt ready to take on the challenge.

I used a bit of solder wick to remove as much solder as possible, and then the chip could be then just gently pried off using a small screwdriver. With the chip free of the board, i then soldered in back onto a SOP-DIP converter board (Another ebay cheapie). This allowed for a easy way to interface the chip using a breadboard only.

Accessing Data with the Bus Pirate

An interesting difference here between this chip and other EEPROM’s i’ve used before. The I2C address is used to select which page to read the data from. This is an interesting design consideration, as it means the A0, A1, & A2 address select lines cannot be used, and only on 16K chip can be used on the bus at one time. Data can be retrieved using this procedure outlined in the datasheet:

Basically, write to the I2c address (0xA1-0xAF, odd numbers only), and the byte you wish to read (0x00-0xFF). Then send a read request. I’ve also listed out the addresses to use to get to each page of the EEPROM

PageRead AddressWrite Address
00xA10xA0
10xA30xA2
20xA50xA4
30xA70xA6
40xA90xA8
50xAB0xAA
60xAD0xAC
70xAF0xAE
Addresses for reading/writing to each page of 32 bytes.

With all the theory done, it was time to hook up the EEPROM to my Bus Pirate, and start read some data! Notice in the below picture the two pullup resistors. The Bus Pirate has the ability to use internal pullup resistors, but where possible, i like to use my own. I’ve used 4.7k resistors here, same as what are used in the Nokia 5110 schematic.

Connections

Rather then drawing a circuit diagram, I’ll just list out the networks attached to the 24C16

  1. GND
  2. GND
  3. GND
  4. GND
  5. MOSI on the Bus Pirate, and a 4.7K resistor to +5V
  6. CLK on the Buspirate, and a 4.7K resistor to +5V
  7. WP to +5V (to inhibit writing to the EEPROM)
  8. +5V

The +5V and GND from the Bus Pirate to the power rails on my breadboard.

Using the Bus Pirate

Minicom in linux has been used to access the Bus Pirate, but any terminal can be used really. The settings for a bus pirate are 115000, 8-N-1, No hardware flow control (This stopped it working with my version of the bus pirate).

First, setup the I2C parameters. I’m using the Bus Pirate to provide power, so the power supply also needs to be turned on.

HiZ>m
   4. I2C
(1)>4
 Set speed:
   3. ~100KHz
(1)>3
 Ready
I2C>W
 Power supplies ON

Now scan the bus I2C. You can see the addresses returned match the address listed in the earlier table.

I2C>(1)
  Searching I2C address space. Found devices at:
  0xA0(0x50 W) 0xA1(0x50 R) 0xA2(0x51 W) 0xA3(0x51 R) 0xA4(0x52 W) 0xA5(0x52 R) 
  0xA6(0x53 W) 0xA7(0x53 R) 0xA8(0x54 W) 0xA9(0x54 R) 0xAA(0x55 W) 0xAB(0x55 R) 
  0xAC(0x56 W) 0xAD(0x56 R) 0xAE(0x57 W) 0xAF(0x57 R)

Now all that is left is to read some data!

** Read 4 bytes from Page 1, 0x00
I2C>[0xA0 0x00] [0xA1 rrrr]
  I2C START BIT
  WRITE: 0xA0 ACK 
  WRITE: 0x00 ACK 
  I2C STOP BIT
  I2C START BIT
  WRITE: 0xA1 ACK 
  READ: 0x61 
  READ:  ACK 0x73 
  READ:  ACK 0xCA 
  READ:  ACK 0x82 
  NACK
  I2C STOP BIT

** Read 2 bytes from Page 4, 0x55
I2C>[0xa6 0x55] [0xa7 rr]
  I2C START BIT
  WRITE: 0xA6 ACK 
  WRITE: 0x55 ACK 
  I2C STOP BIT
  I2C START BIT
  WRITE: 0xA7 ACK 
  READ: 0x3C 
  READ:  ACK 0x19 
  NACK
  I2C STOP BIT

Conclusion

Turned out to be all pretty easy stuff actually! I’m going to follow this through with a Project to read all the data from the EEPROM and dump it to a file. USB enabled of course. And after that, maybe try and access one of those flash chips as well (When my wire wrap cable arrives.) Stay tuned!

 

 

Ebay Cheapies: Arduino Mega and 8×8 LED Matrix w/MAX7219 + ‘Saleae’ Logic Analyzer

 Ebay Cheapies 001

A couple of my ebay cheap buys have arrived. I have an Arduino Mega for $12, and a 8×8 LED Matrix with a (fake) MAX7219 driver for $3. To test them out, i’ve hooked them both up, and wrote some Arduino code to show what they can do.

I usually don’t use Arduino for Projects, but for testing out some parts it works great! Don’t even have to get my MKII programmer out fo the drawer.

Also as a bonus, my knock off Saleae Logic Anayzler ($10) has arrived. I’ve used it to plot out the logic of shifting the data from the Arduino to the MAX7219.

Not much else to say here, so lets get into some pictures, code, and a video!

MAX7219 kit: Pretty simple setup here, the MAX7219 provides 3 data lines to the arduino, which allows the 8×8 matrix to set which leds are to be lit.

ledmatrix_partsledmatrix

Arduino Mega: The back and front of the Arduino Mega. Amazing value at $10, considering the retail price of the non-clone is like $70. The only difference i could tell is the back of the PCB looks to say ‘ROARD’ instead of ‘BOARD’.

mega_frontmega_reverse

‘Saleae’ Logic Anazlyer: A $10 rip off of the Saleae system. Looks nothing like the quite refined Saleae logic, but does the job quite well. Uses the official software form Saleae. Read somewhere that this can be reconfigured to use the USBee software also, but havn’t had a chance to look at that yet.

saleae_clone03

 

saleae_clone02

 

Here is a screen shot showing the analyzer in use, channel description:

  1. MAX7219 Clock line
  2. MAX7219 Load line
  3. MAX7219 Data line
  4. Custom line to show pushing out an entire 8×8 led update

saleae_logic

Here is the video showing a quick demostration of using the MAX7219 kit and the Arduino Mega

 

And finally, the code for doing this. The MAX7219 code has been ripped from the arduino.cc website. The rotary code is my own, which you can read more about in this post here. This will also work on a standard Arduino (clones available on the net form $8!)

You can also find the lastest version of this code on my SVN repo here.

** RECOMMEND using the above WebSVN link until i sort out my Syntax Highligter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#include <Encoder.h>
 
// Delays for certain events in ms
const int ROTARY_DELAY = 100;    // Time between checking for rotary turns
const int BUTTON_DELAY = 800;    // Button debouncing, and repeating
const int FLASH_DELAY = 600;     // How long to flash the entire row after button press
const int CURSOR_DELAY = 200;    // How quickly to flash the cursor led
 
// Pin Assignments
uint8_t rota = 2;
uint8_t rotb = 3;
uint8_t button = 4;
uint8_t dataIn = 5;
uint8_t load = 6;
uint8_t clock = 7;
uint8_t trigger = 12;
 
int maxInUse = 1; 
uint8_t x = 0;
 
byte max7219_reg_noop        = 0x00;
byte max7219_reg_digit0      = 0x01;
byte max7219_reg_digit1      = 0x02;
byte max7219_reg_digit2      = 0x03;
byte max7219_reg_digit3      = 0x04;
byte max7219_reg_digit4      = 0x05;
byte max7219_reg_digit5      = 0x06;
byte max7219_reg_digit6      = 0x07;
byte max7219_reg_digit7      = 0x08;
byte max7219_reg_decodeMode  = 0x09;
byte max7219_reg_intensity   = 0x0a;
byte max7219_reg_scanLimit   = 0x0b;
byte max7219_reg_shutdown    = 0x0c;
byte max7219_reg_displayTest = 0x0f;
 
 
Encoder knobLeft(rota, rotb);
 
 
char id=0;
 
void putByte(byte data) {
  byte i = 8;
  byte mask;
  while(i > 0) {
    mask = 0x01 << (i - 1);      // get bitmask
    digitalWrite( clock, LOW);   // tick
    if (data & mask){            // choose bit
      digitalWrite(dataIn, HIGH);// send 1
    }else{
      digitalWrite(dataIn, LOW); // send 0
    }
    digitalWrite(clock, HIGH);   // tock
    --i;                         // move to lesser bit
  }
}
 
void maxSingle( byte reg, byte col) {    
//maxSingle is the "easy"  function to use for a     //single max7219
 
  digitalWrite(load, LOW);       // begin    
  putByte(reg);                  // specify register
  putByte(col);//((data & 0x01) * 256) + data >> 1); // put data  
  digitalWrite(load, LOW);       // and load da shit
  digitalWrite(load,HIGH);
}
 
void maxAll (byte reg, byte col) {    // initialize  all  MAX7219's in the system
  int c = 0;
  digitalWrite(load, LOW);  // begin    
  for ( c =1; c<= maxInUse; c++) {
  putByte(reg);  // specify register
  putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
    }
  digitalWrite(load, LOW);
  digitalWrite(load,HIGH);
}
 
void maxOne(byte maxNr, byte reg, byte col) {    
//maxOne is for adressing different MAX7219's,
//whilele having a couple of them cascaded
 
  int c = 0;
  digitalWrite(load, LOW);  // begin    
 
  for ( c = maxInUse; c > maxNr; c--) {
    putByte(0);    // means no operation
    putByte(0);    // means no operation
  }
 
  putByte(reg);  // specify register
  putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
 
  for ( c =maxNr-1; c >= 1; c--) {
    putByte(0);    // means no operation
    putByte(0);    // means no operation
  }
 
  digitalWrite(load, LOW); // and load da shit
  digitalWrite(load,HIGH);
}
 
 
void setup () {
  pinMode(dataIn, OUTPUT);
  pinMode(clock,  OUTPUT);
  pinMode(load,   OUTPUT);
  pinMode(button, INPUT);
  pinMode(trigger, OUTPUT);
  digitalWrite(button, HIGH);
  digitalWrite(13, LOW);  
  digitalWrite(trigger, LOW);
 
  //initiation of the max 7219
  maxAll(max7219_reg_scanLimit, 0x07);      
  maxAll(max7219_reg_decodeMode, 0x00);  // using an led matrix (not digits)
  maxAll(max7219_reg_shutdown, 0x01);    // not in shutdown mode
  maxAll(max7219_reg_displayTest, 0x00); // no display test
  for (x=1; x<=8; x++) {    // empty registers, turn all LEDs off
    maxAll(x,0);
  }
  maxAll(max7219_reg_intensity, 0x01 & 0x0f);  // the first 0x0f is the value you can set range: 0x00 to 0x0f
 
}  
 
// Value to display a single LED on a row
uint8_t values[9] = {0, 1, 2, 4, 8, 16, 32, 64, 128};
// Array with all rows, indicating which 'value' to show
uint8_t line[8] = {0, 0, 0, 0, 0, 0, 0, 0}; 
// What row we are current working with
uint8_t idx = 0;
// Our delay counters (in ms)
int delayButton = 0;
int delayRotary = ROTARY_DELAY;
int delayFlash = 0;
int delayCursor = CURSOR_DELAY;
int cursorState = 0;
 
// Rotary position, reset after each loop
long pos = 0;
// Do we update the matrix this run?
uint8_t update = 1;
 
void loop () { 
 
  // Flash the row after a button press
  if (delayFlash && idx != 8) {
    maxSingle(idx+1, 255);
 
    if (delayFlash == 1) {
      maxSingle(idx+1, values[line[idx+1]]);
    }
 
    delayFlash--;
    if (delayButton) delayButton--;
 
    return;
  } 
 
  // Check for button press
  if (delayButton) delayButton--;
  if (!digitalRead(4) && delayButton == 0) { 
    if (idx == 8) 
      idx = 0;
     else
       idx++;
     delayButton = BUTTON_DELAY;
     delayFlash = FLASH_DELAY;
  }
 
 
  // Checks for rotary turning, and update
  //  the matrix buffer
  // My rotary encoder had '2 turns' per detent, making
  //  it awkward to move a single position. This is why
  //  the check is for > 1 and < -1 to make it a full detent.
  if (delayRotary) delayRotary--;
  if (delayRotary == 0 && idx != 8) {
    pos = knobLeft.read();
    if (pos > 1) {
      if (line[idx] != 0)
        line[idx]--;
      else
        line[idx] = 8;
      update = 1;
    } else if (pos < -1) {
      if (line[idx] != 8)
        line[idx]++;
      else
        line[idx]= 0;
      update = 1;
    }
 
    if (update) { 
      knobLeft.write(0);
      delayRotary = ROTARY_DELAY;
 
      if (pos) {
        cursorState = 1;
        delayCursor = CURSOR_DELAY;
      }
    }
  }
 
 
  // Toggle the state of the cursor after the
  //  cursor delay (in ms) has been reached
  if (delayCursor) delayCursor--; 
  if (delayCursor == 0 && idx != 8) {
    delayCursor = CURSOR_DELAY;
    cursorState = 1 - cursorState;
    update = 1; 
  }
 
  // Update the matrix only if a change has been made
  if (update) {
    for (x = 0; x<8; x++) {
       if (x == idx) {
         if (cursorState && line[x] != 0)
             maxSingle(x+1, values[line[x]]);
         else
             maxSingle(x+1, 0);
       } else {
         if (line[x] == 0) continue;
         digitalWrite(trigger, HIGH);
         maxSingle(x+1, values[line[x]]);
         digitalWrite(trigger, LOW);
       }
    }
    update = 0;
  }
 
  delay(1);
 
}