Hi, i'm writing a library to handle the SST25VF016B data flash unit for the land tiger LPC1768. Datasheet for the chip
So far I've written this code:
SPI.h:
#ifndef __SPI_H #define __SPI_H #include "LPC17xx.h" #include "../types.h" #include "../power.h" typedef enum{ ABRT = 1<<3, MODF = 1<<4, ROVR = 1<<5, WCOL = 1<<6, SPIF = 1<<7 }SPIStatus; void SPI_Init(const unsigned long frequency); void enableChipSelect(void); void disableChipSelect(void); uint8_t SPI_Transfer(uint8_t data); #endif /* end __SPI_H */
SPI.c:
#include "lpc17xx.h" #include "SPI.h" uint8_t dummy; void SPI_Init(const unsigned long frequency) { //P0.15 (SCK) LPC_PINCON->PINSEL0 &= ~(3UL<<30); LPC_PINCON->PINSEL0 |= (3UL<<30); //P0.17 (MISO), P0.18 (MOSI) LPC_PINCON->PINSEL1 &= ~((3<<2) | (3<<4)); LPC_PINCON->PINSEL1 |= (3<<2) | (3<<4); //P0.16 (GPIO) LPC_PINCON->PINSEL1 &= ~(3<<0); //SCK, SSEL, MOSI as OUTPUT LPC_GPIO0->FIODIR |= (1<<15) | (1<<16) | (1<<18); //MISO as INPUT LPC_GPIO0->FIODIR &= ~(1<<17); //Enable SPI in PCONP set_peripherals_enabled(PCSPI, true); //SCK = PCLK_SPI/SPCCR LPC_SC->PCLKSEL0 &= ~(3<<16); LPC_SC->PCLKSEL0 |= (1<<16); //First we get 8 bit and since in the user manual is says that this number should be even and > 8, here we check that uint8_t CR = (SystemCoreClock / frequency) & 0xFE; CR = CR >= 8 ? CR : 8; LPC_SPI->SPCCR &= ~0xFF; LPC_SPI->SPCCR |= CR; //Enable MODE0 (should be on with RESET) LPC_SPI->SPCR &= ~((1<<3) | (1<<4)); // CPHA = 0 CPOL = 0 //Set MASTER MODE LPC_SPI->SPCR |= (1<<5); //Set MSB LPC_SPI->SPCR &= ~(1<<6); //Disable bit mode and set 8 bit transfter (should be on by default) LPC_SPI->SPCR &= ~(1<<2); LPC_SPI->SPCR &= ~(0xF<<8); LPC_SPI->SPCR |= (8<<8); //Dummy read to clear the flags dummy = LPC_SPI->SPSR; dummy = LPC_SPI->SPDR; } void enableChipSelect(void) { LPC_GPIO0->FIOCLR |= (1<<16); } void disableChipSelect(void) { LPC_GPIO0->FIOSET |= (1<<16); } //Send 0xFF to read from slave uint8_t SPI_Transfer(uint8_t data) { uint8_t read = 0x0; while((LPC_SPI->SPSR & SPIF) == 1); LPC_SPI->SPDR = data; while((LPC_SPI->SPSR & SPIF) == 0); //Clear bits dummy = LPC_SPI->SPSR; read = LPC_SPI->SPDR; return read; }
DataFlash.h:
#ifndef __DataFlash_H #define __DataFlash_H #include "LPC17xx.h" #include "../types.h" #include "../SPI/SPI.h" typedef enum{ DF_READ_ID = 0x0, DF_JEDEC_READ_ID = 0x9F, DF_GET_STATUS_REGISTER = 0x05, DF_READ_LO_FREQ = 0x03, DF_READ_HI_FREQ = 0x0B, DF_ERASE_4KB = 0x20, DF_ERASE_32KB = 0x52, DF_ERASE_64KB = 0xD8, DF_ERASE_CHIP_1 = 0x60, DF_ERASE_CHIP_2 = 0xC7, DF_BYTE_PROGRAM = 0x02, DF_AUTO_ADDRESS_INCREMENT_PROGRAM = 0xAD, DF_ENABLE_WRITE_STATUS_REGISTER = 0x50, DF_WRITE_STATUS_REGISTER = 0x01, DF_WRITE_ENABLE = 0x06, DF_WRITE_DISABLE = 0x04, DF_READ_ID_1 = 0x90, DF_READ_ID_2 = 0xAB, DF_ENABLE_RY_BY_DURING_AAI_PROGRAMMING = 0x70, DF_DISABLE_RY_BY_DURING_AAI_PROGRAMMING = 0x80 }DFCommand; typedef enum { DF_ERASE_SIZE_4KB, DF_ERASE_SIZE_32KB, DF_ERASE_SIZE_64KB }DFEraseSize; typedef enum{ DF_NONE = 0x0, DF_BUSY = 1<<0, DF_WRITE_ENABLED = 1<<1, DF_BLOCK_PROTECTION_LV_0 = 1<<2, DF_BLOCK_PROTECTION_LV_1 = 1<<3, DF_BLOCK_PROTECTION_LV_2 = 1<<4, DF_BLOCK_PROTECTION_LV_3 = 1<<5, DF_AUTO_ADDRESS_INCREMENT_PROGRAMMING_STATUS = 1<<6, DF_BLOCK_PROTECTION_READ_ONLY = 1<<7 }DFStatus; typedef struct{ uint8_t ManufactorerID; uint16_t DeviceID; }DFJedec; //Commands void DF_Init(bool read_only); uint8_t DF_ReadStatusRegister(void); DFJedec *DF_JedecReadID(void); void DF_ReleaseJedec(DFJedec *jedec); uint8_t DF_ReadStatusRegister(void); uint8_t DF_ReadMemoryByte(bool low, uint32_t addr); void DF_WriteEnable(void); void DF_WriteDisable(void); void DF_WriteByte(uint32_t addr, uint8_t byte); void DF_EraseBlockWithSize(uint32_t addr, DFEraseSize size); void DF_EraseChip(void); void DF_EnableWriteStatusRegister(void); void DF_WriteStatusRegister(uint8_t status); //Utility uint8_t DF_WaitForStatusRegisterWithValues(uint8_t statusON, uint8_t statusOFF); uint8_t *DF_ReadMemoryIntoBuffer(bool low, uint32_t addr, uint32_t length); void DF_WriteBufferToMemory(uint32_t addr, uint8_t *buffer, uint32_t length); void DF_UnlockChip(void); void DF_LockChip(void); void DF_EnableRDYForAAI(void); void DF_DisableRDYForAAI(void); void DF_EnableWriteStatusRegister(void); void DF_WriteStatusRegister(uint8_t status); #endif /* end __DataFlash_H */
DataFlash.c:
#include "DataFlash.h" void DF_Init(bool read_only) { SPI_Init(1000000); disableChipSelect(); if (read_only == false) { while (true) { uint8_t s = DF_ReadStatusRegister(); if (s != 0xFF & (s & (DF_BLOCK_PROTECTION_LV_0 | DF_BLOCK_PROTECTION_LV_1 | DF_BLOCK_PROTECTION_LV_2 | DF_BLOCK_PROTECTION_LV_3)) != 0x0) break; } DF_UnlockChip(); } } uint8_t DF_WaitForStatusRegisterWithValues(uint8_t statusON, uint8_t statusOFF) { enableChipSelect(); SPI_Transfer(DF_GET_STATUS_REGISTER); uint8_t status; while (true) { status = SPI_Transfer(0xFF); if ((((status & statusON) == statusON) && ((status & statusOFF) == 0x0))) { break; } } disableChipSelect(); return status; } uint8_t DF_ReadStatusRegister(void) { return DF_WaitForStatusRegisterWithValues(DF_NONE, DF_NONE); } //Private method to send cmd to the Data Flash void DF_PrepareCommand(DFCommand cmd, uint32_t addr) { enableChipSelect(); SPI_Transfer((uint8_t)cmd); SPI_Transfer((addr & 0xFFFFFF) >> 16); SPI_Transfer((addr & 0xFFFF)>> 8); SPI_Transfer(addr & 0xFF); } //Get Manufactorer infos DFJedec *DF_JedecReadID(void) { DFJedec *jedec = (DFJedec *)malloc(sizeof(DFJedec)); enableChipSelect(); SPI_Transfer(DF_JEDEC_READ_ID); jedec->ManufactorerID = SPI_Transfer(0xFF); jedec->DeviceID = SPI_Transfer(0xFF) << 8; jedec->DeviceID |= SPI_Transfer(0xFF); disableChipSelect(); return jedec; } //Release manufactorer void DF_ReleaseJedec(DFJedec *jedec) { if (jedec) { free(jedec); } } //Read memory starting from the given address. Data will be outputted on every SPI_Transfer till the CE is low (enabled). The memory buffer is circular so when it reaches 0x1FFFFF will return to 0x000000 uint8_t DF_ReadMemoryByte(bool low, uint32_t addr) { DF_PrepareCommand(low == true ? DF_READ_LO_FREQ : DF_READ_HI_FREQ, addr); if (low == false) { SPI_Transfer(0xFF); //Send a dummy byte for the high frequency mode as written in the datasheet } uint8_t byte = SPI_Transfer(0xFF); disableChipSelect(); return byte; } //Utility to load Data Flash memory into a buffer. Keep in mind that this buffer needs to FREED [!important] uint8_t *DF_ReadMemoryIntoBuffer(bool low, uint32_t addr, uint32_t length) { uint8_t *data = (uint8_t *)malloc(sizeof(uint8_t) * length); if (data == NULL) { return NULL; } DF_PrepareCommand(low == true ? DF_READ_LO_FREQ : DF_READ_HI_FREQ, addr); if (low == false) { SPI_Transfer(0xFF); //Send a dummy byte for the high frequency mode as written in the datasheet } int i; for (i = 0; i < length; i++) { *(data + i) = SPI_Transfer(0xFF); } disableChipSelect(); return data; } //Enable Write on the Data Flash. This is mostly a private method since it's needed for every write opetion void DF_WriteEnable(void) { while (true) { uint8_t s = DF_ReadStatusRegister(); uint8_t cmp = s & DF_WRITE_ENABLED; if (s != 0xFF && cmp == DF_WRITE_ENABLED) break; enableChipSelect(); SPI_Transfer(DF_WRITE_ENABLE); disableChipSelect(); } } //Counter part to the DF_WriteEnable(void) void DF_WriteDisable(void) { while (true) { uint8_t s = DF_ReadStatusRegister(); uint8_t cmp = s & DF_WRITE_ENABLED; if (cmp == 0) break; enableChipSelect(); SPI_Transfer(DF_WRITE_DISABLE); disableChipSelect(); } } //Write a single byte into memory at the gigvn address void DF_WriteByte(uint32_t addr, uint8_t byte) { DF_WriteEnable(); DF_PrepareCommand(DF_BYTE_PROGRAM, addr); SPI_Transfer(byte); disableChipSelect(); DF_WaitForStatusRegisterWithValues(DF_NONE, DF_BUSY); } void DF_EnableRDYForAAI(void) { enableChipSelect(); SPI_Transfer(DF_ENABLE_RY_BY_DURING_AAI_PROGRAMMING); disableChipSelect(); } void DF_DisableRDYForAAI(void) { enableChipSelect(); SPI_Transfer(DF_DISABLE_RY_BY_DURING_AAI_PROGRAMMING); disableChipSelect(); } //Utility for DF_WriteAutoIncrementBytes void DF_WriteBufferToMemory(uint32_t addr, uint8_t *buffer, uint32_t length) { if (length == 1) { DF_WriteByte(addr, *buffer); }else{ DF_WriteEnable(); DF_EnableRDYForAAI(); DF_PrepareCommand(DF_AUTO_ADDRESS_INCREMENT_PROGRAM, addr); SPI_Transfer(*(buffer)); SPI_Transfer(*(buffer + 1)); disableChipSelect(); enableChipSelect(); while (((LPC_GPIO0->FIOPIN >> 17) & 1) == 0); disableChipSelect(); int i; buffer += 2; bool flg = length % 2 != 0 ? true : false; if (flg == true) { length--; } for (i = 2; i < length; i = i + 2) { enableChipSelect(); SPI_Transfer(DF_AUTO_ADDRESS_INCREMENT_PROGRAM); SPI_Transfer(*(buffer)); SPI_Transfer(*(buffer+ 1)); disableChipSelect(); enableChipSelect(); while (((LPC_GPIO0->FIOPIN >> 17) & 1) == 0); disableChipSelect(); buffer += 2; } DF_WriteDisable(); DF_WaitForStatusRegisterWithValues(DF_NONE, DF_BUSY); DF_DisableRDYForAAI(); if (flg == true) { DF_WriteByte(addr + length, *(buffer)); } } } //Erase memory with given enum size void DF_EraseBlockWithSize(uint32_t addr, DFEraseSize size) { DF_WriteEnable(); switch (size) { case DF_ERASE_SIZE_4KB: DF_PrepareCommand(DF_ERASE_4KB, addr); break; case DF_ERASE_SIZE_32KB: DF_PrepareCommand(DF_ERASE_32KB, addr); break; case DF_ERASE_SIZE_64KB: DF_PrepareCommand(DF_ERASE_64KB, addr); break; } disableChipSelect(); DF_WaitForStatusRegisterWithValues(DF_NONE, DF_BUSY); } void DF_EraseChip(void) { DF_WriteEnable(); enableChipSelect(); SPI_Transfer(DF_ERASE_CHIP_1); disableChipSelect(); DF_WaitForStatusRegisterWithValues(DF_NONE, DF_BUSY); } //Enable writing on the status register. After this call we need to call DF_WriteStatusRegister void DF_EnableWriteStatusRegister(void) { enableChipSelect(); SPI_Transfer(DF_ENABLE_WRITE_STATUS_REGISTER); disableChipSelect(); } //Write the status register void DF_WriteStatusRegister(uint8_t status) { enableChipSelect(); SPI_Transfer(DF_WRITE_STATUS_REGISTER); SPI_Transfer(status); disableChipSelect(); } void DF_UnlockChip(void) { while (true) { uint8_t s = DF_ReadStatusRegister(); uint8_t cmp = s & (DF_BLOCK_PROTECTION_LV_0 | DF_BLOCK_PROTECTION_LV_1 | DF_BLOCK_PROTECTION_LV_2 | DF_BLOCK_PROTECTION_LV_3); if (cmp == 0x0) break; DF_EnableWriteStatusRegister(); DF_WriteStatusRegister(0x0); } } void DF_LockChip(void) { while (true) { uint8_t s = DF_ReadStatusRegister(); uint8_t cmp = s & (DF_BLOCK_PROTECTION_LV_0 | DF_BLOCK_PROTECTION_LV_1 | DF_BLOCK_PROTECTION_LV_2 | DF_BLOCK_PROTECTION_LV_3); if (s != 0xFF && cmp == (DF_BLOCK_PROTECTION_LV_0 | DF_BLOCK_PROTECTION_LV_1 | DF_BLOCK_PROTECTION_LV_2 | DF_BLOCK_PROTECTION_LV_3)) break; DF_EnableWriteStatusRegister(); DF_WriteStatusRegister(DF_BLOCK_PROTECTION_LV_0 | DF_BLOCK_PROTECTION_LV_1 | DF_BLOCK_PROTECTION_LV_2 | DF_BLOCK_PROTECTION_LV_3); } }
Then in order to test it i wrote a function to read the memory (0x80 bytes) and display those to the LCD and mapped erase and write a byte to the land tiger keys... Nothing special I just call the library functions.
My porblem is that this code work only sometimes. Like for example if it manages to write data to the LCD if I write a byte sometimes it works and sometimes if just freeze on the status register busy check as the SPI_Transfer(0xFF) to read the status register returns 0xFF (this happens also with other functions).
Now after trying to write a byte the device won't pass DF_ChipUnlock() as on DF_Init(false) it passes the first loop to check for 0x1C (block protection on startup) but after that when it enter the method DF_ChipUnlock() it freezes there, as DF_EnableWriteStatusRegister(); DF_WriteStatusRegister(0x0); won't work (the status register always reads 0x1C (all sectors protected)).
Am I missing something?
Thanks for your time.
In case you have this same issue, I've managed to get it working by removing all the unnecessary loops and by adding in my SPI init file this:
LPC_PINCON->PINMODE1 |= (3<<2) | (3<<4);