I built an Arduino data-logger around our 64Mbit Flash memory (BM003) module yesterday. The BM003 is a carrier board for the Macronix MX25L6445E, an SPI Flash memory IC. Flash memory retains its values for years after power is removed. The module has some signal conditioning circuitry to allow you to use 3.3V or 5V i/o connections, and a linear regulator to provide 3.3V to the IC if that’s not available in your system. The Arduino program accepts serial commands at 9600BPS to erase the EEPROM, take and store 3 analog measurements in memory, or read back all of the data stored.
You can find the source code for this design on our web site (BM003_Arduino_flash_data_logger.zip). A schematic connection for the Arduino can be found in the product datasheet located here, as the pins used in this program match our example connection. There are no connections in the datasheet shown for the analog pins. We used A0, A1, and A2, so you can connect those to 0-5V voltage sources of your choosing.
In describing the program, I‘ll start with the basic stuff. Since the memory IC uses an SPI interface, I include the SPI.h Arduino library. Next I define some of the command byte values from the MX25L644E datasheet. The datasheet can be found at the Macronix web site, on Digi-Key, or in the design files for the BM003. There are lots of other commands and flash IC functions, and only 4 are used here.
For the address value used in reading and writing data to the Flash memory I created a union. This makes it easy to break the variable into byte sized chunks and sending them over the SPI bus. Below that I create a few global variables used in the program.
Lastly, in the setup() function I initialize a serial port for the communication protocol as well as initializing the SPI port so it will work with the MX25L6445E.
In the main code loop the Arduino sits around waiting for commands. I’ve implemented 3 that execute when an ASCII byte is received. They are “E” for erase the chip, “L” for take measurements and log the analog values, and “R” to read back the analog values stored in memory. Other commands could be implemented. Reading and writing the address value would be cool. It would also be useful have a command that tells you the next available un-programmed memory location.
Here’s a little more detail related to the three commands I coded.
“E” – Sends a command to erase the entire chip. This takes about 8 seconds. There are commands to erase smaller portions of memory that can be found in the IC’s datasheet (such as erasing 4096 byte sectors only) . I used the erase all command because it was the biggest hammer in the toolbox. How often do you get a chance to use the biggest hammer?
“L” – Takes measurements on A0, A1, ad A2 pins of the Arduino. I divide these values by 4 so they fit in a byte sized variable. If you don’t need the all 64Mbits of memory you could store these as 10-bit values You could also use the upper 6 bits of the most significant byte (MSB) to store additional info. For example, you could clear bit 7 of the MSB to indicate that the memory location is being used. Then on power up you could scan all memory locations to find out which address is the next one you should write to. If your first reading gave you an analog value of 0x1AB (hex) it would be stored in memory like this…
0 hex 0xAB; binary ‘1010 1011’
1 hex 0x7D; binary ‘0111 1101’
Where the red bits are the 10-bit analog value and the green bit is used to indicate that the memory is in use. A reading of 0x1FF would result in 0x7F MSB and 0xFF LSB. Reading an odd address that resulted in 0xFF (highest bit set) would tell you that the even address just before that location is the next available location you would write data to.
”R” – this command reads the contents of the Flash IC from address 0 to the last address written to in the IC.
You’ll note that I’ve set up quite a few text strings for the serial interface to use. These take up Arduino program memory. A second program could be written that stored these strings in memory on the Flash IC. Then you could just call address locations associated with each message and send them out the serial port.
Here is what the serial monitor window looks like when the program is being used. To access the serial monitor just select Serial Monitor from the Tools menu. The commands are case sensitive.
The individual functions that access the SPI flash are pretty basic as well.
To erase the memory IC you send the chip erase command on the SPI port. The chip will be working to erase all 64Mbits of memory for about 8 seconds. There are smaller chunks of memory space that can be erased. They include a sector erase (4K bytes) and two block erase commands (32K or 64K bytes).
In order to write to memory you also need to send the write enable command that unlocks the IC. If you’re using the WP# pin to protect the chip it would also need to be driven high.
Originally I had written this function to be a more generic. I passed the address and byte to be written to the function which made it more applicable to a variety of applications. I’ll post that code as part of another blog post. For this application I thought is was a little more readable if I just used global variables. Another useful function might pass the address, the number of bytes to write, and then use an array to pass/store the bytes to be written. If you took that path you would load the array with data, and then call Flash_Page_Write(address, number of bytes).
Again, this routine would be easier to use if it were more generic. For readability I hard coded things like the start address. Now the function reads Flash memory from locations 0 to (AddressReg.LW – 1). It also sends the address value and the contents of that location to the serial port.
That’s it. A simple device to log analog measurements and store them in non-volatile memory.