Mercurial > ~darius > hgwebdir.cgi > stm32temp
view spiflash.c @ 83:05ba84c7da97
Add a flash layer for compatibility (in future).
Fix SPI flash block writing.
White space fixes.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Mon, 02 Mar 2015 14:32:08 +1030 |
parents | 1a4573062b37 |
children |
line wrap: on
line source
#include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include "stm32f10x.h" #include "spi.h" #include "spiflash.h" #define FL_SELECT() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define FL_DESELECT() GPIO_SetBits(GPIOA, GPIO_Pin_4) const char *flstattbl[] = { "BUSY", "WEL", "BP0", "BP1", "BP2", "BP3", "AAI", "BPL" }; #define RW_IDLE 0 #define RW_RUNNING 1 static int writestate = RW_IDLE; static int readstate = RW_IDLE; void spiflash4kerase(uint32_t addr) { spiflashenablewrite(); /* Enable writing */ FL_SELECT(); /* Select device */ SPI_WriteByte(FL_4KERASE); /* Send command */ SPI_WriteByte(addr >> 16); /* Send address */ SPI_WriteByte(addr >> 8); SPI_WriteByte(addr); FL_DESELECT(); //fputs("4k erase ", stdout); spiflashwait(); } void spiflashwait(void) { uint8_t cnt; /* Wait for not BUSY */ for (cnt = 0; (spiflashreadstatus() & FL_BUSY) != 0; cnt++) ; //printf("cnt = %d\n", cnt); } uint16_t spiflashreadid(void) { uint8_t fac, dev; FL_SELECT(); /* Select device */ SPI_WriteByte(FL_RDID); /* Send command */ SPI_WriteByte(0x00); /* Send address cycles (ID data starts at 0) */ SPI_WriteByte(0x00); SPI_WriteByte(0x00); fac = SPI_WriteByte(0x00); /* Read ID */ dev = SPI_WriteByte(0x00); FL_DESELECT(); /* De-select device */ return fac << 8 | dev; } void spiflashenablewrite(void) { FL_SELECT(); /* Select device */ SPI_WriteByte(FL_WREN); /* Send command */ FL_DESELECT(); /* De-select device */ } uint8_t spiflashreadstatus(void) { uint8_t status; FL_SELECT(); /* Select device */ SPI_WriteByte(FL_RDSR); /* Send command */ SPI_WriteByte(0x00); /* Send dummy byte for address cycle */ status = SPI_WriteByte(0x00); /* Read status */ FL_DESELECT(); /* De-select device */ return status; } void spiflashwritestatus(uint8_t status) { /* Enable status write */ FL_SELECT(); /* Select device */ SPI_WriteByte(FL_EWSR); /* Send command */ SPI_WriteByte(0x00); /* Send data byte */ FL_DESELECT(); /* Actually write status */ FL_SELECT(); /* Re-select device for new command */ SPI_WriteByte(FL_WRSR); /* Send command */ SPI_WriteByte(status); /* Send data byte */ FL_DESELECT(); /* De-select device */ } uint8_t spiflashread(uint32_t addr) { uint8_t data; FL_SELECT(); /* Select device */ SPI_WriteByte(FL_READ); /* Send command */ SPI_WriteByte(addr >> 16); /* Send address */ SPI_WriteByte(addr >> 8); SPI_WriteByte(addr); data = SPI_WriteByte(0x00); /* Read data */ FL_DESELECT(); /* De-select device */ return data; } void spiflashwrite(uint32_t addr, uint8_t data) { spiflashwait(); spiflashenablewrite(); /* Enable writes */ FL_SELECT(); /* Select device */ SPI_WriteByte(FL_BYTEPROG); /* Send command */ SPI_WriteByte(addr >> 16); /* Send address */ SPI_WriteByte(addr >> 8); SPI_WriteByte(addr); SPI_WriteByte(data); /* Write data */ FL_DESELECT(); /* De-select device */ } /* * fStream reading looks like so * */ void spiflashstartread(uint32_t addr) { assert(readstate == RW_IDLE); FL_SELECT(); /* Select device */ SPI_WriteByte(FL_READ); /* Send command */ SPI_WriteByte(addr >> 16); /* Send address */ SPI_WriteByte(addr >> 8); SPI_WriteByte(addr); readstate = RW_RUNNING; } uint8_t spiflashreadbyte(void) { assert(readstate == RW_RUNNING); return SPI_WriteByte(0x00); /* Read data */ } void spiflashstopread(void) { assert(readstate == RW_RUNNING); FL_DESELECT(); readstate = RW_IDLE; } /* * Auto increment writing looks like so * * Enable writing CS, WREN, nCS * Send start address & first data word CS, AAI + addr + data, nCS * Send subsequent words wait for nBUSY, CS, AAI + data, nCS * ... * Disable writing CS, WRDI, nCS * * XXX: EBSY command links SO to flash busy state, I don't think the * STM32 could sample it without switching out of SPI mode. */ void spiflashstartwrite(uint32_t addr, uint16_t data) { assert(writestate == RW_IDLE); spiflashenablewrite(); /* Enable writes */ FL_SELECT(); /* Select device */ SPI_WriteByte(FL_AAIWP); /* Send command */ SPI_WriteByte(addr >> 16); SPI_WriteByte(addr >> 8); SPI_WriteByte(addr & 0xff); /* Send address */ SPI_WriteByte(data & 0xff); /* Write LSB */ SPI_WriteByte(data >> 8); /* Write MSB */ FL_DESELECT(); writestate = RW_RUNNING; } void spiflashwriteword(uint16_t data) { assert(writestate == RW_RUNNING); //fputs("write word ", stdout); spiflashwait(); /* Wait until not busy */ FL_SELECT(); /* Select device */ SPI_WriteByte(FL_AAIWP); /* Send command */ SPI_WriteByte(data & 0xff); /* Write LSB */ SPI_WriteByte(data >> 8); /* Write MSB */ FL_DESELECT(); /* De-select device */ } void spiflashstopwrite(void) { assert(writestate == RW_RUNNING); //fputs("flash stop write start ", stdout); spiflashwait(); /* Wait until not busy */ FL_SELECT(); /* Select device */ SPI_WriteByte(FL_WRDI); /* Send command */ FL_DESELECT(); /* Deselect device */ //fputs("flash stop write end ", stdout); spiflashwait(); /* Wait until not busy */ writestate = RW_IDLE; } int spiflashreadblock(uint32_t addr, uint32_t len, void *_data) { uint32_t *data = _data; uint32_t flashcrc, ramcrc, tmp; /* Must be a multiple of 4 due to CRC check */ assert(len % 4 == 0); spiflashstartread(addr); CRC_ResetDR(); for (int i = len; i > 0; i -= 4) { tmp = spiflashreadbyte() | spiflashreadbyte() << 8 | spiflashreadbyte() << 16 | spiflashreadbyte() << 24; *data = tmp; CRC_CalcCRC(tmp); data++; } flashcrc = spiflashreadbyte(); flashcrc |= spiflashreadbyte() << 8; flashcrc |= spiflashreadbyte() << 16; flashcrc |= spiflashreadbyte() << 24; spiflashstopread(); ramcrc = CRC_GetCRC(); //printf("RAM CRC 0x%08x Flash CRC 0x%08x\n", (uint)ramcrc, (uint)flashcrc); if (ramcrc == flashcrc) return 1; else return 0; } uint32_t spiflashcrcblock(uint32_t addr, uint32_t len) { uint32_t tmp; assert(len % 4 == 0); CRC_ResetDR(); spiflashstartread(addr); for (; len > 0; len -= 4) { tmp = spiflashreadbyte() | spiflashreadbyte() << 8 | spiflashreadbyte() << 16 | spiflashreadbyte() << 24; CRC_CalcCRC(tmp); } spiflashstopread(); return CRC_GetCRC(); } int spiflashwriteblock(uint32_t addr, uint32_t len, void *_data) { uint16_t *data = _data; uint32_t crc, vcrc, tmp; //printf("Writing %u bytes to 0x%06x\n", (uint)len, (uint)addr); /* Ensure data is * - 16 bit aligned * - a multiple of 32 bits in length (for CRCs, the flash only need 16 bits) * - not longer than a sector */ assert(addr % 2 == 0); assert(len % 4 == 0); assert(len <= 4096); /* Disable write protect */ spiflashwritestatus(0x00); /* Erase sector */ spiflash4kerase(addr); /* Write data */ CRC_ResetDR(); for (uint i = 0; i < len / 2; i++) { //printf("0x%04x: %04x\n", i, *data); if (i == 0) spiflashstartwrite(addr, *data); else spiflashwriteword(*data); if (i % 2 == 0) tmp = *data; else { tmp |= *data << 16; CRC_CalcCRC(tmp); } data++; } /* Calculate CRC */ crc = CRC_GetCRC(); //printf("CRC is 0x%08x\n", (uint)crc); /* Write CRC */ spiflashwriteword(crc); spiflashwriteword(crc >> 16); spiflashstopwrite(); /* Read back and check CRC */ vcrc = spiflashcrcblock(addr, len); //printf("CRC read back as 0x%08x\n", (uint)vcrc); if (vcrc != crc) return 1; else return 0; } void spiflashprintstatus(uint8_t status, FILE *out) { for (unsigned int i = 0; i < sizeof(flstattbl) / sizeof(flstattbl[0]); i++) if (status & 1 << i) { fputs(flstattbl[i], out); fputs(" ", out); } }