I bought a bunch of flash memory chips from JLCPCB last time I placed an order there, to use with the DCOs and elsewhere where memory is needed. I chose the Winbond W25Q128JVSQ as it was quite cheap, part of the basic parts at JLCPCB and 128Mbit/16MB which should be plenty for the DCOs.
I thought it would be fairly easy to integrate with but boy was I wrong! :-D I've spent so much time now getting it to work properly. First I couldn't figure out how to write to it, then I got all wrong values back. I spent several evenings debugging this little bugger but FINALLY it works.
I will connect the chip to the same SPI bus as the DAC on the DCO, so it's important that it works with the same SPI mode as the DAC8830. Fortunately this seems to be the case, it works in both SPI mode 0 and 3. Both these have stable data on the rising edge of the clock, mode 0 has a clock idle low and mode 3 has clock idle high. The DAC8830 seems to work with mode 0 so I think I'll be fine.
So what did go wrong?
I'll explain the last thing first - reading back data. I managed to write data, but whenever I had multiple 1s followed by a zero, I'd lose at least one 1, and the missing bytes would be padded with 1s at the end! I wrote 0xAA (0b10101010), but reading it back it was always 0x87 (0b100001111). The first bit seemed to be correct at all times though. I checked 10-15 different values and the pattern seemed to be stable.
I did everything I could think of - writing to different memory locations, replacing the flash chip, slowing down the SPI bus - I even considered testing the memory with a separate PIC16F18346 (e.g. without the DAC on the SPI bus) but couldn't find any in my office (I'm testing from the DCO board).
Finally I tried adding a decoupling cap to the 3v3 input of the flash chip and it immediately worked! Turning the SPI bus speed back up to 8MHz also worked fine (on a side note, 8MHz SPI sampled at 24MSamples/sec on the Saleae Logic 8 does not seem to be fast enough to read it properly.
I'm so happy but also a bit mad at myself for neglecting to do this from the start.
So, what else. First of all, the JVSQ version of the chip is a bit special - it starts up in a different mode than usual but I don't think I ended up doing anything about that (or if I did it got persisted the first time, I need to recheck that. Also, the chip has two pins - pin 3 (HOLD) and pin 7 (WP). They are not needed in normal operation. I've tried both leaving them floating and connecting them to 3v3, both cases seem to work fine. The audio board for the Teensy has them both wired to 3v3 so I guess that's the safer option:
NB! Add a 100nF cap between pin 8 and GND for decoupling. When using with the DCO board, connect MISO/MOSI and SCLK to the DAC SPI bus pins, and CS to one of the UTIL pins |
The CS pin needs to track the power supply pin on startup, so a pull-up resistor from CS to 3v3 is necessary.
Now to the procedure for writing and reading.
First of all, this being flash memory it has to be erased before it can be written. writing can only change values from 1 to 0, so erasing sets all bits to 1.
To be able to erase, we must first remove the write protection - every time. This is done by sending 0x06 (as a separate command, pull CS low and return it to high before continuing.
Then send 0x20 (Sector erase, erases 4k bytes. Larger erases are also available) followed by three address bytes that should hit a 4k boundary.
After doing this we need to wait for the operation to complete before doing anything else. This is checked by sending 0x05, and then reading the result of status register 1 (in a loop) until bit 0 is 1.
Writing works the same way. First remove the write protection (0x06). Then send 0x02, followed by three address bytes, followed by 1 to 256 data bytes. NB: Writing wraps to a 256 byte page, so if you don't start at the page start but write 256 bytes, you will start writing from the page start once the end is reached.
After completion, we once again have to wait for the correct status, just as with erase.
Now we're ready to read. Reading is easy, just write 0x03 followed by three address bytes, then read the bytes you want.
SPI
I was confused by the various SPI modes. First of all, the datasheet for the PIC16F18346 says that data is written transmitted on idle to active or active to idle. It took a long time before I understood that this is in fact when the data CHANGES, not when it should be read. Transmit on idle to active means READ on active to idle.
To make matters worse, the text in the winbond flash datasheet (https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf) says "The DO output pin is used to read data or status from the device on the falling edge of CLK". That is NOT SPI mode 0 or 3. Fortunately, the example timing diagrams show that the data is in fact to be read on the rising clock edge as is expected for SPI mode 0 and 3.
To top it all, the values of the two params of SPI mode (CPOL - clock polarity and CPHA - clock phase) are not consistent between different producers or places of documentation. Looking at these two, notice that the CPHA values for mode 2 and 3 don't match:
https://en.wikipedia.org/wiki/Serial_Peripheral_Interface
https://www.analog.com/en/analog-dialogue/articles/introduction-to-spi-interface.html#
The actual edges used matches though, so it's just a case of using different values for settings. Very confusing.
No comments:
Post a Comment