Important update: Arm Announces End of Life Timeline for Mbed. This site will be archived in July 2026. Read the full announcement.
Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
Record Audio to external Flash
try to use the us_ticker to measure time intervals accurately to get an idea of the cost of writing to flash. You might want to buffer the data before writing; it amortizes the cost. Please report back with costs of accessing the flash. You might need DMA; Mbed is moving towards a DMA API.
A few thoughts on things to try:
Firstly how good quality does the audio need to be? Can you get away with only storing the more significant byte and throwing the last 4 bits away? If so then that's your data rate halved. Similarly, do you really need 40kHz? For most things 16 or 20kHz would be just as good and again have a significant impact on the amount of data you would need to store. It all depends on what you are storing, music needs a reasonable rate, speech is fine at very low data rates.
It's fairly simple to get a high quality audio file on a PC and degrade it to lower sample rates and resolutions to see how it sounds. Make sure you are trying sounds similar to what you want to record, ideally recorded with the microphone you are planning to use.
Don't try to read the ADC and write to flash at 40kHz, that won't work well, put a buffer in between.
The flash chip you mentioned has a block size of 2112 bytes divided into pages of 264 bytes. That would imply that you'll get the best throughput if you write data in chunks that size. Buffer up 264 bytes (or 2112 bytes) in RAM and then write it in one transaction to the flash. Make sure that your flash addresses are aligned to the block boundaries (e.g. don't write 264 bytes starting at address 128, that would take up two different pages, write to address 0 or a multiples of 264)
Have two different RAM buffers so that while one is being written to the flash the second one is being filled by the ADC and keep switching them.
Something like the code below with the missing functions filled in should be a good start (note-I've not even tried compiling this let alone debugging it so there are probably some bugs in there. It certainly doesn't cope with buffer overflows very cleanly) If nothing else it should give you the general idea.
#define recordTimeSec 3 // 1 for 16 bit, 0 for 8 #define Sample16Bit 1 // rate in Hz #define sampleRate 40000 #define bufferSize 264 #define samplePeriodUs (1000000 / sampleRate) char buffer1[bufferSize ]; char buffer2[bufferSize ]; int adcWritePointer = 0; char *adcBuffer; volatile char *flashBuffer; // set in an interupt and checked in main so mark as volatile. AnalogIn audioIn(AIn); Ticker sampleClock; Timer recordLength; void onSampleClock() { #if Sample16Bit *((uint16_t*) (adcBuffer+adcWritePointer)) = audioIn.read_u16(); adcWritePointer+= 2; #else *(adcBuffer+adcWritePointer) = (char) (audioIn.read_u16() >> 8); adcWritePointer++; #endif // buffer full if (adcWritePointer == bufferSize) { adcWritePointer = 0; if (flashBuffer != NULL) printf("ERROR - flash write didn't finish in time"); // write this buffer to flash flashBuffer = adcBuffer; // store ADC in other buffer if (adcBuffer == buffer1) adcBuffer = buffer2; else adcBuffer = buffer1; } } main () { initFlash(); while(true) { // set pointers adcWritePointer = 0; adcBuffer = buffer1; flashBuffer = NULL; // reset timer recordLength.reset(); //wait for the go signal waitForStartButton(); // start the clocks recordLength.start(); sampleClock.attach_us(onSampleClock,samplePeriodUs ); // until we hit the time limit while (recordLength < recordTimeSec) { // if there is data waiting to write if (flashBuffer) { // write it writeToFlash(flashBuffer,bufferSize); // flag the buffer as written flashBuffer = NULL; } } // end of record time. sampleClock.detach(); recordLength.stop(); } }
Some things which can help with this: AnalogIn especially on the LPC1768 is fairly slow. A faster alternative is: https://mbed.org/users/Sissors/code/FastAnalogIn/, although directly addressing hardware is even faster (might look in the near future for some optimizations in that lib).
Writing the flash IC should be fast enough for your purposes if you just increase your spi frequency to the max your chip can handle. But if it needs to be faster you could use BurstSPI lib (mainly useful if you are running at very high spi frequencies), or one of the DMA libs on the LPC1768 (MODDMA and SimpleDMA that I know of). These can let you transfer the RAM buffer to the flash IC in the background.
Hi all,
Sorry I am really very much a beginner still and I am getting codes from various examples and trying to piece them together.
I am sharing my codes below. But I am getting an error which says "No instance of overloaded function "writeEEPROM" matches the argument list.
The function accepts 3 inputs, an integer address, a char * of data to write and an integer size. In main, I am passing flashbuffer into the function. Isn't flashbuffer declared as a char* too? Why am I getting this error?
include the mbed library with this snippet
#include "mbed.h" #define recordTimeSec 3 // 1 for 16 bit, 0 for 8 #define Sample16Bit 0 // rate in Hz #define sampleRate 20000 #define bufferSize 264 #define samplePeriodUs (1000000 / sampleRate) char buffer1[bufferSize ]; char buffer2[bufferSize ]; int adcWritePointer = 0; char *adcBuffer; volatile char *flashBuffer; // set in an interupt and checked in main so mark as volatile. AnalogIn audioIn(p20); Ticker sampleClock; Timer recordLength; void onSampleClock() { #if Sample16Bit *((uint16_t*) (adcBuffer+adcWritePointer)) = audioIn.read_u16(); adcWritePointer+= 2; #else *(adcBuffer+adcWritePointer) = (char) (audioIn.read_u16() >> 8); adcWritePointer++; #endif // buffer full if (adcWritePointer == bufferSize) { adcWritePointer = 0; if (flashBuffer != NULL) printf("ERROR - flash write didn't finish in time"); // write this buffer to flash flashBuffer = adcBuffer; // store ADC in other buffer if (adcBuffer == buffer1) adcBuffer = buffer2; else adcBuffer = buffer1; } } // AT25DF041A EEPROM commands // reading #define ReadArray 0x0B #define ReadArrayLowFrequency 0x03 // programming #define BlockErase4Kb 0x20 #define BlockErase32Kb 0x52 #define BlockErase64Kb 0xD8 #define ChipErase 0x60 #define ByteProgram 0x02 #define SequentialProgram 0xAD // protection #define WriteEnable 0x06 #define WriteDisable 0x04 #define ProtectSector 0x36 #define UnProtectSector 0x39 #define ReadSectorProtection 0x3C // status #define ReadStatus 0x05 #define WriteStatus 0x01 // miscellaneous #define ReadManufacturer 0x9F #define DeepPowerDown 0xB9 #define ResumeFromPowerDown 0xAB SPI spi(p11, p12, p13); // mosi, miso, sclk DigitalOut cs(p19); Serial pc(USBTX, USBRX); // tx, rx // wait until chip not busy void notBusy () { cs = 0; spi.write (ReadStatus); // wait until busy bit cleared while (spi.write(0) & 1) {} cs = 1; } // end notBusy // enable writing void writeEnable () { notBusy (); cs = 0; spi.write(WriteEnable); cs = 1; } // end of writeEnable // read device status int readStatus (void) { cs = 0; spi.write(ReadStatus); int status = spi.write(status); cs = 1; return status; } // end of readStatus // write status register void writeStatus (int status) { writeEnable (); notBusy (); // wait until ready cs = 0; spi.write(WriteStatus); spi.write(status); cs = 1; } // end of writeStatus // send a command to the EEPROM followed by a 3-byte address void sendCommandAndAddress (int command, int addr) { spi.write(command); spi.write((addr >> 16) & 0xFF); spi.write((addr >> 8) & 0xFF); spi.write(addr & 0xFF); } // end of sendCommandAndAddress // write len (max 256) bytes to device // Note that if writing multiple bytes the address plus // length must not cross a 256-byte boundary or it will "wrap" void writeEEPROM (int addr, char * data, int len) { // now write to it writeEnable(); notBusy (); // wait until ready cs = 0; sendCommandAndAddress (ByteProgram, addr); for ( ; len ; --len) { spi.write(*data++); } cs = 1; notBusy (); } // end of writeEEPROM // write one byte to device void writeEEPROM (int addr, char data) { writeEEPROM (addr, &data, 1); } // end of writeEEPROM // read len bytes from device void readEEPROM (int addr, char * data, int len) { notBusy (); // wait until ready cs = 0; sendCommandAndAddress (ReadArray, addr); spi.write(0); // clock in "don't care" byte for ( ; len ; --len) { int result = spi.write(0); printf("read: %02x\n",result); *data++ = result; //*data++ = spi.write(0); } cs = 1; } // end of readEEPROM // erase a 4Kb block of bytes which contains addr void eraseEEPROM (int addr) { writeEnable (); notBusy (); // wait until ready cs = 0; sendCommandAndAddress (BlockErase4Kb, addr); cs = 1; notBusy (); // wait until done } // end of eraseEEPROM // show device info and status void info () { notBusy (); // wait until ready cs = 0; spi.write(ReadManufacturer); printf("Manufacturer: %02x\r\n",spi.write(0)); //printf(spi.write(0)); printf("Device ID Part 1: %02x\r\n",spi.write(0)); //printf(SPI.transfer (0)); printf("Device ID Part 2: %02x\r\n",spi.write(0)); //Serial.println (SPI.transfer (0), HEX); printf("Extended Information Length: %02x\r\n",spi.write(0)); //Serial.println (SPI.transfer (0),HEX); cs = 1; readStatus(); } // end of info int main() { cs = 1; pc.baud(115200); spi.frequency(10000000); // tired with 16000000, did not work wait_ms(500); writeStatus(0); info(); eraseEEPROM(0x1000); printf("Start\n"); // set pointers adcWritePointer = 0; adcBuffer = buffer1; flashBuffer = NULL; // reset timer recordLength.reset(); // start the clocks recordLength.start(); sampleClock.attach_us(onSampleClock,samplePeriodUs ); // until we hit the time limit while (recordLength < recordTimeSec) { // if there is data waiting to write if (flashBuffer) { // write it writeEEPROM (0, flashBuffer, bufferSize); //writeToFlash(flashBuffer,bufferSize); // flag the buffer as written flashBuffer = NULL; } } // end of record time. sampleClock.detach(); recordLength.stop(); }
Just looking at this specific error you report: I don't think it automatically converts between char * and volatile char *. A volatile char is specifically stores in the RAM memory, while a regular char isn't. I don't really know why it doesn't automatically converts it, but you can specify it manually using:
writeEEPROM (0, (char*) flashBuffer, bufferSize);
Also the comments in the EEPROM code say:
write len (max 256) bytes to device Note that if writing multiple bytes the address plus length must not cross a 256-byte boundary or it will "wrap"
Which would indicate that while each page is 264 physically bytes 8 are used for some form of housekeeping giving a far more sensible usable block size of 256 bytes. So you should change the buffersize #define at the start to be 256
The datasheet for the AT45DB081E says the page size is user configurable to either 256 bytes per page or 264 (default) bytes per page. I presume it correct to assume the page size is 264 and I can use a buffersize of 264.
Hi all,
I am using the LPC1768. I am working on a project to record audio and saving it onto an external flash (AT45DB081). What I need is to record sound from a microphone that is connected to the AnalogIn pin of the LPC1768, sample at 40kHz and save 3 secs of data onto the external flash.
I already have codes for sampling data at 40kHz using ADC library (https://mbed.org/users/simonb/code/ADC_test/). I also found some codes to save data into flash.
Separately the codes work fine. However I dont quite know how I can put both codes together to do what I want. Here is my problem. The ADC resolution is 12 bits meaning I have to break it into 2 bytes to save into the external flash. With a sampling rate of 40kHz, I need to acquire every 25 microseconds. Do you think its even possible to do this conversion of 2 bytes and write to memory within 25 microseconds?
Someone did a test with a AT25DF041 with an arduino and he was able to write " "Hello, World!" in about 63 us. Still the timing seems really tight!
I wonder if anyone has done something similar and could provide some advise.
Cheers, Kian