Mercurial > ~darius > hgwebdir.cgi > stm32temp
view flash.c @ 27:5c9d2e3d6591
Add flashread/writeblock commands which read/write a block of data to
flash with a CRC.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Tue, 20 Nov 2012 21:54:06 +1030 |
parents | 74efdb21ae5d |
children | 03592ca4d37e |
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 "flash.h" #define FL_SELECT() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define FL_DESELECT() GPIO_SetBits(GPIOA, GPIO_Pin_4) static const char *flstattbl[] = { "BUSY", "WEL", "BP0", "BP1", "BP2", "BP3", "AAI", "BPL" }; #define RW_IDLE 0 #define RW_RUNNING 1 /* Holds all the settings needed */ typedef struct { uint8_t fermenter_ROM[8]; uint8_t fridge_ROM[8]; uint8_t ambient_ROM[8]; int16_t target_temp; uint16_t hysteresis; /* How much to under/overshoot on heating/cooling */ int16_t minheatovershoot; int16_t mincoolovershoot; /* Minimum time the cooler can be on/off */ int16_t mincoolontime; int16_t mincoolofftime; /* Minimum time the heater can be on/off */ int16_t minheatontime; int16_t minheatofftime; #define TC_MODE_AUTO 'a' /* Automatic control */ #define TC_MODE_HEAT 'h' /* Force heating */ #define TC_MODE_COOL 'c' /* Force cooling */ #define TC_MODE_IDLE 'i' /* Force idle */ #define TC_MODE_NOTHING 'n' /* Do nothing (like idle but log nothing) */ char mode; /* Bit patterns for various modes */ uint8_t coolbits; uint8_t heatbits; uint8_t idlebits; /* Check/stale times */ int16_t check_interval; int16_t stale_factor; /* Beep if stale */ int8_t dobeep; /* Pad to 4 bytes */ uint8_t pad[1]; } __attribute__((packed, aligned(4))) settings_t; const settings_t default_settings = { .fermenter_ROM = { 0x10, 0x4c, 0x7d, 0x53, 0x01, 0x08, 0x00, 0xff }, .fridge_ROM = { 0x10, 0x6f, 0x40, 0x53, 0x01, 0x08, 0x00, 0x16 }, .ambient_ROM = { 0x10, 0x76, 0x05, 0x53, 0x01, 0x08, 0x00, 0x8c }, .target_temp = 1000, .hysteresis = 100, .minheatovershoot = 50, .mincoolovershoot = -50, .mincoolontime = 300, .mincoolofftime = 600, .minheatontime = 60, .minheatofftime = 60, .mode = TC_MODE_AUTO, .coolbits = 1 << 6, .heatbits = 1<< 7, .idlebits = 0x00, .check_interval = 10, .stale_factor = 3, .dobeep = 0 }; /* RAM copy of setting */ static settings_t ram_settings; static int writestate = RW_IDLE; static int readstate = RW_IDLE; void flashcmd(char **argv, int argc) { uint8_t status, tmp, len; uint32_t addr; if (argc == 0) { fputs("No command specified\r\n", stdout); return; } if (!strcmp(argv[0], "str")) { status = flashreadstatus(); fputs("Status = ", stdout); for (unsigned int i = 0; i < sizeof(flstattbl) / sizeof(flstattbl[0]); i++) if (status & 1 << i) { fputs(flstattbl[i], stdout); fputs(" ", stdout); } printf("(0x%02x)\r\n", status); } else if (!strcmp(argv[0], "stw")) { if (argc != 2) { fputs("Incorrect number of arguments\r\n", stdout); return; } tmp = atoi(argv[1]); flashwritestatus(tmp); status = flashreadstatus(); printf("Wrote 0x%02x to status, now 0x%02x\r\n", tmp, status); } else if (!strcmp(argv[0], "er")) { if (argc != 2) { fputs("Incorrect number of arguments\r\n", stdout); return; } addr = atoi(argv[1]); flash4kerase(addr); printf("Erased 0x%x\r\n", (unsigned int)addr); } else if (!strcmp(argv[0], "rd")) { if (argc < 2) { fputs("Incorrect number of arguments\r\n", stdout); return; } addr = atoi(argv[1]); if (argc > 2) len = atoi(argv[2]); else len = 16; flashstartread(addr); for (uint32_t i = 0; i < len; i++) printf("Read 0x%02x from 0x%06x\r\n", flashreadbyte(), (unsigned int)(addr + i)); flashstopread(); fputs("\r\n", stdout); } else if (!strcmp(argv[0], "wr")) { if (argc < 2) { fputs("Incorrect number of arguments\r\n", stdout); return; } addr = atoi(argv[1]); if (argc > 2) len = atoi(argv[2]); else len = 16; for (int i = 0; i < 16; i += 2) { uint16_t data; data = ((i + 1) << 8) | i; printf("Writing 0x%04x to 0x%06x\r\n", data, (unsigned int)(addr + i)); if (i == 0) flashstartwrite(addr, data); else flashwriteword(data); } flashstopwrite(); } else if (!strcmp(argv[0], "tw")) { /* Copy default to RAM */ bcopy(&default_settings, &ram_settings, sizeof(default_settings)); /* Write RAM copy into flash */ flashwriteblock(0, sizeof(ram_settings), &ram_settings); } else if (!strcmp(argv[0], "tr")) { int crcok; /* Read flash copy to RAM */ crcok = flashreadblock(0, sizeof(ram_settings), &ram_settings); printf("CRC is %s\r\n", crcok ? "OK" : "bad"); } else if (!strcmp(argv[0], "id")) { printf("Flash ID = 0x%04hx (expect 0xbf41)\r\n", flashreadid()); } else { fputs("Unknown sub command\r\n", stdout); return; } } void flash4kerase(uint32_t addr) { flashenablewrite(); /* 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); flashwait(); } void flashwait(void) { uint8_t cnt; /* Wait for not BUSY */ for (cnt = 0; (flashreadstatus() & FL_BUSY) != 0; cnt++) ; //printf("cnt = %d\r\n", cnt); } uint16_t flashreadid(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 flashenablewrite(void) { FL_SELECT(); /* Select device */ SPI_WriteByte(FL_WREN); /* Send command */ FL_DESELECT(); /* De-select device */ } uint8_t flashreadstatus(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 flashwritestatus(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 flashread(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 flashwrite(uint32_t addr, uint8_t data) { flashwait(); flashenablewrite(); /* 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 flashstartread(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 flashreadbyte(void) { assert(readstate == RW_RUNNING); return SPI_WriteByte(0x00); /* Read data */ } void flashstopread(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 flashstartwrite(uint32_t addr, uint16_t data) { assert(writestate == RW_IDLE); flashenablewrite(); /* 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 flashwriteword(uint16_t data) { assert(writestate == RW_RUNNING); //fputs("write word ", stdout); flashwait(); /* 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 flashstopwrite(void) { assert(writestate == RW_RUNNING); //fputs("flash stop write start ", stdout); flashwait(); /* Wait until not busy */ FL_SELECT(); /* Select device */ SPI_WriteByte(FL_WRDI); /* Send command */ FL_DESELECT(); /* Deselect device */ //fputs("flash stop write end ", stdout); flashwait(); /* Wait until not busy */ writestate = RW_IDLE; } int flashreadblock(uint32_t addr, uint32_t len, void *_data) { uint8_t *data = _data; uint32_t flashcrc, ramcrc; /* Must be a multiple of 4 due to CRC check */ assert(len % 4 == 0); flashstartread(addr); for (int i = len; i > 0; i--) { *data = flashreadbyte(); data++; } flashcrc = flashreadbyte(); flashcrc |= flashreadbyte() << 8; flashcrc |= flashreadbyte() << 16; flashcrc |= flashreadbyte() << 24; flashstopread(); /* Calculate CRC */ CRC_ResetDR(); ramcrc = CRC_CalcBlockCRC((uint32_t *)_data, len / 4); printf("RAM CRC 0x%08x Flash CRC 0x%08x\r\n", (uint)ramcrc, (uint)flashcrc); if (ramcrc == flashcrc) return 1; else return 0; } void flashwriteblock(uint32_t addr, uint32_t len, void *_data) { uint16_t *data = _data; uint32_t crc; printf("Writing %u bytes to 0x%06x\r\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 */ flashwritestatus(0x00); /* Erase sector */ flash4kerase(addr); /* Write data */ for (uint i = 0; i < len / 2; i++) { if (i == 0) flashstartwrite(addr, *data); else flashwriteword(*data); data++; } /* Calculate CRC */ CRC_ResetDR(); crc = CRC_CalcBlockCRC((uint32_t *)_data, len / 4); printf("CRC is 0x%08x\r\n", (uint)crc); /* Write CRC */ flashwriteword(crc); flashwriteword(crc >> 16); flashstopwrite(); }