I have found a few old EEPROM chips marked as 93C46N (DIP8):
It has only 128 bytes (to be exact 64 words), but it has serious advantage - more than 1 000 000 erase/write cycles, so I'm planning to utilize them for daily recording of some of my devices' indications.
93C46 is serial EEPROM which means it may be accessed via Arduino SPI.
Take a look at the datasheet and wire it up with your Arduino properly:
LED has been added to the circuit to indicate data transmission process and bypass capacitor wired up in order to reduce voltage swing.
Code:
1 // 2 // LED <--> Arduino: 3 // Anode <--> Digital 8 (via 220 Ohm resistor) 4 // Cathode <--> GND 5 // 6 // 93C46 <--> Arduino: 7 // CS <--> Digital 53 (SS) 8 // SCK <--> Digital 52 (SCK) 9 // DI <--> Digital 51 (MOSI) 10 // DO <--> Digital 50 (MISO) 11 // VCC <--> 5V 12 // GND <--> GND 13 // 14 15 #include <avr/io.h> 16 #include <util/delay.h> 17 18 #define CLEAN 0b00000000 19 20 #define DDR93C46 DDRB 21 #define PORT93C46 PORTB 22 #define PIN93C46 PINB 23 24 #define SK PB1 //SCK - Digital 52 25 #define DO PB3 //MISO - Digital 50 26 #define DI PB2 //MOSI - Digital 51 27 #define CS PB0 //SS - Digital 53 28 29 #define READ 0x02 // 10 00ABCD - 0x02 ADDR 30 #define EWEN1 0x00 // 00 11XXXX 31 #define EWEN2 0x30 // - 0x00 0x30 32 #define ERASE 0x03 // 11 00ABCD - 0x03 ADDR 33 #define ERAL1 0x00 // 00 10XXXX 34 #define ERAL2 0x20 // - 0x00 0x20 35 #define WRITE 0x01 // 01 00ABCD - 0x01 ADDR 36 #define WRAL1 0x00 // 00 01XXXX 37 #define WRAL2 0x10 // - 0x00 0x10 38 #define EWDS1 0x00 // 00 00XXXX 39 #define EWDS2 0x00 // - 0x00 0x00 40 41 #define SET_CS PORT93C46 |= (1 << CS) 42 #define CLR_CS PORT93C46 &= ~(1 << CS) 43 44 #define SET_SK {PORT93C46 |= (1 << SK); _delay_us(0.01);} 45 #define CLR_SK PORT93C46 &= ~(1 << SK) 46 47 #define SET_DI PORT93C46 |= (1 << DI) 48 #define CLR_DI PORT93C46 &= ~(1 << DI) 49 50 class SPI_93C46N { 51 private: 52 uint8_t Transfer(uint8_t data) { 53 SPDR = data; 54 while(!(SPSR & (1 << SPIF))); // Wait for SPI interrupt flag 55 return SPDR; // Return byte gathered from SPI data register 56 } 57 void Opcode(uint8_t opcode, uint8_t address) { 58 SET_CS; 59 SPCR &= ~(1 << SPE); // SPI disable 60 _delay_us(1); 61 CLR_SK; 62 SET_DI; // Send start bit 63 SET_SK; 64 CLR_SK; 65 SPCR |= (1 << SPE); // SPI enable 66 Transfer((opcode << 6) | address); // Transmit byte 67 } 68 public: 69 SPI_93C46N() { 70 SPCR = CLEAN // Set SPI control register 71 & ~(1 << SPIE) // SPI interrupt disable 72 | (1 << SPE) // SPI enable 73 & ~(1 << DORD) // MSB first 74 | (1 << MSTR) // SPI master device 75 & ~(1 << CPOL) // SPI 76 & ~(1 << CPHA) // mode 0 77 | (1 << SPR1) // XX/64 MHz 78 & ~(1 << SPR0); // speed 79 SPSR = CLEAN // Init SPI status register 80 & ~(1 << SPI2X); // SPI double speed 81 DDR93C46 = CLEAN // Set up inputs/outputs 82 | (1 << SK) // Serial clock output 83 | (1 << DI) // Data input (MOSI) output 84 | (1 << CS) // Chip select output 85 & ~(1 << DO); // Data output (MISO) input 86 PORT93C46 |= (1 << DO); // Data output (MISO) high 87 CLR_DI; 88 CLR_CS; 89 CLR_SK; 90 _delay_us(1); 91 } 92 ~SPI_93C46N() { 93 // 94 } 95 uint16_t Read(uint8_t address) { 96 uint16_t data = 0; 97 Opcode(READ, address); // READ instruction 98 CLR_DI; 99 SET_SK; // "0" DUMMY bit 100 CLR_SK; 101 SPCR |= (1 << CPHA); // Invert PHA, very important! 102 data = Transfer(0); 103 data = (data << 8) | (Transfer(0)); 104 _delay_us(1); 105 CLR_CS; 106 CLR_DI; 107 SPCR &= ~(1 << CPHA); 108 return data; 109 } 110 void EraseWriteEnable(void) { 111 Opcode(EWEN1, EWEN2); 112 CLR_DI; 113 CLR_CS; 114 SET_CS; 115 _delay_us(0.1); 116 while(!(PIN93C46 & (1 << DO))); // Wait 1 117 CLR_CS; 118 } 119 void EraseWriteDisable(void) { 120 Opcode(EWDS1, EWDS2); 121 CLR_DI; 122 CLR_CS; 123 SET_CS; 124 _delay_us(0.1); 125 while(!(PIN93C46 & (1 << DO))); // Wait 1 126 CLR_CS; 127 } 128 void Erase(uint8_t address) { 129 Opcode(ERASE, address); 130 CLR_DI; 131 CLR_CS; 132 SET_CS; 133 _delay_us(0.1); 134 while(!(PIN93C46 & (1 << DO))); // Wait 1 135 CLR_CS; 136 } 137 void EraseAll(void) { 138 Opcode(ERAL1, ERAL2); 139 CLR_DI; 140 CLR_CS; 141 SET_CS; 142 _delay_us(0.1); 143 while(!(PIN93C46 & (1 << DO))); // Wait 1 144 CLR_CS; 145 } 146 void Write(uint8_t address, uint16_t data) { 147 Opcode(WRITE, address); // WRITE instruction 148 Transfer(data >> 8); // Write data high byte to addreCS 149 Transfer(data & 0xFF); // Write data low byte to addreCS 150 CLR_DI; 151 CLR_CS; 152 SET_CS; 153 _delay_us(0.1); 154 while(!(PIN93C46 & (1 << DO))); // Wait "1" 155 CLR_CS; 156 } 157 void WriteAll(uint16_t data) { 158 Opcode(WRAL1, WRAL2); // WRITE instruction 159 Transfer(data >> 8); // Write data high byte to addreCS 160 Transfer(data & 0xFF); // Write data low byte to addreCS 161 CLR_DI; 162 CLR_CS; 163 SET_CS; 164 _delay_us(0.1); 165 while(!(PIN93C46 & (1 << DO))); // Wait "1" 166 CLR_CS; 167 } 168 }; 169 170 void setup(void) { 171 Serial.begin(115200); 172 _delay_ms(100); 173 Serial.println("EEPROM writing/reading"); 174 pinMode(8, OUTPUT); 175 } 176 177 void loop(void) { 178 SPI_93C46N _93C46N; 179 int i; 180 uint16_t data; 181 _93C46N.EraseWriteEnable(); 182 // for(i = 0; i < 32; i++) 183 // _93C46N.Write(i, 0xDEAD); 184 // for(i = 32; i < 64; i++) 185 // _93C46N.Write(i, 0xBEEF); 186 _93C46N.WriteAll(0xB00B); 187 _93C46N.EraseWriteDisable(); 188 for(i = 0; i < 64; i++) { 189 data = _93C46N.Read(i); 190 analogWrite(8, (unsigned char)(data >> 8)); 191 _delay_ms(50); 192 analogWrite(8, (unsigned char)data); 193 _delay_ms(50); 194 Serial.print(data, HEX); 195 Serial.print(" "); 196 if((i + 1) % 8 == 0) 197 Serial.println(); 198 } 199 analogWrite(8, 0); 200 _delay_ms(10000); 201 }
Thanks for sharing this sketch!
ReplyDeleteI salvaged a similar IC labelled ATMEL480 93C46 (1K) and been trying to get it to work with an Arduino Uno board. I had to make some changes to your sketch as it wouldn't compile for the Uno: PB ports are not recognized as symbols by the compiler. Also the IC I got has an ORG pin for the memory layout: 128 x 8 or 64 x 16.
It works well about 50% of the time when reading the memory in a loop. The loop either starts and keeps going on or gets stuck in the Transfer method for the first Read while waiting for the SPI interrupt flag.
Since am new to the world of electronics, am not quite sure what to look for to fix this. I just ordered an oscilloscope, will definitely help a lot with debugging.
Feel free to have a look at my sketch (and schema made with Fritzing) by using the following link:
https://truck.it/p/VuQiSZ8iwP
If you have any ideas, I'd appreciate any insights you might have!
Cheers!
Thanks, your sketch works perfectly with my AT93C46B.
ReplyDeleteIt works with my Mega only: not with Duemilanove or Uno...
ReplyDeletehow to select 128x8 layout in code? 64x16 works fine!
ReplyDeleteHey
ReplyDeleteAre you still available for some questions ? ;)
Greets
Andi
New to arduino and programming in general, but I want to read a 93c46 chip hex file then copy it to another chip. How and where is the output of the chip displayed?
ReplyDeleteHi, I want to download your sketch but the link's not available anymore. Could you please send me a new link? Thank you.
ReplyDeletehi Tungsten Owl...
ReplyDeletethanks fo this tutorial....
how about 93C86, ?
Hi
ReplyDeleteI used your schema, the same arduino, and part of the code I included in the eprom. I have programmed the HP Color Laser Jet 4500 CHIP this way. I can condition 54% of the drum. OK. The code to paste in arduino below
//
// LED <--> Arduino:
// Anode <--> Digital 8 (via 220 Ohm resistor)
// Cathode <--> GND
//
// 93C46 <--> Arduino:
// CS <--> Digital 53 (SS)
// SCK <--> Digital 52 (SCK)
// DI <--> Digital 51 (MOSI)
// DO <--> Digital 50 (MISO)
// VCC <--> 5V
// GND <--> GND
//
#include
#include
#define CLEAN 0b00000000
#define DDR93C46 DDRB
#define PORT93C46 PORTB
#define PIN93C46 PINB
#define SK PB1 //SCK - Digital 52
#define DO PB3 //MISO - Digital 50
#define DI PB2 //MOSI - Digital 51
#define CS PB0 //SS - Digital 53
#define READ 0x02 // 10 00ABCD - 0x02 ADDR
#define EWEN1 0x00 // 00 11XXXX
#define EWEN2 0x30 // - 0x00 0x30
#define ERASE 0x03 // 11 00ABCD - 0x03 ADDR
#define ERAL1 0x00 // 00 10XXXX
#define ERAL2 0x20 // - 0x00 0x20
#define WRITE 0x01 // 01 00ABCD - 0x01 ADDR
#define WRAL1 0x00 // 00 01XXXX
#define WRAL2 0x10 // - 0x00 0x10
#define EWDS1 0x00 // 00 00XXXX
#define EWDS2 0x00 // - 0x00 0x00
#define SET_CS PORT93C46 |= (1 << CS)
#define CLR_CS PORT93C46 &= ~(1 << CS)
#define SET_SK {PORT93C46 |= (1 << SK); _delay_us(0.01);}
#define CLR_SK PORT93C46 &= ~(1 << SK)
#define SET_DI PORT93C46 |= (1 << DI)
#define CLR_DI PORT93C46 &= ~(1 << DI)
class SPI_93C46N {
private:
uint8_t Transfer(uint8_t data) {
SPDR = data;
while(!(SPSR & (1 << SPIF))); // Wait for SPI interrupt flag
return SPDR; // Return byte gathered from SPI data register
}
void Opcode(uint8_t opcode, uint8_t address) {
SET_CS;
SPCR &= ~(1 << SPE); // SPI disable
_delay_us(1);
CLR_SK;
SET_DI; // Send start bit
SET_SK;
CLR_SK;
SPCR |= (1 << SPE); // SPI enable
Transfer((opcode << 6) | address); // Transmit byte
}
public:
SPI_93C46N() {
SPCR = CLEAN // Set SPI control register
& ~(1 << SPIE) // SPI interrupt disable
| (1 << SPE) // SPI enable
& ~(1 << DORD) // MSB first
| (1 << MSTR) // SPI master device
& ~(1 << CPOL) // SPI
& ~(1 << CPHA) // mode 0
| (1 << SPR1) // XX/64 MHz
& ~(1 << SPR0); // speed
SPSR = CLEAN // Init SPI status register
& ~(1 << SPI2X); // SPI double speed
DDR93C46 = CLEAN // Set up inputs/outputs
| (1 << SK) // Serial clock output
| (1 << DI) // Data input (MOSI) output
| (1 << CS) // Chip select output
& ~(1 << DO); // Data output (MISO) input
PORT93C46 |= (1 << DO); // Data output (MISO) high
CLR_DI;
CLR_CS;
CLR_SK;
_delay_us(1);
}
~SPI_93C46N() {
//
}
uint16_t Read(uint8_t address) {
uint16_t data = 0;
Opcode(READ, address); // READ instruction
CLR_DI;
SET_SK; // "0" DUMMY bit
CLR_SK;
SPCR |= (1 << CPHA); // Invert PHA, very important!
data = Transfer(0);
data = (data << 8) | (Transfer(0));
_delay_us(1);
CLR_CS;
CLR_DI;
SPCR &= ~(1 << CPHA);
return data;
}
void EraseWriteEnable(void) {
ReplyDeleteOpcode(EWEN1, EWEN2);
CLR_DI;
CLR_CS;
SET_CS;
_delay_us(0.1);
while(!(PIN93C46 & (1 << DO))); // Wait 1
CLR_CS;
}
void EraseWriteDisable(void) {
Opcode(EWDS1, EWDS2);
CLR_DI;
CLR_CS;
SET_CS;
_delay_us(0.1);
while(!(PIN93C46 & (1 << DO))); // Wait 1
CLR_CS;
}
void Erase(uint8_t address) {
Opcode(ERASE, address);
CLR_DI;
CLR_CS;
SET_CS;
_delay_us(0.1);
while(!(PIN93C46 & (1 << DO))); // Wait 1
CLR_CS;
}
void EraseAll(void) {
Opcode(ERAL1, ERAL2);
CLR_DI;
CLR_CS;
SET_CS;
_delay_us(0.1);
while(!(PIN93C46 & (1 << DO))); // Wait 1
CLR_CS;
}
void Write(uint8_t address, uint16_t data) {
Opcode(WRITE, address); // WRITE instruction
Transfer(data >> 8); // Write data high byte to addreCS
Transfer(data & 0xFF); // Write data low byte to addreCS
CLR_DI;
CLR_CS;
SET_CS;
_delay_us(0.1);
while(!(PIN93C46 & (1 << DO))); // Wait "1"
CLR_CS;
}
void WriteAll(uint16_t data) {
Opcode(WRAL1, WRAL2); // WRITE instruction
Transfer(data >> 8); // Write data high byte to addreCS
Transfer(data & 0xFF); // Write data low byte to addreCS
CLR_DI;
CLR_CS;
SET_CS;
_delay_us(0.1);
while(!(PIN93C46 & (1 << DO))); // Wait "1"
CLR_CS;
}
};
void setup(void) {
Serial.begin(115200);
_delay_ms(100);
Serial.println("EEPROM writing/reading");
pinMode(8, OUTPUT);
}
void loop(void) {
SPI_93C46N _93C46N;
int i;
uint16_t data;
_93C46N.EraseWriteEnable();
// for(i = 0; i < 32; i++)
// _93C46N.Write(i, 0xDEAD);
// for(i = 32; i < 64; i++)
//_93C46N.Write(i, 0xBEEF);
// _93C46N.Write(i, 0xBEEF);
Serial.println("writing...");
_93C46N.Write(0, 0x0100);
_93C46N.Write(1, 0x5E07);
_93C46N.Write(2, 0x3C06);
_93C46N.Write(3, 0x0002);
_93C46N.Write(4, 0x5700);
_93C46N.Write(5, 0x0515);
_93C46N.Write(6, 0x023F);
_93C46N.Write(7, 0x0200);
_93C46N.Write(8, 0x4816);
_93C46N.Write(9, 0x0200);
_93C46N.Write(10, 0xDA9B);
_93C46N.Write(11, 0x0000);
_93C46N.Write(12, 0xCF00);
_93C46N.Write(13, 0x0100);
_93C46N.Write(14, 0x5E07);
_93C46N.Write(15, 0x3C06);
_93C46N.Write(16, 0x0002);
_93C46N.Write(17, 0x5700);
_93C46N.Write(18, 0x0515);
_93C46N.Write(19, 0x023F);
_93C46N.Write(20, 0x0200);
_93C46N.Write(21, 0x4816);
_93C46N.Write(22, 0x0200);
_93C46N.Write(23, 0xDA9B);
_93C46N.Write(24, 0x0000);
_93C46N.Write(25, 0xCF00);
_93C46N.Write(26, 0x0000);
_93C46N.Write(27, 0x0000);
_93C46N.Write(28, 0x0000);
_93C46N.Write(29, 0x0000);
_93C46N.Write(30, 0x0000);
_93C46N.Write(31, 0x0000);
_93C46N.Write(32, 0x0000);
_93C46N.Write(33, 0x6BF9);
_93C46N.Write(34, 0x0100);
_93C46N.Write(35, 0xA815);
_93C46N.Write(36, 0x0000);
_93C46N.Write(37, 0x0000);
_93C46N.Write(38, 0xCB00);
_93C46N.Write(39, 0x1400);
_93C46N.Write(40, 0x0000);
_93C46N.Write(41, 0x0000);
_93C46N.Write(42, 0x0000);
_93C46N.Write(43, 0x0000);
_93C46N.Write(44, 0x0000);
_93C46N.Write(45, 0x0000);
_93C46N.Write(46, 0x0000);
_93C46N.Write(47, 0x0000);
_93C46N.Write(48, 0x0000);
_93C46N.Write(49, 0x0000);
_93C46N.Write(50, 0x0000);
_93C46N.Write(51, 0x0000);
_93C46N.Write(52, 0x0000);
_93C46N.Write(53, 0x0000);
_93C46N.Write(54, 0x0000);
_93C46N.Write(55, 0x0000);
_93C46N.Write(56, 0x0000);
_93C46N.Write(57, 0x0000);
_93C46N.Write(58, 0x0000);
_93C46N.Write(59, 0x0000);
_93C46N.Write(60, 0x0000);
_93C46N.Write(61, 0x0000);
_93C46N.Write(62, 0x0000);
_93C46N.Write(63, 0x0000);
ReplyDeleteSerial.println("END!");
//_93C46N.WriteAll(0x0000);
_93C46N.EraseWriteDisable();
for(i = 0; i < 64; i++) {
data = _93C46N.Read(i);
analogWrite(8, (unsigned char)(data >> 8));
_delay_ms(50);
analogWrite(8, (unsigned char)data);
_delay_ms(50);
Serial.print(data, HEX);
Serial.print(" ");
if((i + 1) % 8 == 0)
Serial.println();
}
analogWrite(8, 0);
_delay_ms(10000);
}
This post is parted do 3 pcs. copy & paste it tohgether in Adruino IDE.