Hexiwear 8MB NOR flash memory library.
W25Q64FV.cpp@0:b71060e03299, 2016-10-09 (annotated)
- Committer:
- DimiterK
- Date:
- Sun Oct 09 23:05:55 2016 +0000
- Revision:
- 0:b71060e03299
First release
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
DimiterK | 0:b71060e03299 | 1 | #include "W25Q64FV.h" |
DimiterK | 0:b71060e03299 | 2 | |
DimiterK | 0:b71060e03299 | 3 | W25Q64FV::W25Q64FV(PinName mosi, PinName miso, PinName sclk, PinName cs, int frequency) |
DimiterK | 0:b71060e03299 | 4 | { |
DimiterK | 0:b71060e03299 | 5 | |
DimiterK | 0:b71060e03299 | 6 | this->cs = new DigitalOut(cs); |
DimiterK | 0:b71060e03299 | 7 | this->cs->write(true); |
DimiterK | 0:b71060e03299 | 8 | this->frequency = frequency; |
DimiterK | 0:b71060e03299 | 9 | |
DimiterK | 0:b71060e03299 | 10 | spi = new SPI(mosi, miso, sclk); |
DimiterK | 0:b71060e03299 | 11 | spi->format(8, 0); |
DimiterK | 0:b71060e03299 | 12 | spi->frequency(frequency); |
DimiterK | 0:b71060e03299 | 13 | } |
DimiterK | 0:b71060e03299 | 14 | |
DimiterK | 0:b71060e03299 | 15 | W25Q64FV::~W25Q64FV() |
DimiterK | 0:b71060e03299 | 16 | { |
DimiterK | 0:b71060e03299 | 17 | delete spi; |
DimiterK | 0:b71060e03299 | 18 | delete cs; |
DimiterK | 0:b71060e03299 | 19 | } |
DimiterK | 0:b71060e03299 | 20 | |
DimiterK | 0:b71060e03299 | 21 | uint16_t W25Q64FV::Id() |
DimiterK | 0:b71060e03299 | 22 | { |
DimiterK | 0:b71060e03299 | 23 | cs->write(0); |
DimiterK | 0:b71060e03299 | 24 | spi->write(CMD_MANU_ID); |
DimiterK | 0:b71060e03299 | 25 | spi->write(0); |
DimiterK | 0:b71060e03299 | 26 | spi->write(0); |
DimiterK | 0:b71060e03299 | 27 | spi->write(0); |
DimiterK | 0:b71060e03299 | 28 | unsigned id = (spi->write(0) << 8) | spi->write(0); |
DimiterK | 0:b71060e03299 | 29 | cs->write(1); |
DimiterK | 0:b71060e03299 | 30 | return id; |
DimiterK | 0:b71060e03299 | 31 | } |
DimiterK | 0:b71060e03299 | 32 | |
DimiterK | 0:b71060e03299 | 33 | uint32_t W25Q64FV::JEDECId() |
DimiterK | 0:b71060e03299 | 34 | { |
DimiterK | 0:b71060e03299 | 35 | cs->write(0); |
DimiterK | 0:b71060e03299 | 36 | spi->write(CMD_JEDEC_ID); |
DimiterK | 0:b71060e03299 | 37 | unsigned id = (spi->write(0) << 16) | (spi->write(0) << 8) | spi->write(0); |
DimiterK | 0:b71060e03299 | 38 | cs->write(1); |
DimiterK | 0:b71060e03299 | 39 | return id; |
DimiterK | 0:b71060e03299 | 40 | } |
DimiterK | 0:b71060e03299 | 41 | |
DimiterK | 0:b71060e03299 | 42 | void W25Q64FV::writeEnable(void) |
DimiterK | 0:b71060e03299 | 43 | { |
DimiterK | 0:b71060e03299 | 44 | cs->write(0); |
DimiterK | 0:b71060e03299 | 45 | spi->write(CMD_WREN); |
DimiterK | 0:b71060e03299 | 46 | cs->write(1); |
DimiterK | 0:b71060e03299 | 47 | } |
DimiterK | 0:b71060e03299 | 48 | |
DimiterK | 0:b71060e03299 | 49 | void W25Q64FV::writeDisable(void) |
DimiterK | 0:b71060e03299 | 50 | { |
DimiterK | 0:b71060e03299 | 51 | cs->write(0); |
DimiterK | 0:b71060e03299 | 52 | spi->write(CMD_WR_DISABLE) ; |
DimiterK | 0:b71060e03299 | 53 | cs->write(1); |
DimiterK | 0:b71060e03299 | 54 | } |
DimiterK | 0:b71060e03299 | 55 | |
DimiterK | 0:b71060e03299 | 56 | uint8_t W25Q64FV::readStatus(void) |
DimiterK | 0:b71060e03299 | 57 | { |
DimiterK | 0:b71060e03299 | 58 | uint8_t data ; |
DimiterK | 0:b71060e03299 | 59 | cs->write(0); |
DimiterK | 0:b71060e03299 | 60 | spi->write(CMD_RDSR1) ; |
DimiterK | 0:b71060e03299 | 61 | data = spi->write(DUMMY) ; // dummy |
DimiterK | 0:b71060e03299 | 62 | cs->write(1); |
DimiterK | 0:b71060e03299 | 63 | return( data ) ; |
DimiterK | 0:b71060e03299 | 64 | } |
DimiterK | 0:b71060e03299 | 65 | |
DimiterK | 0:b71060e03299 | 66 | void W25Q64FV::writeStatusReg(int addr) // Write SR cmd 01h + 3B data |
DimiterK | 0:b71060e03299 | 67 | { |
DimiterK | 0:b71060e03299 | 68 | cs->write(0); |
DimiterK | 0:b71060e03299 | 69 | spi->write(CMD_WRSR1) ; // Write SR cmd 01h |
DimiterK | 0:b71060e03299 | 70 | spi->write((addr >> 16)&0xFF) ; // address |
DimiterK | 0:b71060e03299 | 71 | spi->write((addr >> 8)&0xFF) ; |
DimiterK | 0:b71060e03299 | 72 | spi->write(addr & 0xFF) ; |
DimiterK | 0:b71060e03299 | 73 | cs->write(1); |
DimiterK | 0:b71060e03299 | 74 | } |
DimiterK | 0:b71060e03299 | 75 | |
DimiterK | 0:b71060e03299 | 76 | void W25Q64FV::writeSecurityReg(int addr) // WRSCUR cmd 2Fh + 1B data |
DimiterK | 0:b71060e03299 | 77 | { |
DimiterK | 0:b71060e03299 | 78 | cs->write(0); |
DimiterK | 0:b71060e03299 | 79 | // spi->write(CMD_WRSCUR) ; // Write SR cmd 01h |
DimiterK | 0:b71060e03299 | 80 | spi->write(addr & 0xFF) ; |
DimiterK | 0:b71060e03299 | 81 | cs->write(1); |
DimiterK | 0:b71060e03299 | 82 | } |
DimiterK | 0:b71060e03299 | 83 | |
DimiterK | 0:b71060e03299 | 84 | |
DimiterK | 0:b71060e03299 | 85 | uint8_t W25Q64FV::readByte(int addr) // Single Byte Read |
DimiterK | 0:b71060e03299 | 86 | { |
DimiterK | 0:b71060e03299 | 87 | uint8_t data ; |
DimiterK | 0:b71060e03299 | 88 | cs->write(0); |
DimiterK | 0:b71060e03299 | 89 | spi->write(CMD_READ_DATA) ; // send 03h |
DimiterK | 0:b71060e03299 | 90 | spi->write((addr >> 16)&0xFF) ; |
DimiterK | 0:b71060e03299 | 91 | spi->write((addr >> 8)&0xFF) ; |
DimiterK | 0:b71060e03299 | 92 | spi->write(addr & 0xFF) ; |
DimiterK | 0:b71060e03299 | 93 | data = spi->write(DUMMY) ; // write data is dummy |
DimiterK | 0:b71060e03299 | 94 | cs->write(1); |
DimiterK | 0:b71060e03299 | 95 | return( data ) ; // return 1 byte |
DimiterK | 0:b71060e03299 | 96 | } |
DimiterK | 0:b71060e03299 | 97 | |
DimiterK | 0:b71060e03299 | 98 | bool W25Q64FV::read(uint32_t addr, uint8_t* dst, uint32_t len) |
DimiterK | 0:b71060e03299 | 99 | { |
DimiterK | 0:b71060e03299 | 100 | cs->write(0); |
DimiterK | 0:b71060e03299 | 101 | spi->write(0x03); |
DimiterK | 0:b71060e03299 | 102 | spi->write((addr >> 16) & 0xff); |
DimiterK | 0:b71060e03299 | 103 | spi->write((addr >> 8) & 0xff); |
DimiterK | 0:b71060e03299 | 104 | spi->write(addr & 0xff); |
DimiterK | 0:b71060e03299 | 105 | for (uint32_t i=0; i<len; ++i) |
DimiterK | 0:b71060e03299 | 106 | dst[i] = spi->write(0); |
DimiterK | 0:b71060e03299 | 107 | cs->write(1); |
DimiterK | 0:b71060e03299 | 108 | |
DimiterK | 0:b71060e03299 | 109 | return true; |
DimiterK | 0:b71060e03299 | 110 | } |
DimiterK | 0:b71060e03299 | 111 | |
DimiterK | 0:b71060e03299 | 112 | void W25Q64FV::hsread(uint32_t addr, uint8_t* dst, uint32_t len, int frequency) |
DimiterK | 0:b71060e03299 | 113 | { |
DimiterK | 0:b71060e03299 | 114 | int save_frequency = this->frequency; |
DimiterK | 0:b71060e03299 | 115 | spi->frequency(frequency); |
DimiterK | 0:b71060e03299 | 116 | cs->write(0); |
DimiterK | 0:b71060e03299 | 117 | spi->write(0x0B); |
DimiterK | 0:b71060e03299 | 118 | spi->write((addr >> 16) & 0xff); |
DimiterK | 0:b71060e03299 | 119 | spi->write((addr >> 8) & 0xff); |
DimiterK | 0:b71060e03299 | 120 | spi->write(addr & 0xff); |
DimiterK | 0:b71060e03299 | 121 | spi->write(0); // dummy |
DimiterK | 0:b71060e03299 | 122 | for (uint32_t i=0; i<len; ++i) |
DimiterK | 0:b71060e03299 | 123 | dst[i] = spi->write(0); |
DimiterK | 0:b71060e03299 | 124 | cs->write(1); |
DimiterK | 0:b71060e03299 | 125 | spi->frequency(save_frequency); |
DimiterK | 0:b71060e03299 | 126 | } |
DimiterK | 0:b71060e03299 | 127 | |
DimiterK | 0:b71060e03299 | 128 | uint8_t W25Q64FV::readSFDP(int addr) // Read SFDP |
DimiterK | 0:b71060e03299 | 129 | { |
DimiterK | 0:b71060e03299 | 130 | uint8_t data ; |
DimiterK | 0:b71060e03299 | 131 | cs->write(0); |
DimiterK | 0:b71060e03299 | 132 | spi->write(CMD_READ_SFDP) ; // send cmd 5Ah |
DimiterK | 0:b71060e03299 | 133 | spi->write((addr >> 16)&0xFF) ; // address[23:16] |
DimiterK | 0:b71060e03299 | 134 | spi->write((addr >> 8)&0xFF) ; // address[15:8] |
DimiterK | 0:b71060e03299 | 135 | spi->write(addr & 0xFF) ; // address[7:0] |
DimiterK | 0:b71060e03299 | 136 | spi->write(DUMMY) ; // dummy cycle |
DimiterK | 0:b71060e03299 | 137 | data = spi->write(DUMMY) ; // return 1 byte |
DimiterK | 0:b71060e03299 | 138 | cs->write(1); |
DimiterK | 0:b71060e03299 | 139 | return( data ) ; |
DimiterK | 0:b71060e03299 | 140 | } |
DimiterK | 0:b71060e03299 | 141 | |
DimiterK | 0:b71060e03299 | 142 | uint8_t W25Q64FV::wait_while_busy(void) |
DimiterK | 0:b71060e03299 | 143 | { |
DimiterK | 0:b71060e03299 | 144 | uint8_t temp = 0; |
DimiterK | 0:b71060e03299 | 145 | cs->write(0); //Enable device |
DimiterK | 0:b71060e03299 | 146 | spi->write(CMD_RDSR1); //Send RDSR command |
DimiterK | 0:b71060e03299 | 147 | temp = spi->write(DUMMY); |
DimiterK | 0:b71060e03299 | 148 | cs->write(1); //Disable |
DimiterK | 0:b71060e03299 | 149 | if (temp & 0x01) return 1; |
DimiterK | 0:b71060e03299 | 150 | else return 0; |
DimiterK | 0:b71060e03299 | 151 | } |
DimiterK | 0:b71060e03299 | 152 | |
DimiterK | 0:b71060e03299 | 153 | |
DimiterK | 0:b71060e03299 | 154 | bool W25Q64FV::page_program(uint32_t addr, uint8_t* write_buffer, uint8_t len) |
DimiterK | 0:b71060e03299 | 155 | { |
DimiterK | 0:b71060e03299 | 156 | // no point in writing FF as an empty sector already has those |
DimiterK | 0:b71060e03299 | 157 | // (and if not empty, write won't succeed) |
DimiterK | 0:b71060e03299 | 158 | bool skipped = false; |
DimiterK | 0:b71060e03299 | 159 | while (len > 0 && *write_buffer == 0xFF) |
DimiterK | 0:b71060e03299 | 160 | { |
DimiterK | 0:b71060e03299 | 161 | ++write_buffer; |
DimiterK | 0:b71060e03299 | 162 | --len; |
DimiterK | 0:b71060e03299 | 163 | ++addr; |
DimiterK | 0:b71060e03299 | 164 | skipped = true; |
DimiterK | 0:b71060e03299 | 165 | } |
DimiterK | 0:b71060e03299 | 166 | if (len == 0 && skipped) |
DimiterK | 0:b71060e03299 | 167 | return true; // special case when succeeds when nothing to do |
DimiterK | 0:b71060e03299 | 168 | |
DimiterK | 0:b71060e03299 | 169 | if (len < 1 || len > 256) |
DimiterK | 0:b71060e03299 | 170 | return false; |
DimiterK | 0:b71060e03299 | 171 | |
DimiterK | 0:b71060e03299 | 172 | // write enable |
DimiterK | 0:b71060e03299 | 173 | writeEnable(); |
DimiterK | 0:b71060e03299 | 174 | |
DimiterK | 0:b71060e03299 | 175 | cs->write(0); |
DimiterK | 0:b71060e03299 | 176 | spi->write(0x02); |
DimiterK | 0:b71060e03299 | 177 | spi->write((uint8_t)(addr >> 16)); |
DimiterK | 0:b71060e03299 | 178 | spi->write((uint8_t)(addr >> 8)); |
DimiterK | 0:b71060e03299 | 179 | spi->write((uint8_t)addr); |
DimiterK | 0:b71060e03299 | 180 | for (uint16_t i=0; i<len; ++i) |
DimiterK | 0:b71060e03299 | 181 | spi->write(write_buffer[i]); |
DimiterK | 0:b71060e03299 | 182 | cs->write(1); |
DimiterK | 0:b71060e03299 | 183 | wait_while_busy(); |
DimiterK | 0:b71060e03299 | 184 | |
DimiterK | 0:b71060e03299 | 185 | return true; |
DimiterK | 0:b71060e03299 | 186 | } |
DimiterK | 0:b71060e03299 | 187 | |
DimiterK | 0:b71060e03299 | 188 | void W25Q64FV::sector_erase_4k(uint32_t addr) |
DimiterK | 0:b71060e03299 | 189 | { |
DimiterK | 0:b71060e03299 | 190 | cs->write(0); |
DimiterK | 0:b71060e03299 | 191 | spi->write(CMD_ERASE_SECTOR); |
DimiterK | 0:b71060e03299 | 192 | spi->write((uint8_t)(addr >> 16)); |
DimiterK | 0:b71060e03299 | 193 | spi->write((uint8_t)(addr >> 8)); |
DimiterK | 0:b71060e03299 | 194 | spi->write((uint8_t)addr); |
DimiterK | 0:b71060e03299 | 195 | cs->write(1); |
DimiterK | 0:b71060e03299 | 196 | wait_while_busy(); |
DimiterK | 0:b71060e03299 | 197 | } |
DimiterK | 0:b71060e03299 | 198 | |
DimiterK | 0:b71060e03299 | 199 | void W25Q64FV::block_erase_32k(uint32_t addr) |
DimiterK | 0:b71060e03299 | 200 | { |
DimiterK | 0:b71060e03299 | 201 | cs->write(0); |
DimiterK | 0:b71060e03299 | 202 | spi->write(CMD_ERASE_BLOCK32); |
DimiterK | 0:b71060e03299 | 203 | spi->write((uint8_t)(addr >> 16)); |
DimiterK | 0:b71060e03299 | 204 | spi->write((uint8_t)(addr >> 8)); |
DimiterK | 0:b71060e03299 | 205 | spi->write((uint8_t)addr); |
DimiterK | 0:b71060e03299 | 206 | cs->write(1); |
DimiterK | 0:b71060e03299 | 207 | wait_while_busy(); |
DimiterK | 0:b71060e03299 | 208 | } |
DimiterK | 0:b71060e03299 | 209 | |
DimiterK | 0:b71060e03299 | 210 | void W25Q64FV::block_erase_64k(uint32_t addr) |
DimiterK | 0:b71060e03299 | 211 | { |
DimiterK | 0:b71060e03299 | 212 | cs->write(0); |
DimiterK | 0:b71060e03299 | 213 | spi->write(CMD_ERASE_BLOCK64); |
DimiterK | 0:b71060e03299 | 214 | spi->write((uint8_t)(addr >> 16)); |
DimiterK | 0:b71060e03299 | 215 | spi->write((uint8_t)(addr >> 8)); |
DimiterK | 0:b71060e03299 | 216 | spi->write((uint8_t)addr); |
DimiterK | 0:b71060e03299 | 217 | cs->write(1); |
DimiterK | 0:b71060e03299 | 218 | wait_while_busy(); |
DimiterK | 0:b71060e03299 | 219 | } |
DimiterK | 0:b71060e03299 | 220 | |
DimiterK | 0:b71060e03299 | 221 | void W25Q64FV::chip_erase() |
DimiterK | 0:b71060e03299 | 222 | { |
DimiterK | 0:b71060e03299 | 223 | cs->write(0); |
DimiterK | 0:b71060e03299 | 224 | spi->write(CMD_ERASE_CHIP); |
DimiterK | 0:b71060e03299 | 225 | cs->write(1); |
DimiterK | 0:b71060e03299 | 226 | wait_while_busy(); |
DimiterK | 0:b71060e03299 | 227 | } |
DimiterK | 0:b71060e03299 | 228 | |
DimiterK | 0:b71060e03299 | 229 | |
DimiterK | 0:b71060e03299 | 230 | |
DimiterK | 0:b71060e03299 | 231 | |
DimiterK | 0:b71060e03299 | 232 | |
DimiterK | 0:b71060e03299 | 233 | |
DimiterK | 0:b71060e03299 | 234 | void W25Q64FV::writeArray(uint32_t address, uint8_t* pData, uint32_t arrayLength) |
DimiterK | 0:b71060e03299 | 235 | { |
DimiterK | 0:b71060e03299 | 236 | unsigned int i = 0; |
DimiterK | 0:b71060e03299 | 237 | cs->write(0); //Enable device |
DimiterK | 0:b71060e03299 | 238 | spi->write(CMD_WREN); //Send WREN command |
DimiterK | 0:b71060e03299 | 239 | cs->write(1); //Disable device |
DimiterK | 0:b71060e03299 | 240 | |
DimiterK | 0:b71060e03299 | 241 | cs->write(0); //Enable device |
DimiterK | 0:b71060e03299 | 242 | spi->write(CMD_PAGEPROG); //Send Byte Program command |
DimiterK | 0:b71060e03299 | 243 | spi->write((uint8_t)(address >> 16) & 0xFF); |
DimiterK | 0:b71060e03299 | 244 | spi->write((uint8_t)(address >> 8) & 0xFF); |
DimiterK | 0:b71060e03299 | 245 | spi->write(0x00); |
DimiterK | 0:b71060e03299 | 246 | |
DimiterK | 0:b71060e03299 | 247 | for (i=0;i<arrayLength;i++) |
DimiterK | 0:b71060e03299 | 248 | { |
DimiterK | 0:b71060e03299 | 249 | spi->write(pData[i]); //Send byte to be programmed |
DimiterK | 0:b71060e03299 | 250 | } |
DimiterK | 0:b71060e03299 | 251 | cs->write(1); //Disable device |
DimiterK | 0:b71060e03299 | 252 | |
DimiterK | 0:b71060e03299 | 253 | //Wait Busy |
DimiterK | 0:b71060e03299 | 254 | while ((readStatus() & 0x01) == 0x01) //Waste time until not busy |
DimiterK | 0:b71060e03299 | 255 | { |
DimiterK | 0:b71060e03299 | 256 | } |
DimiterK | 0:b71060e03299 | 257 | } |
DimiterK | 0:b71060e03299 | 258 | |
DimiterK | 0:b71060e03299 | 259 | |
DimiterK | 0:b71060e03299 | 260 | void W25Q64FV::readArray(uint32_t address, uint8_t* pData, uint32_t arrayLength) |
DimiterK | 0:b71060e03299 | 261 | { |
DimiterK | 0:b71060e03299 | 262 | unsigned int i = 0; |
DimiterK | 0:b71060e03299 | 263 | |
DimiterK | 0:b71060e03299 | 264 | cs->write(0); //Enable device |
DimiterK | 0:b71060e03299 | 265 | spi->write(CMD_READ_DATA); //Read command |
DimiterK | 0:b71060e03299 | 266 | spi->write((uint8_t)(address >> 16)); |
DimiterK | 0:b71060e03299 | 267 | spi->write((uint8_t)(address >> 8)); |
DimiterK | 0:b71060e03299 | 268 | spi->write((uint8_t) address); |
DimiterK | 0:b71060e03299 | 269 | |
DimiterK | 0:b71060e03299 | 270 | for (i = 0; i <arrayLength; i++) //Read until no_bytes is reached |
DimiterK | 0:b71060e03299 | 271 | { |
DimiterK | 0:b71060e03299 | 272 | pData[i] = spi->write(DUMMY); //Receive bytes |
DimiterK | 0:b71060e03299 | 273 | } |
DimiterK | 0:b71060e03299 | 274 | cs->write(1); //Disable device |
DimiterK | 0:b71060e03299 | 275 | |
DimiterK | 0:b71060e03299 | 276 | //Wait Busy |
DimiterK | 0:b71060e03299 | 277 | while (readStatus() & 0x01) //Waste time until not busy |
DimiterK | 0:b71060e03299 | 278 | { |
DimiterK | 0:b71060e03299 | 279 | } |
DimiterK | 0:b71060e03299 | 280 | } |
DimiterK | 0:b71060e03299 | 281 | |
DimiterK | 0:b71060e03299 | 282 | |
DimiterK | 0:b71060e03299 | 283 |