Merge pull request #528 from UncleRus/extras/f-ram
Driver for Cypress serial F-RAM
This commit is contained in:
commit
126fbc20a7
5 changed files with 361 additions and 0 deletions
|
@ -297,6 +297,41 @@ size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len
|
|||
return len;
|
||||
}
|
||||
|
||||
static void _spi_buf_read(uint8_t bus, uint8_t b, void *in_data,
|
||||
size_t len, spi_endianness_t e, spi_word_size_t word_size)
|
||||
{
|
||||
_wait(bus);
|
||||
size_t bytes = len * (uint8_t)word_size;
|
||||
_set_size(bus, bytes);
|
||||
uint32_t w = ((uint32_t)b << 24) | ((uint32_t)b << 16) | ((uint32_t)b << 8) | b;
|
||||
for (uint8_t i = 0; i < _SPI_BUF_SIZE / 4; i ++)
|
||||
SPI(bus).W[i] = w;
|
||||
_start(bus);
|
||||
_wait(bus);
|
||||
_spi_buf_prepare(bus, len, e, word_size);
|
||||
memcpy(in_data, (void *)SPI(bus).W, bytes);
|
||||
}
|
||||
|
||||
void spi_read(uint8_t bus, uint8_t out_byte, void *in_data, size_t len, spi_word_size_t word_size)
|
||||
{
|
||||
spi_endianness_t e = spi_get_endianness(bus);
|
||||
uint8_t buf_size = _SPI_BUF_SIZE / (uint8_t)word_size;
|
||||
|
||||
size_t blocks = len / buf_size;
|
||||
for (size_t i = 0; i < blocks; i++)
|
||||
{
|
||||
size_t offset = i * _SPI_BUF_SIZE;
|
||||
_spi_buf_read(bus, out_byte, (uint8_t *)in_data + offset, buf_size, e, word_size);
|
||||
_rearm_extras_bit(bus, false);
|
||||
}
|
||||
|
||||
uint8_t tail = len % buf_size;
|
||||
if (tail)
|
||||
_spi_buf_read(bus, out_byte, (uint8_t *)in_data + blocks * _SPI_BUF_SIZE, tail, e, word_size);
|
||||
|
||||
if (blocks) _rearm_extras_bit(bus, true);
|
||||
}
|
||||
|
||||
static void _repeat_send(uint8_t bus, uint32_t *dword, int32_t *repeats,
|
||||
spi_word_size_t size)
|
||||
{
|
||||
|
|
|
@ -400,6 +400,16 @@ void spi_repeat_send_16(uint8_t bus, uint16_t data, int32_t repeats);
|
|||
*/
|
||||
void spi_repeat_send_32(uint8_t bus, uint32_t data, int32_t repeats);
|
||||
|
||||
/**
|
||||
* \brief Repeatedly send byte over SPI and receive data
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param out_byte Byte to send
|
||||
* \param in_data Receive buffer
|
||||
* \param len Buffer size in words
|
||||
* \param word_size Size of the word
|
||||
*/
|
||||
void spi_read(uint8_t bus, uint8_t out_byte, void *in_data, size_t len, spi_word_size_t word_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
9
extras/fram/component.mk
Normal file
9
extras/fram/component.mk
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Component makefile for extras/fram
|
||||
|
||||
# expected anyone using ADC driver includes it as 'fram/fram.h'
|
||||
INC_DIRS += $(fram_ROOT)..
|
||||
|
||||
# args for passing into compile rule generation
|
||||
fram_SRC_DIR = $(fram_ROOT)
|
||||
|
||||
$(eval $(call component_compile_rules,fram))
|
183
extras/fram/fram.c
Normal file
183
extras/fram/fram.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/**
|
||||
* Driver for serial nonvolatile ferroelectric random access
|
||||
* memory or F-RAM.
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2017 Ruslan V. Uss <unclerus@gmail.com>
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#include "fram.h"
|
||||
|
||||
#include <esp/gpio.h>
|
||||
|
||||
#define CMD_WRSR 0x01 // 0b00000001
|
||||
#define CMD_WRITE 0x02 // 0b00000010
|
||||
#define CMD_READ 0x03 // 0b00000011
|
||||
#define CMD_WRDI 0x04 // 0b00000100
|
||||
#define CMD_RDSR 0x05 // 0b00000101
|
||||
#define CMD_WREN 0x06 // 0b00000110
|
||||
#define CMD_FSTRD 0x0b // 0b00001011
|
||||
#define CMD_RDID 0x9f // 0b10011111
|
||||
#define CMD_SLEEP 0xb9 // 0b10111001
|
||||
#define CMD_SNR 0xc3 // 0b11000011
|
||||
|
||||
#define SR_BIT_WEL 1
|
||||
#define SR_BIT_BP0 2
|
||||
#define SR_BIT_BP1 3
|
||||
#define SR_BIT_WPEN 7
|
||||
|
||||
#define SR_MASK_BP (0x03 << SR_BIT_BP0)
|
||||
|
||||
static const spi_settings_t defaults = {
|
||||
.endianness = SPI_BIG_ENDIAN,
|
||||
.msb = true,
|
||||
.mode = SPI_MODE0,
|
||||
.minimal_pins = true,
|
||||
.freq_divider = SPI_FREQ_DIV_40M
|
||||
};
|
||||
|
||||
inline static void chip_select(const fram_t *dev)
|
||||
{
|
||||
gpio_write(dev->cs_gpio, false);
|
||||
}
|
||||
|
||||
inline static void chip_unselect(const fram_t *dev)
|
||||
{
|
||||
gpio_write(dev->cs_gpio, true);
|
||||
}
|
||||
|
||||
static uint8_t read_status_reg(const fram_t *dev)
|
||||
{
|
||||
chip_select(dev);
|
||||
spi_transfer_8(dev->spi_bus, CMD_RDSR);
|
||||
uint8_t res = spi_transfer_8(dev->spi_bus, 0xff);
|
||||
chip_unselect(dev);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void write_status_reg(const fram_t *dev, uint8_t val)
|
||||
{
|
||||
chip_select(dev);
|
||||
spi_transfer_8(dev->spi_bus, CMD_WREN);
|
||||
chip_unselect(dev);
|
||||
|
||||
chip_select(dev);
|
||||
spi_transfer_8(dev->spi_bus, CMD_WRSR);
|
||||
spi_transfer_8(dev->spi_bus, val);
|
||||
}
|
||||
|
||||
void fram_init(const fram_t *dev)
|
||||
{
|
||||
gpio_enable(dev->cs_gpio, GPIO_OUTPUT);
|
||||
gpio_set_pullup(dev->cs_gpio, true, true);
|
||||
chip_unselect(dev);
|
||||
}
|
||||
|
||||
static void begin(const fram_t *dev, spi_settings_t *s, spi_settings_t *old)
|
||||
{
|
||||
spi_get_settings(dev->spi_bus, &old);
|
||||
memcpy(&s, &defaults, sizeof(spi_settings_t));
|
||||
s->freq_divider = dev->spi_freq_div;
|
||||
spi_set_settings(dev->spi_bus, &s);
|
||||
chip_select(dev);
|
||||
}
|
||||
|
||||
static void end(const fram_t *dev, spi_settings_t *old)
|
||||
{
|
||||
chip_unselect(dev);
|
||||
spi_set_settings(dev->spi_bus, &old);
|
||||
}
|
||||
|
||||
void fram_read(const fram_t *dev, void *to, void *from, size_t size)
|
||||
{
|
||||
spi_settings_t s, old;
|
||||
begin(dev, &s, &old);
|
||||
|
||||
uint32_t header = ((uint32_t)CMD_READ << 24) | ((uint32_t)from & 0x00ffffff);
|
||||
spi_transfer_32(dev->spi_bus, header);
|
||||
|
||||
spi_set_endianness(dev->spi_bus, SPI_LITTLE_ENDIAN);
|
||||
spi_read(dev->spi_bus, 0xff, to, size, SPI_8BIT);
|
||||
|
||||
end(dev, &old);
|
||||
}
|
||||
|
||||
void fram_write(const fram_t *dev, void *from, void *to, size_t size)
|
||||
{
|
||||
spi_settings_t s, old;
|
||||
begin(dev, &s, &old);
|
||||
|
||||
spi_transfer_8(dev->spi_bus, CMD_WREN);
|
||||
chip_unselect(dev);
|
||||
|
||||
chip_select(dev);
|
||||
uint32_t header = ((uint32_t)CMD_WRITE << 24) | ((uint32_t)to & 0x00ffffff);
|
||||
spi_transfer_32(dev->spi_bus, header);
|
||||
|
||||
spi_set_endianness(dev->spi_bus, SPI_LITTLE_ENDIAN);
|
||||
spi_transfer(dev->spi_bus, from, NULL, size, SPI_8BIT);
|
||||
|
||||
end(dev, &old);
|
||||
}
|
||||
|
||||
void fram_sleep(const fram_t *dev, bool sleep)
|
||||
{
|
||||
if (!sleep)
|
||||
{
|
||||
chip_select(dev);
|
||||
chip_unselect(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
spi_settings_t s, old;
|
||||
begin(dev, &s, &old);
|
||||
|
||||
spi_transfer_8(dev->spi_bus, CMD_SLEEP);
|
||||
|
||||
end(dev, &old);
|
||||
}
|
||||
|
||||
bool fram_busy(const fram_t *dev)
|
||||
{
|
||||
gpio_enable(dev->cs_gpio, GPIO_INPUT);
|
||||
bool res = !gpio_read(dev->cs_gpio);
|
||||
fram_init(dev);
|
||||
return res;
|
||||
}
|
||||
|
||||
void fram_id(const fram_t *dev, fram_id_t *id)
|
||||
{
|
||||
spi_settings_t s, old;
|
||||
begin(dev, &s, &old);
|
||||
|
||||
spi_transfer_8(dev->spi_bus, CMD_RDID);
|
||||
|
||||
for (uint8_t i = 0; i < FRAM_ID_LEN; i ++)
|
||||
id->data[FRAM_ID_LEN - i - 1] = spi_transfer_8(dev->spi_bus, 0xff);
|
||||
|
||||
end(dev, &old);
|
||||
}
|
||||
|
||||
void fram_set_wp_mode(const fram_t *dev, fram_wp_mode_t mode)
|
||||
{
|
||||
spi_settings_t s, old;
|
||||
begin(dev, &s, &old);
|
||||
|
||||
write_status_reg(dev, (read_status_reg(dev) & ~SR_MASK_BP) | ((mode & 0x03) << SR_BIT_BP0));
|
||||
|
||||
end(dev, &old);
|
||||
}
|
||||
|
||||
fram_wp_mode_t fram_get_wp_mode(const fram_t *dev)
|
||||
{
|
||||
spi_settings_t s, old;
|
||||
begin(dev, &s, &old);
|
||||
|
||||
fram_wp_mode_t res = (fram_wp_mode_t)((read_status_reg(dev) & SR_MASK_BP) >> SR_BIT_BP0);
|
||||
|
||||
end(dev, &old);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
124
extras/fram/fram.h
Normal file
124
extras/fram/fram.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* Driver for serial nonvolatile ferroelectric random access
|
||||
* memory or F-RAM.
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2017 Ruslan V. Uss <unclerus@gmail.com>
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#ifndef EXTRAS_FRAM_H_
|
||||
#define EXTRAS_FRAM_H_
|
||||
|
||||
#include <esp/spi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FRAM_ID_LEN 9
|
||||
|
||||
/**
|
||||
* F-RAM device descriptor
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t spi_bus; //!< SPI bus
|
||||
uint8_t cs_gpio; //!< chip select GPIO
|
||||
uint32_t spi_freq_div; //!< SPI bus frequency divider
|
||||
} fram_t;
|
||||
|
||||
/**
|
||||
* F-RAM device ID
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t data[FRAM_ID_LEN];
|
||||
union
|
||||
{
|
||||
uint8_t rsvd: 3;
|
||||
uint8_t rev: 3;
|
||||
uint8_t sub: 2;
|
||||
uint8_t density: 5;
|
||||
uint8_t family: 3;
|
||||
uint8_t manufacturer[FRAM_ID_LEN - 2];
|
||||
};
|
||||
} fram_id_t;
|
||||
|
||||
|
||||
/**
|
||||
* Write protection mode
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
FRAM_WP_NONE = 0, //!< No write protection
|
||||
FRAM_WP_UPPER_QUARTER,//!< Upper 1/4 write protection
|
||||
FRAM_WP_UPPER_HALF, //!< Upper 1/2 write protection
|
||||
FRAM_WP_ALL //!< All memory write protection
|
||||
} fram_wp_mode_t;
|
||||
|
||||
/**
|
||||
* Prepare to read/write F-RAM
|
||||
* @param dev Pointer to device descriptor
|
||||
*/
|
||||
void fram_init(const fram_t *dev);
|
||||
|
||||
/**
|
||||
* Read data from F-RAM
|
||||
* @param dev Pointer to device descriptor
|
||||
* @param to Buffer to store data
|
||||
* @param from F-RAM address
|
||||
* @param size Bytes to read
|
||||
*/
|
||||
void fram_read(const fram_t *dev, void *to, void *from, size_t size);
|
||||
|
||||
/**
|
||||
* Write data to F-RAM
|
||||
* @param dev Pointer to device descriptor
|
||||
* @param from Data buffer
|
||||
* @param to F-RAM address
|
||||
* @param size Bytes to write
|
||||
*/
|
||||
void fram_write(const fram_t *dev, void *from, void *to, size_t size);
|
||||
|
||||
/**
|
||||
* Set device to sleep mode or wake up
|
||||
* @param dev Pointer to device descriptor
|
||||
* @param sleep Set to sleep mode when true, wake up otherwise
|
||||
*/
|
||||
void fram_sleep(const fram_t *dev, bool sleep);
|
||||
|
||||
/**
|
||||
* Check if F-RAM busy with another SPI master.
|
||||
* To use this method of sharing F-RAM between two masters
|
||||
* you'll need to pull up CS GPIO line
|
||||
* @param dev Pointer to device descriptor
|
||||
* @return true when device is busy
|
||||
*/
|
||||
bool fram_busy(const fram_t *dev);
|
||||
|
||||
/**
|
||||
* Read the device ID
|
||||
* @param dev Pointer to device descriptor
|
||||
* @param id Poiner to device ID structure
|
||||
*/
|
||||
void fram_id(const fram_t *dev, fram_id_t *id);
|
||||
|
||||
/**
|
||||
* Set block write protection mode
|
||||
* @param dev Pointer to device descriptor
|
||||
* @param mode Write protection mode
|
||||
*/
|
||||
void fram_set_wp_mode(const fram_t *dev, fram_wp_mode_t mode);
|
||||
|
||||
/**
|
||||
* Get current write protection mode
|
||||
* @param dev Pointer to device descriptor
|
||||
* @return Write protection mode
|
||||
*/
|
||||
fram_wp_mode_t fram_get_wp_mode(const fram_t *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXTRAS_FRAM_H_ */
|
Loading…
Reference in a new issue