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.
i am looking for a designer for a company profile design Dubai that provides the best-related designing work, even we are here for also helping you right now i need you help. to handle the SST25VF016B data flash you need to do some change in programming.