FastArduino  v1.7
C++ library to build fast but small Arduino/AVR projects
winbond.h
Go to the documentation of this file.
1 // Copyright 2016-2021 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 
45 namespace 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  {
115  bool busy() const
116  {
117  return value & bits::BV16(BUSY);
118  }
119  bool write_enable_latch() const
120  {
121  return value & bits::BV16(WEL);
122  }
123  BlockProtect block_protect() const
124  {
125  return static_cast<BlockProtect>(value & bits::BV16(BP0, BP1, BP2, TB, SEC));
126  }
127  bool complement_protect() const
128  {
129  return value & bits::BV16(CMP);
130  }
131  bool suspend_status() const
132  {
133  return value & bits::BV16(SUS);
134  }
135  StatusRegisterProtect status_register_protect() const
136  {
137  return static_cast<StatusRegisterProtect>(value & bits::BV16(SRP0, SRP1));
138  }
139 
140  const uint16_t value;
141 
142  private:
143  Status(uint8_t sr1, uint8_t sr2) : value(utils::as_uint16_t(sr2, sr1)) {}
144 
145  static constexpr const uint8_t BUSY = 0;
146  static constexpr const uint8_t WEL = 1;
147  static constexpr const uint8_t BP0 = 2;
148  static constexpr const uint8_t BP1 = 3;
149  static constexpr const uint8_t BP2 = 4;
150  static constexpr const uint8_t TB = 5;
151  static constexpr const uint8_t SEC = 6;
152  static constexpr const uint8_t SRP0 = 7;
153  static constexpr const uint8_t SRP1 = 8;
154  static constexpr const uint8_t CMP = 14;
155  static constexpr const uint8_t SUS = 15;
156 
157  friend class WinBond<CS>;
158  };
159 
164  {
165  return Status(read(READ_STATUS_1), read(READ_STATUS_2));
166  }
167 
172  void set_status(uint16_t status);
173 
187  bool wait_until_ready(uint16_t timeout_ms);
188 
192  void power_down()
193  {
194  send(POWER_DOWN);
195  }
196 
200  void power_up()
201  {
202  send(POWER_UP);
203  time::delay_us(3);
204  }
205 
210  struct Device
211  {
212  uint8_t manufacturer_ID;
213  uint8_t device_ID;
214  };
215 
220 
224  uint64_t read_unique_ID();
225 
231  {
232  send(WRITE_ENABLE);
233  }
234 
240  {
241  send(WRITE_DISABLE);
242  }
243 
249  void erase_sector(uint32_t address)
250  {
251  send(SECTOR_ERASE, address);
252  }
253 
259  void erase_block_32K(uint32_t address)
260  {
261  send(BLOCK_32K_ERASE, address);
262  }
263 
269  void erase_block_64K(uint32_t address)
270  {
271  send(BLOCK_64K_ERASE, address);
272  }
273 
278  void erase_chip()
279  {
280  send(CHIP_ERASE);
281  }
282 
291  void write_page(uint32_t address, uint8_t* data, uint8_t size)
292  {
293  send(PAGE_PROGRAM, address, data, ((size == 0) ? (1U + UINT8_MAX) : size));
294  }
295 
301  uint8_t read_data(uint32_t address);
302 
310  void read_data(uint32_t address, uint8_t* data, uint16_t size);
311 
312  private:
313  uint8_t read(uint8_t code);
314  void send(uint8_t code);
315  void send(uint8_t code, uint32_t address)
316  {
317  send(code, address, nullptr, 0);
318  }
319  void send(uint8_t code, uint32_t address, uint8_t* data, uint16_t size);
320 
321  // Instructions
322  static constexpr const uint8_t WRITE_STATUS = 0x01;
323  static constexpr const uint8_t PAGE_PROGRAM = 0x02;
324  static constexpr const uint8_t READ_DATA = 0x03;
325  static constexpr const uint8_t WRITE_DISABLE = 0x04;
326  static constexpr const uint8_t READ_STATUS_1 = 0x05;
327  static constexpr const uint8_t WRITE_ENABLE = 0x06;
328  static constexpr const uint8_t FAST_READ = 0x0B;
329  static constexpr const uint8_t SECTOR_ERASE = 0x20;
330  static constexpr const uint8_t READ_STATUS_2 = 0x35;
331  static constexpr const uint8_t READ_UNIQUE_ID = 0x4B;
332  static constexpr const uint8_t BLOCK_32K_ERASE = 0x52;
333  static constexpr const uint8_t DEVICE_ID = 0x90;
334  static constexpr const uint8_t POWER_UP = 0xAB;
335  static constexpr const uint8_t POWER_DOWN = 0xB9;
336  static constexpr const uint8_t CHIP_ERASE = 0xC7;
337  static constexpr const uint8_t BLOCK_64K_ERASE = 0xD8;
338 
339  };
340 
341  template<board::DigitalPin CS> void WinBond<CS>::set_status(uint16_t status)
342  {
343  this->start_transfer();
344  this->transfer(bits::LOW_BYTE(status));
345  this->transfer(bits::HIGH_BYTE(status));
346  this->end_transfer();
347  }
348 
349  template<board::DigitalPin CS> bool WinBond<CS>::wait_until_ready(uint16_t timeout_ms)
350  {
351  bool ready = false;
352  this->start_transfer();
353  this->transfer(READ_STATUS_1);
354  uint32_t start = time::millis();
355  while (true)
356  {
357  uint8_t status = this->transfer(0x00);
358  if ((status & bits::BV8(Status::BUSY)) == 0)
359  {
360  ready = true;
361  break;
362  }
363  if ((timeout_ms != 0) && (time::since(start) > timeout_ms)) break;
364  time::yield();
365  }
366  this->end_transfer();
367  return ready;
368  }
369 
370  template<board::DigitalPin CS> typename WinBond<CS>::Device WinBond<CS>::read_device()
371  {
372  Device device;
373  send(DEVICE_ID, 0, (uint8_t*) &device, sizeof(device));
374  return device;
375  }
376 
377  template<board::DigitalPin CS> uint64_t WinBond<CS>::read_unique_ID()
378  {
379  // Since the Read ID instruction must be followed by 4 dummy bytes before
380  // returning the 8 bytes ID, we must use a 9-bytes buffer and skip its
381  // first byte (the 3 other dummy bytes are already sent by send() as the
382  // 0 address)
383  struct PAYLOAD
384  {
385  uint8_t dummy;
386  uint64_t id;
387  };
388  PAYLOAD buffer;
389  send(READ_UNIQUE_ID, 0, (uint8_t*) &buffer, sizeof buffer);
390  // WinBond ID is big-endian (high byte first) but AVR is little-endian
391  // hence we need to convert result (using GCC builtin utility)
392  return __builtin_bswap64(buffer.id);
393  }
394 
395  template<board::DigitalPin CS> uint8_t WinBond<CS>::read_data(uint32_t address)
396  {
397  uint8_t data;
398  read_data(address, &data, 1);
399  return data;
400  }
401 
402  template<board::DigitalPin CS> void WinBond<CS>::read_data(uint32_t address, uint8_t* data, uint16_t size)
403  {
404  send(READ_DATA, address, data, size);
405  }
406 
407  template<board::DigitalPin CS> uint8_t WinBond<CS>::read(uint8_t code)
408  {
409  this->start_transfer();
410  this->transfer(code);
411  uint8_t result = this->transfer(0);
412  this->end_transfer();
413  return result;
414  }
415 
416  template<board::DigitalPin CS> void WinBond<CS>::send(uint8_t code)
417  {
418  this->start_transfer();
419  this->transfer(code);
420  this->end_transfer();
421  }
422 
423  template<board::DigitalPin CS> void WinBond<CS>::send(uint8_t code, uint32_t address, uint8_t* data, uint16_t size)
424  {
425  this->start_transfer();
426  this->transfer(code);
427  this->transfer(address >> 16);
428  this->transfer(bits::HIGH_BYTE(address));
429  this->transfer(bits::LOW_BYTE(address));
430  this->transfer(data, size);
431  this->end_transfer();
432  }
433 }
434 
435 #endif /* WINBOND_HH */
436 
devices::WinBond::disable_write
void disable_write()
Disable chip write mode (§6.2.7).
Definition: winbond.h:239
devices::WinBond::power_down
void power_down()
Set the chip to low power mode (§6.2.29).
Definition: winbond.h:192
devices::WinBond::erase_sector
void erase_sector(uint32_t address)
Erase the sector (4KB) at address (§6.2.23).
Definition: winbond.h:249
devices
Defines all API for all external devices supported by FastArduino.
Definition: common_magneto.h:27
time::yield
void yield()
Utility method used by many FastArduino API in order to "yield" some processor time; concretely it ju...
Definition: time.cpp:22
devices::WinBond::erase_block_32K
void erase_block_32K(uint32_t address)
Erase the block (32KB) at address (§6.2.24).
Definition: winbond.h:259
devices::WinBond::StatusRegisterProtect
StatusRegisterProtect
This enum provides information about the method of write protection of the Status register itself (bi...
Definition: winbond.h:101
bits::BV8
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
devices::WinBond::Status
This type maps WinBond Status register (§6.1) to more readable pieces.
Definition: winbond.h:114
devices::WinBond::read_unique_ID
uint64_t read_unique_ID()
Get chip unique ID (§6.2.34).
Definition: winbond.h:377
devices::WinBond::power_up
void power_up()
Release power-down mode (§6.2.30).
Definition: winbond.h:200
devices::WinBond::wait_until_ready
bool wait_until_ready(uint16_t timeout_ms)
Wait until any erase or write operation is finished.
Definition: winbond.h:349
time::delay_us
void delay_us(uint16_t us) INLINE
Delay program execution for the given amount of microseconds.
Definition: time.h:334
i2c::Status
Status
Transmission status codes.
Definition: i2c.h:66
utils::as_uint16_t
constexpr uint16_t as_uint16_t(uint8_t high, uint8_t low)
Convert 2 bytes into an unsigned int.
Definition: utilities.h:295
time::since
uint32_t since(uint32_t start_ms)
Compute the time elapsed, in milliseconds, since start_ms.
Definition: time.cpp:41
time::millis
MILLIS_PTR millis
Count number of milliseconds elapsed since some time base reference (generally since MCU startup).
Definition: time.cpp:20
devices::WinBond::erase_block_64K
void erase_block_64K(uint32_t address)
Erase the sector (64KB) at address (§6.2.25).
Definition: winbond.h:269
bits::HIGH_BYTE
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
spi::SPIDevice
Base class for any SPI slave device.
Definition: spi.h:328
devices::WinBond::status
Status status()
Get the value of the chip's Status register (§6.1, §6.2.8).
Definition: winbond.h:163
bits::BV16
static constexpr uint16_t BV16(uint8_t bit)
Create a uint16_t bitmask for the given bit number.
Definition: bits.h:148
devices::WinBond::BlockProtect
BlockProtect
This enum provides information about block protection (bits BP0-2, TB and SEC of Status register,...
Definition: winbond.h:70
devices::WinBond::set_status
void set_status(uint16_t status)
Change the Status register (only writable bits, §6.2.9).
Definition: winbond.h:341
devices::WinBond::write_page
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:291
bits::LOW_BYTE
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
devices::WinBond::read_device
Device read_device()
Get device informaton §6.2.31).
Definition: winbond.h:370
devices::WinBond::Device
Device information (§6.2.31)
Definition: winbond.h:211
devices::WinBond::read_data
uint8_t read_data(uint32_t address)
Read one byte of flash memory (§6.2.10).
Definition: winbond.h:395
devices::WinBond
SPI device driver for WinBond flash memory chips, like W25Q80BV (8 Mbit flash).
Definition: winbond.h:55
devices::WinBond::WinBond
WinBond()=default
Create a new device driver for a WinBond chip.
devices::WinBond::erase_chip
void erase_chip()
Erase the whole chip memory (§6.2.26).
Definition: winbond.h:278
devices::WinBond::enable_write
void enable_write()
Enable write mode for the chip (§6.2.5).
Definition: winbond.h:230