Marzogh / SPIMemory

Arduino library for Flash Memory Chips (SPI based only). Formerly SPIFlash
http://spimemory.readthedocs.io/en/latest/
GNU General Public License v3.0
430 stars 136 forks source link

Problem with powerDown #15

Closed joaquimortega closed 8 years ago

joaquimortega commented 8 years ago

Systematically powerDown() is not working for me (more precisely it returns a false) powerUp() and other functions are working properly. The chip is a Winbond W25Q32BV

Marzogh commented 8 years ago

Thanks for reporting this. I'll check it out and get it fixed asap.

Marzogh commented 8 years ago

All fixed. Make sure you're running v2.1.1 :) Thanks for the heads up!

joaquimortega commented 8 years ago

Thanks for your effort. Unfortunately I am still getting a false bool when I power.Down() with version v2.1.1. power.Up() seems OK.

Marzogh commented 8 years ago

Ok. Try this please. Save the code below as "recover_from_powerDown.ino" and run it.

#include<SPI.h>

#define RELEASE 0xAB
#define READSTAT1 0x05
#define POWERDOWN 0xB9

#define cs 10
#define xfer(n) SPI.transfer(n)

uint8_t stat1;
uint8_t devID;

void setup() {
  Serial.begin(115200);
  SPI.begin();
  SPI.setDataMode(0);
  SPI.setBitOrder(MSBFIRST);
  pinMode(cs, OUTPUT);

  digitalWrite(cs, HIGH);

  digitalWrite(cs, LOW);
  xfer(POWERDOWN);
  digitalWrite(cs, HIGH);
  delay(3);

  digitalWrite(cs, LOW);
  xfer(READSTAT1);
  stat1 = xfer(0);
  digitalWrite(cs, HIGH);
  Serial.println(stat1, BIN);

  digitalWrite(cs, LOW);
  xfer(RELEASE);
  (void)xfer(0);
  (void)xfer(0);
  (void)xfer(0);
  devID = xfer(0);
  digitalWrite(cs, HIGH);
  delay(3);
  Serial.println(devID, HEX);

  digitalWrite(cs, LOW);
  xfer(READSTAT1);
  stat1 = xfer(0);
  digitalWrite(cs, HIGH);
  delay(3);
  Serial.println(stat1, BIN);

  digitalWrite(cs, LOW);
  xfer(RELEASE);
  (void)xfer(0);
  (void)xfer(0);
  (void)xfer(0);
  devID = xfer(0);
  digitalWrite(cs, HIGH);
  delay(3);
  Serial.println(devID, HEX);
}

void loop() {
}

When it is run it should print

11111111
15
0
15

to your serial stream at 115200 baud.

Once that is done, upload the code below as "powerDown_test.ino" to your Arduino board and run it.

#include<SPI.h>
#include<SPIFlash.h>

String testString = "This is a test String";
uint32_t stringAddress1, stringAddress2, stringAddress3, capacity;

SPIFlash flash(10);

void setup() {
  flash.begin();
  capacity = flash.getCapacity();
  Serial.begin(115200);
  Serial.println("Starting flash memory...");
  randomSeed(analogRead(A0));
  stringAddress1 = random(0, capacity);
  stringAddress2 = random(0, capacity);
  stringAddress3 = random(0, capacity);

  Serial.print("String write function");
  if (flash.writeStr(stringAddress1, testString))
    Serial.println(" successful");
  else
    Serial.println(" unsuccessful");

  Serial.print("Chip powerdown");
  if (flash.powerDown())
    Serial.println(" successful");
  else
    Serial.println(" unsuccessful");

  Serial.print("String write function");
  if (flash.writeStr(stringAddress2, testString))
    Serial.println(" successful");
  else
    Serial.println(" unsuccessful");

  Serial.print("Chip powerup");
  if (flash.powerUp())
    Serial.println(" successful");
  else
    Serial.println(" unsuccessful");

  Serial.print("String write function");
  if (flash.writeStr(stringAddress3, testString))
    Serial.println(" successful");
  else
    Serial.println(" unsuccessful");

  flash.eraseSector(stringAddress1);
  flash.eraseSector(stringAddress2);
  flash.eraseSector(stringAddress3);
}

void loop() {

}

When run, it should print

Starting flash memory...
String write function successful
Chip powerdown successful
String write function unsuccessful
Chip powerup successful
String write function successful

to your serial monitor at 115200 baud.

If neither or both of them don't work, could you provide me with a copy of the code you are using and let me know which Arduino board you are working with?

joaquimortega commented 8 years ago

"recover_from_powerDown.ino" gave the expected output. However, "powerDown_test.ino" produced the following output: Starting flash memory... String write function successful Chip powerdown unsuccessful String write function unsuccessful Chip powerup successful String write function successful

I attach the copy of the code that gave problems in the first place. The board is selfmade, with an ATMEGA328P running at 8 MHz with no crystal to lower battery use. This implies that the serial monitor runs at most at 9600 bauds.

#include <UnFil.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <PCD8544.h>
#include <SFE_BMP180.h>
#include <Wire.h>
#include <dht.h>
#include <SPIFlash.h>
#include <SPI.h>  
#include <RTClib.h>

#define SPIFLASH true
#define LCD true
//Define DEBUG as true/false to choose among the debugging/release version
#define DEBUG true

char printBuffer[128];

struct Mostra {
  int voltsun;
  int voltbat;
  int myear;
  int mmonth;
  int mday;
  int mhour;
  int mminute;
  int msecond;
  double tempd;
  double tempb;
  double temph;
  double pres;
  double humi;
};

//Battery Voltage
const int batpin=A1;

//Solar panel Voltage
const int solarpin=A0;

// Initalize RTC
Chronodot RTC;

// Define a custom "degrees" symbol...
static const byte DEGREES_CHAR = 1;
static const byte degrees_glyph[] = { 0x00, 0x07, 0x05, 0x07, 0x00 };

//Initialize lcd
#if LCD
  static PCD8544 lcd;
#endif

// Dallas data wire is plugged into pin 14 on the ATmega328p. It is A2 on proto, it should be 8
const int ONE_WIRE_BUS = A2;

//Mosfet for a fake ground
const int fakegnd= 2;

//Initialize temperature chip ds18S20 i/o
ds dallas(ONE_WIRE_BUS);  // on digital pin 8 (A2 in proto)

//Initialize BMP180
#define ALTITUDE 124.0 // Altitude of St. Cugat in meters
SFE_BMP180 pressure;

//Initialize dh11
dht DHT;
#define DHT11_PIN A3

//Pin for memory select
const int flashpin=10;
SPIFlash flash(flashpin);
uint32_t address;

void setup()
{

  //Initialize pins
  analogReference(DEFAULT);
  pinMode(batpin, INPUT);
  pinMode(solarpin, INPUT);
  pinMode(fakegnd, OUTPUT);

  //Power up sensors
  digitalWrite(fakegnd, HIGH);

#if LCD  
  //Start lcd
  lcd.begin(84, 48);
  lcd.setContrast(80);
  // Register the custom symbol...
  lcd.createChar(DEGREES_CHAR, degrees_glyph);
  lcd.setCursor(0, 0);
  lcd.print("  Weather ");
  lcd.setCursor(0,2); 
  lcd.print("    Station ");
  lcd.setCursor(0,4);
  lcd.print("@ Quim 2015");
//  lcd.setCursor(0,5);
//  lcd.print(sizeof(Mostra));
  delay(3000);
  lcd.clear();
#endif

  // Start sensors
  pressure.begin();
  Wire.begin();
  RTC.begin();

  //Start memory
  #if SPIFLASH
    #if DEBUG
      Serial.begin(9600);
      Serial.println("Initializing flash");
      flash.begin();
      if(flash.eraseChip())
         Serial.println("Flash erased properly");
      else  
         Serial.println("Problems erasing or initializing memory");
      ID();
      Serial.end();
    #else
      flash.begin();
    #endif
//  address=eeprom_read_dword((uint32_t *)0);
    address=0;
  #endif

  // Let everybody warm up
  delay(300);

}

void loop()
{

  Mostra mostra;
  //powerup sensors
  digitalWrite(fakegnd,HIGH);
   //Start serial comunications
  #if DEBUG
    Serial.begin(9600);
    delay(100);
  #endif

  //powerup memory
  #if SPIFLASH
    #if DEBUG
    if( flash.powerUp())
      Serial.println("Powering up flash correctly");
    else
      Serial.println("Problem powering flash");
    #else  
    flash.powerUp();
    #endif
  #endif

  // Warm up
  delay(100);

  // Reading of voltages
  mostra.voltbat = analogRead(batpin);
  mostra.voltsun = analogRead(solarpin);
  // Dallas temperature sensor
  mostra.tempd=dallas.readtempcrc();

  // Reading the humidity sensor
  int chk = DHT.read11(DHT11_PIN);
  mostra.temph=DHT.temperature;
  mostra.humi=DHT.humidity;

// Time RTC
  DateTime now = RTC.now();
  mostra.myear=now.year();
  mostra.mmonth=now.month();
  mostra.mday=now.day();
  mostra.mhour=now.hour();
  mostra.mminute=now.minute();
  mostra.msecond=now.second();

// Pressure measure BMP180
  double p0;
  char status = pressure.startTemperature();
  if (status != 0)
  {
    // Wait for the measurement to complete:
    delay(status);

    // Retrieve the completed temperature measurement:
    // Note that the measurement is stored in the variable T.
    // Function returns 1 if successful, 0 if failure.

    status = pressure.getTemperature(mostra.tempb);
    if (status != 0)
    {
      #if DEBUG
        // Print out the measurement:
        Serial.print("temperature: ");
        Serial.print(mostra.tempb,2);
        Serial.print(" deg C, ");
      #endif

      // Start a pressure measurement:
      // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
      // If request is successful, the number of ms to wait is returned.
      // If request is unsuccessful, 0 is returned.

      status = pressure.startPressure(3);
      if (status != 0)
      {
        // Wait for the measurement to complete:
        delay(status);

        // Retrieve the completed pressure measurement:
        // Note that the measurement is stored in the variable P.
        // Note also that the function requires the previous temperature measurement (T).
        // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
        // Function returns 1 if successful, 0 if failure.

        status = pressure.getPressure(mostra.pres,mostra.tempb);
        if (status != 0)
        {
          #if DEBUG
            // Print out the measurement:
            Serial.print("absolute pressure: ");
            Serial.print(mostra.pres,2);
            Serial.print(" mb, ");
            Serial.print(mostra.pres*0.0295333727,2);
            Serial.println(" inHg");
           #endif

          // The pressure sensor returns abolute pressure, which varies with altitude.
          // To remove the effects of altitude, use the sealevel function and your current altitude.
          // This number is commonly used in weather reports.
          // Parameters: P = absolute pressure in mb, ALTITUDE = current altitude in m.
          // Result: p0 = sea-level compensated pressure in mb

          p0 = pressure.sealevel(mostra.pres,ALTITUDE); // we're at 120 meters (St. Cugat)

          #if DEBUG
            Serial.print("relative (sea-level) pressure: ");
            Serial.print(p0,2);
            Serial.print(" mb, ");
            Serial.print(p0*0.0295333727,2);
            Serial.println(" inHg");
          #endif
        }
        #if DEBUG
          else Serial.println("error retrieving pressure measurement\n");
        #endif
      }
      #if DEBUG
        else Serial.println("error starting pressure measurement\n");
      #endif
    }
    #if DEBUG
      else Serial.println("error retrieving temperature measurement\n");
    #endif
  }
  #if DEBUG
    else Serial.println("error starting temperature measurement\n");
  #endif

  #if DEBUG  
    Serial.print("Saving at adress: ");
    Serial.println(address);
    Serial.print(mostra.myear, DEC);
    Serial.print('/');
    Serial.print(mostra.mmonth, DEC);
    Serial.print('/');
    Serial.print(mostra.mday, DEC);
    Serial.print(' ');
    Serial.print(mostra.mhour, DEC);
    Serial.print(':');
    Serial.print(mostra.mminute, DEC);
    Serial.print(':');
    Serial.print(mostra.msecond, DEC);
    Serial.println();
    Serial.print("Voltage of battery:");
    Serial.println(mostra.voltbat*6.6/1023);
    Serial.print("Voltage of solar pannel:");
    Serial.println(mostra.voltsun*6.6/1023);
    Serial.print("Temperatura en ds1850: ");
    Serial.println(mostra.tempd);
    Serial.print ("Humitat: ");
    Serial.println(DHT.humidity,1);
    Serial.print("Temperatura en DHT11: ");
    Serial.println(DHT.temperature,1);
  #endif

  // Display Data on lcd
  #if LCD
  lcd.setCursor(0, 0);
  lcd.print("Temp: ");
  lcd.print(mostra.tempd, 1);
  lcd.print(" \001C ");
  lcd.setCursor(0, 1);
  lcd.print("Prs: ");
  lcd.print(p0, 1);
  lcd.print(" mb");
  lcd.setCursor(0, 2);
  lcd.print("Hum: ");
  lcd.print(mostra.humi, 1);
  lcd.print(" %");
  lcd.setCursor(0, 3);
  lcd.print("Volt: ");
  lcd.print(mostra.voltbat*6.6/1023, 2);
  lcd.print(" v");
  lcd.setCursor(0, 4);
//  lcd.print("Dt: ");
//  lcd.print(mostra.now.day(), DEC);
//  lcd.print('/');
//  lcd.print(mostra.now.month(), DEC);
//  lcd.print('/');
//  lcd.print(mostra.now.year(), DEC);
  lcd.print("Sol: ");
  lcd.print(mostra.voltsun*6.6/1023,2);
  lcd.print(" v");
  lcd.setCursor(0,5);
  lcd.print("Time: ");
  lcd.print(mostra.mhour, DEC);
  lcd.print(':');
  lcd.print(mostra.mminute, DEC);
  lcd.print(':');
  lcd.print(mostra.msecond, DEC);
  #endif

// Write to memory  
  #if SPIFLASH
    #if DEBUG    
      if(flash.writeAnything(address,mostra))
        Serial.println("Sample written correctly");
      else
        Serial.println("Problem writing sample");
    #else
      flash.writeAnything(address,mostra);
    #endif
    address += sizeof(mostra);
//    eeprom_write_dword((uint32_t *)0,address);

    #if DEBUG
    if( flash.powerDown())
      Serial.println("Powering off flash correctly");
    else
      Serial.println("Problem powering off flash");
    #else  
    flash.powerDown();
    #endif
  #endif

  // Switch off sensors
  digitalWrite(fakegnd,LOW);
  #if DEBUG
     Serial.end();
  #endif

  // sleep for a total of 64 seconds (8 x 8)
  for (int i = 0; i < 2; i++)
    myWatchdogEnable (); 
}

// watchdog interrupt
ISR (WDT_vect) 
{
   wdt_disable();  // disable watchdog
}  // end of WDT_vect

void myWatchdogEnable() 
{
 // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset
  WDTCSR = bit (WDCE) | bit (WDE);
  // set interrupt mode and an interval 
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  wdt_reset();  // pat the dog

  // disable ADC
  byte old_ADCSRA = ADCSRA;
  ADCSRA = 0;  

  // turn off various modules
  byte old_PRR = PRR;
  PRR = 0xFF; 

  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  noInterrupts ();           // timed sequence follows
  sleep_enable();

  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  interrupts ();             // guarantees next instruction executed
  sleep_cpu ();  

  // cancel sleep as a precaution
  sleep_disable();
  PRR = old_PRR;
  ADCSRA = old_ADCSRA;
} 

void ID() {
  Serial.println(F("----------------------------------------------------------------------------------------------------------------------------------"));
  Serial.println(F("                                                           Get ID                                                                 "));
  Serial.println(F("----------------------------------------------------------------------------------------------------------------------------------"));
  Serial.println(F("----------------------------------------------------------------------------------------------------------------------------------"));
  uint8_t b1, b2;
  uint16_t b3;
  uint32_t JEDEC = flash.getJEDECID();
  uint16_t ManID = flash.getManID();
  b1 = (ManID >> 8);
  b2 = (ManID >> 0);
  b3 = (JEDEC >> 0);
  clearprintBuffer();
  sprintf(printBuffer, "Manufacturer ID: %02xh\nMemory Type: %02xh\nCapacity: %02xh", b1, b2, b3);
  Serial.println(printBuffer);
  clearprintBuffer();
  sprintf(printBuffer, "JEDEC ID: %04lxh", JEDEC);
  Serial.println(printBuffer);
}
void clearprintBuffer()
{
  for (uint8_t i = 0; i < 128; i++) {
    printBuffer[i] = 0;
  }
}
joaquimortega commented 8 years ago

I think that I found the problem. According to the datasheet:

"While in the power-down state only the Release from Power-down / Device ID instruction, which restores the device to normal operation, will be recognized. All other instructions are ignored. This includes the Read Status Register instruction, which is always available during normal operation."

Thus to check whether we are in powerdown mode we cannot read the status register 1. It returns 0.

Marzogh commented 8 years ago

This is weird. I've just run my test code on an Uno, Leonardo, Mega and Due and all of them respond properly. The fact that you get the right results when you run recover_from_powerDown.ino means your flash chip is behaving normally.

The 'recover_from_powerDown.ino' sketch does the following:

So reading from the status register 1 when in powerDown returns 255 (decimal) and not 0. That is what the powerDown function is set to do:

bool SPIFlash::powerDown(void) {
    if(!_notBusy())
        return false;                                      // returns false if the chip is busy

    _cmd(POWERDOWN, NO_CONTINUE);
    #if defined (__AVR__)
    CHIP_DESELECT
    #endif
    _delay_us(3);                       //Max powerDown enable time according to the Datasheet

    if (_readStat1() != 255)                     //If the status register 1 does not read 255 (binary 11111111)
        return false;
    return true;
}

So, if you are running v2.1.1, then, I suspect the false you are getting is because the chip is busy when you try and execute a powerDown() and not because it was of issues after powerDown. I've just double checked all my read/write functions and they all make sure that the chipSelect is pulled high before they sound the all clear, so they aren't what's locking up the chip in busy.

Have you tried to run Diagnostics.ino from the library examples and see if it passes everything?

if that works, could you try change the

_delay_us(3);

in the powerDown function in your copy of the library to

_delay_us(6);

and see if that works? (I'm just trying to see if your lowered clock speed might be affecting the _delay_us() function - it technically shouldn't).

joaquimortega commented 8 years ago

The chip passes all the tests from Diagnostics.ino correctly. I don't think that the powerDown function fails when checking if the device is busy, because I modified the code as follows:

bool SPIFlash::powerDown(void) {
    if(!_notBusy())
        return false;                                      // returns false if the chip is busy
    Serial.println("Not busy");
    _cmd(POWERDOWN, NO_CONTINUE);
    #if defined (__AVR__)
    CHIP_DESELECT
    #endif
    _delay_us(3);                       //Max powerDown enable time according to the Datasheet

    if (_readStat1() != 255)                     //If the status register 1 does not read 255 (binary 11111111)
        return false;
    return true;
}

And it always printed the string "Not busy". I also increased the delay_us without success.

In fact, I ran again recover_from_powerDown.ino moving the relevant part of the code into the loop to see the output repeatedly, and it didn't always answer the same. A typical output was the following: 0 15 0 15

10101110 15 0 15

11111011 15 0 15

11111111 15 0 15

As you can see, the output of the register while powered down doesn't seem to be predictable. This suggests that the powerDown function is powering down the device, but the device isn't answering the request to read the register. This is consistent with the behavior described in the datasheet.

Marzogh commented 8 years ago

D'oh! Silly me. I should've checked to see what happens in a loop! Thanks for pointing it out, I'll fix it with the next release. Also, thanks for raising the issue in the first place, it let me catch a couple of serious bugs in the powerDown/powerUp functions :)

Marzogh commented 8 years ago

Its all been fixed in the latest v2.2.0-w.i.p branch. The code in the branch is stable as of now if you'd like to use it. The final release of v2.2.0 is a while away and I thought you might want to play around with a version of the library without the powerDown bug. :)

joaquimortega commented 8 years ago

I will give it a try. A workaround that I found is to use a pull up resistor (10k) on the MISO line. In this way when the memory is in sleep mode there is no noise on the data line and its driven high by the pullup resistor and you read all 1 to the status request while sleeping.

2015-10-28 9:14 GMT+01:00 Praj notifications@github.com:

Its all been fixed in the latest v2.2.0-w.i.p branch. The code in the branch is stable as of now if you'd like to use it. The final release of v2.2.0 is a while away and I thought you might want to play around with a version of the library without the powerDown bug. :)

— Reply to this email directly or view it on GitHub https://github.com/Marzogh/SPIFlash/issues/15#issuecomment-151759780.

Marzogh commented 8 years ago

That is a great idea. I'll test it and include it in the notes for the next release. :)