Using an SPI Flash chip with the STM32L031 Nucleo and mbed

breadboard_spi_flash_stm32l031

The W25Q32 is a 4 MByte SPI Flash ROM device which costs about 30 cents. It comes in an 8 pin surface mount package and so must be mounted on a breakout board if you want to use it with a breadboard. I designed a breakout board with KiCad and you can see it in the above image.

A test program was written along with a C++ class to encapsulate the device’s functions. The test program begins by powering up the device, it then does a bulk erase followed by a write of the string “Hello World”. The main loop then continuously reads this string back from the chip. Be careful how often you erase the device – you only get so many write/erase cycles.
This sort of device could be useful for event logging in an embedded system or it could be used to store an large program that is pulled in to RAM by a bootloader.

file: main.cpp

// Developed on the NUCLEO-L031K6 board from ST Micro and 
// the W25Q32BV SPI Flash rom
#include "mbed.h"
#include "spiflash.h"
DigitalOut myled(LED1);
SPI spi(PB_5, PB_4, PB_3);
Serial pc(USBTX, USBRX);
DigitalOut nSS(PA_11);
spiflash Myflash(spi,nSS);

int main() {     
    spi.format(8,3);  
    pc.printf("Powering up\r\n");
    Myflash.powerUp();   
    wait(0.1);
    pc.printf("Erasing\r\n");
    Myflash.eraseAll();    
    pc.printf("Writing\r\n");    
    Myflash.writeDataBytes(0,(uint8_t *)"Hello World",11);    
    while(1) {
        uint8_t Contents[16];                
        // Write guard bytes to the end of the buffer to test operation
        // of read function.
        Contents[13]=13;
        Contents[14]=14;
        Contents[15]=15;        
        Myflash.readDataBytes(0,Contents,14);
        pc.printf("\r\n++++++++++++++++++++++++++++++++++\r\n");
        for (int i=0;i < 16; i++)
        {
            pc.printf("%02x ", Contents[i]);
        }             
        myled = 1; // LED is ON
        wait(0.2); // 200 ms
        myled = 0; // LED is OFF
        wait(1.0); // 1 sec
        pc.printf("\r\nID = %X\r\n",Myflash.readDeviceIdentifier());
    }
}

The listings for the spiflash class are listed further down this post. I used PulseView and a cheap 8 channel USB logic analyzer to capture the following waveforms for the function readDeviceIdentifier()
pulseview_spi_flash_stm32l031

file: spiflash.h

#ifndef __spiflash_h
#define __spiflash_h
#include "mbed.h"
class spiflash {
public:
    spiflash(SPI &Spi, DigitalOut & NSS);
    void powerUp();
    void eraseAll();
    void eraseSector(uint32_t Address);
    void enableWrite();
    void disableWrite();
    void readDataBytes(uint32_t Address, uint8_t *ByteArray, uint32_t Length);
    void writeDataBytes(uint32_t Address, uint8_t *ByteArray, uint32_t Length);
    uint32_t readDeviceIdentifier();

private:
    static const uint32_t FlashMemorySize=4194304; // Capacity of W25Q32BV
    static const uint32_t SectorSize=4096; // Erase sector size
    static const uint32_t PageSize=256; // Number of bytes that can be programmed at one time
    SPI & spi;
    DigitalOut &nSS;    
    uint32_t readStatusRegister1();
};
#endif

file: spiflash.cpp

#include "spiflash.h"
spiflash::spiflash(SPI &Spi, DigitalOut & NSS) : spi(Spi), nSS(NSS)
{
   
}
void spiflash::powerUp()
{
    char TxBuffer[4];  // Transmit buffer
    char RxBuffer[4];  // Receive buffer
    TxBuffer[0]=0xAB;  // Command 0xAB = "Release from power down"
    nSS = 0;           // Drive nCS low to connect slave to the bus
    spi.write(TxBuffer,1,RxBuffer,1); // Write 1 byte and read 1 byte
    nSS = 1;           // Drive nCS high to disconnect slave from the bus
}
void spiflash::eraseAll()
{
    enableWrite();
    // Use sparingly!!! Limited number of cycles available
    char TxBuffer[4];  // Transmit buffer
    char RxBuffer[4];  // Receive buffer
    TxBuffer[0]=0xc7;  // Command 0xc7 = "Bulk Erase"
    nSS = 0;           // Drive nCS low to connect slave to the bus
    spi.write(TxBuffer,1,RxBuffer,1); // Write 1 byte and read 1 byte
    nSS = 1;           // Drive nCS high to disconnect slave from the bus
    while (readStatusRegister1() & 1)
    { // Wait for device erase to complete       
    }
}
void spiflash::eraseSector(uint32_t Address)
{
    enableWrite();
    // Mask off the lower bits of the Sector Address (redundant?)
    //Address = Address & 0xfffc00; //0b1111 1111 1111 1100 0000 0000
    // Use sparingly!!! Limited number of cycles available
    char TxBuffer[4];  // Transmit buffer
    char RxBuffer[4];  // Receive buffer
    
    TxBuffer[0]=0x20;  // Command 0x20 = "Sector Erase"
    TxBuffer[1] = (Address >> 16) & 0xff;
    TxBuffer[2] = (Address >> 8) & 0xff;
    TxBuffer[3] = (Address & 0xff);
    nSS = 0;           // Drive nCS low to connect slave to the bus
    spi.write(TxBuffer,4,RxBuffer,1); // Write 1 byte and read 1 byte
    nSS = 1;           // Drive nCS high to disconnect slave from the bus
    while (readStatusRegister1() & 1)
    {
        // Wait for sector erase to complete
    }
}
void spiflash::enableWrite()
{
    char TxBuffer[4];  // Transmit buffer
    char RxBuffer[4];  // Receive buffer
    TxBuffer[0]=0x06;  // Command 0x06 = "Enable writes"
    nSS = 0;           // Drive nCS low to connect slave to the bus
    spi.write(TxBuffer,1,RxBuffer,1); // Write 1 byte and read 1 byte
    nSS = 1;           // Drive nSS high to disconnect slave from the bus
}
void spiflash::disableWrite()
{
    char TxBuffer[4];  // Transmit buffer
    char RxBuffer[4];  // Receive buffer
    TxBuffer[0]=0x06;  // Command 0x06 = "Disable writes"
    nSS = 0;           // Drive nCS low to connect slave to the bus
    spi.write(TxBuffer,1,RxBuffer,1); // Write 1 byte and read 1 byte
    nSS = 1;           // Drive nSS high to disconnect slave from the bus
}
void spiflash::readDataBytes(uint32_t Address, uint8_t *ByteArray, uint32_t Length)
{    
    // Read Length bytes starting at Address and return in ByteArray
    char TxBuffer[4];   // Transmit buffer
    TxBuffer[0]=0x03;   // Command 0x03 = "Read bytes"
    nSS = 0;
    TxBuffer[1] = (Address >> 16) & 0xff;
    TxBuffer[2] = (Address >> 8) & 0xff;
    TxBuffer[3] = (Address) & 0xff;
    spi.write(TxBuffer,4,0,0); // Send command and Address of interest
    spi.write(0,0,(char *)ByteArray,Length); // Send command and Address of interest
    nSS = 1;
}
void spiflash::writeDataBytes(uint32_t Address, uint8_t *ByteArray, uint32_t Length)
{
    enableWrite();
    char TxBuffer[4];   // Transmit buffer
    
    TxBuffer[0]=0x02;   // Command 0x02 = "Write bytes"
    TxBuffer[1] = (Address >> 16) & 0xff;
    TxBuffer[2] = (Address >> 8) & 0xff;
    TxBuffer[3] = (Address) & 0xff;
    nSS = 0;
    spi.write(TxBuffer,4,0,0); // Send command and Address of interest
    spi.write((char *)ByteArray, Length, 0,0); // Send command and Address of interest
    nSS = 1;
    while (readStatusRegister1() & 1)
    {
        // Wait for write data to complete
    }
}
uint32_t spiflash::readDeviceIdentifier()
{
    // If the chip conforms with JEDEC norms, the last byte of the ID should 
    // indicate the capacity.  The last byte is the size of the chip express
    // in powers of 2.  For example if you read 0x16 (22 decimal) then the
    // chip should have a capacity of 2^22 = 4MiB.
    char TxBuffer[4];  // Transmit buffer
    char RxBuffer[4];  // Receive buffer
    TxBuffer[0]=0x9f;  // Command 0x9f = "Read status Device ID"
    nSS = 0;           // Drive nCS low to connect slave to the bus
    spi.write(TxBuffer,1,RxBuffer,4); // Write 1 byte and read 4 bytes
    nSS = 1;           // Drive nCS high to disconnect slave from the bus
    uint32_t ID=RxBuffer[1];
    ID = ID << 8;
    ID += RxBuffer[2];
    ID = ID << 8;
    ID += RxBuffer[3];
    return ID; // return the status register contents     
}
uint32_t spiflash::readStatusRegister1()
{
    char TxBuffer[4];  // Transmit buffer
    char RxBuffer[4];  // Receive buffer
    TxBuffer[0]=0x05;  // Command 0x9f = "Read Status register 1"
    nSS = 0;           // Drive nCS low to connect slave to the bus
    spi.write(TxBuffer,1,RxBuffer,2); // Write 1 byte and read 1 byte
    nSS = 1;           // Drive nCS high to disconnect slave from the bus
    return RxBuffer[1]; // return the status register contents 
    
}

The STM32F429 Discovery

I was rummaging around in my components drawer the other day and came across an STM32F429 Discovery board that I had bought quite a while ago. This was timely as I was about to take a long train journey and needed something to play with.
en.stm32f429i-disco
Once again I’ve taken a “bare-metal”, command line approach to development here and I’ve used SVDConv.exe to generate the device header file.
My laptop runs Linux and did not show the virtual com port exported by the embedded STLink-V2 interface. This hampered the UART demo program however with an external USB/Serial interface I was able to get things going. If you are running Windows you should be able to avoid this step.
Code is over on GitHub and will hopefully grow over time.
Examples on GitHub

BBG 2018 first day

BBG2018Participants
We had two workshops in Gorey Library (Wexford, Ireland) on Friday 10th of August. In total there were 35 attendees spread across the morning and afternoon. Happily no components were forgotten and apart from one or two malfunctioning STM32 “Blue pill” boards, all went well. We got great feedback from the library staff, parents and children.
The library’s Facebook post regarding the event is here

Nearly ready for Dublin Maker 2018

Putting the (hopefully) final touches to the paper based programming demos for Dublin Maker 2018 (July 21st, Merrion Square).
There are two demos here. The first controls a robot arm that can retrieve a sweet for the user. The user “programs” the arm by colouring in bits on the paper roll. Each column of the paper roll corresponds to an 8 bit opcode. The two MSB’s select which of the servo motors is to be controlled, the remaining 6 bits determine the angle.
The second demo is a music box similar in design to the first except that the opcodes are 15 bits long. 12 of these select the note, 2 select the octave and the 15th bit is a clock bit that allows the microcontroller control the motor speed. Fingers crossed these will work on the day 🙂

Breaboard Games 2018

Following on from last years successful day in Bunclody there will be 5 days of BBG in August this year scattered across various libraries in Co. Wexford. This year the hardware is the same as last – subject to random changes in the display sent by the supplier :). The software has moved on a bit however and the system can now use the touch screen to allow users edit sprites.
bbg2018edit
Apart from the addition of the edit function, the software has been refactored internally to (hopefully) allow for greater community involvement and to ease the transition between different screen types.
Full parts lists, schematics and source code will follow in July after a little more polishing.