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…
	
	Add table
		Add a link
		
	
		Reference in a new issue