I finally have a board without faults, lines uncrossed, chips soldered in place. The memory seems to read, at first glance. The display shows the test text. It runs through the test cycle. Cause for celebration.
But the next morning I come back and try it again. And it dies after ten seconds. Repeat. It dies in three seconds. What is going on?
But the next morning I come back and try it again. And it dies after ten seconds. Repeat. It dies in three seconds. What is going on?

I'm using the WinBond W25Q80BV memory chip (as often, an excellent tutorial and basic code available on Adafruit) for storing the sounds in the Tabata timer:
To use a FLASH memory chip to store and recall memories takes three steps:
1) Erase the memory EACH TIME you want to store new data. As clearly stated in the datasheet and Wikipedia, the memory is set to '1's and then selectively zeroed - making it possible to overlay one write with another. I've been caught off guard several times by this and it has cost me several days of debugging.
3) The chip select line is as important as the data lines. The 'chip' or 'slave' select pin is pulled logic low (e.g. near ground) to tell the chip that you are talking to it. But it also lets the chip know when you are finished - upon which it begins executing the instruction (I did not realize that it keeps the data in a buffer until execution).
I am sure I will run into these pitfalls over and over again. It is a fine balance between taking the time to read the datasheet in its entirety (the WinBond datasheet is seventy-five pages long and covers many special high-speed features) versus getting a chip on the board and testing actual code. It recalls early days of programming, when I struggled with simple things like syntax errors and pointers. I hope that this description provides a useful overview of common pitfalls and how to identify and avoid them.
Happy memory-chipping!
- It provides 1MByte of non-volatile (e.g. stays when the power is turned off) FLASH storage for about $0.40.
- It can be accessed using just four wires on the SPI (Serial Peripheral Interface) bus at high speed - up to 104MHz (about 1.2Mbytes/sec at 10MHz more than fast enough to support an audio stream).
- It comes in a convenient SOIC-150 package and has an extensive datasheet.
To use a FLASH memory chip to store and recall memories takes three steps:
- Erase the memory - this sets it all to 1's
- Write to memory - often called 'programming', this changes some of the 1's to zeroes
- Read from memory - the chip reads back the data from the address you supply
1) Erase the memory EACH TIME you want to store new data. As clearly stated in the datasheet and Wikipedia, the memory is set to '1's and then selectively zeroed - making it possible to overlay one write with another. I've been caught off guard several times by this and it has cost me several days of debugging.
- The memory passes my first tests. I could write and read a few bytes (hooray!) but then it starts giving me slightly corrupted data when I scale up to tens or hundreds of kilobytes. I fool with the code, searching for a subtle problem - and it is hard to detect, because I have a lot more data to look through. But things get worse - after a few hours of debugging, the memory gives me mostly zeroes with an occasional 'chirp'. I check and recheck my code. I suspect hardware and replace the chip - which (hooray!) seems to solve the problem and I move on. After a few hours, I discover that other problems in my code are actually memory problems. I come to the slow realization that I am reading the same area of corrupted memory over and over. Everything clears up when I finally check the chip-level erase and discover that is not working (see the next point!).
- The signals on the logic analyzer look sound. My code sends 0x06 (hexadecimal, write enable) followed by 0xC7 (chip erase), then 0x20 (program memory) and 0x30 (read) commands. But the read is giving gibberish. After some struggling, I finally add code to poll the chip with a 0x05 (status register) command immediately after the chip erase command. The chip returns a 0x03 meaning that the chip is both busy (bit 0) and write is enabled (bit 1). I discover that the chip erase takes nearly a second (hundreds of polls) before the status goes to 0x00 and the erase is done. Everything then works perfectly.
3) The chip select line is as important as the data lines. The 'chip' or 'slave' select pin is pulled logic low (e.g. near ground) to tell the chip that you are talking to it. But it also lets the chip know when you are finished - upon which it begins executing the instruction (I did not realize that it keeps the data in a buffer until execution).
- I would notice that the chip would not seem to remember anything in long commands - despite all of the instructions being correct, the chip select pin being pulled down, etc. But I did not pay attention to the tail of the command - where chip select never returned high, or returned high too early. Or, for short but important commands such as 'write enable', chip-select returned high before the command was completed. In both cases, the culprit was a timing conflict in my code.
- When first installing the KL25, the debugger would get hung up on the FLASH programming step - but occasionally succeed and the code would run just fine. In the case of memory, FLASH programming was fine for short erases, but as soon as I did a chip-level erase, it would take a minute or more and then. But then, I watched in amazement as the programming cycles got shorter and shorter, until it was just fine a few minutes later.
- Logic analyzer. This specialized tool reads multiple digital channels at high speed - mine goes up to 24MHz. It gives me the 'ground truth' about timing and signals and lets me distinguish between hardware and software problems. Saleae offers an excellent, USB based tool that is compact and easy to use, and includes readouts of SPI and other protocols (and analog, coming soon!).
- Dedicated test code. My code polls the chip immediately for 'manufacturer ID' to check that the signals are working properly. It loops through a short sector erase followed by a program and read cycle - with the values changing to ensure that it really programmed the data. It does a chip-level erase, then a multi-page (256 bytes) program, then a multi-page read. It then runs in the real application environment (sound and display) to test for problems under load.
- Extra chips. I try to have five or ten fresh chips lying around. When that problem starts looking like a hardware fault, it often is better to quickly swap out the chip (5-10 mins) than chase a potential red-herring (1-2 hours and a lot of frustration - and you swap the chip anyways).
I am sure I will run into these pitfalls over and over again. It is a fine balance between taking the time to read the datasheet in its entirety (the WinBond datasheet is seventy-five pages long and covers many special high-speed features) versus getting a chip on the board and testing actual code. It recalls early days of programming, when I struggled with simple things like syntax errors and pointers. I hope that this description provides a useful overview of common pitfalls and how to identify and avoid them.
Happy memory-chipping!