Closed joaquimortega closed 8 years ago
Thanks for reporting this. I'll check it out and get it fixed asap.
All fixed. Make sure you're running v2.1.1 :) Thanks for the heads up!
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.
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?
"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;
}
}
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.
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).
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.
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 :)
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. :)
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.
That is a great idea. I'll test it and include it in the notes for the next release. :)
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