This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

SPI SST25VF016B

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.