FastArduino v1.10
C++ library to build fast but small Arduino/AVR projects
Loading...
Searching...
No Matches
winbond.h
Go to the documentation of this file.
1// Copyright 2016-2023 Jean-Francois Poilpret
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
16
38#ifndef WINBOND_HH
39#define WINBOND_HH
40
41#include "../spi.h"
42#include "../time.h"
43#include "../utilities.h"
44
45namespace devices
46{
53 template<board::DigitalPin CS>
54 class WinBond : public spi::SPIDevice<CS, spi::ChipSelect::ACTIVE_LOW, spi::ClockRate::CLOCK_DIV_2>
55 {
56 public:
60 WinBond() = default;
61
69 enum class BlockProtect : uint16_t
70 {
71 BLOCK_NONE = 0x00,
72 BLOCK_UPPER_64KB = 0x01 << 2,
73 BLOCK_UPPER_128KB = 0x02 << 2,
74 BLOCK_UPPER_256KB = 0x03 << 2,
75 BLOCK_UPPER_512KB = 0x04 << 2,
76
77 BLOCK_LOWER_64KB = 0x09 << 2,
78 BLOCK_LOWER_128KB = 0x0A << 2,
79 BLOCK_LOWER_256KB = 0x0B << 2,
80 BLOCK_LOWER_512KB = 0x0C << 2,
81 BLOCK_ALL = 0x07 << 2,
82
83 BLOCK_UPPER_4KB = 0x11 << 2,
84 BLOCK_UPPER_8KB = 0x12 << 2,
85 BLOCK_UPPER_16KB = 0x13 << 2,
86 BLOCK_UPPER_32KB = 0x14 << 2,
87
88 BLOCK_LOWER_4KB = 0x19 << 2,
89 BLOCK_LOWER_8KB = 0x1A << 2,
90 BLOCK_LOWER_16KB = 0x1B << 2,
91 BLOCK_LOWER_32KB = 0x1C << 2
92 };
93
100 enum class StatusRegisterProtect : uint16_t
101 {
102 SOFTWARE_PROTECTION = 0x0000,
103 HARDWARE_PROTECTION = 0x0080,
104 POWER_SUPPLY_LOCKDOWN = 0x0100
105 };
106
113 struct Status
114 {
116 bool busy() const
117 {
118 return value & bits::BV16(BUSY);
119 }
120 bool write_enable_latch() const
121 {
122 return value & bits::BV16(WEL);
123 }
124 BlockProtect block_protect() const
125 {
126 return static_cast<BlockProtect>(value & bits::BV16(BP0, BP1, BP2, TB, SEC));
127 }
128 bool complement_protect() const
129 {
130 return value & bits::BV16(CMP);
131 }
132 bool suspend_status() const
133 {
134 return value & bits::BV16(SUS);
135 }
136 StatusRegisterProtect status_register_protect() const
137 {
138 return static_cast<StatusRegisterProtect>(value & bits::BV16(SRP0, SRP1));
139 }
140
141 const uint16_t value;
143
144 private:
145 Status(uint8_t sr1, uint8_t sr2) : value(utils::as_uint16_t(sr2, sr1)) {}
146
147 static constexpr const uint8_t BUSY = 0;
148 static constexpr const uint8_t WEL = 1;
149 static constexpr const uint8_t BP0 = 2;
150 static constexpr const uint8_t BP1 = 3;
151 static constexpr const uint8_t BP2 = 4;
152 static constexpr const uint8_t TB = 5;
153 static constexpr const uint8_t SEC = 6;
154 static constexpr const uint8_t SRP0 = 7;
155 static constexpr const uint8_t SRP1 = 8;
156 static constexpr const uint8_t CMP = 14;
157 static constexpr const uint8_t SUS = 15;
158
159 friend class WinBond<CS>;
160 };
161
166 {
167 return Status(read(READ_STATUS_1), read(READ_STATUS_2));
168 }
169
174 void set_status(uint16_t status);
175
189 bool wait_until_ready(uint16_t timeout_ms);
190
195 {
196 send(POWER_DOWN);
197 }
198
202 void power_up()
203 {
204 send(POWER_UP);
206 }
207
212 struct Device
213 {
217 uint8_t device_ID;
218 };
219
224
228 uint64_t read_unique_ID();
229
235 {
236 send(WRITE_ENABLE);
237 }
238
244 {
245 send(WRITE_DISABLE);
246 }
247
253 void erase_sector(uint32_t address)
254 {
255 send(SECTOR_ERASE, address);
256 }
257
263 void erase_block_32K(uint32_t address)
264 {
265 send(BLOCK_32K_ERASE, address);
266 }
267
273 void erase_block_64K(uint32_t address)
274 {
275 send(BLOCK_64K_ERASE, address);
276 }
277
283 {
284 send(CHIP_ERASE);
285 }
286
295 void write_page(uint32_t address, uint8_t* data, uint8_t size)
296 {
297 send(PAGE_PROGRAM, address, data, ((size == 0) ? (1U + UINT8_MAX) : size));
298 }
299
305 uint8_t read_data(uint32_t address);
306
314 void read_data(uint32_t address, uint8_t* data, uint16_t size);
315
316 private:
317 uint8_t read(uint8_t code);
318 void send(uint8_t code);
319 void send(uint8_t code, uint32_t address)
320 {
321 send(code, address, nullptr, 0);
322 }
323 void send(uint8_t code, uint32_t address, uint8_t* data, uint16_t size);
324
325 // Instructions
326 static constexpr const uint8_t WRITE_STATUS = 0x01;
327 static constexpr const uint8_t PAGE_PROGRAM = 0x02;
328 static constexpr const uint8_t READ_DATA = 0x03;
329 static constexpr const uint8_t WRITE_DISABLE = 0x04;
330 static constexpr const uint8_t READ_STATUS_1 = 0x05;
331 static constexpr const uint8_t WRITE_ENABLE = 0x06;
332 static constexpr const uint8_t FAST_READ = 0x0B;
333 static constexpr const uint8_t SECTOR_ERASE = 0x20;
334 static constexpr const uint8_t READ_STATUS_2 = 0x35;
335 static constexpr const uint8_t READ_UNIQUE_ID = 0x4B;
336 static constexpr const uint8_t BLOCK_32K_ERASE = 0x52;
337 static constexpr const uint8_t DEVICE_ID = 0x90;
338 static constexpr const uint8_t POWER_UP = 0xAB;
339 static constexpr const uint8_t POWER_DOWN = 0xB9;
340 static constexpr const uint8_t CHIP_ERASE = 0xC7;
341 static constexpr const uint8_t BLOCK_64K_ERASE = 0xD8;
342
343 };
344
345 template<board::DigitalPin CS> void WinBond<CS>::set_status(uint16_t status)
346 {
347 this->start_transfer();
348 this->transfer(bits::LOW_BYTE(status));
349 this->transfer(bits::HIGH_BYTE(status));
350 this->end_transfer();
351 }
352
353 template<board::DigitalPin CS> bool WinBond<CS>::wait_until_ready(uint16_t timeout_ms)
354 {
355 bool ready = false;
356 this->start_transfer();
357 this->transfer(READ_STATUS_1);
358 uint32_t start = time::millis();
359 while (true)
360 {
361 uint8_t status = this->transfer(0x00);
362 if ((status & bits::BV8(Status::BUSY)) == 0)
363 {
364 ready = true;
365 break;
366 }
367 if ((timeout_ms != 0) && (time::since(start) > timeout_ms)) break;
368 time::yield();
369 }
370 this->end_transfer();
371 return ready;
372 }
373
374 template<board::DigitalPin CS> typename WinBond<CS>::Device WinBond<CS>::read_device()
375 {
376 Device device;
377 send(DEVICE_ID, 0, (uint8_t*) &device, sizeof(device));
378 return device;
379 }
380
381 template<board::DigitalPin CS> uint64_t WinBond<CS>::read_unique_ID()
382 {
383 // Since the Read ID instruction must be followed by 4 dummy bytes before
384 // returning the 8 bytes ID, we must use a 9-bytes buffer and skip its
385 // first byte (the 3 other dummy bytes are already sent by send() as the
386 // 0 address)
387 struct PAYLOAD
388 {
389 uint8_t dummy;
390 uint64_t id;
391 };
392 PAYLOAD buffer;
393 send(READ_UNIQUE_ID, 0, (uint8_t*) &buffer, sizeof buffer);
394 // WinBond ID is big-endian (high byte first) but AVR is little-endian
395 // hence we need to convert result (using GCC builtin utility)
396 return __builtin_bswap64(buffer.id);
397 }
398
399 template<board::DigitalPin CS> uint8_t WinBond<CS>::read_data(uint32_t address)
400 {
401 uint8_t data;
402 read_data(address, &data, 1);
403 return data;
404 }
405
406 template<board::DigitalPin CS> void WinBond<CS>::read_data(uint32_t address, uint8_t* data, uint16_t size)
407 {
408 send(READ_DATA, address, data, size);
409 }
410
411 template<board::DigitalPin CS> uint8_t WinBond<CS>::read(uint8_t code)
412 {
413 this->start_transfer();
414 this->transfer(code);
415 uint8_t result = this->transfer(0);
416 this->end_transfer();
417 return result;
418 }
419
420 template<board::DigitalPin CS> void WinBond<CS>::send(uint8_t code)
421 {
422 this->start_transfer();
423 this->transfer(code);
424 this->end_transfer();
425 }
426
427 template<board::DigitalPin CS> void WinBond<CS>::send(uint8_t code, uint32_t address, uint8_t* data, uint16_t size)
428 {
429 this->start_transfer();
430 this->transfer(code);
431 this->transfer(address >> 16);
432 this->transfer(bits::HIGH_BYTE(address));
433 this->transfer(bits::LOW_BYTE(address));
434 this->transfer(data, size);
435 this->end_transfer();
436 }
437}
438
439#endif /* WINBOND_HH */
SPI device driver for WinBond flash memory chips, like W25Q80BV (8 Mbit flash).
Definition: winbond.h:55
void set_status(uint16_t status)
Change the Status register (only writable bits, §6.2.9).
Definition: winbond.h:345
void disable_write()
Disable chip write mode (§6.2.7).
Definition: winbond.h:243
void erase_sector(uint32_t address)
Erase the sector (4KB) at address (§6.2.23).
Definition: winbond.h:253
void power_down()
Set the chip to low power mode (§6.2.29).
Definition: winbond.h:194
Status status()
Get the value of the chip's Status register (§6.1, §6.2.8).
Definition: winbond.h:165
uint64_t read_unique_ID()
Get chip unique ID (§6.2.34).
Definition: winbond.h:381
void power_up()
Release power-down mode (§6.2.30).
Definition: winbond.h:202
void write_page(uint32_t address, uint8_t *data, uint8_t size)
Write data (max 256 bytes) to a page (§6.2.21).
Definition: winbond.h:295
void erase_chip()
Erase the whole chip memory (§6.2.26).
Definition: winbond.h:282
StatusRegisterProtect
This enum provides information about the method of write protection of the Status register itself (bi...
Definition: winbond.h:101
void erase_block_32K(uint32_t address)
Erase the block (32KB) at address (§6.2.24).
Definition: winbond.h:263
void erase_block_64K(uint32_t address)
Erase the sector (64KB) at address (§6.2.25).
Definition: winbond.h:273
void enable_write()
Enable write mode for the chip (§6.2.5).
Definition: winbond.h:234
Device read_device()
Get device informaton §6.2.31).
Definition: winbond.h:374
BlockProtect
This enum provides information about block protection (bits BP0-2, TB and SEC of Status register,...
Definition: winbond.h:70
bool wait_until_ready(uint16_t timeout_ms)
Wait until any erase or write operation is finished.
Definition: winbond.h:353
WinBond()=default
Create a new device driver for a WinBond chip.
uint8_t read_data(uint32_t address)
Read one byte of flash memory (§6.2.10).
Definition: winbond.h:399
Base class for any SPI slave device.
Definition: spi.h:331
static constexpr uint8_t HIGH_BYTE(uint16_t value)
Extract the high byte (aka Most Significant Byte, MSB) of a uint16_t value.
Definition: bits.h:269
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
static constexpr uint16_t BV16(uint8_t bit)
Create a uint16_t bitmask for the given bit number.
Definition: bits.h:148
static constexpr uint8_t LOW_BYTE(uint16_t value)
Extract the low byte (aka Least Significant Byte, LSB) of a uint16_t value.
Definition: bits.h:261
Defines all API for all external devices supported by FastArduino.
MILLIS_PTR millis
Count number of milliseconds elapsed since some time base reference (generally since MCU startup).
Definition: time.cpp:20
void delay_us(uint16_t us) INLINE
Delay program execution for the given amount of microseconds.
Definition: time.h:334
void yield()
Utility method used by many FastArduino API in order to "yield" some processor time; concretely it ju...
Definition: time.cpp:22
uint32_t since(uint32_t start_ms)
Compute the time elapsed, in milliseconds, since start_ms.
Definition: time.cpp:41
constexpr uint16_t as_uint16_t(uint8_t high, uint8_t low)
Convert 2 bytes into an unsigned int.
Definition: utilities.h:327
Device information (§6.2.31)
Definition: winbond.h:213
uint8_t manufacturer_ID
Manufacturer ID, always 0xEF.
Definition: winbond.h:215
uint8_t device_ID
Device ID.
Definition: winbond.h:217
This type maps WinBond Status register (§6.1) to more readable pieces.
Definition: winbond.h:114