|
|
@@ -0,0 +1,272 @@ |
|
|
|
/* |
|
|
|
* This is free and unencumbered software released into the public domain. |
|
|
|
* |
|
|
|
* Anyone is free to copy, modify, publish, use, compile, sell, or |
|
|
|
* distribute this software, either in source code form or as a compiled |
|
|
|
* binary, for any purpose, commercial or non-commercial, and by any |
|
|
|
* means. |
|
|
|
* |
|
|
|
* In jurisdictions that recognize copyright laws, the author or authors |
|
|
|
* of this software dedicate any and all copyright interest in the |
|
|
|
* software to the public domain. We make this dedication for the benefit |
|
|
|
* of the public at large and to the detriment of our heirs and |
|
|
|
* successors. We intend this dedication to be an overt act of |
|
|
|
* relinquishment in perpetuity of all present and future rights to this |
|
|
|
* software under copyright law. |
|
|
|
* |
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
|
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
|
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
|
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
|
|
|
* OTHER DEALINGS IN THE SOFTWARE. |
|
|
|
* |
|
|
|
* For more information, please refer to <http://unlicense.org> |
|
|
|
* ------------------------------------------------------------------------- |
|
|
|
* |
|
|
|
* This is example code to 1) format an SPI Flash chip, and 2) copy raw |
|
|
|
* audio files (mono channel, 16 bit signed, 44100Hz) to it using the |
|
|
|
* SerialFlash library. The audio can then be played back using the |
|
|
|
* AudioPlaySerialflashRaw object in the Teensy Audio library. |
|
|
|
* |
|
|
|
* To convert a .wav file to the proper .RAW format, use sox: |
|
|
|
* sox input.wav -r 44100 -b 16 --norm -e signed-integer -t raw OUTPUT.RAW remix 1,2 |
|
|
|
* |
|
|
|
* Note that the OUTPUT.RAW filename must be all caps and contain only the following |
|
|
|
* characters: A-Z, 0-9, comma, period, colon, dash, underscore. (The SerialFlash |
|
|
|
* library converts filenames to caps, so to avoid confusion we just enforce it here). |
|
|
|
* |
|
|
|
* It is a little difficult to see what is happening; aswe are using the Serial port |
|
|
|
* to upload files, we can't just throw out debug information. Instead, we use the LED |
|
|
|
* (pin 13) to convey state. |
|
|
|
* |
|
|
|
* While the chip is being formatted, the LED (pin 13) will toggle at 1Hz rate. When |
|
|
|
* the formatting is done, it flashes quickly (10Hz) for one second, then stays on |
|
|
|
* solid. When nothing has been received for 3 seconds, the upload is assumed to be |
|
|
|
* completed, and the light goes off. |
|
|
|
* |
|
|
|
* Use the 'rawfile-uploader.py' python script (included in the extras folder) to upload |
|
|
|
* the files. You can start the script as soon as the Teensy is turned on, and the |
|
|
|
* USB serial upload will just buffer and wait until the flash is formatted. |
|
|
|
* |
|
|
|
* This code was written by Wyatt Olson <wyatt@digitalcave.ca> (originally as part |
|
|
|
* of Drum Master http://drummaster.digitalcave.ca and later modified into a |
|
|
|
* standalone sample). |
|
|
|
* |
|
|
|
* Enjoy! |
|
|
|
*/ |
|
|
|
|
|
|
|
#include <SerialFlash.h> |
|
|
|
#include <SPI.h> |
|
|
|
|
|
|
|
//Buffer sizes |
|
|
|
#define USB_BUFFER_SIZE 128 |
|
|
|
#define FLASH_BUFFER_SIZE 4096 |
|
|
|
|
|
|
|
//Max filename length (8.3 plus a null char terminator) |
|
|
|
#define FILENAME_STRING_SIZE 13 |
|
|
|
|
|
|
|
//State machine |
|
|
|
#define STATE_START 0 |
|
|
|
#define STATE_SIZE 1 |
|
|
|
#define STATE_CONTENT 2 |
|
|
|
|
|
|
|
//Special bytes in the communication protocol |
|
|
|
#define BYTE_START 0x7e |
|
|
|
#define BYTE_ESCAPE 0x7d |
|
|
|
#define BYTE_SEPARATOR 0x7c |
|
|
|
|
|
|
|
|
|
|
|
//SPI Pins (these are the values on the Audio board; change them if you have different ones) |
|
|
|
#define MOSI 7 |
|
|
|
#define MISO 12 |
|
|
|
#define SCK 14 |
|
|
|
|
|
|
|
void setup(){ |
|
|
|
Serial.begin(9600); //Teensy serial is always at full USB speed and buffered... the baud rate here is required but ignored |
|
|
|
|
|
|
|
pinMode(13, OUTPUT); |
|
|
|
|
|
|
|
//Set up SPI |
|
|
|
SPI.setMOSI(MOSI); |
|
|
|
SPI.setMISO(MISO); |
|
|
|
SPI.setSCK(SCK); |
|
|
|
SerialFlash.begin(); |
|
|
|
|
|
|
|
//We start by formatting the flash... |
|
|
|
uint8_t id[3]; |
|
|
|
SerialFlash.readID(id); |
|
|
|
SerialFlash.eraseAll(); |
|
|
|
|
|
|
|
//Flash LED at 1Hz while formatting |
|
|
|
while (!SerialFlash.ready()) { |
|
|
|
delay(500); |
|
|
|
digitalWrite(13, HIGH); |
|
|
|
delay(500); |
|
|
|
digitalWrite(13, LOW); |
|
|
|
} |
|
|
|
|
|
|
|
//Quickly flash LED a few times when completed, then leave the light on solid |
|
|
|
for(uint8_t i = 0; i < 10; i++){ |
|
|
|
delay(100); |
|
|
|
digitalWrite(13, HIGH); |
|
|
|
delay(100); |
|
|
|
digitalWrite(13, LOW); |
|
|
|
} |
|
|
|
digitalWrite(13, HIGH); |
|
|
|
|
|
|
|
//We are now going to wait for the upload program |
|
|
|
while(!Serial.available()); |
|
|
|
|
|
|
|
SerialFlashFile flashFile; |
|
|
|
|
|
|
|
uint8_t state = STATE_START; |
|
|
|
uint8_t escape = 0; |
|
|
|
uint8_t fileSizeIndex = 0; |
|
|
|
uint32_t fileSize = 0; |
|
|
|
char filename[FILENAME_STRING_SIZE]; |
|
|
|
|
|
|
|
char usbBuffer[USB_BUFFER_SIZE]; |
|
|
|
uint8_t flashBuffer[FLASH_BUFFER_SIZE]; |
|
|
|
|
|
|
|
uint16_t flashBufferIndex = 0; |
|
|
|
uint8_t filenameIndex = 0; |
|
|
|
|
|
|
|
uint32_t lastReceiveTime = millis(); |
|
|
|
|
|
|
|
//We assume the serial receive part is finished when we have not received something for 3 seconds |
|
|
|
while(Serial.available() || lastReceiveTime + 3000 > millis()){ |
|
|
|
uint16_t available = Serial.readBytes(usbBuffer, USB_BUFFER_SIZE); |
|
|
|
if (available){ |
|
|
|
lastReceiveTime = millis(); |
|
|
|
} |
|
|
|
|
|
|
|
for (uint16_t usbBufferIndex = 0; usbBufferIndex < available; usbBufferIndex++){ |
|
|
|
uint8_t b = usbBuffer[usbBufferIndex]; |
|
|
|
|
|
|
|
if (state == STATE_START){ |
|
|
|
//Start byte. Repeat start is fine. |
|
|
|
if (b == BYTE_START){ |
|
|
|
for (uint8_t i = 0; i < FILENAME_STRING_SIZE; i++){ |
|
|
|
filename[i] = 0x00; |
|
|
|
} |
|
|
|
filenameIndex = 0; |
|
|
|
} |
|
|
|
//Valid characters are A-Z, 0-9, comma, period, colon, dash, underscore |
|
|
|
else if ((b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '.' || b == ',' || b == ':' || b == '-' || b == '_'){ |
|
|
|
filename[filenameIndex++] = b; |
|
|
|
if (filenameIndex >= FILENAME_STRING_SIZE){ |
|
|
|
//Error name too long |
|
|
|
flushError(); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
//Filename end character |
|
|
|
else if (b == BYTE_SEPARATOR){ |
|
|
|
if (filenameIndex == 0){ |
|
|
|
//Error empty filename |
|
|
|
flushError(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
//Change state |
|
|
|
state = STATE_SIZE; |
|
|
|
fileSizeIndex = 0; |
|
|
|
fileSize = 0; |
|
|
|
|
|
|
|
} |
|
|
|
//Invalid character |
|
|
|
else { |
|
|
|
//Error bad filename |
|
|
|
flushError(); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
//We read 4 bytes as a uint32_t for file size |
|
|
|
else if (state == STATE_SIZE){ |
|
|
|
if (fileSizeIndex < 4){ |
|
|
|
fileSize = (fileSize << 8) + b; |
|
|
|
fileSizeIndex++; |
|
|
|
} |
|
|
|
else if (b == BYTE_SEPARATOR){ |
|
|
|
state = STATE_CONTENT; |
|
|
|
flashBufferIndex = 0; |
|
|
|
escape = 0; |
|
|
|
|
|
|
|
if (SerialFlash.exists(filename)){ |
|
|
|
SerialFlash.remove(filename); //It doesn't reclaim the space, but it does let you create a new file with the same name. |
|
|
|
} |
|
|
|
|
|
|
|
//Create a new file and open it for writing |
|
|
|
if (SerialFlash.create(filename, fileSize)) { |
|
|
|
flashFile = SerialFlash.open(filename); |
|
|
|
if (!flashFile) { |
|
|
|
//Error flash file open |
|
|
|
flushError(); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
//Error flash create (no room left?) |
|
|
|
flushError(); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
//Error invalid length requested |
|
|
|
flushError(); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (state == STATE_CONTENT){ |
|
|
|
//Previous byte was escaped; unescape and add to buffer |
|
|
|
if (escape){ |
|
|
|
escape = 0; |
|
|
|
flashBuffer[flashBufferIndex++] = b ^ 0x20; |
|
|
|
} |
|
|
|
//Escape the next byte |
|
|
|
else if (b == BYTE_ESCAPE){ |
|
|
|
//Serial.println("esc"); |
|
|
|
escape = 1; |
|
|
|
} |
|
|
|
//End of file |
|
|
|
else if (b == BYTE_START){ |
|
|
|
//Serial.println("End of file"); |
|
|
|
state = STATE_START; |
|
|
|
flashFile.write(flashBuffer, flashBufferIndex); |
|
|
|
flashFile.close(); |
|
|
|
flashBufferIndex = 0; |
|
|
|
} |
|
|
|
//Normal byte; add to buffer |
|
|
|
else { |
|
|
|
flashBuffer[flashBufferIndex++] = b; |
|
|
|
} |
|
|
|
|
|
|
|
//The buffer is filled; write to SD card |
|
|
|
if (flashBufferIndex >= FLASH_BUFFER_SIZE){ |
|
|
|
flashFile.write(flashBuffer, FLASH_BUFFER_SIZE); |
|
|
|
flashBufferIndex = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//Success! Turn the light off. |
|
|
|
digitalWrite(13, LOW); |
|
|
|
} |
|
|
|
|
|
|
|
void loop(){ |
|
|
|
//Do nothing. |
|
|
|
} |
|
|
|
|
|
|
|
void flushError(){ |
|
|
|
uint32_t lastReceiveTime = millis(); |
|
|
|
char usbBuffer[USB_BUFFER_SIZE]; |
|
|
|
//We assume the serial receive part is finished when we have not received something for 3 seconds |
|
|
|
while(Serial.available() || lastReceiveTime + 3000 > millis()){ |
|
|
|
if (Serial.readBytes(usbBuffer, USB_BUFFER_SIZE)){ |
|
|
|
lastReceiveTime = millis(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |