Saturday, June 26, 2021

0000 0000 0110 0111

25Q32FVSIG FLASH MEMORY

At some point in an Arduino Journey you might lament the "tiny" amount of non-volatile memory (1024 bytes) found on the board. Some projects require data storage such that the data doesn't disappear when the power does!

The use of an SD-card module is possible, but I did see this little PCB on David Johnson-Davies most excellent Technoblogy site a few years ago.

The mastery of this PCB is the ability to plug straight in to a standard Arduino Uno and then be used, for example, as a data logger. For instance if you are doing long-term environmental monitoring then you could set up the project along the following lines:


I have done some of this type of work before (and used a connection to the internet to store the data), but lately it's all been about candles and so I have decided to demonstrate this little wonder board by sampling the output of an actual "flame-driven, likely to get burnt and torch the house, turn off the fire alarm, warn the neighbours, saddle the horses just in case" candle. 

Then, if successful in capturing the data, I could try running the output through an LED to see if the real life random brightness oscillations can be reproduced. 

Here is David's code, modified to include the input of a "button" (I just plug it into VCC or GND). If the button is HIGH then the code is set to capture data and if the button is LOW then we output via PWM to the LED.

/*
   Uses David Johnson-Davies (Technoblogy) DataFlash project to collect
   candle lumens via an LDR and then pump out the random candle goodness
   to an LED depending on the state of a button

   OneCircuit Sunday 6 June  16:54:19 AEST 2021
   YT: https://www.youtube.com/channel/UCTm3eCrN6wk-Ozt9ymyKIIg/videos
   Blog:https://onecircuit.blogspot.com/

   David's site: http://www.technoblogy.com/show?2JMU

*/

const byte analogInPin = A0;      // pin for LDR
const byte buttonPin = 8;         // pin for button
const byte outputPin = 6;         // pin for LED
boolean buttonPressed = false;    // is the button pressed?
int sensorValue = 0;              // initialise sensor reading
byte outputValue = 0;             // initialise output

// change these to suit
const int sizeofbuffer = 1000;
const int numberofbuffers = 40;
byte readdelay = 5;

byte flamedata[sizeofbuffer];     // data stored in buffer first

// start of David's code
#define PAGEPROG      0x02
#define READSTATUS    0x05
#define READDATA      0x03
#define WRITEENABLE   0x06
#define CHIPERASE     0x60
#define READID        0x90

const byte sck = 13;
const byte miso = 12;
const byte mosi = 11;
const byte cs = 10;
const byte power = 9;

class DF {
  public:
    boolean Setup();
    void BeginRead(unsigned long addr);
    void BeginWrite(void);
    uint8_t ReadByte(void);
    void WriteByte(uint8_t data);
    void EndRead(void);
    void EndWrite(void);
  private:
    unsigned long addr;
    uint8_t Read(void);
    void Write(uint8_t);
    void Busy(void);
    void WriteEnable(void);
};

boolean DF::Setup () {
  uint8_t manID, devID;
  pinMode(power, OUTPUT); digitalWrite(power, HIGH);
  digitalWrite(cs, HIGH); pinMode(cs, OUTPUT);
  pinMode(sck, OUTPUT);
  pinMode(mosi, OUTPUT);
  pinMode(miso, INPUT);
  digitalWrite(sck, LOW); digitalWrite(mosi, HIGH);
  delay(1);
  digitalWrite(cs, LOW);
  delay(100);
  Write(READID); Write(0); Write(0); Write(0);
  manID = Read();
  devID = Read();
  digitalWrite(cs, HIGH);
  return (devID == 0x15); // Found correct device
}

void DF::Write (uint8_t data) {
  shiftOut(mosi, sck, MSBFIRST, data);
}

void DF::Busy () {
  digitalWrite(cs, 0);
  Write(READSTATUS);
  while (Read() & 1 != 0);
  digitalWrite(cs, 1);
}

void DF::WriteEnable () {
  digitalWrite(cs, 0);
  Write(WRITEENABLE);
  digitalWrite(cs, 1);
}

void DF::BeginRead (unsigned long start) {
  addr = start;
  Busy();
  digitalWrite(cs, 0);
  Write(READDATA);
  Write(addr >> 16);
  Write(addr >> 8);
  Write(addr);
}

uint8_t DF::Read () {
  return shiftIn(miso, sck, MSBFIRST);
}

void DF::EndRead(void) {
  digitalWrite(cs, 1);
}

void DF::BeginWrite () {
  addr = 0;
  Busy();
  // Erase DataFlash
  WriteEnable();
  digitalWrite(cs, 0);
  Write(CHIPERASE);
  digitalWrite(cs, 1);
  Busy();
}

uint8_t DF::ReadByte () {
  return Read();
}

void DF::WriteByte (uint8_t data) {
  // New page
  if ((addr & 0xFF) == 0) {
    digitalWrite(cs, 1);
    Busy();
    WriteEnable();
    digitalWrite(cs, 0);
    Write(PAGEPROG);
    Write(addr >> 16);
    Write(addr >> 8);
    Write(0);
  }
  Write(data);
  addr++;
}

void DF::EndWrite (void) {
  digitalWrite(cs, 1);
}

DF DataFlash;

// end of David's code

void setup() {

  // look for the DataFlash module
  Serial.begin(57600);
  while (!Serial);
  if (!DataFlash.Setup()) {
    Serial.println("DataFlash not found");
    for (;;);
  }

  // initialise pins
  pinMode(analogInPin, INPUT);
  pinMode(buttonPin, INPUT);
  pinMode(outputPin, OUTPUT);
  delay(2000);

  // are we collect data or output data?
  buttonPressed = digitalRead(buttonPin);
}

void loop() {

  // collect data
  if (buttonPressed) {
    digitalWrite(outputPin, LOW);
    Serial.println("Collecting and writing data:");
    DataFlash.BeginWrite();

    for (byte readings = 0; readings < numberofbuffers; readings++) {
      for (int writenum = 0; writenum < sizeofbuffer; writenum++) {
        sensorValue = analogRead(analogInPin);
        outputValue = map(sensorValue, 100, 900, 0, 230);   // change to suit candle/lighting/etc
        if ((outputValue < 5) or (outputValue > 240)) {     // clean up rough edges
          outputValue = 0;
        }
        flamedata[writenum] = outputValue;                  // write to buffer
        delay(readdelay);                                   // delay between readings
      }

      // now write buffer to DataFlash module
      for (int writenum = 0; writenum < sizeofbuffer; writenum++) {
        DataFlash.WriteByte(flamedata[writenum]);
      }
    }
    DataFlash.EndWrite(); // done writing
    while (1) {};         // just wait, could flash LED?
  }

  // output data to LED
  if (!buttonPressed) {
    while (1) {
      DataFlash.BeginRead(0);
      for (byte readings = 0; readings < numberofbuffers; readings++) {
        for (int writenum = 0; writenum < sizeofbuffer; writenum++) {
          byte thisdata = DataFlash.ReadByte();
          analogWrite(outputPin, thisdata);
          delay(readdelay);
        }
      }
      delay(10);
      DataFlash.EndRead();
      delay(10);
} } }

The code seems to work fine, but some real issues emerged with the construction of David's flash module.

1. I oven baked the first one with the worst application of solder paste seen since the great solder paste disaster of 1687 in Cornwall. The program reported "DataFlash not found".

2. The first couple of soldered modules also came back "DataFlash not found".

3. Several versions of 3.3V regulator needed to be tested until I found a couple of guys that worked as advertised.

4. Quite a few of the Winbond 25Q32FVSIG chips were either faulty or I fried them in the process - a separate blog will look at these chips - are they really dead?

5. The wackiest problem? The diodes linking the flash memory to the input/output as per David's circuit stops the whole shebang from working? I've indicated this below with menacing red circles.

In a fit of rage/genius I simply removed them. The modules then worked and the universe celebrated (well, I celebrated).


Net result? Four fully working 4Mb flash memory boards that can sit on the edge of the Arduino Uno doing it's thing and record data happily, then regurgitate it as required.

Things to try?

1. Check out the Flash Chip "failures" using an EEProm adapter and appropriate tester.

2. Use bigger capacity chips! I've got some 25Q64FVSIG (8Mb) and 25Q128FVSIG (16Mb) chips on the way.

3. Design my own (larger) board. Omit diodes?



No comments:

Post a Comment