mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 04:52:59 +01:00
drivers: add AT25xxx series EEPROM
This adds a driver for the ST M95xxx series SPI EEPROMs. The driver has been tested with the M95M01 EEPROM, but should work with other chips from that family. SPI-EEPROMs from other vendors from the families AT25xxx, 25AAxxx, 25LCxxx, CAT25xxx & BR25Sxxx should also in the same way.
This commit is contained in:
parent
ab333f3fb2
commit
97fc2f2af1
@ -40,6 +40,11 @@ ifneq (,$(filter at24mac,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_i2c
|
||||
endif
|
||||
|
||||
ifneq (,$(filter at25xxx,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_spi
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter at30tse75x,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
FEATURES_REQUIRED += periph_i2c
|
||||
|
@ -24,6 +24,10 @@ ifneq (,$(filter at24mac,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/at24mac/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter at25xxx,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/at25xxx/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter at86rf2xx,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/at86rf2xx/include
|
||||
endif
|
||||
|
1
drivers/at25xxx/Makefile
Normal file
1
drivers/at25xxx/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
228
drivers/at25xxx/at25xxx.c
Normal file
228
drivers/at25xxx/at25xxx.c
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2019 ML!PA Consulting GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup drivers_at25xxx
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Driver for the AT25xxx family of SPI-EEPROMs.
|
||||
* This also includes M95xxx, 25AAxxx, 25LCxxx,
|
||||
* CAT25xxx & BR25Sxxx.
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "at25xxx.h"
|
||||
#include "at25xxx_constants.h"
|
||||
#include "at25xxx_params.h"
|
||||
#include "byteorder.h"
|
||||
|
||||
#include "xtimer.h"
|
||||
#define POLL_DELAY_US (1000)
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) > (b) ? (b) : (a))
|
||||
#endif
|
||||
|
||||
#define PAGE_SIZE (dev->params.page_size)
|
||||
#define ADDR_LEN (AT25XXX_PARAM_ADDR_LEN)
|
||||
#define ADDR_MSK ((1UL << ADDR_LEN) - 1)
|
||||
|
||||
#ifndef AT25XXXX_SET_BUF_SIZE
|
||||
/**
|
||||
* @brief Adjust to configure buffer size
|
||||
*/
|
||||
#define AT225XXXX_SET_BUF_SIZE (64)
|
||||
#endif
|
||||
|
||||
static inline int getbus(const at25xxx_t *dev)
|
||||
{
|
||||
return spi_acquire(dev->params.spi, dev->params.cs_pin, SPI_MODE_0, dev->params.spi_clk);
|
||||
}
|
||||
|
||||
static inline uint32_t _pos(uint8_t cmd, uint32_t pos)
|
||||
{
|
||||
/* first byte is CMD, then addr with MSB first */
|
||||
pos = htonl((pos & ADDR_MSK) | ((uint32_t)cmd << ADDR_LEN));
|
||||
pos >>= 8 * sizeof(pos) - (ADDR_LEN + 8);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static inline bool _write_in_progress(const at25xxx_t *dev)
|
||||
{
|
||||
return spi_transfer_reg(dev->params.spi, dev->params.cs_pin, CMD_RDSR, 0) & SR_WIP;
|
||||
}
|
||||
|
||||
static inline bool _write_enabled(const at25xxx_t *dev)
|
||||
{
|
||||
return spi_transfer_reg(dev->params.spi, dev->params.cs_pin, CMD_RDSR, 0) & SR_WEL;
|
||||
}
|
||||
|
||||
static inline int _wait_until_eeprom_ready(const at25xxx_t *dev)
|
||||
{
|
||||
uint8_t tries = 10;
|
||||
while (_write_in_progress(dev) && --tries) {
|
||||
spi_release(dev->params.spi);
|
||||
xtimer_usleep(POLL_DELAY_US);
|
||||
getbus(dev);
|
||||
}
|
||||
|
||||
return tries == 0 ? -ETIMEDOUT : 0;
|
||||
}
|
||||
|
||||
static ssize_t _write_page(const at25xxx_t *dev, uint32_t pos, const void *data, size_t len)
|
||||
{
|
||||
/* write no more than to the end of the current page to prevent wrap-around */
|
||||
size_t remaining = PAGE_SIZE - (pos & (PAGE_SIZE - 1));
|
||||
len = min(len, remaining);
|
||||
pos = _pos(CMD_WRITE, pos);
|
||||
|
||||
/* wait for previous write to finish - may take up to 5 ms */
|
||||
int res = _wait_until_eeprom_ready(dev);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* set write enable and wait for status change */
|
||||
spi_transfer_byte(dev->params.spi, dev->params.cs_pin, false, CMD_WREN);
|
||||
|
||||
unsigned tries = 1000;
|
||||
while (!_write_enabled(dev) && --tries) {}
|
||||
|
||||
if (tries == 0) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* write the data */
|
||||
spi_transfer_bytes(dev->params.spi, dev->params.cs_pin, true, &pos, NULL, 1 + ADDR_LEN / 8);
|
||||
spi_transfer_bytes(dev->params.spi, dev->params.cs_pin, false, data, NULL, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int at25xxx_write(const at25xxx_t *dev, uint32_t pos, const void *data, size_t len)
|
||||
{
|
||||
int res = 0;
|
||||
const uint8_t *d = data;
|
||||
|
||||
if (pos + len > dev->params.size) {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
getbus(dev);
|
||||
|
||||
while (len) {
|
||||
ssize_t written = _write_page(dev, pos, d, len);
|
||||
if (written < 0) {
|
||||
res = written;
|
||||
break;
|
||||
}
|
||||
|
||||
len -= written;
|
||||
pos += written;
|
||||
d += written;
|
||||
}
|
||||
|
||||
spi_release(dev->params.spi);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void at25xxx_write_byte(const at25xxx_t *dev, uint32_t pos, uint8_t data)
|
||||
{
|
||||
at25xxx_write(dev, pos, &data, sizeof(data));
|
||||
}
|
||||
|
||||
int at25xxx_read(const at25xxx_t *dev, uint32_t pos, void *data, size_t len)
|
||||
{
|
||||
if (pos + len > dev->params.size) {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
getbus(dev);
|
||||
|
||||
/* wait for previous write to finish - may take up to 5 ms */
|
||||
int res = _wait_until_eeprom_ready(dev);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
pos = _pos(CMD_READ, pos);
|
||||
spi_transfer_bytes(dev->params.spi, dev->params.cs_pin, true, &pos, NULL, 1 + ADDR_LEN / 8);
|
||||
spi_transfer_bytes(dev->params.spi, dev->params.cs_pin, false, NULL, data, len);
|
||||
|
||||
spi_release(dev->params.spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t at25xxx_read_byte(const at25xxx_t *dev, uint32_t pos)
|
||||
{
|
||||
uint8_t b;
|
||||
at25xxx_read(dev, pos, &b, sizeof(b));
|
||||
return b;
|
||||
}
|
||||
|
||||
int at25xxx_set(const at25xxx_t *dev, uint32_t pos, uint8_t val, size_t len)
|
||||
{
|
||||
uint8_t data[AT225XXXX_SET_BUF_SIZE];
|
||||
size_t total = 0;
|
||||
|
||||
if (pos + len > dev->params.size) {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
memset(data, val, sizeof(data));
|
||||
|
||||
getbus(dev);
|
||||
|
||||
while (len) {
|
||||
size_t written = _write_page(dev, pos, data, min(len, sizeof(data)));
|
||||
len -= written;
|
||||
pos += written;
|
||||
total += written;
|
||||
}
|
||||
|
||||
spi_release(dev->params.spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int at25xxx_clear(const at25xxx_t *dev, uint32_t pos, size_t len)
|
||||
{
|
||||
return at25xxx_set(dev, pos, 0, len);
|
||||
}
|
||||
|
||||
int at25xxx_init(at25xxx_t *dev, const at25xxx_params_t *params)
|
||||
{
|
||||
dev->params = *params;
|
||||
spi_init_cs(dev->params.spi, dev->params.cs_pin);
|
||||
|
||||
if (dev->params.wp_pin != GPIO_UNDEF) {
|
||||
gpio_init(dev->params.wp_pin, GPIO_OUT);
|
||||
gpio_set(dev->params.wp_pin);
|
||||
}
|
||||
|
||||
if (dev->params.hold_pin != GPIO_UNDEF) {
|
||||
gpio_init(dev->params.hold_pin, GPIO_OUT);
|
||||
gpio_set(dev->params.hold_pin);
|
||||
}
|
||||
|
||||
/* check if the SPI configuration is valid */
|
||||
if (getbus(dev) != SPI_OK) {
|
||||
return -1;
|
||||
}
|
||||
spi_release(dev->params.spi);
|
||||
return 0;
|
||||
}
|
44
drivers/at25xxx/include/at25xxx_constants.h
Normal file
44
drivers/at25xxx/include/at25xxx_constants.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2019 ML!PA Consulting GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup drivers_at25xxx
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Commands for the AT25xxx family of SPI-EEPROMs
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef AT25XXX_CONSTANTS_H
|
||||
#define AT25XXX_CONSTANTS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CMD_WREN (0x6) /**< Write Enable */
|
||||
#define CMD_WRDI (0x4) /**< Write Disable */
|
||||
#define CMD_RDSR (0x5) /**< Read Status Register */
|
||||
#define CMD_WRSR (0x1) /**< Write Status Register */
|
||||
#define CMD_READ (0x3) /**< Read from Memory Array */
|
||||
#define CMD_WRITE (0x2) /**< Write to Memory Array */
|
||||
|
||||
#define SR_WIP (0x01) /**< Write In Progress */
|
||||
#define SR_WEL (0x02) /**< Write Enable Latch */
|
||||
#define SR_BP0 (0x04) /**< Block Protect 0 */
|
||||
#define SR_BP1 (0x08) /**< Block Protect 1 */
|
||||
#define SR_SRWD (0x80) /**< Status Register Write Disable */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* AT25XXX_CONSTANTS_H */
|
81
drivers/at25xxx/include/at25xxx_params.h
Normal file
81
drivers/at25xxx/include/at25xxx_params.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2019 ML!PA Consulting GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup drivers_at25xxx
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Default configuration for the M95M01 EEPROM
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef AT25XXX_PARAMS_H
|
||||
#define AT25XXX_PARAMS_H
|
||||
|
||||
#include "board.h"
|
||||
#include "at25xxx.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Set default configuration parameters for the AT25XXX driver
|
||||
* @{
|
||||
*/
|
||||
#ifndef AT25XXX_PARAM_SPI
|
||||
#define AT25XXX_PARAM_SPI (SPI_DEV(0))
|
||||
#endif
|
||||
#ifndef AT25XXX_PARAM_SPI_CLK
|
||||
#define AT25XXX_PARAM_SPI_CLK (SPI_CLK_5MHZ)
|
||||
#endif
|
||||
#ifndef AT25XXX_PARAM_CS
|
||||
#define AT25XXX_PARAM_CS (GPIO_PIN(0, 0))
|
||||
#endif
|
||||
#ifndef AT25XXX_PARAM_WP
|
||||
#define AT25XXX_PARAM_WP (GPIO_UNDEF)
|
||||
#endif
|
||||
#ifndef AT25XXX_PARAM_HOLD
|
||||
#define AT25XXX_PARAM_HOLD (GPIO_UNDEF)
|
||||
#endif
|
||||
#ifndef AT25XXX_PARAM_SIZE
|
||||
#define AT25XXX_PARAM_SIZE (128 * 1024UL) /* EEPROM size, in bytes */
|
||||
#endif
|
||||
#ifndef AT25XXX_PARAM_ADDR_LEN
|
||||
#define AT25XXX_PARAM_ADDR_LEN (24) /* Address length, in bits */
|
||||
#endif
|
||||
#ifndef AT25XXX_PARAM_PAGE_SIZE
|
||||
#define AT25XXX_PARAM_PAGE_SIZE (256) /* Page size, in bytes */
|
||||
#endif
|
||||
|
||||
#ifndef AT25XXX_PARAMS
|
||||
#define AT25XXX_PARAMS { .spi = AT25XXX_PARAM_SPI, \
|
||||
.spi_clk = AT25XXX_PARAM_SPI_CLK, \
|
||||
.cs_pin = AT25XXX_PARAM_CS, \
|
||||
.wp_pin = AT25XXX_PARAM_WP, \
|
||||
.hold_pin = AT25XXX_PARAM_HOLD, \
|
||||
.size = AT25XXX_PARAM_SIZE, \
|
||||
.page_size = AT25XXX_PARAM_PAGE_SIZE }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief AT25XXX configuration
|
||||
*/
|
||||
static const at25xxx_params_t at25xxx_params[] =
|
||||
{
|
||||
AT25XXX_PARAMS
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* AT25XXX_PARAMS_H */
|
144
drivers/include/at25xxx.h
Normal file
144
drivers/include/at25xxx.h
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2019 ML!PA Consulting GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup drivers_at25xxx AT25xxx family of SPI-EEPROMs
|
||||
* @ingroup drivers_misc
|
||||
*
|
||||
* @brief This driver also support M95xxx, 25AAxxx, 25LCxxx, CAT25xxx & BR25Sxxx families
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Driver for the AT25xxx series of EEPROMs
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef AT25XXX_H
|
||||
#define AT25XXX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "periph/spi.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief struct holding all params needed for device initialization
|
||||
*/
|
||||
typedef struct {
|
||||
spi_t spi; /**< SPI bus the device is connected to */
|
||||
spi_clk_t spi_clk; /**< SPI clock speed to use */
|
||||
spi_cs_t cs_pin; /**< GPIO pin connected to chip select */
|
||||
gpio_t wp_pin; /**< GPIO pin connected to the write-protect pin */
|
||||
gpio_t hold_pin; /**< GPIO pin connected to the hold pin */
|
||||
uint32_t size; /**< Size of the EEPROM in bytes */
|
||||
uint16_t page_size; /**< Page Size of the EEPROM in bytes */
|
||||
} at25xxx_params_t;
|
||||
|
||||
/**
|
||||
* @brief struct that represents an AT25XXX device
|
||||
*/
|
||||
typedef struct {
|
||||
at25xxx_params_t params; /**< parameters */
|
||||
} at25xxx_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize an AT25XXX device handle with AT25XXX parameters
|
||||
*
|
||||
* @param[in, out] dev AT25XXX device handle
|
||||
* @param[in] params AT25XXX parameters
|
||||
*
|
||||
* @return 0 on success, -1 on failure
|
||||
*
|
||||
*/
|
||||
int at25xxx_init(at25xxx_t *dev, const at25xxx_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Read a byte at a given position @p pos
|
||||
*
|
||||
* @param[in] dev AT25XXX device handle
|
||||
* @param[in] pos position in EEPROM memory
|
||||
*
|
||||
* @return read byte
|
||||
*/
|
||||
uint8_t at25xxx_read_byte(const at25xxx_t *dev, uint32_t pos);
|
||||
|
||||
/**
|
||||
* @brief Sequentially read @p len bytes from a given position @p pos
|
||||
*
|
||||
* @param[in] dev AT25XXX device handle
|
||||
* @param[in] pos position in EEPROM memory
|
||||
* @param[out] data read buffer
|
||||
* @param[in] len requested length to be read
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ERANGE if pos + len > EEPROM size
|
||||
*/
|
||||
int at25xxx_read(const at25xxx_t *dev, uint32_t pos, void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Write a byte at a given position @p pos
|
||||
*
|
||||
* @param[in] dev AT25XXX device handle
|
||||
* @param[in] pos position in EEPROM memory
|
||||
* @param[in] data value to be written
|
||||
*/
|
||||
void at25xxx_write_byte(const at25xxx_t *dev, uint32_t pos, uint8_t data);
|
||||
|
||||
/**
|
||||
* @brief Sequentially write @p len bytes from a given position @p pos
|
||||
*
|
||||
* @param[in] dev AT25XXX device handle
|
||||
* @param[in] pos position in EEPROM memory
|
||||
* @param[in] data write buffer
|
||||
* @param[in] len requested length to be written
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ERANGE if pos + len > EEPROM size
|
||||
*/
|
||||
int at25xxx_write(const at25xxx_t *dev, uint32_t pos, const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Set @p len bytes from a given position @p pos to the
|
||||
* value @p val
|
||||
*
|
||||
* @param[in] dev AT25XXX device handle
|
||||
* @param[in] pos position in EEPROM memory
|
||||
* @param[in] val value to be set
|
||||
* @param[in] len requested length to be written
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ERANGE if pos + len > EEPROM size
|
||||
*/
|
||||
int at25xxx_set(const at25xxx_t *dev, uint32_t pos, uint8_t val, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Set @p len bytes from position @p pos to 0
|
||||
*
|
||||
* This is a wrapper around @see at25xxx_set.
|
||||
*
|
||||
* @param[in] dev AT25XXX device handle
|
||||
* @param[in] pos position in EEPROM memory
|
||||
* @param[in] len requested length to be written
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ERANGE if pos + len > EEPROM size
|
||||
*/
|
||||
int at25xxx_clear(const at25xxx_t *dev, uint32_t pos, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* AT25XXX_H */
|
Loading…
Reference in New Issue
Block a user