[Solved] Flash Memory Programmer "AM29F040B"

Hi Pito,

As requested, this is code that I have wrote. Erase function still remain for later when write and read are functionnal:

#define SERIALDATA 10
#define SERIALCLK 12
#define LATCH 11

#define WE A3
#define OE A4
#define CE A5

#define A18 A0
#define A17 A1
#define A16 A2

#define OUT 0
#define IN  1

//Change pinMode of D0- D7
void flash_change_pins_mode(boolean io)
{
  if (io) {
    for (int i = 2; i < 10; i++)
    pinMode(i, INPUT);
  }
  else
  {
    for (int i = 2; i < 10; i++)
    pinMode(i, OUTPUT);
  }
}

void flash_ctrl_deselect()
{
  digitalWrite(OE, HIGH);
  digitalWrite(WE, HIGH);
  digitalWrite(CE, HIGH);
}

void flash_ctrl_rd_enable()
{
  digitalWrite(CE, LOW);
  digitalWrite(OE, LOW);
}

void flash_ctrl_rd_disable()
{
  digitalWrite(CE, HIGH);
  digitalWrite(OE, HIGH);
}

void flash_ctrl_wr_enable()
{
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
}

void flash_ctrl_wr_disable()
{
  digitalWrite(CE, HIGH);
  digitalWrite(WE, HIGH);
}

void flash_addr_set(unsigned int address)
{
  digitalWrite(LATCH, LOW);
  shiftOut(SERIALDATA, SERIALCLK, MSBFIRST, (address>>8));
  shiftOut(SERIALDATA, SERIALCLK, MSBFIRST, (address&0xFF));
  digitalWrite(LATCH, HIGH);
}

void flash_data_set(byte data)
{
  flash_change_pins_mode(OUT);
  for (int i = 0; i < 8; i++)
  {
    if (data&bit(i))
    digitalWrite(i+2, HIGH);
    else
    digitalWrite(i+2, LOW);
  }
}

byte flash_data_get()
{
  byte data = 0;
  boolean state = LOW;
  
  flash_change_pins_mode(IN);
  for (int i = 2, j = 0; i <= 9; i++, j++)
  {
        state = digitalRead(i);
        if (state == HIGH)
            bitWrite(data, j, HIGH); 
  }
  return data;
}

byte flash_read(unsigned int address)
{
  byte data = 0;
  
  flash_ctrl_deselect();
  flash_change_pins_mode(IN);
  
  flash_addr_set(address);
  flash_ctrl_rd_enable();
 
  delayMicroseconds(7);
  
  data = flash_data_get();
  flash_ctrl_rd_disable();
  return data;
}

boolean flash_get_DQ7()
{
  boolean state = LOW;
  
  pinMode(9, INPUT);
  state = digitalRead(9);
  if (state == HIGH)
  return true;
  else return false;
}

boolean flash_get_DQ6()
{
  unsigned long duration = 0;
  
  pinMode(8, INPUT);
  duration = pulseIn(8, HIGH);
  if (duration > 0)
  return true;
  else return false;
}

boolean flash_get_DQ5()
{
  boolean state = LOW;
  
  pinMode(7, INPUT);
  state = digitalRead(7);
  if (state == HIGH)
  return true;
  else return false;
}

boolean flash_get_DQ3()
{
  boolean state = LOW;
  
  pinMode(5, INPUT);
  state = digitalRead(5);
  if (state == HIGH)
  return true;
  else return false;
}

boolean flash_get_DQ2()
{
  boolean state = LOW;
  
  pinMode(4, INPUT);
  state = digitalRead(4);
  if (state == HIGH)
  return true;
  else return false;
}

//Check data
boolean flash_byte_write_poll()
{
  boolean state = LOW;
  state = flash_get_DQ7();
  if (state)
  return true; //Under process
  else return false; //Done
}

//Sequence/Command
void flash_write_command(unsigned int address, byte data)
{
    flash_change_pins_mode(OUT);
    flash_addr_set(address);
    flash_data_set(data);
}

void flash_write(unsigned int address, byte data)
{
  
  flash_ctrl_deselect();
  flash_change_pins_mode(OUT);
  
  //Set address and data
  flash_addr_set(address);
  flash_data_set(data);
  flash_ctrl_wr_enable();
  
  //Send command sequence
  flash_write_command(0x555, 0xAA);
  flash_write_command(0x2AA, 0x55);
  flash_write_command(0x555, 0xA0);
  
  flash_ctrl_wr_disable();
}

void flash_erase()
{
}

void setup()
{
  Serial.begin(9600);
  
  //Init A16 - A18 flash pins
  pinMode(A17, OUTPUT);
  pinMode(A16, OUTPUT);
  pinMode(A18, OUTPUT);
  
  //Init pin's shoft register
  pinMode(LATCH, OUTPUT);
  pinMode(SERIALCLK, OUTPUT);
  pinMode(SERIALDATA, OUTPUT);
  
  //Init flash command pins
  pinMode(WE, OUTPUT);
  pinMode(CE, OUTPUT);
  pinMode(OE, OUTPUT);
}

void loop()
{
  if (Serial.available() > 0)
  {
    if (Serial.read() == 'r') {
      int count = 0;
      Serial.println("");
      for (unsigned int addr = 0; addr < 0xFF; addr++) {
        Serial.print(flash_read(addr), HEX);
        Serial.print(" ");
        if (++count == 15) {
        Serial.println("");
        count = 0;
        }
      }
    }
    Serial.flush();
  }
  
  if (Serial.available() > 0)
  {
    if (Serial.read() == 'w')
    {
      for (unsigned int addr = 0; addr < 100; addr++)
      {
        byte value = 0xAB;
        Serial.println("");
        flash_write(addr, value);
        while (flash_byte_write_poll()) {
          Serial.print("*");
          delay(5);
        }  
        Serial.print("Byte ");
        Serial.print(addr);
        Serial.println(" Written");
      }  
      Serial.println("Operation complete");
    }
    Serial.flush();
  }
}

I tested it out but still no result.
There is one thing which I can't understand why I should execute device command at last ? shouldn't be at first ?

What I'm supposed to do now ?

Thanks.

First glance:
use only these 3 functions:

void flash_ctrl_deselect()
{
  digitalWrite(OE, HIGH);
  digitalWrite(WE, HIGH);
  digitalWrite(CE, HIGH);
}

void flash_ctrl_rd()
{
  digitalWrite(CE, LOW);
  digitalWrite(OE, LOW);
}

void flash_ctrl_wr()
{
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
}

Mind - for the addresses: the "unsigned int" is 16bit, you need "unsigned long" (or simply uint32).. !! 16+8bit!!

// read a single byte from flash
byte flash_read(unsigned long address)
{
  byte data = 0;  
  flash_ctrl_deselect();
  flash_change_pins_mode(IN);  
  flash_addr_set(address);
  flash_ctrl_rd();  // rd/oe goes LOW
  delayMicroseconds(1);  // 1usec is ok when reading the flash (70ns is min)  
  data = flash_data_get();
  flash_ctrl_deselect();  // rd/oe goes HIGH, cs goes HIGH
  return data;
}

BTW: Polling a bit on the data bus require "while()":

boolean flash_get_DQ7()
{...
while (...) ... ;
..
}

Mind - for the address: the "unsigned int" is 16bit, you need "unsigned long" (or simply uint32).. !! 16+8bit!!

void flash_addr_set(unsigned long address)
{
  digitalWrite(LATCH, LOW);

  // YOU HAVE TO SET THE HIGHEST 8BITS SOMEWHERE !!
  // for example a function:
  set_highest_flash_address(address>>16);

  shiftOut(SERIALDATA, SERIALCLK, MSBFIRST, (address>>8));
  shiftOut(SERIALDATA, SERIALCLK, MSBFIRST, (address&0xFF));
  digitalWrite(LATCH, HIGH);
}
// write flash Command - using min 70ns long write
void flash_write_command(unsigned long address, byte data)
{
    flash_ctrl_deselect();  
    flash_change_pins_mode(OUT);
    flash_addr_set(address);
    flash_data_set(data);
    flash_ctrl_wr();  //wr goes LOW
    delayMicroseconds(1);  // for example 1us delay
    flash_ctrl_deselect();    //wr goes HIGH, cs goes HIGH
}

YOU NEED:

// write flash memory Data byte - with 10ms delay or POLLING the data bit - see the datasheet
void flash_write_data(unsigned long address, byte data)
{
    flash_ctrl_deselect();  
    flash_change_pins_mode(OUT);
    flash_addr_set(address);
    flash_data_set(data);
    flash_ctrl_wr();   //wr goes LOW
    delay(10);  // for example 10ms delay OR POLLING THE DQ7
    flash_ctrl_deselect();    //wr goes HIGH, cs goes HIGH
}
// write single byte into the flash (Commands + Data)
void flash_write(unsigned long address, byte data)
{  
  //Send the command sequence
  flash_write_command(0x555, 0xAA);  //this is with 1usec delay (70ns is enough)
  flash_write_command(0x2AA, 0x55);  //this is with 1usec delay (70ns is enough)
  flash_write_command(0x555, 0xA0);  //this is with 1usec delay (70ns is enough)

//HERE YOU HAVE TO WRITE THE BYTE INTO THE FLASH:
  flash_write_data(address, data);  //this is a write with 10ms delay or with POLLING THE DQ7  
}

The last function in setup() shall be "flash_ctrl_deselect();" that is the default state !

Usually the next step is to check each function, whether it works properly or not (call the single function and check the pins with voltmeter or oscilloscope or logic analyzer). All above functions must work properly.

Be aware of pin direction setting. Double check.

Mind your address is more than 16bit! Try to write a single byte, and read it back. Do it for few addresses, step by step.
Do not expect it will work on the first attempt. It takes time.. There is a lot of signals and wirings you have to clean up.. It takes time..

Do not try to optimize/simplify the code unless it works - it means you flashed/erased your memory successfully at least 20x :slight_smile:

Do not use polling yet - use the 10-20ms delay as described above - 10ms is usually enough flash write time. Do not decrease the time yet. Rather wait few minutes to write the entire flash. When it works rock stable then you may start with code optimization and/or polling the DQx instead of using the fixed delay..
HTH

Hi pito and everyone,

Sorry for the late response.

I'm agree with you that programming flash will take time, this is why I decided to study and read over and over the datasheet to understand how the chip really works. However, I'm struggling to understand some sentences in the datasheet, if someone please can help me to find out what these phrases mean:

Byte Program Command Sequence

Programming is a four bus cycle operation. The program command sequence is initiated by writing two unlock write cycles, followed by the program set-up command. The program address and data are written next, which in turn initiate the Embedded Program algorithm. The system is not required to provide further controls or timings. The device automatically provides internally generated program pulses and verify the programmed cell margin. The command definitions take shows the address and data requirements for the byte program command sequence.

What's set-up command ? is the (CE#, OE#, WE#) command register ?
Is unlock write cycles contain address and data ?

Another one:

Programming is allowed in any sequence and across sector boundaries. A bit cannot be programmed from a "0" back to a "1". Attempting to do so may halt the operation and set DQ5 to "1".

Is that means that only erase operation can do this ?
How can I know if command registers (CS, WE, OE) are sent after a set of address and data or before ?

Please those questions are important for me to understand.

Regards.

What's set-up command ? is the (CE#, OE#, WE#) command register ?
Is unlock write cycles contain address and data ?

Read carefully the table 4.
Programming Set-up command is writing the 0Ah at the address 555h ("cycle" n.3).
(CE#, OE#, WE#) - those are just signals you have to bitbang during each cycle as required.
Unlock write cycles does contain address and data:
adr data

  1. cycle: 555 aa //unlock
  2. cycle: 2aa 55 //unlock
  3. cycle: 555 0a // setup command
  4. cycle: PA PD // actual address and data to be written into flash

Is that means that only erase operation can do this ?
How can I know if command registers (CS, WE, OE) are sent after a set of address and data or before ?

Yes.
(CS, WE, OE) - those are not command registers but signals. Those signals are set by calling
void flash_ctrl_deselect()
void flash_ctrl_rd()
void flash_ctrl_wr()..
You know where to sent them because you know how a typical rd/wr into a memory works.

Thanks for you quick reply,

Can you please be more specific about this:

You know where to sent them because you know how a typical rd/wr into a memory works.

My question was when can I send them ? Because reading the datasheet especially in Table 1 (Device bus operations), they just mention the requirements to read or write data, there is any order how to send them. To be more clear this is an example of what I mean

Should do like this:

1- flash_addr_set()
2-flash_data_set()
3-flash_ctrl_rd() //send address and data then enable read operation

or like this:

1- flash_ctrl_rd() //Enable read operation and send address and data
2-flash_addr_set()
3-Flash_data_set()

I would like to know the same thing about write and erase operation.

Thanks.

Set the address and data first, then do flash_ctrl_rd(). The same with write. Mind the delays. Erase is a sequence of commands (see table 4).

I made an sst39sf040 programmer you can base your programmer off my code see
This will run on your arduino uno note that I used avr-gcc to compile it instead of the arduino ide GitHub - ComputerNerd/sst39sf040-avr: This is the avr-side of the SST39F040 flash programmer.
This will run on your computer GitHub - ComputerNerd/sst39sf040-computerside: This is the other part of the SST39F040 that runs on the computer. Note that I do not take credit for rs232.c and rs232.h

Hi everyone,

Regarding the flash memory programming, I can say now that I can read the eeprom. I cleaned up the circuit (Pulldown resistors & shift registers ...). I'm sure now because when I take WE off it returns 0 rather than FF. (may be wrong please advise)

I used this logic to get data, any other way won't work I'll get just 0s:
flash_ctrl_deselect();
flash_ctrl_rd();
flash_addr_set();
flash_get_data();
flash_ctrl_deselect();

Howewer, the remaining challenge now is Writing program I can't actually figure out how to do this. I tried several algorithm from the writing timing Diagram without success.

Please in the attachement the timing diagram of Write operation, and below the flow of what I have understood from it. If there is any recommandation or correction please don't hesistate to do it to help me out:

Scenario 1:

  • CE high to low
  • OE HIGH
  • WE HIGH
  • Command 555
  • WE to LOW
  • Data command
  • WE to HIGH
  • CE High to low
  • PA
  • WE to LOW
  • PD
  • WE to HIGH
  • delay 10 us

Scenario 2:

  • CE high & WE HIGH & OE HIGH/li]
  • Address command 555h
  • CE to LOW
  • WE to LOW
  • data command A0h
  • WE to HIGH
  • PA
  • CE High to low
  • WE to LOW
  • PD
  • WE to HIGH
  • CE to HIGH
  • delay 10 us

What do you think about them ? what's the one that you think it may work for me.

Any other clue or idea will be appreciated.

Thanks.

Timing is critical when you are trying to run fast - like connected in circuit to a CPU. To read data -t is important that you properly set the address inputs before you try to set the chip selects. * WE or *OE can be set before the *OE. Timings on the chart are generally the minimums - meaning that there is a minimum time from the address line being set before the *CE and then some delay between the *CE before the *OE or *WE have to be stable. But a best practice is set the *OE or *WE at he same time the *OE is set.

To use some of the features - sector erase for instance - you do need to be able to control all address pins.

Thanks for this discussion - I missed it when I asked if anyone had any experience with these devices... I am preparing to build a Z80 CPM single board computer, and maybe even a 65C02 based board to play with. Will be making a Flash Programmer shield to program these devices.

Thanks for your reply kf2qd,

If understand, the timing is critical for any operations. I was thinking if it's possible to bypass this problem especially for those who are not familiar with electronics, to just use the AVR interrupts and check if there is any data coming from an external device (Like EEPROM or Flash) in the reading operation for instance.

I was thinking of that, because even my reading algorithm doesn't work too, and I try to figure out if there is any missing thing that I forgot (shiftregister didn't give the correct address, timings) . I have changed my code from the arduino style to AVR style to increase the speed of the functions, unfortunately, no result too. The remaining idea that I have now is the pinchange interrupts, after that, if nothing seems to work ... :.

I have a questions if someone can clarify these for me:
using the avr style code, Can I be able to receive and detect if there is any input coming from an external device even if the arduino pins are in output mode and not set to HIGH (Read PIND or PINB) ?
If the answer of the above question is yes why we whould change the pin mode ?
How can I test if my flash are working or not ? Because it's very weird that til now nothing is working for me.

Thanks and sorry for the inconvenience.

The timing is not critical with your application, as the arduino's bitbanging is much slower than the minimal timing requirements of the flash memory. As I wrote previously, try to get the stuff working in slow mode, speed optimization is long way to go (and not required for your programmer..

I was thinking of that, because even my reading algorithm doesn't work too

How do you know that? Do you have a programmed chip with known data in it? Or do you try to read empty chips?

You can take a memory chip. put it on a breadboard and wire it fir an address and then wire the *OE to ground and then wire the *CE to ground and it will put the data for that address on the data lines. timing is not a problem. That timing chart just shows the minimum times. Can't run the chip any faster than those timings. Can run it way slower...

Are you sure what you are putting on the address lines? Verify address inputs before you worry about anything else. If they aren't correct then everything else is a waste of time.

Several decades back we programmed PROMS and EEPROMs with 16+8 dil switches with 24 10k pullups, a 27V power source (w/ a pnp high side switch) and a push button (with a 7400 10ms mono). And it worked fine :slight_smile:

To answer you Pito,

How do you know that? Do you have a programmed chip with known data in it? Or do you try to read empty chips?

In fact, I don't know If the flash are empty or not, because I found a weird behavior: when I try to send a command sequence to the flash, the data is put too no problem here, however when I try to read data, is always equal the data puted before even if i changed the pins in the input mode. This is why I asked this question:

using the avr style code, Can I be able to receive and detect if there is any input coming from an external device even if the arduino pins are in output mode and not set to HIGH (Read PIND or PINB) ?

After several checks the data is always 0 not 0xFF as the flash should be, this is the reason that I was thinking about interrupts.
To answer you kf2qd I'am sure about the address lines I checked one by one using multiple addresses.

If you need something to check from your side (Code or circuit) don't hesitate to tell me.

Many thanks.

Here's a drawing of the schematic that will work with the programs that PITO created.

If there are any errors,please let me know Pito.

Pito - with your programs - how are you feeding the data to the programmer?

sst39sf010 PROGRAMMER.bmp (1.72 MB)

In fact, I don't know If the flash are empty or not, because I found a weird behavior: when I try to send a command sequence to the flash, the data is put too no problem here, however when I try to read data, is always equal the data puted before even if i changed the pins in the input mode.

When reading the flash you do not issue any special command sequence - you just read the flash with for example:

// read a single byte from flash
byte flash_read(unsigned long address)
{
  byte data = 0x00;  //try to change to 0xFF to see the difference
  flash_ctrl_deselect();
  flash_change_data_pins_mode(IN);  
  flash_addr_set(address);
  flash_ctrl_rd();  // rd/oe goes LOW
  delayMicroseconds(1);  // 1usec is ok when reading the flash (70ns is min)  
  data = flash_data_get();
  flash_ctrl_deselect();  // rd/oe goes HIGH, cs goes HIGH
  return data;
}

Pito - with your programs - how are you feeding the data to the programmer?

Frankly, last time I built my pc controlled eeprom programmer was in '87 where I did it by wiring a 8255A to Atari520ST' ACSI bus (an SCSI HDD bus), wrote a driver in asm, and the main program in gfbasic (or something called like that). I opened a file on the diskette, read binary and flashed a 27C512 EEPROM (Atari TOS upgrade,, six eeproms). That is all I can say to this topic.. :slight_smile:
Based on my experience with this stuff, it works fine when:

  1. you carefully check the wiring (many times..)
  2. you build the sw routines step by step, testing each function carefully
  3. you do not try to speedup/optimize the sw - do it slow and simple, ie connect LEDs to cs, oe, we (such it lits when active), add delays so you can see the sequence, when it works properly remove the delays.
  4. mind that by wiring error swapped data or address lines will write/read the flash properly (against the binary), but it will not work in your final hardware (unless you swap the lines there in the same way too)..

Feeding the data: that is the last issue I would mess with at this stage. First, try to flash several bytes (ie 4) to 4 addresses somewhere. Verify. Erase, and do it again (different data, different addresses). Repeat many times. When it works fine, then start to think how to pass the data (via serial, from an sdcard, etc).

@kf2qd: Your schematics: I would use 74HC or HCT 595 shifters, and do care about all its input pins (10.11.12.13.14) somehow..
PS: I would put pullups (ie an 8x10k sip module) on the data bus, thus I will read 0xFF even the memory is deselected.

Pito,
I mean command sequence not in read operation but in write operation of course, the read operation always return 0 but when it comes to write to the flash you must change the mode of the pins in input to read data. The idea is that I should clear the address "Data = 0x00" as you mentionned in your last post.

I understand now your method to do things I'm sure that the addresses are correct, I have just to test read operation closely until it works fine, same thing for write and so on. (this time I will be careful with delays)

I will keep you informed about everything and sorry for my limited knowledge about electronics. Your help is very helpful :).

Many thanks

Learn how to test the stuff step by step. Do not hurry, quality needs time :slight_smile:
You do not need the flash memory chip inserted into the circuit for testing purposes.

For example put a few pullups (10k or similar) on a few data lines and run your flash_read(). See what it returns (it shall return 1 where the pullups are wired).

Short the pins to ground with ie 330ohm pulldowns (10k pullups still there) and read with flash_read() (you have to read 0 where the 330ohm pulldowns are wired).

Observe the data - that way you may check the data bus reads well. Do not care about addresses while checking the databus.

Try to write something on the data bus (ie 0xFF or 0x00), and then read it back while the resistors there. You have to read the logic levels set by resistors at the specific pins.

Do not short atmega's pins with a straight wire to gnd or Vcc directly, but always via the resistors - ie 10k pullup (to Vcc) and 330ohm pulldowns (to gnd).

Do not use 330ohms pulldowns in your final wiring, use it for testing purposes only (to set logical 0).

I would also place 8x10k pullups on the databus, and 10k pullups on CE, OE, WE signals, as well as on all active 595 input signals..

Yeah sure Pito, but if data is coming from the flash and the pullups are still there the result will be always 1 even if I read the flash or not (because the current flow to the inputs) am I wrong ?
Rather I have put the pulldown resistors in the data bus to be always 0 until something is coming from the flash to change the logic to 5v.

I will try your idea since you are more experienced with this stuff :slight_smile: