From 1dfcc87f389723c32ffe6bc1070463f60c709449 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Thu, 13 Jul 2017 23:16:10 +0100 Subject: [PATCH 01/11] Add driver for Adesto AT45DB family --- AT45DBBlockDevice.cpp | 665 +++++++++++++++++++++++++++++ AT45DBBlockDevice.h | 160 +++++++ TESTS/block_device/at45db/main.cpp | 135 ++++++ 3 files changed, 960 insertions(+) create mode 100644 AT45DBBlockDevice.cpp create mode 100644 AT45DBBlockDevice.h create mode 100644 TESTS/block_device/at45db/main.cpp diff --git a/AT45DBBlockDevice.cpp b/AT45DBBlockDevice.cpp new file mode 100644 index 0000000000..8a53eb9081 --- /dev/null +++ b/AT45DBBlockDevice.cpp @@ -0,0 +1,665 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AT45DBBlockDevice.h" + +#include + +/* constants */ +#define AT45DB_READ_SIZE 1 +#define AT45DB_PROG_SIZE 1 +#define AT45DB_TIMEOUT 10000 +#define AT45DB_ID_MATCH 0x1F20 +#define AT45DB_ID_DENSITY_MASK 0x001F +#define AT45DB_PAGE_SIZE_256 0x0100 +#define AT45DB_PAGE_SIZE_512 0x0200 +#define AT45DB_BLOCK_SIZE_2K 0x0800 +#define AT45DB_BLOCK_SIZE_4K 0x1000 + +/* enable debug */ +#define AT45DB_DEBUG 0 + +#if AT45DB_DEBUG +#define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else +#define DEBUG_PRINTF(...) +#endif + +void _print_status(uint16_t status); + +/* non-exhaustive opcode list */ +enum opcode { + AT45DB_OP_NOP = 0x00, + AT45DB_OP_STATUS = 0xD7, + AT45DB_OP_ID = 0x9F, + AT45DB_OP_READ_LOW_POWER = 0x01, + AT45DB_OP_READ_LOW_FREQUENCY = 0x03, + AT45DB_OP_PROGRAM_DIRECT = 0x02, // Program through Buffer 1 without Built-In Erase + AT45DB_OP_ERASE_BLOCK = 0x50, +}; + +/* non-exhaustive command list */ +enum command { + AT45DB_COMMAND_WRITE_DISABLE = 0x3D2A7F9A, + AT45DB_COMMAND_WRITE_ENABLE = 0x3D2A7FA9, + AT45DB_COMMAND_SET_PAGE_SIZE = 0x3D2A80A6, +}; + +/* bit masks for interpreting the status register */ +enum status_bit { + AT45DB_BIT_READY = (0x01 << 15), + AT45DB_BIT_COMPARE = (0x01 << 14), + AT45DB_BIT_DENSITY = (0x0F << 10), + AT45DB_BIT_PROTECT = (0x01 << 9), + AT45DB_BIT_PAGE_SIZE = (0x01 << 8), + + AT45DB_BIT_ERASE_PROGRAM_ERROR = (0x01 << 5), + AT45DB_BIT_SECTOR_LOCKDOWN = (0x01 << 3), + AT45DB_BIT_PROGRAM_SUSPEND_2 = (0x01 << 2), + AT45DB_BIT_PROGRAM_SUSPEND_1 = (0x01 << 1), + AT45DB_BIT_ERASE_SUSPEND = (0x01 << 0), +}; + +/* bit masks for detecting density from status register */ +enum status_density { + AT45DB_STATUS_DENSITY_2_MBIT = (0x05 << 10), + AT45DB_STATUS_DENSITY_4_MBIT = (0x07 << 10), + AT45DB_STATUS_DENSITY_8_MBIT = (0x09 << 10), + AT45DB_STATUS_DENSITY_16_MBIT = (0x0B << 10), + AT45DB_STATUS_DENSITY_32_MBIT = (0x0D << 10), + AT45DB_STATUS_DENSITY_64_MBIT = (0x0F << 10), +}; + +/* code for calculating density */ +enum id_density { + AT45DB_ID_DENSITY_2_MBIT = 0x03, + AT45DB_ID_DENSITY_4_MBIT = 0x04, + AT45DB_ID_DENSITY_8_MBIT = 0x05, + AT45DB_ID_DENSITY_16_MBIT = 0x06, + AT45DB_ID_DENSITY_32_MBIT = 0x07, + AT45DB_ID_DENSITY_64_MBIT = 0x08, +}; + +/* typical duration in milliseconds for each operation */ +enum timing { + AT45DB_TIMING_ERASE_PROGRAM_PAGE = 17, + AT45DB_TIMING_PROGRAM_PAGE = 3, + AT45DB_TIMING_ERASE_PAGE = 12, + AT45DB_TIMING_ERASE_BLOCK = 45, + AT45DB_TIMING_ERASE_SECTOR = 700, + AT45DB_TIMING_ERASE_CHIP = 45000 +}; + +/* frequency domains */ +enum frequency { + AT45DB_LOW_POWER_FREQUENCY = 15000000, + AT45DB_LOW_FREQUENCY = 50000000, + AT45DB_HIGH_FREQUENCY = 85000000, + AT45DB_HIGHEST_FREQUENCY = 104000000 +}; + +/* number of dummy bytes required in each frequency domain */ +enum dummy { + AT45DB_LOW_POWER_BYTES = 0, + AT45DB_LOW_FREQUENCY_BYTES = 0, + AT45DB_HIGH_FREQUENCY_BYTES = 1, + AT45DB_HIGHEST_FREQUENCY_BYTES = 2 +}; + +AT45DBBlockDevice::AT45DBBlockDevice(PinName mosi, + PinName miso, + PinName sclk, + PinName cs, + int freq, + PinName nwp) + : _spi(mosi, miso, sclk), + _cs(cs, 1), + _nwp(nwp), + _device_size(0) +{ + /* check that frequency is within range */ + if (freq > AT45DB_LOW_FREQUENCY) { + + /* cap frequency at the highest supported one */ + _spi.frequency(AT45DB_LOW_FREQUENCY); + + } else { + /* freqency is valid, use as-is */ + _spi.frequency(freq); + } + + /* write protect chip if pin is connected */ + if (nwp != NC) { + _nwp = 0; + } +} + +int AT45DBBlockDevice::init() +{ + DEBUG_PRINTF("init\r\n"); + + int result = BD_ERROR_DEVICE_ERROR; + + /* read ID register to validate model and set dimensions */ + uint16_t id = _get_register(AT45DB_OP_ID); + + DEBUG_PRINTF("id: %04X\r\n", id & AT45DB_ID_MATCH); + + /* manufacture ID match */ + if ((id & AT45DB_ID_MATCH) == AT45DB_ID_MATCH) { + /* calculate density */ + _device_size = 0x8000 << (id & AT45DB_ID_DENSITY_MASK); + + /* set page program size and block erase size */ + switch (id & AT45DB_ID_DENSITY_MASK) { + case AT45DB_ID_DENSITY_2_MBIT: + case AT45DB_ID_DENSITY_4_MBIT: + case AT45DB_ID_DENSITY_8_MBIT: + case AT45DB_ID_DENSITY_64_MBIT: + _page_size = AT45DB_PAGE_SIZE_256; + _block_size = AT45DB_BLOCK_SIZE_2K; + break; + case AT45DB_ID_DENSITY_16_MBIT: + case AT45DB_ID_DENSITY_32_MBIT: + _page_size = AT45DB_PAGE_SIZE_512; + _block_size = AT45DB_BLOCK_SIZE_4K; + break; + default: + break; + } + + DEBUG_PRINTF("density: %" PRIu16 "\r\n", id & AT45DB_ID_DENSITY_MASK); + DEBUG_PRINTF("size: %" PRIu32 "\r\n", _device_size); + + /* device successfully detected, set OK error code */ + result = BD_ERROR_OK; + } + + /* get status register to verify the page size mode */ + uint16_t status = _get_register(AT45DB_OP_STATUS); + + /* If the page size is set to a size that is not a power of two + then reconfigure it to use binary sizes instead. + + In the future, the extra space can be used for error correction codes. + */ + if ((status & AT45DB_BIT_PAGE_SIZE) != AT45DB_BIT_PAGE_SIZE) { + DEBUG_PRINTF("Page size is 528 bytes\r\n"); + + /* send reconfiguration command */ + _write_command(AT45DB_COMMAND_SET_PAGE_SIZE, NULL, 0); + + /* wait for device to be ready and update return code */ + result = _sync(); + + } else { + DEBUG_PRINTF("Page size is 512 bytes\r\n"); + } + + /* write protect device when idle */ + _write_enable(false); + + return result; +} + +int AT45DBBlockDevice::deinit() +{ + DEBUG_PRINTF("deinit\r\n"); + + return BD_ERROR_OK; +} + +int AT45DBBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + DEBUG_PRINTF("program: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); + + int result = BD_ERROR_DEVICE_ERROR; + + /* check parameters are valid and the read is within bounds */ + if (is_valid_read(addr, size) && buffer) { + + uint8_t *external_buffer = static_cast(buffer); + + /* activate device */ + _cs = 0; + + /* send read opcode */ + _spi.write(AT45DB_OP_READ_LOW_FREQUENCY); + + /* send read address */ + _spi.write((addr >> 16) & 0xFF); + _spi.write((addr >> 8) & 0xFF); + _spi.write(addr & 0xFF); + + /* clock out one byte at a time and store in external buffer */ + for (uint32_t index = 0; index < size; index++) { + external_buffer[index] = _spi.write(AT45DB_OP_NOP); + } + + /* deactivate device */ + _cs = 1; + + result = BD_ERROR_OK; + } + + return result; +} + +int AT45DBBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +{ + DEBUG_PRINTF("program: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); + + int result = BD_ERROR_DEVICE_ERROR; + + /* check parameters are valid and the write is within bounds */ + if (is_valid_program(addr, size) && buffer) { + + const uint8_t * external_buffer = static_cast(buffer); + + /* Each write command can only cover one page at a time. + Find page and current page offset for handling unaligned writes. + */ + uint32_t write_page = addr & ~(_page_size - 1); + uint32_t page_offset = addr & (_page_size - 1); + + /* disable write protection */ + _write_enable(true); + + /* continue until all bytes have been written */ + uint32_t bytes_written = 0; + while (bytes_written < size) { + + /* find remaining bytes to be written */ + uint32_t bytes_remaining = size - bytes_written; + + /* cap the value at the page size */ + if (bytes_remaining > _page_size) { + bytes_remaining = _page_size; + } + + /* Write one page, bytes_written keeps track of the progress, + write_page is the page address, and page_offset is non-zero for + unaligned writes. + */ + result = _write_page(&external_buffer[bytes_written], + write_page + page_offset, + bytes_remaining - page_offset); + + /* update loop variables upon success otherwise break loop */ + if (result == BD_ERROR_OK) { + bytes_written += (_page_size - page_offset); + write_page += _page_size; + + /* After the first successful write, + all subsequent writes will be aligned. + */ + page_offset = 0; + } else { + break; + } + } + + /* enable write protection */ + _write_enable(false); + } + + return result; +} + +int AT45DBBlockDevice::erase(bd_addr_t addr, bd_size_t size) +{ + DEBUG_PRINTF("erase: %" PRIX64 " %" PRIX64 "\r\n", addr, size); + + int result = BD_ERROR_DEVICE_ERROR; + + /* check parameters are valid and the erase is within bounds */ + if (is_valid_erase(addr, size)) { + + /* disable write protection */ + _write_enable(true); + + /* erase one block at a time until the full size has been erased */ + uint32_t erased = 0; + while (erased < size) { + + /* set block erase opcode */ + uint32_t command = AT45DB_OP_ERASE_BLOCK; + + /* set block address */ + command = (command << 8) | ((addr >> 16) & 0xFF); + command = (command << 8) | ((addr >> 8) & 0xFF); + command = (command << 8) | (addr & 0xFF); + + /* send command to device */ + _write_command(command, NULL, 0); + + /* wait until device is ready and update return value */ + result = _sync(); + + /* if erase failed, break loop */ + if (result != BD_ERROR_OK) { + break; + } + + /* update loop variables */ + addr += _block_size; + erased -= _block_size; + } + + /* enable write protection */ + _write_enable(false); + } + + return result; +} + +bd_size_t AT45DBBlockDevice::get_read_size() const +{ + DEBUG_PRINTF("size: %d\r\n", AT45DB_READ_SIZE); + + return AT45DB_READ_SIZE; +} + +bd_size_t AT45DBBlockDevice::get_program_size() const +{ + DEBUG_PRINTF("size: %d\r\n", AT45DB_PROG_SIZE); + + return AT45DB_PROG_SIZE; +} + +bd_size_t AT45DBBlockDevice::get_erase_size() const +{ + DEBUG_PRINTF("size: %" PRIX16 "\r\n", _block_size); + + return _block_size; +} + +bd_size_t AT45DBBlockDevice::size() const +{ + DEBUG_PRINTF("size: %" PRIX32 "\r\n", _device_size); + + return _device_size; +} + +/** + * @brief Function for reading a specific register. + * @details Used for reading either the Status Register or Manufacture and ID Register. + * + * @param opcode Register to be read. + * @return value. + */ +uint16_t AT45DBBlockDevice::_get_register(uint8_t opcode) +{ + DEBUG_PRINTF("_get_register: %" PRIX8 "\r\n", opcode); + + /* activate device */ + _cs = 0; + + /* write opcode */ + _spi.write(opcode); + + /* read and store result */ + int status = (_spi.write(AT45DB_OP_NOP)); + status = (status << 8) | (_spi.write(AT45DB_OP_NOP)); + + /* deactivate device */ + _cs = 1; + + return status; +} + +/** + * @brief Function for sending command and data to device. + * @details The command can be an opcode with address and data or + * a 4 byte command without data. + * + * The supported frequencies and the opcode used do not + * require dummy bytes to be sent after command. + * + * @param command Opcode with address or 4 byte command. + * @param buffer Data to be sent after command. + * @param size Size of buffer. + */ +void AT45DBBlockDevice::_write_command(uint32_t command, const uint8_t *buffer, uint32_t size) +{ + DEBUG_PRINTF("_write_command: %" PRIX32 " %p %" PRIX32 "\r\n", command, buffer, size); + + /* activate device */ + _cs = 0; + + /* send command (opcode with data or 4 byte command) */ + _spi.write((command >> 24) & 0xFF); + _spi.write((command >> 16) & 0xFF); + _spi.write((command >> 8) & 0xFF); + _spi.write(command & 0xFF); + + /* send optional data */ + if (buffer && size) { + for (uint32_t index = 0; index < size; index++) { + _spi.write(buffer[index]); + } + } + + /* deactivate device */ + _cs = 1; +} + +/** + * @brief Enable and disable write protection. + * + * @param enable Boolean for enabling or disabling write protection. + */ +void AT45DBBlockDevice::_write_enable(bool enable) +{ + DEBUG_PRINTF("_write_enable: %d\r\n", enable); + + /* enable writing, disable write protection */ + if (enable) { + + /* send 4 byte command enabling writes */ + _write_command(AT45DB_COMMAND_WRITE_ENABLE, NULL, 0); + + /* if not-write-protected pin is connected, deselect it */ + if (_nwp.is_connected()) { + _nwp = 0; + } + + } else { + + /* if not-write-protected pin is connected, select it */ + if (_nwp.is_connected()) { + _nwp = 1; + } + + /* send 4 byte command disabling writes */ + _write_command(AT45DB_COMMAND_WRITE_DISABLE, NULL, 0); + } +} + +/** + * @brief Sleep and poll status register until device is ready for next command. + * + * @return BlockDevice compatible error code. + */ +int AT45DBBlockDevice::_sync(void) +{ + DEBUG_PRINTF("_sync\r\n"); + + /* default return value if operation times out */ + int result = BD_ERROR_DEVICE_ERROR; + + /* Poll device until a hard coded timeout is reached. + The polling interval is based on the typical page program time. + */ + for (uint32_t timeout = 0; + timeout < AT45DB_TIMEOUT; + timeout += AT45DB_TIMING_ERASE_PROGRAM_PAGE) { + + /* get status register */ + uint16_t status = _get_register(AT45DB_OP_STATUS); + + /* erase/program bit set, exit with error code set */ + if (status & AT45DB_BIT_ERASE_PROGRAM_ERROR) { + DEBUG_PRINTF("AT45DB_BIT_ERASE_PROGRAM_ERROR\r\n"); + break; + /* device ready, set OK code set */ + } else if (status & AT45DB_BIT_READY) { + DEBUG_PRINTF("AT45DB_BIT_READY\r\n"); + result = BD_ERROR_OK; + break; + /* wait the typical write period before trying again */ + } else { + DEBUG_PRINTF("wait_ms: %d\r\n", AT45DB_TIMING_ERASE_PROGRAM_PAGE); + wait_ms(AT45DB_TIMING_ERASE_PROGRAM_PAGE); + } + } + + return result; +} + +/** + * @brief Write single page. + * @details Address can be unaligned. + * + * @param buffer Data to write. + * @param addr Address to write from. + * @param size Bytes to write. Can at most be the full page. + * @return BlockDevice error code. + */ +int AT45DBBlockDevice::_write_page(const uint8_t *buffer, uint32_t addr, uint32_t size) +{ + DEBUG_PRINTF("_write_page: %p %" PRIX32 " %" PRIX32 "\r\n", buffer, addr, size); + + /* opcode for writing directly to device, in a single command, + assuming the page has been erased before hand. + */ + uint32_t command = AT45DB_OP_PROGRAM_DIRECT; + + /* set write address */ + command = (command << 8) | ((addr >> 16) & 0xFF); + command = (command << 8) | ((addr >> 8) & 0xFF); + command = (command << 8) | (addr & 0xFF); + + /* send write command with data */ + _write_command(command, buffer, size); + + /* wait until device is ready before continuing */ + int result = _sync(); + + return result; +} + +/** + * @brief Internal function for printing out each bit set in status register. + * + * @param status Status register. + */ +void _print_status(uint16_t status) +{ +#if AT45DB_DEBUG + DEBUG_PRINTF("%04X\r\n", status); + + /* device is ready (after write/erase) */ + if (status & AT45DB_BIT_READY) + { + DEBUG_PRINTF("AT45DB_BIT_READY\r\n"); + } + + /* Buffer comparison failed */ + if (status & AT45DB_BIT_COMPARE) + { + DEBUG_PRINTF("AT45DB_BIT_COMPARE\r\n"); + } + + /* device size is 2 MB */ + if (status & AT45DB_STATUS_DENSITY_2_MBIT) + { + DEBUG_PRINTF("AT45DB_STATUS_DENSITY_2_MBIT\r\n"); + } + + /* device size is 4 MB */ + if (status & AT45DB_STATUS_DENSITY_4_MBIT) + { + DEBUG_PRINTF("AT45DB_STATUS_DENSITY_4_MBIT\r\n"); + } + + /* device size is 8 MB */ + if (status & AT45DB_STATUS_DENSITY_8_MBIT) + { + DEBUG_PRINTF("AT45DB_STATUS_DENSITY_8_MBIT\r\n"); + } + + /* device size is 16 MB */ + if (status & AT45DB_STATUS_DENSITY_16_MBIT) + { + DEBUG_PRINTF("AT45DB_STATUS_DENSITY_16_MBIT\r\n"); + } + + /* device size is 32 MB */ + if (status & AT45DB_STATUS_DENSITY_32_MBIT) + { + DEBUG_PRINTF("AT45DB_STATUS_DENSITY_32_MBIT\r\n"); + } + + /* device size is 64 MB */ + if (status & AT45DB_STATUS_DENSITY_64_MBIT) + { + DEBUG_PRINTF("AT45DB_STATUS_DENSITY_64_MBIT\r\n"); + } + + /* sector protectino enabled */ + if (status & AT45DB_BIT_PROTECT) + { + DEBUG_PRINTF("AT45DB_BIT_PROTECT\r\n"); + } + + /* page size is a power of 2 */ + if (status & AT45DB_BIT_PAGE_SIZE) + { + DEBUG_PRINTF("AT45DB_BIT_PAGE_SIZE\r\n"); + } + + /* erase/program error */ + if (status & AT45DB_BIT_ERASE_PROGRAM_ERROR) + { + DEBUG_PRINTF("AT45DB_BIT_ERASE_PROGRAM_ERROR\r\n"); + } + + /* sector lockdown still possible */ + if (status & AT45DB_BIT_SECTOR_LOCKDOWN) + { + DEBUG_PRINTF("AT45DB_BIT_SECTOR_LOCKDOWN\r\n"); + } + + /* program operation suspended while using buffer 2 */ + if (status & AT45DB_BIT_PROGRAM_SUSPEND_2) + { + DEBUG_PRINTF("AT45DB_BIT_PROGRAM_SUSPEND_2\r\n"); + } + + /* program operation suspended while using buffer 1 */ + if (status & AT45DB_BIT_PROGRAM_SUSPEND_1) + { + DEBUG_PRINTF("AT45DB_BIT_PROGRAM_SUSPEND_1\r\n"); + } + + /* erase has been suspended */ + if (status & AT45DB_BIT_ERASE_SUSPEND) + { + DEBUG_PRINTF("AT45DB_BIT_ERASE_SUSPEND\r\n"); + } +#endif +} diff --git a/AT45DBBlockDevice.h b/AT45DBBlockDevice.h new file mode 100644 index 0000000000..358da4db08 --- /dev/null +++ b/AT45DBBlockDevice.h @@ -0,0 +1,160 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_AT45DB_BLOCK_DEVICE_H +#define MBED_AT45DB_BLOCK_DEVICE_H + +#include +#include "BlockDevice.h" + + +/** BlockDevice for AT45DB flash devices + * + * @code + * #include "mbed.h" + * #include "AT45DBBlockDevice.h" + * + * // Create at45db on SPI bus with PTE5 as chip select + * AT45DBBlockDevice at45db(PTE2, PTE4, PTE1, PTE5); + * + * // Create at45db on SPI bus with PTE6 as write-protect + * AT45DBBlockDevice at45db(PTE2, PTE4, PTE1, PTE5, PTE6); + * + * int main() { + * printf("at45db test\n"); + * at45db.init(); + * printf("at45db size: %llu\n", at45db.size()); + * printf("at45db read size: %llu\n", at45db.get_read_size()); + * printf("at45db program size: %llu\n", at45db.get_program_size()); + * printf("at45db erase size: %llu\n", at45db.get_erase_size()); + * + * uint8_t *buffer = malloc(at45db.get_erase_size()); + * sprintf(buffer, "Hello World!\n"); + * at45db.erase(0, at45db.get_erase_size()); + * at45db.program(buffer, 0, at45db.get_erase_size()); + * at45db.read(buffer, 0, at45db.get_erase_size()); + * printf("%s", buffer); + * + * at45db.deinit(); + * } + */ +class AT45DBBlockDevice : public BlockDevice { +public: + /** Creates a AT45DBBlockDevice on a SPI bus specified by pins + * + * @param mosi SPI master out, slave in pin + * @param miso SPI master in, slave out pin + * @param sclk SPI clock pin + * @param csel SPI chip select pin + * @param nowp GPIO not-write-protect + * @param freq Clock speed of the SPI bus (defaults to 40MHz) + */ + AT45DBBlockDevice(PinName mosi, + PinName miso, + PinName sclk, + PinName csel, + int freq = 40000000, + PinName nowp = NC); + + /** Initialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int deinit(); + + /** Read blocks from a block device + * + * @param buffer Buffer to write blocks to + * @param addr Address of block to begin reading from + * @param size Size to read in bytes, must be a multiple of read block size + * @return 0 on success, negative error code on failure + */ + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); + + /** Program blocks to a block device + * + * The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return 0 on success, negative error code on failure + */ + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); + + /** Erase blocks on a block device + * + * The state of an erased block is undefined until it has been programmed + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return 0 on success, negative error code on failure + */ + virtual int erase(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a programable block in bytes + * @note Must be a multiple of the read size + */ + virtual bd_size_t get_program_size() const; + + /** Get the size of a eraseable block + * + * @return Size of a eraseable block in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + +private: + // Master side hardware + SPI _spi; + DigitalOut _cs; + DigitalOut _nwp; + + // Device configuration + uint32_t _device_size; + uint16_t _page_size; + uint16_t _block_size; + + // Internal functions + uint16_t _get_register(uint8_t opcode); + void _write_command(uint32_t command, const uint8_t *buffer, uint32_t size); + void _write_enable(bool enable); + int _sync(void); + int _write_page(const uint8_t *buffer, uint32_t addr, uint32_t size); +}; + + +#endif /* MBED_AT45DB_BLOCK_DEVICE_H */ diff --git a/TESTS/block_device/at45db/main.cpp b/TESTS/block_device/at45db/main.cpp new file mode 100644 index 0000000000..605ae0b73f --- /dev/null +++ b/TESTS/block_device/at45db/main.cpp @@ -0,0 +1,135 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "AT45DBBlockDevice.h" +#include + +using namespace utest::v1; + +#define TEST_PINS PTB22, PTB23, PTB21, PTB20 +#define TEST_FREQ 40000000 + +#define TEST_BLOCK_COUNT 10 +#define TEST_ERROR_MASK 16 + +const struct { + const char *name; + bd_size_t (BlockDevice::*method)() const; +} ATTRS[] = { + {"read size", &BlockDevice::get_read_size}, + {"program size", &BlockDevice::get_program_size}, + {"erase size", &BlockDevice::get_erase_size}, + {"total size", &BlockDevice::size}, +}; + + +void test_read_write() { + AT45DBBlockDevice bd(TEST_PINS, TEST_FREQ); + + int err = bd.init(); + TEST_ASSERT_EQUAL(0, err); + + for (unsigned a = 0; a < sizeof(ATTRS)/sizeof(ATTRS[0]); a++) { + static const char *prefixes[] = {"", "k", "M", "G"}; + for (int i = 3; i >= 0; i--) { + bd_size_t size = (bd.*ATTRS[a].method)(); + if (size >= (1ULL << 10*i)) { + printf("%s: %llu%sbytes (%llubytes)\n", + ATTRS[a].name, size >> 10*i, prefixes[i], size); + break; + } + } + } + + bd_size_t block_size = bd.get_erase_size(); + uint8_t *write_block = new uint8_t[block_size]; + uint8_t *read_block = new uint8_t[block_size]; + uint8_t *error_mask = new uint8_t[TEST_ERROR_MASK]; + unsigned addrwidth = ceil(log(float(bd.size()-1)) / log(float(16)))+1; + + for (int b = 0; b < TEST_BLOCK_COUNT; b++) { + // Find a random block + bd_addr_t block = (rand()*block_size) % bd.size(); + + // Use next random number as temporary seed to keep + // the address progressing in the pseudorandom sequence + unsigned seed = rand(); + + // Fill with random sequence + srand(seed); + for (bd_size_t i = 0; i < block_size; i++) { + write_block[i] = 0xff & rand(); + } + + // Write, sync, and read the block + printf("test %0*llx:%llu...\n", addrwidth, block, block_size); + + err = bd.erase(block, block_size); + TEST_ASSERT_EQUAL(0, err); + + err = bd.program(write_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + printf("write %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", write_block[i]); + } + printf("...\n"); + + err = bd.read(read_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + printf("read %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", read_block[i]); + } + printf("...\n"); + + // Find error mask for debugging + memset(error_mask, 0, TEST_ERROR_MASK); + bd_size_t error_scale = block_size / (TEST_ERROR_MASK*8); + + srand(seed); + for (bd_size_t i = 0; i < TEST_ERROR_MASK*8; i++) { + for (bd_size_t j = 0; j < error_scale; j++) { + if ((0xff & rand()) != read_block[i*error_scale + j]) { + error_mask[i/8] |= 1 << (i%8); + } + } + } + + printf("error %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", error_mask[i]); + } + printf("\n"); + + // Check that the data was unmodified + srand(seed); + for (bd_size_t i = 0; i < block_size; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]); + } + } + + err = bd.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(30, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Testing read write random blocks", test_read_write), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} From d60b22b1316968df611f92fcf1603f14f5bfd565 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Sat, 15 Jul 2017 01:25:13 +0100 Subject: [PATCH 02/11] Enabled non-binary address mode on the AT45DB family --- AT45DBBlockDevice.cpp | 266 +++++++++++++++++++++++++++--------------- AT45DBBlockDevice.h | 3 +- mbed_lib.json | 13 +++ 3 files changed, 189 insertions(+), 93 deletions(-) create mode 100644 mbed_lib.json diff --git a/AT45DBBlockDevice.cpp b/AT45DBBlockDevice.cpp index 8a53eb9081..4680ebdced 100644 --- a/AT45DBBlockDevice.cpp +++ b/AT45DBBlockDevice.cpp @@ -25,9 +25,15 @@ #define AT45DB_ID_MATCH 0x1F20 #define AT45DB_ID_DENSITY_MASK 0x001F #define AT45DB_PAGE_SIZE_256 0x0100 +#define AT45DB_PAGE_SIZE_264 0x0108 #define AT45DB_PAGE_SIZE_512 0x0200 +#define AT45DB_PAGE_SIZE_528 0x0210 #define AT45DB_BLOCK_SIZE_2K 0x0800 +#define AT45DB_BLOCK_SIZE_2K1 0x0840 #define AT45DB_BLOCK_SIZE_4K 0x1000 +#define AT45DB_BLOCK_SIZE_4K1 0x1080 +#define AT45DB_PAGE_BIT_264 9 +#define AT45DB_PAGE_BIT_528 10 /* enable debug */ #define AT45DB_DEBUG 0 @@ -42,20 +48,22 @@ void _print_status(uint16_t status); /* non-exhaustive opcode list */ enum opcode { - AT45DB_OP_NOP = 0x00, - AT45DB_OP_STATUS = 0xD7, - AT45DB_OP_ID = 0x9F, - AT45DB_OP_READ_LOW_POWER = 0x01, - AT45DB_OP_READ_LOW_FREQUENCY = 0x03, - AT45DB_OP_PROGRAM_DIRECT = 0x02, // Program through Buffer 1 without Built-In Erase - AT45DB_OP_ERASE_BLOCK = 0x50, + AT45DB_OP_NOP = 0x00, + AT45DB_OP_STATUS = 0xD7, + AT45DB_OP_ID = 0x9F, + AT45DB_OP_READ_LOW_POWER = 0x01, + AT45DB_OP_READ_LOW_FREQUENCY = 0x03, + AT45DB_OP_PROGRAM_DIRECT = 0x02, // Program through Buffer 1 without Built-In Erase + AT45DB_OP_PROGRAM_DIRECT_WITH_ERASE = 0x82, + AT45DB_OP_ERASE_BLOCK = 0x50, }; /* non-exhaustive command list */ enum command { - AT45DB_COMMAND_WRITE_DISABLE = 0x3D2A7F9A, - AT45DB_COMMAND_WRITE_ENABLE = 0x3D2A7FA9, - AT45DB_COMMAND_SET_PAGE_SIZE = 0x3D2A80A6, + AT45DB_COMMAND_WRITE_DISABLE = 0x3D2A7F9A, + AT45DB_COMMAND_WRITE_ENABLE = 0x3D2A7FA9, + AT45DB_COMMAND_BINARY_PAGE_SIZE = 0x3D2A80A6, + AT45DB_COMMAND_DATAFLASH_PAGE_SIZE = 0x3D2A80A7, }; /* bit masks for interpreting the status register */ @@ -158,24 +166,83 @@ int AT45DBBlockDevice::init() DEBUG_PRINTF("id: %04X\r\n", id & AT45DB_ID_MATCH); + /* get status register to verify the page size mode */ + uint16_t status = _get_register(AT45DB_OP_STATUS); + /* manufacture ID match */ if ((id & AT45DB_ID_MATCH) == AT45DB_ID_MATCH) { + /* calculate density */ _device_size = 0x8000 << (id & AT45DB_ID_DENSITY_MASK); + bool binary_page_size = true; + + /* check if device is configured for binary page sizes */ + if ((status & AT45DB_BIT_PAGE_SIZE) == AT45DB_BIT_PAGE_SIZE) { + DEBUG_PRINTF("Page size is binary\r\n"); + +#if MBED_CONF_AT45DB_DATAFLASH_SIZE + /* send reconfiguration command */ + _write_command(AT45DB_COMMAND_DATAFLASH_PAGE_SIZE, NULL, 0); + + /* wait for device to be ready and update return code */ + result = _sync(); + + /* set binary flag */ + binary_page_size = false; +#else + /* set binary flag */ + binary_page_size = true; +#endif + } else { + DEBUG_PRINTF("Page size is not binary\r\n"); + +#if MBED_CONF_AT45DB_BINARY_SIZE + /* send reconfiguration command */ + _write_command(AT45DB_COMMAND_BINARY_PAGE_SIZE, NULL, 0); + + /* wait for device to be ready and update return code */ + result = _sync(); + + /* set binary flag */ + binary_page_size = true; +#else + /* set binary flag */ + binary_page_size = false; +#endif + } + /* set page program size and block erase size */ switch (id & AT45DB_ID_DENSITY_MASK) { case AT45DB_ID_DENSITY_2_MBIT: case AT45DB_ID_DENSITY_4_MBIT: case AT45DB_ID_DENSITY_8_MBIT: case AT45DB_ID_DENSITY_64_MBIT: - _page_size = AT45DB_PAGE_SIZE_256; - _block_size = AT45DB_BLOCK_SIZE_2K; + if (binary_page_size) { + _page_size = AT45DB_PAGE_SIZE_256; + _block_size = AT45DB_BLOCK_SIZE_2K; + } else { + _page_size = AT45DB_PAGE_SIZE_264; + _block_size = AT45DB_BLOCK_SIZE_2K1; + + /* adjust device size */ + _device_size = (_device_size / AT45DB_PAGE_SIZE_256) * + AT45DB_PAGE_SIZE_264; + } break; case AT45DB_ID_DENSITY_16_MBIT: case AT45DB_ID_DENSITY_32_MBIT: - _page_size = AT45DB_PAGE_SIZE_512; - _block_size = AT45DB_BLOCK_SIZE_4K; + if (binary_page_size) { + _page_size = AT45DB_PAGE_SIZE_512; + _block_size = AT45DB_BLOCK_SIZE_4K; + } else { + _page_size = AT45DB_PAGE_SIZE_528; + _block_size = AT45DB_BLOCK_SIZE_4K1; + + /* adjust device size */ + _device_size = (_device_size / AT45DB_PAGE_SIZE_512) * + AT45DB_PAGE_SIZE_528; + } break; default: break; @@ -183,32 +250,13 @@ int AT45DBBlockDevice::init() DEBUG_PRINTF("density: %" PRIu16 "\r\n", id & AT45DB_ID_DENSITY_MASK); DEBUG_PRINTF("size: %" PRIu32 "\r\n", _device_size); + DEBUG_PRINTF("page: %" PRIu16 "\r\n", _page_size); + DEBUG_PRINTF("block: %" PRIu16 "\r\n", _block_size); /* device successfully detected, set OK error code */ result = BD_ERROR_OK; } - /* get status register to verify the page size mode */ - uint16_t status = _get_register(AT45DB_OP_STATUS); - - /* If the page size is set to a size that is not a power of two - then reconfigure it to use binary sizes instead. - - In the future, the extra space can be used for error correction codes. - */ - if ((status & AT45DB_BIT_PAGE_SIZE) != AT45DB_BIT_PAGE_SIZE) { - DEBUG_PRINTF("Page size is 528 bytes\r\n"); - - /* send reconfiguration command */ - _write_command(AT45DB_COMMAND_SET_PAGE_SIZE, NULL, 0); - - /* wait for device to be ready and update return code */ - result = _sync(); - - } else { - DEBUG_PRINTF("Page size is 512 bytes\r\n"); - } - /* write protect device when idle */ _write_enable(false); @@ -224,7 +272,7 @@ int AT45DBBlockDevice::deinit() int AT45DBBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) { - DEBUG_PRINTF("program: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); + DEBUG_PRINTF("read: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); int result = BD_ERROR_DEVICE_ERROR; @@ -239,10 +287,15 @@ int AT45DBBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) /* send read opcode */ _spi.write(AT45DB_OP_READ_LOW_FREQUENCY); + /* translate address */ + uint32_t address = _translate_address(addr); + + DEBUG_PRINTF("address: %" PRIX32 "\r\n", address); + /* send read address */ - _spi.write((addr >> 16) & 0xFF); - _spi.write((addr >> 8) & 0xFF); - _spi.write(addr & 0xFF); + _spi.write((address >> 16) & 0xFF); + _spi.write((address >> 8) & 0xFF); + _spi.write(address & 0xFF); /* clock out one byte at a time and store in external buffer */ for (uint32_t index = 0; index < size; index++) { @@ -272,8 +325,8 @@ int AT45DBBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t siz /* Each write command can only cover one page at a time. Find page and current page offset for handling unaligned writes. */ - uint32_t write_page = addr & ~(_page_size - 1); - uint32_t page_offset = addr & (_page_size - 1); + uint32_t page_number = addr / _page_size; + uint32_t page_offset = addr % _page_size; /* disable write protection */ _write_enable(true); @@ -285,23 +338,24 @@ int AT45DBBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t siz /* find remaining bytes to be written */ uint32_t bytes_remaining = size - bytes_written; - /* cap the value at the page size */ - if (bytes_remaining > _page_size) { - bytes_remaining = _page_size; + /* cap the value at the page size and offset */ + if (bytes_remaining > (_page_size - page_offset)) { + bytes_remaining = _page_size - page_offset; } /* Write one page, bytes_written keeps track of the progress, - write_page is the page address, and page_offset is non-zero for + page_number is the page address, and page_offset is non-zero for unaligned writes. */ result = _write_page(&external_buffer[bytes_written], - write_page + page_offset, - bytes_remaining - page_offset); + page_number, + page_offset, + bytes_remaining); /* update loop variables upon success otherwise break loop */ if (result == BD_ERROR_OK) { - bytes_written += (_page_size - page_offset); - write_page += _page_size; + bytes_written += bytes_remaining; + page_number++; /* After the first successful write, all subsequent writes will be aligned. @@ -338,10 +392,13 @@ int AT45DBBlockDevice::erase(bd_addr_t addr, bd_size_t size) /* set block erase opcode */ uint32_t command = AT45DB_OP_ERASE_BLOCK; + /* translate address */ + uint32_t address = _translate_address(addr); + /* set block address */ - command = (command << 8) | ((addr >> 16) & 0xFF); - command = (command << 8) | ((addr >> 8) & 0xFF); - command = (command << 8) | (addr & 0xFF); + command = (command << 8) | ((address >> 16) & 0xFF); + command = (command << 8) | ((address >> 8) & 0xFF); + command = (command << 8) | (address & 0xFF); /* send command to device */ _write_command(command, NULL, 0); @@ -368,28 +425,28 @@ int AT45DBBlockDevice::erase(bd_addr_t addr, bd_size_t size) bd_size_t AT45DBBlockDevice::get_read_size() const { - DEBUG_PRINTF("size: %d\r\n", AT45DB_READ_SIZE); + DEBUG_PRINTF("read size: %d\r\n", AT45DB_READ_SIZE); return AT45DB_READ_SIZE; } bd_size_t AT45DBBlockDevice::get_program_size() const { - DEBUG_PRINTF("size: %d\r\n", AT45DB_PROG_SIZE); + DEBUG_PRINTF("program size: %d\r\n", AT45DB_PROG_SIZE); return AT45DB_PROG_SIZE; } bd_size_t AT45DBBlockDevice::get_erase_size() const { - DEBUG_PRINTF("size: %" PRIX16 "\r\n", _block_size); + DEBUG_PRINTF("erase size: %" PRIX16 "\r\n", _block_size); return _block_size; } bd_size_t AT45DBBlockDevice::size() const { - DEBUG_PRINTF("size: %" PRIX32 "\r\n", _device_size); + DEBUG_PRINTF("device size: %" PRIX32 "\r\n", _device_size); return _device_size; } @@ -539,19 +596,35 @@ int AT45DBBlockDevice::_sync(void) * @param size Bytes to write. Can at most be the full page. * @return BlockDevice error code. */ -int AT45DBBlockDevice::_write_page(const uint8_t *buffer, uint32_t addr, uint32_t size) +int AT45DBBlockDevice::_write_page(const uint8_t *buffer, + uint32_t page, + uint32_t offset, + uint32_t size) { - DEBUG_PRINTF("_write_page: %p %" PRIX32 " %" PRIX32 "\r\n", buffer, addr, size); + DEBUG_PRINTF("_write_page: %p %" PRIX32 " %" PRIX32 "\r\n", buffer, page, size); + + uint32_t command = AT45DB_OP_NOP; /* opcode for writing directly to device, in a single command, assuming the page has been erased before hand. */ - uint32_t command = AT45DB_OP_PROGRAM_DIRECT; + command = AT45DB_OP_PROGRAM_DIRECT; + + uint32_t address = 0; + + /* convert page number and offset into device address based on address format */ + if (_page_size == AT45DB_PAGE_SIZE_264) { + address = (page << AT45DB_PAGE_BIT_264) | offset; + } else if (_page_size == AT45DB_PAGE_SIZE_528) { + address = (page << AT45DB_PAGE_BIT_528) | offset; + } else { + address = (page * _page_size) | offset; + } /* set write address */ - command = (command << 8) | ((addr >> 16) & 0xFF); - command = (command << 8) | ((addr >> 8) & 0xFF); - command = (command << 8) | (addr & 0xFF); + command = (command << 8) | ((address >> 16) & 0xFF); + command = (command << 8) | ((address >> 8) & 0xFF); + command = (command << 8) | (address & 0xFF); /* send write command with data */ _write_command(command, buffer, size); @@ -562,6 +635,30 @@ int AT45DBBlockDevice::_write_page(const uint8_t *buffer, uint32_t addr, uint32_ return result; } +/** + * @brief Translate address. + * @details If the device is configured for non-binary page sizes, + * the address is translated from binary to non-binary form. + * + * @param addr Binary address. + * @return Address in format expected by device. + */ +uint32_t AT45DBBlockDevice::_translate_address(bd_addr_t addr) +{ + uint32_t address = addr; + + /* translate address if page size is non-binary */ + if (_page_size == AT45DB_PAGE_SIZE_264) { + address = ((addr / AT45DB_PAGE_SIZE_264) << AT45DB_PAGE_BIT_264) | + (addr % AT45DB_PAGE_SIZE_264); + } else if (_page_size == AT45DB_PAGE_SIZE_528) { + address = ((addr / AT45DB_PAGE_SIZE_528) << AT45DB_PAGE_BIT_528) | + (addr % AT45DB_PAGE_SIZE_528); + } + + return address; +} + /** * @brief Internal function for printing out each bit set in status register. * @@ -573,92 +670,77 @@ void _print_status(uint16_t status) DEBUG_PRINTF("%04X\r\n", status); /* device is ready (after write/erase) */ - if (status & AT45DB_BIT_READY) - { + if (status & AT45DB_BIT_READY) { DEBUG_PRINTF("AT45DB_BIT_READY\r\n"); } /* Buffer comparison failed */ - if (status & AT45DB_BIT_COMPARE) - { + if (status & AT45DB_BIT_COMPARE) { DEBUG_PRINTF("AT45DB_BIT_COMPARE\r\n"); } /* device size is 2 MB */ - if (status & AT45DB_STATUS_DENSITY_2_MBIT) - { + if (status & AT45DB_STATUS_DENSITY_2_MBIT) { DEBUG_PRINTF("AT45DB_STATUS_DENSITY_2_MBIT\r\n"); } /* device size is 4 MB */ - if (status & AT45DB_STATUS_DENSITY_4_MBIT) - { + if (status & AT45DB_STATUS_DENSITY_4_MBIT) { DEBUG_PRINTF("AT45DB_STATUS_DENSITY_4_MBIT\r\n"); } /* device size is 8 MB */ - if (status & AT45DB_STATUS_DENSITY_8_MBIT) - { + if (status & AT45DB_STATUS_DENSITY_8_MBIT) { DEBUG_PRINTF("AT45DB_STATUS_DENSITY_8_MBIT\r\n"); } /* device size is 16 MB */ - if (status & AT45DB_STATUS_DENSITY_16_MBIT) - { + if (status & AT45DB_STATUS_DENSITY_16_MBIT) { DEBUG_PRINTF("AT45DB_STATUS_DENSITY_16_MBIT\r\n"); } /* device size is 32 MB */ - if (status & AT45DB_STATUS_DENSITY_32_MBIT) - { + if (status & AT45DB_STATUS_DENSITY_32_MBIT) { DEBUG_PRINTF("AT45DB_STATUS_DENSITY_32_MBIT\r\n"); } /* device size is 64 MB */ - if (status & AT45DB_STATUS_DENSITY_64_MBIT) - { + if (status & AT45DB_STATUS_DENSITY_64_MBIT) { DEBUG_PRINTF("AT45DB_STATUS_DENSITY_64_MBIT\r\n"); } /* sector protectino enabled */ - if (status & AT45DB_BIT_PROTECT) - { + if (status & AT45DB_BIT_PROTECT) { DEBUG_PRINTF("AT45DB_BIT_PROTECT\r\n"); } /* page size is a power of 2 */ - if (status & AT45DB_BIT_PAGE_SIZE) - { + if (status & AT45DB_BIT_PAGE_SIZE) { DEBUG_PRINTF("AT45DB_BIT_PAGE_SIZE\r\n"); } /* erase/program error */ - if (status & AT45DB_BIT_ERASE_PROGRAM_ERROR) - { + if (status & AT45DB_BIT_ERASE_PROGRAM_ERROR) { DEBUG_PRINTF("AT45DB_BIT_ERASE_PROGRAM_ERROR\r\n"); } /* sector lockdown still possible */ - if (status & AT45DB_BIT_SECTOR_LOCKDOWN) - { + if (status & AT45DB_BIT_SECTOR_LOCKDOWN) { DEBUG_PRINTF("AT45DB_BIT_SECTOR_LOCKDOWN\r\n"); } /* program operation suspended while using buffer 2 */ - if (status & AT45DB_BIT_PROGRAM_SUSPEND_2) - { + if (status & AT45DB_BIT_PROGRAM_SUSPEND_2) { DEBUG_PRINTF("AT45DB_BIT_PROGRAM_SUSPEND_2\r\n"); } /* program operation suspended while using buffer 1 */ - if (status & AT45DB_BIT_PROGRAM_SUSPEND_1) - { + if (status & AT45DB_BIT_PROGRAM_SUSPEND_1) { DEBUG_PRINTF("AT45DB_BIT_PROGRAM_SUSPEND_1\r\n"); } /* erase has been suspended */ - if (status & AT45DB_BIT_ERASE_SUSPEND) - { + if (status & AT45DB_BIT_ERASE_SUSPEND) { DEBUG_PRINTF("AT45DB_BIT_ERASE_SUSPEND\r\n"); } #endif diff --git a/AT45DBBlockDevice.h b/AT45DBBlockDevice.h index 358da4db08..d7035c3535 100644 --- a/AT45DBBlockDevice.h +++ b/AT45DBBlockDevice.h @@ -153,7 +153,8 @@ private: void _write_command(uint32_t command, const uint8_t *buffer, uint32_t size); void _write_enable(bool enable); int _sync(void); - int _write_page(const uint8_t *buffer, uint32_t addr, uint32_t size); + int _write_page(const uint8_t *buffer, uint32_t addr, uint32_t offset, uint32_t size); + uint32_t _translate_address(bd_addr_t addr); }; diff --git a/mbed_lib.json b/mbed_lib.json new file mode 100644 index 0000000000..70882dba19 --- /dev/null +++ b/mbed_lib.json @@ -0,0 +1,13 @@ +{ + "name": "at45db", + "config": { + "binary-size": { + "help": "Configure device to use binary address space.", + "value": "0" + }, + "dataflash-size": { + "help": "Configure device to use DataFlash address space.", + "value": "0" + } + } +} From f65c4298c0911870652f2fe6b0e9bde4a137c370 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 16 Jul 2017 13:59:58 -0500 Subject: [PATCH 03/11] Renamed AT45DB -> DataFlash Reasoning being that this protocol potentially covers more products than the AT45DB line and the term DataFlash is a more likely search term. If another DataFlash product comes along that uses a different underlying protocol, we can add a new class with the name HDMIDataFlashBlockDevice, similarly to what we will need to do with the SPIFBlockDevice driver. --- ...lockDevice.cpp => DataFlashBlockDevice.cpp | 354 +++++++++--------- AT45DBBlockDevice.h => DataFlashBlockDevice.h | 46 +-- .../{at45db => dataflash}/main.cpp | 4 +- mbed_lib.json | 2 +- 4 files changed, 203 insertions(+), 203 deletions(-) rename AT45DBBlockDevice.cpp => DataFlashBlockDevice.cpp (59%) rename AT45DBBlockDevice.h => DataFlashBlockDevice.h (77%) rename TESTS/block_device/{at45db => dataflash}/main.cpp (97%) diff --git a/AT45DBBlockDevice.cpp b/DataFlashBlockDevice.cpp similarity index 59% rename from AT45DBBlockDevice.cpp rename to DataFlashBlockDevice.cpp index 4680ebdced..c94dfedd21 100644 --- a/AT45DBBlockDevice.cpp +++ b/DataFlashBlockDevice.cpp @@ -14,31 +14,31 @@ * limitations under the License. */ -#include "AT45DBBlockDevice.h" +#include "DataFlashBlockDevice.h" #include /* constants */ -#define AT45DB_READ_SIZE 1 -#define AT45DB_PROG_SIZE 1 -#define AT45DB_TIMEOUT 10000 -#define AT45DB_ID_MATCH 0x1F20 -#define AT45DB_ID_DENSITY_MASK 0x001F -#define AT45DB_PAGE_SIZE_256 0x0100 -#define AT45DB_PAGE_SIZE_264 0x0108 -#define AT45DB_PAGE_SIZE_512 0x0200 -#define AT45DB_PAGE_SIZE_528 0x0210 -#define AT45DB_BLOCK_SIZE_2K 0x0800 -#define AT45DB_BLOCK_SIZE_2K1 0x0840 -#define AT45DB_BLOCK_SIZE_4K 0x1000 -#define AT45DB_BLOCK_SIZE_4K1 0x1080 -#define AT45DB_PAGE_BIT_264 9 -#define AT45DB_PAGE_BIT_528 10 +#define DATAFLASH_READ_SIZE 1 +#define DATAFLASH_PROG_SIZE 1 +#define DATAFLASH_TIMEOUT 10000 +#define DATAFLASH_ID_MATCH 0x1F20 +#define DATAFLASH_ID_DENSITY_MASK 0x001F +#define DATAFLASH_PAGE_SIZE_256 0x0100 +#define DATAFLASH_PAGE_SIZE_264 0x0108 +#define DATAFLASH_PAGE_SIZE_512 0x0200 +#define DATAFLASH_PAGE_SIZE_528 0x0210 +#define DATAFLASH_BLOCK_SIZE_2K 0x0800 +#define DATAFLASH_BLOCK_SIZE_2K1 0x0840 +#define DATAFLASH_BLOCK_SIZE_4K 0x1000 +#define DATAFLASH_BLOCK_SIZE_4K1 0x1080 +#define DATAFLASH_PAGE_BIT_264 9 +#define DATAFLASH_PAGE_BIT_528 10 /* enable debug */ -#define AT45DB_DEBUG 0 +#define DATAFLASH_DEBUG 0 -#if AT45DB_DEBUG +#if DATAFLASH_DEBUG #define DEBUG_PRINTF(...) printf(__VA_ARGS__) #else #define DEBUG_PRINTF(...) @@ -48,86 +48,86 @@ void _print_status(uint16_t status); /* non-exhaustive opcode list */ enum opcode { - AT45DB_OP_NOP = 0x00, - AT45DB_OP_STATUS = 0xD7, - AT45DB_OP_ID = 0x9F, - AT45DB_OP_READ_LOW_POWER = 0x01, - AT45DB_OP_READ_LOW_FREQUENCY = 0x03, - AT45DB_OP_PROGRAM_DIRECT = 0x02, // Program through Buffer 1 without Built-In Erase - AT45DB_OP_PROGRAM_DIRECT_WITH_ERASE = 0x82, - AT45DB_OP_ERASE_BLOCK = 0x50, + DATAFLASH_OP_NOP = 0x00, + DATAFLASH_OP_STATUS = 0xD7, + DATAFLASH_OP_ID = 0x9F, + DATAFLASH_OP_READ_LOW_POWER = 0x01, + DATAFLASH_OP_READ_LOW_FREQUENCY = 0x03, + DATAFLASH_OP_PROGRAM_DIRECT = 0x02, // Program through Buffer 1 without Built-In Erase + DATAFLASH_OP_PROGRAM_DIRECT_WITH_ERASE = 0x82, + DATAFLASH_OP_ERASE_BLOCK = 0x50, }; /* non-exhaustive command list */ enum command { - AT45DB_COMMAND_WRITE_DISABLE = 0x3D2A7F9A, - AT45DB_COMMAND_WRITE_ENABLE = 0x3D2A7FA9, - AT45DB_COMMAND_BINARY_PAGE_SIZE = 0x3D2A80A6, - AT45DB_COMMAND_DATAFLASH_PAGE_SIZE = 0x3D2A80A7, + DATAFLASH_COMMAND_WRITE_DISABLE = 0x3D2A7F9A, + DATAFLASH_COMMAND_WRITE_ENABLE = 0x3D2A7FA9, + DATAFLASH_COMMAND_BINARY_PAGE_SIZE = 0x3D2A80A6, + DATAFLASH_COMMAND_DATAFLASH_PAGE_SIZE = 0x3D2A80A7, }; /* bit masks for interpreting the status register */ enum status_bit { - AT45DB_BIT_READY = (0x01 << 15), - AT45DB_BIT_COMPARE = (0x01 << 14), - AT45DB_BIT_DENSITY = (0x0F << 10), - AT45DB_BIT_PROTECT = (0x01 << 9), - AT45DB_BIT_PAGE_SIZE = (0x01 << 8), + DATAFLASH_BIT_READY = (0x01 << 15), + DATAFLASH_BIT_COMPARE = (0x01 << 14), + DATAFLASH_BIT_DENSITY = (0x0F << 10), + DATAFLASH_BIT_PROTECT = (0x01 << 9), + DATAFLASH_BIT_PAGE_SIZE = (0x01 << 8), - AT45DB_BIT_ERASE_PROGRAM_ERROR = (0x01 << 5), - AT45DB_BIT_SECTOR_LOCKDOWN = (0x01 << 3), - AT45DB_BIT_PROGRAM_SUSPEND_2 = (0x01 << 2), - AT45DB_BIT_PROGRAM_SUSPEND_1 = (0x01 << 1), - AT45DB_BIT_ERASE_SUSPEND = (0x01 << 0), + DATAFLASH_BIT_ERASE_PROGRAM_ERROR = (0x01 << 5), + DATAFLASH_BIT_SECTOR_LOCKDOWN = (0x01 << 3), + DATAFLASH_BIT_PROGRAM_SUSPEND_2 = (0x01 << 2), + DATAFLASH_BIT_PROGRAM_SUSPEND_1 = (0x01 << 1), + DATAFLASH_BIT_ERASE_SUSPEND = (0x01 << 0), }; /* bit masks for detecting density from status register */ enum status_density { - AT45DB_STATUS_DENSITY_2_MBIT = (0x05 << 10), - AT45DB_STATUS_DENSITY_4_MBIT = (0x07 << 10), - AT45DB_STATUS_DENSITY_8_MBIT = (0x09 << 10), - AT45DB_STATUS_DENSITY_16_MBIT = (0x0B << 10), - AT45DB_STATUS_DENSITY_32_MBIT = (0x0D << 10), - AT45DB_STATUS_DENSITY_64_MBIT = (0x0F << 10), + DATAFLASH_STATUS_DENSITY_2_MBIT = (0x05 << 10), + DATAFLASH_STATUS_DENSITY_4_MBIT = (0x07 << 10), + DATAFLASH_STATUS_DENSITY_8_MBIT = (0x09 << 10), + DATAFLASH_STATUS_DENSITY_16_MBIT = (0x0B << 10), + DATAFLASH_STATUS_DENSITY_32_MBIT = (0x0D << 10), + DATAFLASH_STATUS_DENSITY_64_MBIT = (0x0F << 10), }; /* code for calculating density */ enum id_density { - AT45DB_ID_DENSITY_2_MBIT = 0x03, - AT45DB_ID_DENSITY_4_MBIT = 0x04, - AT45DB_ID_DENSITY_8_MBIT = 0x05, - AT45DB_ID_DENSITY_16_MBIT = 0x06, - AT45DB_ID_DENSITY_32_MBIT = 0x07, - AT45DB_ID_DENSITY_64_MBIT = 0x08, + DATAFLASH_ID_DENSITY_2_MBIT = 0x03, + DATAFLASH_ID_DENSITY_4_MBIT = 0x04, + DATAFLASH_ID_DENSITY_8_MBIT = 0x05, + DATAFLASH_ID_DENSITY_16_MBIT = 0x06, + DATAFLASH_ID_DENSITY_32_MBIT = 0x07, + DATAFLASH_ID_DENSITY_64_MBIT = 0x08, }; /* typical duration in milliseconds for each operation */ enum timing { - AT45DB_TIMING_ERASE_PROGRAM_PAGE = 17, - AT45DB_TIMING_PROGRAM_PAGE = 3, - AT45DB_TIMING_ERASE_PAGE = 12, - AT45DB_TIMING_ERASE_BLOCK = 45, - AT45DB_TIMING_ERASE_SECTOR = 700, - AT45DB_TIMING_ERASE_CHIP = 45000 + DATAFLASH_TIMING_ERASE_PROGRAM_PAGE = 17, + DATAFLASH_TIMING_PROGRAM_PAGE = 3, + DATAFLASH_TIMING_ERASE_PAGE = 12, + DATAFLASH_TIMING_ERASE_BLOCK = 45, + DATAFLASH_TIMING_ERASE_SECTOR = 700, + DATAFLASH_TIMING_ERASE_CHIP = 45000 }; /* frequency domains */ enum frequency { - AT45DB_LOW_POWER_FREQUENCY = 15000000, - AT45DB_LOW_FREQUENCY = 50000000, - AT45DB_HIGH_FREQUENCY = 85000000, - AT45DB_HIGHEST_FREQUENCY = 104000000 + DATAFLASH_LOW_POWER_FREQUENCY = 15000000, + DATAFLASH_LOW_FREQUENCY = 50000000, + DATAFLASH_HIGH_FREQUENCY = 85000000, + DATAFLASH_HIGHEST_FREQUENCY = 104000000 }; /* number of dummy bytes required in each frequency domain */ enum dummy { - AT45DB_LOW_POWER_BYTES = 0, - AT45DB_LOW_FREQUENCY_BYTES = 0, - AT45DB_HIGH_FREQUENCY_BYTES = 1, - AT45DB_HIGHEST_FREQUENCY_BYTES = 2 + DATAFLASH_LOW_POWER_BYTES = 0, + DATAFLASH_LOW_FREQUENCY_BYTES = 0, + DATAFLASH_HIGH_FREQUENCY_BYTES = 1, + DATAFLASH_HIGHEST_FREQUENCY_BYTES = 2 }; -AT45DBBlockDevice::AT45DBBlockDevice(PinName mosi, +DataFlashBlockDevice::DataFlashBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, @@ -139,10 +139,10 @@ AT45DBBlockDevice::AT45DBBlockDevice(PinName mosi, _device_size(0) { /* check that frequency is within range */ - if (freq > AT45DB_LOW_FREQUENCY) { + if (freq > DATAFLASH_LOW_FREQUENCY) { /* cap frequency at the highest supported one */ - _spi.frequency(AT45DB_LOW_FREQUENCY); + _spi.frequency(DATAFLASH_LOW_FREQUENCY); } else { /* freqency is valid, use as-is */ @@ -155,35 +155,35 @@ AT45DBBlockDevice::AT45DBBlockDevice(PinName mosi, } } -int AT45DBBlockDevice::init() +int DataFlashBlockDevice::init() { DEBUG_PRINTF("init\r\n"); int result = BD_ERROR_DEVICE_ERROR; /* read ID register to validate model and set dimensions */ - uint16_t id = _get_register(AT45DB_OP_ID); + uint16_t id = _get_register(DATAFLASH_OP_ID); - DEBUG_PRINTF("id: %04X\r\n", id & AT45DB_ID_MATCH); + DEBUG_PRINTF("id: %04X\r\n", id & DATAFLASH_ID_MATCH); /* get status register to verify the page size mode */ - uint16_t status = _get_register(AT45DB_OP_STATUS); + uint16_t status = _get_register(DATAFLASH_OP_STATUS); /* manufacture ID match */ - if ((id & AT45DB_ID_MATCH) == AT45DB_ID_MATCH) { + if ((id & DATAFLASH_ID_MATCH) == DATAFLASH_ID_MATCH) { /* calculate density */ - _device_size = 0x8000 << (id & AT45DB_ID_DENSITY_MASK); + _device_size = 0x8000 << (id & DATAFLASH_ID_DENSITY_MASK); bool binary_page_size = true; /* check if device is configured for binary page sizes */ - if ((status & AT45DB_BIT_PAGE_SIZE) == AT45DB_BIT_PAGE_SIZE) { + if ((status & DATAFLASH_BIT_PAGE_SIZE) == DATAFLASH_BIT_PAGE_SIZE) { DEBUG_PRINTF("Page size is binary\r\n"); -#if MBED_CONF_AT45DB_DATAFLASH_SIZE +#if MBED_CONF_DATAFLASH_DATAFLASH_SIZE /* send reconfiguration command */ - _write_command(AT45DB_COMMAND_DATAFLASH_PAGE_SIZE, NULL, 0); + _write_command(DATAFLASH_COMMAND_DATAFLASH_PAGE_SIZE, NULL, 0); /* wait for device to be ready and update return code */ result = _sync(); @@ -197,9 +197,9 @@ int AT45DBBlockDevice::init() } else { DEBUG_PRINTF("Page size is not binary\r\n"); -#if MBED_CONF_AT45DB_BINARY_SIZE +#if MBED_CONF_DATAFLASH_BINARY_SIZE /* send reconfiguration command */ - _write_command(AT45DB_COMMAND_BINARY_PAGE_SIZE, NULL, 0); + _write_command(DATAFLASH_COMMAND_BINARY_PAGE_SIZE, NULL, 0); /* wait for device to be ready and update return code */ result = _sync(); @@ -213,42 +213,42 @@ int AT45DBBlockDevice::init() } /* set page program size and block erase size */ - switch (id & AT45DB_ID_DENSITY_MASK) { - case AT45DB_ID_DENSITY_2_MBIT: - case AT45DB_ID_DENSITY_4_MBIT: - case AT45DB_ID_DENSITY_8_MBIT: - case AT45DB_ID_DENSITY_64_MBIT: + switch (id & DATAFLASH_ID_DENSITY_MASK) { + case DATAFLASH_ID_DENSITY_2_MBIT: + case DATAFLASH_ID_DENSITY_4_MBIT: + case DATAFLASH_ID_DENSITY_8_MBIT: + case DATAFLASH_ID_DENSITY_64_MBIT: if (binary_page_size) { - _page_size = AT45DB_PAGE_SIZE_256; - _block_size = AT45DB_BLOCK_SIZE_2K; + _page_size = DATAFLASH_PAGE_SIZE_256; + _block_size = DATAFLASH_BLOCK_SIZE_2K; } else { - _page_size = AT45DB_PAGE_SIZE_264; - _block_size = AT45DB_BLOCK_SIZE_2K1; + _page_size = DATAFLASH_PAGE_SIZE_264; + _block_size = DATAFLASH_BLOCK_SIZE_2K1; /* adjust device size */ - _device_size = (_device_size / AT45DB_PAGE_SIZE_256) * - AT45DB_PAGE_SIZE_264; + _device_size = (_device_size / DATAFLASH_PAGE_SIZE_256) * + DATAFLASH_PAGE_SIZE_264; } break; - case AT45DB_ID_DENSITY_16_MBIT: - case AT45DB_ID_DENSITY_32_MBIT: + case DATAFLASH_ID_DENSITY_16_MBIT: + case DATAFLASH_ID_DENSITY_32_MBIT: if (binary_page_size) { - _page_size = AT45DB_PAGE_SIZE_512; - _block_size = AT45DB_BLOCK_SIZE_4K; + _page_size = DATAFLASH_PAGE_SIZE_512; + _block_size = DATAFLASH_BLOCK_SIZE_4K; } else { - _page_size = AT45DB_PAGE_SIZE_528; - _block_size = AT45DB_BLOCK_SIZE_4K1; + _page_size = DATAFLASH_PAGE_SIZE_528; + _block_size = DATAFLASH_BLOCK_SIZE_4K1; /* adjust device size */ - _device_size = (_device_size / AT45DB_PAGE_SIZE_512) * - AT45DB_PAGE_SIZE_528; + _device_size = (_device_size / DATAFLASH_PAGE_SIZE_512) * + DATAFLASH_PAGE_SIZE_528; } break; default: break; } - DEBUG_PRINTF("density: %" PRIu16 "\r\n", id & AT45DB_ID_DENSITY_MASK); + DEBUG_PRINTF("density: %" PRIu16 "\r\n", id & DATAFLASH_ID_DENSITY_MASK); DEBUG_PRINTF("size: %" PRIu32 "\r\n", _device_size); DEBUG_PRINTF("page: %" PRIu16 "\r\n", _page_size); DEBUG_PRINTF("block: %" PRIu16 "\r\n", _block_size); @@ -263,14 +263,14 @@ int AT45DBBlockDevice::init() return result; } -int AT45DBBlockDevice::deinit() +int DataFlashBlockDevice::deinit() { DEBUG_PRINTF("deinit\r\n"); return BD_ERROR_OK; } -int AT45DBBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +int DataFlashBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) { DEBUG_PRINTF("read: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); @@ -285,7 +285,7 @@ int AT45DBBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) _cs = 0; /* send read opcode */ - _spi.write(AT45DB_OP_READ_LOW_FREQUENCY); + _spi.write(DATAFLASH_OP_READ_LOW_FREQUENCY); /* translate address */ uint32_t address = _translate_address(addr); @@ -299,7 +299,7 @@ int AT45DBBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) /* clock out one byte at a time and store in external buffer */ for (uint32_t index = 0; index < size; index++) { - external_buffer[index] = _spi.write(AT45DB_OP_NOP); + external_buffer[index] = _spi.write(DATAFLASH_OP_NOP); } /* deactivate device */ @@ -311,7 +311,7 @@ int AT45DBBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) return result; } -int AT45DBBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +int DataFlashBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) { DEBUG_PRINTF("program: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); @@ -373,7 +373,7 @@ int AT45DBBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t siz return result; } -int AT45DBBlockDevice::erase(bd_addr_t addr, bd_size_t size) +int DataFlashBlockDevice::erase(bd_addr_t addr, bd_size_t size) { DEBUG_PRINTF("erase: %" PRIX64 " %" PRIX64 "\r\n", addr, size); @@ -390,7 +390,7 @@ int AT45DBBlockDevice::erase(bd_addr_t addr, bd_size_t size) while (erased < size) { /* set block erase opcode */ - uint32_t command = AT45DB_OP_ERASE_BLOCK; + uint32_t command = DATAFLASH_OP_ERASE_BLOCK; /* translate address */ uint32_t address = _translate_address(addr); @@ -423,28 +423,28 @@ int AT45DBBlockDevice::erase(bd_addr_t addr, bd_size_t size) return result; } -bd_size_t AT45DBBlockDevice::get_read_size() const +bd_size_t DataFlashBlockDevice::get_read_size() const { - DEBUG_PRINTF("read size: %d\r\n", AT45DB_READ_SIZE); + DEBUG_PRINTF("read size: %d\r\n", DATAFLASH_READ_SIZE); - return AT45DB_READ_SIZE; + return DATAFLASH_READ_SIZE; } -bd_size_t AT45DBBlockDevice::get_program_size() const +bd_size_t DataFlashBlockDevice::get_program_size() const { - DEBUG_PRINTF("program size: %d\r\n", AT45DB_PROG_SIZE); + DEBUG_PRINTF("program size: %d\r\n", DATAFLASH_PROG_SIZE); - return AT45DB_PROG_SIZE; + return DATAFLASH_PROG_SIZE; } -bd_size_t AT45DBBlockDevice::get_erase_size() const +bd_size_t DataFlashBlockDevice::get_erase_size() const { DEBUG_PRINTF("erase size: %" PRIX16 "\r\n", _block_size); return _block_size; } -bd_size_t AT45DBBlockDevice::size() const +bd_size_t DataFlashBlockDevice::size() const { DEBUG_PRINTF("device size: %" PRIX32 "\r\n", _device_size); @@ -458,7 +458,7 @@ bd_size_t AT45DBBlockDevice::size() const * @param opcode Register to be read. * @return value. */ -uint16_t AT45DBBlockDevice::_get_register(uint8_t opcode) +uint16_t DataFlashBlockDevice::_get_register(uint8_t opcode) { DEBUG_PRINTF("_get_register: %" PRIX8 "\r\n", opcode); @@ -469,8 +469,8 @@ uint16_t AT45DBBlockDevice::_get_register(uint8_t opcode) _spi.write(opcode); /* read and store result */ - int status = (_spi.write(AT45DB_OP_NOP)); - status = (status << 8) | (_spi.write(AT45DB_OP_NOP)); + int status = (_spi.write(DATAFLASH_OP_NOP)); + status = (status << 8) | (_spi.write(DATAFLASH_OP_NOP)); /* deactivate device */ _cs = 1; @@ -490,7 +490,7 @@ uint16_t AT45DBBlockDevice::_get_register(uint8_t opcode) * @param buffer Data to be sent after command. * @param size Size of buffer. */ -void AT45DBBlockDevice::_write_command(uint32_t command, const uint8_t *buffer, uint32_t size) +void DataFlashBlockDevice::_write_command(uint32_t command, const uint8_t *buffer, uint32_t size) { DEBUG_PRINTF("_write_command: %" PRIX32 " %p %" PRIX32 "\r\n", command, buffer, size); @@ -519,7 +519,7 @@ void AT45DBBlockDevice::_write_command(uint32_t command, const uint8_t *buffer, * * @param enable Boolean for enabling or disabling write protection. */ -void AT45DBBlockDevice::_write_enable(bool enable) +void DataFlashBlockDevice::_write_enable(bool enable) { DEBUG_PRINTF("_write_enable: %d\r\n", enable); @@ -527,7 +527,7 @@ void AT45DBBlockDevice::_write_enable(bool enable) if (enable) { /* send 4 byte command enabling writes */ - _write_command(AT45DB_COMMAND_WRITE_ENABLE, NULL, 0); + _write_command(DATAFLASH_COMMAND_WRITE_ENABLE, NULL, 0); /* if not-write-protected pin is connected, deselect it */ if (_nwp.is_connected()) { @@ -542,7 +542,7 @@ void AT45DBBlockDevice::_write_enable(bool enable) } /* send 4 byte command disabling writes */ - _write_command(AT45DB_COMMAND_WRITE_DISABLE, NULL, 0); + _write_command(DATAFLASH_COMMAND_WRITE_DISABLE, NULL, 0); } } @@ -551,7 +551,7 @@ void AT45DBBlockDevice::_write_enable(bool enable) * * @return BlockDevice compatible error code. */ -int AT45DBBlockDevice::_sync(void) +int DataFlashBlockDevice::_sync(void) { DEBUG_PRINTF("_sync\r\n"); @@ -562,25 +562,25 @@ int AT45DBBlockDevice::_sync(void) The polling interval is based on the typical page program time. */ for (uint32_t timeout = 0; - timeout < AT45DB_TIMEOUT; - timeout += AT45DB_TIMING_ERASE_PROGRAM_PAGE) { + timeout < DATAFLASH_TIMEOUT; + timeout += DATAFLASH_TIMING_ERASE_PROGRAM_PAGE) { /* get status register */ - uint16_t status = _get_register(AT45DB_OP_STATUS); + uint16_t status = _get_register(DATAFLASH_OP_STATUS); /* erase/program bit set, exit with error code set */ - if (status & AT45DB_BIT_ERASE_PROGRAM_ERROR) { - DEBUG_PRINTF("AT45DB_BIT_ERASE_PROGRAM_ERROR\r\n"); + if (status & DATAFLASH_BIT_ERASE_PROGRAM_ERROR) { + DEBUG_PRINTF("DATAFLASH_BIT_ERASE_PROGRAM_ERROR\r\n"); break; /* device ready, set OK code set */ - } else if (status & AT45DB_BIT_READY) { - DEBUG_PRINTF("AT45DB_BIT_READY\r\n"); + } else if (status & DATAFLASH_BIT_READY) { + DEBUG_PRINTF("DATAFLASH_BIT_READY\r\n"); result = BD_ERROR_OK; break; /* wait the typical write period before trying again */ } else { - DEBUG_PRINTF("wait_ms: %d\r\n", AT45DB_TIMING_ERASE_PROGRAM_PAGE); - wait_ms(AT45DB_TIMING_ERASE_PROGRAM_PAGE); + DEBUG_PRINTF("wait_ms: %d\r\n", DATAFLASH_TIMING_ERASE_PROGRAM_PAGE); + wait_ms(DATAFLASH_TIMING_ERASE_PROGRAM_PAGE); } } @@ -596,27 +596,27 @@ int AT45DBBlockDevice::_sync(void) * @param size Bytes to write. Can at most be the full page. * @return BlockDevice error code. */ -int AT45DBBlockDevice::_write_page(const uint8_t *buffer, +int DataFlashBlockDevice::_write_page(const uint8_t *buffer, uint32_t page, uint32_t offset, uint32_t size) { DEBUG_PRINTF("_write_page: %p %" PRIX32 " %" PRIX32 "\r\n", buffer, page, size); - uint32_t command = AT45DB_OP_NOP; + uint32_t command = DATAFLASH_OP_NOP; /* opcode for writing directly to device, in a single command, assuming the page has been erased before hand. */ - command = AT45DB_OP_PROGRAM_DIRECT; + command = DATAFLASH_OP_PROGRAM_DIRECT; uint32_t address = 0; /* convert page number and offset into device address based on address format */ - if (_page_size == AT45DB_PAGE_SIZE_264) { - address = (page << AT45DB_PAGE_BIT_264) | offset; - } else if (_page_size == AT45DB_PAGE_SIZE_528) { - address = (page << AT45DB_PAGE_BIT_528) | offset; + if (_page_size == DATAFLASH_PAGE_SIZE_264) { + address = (page << DATAFLASH_PAGE_BIT_264) | offset; + } else if (_page_size == DATAFLASH_PAGE_SIZE_528) { + address = (page << DATAFLASH_PAGE_BIT_528) | offset; } else { address = (page * _page_size) | offset; } @@ -643,17 +643,17 @@ int AT45DBBlockDevice::_write_page(const uint8_t *buffer, * @param addr Binary address. * @return Address in format expected by device. */ -uint32_t AT45DBBlockDevice::_translate_address(bd_addr_t addr) +uint32_t DataFlashBlockDevice::_translate_address(bd_addr_t addr) { uint32_t address = addr; /* translate address if page size is non-binary */ - if (_page_size == AT45DB_PAGE_SIZE_264) { - address = ((addr / AT45DB_PAGE_SIZE_264) << AT45DB_PAGE_BIT_264) | - (addr % AT45DB_PAGE_SIZE_264); - } else if (_page_size == AT45DB_PAGE_SIZE_528) { - address = ((addr / AT45DB_PAGE_SIZE_528) << AT45DB_PAGE_BIT_528) | - (addr % AT45DB_PAGE_SIZE_528); + if (_page_size == DATAFLASH_PAGE_SIZE_264) { + address = ((addr / DATAFLASH_PAGE_SIZE_264) << DATAFLASH_PAGE_BIT_264) | + (addr % DATAFLASH_PAGE_SIZE_264); + } else if (_page_size == DATAFLASH_PAGE_SIZE_528) { + address = ((addr / DATAFLASH_PAGE_SIZE_528) << DATAFLASH_PAGE_BIT_528) | + (addr % DATAFLASH_PAGE_SIZE_528); } return address; @@ -666,82 +666,82 @@ uint32_t AT45DBBlockDevice::_translate_address(bd_addr_t addr) */ void _print_status(uint16_t status) { -#if AT45DB_DEBUG +#if DATAFLASH_DEBUG DEBUG_PRINTF("%04X\r\n", status); /* device is ready (after write/erase) */ - if (status & AT45DB_BIT_READY) { - DEBUG_PRINTF("AT45DB_BIT_READY\r\n"); + if (status & DATAFLASH_BIT_READY) { + DEBUG_PRINTF("DATAFLASH_BIT_READY\r\n"); } /* Buffer comparison failed */ - if (status & AT45DB_BIT_COMPARE) { - DEBUG_PRINTF("AT45DB_BIT_COMPARE\r\n"); + if (status & DATAFLASH_BIT_COMPARE) { + DEBUG_PRINTF("DATAFLASH_BIT_COMPARE\r\n"); } /* device size is 2 MB */ - if (status & AT45DB_STATUS_DENSITY_2_MBIT) { - DEBUG_PRINTF("AT45DB_STATUS_DENSITY_2_MBIT\r\n"); + if (status & DATAFLASH_STATUS_DENSITY_2_MBIT) { + DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_2_MBIT\r\n"); } /* device size is 4 MB */ - if (status & AT45DB_STATUS_DENSITY_4_MBIT) { - DEBUG_PRINTF("AT45DB_STATUS_DENSITY_4_MBIT\r\n"); + if (status & DATAFLASH_STATUS_DENSITY_4_MBIT) { + DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_4_MBIT\r\n"); } /* device size is 8 MB */ - if (status & AT45DB_STATUS_DENSITY_8_MBIT) { - DEBUG_PRINTF("AT45DB_STATUS_DENSITY_8_MBIT\r\n"); + if (status & DATAFLASH_STATUS_DENSITY_8_MBIT) { + DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_8_MBIT\r\n"); } /* device size is 16 MB */ - if (status & AT45DB_STATUS_DENSITY_16_MBIT) { - DEBUG_PRINTF("AT45DB_STATUS_DENSITY_16_MBIT\r\n"); + if (status & DATAFLASH_STATUS_DENSITY_16_MBIT) { + DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_16_MBIT\r\n"); } /* device size is 32 MB */ - if (status & AT45DB_STATUS_DENSITY_32_MBIT) { - DEBUG_PRINTF("AT45DB_STATUS_DENSITY_32_MBIT\r\n"); + if (status & DATAFLASH_STATUS_DENSITY_32_MBIT) { + DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_32_MBIT\r\n"); } /* device size is 64 MB */ - if (status & AT45DB_STATUS_DENSITY_64_MBIT) { - DEBUG_PRINTF("AT45DB_STATUS_DENSITY_64_MBIT\r\n"); + if (status & DATAFLASH_STATUS_DENSITY_64_MBIT) { + DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_64_MBIT\r\n"); } /* sector protectino enabled */ - if (status & AT45DB_BIT_PROTECT) { - DEBUG_PRINTF("AT45DB_BIT_PROTECT\r\n"); + if (status & DATAFLASH_BIT_PROTECT) { + DEBUG_PRINTF("DATAFLASH_BIT_PROTECT\r\n"); } /* page size is a power of 2 */ - if (status & AT45DB_BIT_PAGE_SIZE) { - DEBUG_PRINTF("AT45DB_BIT_PAGE_SIZE\r\n"); + if (status & DATAFLASH_BIT_PAGE_SIZE) { + DEBUG_PRINTF("DATAFLASH_BIT_PAGE_SIZE\r\n"); } /* erase/program error */ - if (status & AT45DB_BIT_ERASE_PROGRAM_ERROR) { - DEBUG_PRINTF("AT45DB_BIT_ERASE_PROGRAM_ERROR\r\n"); + if (status & DATAFLASH_BIT_ERASE_PROGRAM_ERROR) { + DEBUG_PRINTF("DATAFLASH_BIT_ERASE_PROGRAM_ERROR\r\n"); } /* sector lockdown still possible */ - if (status & AT45DB_BIT_SECTOR_LOCKDOWN) { - DEBUG_PRINTF("AT45DB_BIT_SECTOR_LOCKDOWN\r\n"); + if (status & DATAFLASH_BIT_SECTOR_LOCKDOWN) { + DEBUG_PRINTF("DATAFLASH_BIT_SECTOR_LOCKDOWN\r\n"); } /* program operation suspended while using buffer 2 */ - if (status & AT45DB_BIT_PROGRAM_SUSPEND_2) { - DEBUG_PRINTF("AT45DB_BIT_PROGRAM_SUSPEND_2\r\n"); + if (status & DATAFLASH_BIT_PROGRAM_SUSPEND_2) { + DEBUG_PRINTF("DATAFLASH_BIT_PROGRAM_SUSPEND_2\r\n"); } /* program operation suspended while using buffer 1 */ - if (status & AT45DB_BIT_PROGRAM_SUSPEND_1) { - DEBUG_PRINTF("AT45DB_BIT_PROGRAM_SUSPEND_1\r\n"); + if (status & DATAFLASH_BIT_PROGRAM_SUSPEND_1) { + DEBUG_PRINTF("DATAFLASH_BIT_PROGRAM_SUSPEND_1\r\n"); } /* erase has been suspended */ - if (status & AT45DB_BIT_ERASE_SUSPEND) { - DEBUG_PRINTF("AT45DB_BIT_ERASE_SUSPEND\r\n"); + if (status & DATAFLASH_BIT_ERASE_SUSPEND) { + DEBUG_PRINTF("DATAFLASH_BIT_ERASE_SUSPEND\r\n"); } #endif } diff --git a/AT45DBBlockDevice.h b/DataFlashBlockDevice.h similarity index 77% rename from AT45DBBlockDevice.h rename to DataFlashBlockDevice.h index d7035c3535..219b832f66 100644 --- a/AT45DBBlockDevice.h +++ b/DataFlashBlockDevice.h @@ -14,46 +14,46 @@ * limitations under the License. */ -#ifndef MBED_AT45DB_BLOCK_DEVICE_H -#define MBED_AT45DB_BLOCK_DEVICE_H +#ifndef MBED_DATAFLASH_BLOCK_DEVICE_H +#define MBED_DATAFLASH_BLOCK_DEVICE_H #include #include "BlockDevice.h" -/** BlockDevice for AT45DB flash devices +/** BlockDevice for DataFlash flash devices * * @code * #include "mbed.h" - * #include "AT45DBBlockDevice.h" + * #include "DataFlashBlockDevice.h" * - * // Create at45db on SPI bus with PTE5 as chip select - * AT45DBBlockDevice at45db(PTE2, PTE4, PTE1, PTE5); + * // Create DataFlash on SPI bus with PTE5 as chip select + * DataFlashBlockDevice dataflash(PTE2, PTE4, PTE1, PTE5); * - * // Create at45db on SPI bus with PTE6 as write-protect - * AT45DBBlockDevice at45db(PTE2, PTE4, PTE1, PTE5, PTE6); + * // Create DataFlash on SPI bus with PTE6 as write-protect + * DataFlashBlockDevice dataflash(PTE2, PTE4, PTE1, PTE5, PTE6); * * int main() { - * printf("at45db test\n"); - * at45db.init(); - * printf("at45db size: %llu\n", at45db.size()); - * printf("at45db read size: %llu\n", at45db.get_read_size()); - * printf("at45db program size: %llu\n", at45db.get_program_size()); - * printf("at45db erase size: %llu\n", at45db.get_erase_size()); + * printf("dataflash test\n"); + * dataflash.init(); + * printf("dataflash size: %llu\n", dataflash.size()); + * printf("dataflash read size: %llu\n", dataflash.get_read_size()); + * printf("dataflash program size: %llu\n", dataflash.get_program_size()); + * printf("dataflash erase size: %llu\n", dataflash.get_erase_size()); * - * uint8_t *buffer = malloc(at45db.get_erase_size()); + * uint8_t *buffer = malloc(dataflash.get_erase_size()); * sprintf(buffer, "Hello World!\n"); - * at45db.erase(0, at45db.get_erase_size()); - * at45db.program(buffer, 0, at45db.get_erase_size()); - * at45db.read(buffer, 0, at45db.get_erase_size()); + * dataflash.erase(0, dataflash.get_erase_size()); + * dataflash.program(buffer, 0, dataflash.get_erase_size()); + * dataflash.read(buffer, 0, dataflash.get_erase_size()); * printf("%s", buffer); * - * at45db.deinit(); + * dataflash.deinit(); * } */ -class AT45DBBlockDevice : public BlockDevice { +class DataFlashBlockDevice : public BlockDevice { public: - /** Creates a AT45DBBlockDevice on a SPI bus specified by pins + /** Creates a DataFlashBlockDevice on a SPI bus specified by pins * * @param mosi SPI master out, slave in pin * @param miso SPI master in, slave out pin @@ -62,7 +62,7 @@ public: * @param nowp GPIO not-write-protect * @param freq Clock speed of the SPI bus (defaults to 40MHz) */ - AT45DBBlockDevice(PinName mosi, + DataFlashBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName csel, @@ -158,4 +158,4 @@ private: }; -#endif /* MBED_AT45DB_BLOCK_DEVICE_H */ +#endif /* MBED_DATAFLASH_BLOCK_DEVICE_H */ diff --git a/TESTS/block_device/at45db/main.cpp b/TESTS/block_device/dataflash/main.cpp similarity index 97% rename from TESTS/block_device/at45db/main.cpp rename to TESTS/block_device/dataflash/main.cpp index 605ae0b73f..35b9adcd17 100644 --- a/TESTS/block_device/at45db/main.cpp +++ b/TESTS/block_device/dataflash/main.cpp @@ -3,7 +3,7 @@ #include "unity.h" #include "utest.h" -#include "AT45DBBlockDevice.h" +#include "DataFlashBlockDevice.h" #include using namespace utest::v1; @@ -26,7 +26,7 @@ const struct { void test_read_write() { - AT45DBBlockDevice bd(TEST_PINS, TEST_FREQ); + DataFlashBlockDevice bd(TEST_PINS, TEST_FREQ); int err = bd.init(); TEST_ASSERT_EQUAL(0, err); diff --git a/mbed_lib.json b/mbed_lib.json index 70882dba19..5eefd2978d 100644 --- a/mbed_lib.json +++ b/mbed_lib.json @@ -1,5 +1,5 @@ { - "name": "at45db", + "name": "dataflash", "config": { "binary-size": { "help": "Configure device to use binary address space.", From 4ae3dc05a6542263de39c72595ec14646c5c18c8 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 16 Jul 2017 14:28:21 -0500 Subject: [PATCH 04/11] Added README and LICENSE --- DataFlashBlockDevice.h | 22 ++++-- LICENSE.md | 165 +++++++++++++++++++++++++++++++++++++++++ README.md | 54 ++++++++++++++ 3 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 LICENSE.md create mode 100644 README.md diff --git a/DataFlashBlockDevice.h b/DataFlashBlockDevice.h index 219b832f66..43a4eee4e7 100644 --- a/DataFlashBlockDevice.h +++ b/DataFlashBlockDevice.h @@ -24,32 +24,40 @@ /** BlockDevice for DataFlash flash devices * * @code + * // Here's an example using the AT45DB on the K64F * #include "mbed.h" * #include "DataFlashBlockDevice.h" * * // Create DataFlash on SPI bus with PTE5 as chip select - * DataFlashBlockDevice dataflash(PTE2, PTE4, PTE1, PTE5); + * DataFlashBlockDevice at45db(PTE2, PTE4, PTE1, PTE5); * * // Create DataFlash on SPI bus with PTE6 as write-protect - * DataFlashBlockDevice dataflash(PTE2, PTE4, PTE1, PTE5, PTE6); + * DataFlashBlockDevice at45db(PTE2, PTE4, PTE1, PTE5, PTE6); * * int main() { * printf("dataflash test\n"); + * + * // Initialize the SPI flash device and print the memory layout * dataflash.init(); * printf("dataflash size: %llu\n", dataflash.size()); * printf("dataflash read size: %llu\n", dataflash.get_read_size()); * printf("dataflash program size: %llu\n", dataflash.get_program_size()); * printf("dataflash erase size: %llu\n", dataflash.get_erase_size()); * - * uint8_t *buffer = malloc(dataflash.get_erase_size()); + * // Write "Hello World!" to the first block + * uint8_t *buffer = (uint8_t*)malloc(spif.get_erase_size()); * sprintf(buffer, "Hello World!\n"); - * dataflash.erase(0, dataflash.get_erase_size()); - * dataflash.program(buffer, 0, dataflash.get_erase_size()); - * dataflash.read(buffer, 0, dataflash.get_erase_size()); + * spif.erase(0, spif.get_erase_size()); + * spif.program(buffer, 0, spif.get_erase_size()); + * + * // Read back what was stored + * spif.read(buffer, 0, spif.get_erase_size()); * printf("%s", buffer); * - * dataflash.deinit(); + * // Deinitialize the device + * spif.deinit(); * } + * @endcode */ class DataFlashBlockDevice : public BlockDevice { public: diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..59cd3f8a32 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,165 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..fd8d354a18 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# DataFlash Driver + +Block device driver for I2C based EEPROM devices such as the Adesto AT45DB +series of devices. + +DataFlash is a memory protocol that combines flash with SRAM buffers for a +simple programming interface. DataFlash supports byte-sized read and writes, +with an erase size of around 528 bytes or sometimes 1056 bytes. DataFlash +provides erase sizes with and extra 16 bytes for error correction codes (ECC) +so that a flash translation layer (FTL) may still present 512 byte erase sizes. + +The DataFlashBlockDevice can be configured to force the underlying device +to use either the binary size (ie 512 bytes) or the raw DataFlash size +(ie 528 bytes). + +More info on DataFlash can be found on wikipedia: +https://en.wikipedia.org/wiki/DataFlash + +``` cpp +// Here's an example using the AT45DB on the K64F +#include "mbed.h" +#include "DataFlashBlockDevice.h" + +// Create DataFlash on SPI bus with PTE5 as chip select +DataFlashBlockDevice at45db(PTE2, PTE4, PTE1, PTE5); + +// Create DataFlash on SPI bus with PTE6 as write-protect +DataFlashBlockDevice at45db(PTE2, PTE4, PTE1, PTE5, PTE6); + +int main() { + printf("dataflash test\n"); + + // Initialize the SPI flash device and print the memory layout + dataflash.init(); + printf("dataflash size: %llu\n", dataflash.size()); + printf("dataflash read size: %llu\n", dataflash.get_read_size()); + printf("dataflash program size: %llu\n", dataflash.get_program_size()); + printf("dataflash erase size: %llu\n", dataflash.get_erase_size()); + + // Write "Hello World!" to the first block + uint8_t *buffer = (uint8_t*)malloc(spif.get_erase_size()); + sprintf(buffer, "Hello World!\n"); + spif.erase(0, spif.get_erase_size()); + spif.program(buffer, 0, spif.get_erase_size()); + + // Read back what was stored + spif.read(buffer, 0, spif.get_erase_size()); + printf("%s", buffer); + + // Deinitialize the device + spif.deinit(); +} +``` + From daf4a7fea113ce4667c0570cfb93ebadac1be8af Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 16 Jul 2017 14:34:05 -0500 Subject: [PATCH 05/11] Added support for Travis CI --- .travis.yml | 28 ++++++++++++++++++++++++++++ DataFlashBlockDevice.h | 14 +++++++------- README.md | 14 +++++++------- 3 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..96a6d79ddd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +script: + # Check that examples compile + - sed -n '/``` cpp/,${/```$/q;/```/d;p}' README.md > main.cpp && + PYTHONPATH=mbed-os python mbed-os/tools/make.py -t GCC_ARM -m K82F + --source=. --build=BUILD/K82F/GCC_ARM -j0 && + rm main.cpp + - sed -n '/@code/,${/@endcode/q;/@/d;s/^ \*//;p}' DataFlashBlockDevice.h > main.cpp && + PYTHONPATH=mbed-os python mbed-os/tools/make.py -t GCC_ARM -m K82F + --source=. --build=BUILD/K82F/GCC_ARM -j0 && + rm main.cpp + + # Check that tests compile + - rm -r BUILD && PYTHONPATH=mbed-os python mbed-os/tools/test.py + -t GCC_ARM -m K82F --source=. --build=BUILD/TESTS/K82F/GCC_ARM -j0 + -n tests* + +python: + - "2.7" + +install: + # Get arm-none-eabi-gcc + - sudo add-apt-repository -y ppa:terry.guo/gcc-arm-embedded + - sudo apt-get update -qq + - sudo apt-get install -qq gcc-arm-none-eabi --force-yes + # Get dependencies + - git clone https://github.com/armmbed/mbed-os.git + # Install python dependencies + - sudo pip install -r mbed-os/requirements.txt diff --git a/DataFlashBlockDevice.h b/DataFlashBlockDevice.h index 43a4eee4e7..31ced0c4d1 100644 --- a/DataFlashBlockDevice.h +++ b/DataFlashBlockDevice.h @@ -29,10 +29,10 @@ * #include "DataFlashBlockDevice.h" * * // Create DataFlash on SPI bus with PTE5 as chip select - * DataFlashBlockDevice at45db(PTE2, PTE4, PTE1, PTE5); + * DataFlashBlockDevice dataflash(PTE2, PTE4, PTE1, PTE5); * * // Create DataFlash on SPI bus with PTE6 as write-protect - * DataFlashBlockDevice at45db(PTE2, PTE4, PTE1, PTE5, PTE6); + * DataFlashBlockDevice dataflash2(PTE2, PTE4, PTE1, PTE5, PTE6); * * int main() { * printf("dataflash test\n"); @@ -45,17 +45,17 @@ * printf("dataflash erase size: %llu\n", dataflash.get_erase_size()); * * // Write "Hello World!" to the first block - * uint8_t *buffer = (uint8_t*)malloc(spif.get_erase_size()); + * char *buffer = (char*)malloc(dataflash.get_erase_size()); * sprintf(buffer, "Hello World!\n"); - * spif.erase(0, spif.get_erase_size()); - * spif.program(buffer, 0, spif.get_erase_size()); + * dataflash.erase(0, dataflash.get_erase_size()); + * dataflash.program(buffer, 0, dataflash.get_erase_size()); * * // Read back what was stored - * spif.read(buffer, 0, spif.get_erase_size()); + * dataflash.read(buffer, 0, dataflash.get_erase_size()); * printf("%s", buffer); * * // Deinitialize the device - * spif.deinit(); + * dataflash.deinit(); * } * @endcode */ diff --git a/README.md b/README.md index fd8d354a18..2a0522124e 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,10 @@ https://en.wikipedia.org/wiki/DataFlash #include "DataFlashBlockDevice.h" // Create DataFlash on SPI bus with PTE5 as chip select -DataFlashBlockDevice at45db(PTE2, PTE4, PTE1, PTE5); +DataFlashBlockDevice dataflash(PTE2, PTE4, PTE1, PTE5); // Create DataFlash on SPI bus with PTE6 as write-protect -DataFlashBlockDevice at45db(PTE2, PTE4, PTE1, PTE5, PTE6); +DataFlashBlockDevice dataflash2(PTE2, PTE4, PTE1, PTE5, PTE6); int main() { printf("dataflash test\n"); @@ -38,17 +38,17 @@ int main() { printf("dataflash erase size: %llu\n", dataflash.get_erase_size()); // Write "Hello World!" to the first block - uint8_t *buffer = (uint8_t*)malloc(spif.get_erase_size()); + char *buffer = (char*)malloc(dataflash.get_erase_size()); sprintf(buffer, "Hello World!\n"); - spif.erase(0, spif.get_erase_size()); - spif.program(buffer, 0, spif.get_erase_size()); + dataflash.erase(0, dataflash.get_erase_size()); + dataflash.program(buffer, 0, dataflash.get_erase_size()); // Read back what was stored - spif.read(buffer, 0, spif.get_erase_size()); + dataflash.read(buffer, 0, dataflash.get_erase_size()); printf("%s", buffer); // Deinitialize the device - spif.deinit(); + dataflash.deinit(); } ``` From 0b102e86b552a982f08ae5a3ce1a22a594dd5bae Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Wed, 1 Nov 2017 08:42:42 -0700 Subject: [PATCH 06/11] Add macros for default pin names --- mbed_lib.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mbed_lib.json b/mbed_lib.json index 5eefd2978d..862b99e7d7 100644 --- a/mbed_lib.json +++ b/mbed_lib.json @@ -1,6 +1,10 @@ { "name": "dataflash", "config": { + "SPI_MOSI": "NC", + "SPI_MISO": "NC", + "SPI_CLK": "NC", + "SPI_CS": "NC", "binary-size": { "help": "Configure device to use binary address space.", "value": "0" From 9ee3f5afa73406a6d9b6fc657a42bae71236ad02 Mon Sep 17 00:00:00 2001 From: "heikki.rautakoski" Date: Wed, 2 May 2018 12:31:44 +0300 Subject: [PATCH 07/11] Increment erased block count in erase loop --- DataFlashBlockDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataFlashBlockDevice.cpp b/DataFlashBlockDevice.cpp index c94dfedd21..a5d9d3da8c 100644 --- a/DataFlashBlockDevice.cpp +++ b/DataFlashBlockDevice.cpp @@ -413,7 +413,7 @@ int DataFlashBlockDevice::erase(bd_addr_t addr, bd_size_t size) /* update loop variables */ addr += _block_size; - erased -= _block_size; + erased += _block_size; } /* enable write protection */ From ba46863c427eaffab21ed2658d791816580b5fdf Mon Sep 17 00:00:00 2001 From: "heikki.rautakoski" Date: Thu, 24 May 2018 14:52:18 +0300 Subject: [PATCH 08/11] Fix write_enable commands and method. -This only has effect if sector protection enabled. --- DataFlashBlockDevice.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/DataFlashBlockDevice.cpp b/DataFlashBlockDevice.cpp index a5d9d3da8c..cbff1948f3 100644 --- a/DataFlashBlockDevice.cpp +++ b/DataFlashBlockDevice.cpp @@ -60,8 +60,8 @@ enum opcode { /* non-exhaustive command list */ enum command { - DATAFLASH_COMMAND_WRITE_DISABLE = 0x3D2A7F9A, - DATAFLASH_COMMAND_WRITE_ENABLE = 0x3D2A7FA9, + DATAFLASH_COMMAND_WRITE_DISABLE = 0x3D2A7FA9, + DATAFLASH_COMMAND_WRITE_ENABLE = 0x3D2A7F9A, DATAFLASH_COMMAND_BINARY_PAGE_SIZE = 0x3D2A80A6, DATAFLASH_COMMAND_DATAFLASH_PAGE_SIZE = 0x3D2A80A7, }; @@ -525,22 +525,20 @@ void DataFlashBlockDevice::_write_enable(bool enable) /* enable writing, disable write protection */ if (enable) { + /* if not-write-protected pin is connected, select it */ + if (_nwp.is_connected()) { + _nwp = 1; + } /* send 4 byte command enabling writes */ _write_command(DATAFLASH_COMMAND_WRITE_ENABLE, NULL, 0); + } else { /* if not-write-protected pin is connected, deselect it */ if (_nwp.is_connected()) { _nwp = 0; } - } else { - - /* if not-write-protected pin is connected, select it */ - if (_nwp.is_connected()) { - _nwp = 1; - } - /* send 4 byte command disabling writes */ _write_command(DATAFLASH_COMMAND_WRITE_DISABLE, NULL, 0); } From a0c4fb65658588c8f82bf8b732a935f00d9abebd Mon Sep 17 00:00:00 2001 From: DL6AKU Date: Fri, 1 Jun 2018 15:23:43 +0200 Subject: [PATCH 09/11] Enable DATAFLASH_DEBUG to be defined externally (for example in a Makefile) instead of it being hardcoded in the .cpp file. --- DataFlashBlockDevice.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DataFlashBlockDevice.cpp b/DataFlashBlockDevice.cpp index cbff1948f3..e367b2d398 100644 --- a/DataFlashBlockDevice.cpp +++ b/DataFlashBlockDevice.cpp @@ -36,7 +36,9 @@ #define DATAFLASH_PAGE_BIT_528 10 /* enable debug */ +#ifndef DATAFLASH_DEBUG #define DATAFLASH_DEBUG 0 +#endif /* DATAFLASH_DEBUG */ #if DATAFLASH_DEBUG #define DEBUG_PRINTF(...) printf(__VA_ARGS__) From 6f3a342f6105813a6205875339cae9f374298704 Mon Sep 17 00:00:00 2001 From: David Saada Date: Wed, 4 Jul 2018 16:37:18 +0300 Subject: [PATCH 10/11] Implement the get_erase_size API (based on address) --- DataFlashBlockDevice.cpp | 7 +++++++ DataFlashBlockDevice.h | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/DataFlashBlockDevice.cpp b/DataFlashBlockDevice.cpp index e367b2d398..0d2309661a 100644 --- a/DataFlashBlockDevice.cpp +++ b/DataFlashBlockDevice.cpp @@ -446,6 +446,13 @@ bd_size_t DataFlashBlockDevice::get_erase_size() const return _block_size; } +bd_size_t DataFlashBlockDevice::get_erase_size(bd_addr_t addr) const +{ + DEBUG_PRINTF("erase size: %" PRIX16 "\r\n", _block_size); + + return _block_size; +} + bd_size_t DataFlashBlockDevice::size() const { DEBUG_PRINTF("device size: %" PRIX32 "\r\n", _device_size); diff --git a/DataFlashBlockDevice.h b/DataFlashBlockDevice.h index 31ced0c4d1..e73b40222f 100644 --- a/DataFlashBlockDevice.h +++ b/DataFlashBlockDevice.h @@ -139,6 +139,14 @@ public: */ virtual bd_size_t get_erase_size() const; + /** Get the size of an erasable block given address + * + * @param addr Address within the erasable block + * @return Size of an erasable block in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size(bd_addr_t addr) const; + /** Get the total size of the underlying device * * @return Size of the underlying device in bytes From c7685742be49bd4e6f9dc97723238c63dadaad88 Mon Sep 17 00:00:00 2001 From: David Saada Date: Tue, 14 Aug 2018 19:51:03 +0300 Subject: [PATCH 11/11] Add some logic related to initialization - Add an initialization flag on which read/program/erase actions depend. - Add an init reference count. --- DataFlashBlockDevice.cpp | 45 +++++++++++++++++++++++++++++++++++++++- DataFlashBlockDevice.h | 2 ++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/DataFlashBlockDevice.cpp b/DataFlashBlockDevice.cpp index 0d2309661a..3661b78d32 100644 --- a/DataFlashBlockDevice.cpp +++ b/DataFlashBlockDevice.cpp @@ -15,6 +15,7 @@ */ #include "DataFlashBlockDevice.h" +#include "mbed_critical.h" #include @@ -138,7 +139,11 @@ DataFlashBlockDevice::DataFlashBlockDevice(PinName mosi, : _spi(mosi, miso, sclk), _cs(cs, 1), _nwp(nwp), - _device_size(0) + _device_size(0), + _page_size(0), + _block_size(0), + _is_initialized(0), + _init_ref_count(0) { /* check that frequency is within range */ if (freq > DATAFLASH_LOW_FREQUENCY) { @@ -161,6 +166,16 @@ int DataFlashBlockDevice::init() { DEBUG_PRINTF("init\r\n"); + if (!_is_initialized) { + _init_ref_count = 0; + } + + uint32_t val = core_util_atomic_incr_u32(&_init_ref_count, 1); + + if (val != 1) { + return BD_ERROR_OK; + } + int result = BD_ERROR_DEVICE_ERROR; /* read ID register to validate model and set dimensions */ @@ -262,6 +277,10 @@ int DataFlashBlockDevice::init() /* write protect device when idle */ _write_enable(false); + if (result == BD_ERROR_OK) { + _is_initialized = true; + } + return result; } @@ -269,6 +288,18 @@ int DataFlashBlockDevice::deinit() { DEBUG_PRINTF("deinit\r\n"); + if (!_is_initialized) { + _init_ref_count = 0; + return BD_ERROR_OK; + } + + uint32_t val = core_util_atomic_decr_u32(&_init_ref_count, 1); + + if (val) { + return BD_ERROR_OK; + } + + _is_initialized = false; return BD_ERROR_OK; } @@ -276,6 +307,10 @@ int DataFlashBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) { DEBUG_PRINTF("read: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); + if (!_is_initialized) { + return BD_ERROR_DEVICE_ERROR; + } + int result = BD_ERROR_DEVICE_ERROR; /* check parameters are valid and the read is within bounds */ @@ -317,6 +352,10 @@ int DataFlashBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t { DEBUG_PRINTF("program: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); + if (!_is_initialized) { + return BD_ERROR_DEVICE_ERROR; + } + int result = BD_ERROR_DEVICE_ERROR; /* check parameters are valid and the write is within bounds */ @@ -379,6 +418,10 @@ int DataFlashBlockDevice::erase(bd_addr_t addr, bd_size_t size) { DEBUG_PRINTF("erase: %" PRIX64 " %" PRIX64 "\r\n", addr, size); + if (!_is_initialized) { + return BD_ERROR_DEVICE_ERROR; + } + int result = BD_ERROR_DEVICE_ERROR; /* check parameters are valid and the erase is within bounds */ diff --git a/DataFlashBlockDevice.h b/DataFlashBlockDevice.h index e73b40222f..d3e0448383 100644 --- a/DataFlashBlockDevice.h +++ b/DataFlashBlockDevice.h @@ -163,6 +163,8 @@ private: uint32_t _device_size; uint16_t _page_size; uint16_t _block_size; + bool _is_initialized; + uint32_t _init_ref_count; // Internal functions uint16_t _get_register(uint8_t opcode);