From 53156895f3ace7e5b3828204b92ffc2e684e9112 Mon Sep 17 00:00:00 2001 From: Felix Rusu Date: Sat, 13 Jul 2013 23:07:08 -0400 Subject: [PATCH] adding sleep/wakeup functions, moving WirelessHEX --- README.md | 2 +- SPIFlash.cpp | 17 +- SPIFlash.h | 35 +++-- WirelessHEX.cpp | 407 ------------------------------------------------ WirelessHEX.h | 47 ------ keywords.txt | 14 +- 6 files changed, 42 insertions(+), 480 deletions(-) delete mode 100644 WirelessHEX.cpp delete mode 100644 WirelessHEX.h diff --git a/README.md b/README.md index 006ea4e..8ac7d1d 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ Copy the content of this library in the "Arduino/libraries/SPIFlash" folder.
To find your Arduino folder go to File>Preferences in the Arduino IDE.
-See [this tutorial](http://learn.adafruit.com/arduino-tips-tricks-and-techniques/arduino-libraries) on Arduino libraries. +See [this tutorial](http://learn.adafruit.com/arduino-tips-tricks-and-techniques/arduino-libraries) on Arduino libraries. \ No newline at end of file diff --git a/SPIFlash.cpp b/SPIFlash.cpp index ca006cb..28536bf 100644 --- a/SPIFlash.cpp +++ b/SPIFlash.cpp @@ -14,6 +14,11 @@ #include +/// IMPORTANT: NAND FLASH memory requires erase before write, because +/// it can only transition from 1s to 0s and only the erase command can reset all 0s to 1s +/// See http://en.wikipedia.org/wiki/Flash_memory +/// The smallest range that can be erased is a sector (4K, 32K, 64K); there is also a chip erase command + /// Constructor. JedecID is optional but recommended, since this will ensure that the device is present and has a valid response /// get this from the datasheet of your flash chip /// Example for Atmel-Adesto 4Mbit AT25DF041A: 0x1F44 (page 27: http://www.adestotech.com/sites/default/files/datasheets/doc3668.pdf) @@ -184,7 +189,17 @@ void SPIFlash::blockErase32K(long addr) { unselect(); } +void SPIFlash::sleep() { + command(SPIFLASH_SLEEP); // Block Erase + unselect(); +} + +void SPIFlash::wakeup() { + command(SPIFLASH_WAKE); // Block Erase + unselect(); +} + /// cleanup void SPIFlash::end() { SPI.end(); -} +} \ No newline at end of file diff --git a/SPIFlash.h b/SPIFlash.h index 354e408..5432ce3 100644 --- a/SPIFlash.h +++ b/SPIFlash.h @@ -24,23 +24,28 @@ #include +/// IMPORTANT: NAND FLASH memory requires erase before write, because +/// it can only transition from 1s to 0s and only the erase command can reset all 0s to 1s +/// See http://en.wikipedia.org/wiki/Flash_memory +/// The smallest range that can be erased is a sector (4K, 32K, 64K); there is also a chip erase command + /// Standard SPI flash commands /// Assuming the WP pin is pulled up (to disable hardware write protection) /// To use any write commands the WEL bit in the status register must be set to 1. /// This is accomplished by sending a 0x06 command before any such write/erase command. -/// The WEL bit in the status register resets to the logical “0” state after a -/// device power-up or reset. In addition, the WEL bit will be reset to the logical “0” state automatically under the following conditions: -/// • Write Disable operation completes successfully -/// • Write Status Register operation completes successfully or aborts -/// • Protect Sector operation completes successfully or aborts -/// • Unprotect Sector operation completes successfully or aborts -/// • Byte/Page Program operation completes successfully or aborts -/// • Sequential Program Mode reaches highest unprotected memory location -/// • Sequential Program Mode reaches the end of the memory array -/// • Sequential Program Mode aborts -/// • Block Erase operation completes successfully or aborts -/// • Chip Erase operation completes successfully or aborts -/// • Hold condition aborts +/// The WEL bit in the status register resets to the logical 0 state after a +/// device power-up or reset. In addition, the WEL bit will be reset to the logical 0 state automatically under the following conditions: +/// Write Disable operation completes successfully +/// Write Status Register operation completes successfully or aborts +/// Protect Sector operation completes successfully or aborts +/// Unprotect Sector operation completes successfully or aborts +/// Byte/Page Program operation completes successfully or aborts +/// Sequential Program Mode reaches highest unprotected memory location +/// Sequential Program Mode reaches the end of the memory array +/// Sequential Program Mode aborts +/// Block Erase operation completes successfully or aborts +/// Chip Erase operation completes successfully or aborts +/// Hold condition aborts #define SPIFLASH_WRITEENABLE 0x06 // write enable #define SPIFLASH_WRITEDISABLE 0x04 // write disable @@ -76,6 +81,8 @@ public: void blockErase4K(long address); void blockErase32K(long address); word readDeviceId(); + void sleep(); + void wakeup(); void end(); protected: void select(); @@ -84,4 +91,4 @@ protected: uint16_t _jedecID; }; -#endif +#endif \ No newline at end of file diff --git a/WirelessHEX.cpp b/WirelessHEX.cpp deleted file mode 100644 index 107d31e..0000000 --- a/WirelessHEX.cpp +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (c) 2013 by Felix Rusu - * Library for facilitating wireless programming using an RFM12B radio (get library at: https://github.com/LowPowerLab/RFM12B) - * and the SPI Flash memory library for arduino/moteino (get library at: TODO). - * DEPENDS ON the two libraries mentioned above - * Install all three of these libraries in your Arduino/libraries folder ([Arduino > Preferences] for location of Arduino folder) - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of either the GNU General Public License version 2 - * or the GNU Lesser General Public License version 2.1, both as - * published by the Free Software Foundation. - */ - -#include -#include - -/// Checks whether the last message received was a wireless programming request handshake -/// If so it will start the handshake protocol, receive the new HEX image and -/// store it on the external flash chip, then reboot -/// Assumes radio has been initialized and has just received a message (is not in SLEEP mode, and you called CRCPass()) -/// Assumes flash is an external SPI flash memory chip that has been initialized -void CheckForWirelessHEX(RFM12B radio, SPIFlash flash, boolean DEBUG) -{ - //special FLASH command, enter a FLASH image exchange sequence - if (*radio.DataLen >= 4 && radio.Data[0]=='F' && radio.Data[1]=='L' && radio.Data[2]=='X' && radio.Data[3]=='?') - { - byte remoteID = radio.GetSender(); - if (*radio.DataLen == 7 && radio.Data[4]=='E' && radio.Data[5]=='O' && radio.Data[6]=='F') - { //sender must have not received EOF ACK so just resend - radio.Send(remoteID, "FLX?OK",6); - } - else if (HandleWirelessHEXData(radio, remoteID, flash, DEBUG)) - { - if (DEBUG) Serial.print("FLASH IMG TRANSMISSION SUCCESS!\n"); - resetUsingWatchdog(DEBUG); - } - else - { - if (DEBUG) Serial.print("Timeout, erasing written data ... "); - flash.blockErase32K(0); //clear any written data - if (DEBUG) Serial.println("DONE"); - } - } -} - -boolean HandleWirelessHEXData(RFM12B radio, byte remoteID, SPIFlash flash, boolean DEBUG) { - uint8_t c; - long now=0; - uint16_t tmp,seq=0,len; - char buffer[16]; - int timeout = 3000; //3s for flash data - uint16_t bytesFlashed=10; - - radio.SendACK("FLX?OK",6); //ACK the HANDSHAKE - if (DEBUG) Serial.println("FLX?OK (ACK sent)"); - - //first clear the fist 32k block (dedicated to a new FLASH image) - flash.blockErase32K(0); - flash.writeBytes(0,"FLXIMG:", 7); - flash.writeByte(9,':'); - now=millis(); - - while(1) - { - if (radio.ReceiveComplete() && radio.CRCPass() && radio.GetSender() == remoteID) - { - byte dataLen = *radio.DataLen; - - if (dataLen >= 4 && radio.Data[0]=='F' && radio.Data[1]=='L' && radio.Data[2]=='X') - { - if (radio.Data[3]==':' && dataLen >= 7) //FLX:_:_ - { - byte index=3; - tmp = 0; - - //read packet SEQ - for (byte i = 4; i<8; i++) //up to 4 characters for seq number - { - index++; - if (radio.Data[i] >=48 && radio.Data[i]<=57) - tmp = tmp*10+radio.Data[i]-48; - else if (radio.Data[i]==':') - { - if (i==4) - return false; - else break; - } - } - - if (DEBUG) { - Serial.print("radio ["); - Serial.print(dataLen); - Serial.print("] > "); - PrintHex83((byte*)radio.Data, dataLen); - } - - if (radio.Data[index++] != ':') return false; - now = millis(); //got "good" packet - - if (tmp==seq) - { - for(byte i=index;i31744) { - if (DEBUG) Serial.println("IMG exceeds 31k"); - - return false; //just return, let MAIN timeout - } - radio.SendACK("FLX?OK",6); - if (DEBUG) Serial.println("FLX?OK"); - - //save # of bytes written - flash.writeByte(7,(bytesFlashed-10)>>8); - flash.writeByte(8,(bytesFlashed-10)); - flash.writeByte(9,':'); - return true; - } - } - } - #ifdef LED //blink! - pinMode(LED,OUTPUT); digitalWrite(LED,HIGH); delay(1); digitalWrite(LED,LOW); - #endif - } - - //abort FLASH sequence if no valid packet received for a long time - if (millis()-now > timeout) - { - return false; - } - } -} - -// reads a line feed (\n) terminated line from the serial stream -// returns # of bytes read, up to 255 -// timeout in ms, will timeout and return after so long -byte readSerialLine(char* input, char endOfLineChar, byte maxLength, uint16_t timeout) -{ - byte inputLen = 0; - Serial.setTimeout(timeout); - inputLen = Serial.readBytesUntil(endOfLineChar, input, maxLength); - input[inputLen]=0;//null-terminate it - Serial.setTimeout(0); - //Serial.println(); - return inputLen; -} - -/// returns TRUE if a HEX file transmission was detected and it was actually transmitted successfully -boolean CheckForSerialHEX(byte* input, byte inputLen, RFM12B radio, byte targetID, uint16_t TIMEOUT, uint16_t ACKTIMEOUT, boolean DEBUG) -{ - if (inputLen == 4 && input[0]=='F' && input[1]=='L' && input[2]=='X' && input[3]=='?') { - if (HandleSerialHandshake(radio, targetID, false, TIMEOUT, ACKTIMEOUT, DEBUG)) - { - Serial.println("FLX?OK"); //signal serial handshake back to host script - if (HandleSerialHEXData(radio, targetID, TIMEOUT, ACKTIMEOUT, DEBUG)) - { - Serial.println("FLX?OK"); //signal EOF serial handshake back to host script - if (DEBUG) Serial.println("FLASH IMG TRANSMISSION SUCCESS"); - return true; - } - if (DEBUG) Serial.println("FLASH IMG TRANSMISSION FAIL"); - return false; - } - } - return false; -} - -boolean HandleSerialHandshake(RFM12B radio, byte targetID, boolean isEOF, uint16_t TIMEOUT, uint16_t ACKTIMEOUT, boolean DEBUG) -{ - //temp - //return true; - - byte retries = 3; - long now = millis(); - - while (millis()-now= 6) { //FLX:9: - if (input[0]=='F' && input[1]=='L' && input[2]=='X') - { - if (input[3]==':') - { - byte index = 3; - for (byte i = 4; i<8; i++) //up to 4 characters for seq number - { - index++; - if (input[i] >=48 && input[i]<=57) - tmp = tmp*10+input[i]-48; - else if (input[i]==':') - { - if (i==4) - return false; - else break; - } - } - //Serial.print("input[index] = ");Serial.print("[");Serial.print(index);Serial.print("]=");Serial.println(input[index]); - if (input[index++] != ':') return false; - now = millis(); //got good packet - - byte hexDataLen = validateHEXData(input+index, inputLen-index); - if (hexDataLen>0) - { - if (tmp==seq) //only read data when packet number is the next expected SEQ number - { - byte sendBufLen = prepareSendBuffer(input+index+8, sendBuf, hexDataLen, seq); //extract HEX data from input to BYTE data into sendBuf (go from 2 HEX bytes to 1 byte), +8 will jump over the header directly to the HEX raw data - //Serial.print("PREP ");Serial.print(sendBufLen); Serial.print(" > "); PrintHex83(sendBuf, sendBufLen); - - //SEND RADIO DATA - if (sendHEXPacket(radio, remoteID, sendBuf, sendBufLen, seq, TIMEOUT, ACKTIMEOUT, DEBUG)) - { - sprintf((char*)sendBuf, "FLX:%u:OK",seq); - Serial.println((char*)sendBuf); //response to host (python?) - seq++; - } - else return false; - } - } - else Serial.println("FLX:INV"); - } - if (inputLen==7 && input[3]=='?' && input[4]=='E' && input[5]=='O' && input[6]=='F') - { - //SEND RADIO EOF - return HandleSerialHandshake(radio, targetID, true, TIMEOUT, ACKTIMEOUT, DEBUG); - } - } - } - - //abort FLASH sequence if no valid packet received for a long time -timeoutcheck: - if (millis()-now > TIMEOUT) - { - Serial.print("Timeout receiving FLASH image from SERIAL, aborting..."); - //send abort msg or just let node timeout as well? - return false; - } - } - return true; -} - -//returns length of HEX data bytes if everything is valid -//returns 0 if any validation failed -byte validateHEXData(void* data, byte length) -{ - //assuming 1 byte record length, 2 bytes address, 1 byte record type, N data bytes, 1 CRC byte - char* input = (char*)data; - if (length <12 || length%2!=0) return 0; //shortest possible intel data HEX record is 12 bytes - //Serial.print("VAL > "); Serial.println((char*)input); - - uint16_t checksum=0; - //check valid HEX data and CRC - for (byte i=0; i=48 && input[i]<=57) || (input[i] >=65 && input[i]<=70))) //0-9,A-F - return 0; - if (i%2 && i=65?MSB-55:MSB-48)*16 + (LSB>=65?LSB-55:LSB-48); -} - -//return the SEQ of the ACK received, or -1 if invalid -boolean sendHEXPacket(RFM12B radio, byte targetID, byte* sendBuf, byte hexDataLen, uint16_t seq, uint16_t TIMEOUT, uint16_t ACKTIMEOUT, boolean DEBUG) -{ - long now = millis(); - - while(1) { - if (DEBUG) { Serial.print("RFTX > "); PrintHex83(sendBuf, hexDataLen); } - radio.Send(targetID, sendBuf, hexDataLen, true); - - if (waitForAck(radio, ACKTIMEOUT)) - { - byte ackLen = *radio.DataLen; - - if (DEBUG) { Serial.print("RFACK > "); Serial.print(ackLen); Serial.print(" > "); PrintHex83((byte*)radio.Data, ackLen); } - - if (ackLen >= 8 && radio.Data[0]=='F' && radio.Data[1]=='L' && radio.Data[2]=='X' && - radio.Data[3]==':' && radio.Data[ackLen-3]==':' && - radio.Data[ackLen-2]=='O' && radio.Data[ackLen-1]=='K') - { - uint16_t tmp=0; - sscanf((const char*)radio.Data, "FLX:%u:OK", &tmp); - return tmp == seq; - } - } - - if (millis()-now > TIMEOUT) - { - Serial.println("Timeout waiting for packet ACK, aborting FLASH operation ..."); - break; //abort FLASH sequence if no valid ACK was received for a long time - } - } - return false; -} - -// wait a few milliseconds for proper ACK to me, return true if indeed received -boolean waitForAck(RFM12B radio, uint16_t ACKTIMEOUT) -{ - long now = millis(); - while (millis() - now <= ACKTIMEOUT) { - if (radio.ACKReceived(radio.GetSender())) - return true; - } - return false; -} - -void PrintHex83(uint8_t *data, uint8_t length) // prints 8-bit data in hex -{ - char tmp[length*2+1]; - byte first ; - int j=0; - for (uint8_t i=0; i> 4) | 48; - if (first > 57) tmp[j] = first + (byte)39; - else tmp[j] = first ; - j++; - - first = (data[i] & 0x0F) | 48; - if (first > 57) tmp[j] = first + (byte)39; - else tmp[j] = first; - j++; - } - tmp[length*2] = 0; - Serial.println(tmp); -} - -/// Use watchdog to reset -void resetUsingWatchdog(boolean DEBUG) -{ - //wdt_disable(); - if (DEBUG) Serial.print("REBOOTING"); - wdt_enable(WDTO_15MS); - while(1) if (DEBUG) Serial.print('.'); -} diff --git a/WirelessHEX.h b/WirelessHEX.h deleted file mode 100644 index c94e62b..0000000 --- a/WirelessHEX.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2013 by Felix Rusu - * Library for facilitating wireless programming using an RFM12B radio (get library at: http://github.com/LowPowerLab/RFM12B) - * and the SPI Flash memory library for arduino/moteino (get library at: http://github.com/LowPowerLab/SPIFlash) - * DEPENDS ON the two libraries mentioned above, and comes bundled with the SPIFlash library (above link) - * Install all three of these libraries in your Arduino/libraries folder ([Arduino > Preferences] for location of Arduino folder) - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of either the GNU General Public License version 2 - * or the GNU Lesser General Public License version 2.1, both as - * published by the Free Software Foundation. - */ - -#ifndef _WirelessHEX_H_ -#define _WirelessHEX_H_ -#define LED 9 //LED on digital pin 9 - -#ifndef DEFAULT_TIMEOUT - #define DEFAULT_TIMEOUT 3000 -#endif - -#ifndef ACK_TIMEOUT - #define ACK_TIMEOUT 20 -#endif - -#include -#include - -//functions used in the REMOTE node -void CheckForWirelessHEX(RFM12B radio, SPIFlash flash, boolean DEBUG=false); -void resetUsingWatchdog(boolean DEBUG=false); -boolean HandleWirelessHEXData(RFM12B radio, byte remoteID, SPIFlash flash, boolean DEBUG=false); - -//functions used in the MAIN node -boolean CheckForSerialHEX(byte* input, byte inputLen, RFM12B radio, byte targetID, uint16_t TIMEOUT=DEFAULT_TIMEOUT, uint16_t ACKTIMEOUT=ACK_TIMEOUT, boolean DEBUG=false); -boolean HandleSerialHandshake(RFM12B radio, byte targetID, boolean isEOF, uint16_t TIMEOUT=DEFAULT_TIMEOUT, uint16_t ACKTIMEOUT=ACK_TIMEOUT, boolean DEBUG=false); -boolean HandleSerialHEXData(RFM12B radio, byte targetID, uint16_t TIMEOUT=DEFAULT_TIMEOUT, uint16_t ACKTIMEOUT=ACK_TIMEOUT, boolean DEBUG=false); -boolean waitForAck(RFM12B radio, uint16_t ACKTIMEOUT=ACK_TIMEOUT); - -byte validateHEXData(void* data, byte length); -byte prepareSendBuffer(char* hexdata, byte*buf, byte length, uint16_t seq); -boolean sendHEXPacket(RFM12B radio, byte remoteID, byte* sendBuf, byte hexDataLen, uint16_t seq, uint16_t TIMEOUT=DEFAULT_TIMEOUT, uint16_t ACKTIMEOUT=ACK_TIMEOUT, boolean DEBUG=false); -byte BYTEfromHEX(char MSB, char LSB); -byte readSerialLine(char* input, char endOfLineChar=10, byte maxLength=64, uint16_t timeout=1000); -void PrintHex83(byte* data, byte length); - -#endif diff --git a/keywords.txt b/keywords.txt index c0c3a30..6c6903c 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,5 +1,4 @@ -SPIFlash KEYWORD1 -WirelessHEX KEYWORD1 +SPIFlash KEYWORD1 initialize KEYWORD2 command KEYWORD2 readStatus KEYWORD2 @@ -12,11 +11,6 @@ chipErase KEYWORD2 blockErase4K KEYWORD2 blockErase32K KEYWORD2 readDeviceId KEYWORD2 -end KEYWORD2 -CheckForWirelessHEX KEYWORD2 -HandleWirelessHEXData KEYWORD2 -readSerialLine KEYWORD2 -BYTEfromHEX KEYWORD2 -waitForAck KEYWORD2 -PrintHex83 KEYWORD2 -resetUsingWatchdog KEYWORD2 +sleep KEYWORD2 +wakeup KEYWORD2 +end KEYWORD2 \ No newline at end of file