diff options
Diffstat (limited to 'os/hal')
-rw-r--r-- | os/hal/hal.mk | 5 | ||||
-rw-r--r-- | os/hal/include/ee24xx.h | 64 | ||||
-rw-r--r-- | os/hal/include/ee25xx.h | 63 | ||||
-rw-r--r-- | os/hal/include/eeprom.h | 147 | ||||
-rw-r--r-- | os/hal/include/hal_community.h | 1 | ||||
-rw-r--r-- | os/hal/src/ee24xx.c | 353 | ||||
-rw-r--r-- | os/hal/src/ee25xx.c | 404 | ||||
-rw-r--r-- | os/hal/src/eeprom.c | 197 |
8 files changed, 1233 insertions, 1 deletions
diff --git a/os/hal/hal.mk b/os/hal/hal.mk index 2802a09..0fcced7 100644 --- a/os/hal/hal.mk +++ b/os/hal/hal.mk @@ -11,6 +11,9 @@ HALSRC += ${CHIBIOS_CONTRIB}/os/hal/src/hal_community.c \ ${CHIBIOS_CONTRIB}/os/hal/src/usbh/usbh_hub.c \
${CHIBIOS_CONTRIB}/os/hal/src/usbh/usbh_msd.c \
${CHIBIOS_CONTRIB}/os/hal/src/usbh/usbh_ftdi.c \
- ${CHIBIOS_CONTRIB}/os/hal/src/usbh/usbh_uvc.c
+ ${CHIBIOS_CONTRIB}/os/hal/src/usbh/usbh_uvc.c \
+ ${CHIBIOS_CONTRIB}/os/hal/src/ee24xx.c \
+ ${CHIBIOS_CONTRIB}/os/hal/src/ee25xx.c \
+ ${CHIBIOS_CONTRIB}/os/hal/src/eeprom.c
HALINC += ${CHIBIOS_CONTRIB}/os/hal/include
diff --git a/os/hal/include/ee24xx.h b/os/hal/include/ee24xx.h new file mode 100644 index 0000000..ab12fd1 --- /dev/null +++ b/os/hal/include/ee24xx.h @@ -0,0 +1,64 @@ +/* + Copyright 2012 Uladzimir Pylinski aka barthess. + You may use this work without restrictions, as long as this notice is included. + The work is provided "as is" without warranty of any kind, neither express nor implied. +*/ + +#ifndef EE24XX_H +#define EE24XX_H + +#include "hal.h" + +#if defined(HAL_USE_EEPROM) && HAL_USE_EEPROM && EEPROM_USE_EE24XX + +#define EEPROM_DEV_24XX 24 + +/** + * @extends EepromFileConfig + */ +typedef struct { + _eeprom_file_config_data + /** + * Driver connected to IC. + */ + I2CDriver *i2cp; + /** + * Address of IC on I2C bus. + */ + i2caddr_t addr; + /** + * Pointer to write buffer. The safest size is (pagesize + 2) + */ + uint8_t *write_buf; +} I2CEepromFileConfig; + +/** + * @brief @p I2CEepromFileStream specific data. + */ +#define _eeprom_file_stream_data_i2c \ + _eeprom_file_stream_data + +/** + * @extends EepromFileStream + * + * @brief EEPROM file stream driver class for I2C device. + */ +typedef struct { + const struct EepromFileStreamVMT *vmt; + _eeprom_file_stream_data_i2c + /* Overwritten parent data member. */ + const I2CEepromFileConfig *cfg; +} I2CEepromFileStream; + + +/** + * Open I2C EEPROM IC as file and return pointer to the file stream object + * @note Fucntion allways successfully open file. All checking makes + * in read/write functions. + */ +#define I2CEepromFileOpen(efs, eepcfg, eepdev) \ + EepromFileOpen((EepromFileStream *)efs, (EepromFileConfig *)eepcfg, eepdev); + +#endif /* #if defined(EEPROM_USE_EE24XX) && EEPROM_USE_EE24XX */ + +#endif // EE24XX_H diff --git a/os/hal/include/ee25xx.h b/os/hal/include/ee25xx.h new file mode 100644 index 0000000..fc2ad6f --- /dev/null +++ b/os/hal/include/ee25xx.h @@ -0,0 +1,63 @@ +/* + Copyright 2012 Uladzimir Pylinski aka barthess. + You may use this work without restrictions, as long as this notice is included. + The work is provided "as is" without warranty of any kind, neither express nor implied. +*/ + +#ifndef EE25XX_H +#define EE25XX_H + +#include "hal.h" + +#if defined(HAL_USE_EEPROM) && HAL_USE_EEPROM && EEPROM_USE_EE25XX + +#define EEPROM_DEV_25XX 25 + +/** + * @extends EepromFileConfig + */ +typedef struct { + _eeprom_file_config_data + /** + * Driver connected to IC. + */ + SPIDriver *spip; + /** + * Config associated with SPI driver. + */ + const SPIConfig *spicfg; +} SPIEepromFileConfig; + +/** + * @brief @p SPIEepromFileStream specific data. + */ +#define _eeprom_file_stream_data_spi \ + _eeprom_file_stream_data + +/** + * @extends EepromFileStream + * + * @brief EEPROM file stream driver class for SPI device. + */ +typedef struct { + const struct EepromFileStreamVMT *vmt; + _eeprom_file_stream_data_spi + /* Overwritten parent data member. */ + const SPIEepromFileConfig *cfg; +} SPIEepromFileStream; + +/** + * Open SPI EEPROM IC as file and return pointer to the file stream object + * @note Fucntion allways successfully open file. All checking makes + * in read/write functions. + */ +EepromFileStream *SPIEepromFileOpen(SPIEepromFileStream *efs, + const SPIEepromFileConfig *eepcfg, + const EepromDevice *eepdev); + +#define SPIEepromFileOpen(efs, eepcfg, eepdev) \ + EepromFileOpen((EepromFileStream *)efs, (EepromFileConfig *)eepcfg, eepdev); + +#endif /* #if defined(EEPROM_USE_EE25XX) && EEPROM_USE_EE25XX */ + +#endif // EE25XX_H diff --git a/os/hal/include/eeprom.h b/os/hal/include/eeprom.h new file mode 100644 index 0000000..b0fd360 --- /dev/null +++ b/os/hal/include/eeprom.h @@ -0,0 +1,147 @@ +/* + Copyright (c) 2013 Timon Wong + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* + Copyright 2012 Uladzimir Pylinski aka barthess. + You may use this work without restrictions, as long as this notice is included. + The work is provided "as is" without warranty of any kind, neither express nor implied. +*/ + +#ifndef __EEPROM_H__ +#define __EEPROM_H__ + +#include "ch.h" +#include "hal.h" + +#ifndef HAL_USE_EEPROM +#define HAL_USE_EEPROM FALSE +#endif + +#ifndef EEPROM_USE_EE25XX +#define EEPROM_USE_EE25XX FALSE +#endif + +#ifndef EEPROM_USE_EE24XX +#define EEPROM_USE_EE24XX FALSE +#endif + +#if HAL_USE_EEPROM + +#if EEPROM_USE_EE25XX && EEPROM_USE_EE24XX +#define EEPROM_TABLE_SIZE 2 +#elif EEPROM_USE_EE25XX || EEPROM_USE_EE24XX +#define EEPROM_TABLE_SIZE 1 +#else +#error "No EEPROM device selected!" +#endif + +#if EEPROM_USE_EE25XX && !HAL_USE_SPI +#error "25xx enabled but SPI driver is disabled!" +#endif + +#if EEPROM_USE_EE24XX && !HAL_USE_I2C +#error "24xx enabled but I2C driver is disabled!" +#endif + +#define _eeprom_file_config_data \ + /* Lower barrier of file in EEPROM memory array. */ \ + uint32_t barrier_low; \ + /* Higher barrier of file in EEPROM memory array. */ \ + uint32_t barrier_hi; \ + /* Size of memory array in bytes. */ \ + uint32_t size; \ + /* Size of single page in bytes. */ \ + uint16_t pagesize; \ + /* Time needed by IC for single byte/page writing. */ \ + systime_t write_time; + +typedef uint32_t fileoffset_t; + +typedef struct { + _eeprom_file_config_data +} EepromFileConfig; + +/** + * @brief @p EepromFileStream specific data. + */ +#define _eeprom_file_stream_data \ + _base_sequential_stream_data \ + uint32_t errors; \ + uint32_t position; \ + +/** + * @extends BaseFileStreamVMT + * + * @brief @p EepromFileStream virtual methods table. + */ +struct EepromFileStreamVMT { + _file_stream_methods +}; + +/** + * @extends BaseFileStream + * + * @brief EEPROM file stream driver class. + * @details This class extends @p BaseFileStream by adding some fields. + */ +typedef struct { + /** @brief Virtual Methods Table.*/ + const struct EepromFileStreamVMT *vmt; + _eeprom_file_stream_data + /** pointer to config object, must be overwritten by all derived classes.*/ + const EepromFileConfig *cfg; +} EepromFileStream; + +/** + * @brief Low level device descriptor. + */ +typedef struct { + const uint8_t id; + const struct EepromFileStreamVMT *efsvmt; +} EepromDevice; + +const EepromDevice *EepromFindDevice(uint8_t id); + +EepromFileStream *EepromFileOpen(EepromFileStream *efs, + const EepromFileConfig *eepcfg, + const EepromDevice *eepdev); + +uint8_t EepromReadByte(EepromFileStream *efs); +uint16_t EepromReadHalfword(EepromFileStream *efs); +uint32_t EepromReadWord(EepromFileStream *efs); +size_t EepromWriteByte(EepromFileStream *efs, uint8_t data); +size_t EepromWriteHalfword(EepromFileStream *efs, uint16_t data); +size_t EepromWriteWord(EepromFileStream *efs, uint32_t data); + +msg_t eepfs_getsize(void *ip); +msg_t eepfs_getposition(void *ip); +msg_t eepfs_lseek(void *ip, fileoffset_t offset); +msg_t eepfs_close(void *ip); +msg_t eepfs_geterror(void *ip); +msg_t eepfs_put(void *ip, uint8_t b); +msg_t eepfs_get(void *ip); + +#include "ee24xx.h" +#include "ee25xx.h" + +#endif /* #if defined(HAL_USE_EEPROM) && HAL_USE_EEPROM */ +#endif /* __EEPROM_H__ */ diff --git a/os/hal/include/hal_community.h b/os/hal/include/hal_community.h index 8a17765..5ed561f 100644 --- a/os/hal/include/hal_community.h +++ b/os/hal/include/hal_community.h @@ -37,6 +37,7 @@ /* Complex drivers.*/
#include "onewire.h"
#include "crc.h"
+#include "eeprom.h"
/*===========================================================================*/
/* Driver constants. */
diff --git a/os/hal/src/ee24xx.c b/os/hal/src/ee24xx.c new file mode 100644 index 0000000..c89635f --- /dev/null +++ b/os/hal/src/ee24xx.c @@ -0,0 +1,353 @@ +/* + Copyright (c) 2013 Timon Wong + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* + Copyright 2012 Uladzimir Pylinski aka barthess. + You may use this work without restrictions, as long as this notice is included. + The work is provided "as is" without warranty of any kind, neither express nor implied. +*/ + +/***************************************************************************** + * DATASHEET NOTES + ***************************************************************************** +Write cycle time (byte or page) - 5 ms + +Note: + Page write operations are limited to writing bytes within a single physical + page, regardless of the number of bytes actually being written. Physical page + boundaries start at addresses that are integer multiples of the page buffer + size (or page size and end at addresses that are integer multiples of + [page size]. If a Page Write command attempts to write across a physical + page boundary, the result is that the data wraps around to the beginning of + the current page (overwriting data previously stored there), instead of + being written to the next page as might be expected. +*********************************************************************/ + +#include "ee24xx.h" +#include <string.h> + +#if (defined(HAL_USE_EEPROM) && HAL_USE_EEPROM && EEPROM_USE_EE24XX) || defined(__DOXYGEN__) + +/* + ****************************************************************************** + * DEFINES + ****************************************************************************** + */ +/* +#if defined(SAM7_PLATFORM) +#define EEPROM_I2C_CLOCK (MCK / (((i2cp->config->cwgr & 0xFF) + ((i2cp->config->cwgr >> 8) & 0xFF)) * (1 << ((i2cp->config->cwgr >> 16) & 7)) + 6)) +#else +#define EEPROM_I2C_CLOCK (i2cp->config->clock_speed) +#endif +*/ +#define EEPROM_I2C_CLOCK 400000 + +/* + ****************************************************************************** + * EXTERNS + ****************************************************************************** + */ + +/* + ****************************************************************************** + * GLOBAL VARIABLES + ****************************************************************************** + */ + +/* + ******************************************************************************* + * LOCAL FUNCTIONS + ******************************************************************************* + */ +/** + * @brief Split one uint16_t address to two uint8_t. + * + * @param[in] txbuf pointer to driver transmit buffer + * @param[in] addr uint16_t address + */ +#define eeprom_split_addr(txbuf, addr){ \ + (txbuf)[0] = ((uint8_t)((addr >> 8) & 0xFF)); \ + (txbuf)[1] = ((uint8_t)(addr & 0xFF)); \ + } + +/* + ******************************************************************************* + * EXPORTED FUNCTIONS + ******************************************************************************* + */ + +/** + * @brief Calculates requred timeout. + */ +static systime_t calc_timeout(I2CDriver *i2cp, size_t txbytes, size_t rxbytes) { + (void)i2cp; + const uint32_t bitsinbyte = 10; + uint32_t tmo; + tmo = ((txbytes + rxbytes + 1) * bitsinbyte * 1000); + tmo /= EEPROM_I2C_CLOCK; + tmo += 10; /* some additional milliseconds to be safer */ + return MS2ST(tmo); +} + +/** + * @brief EEPROM read routine. + * + * @param[in] eepcfg pointer to configuration structure of eeprom file + * @param[in] offset addres of 1-st byte to be read + * @param[in] data pointer to buffer with data to be written + * @param[in] len number of bytes to be red + */ +static msg_t eeprom_read(const I2CEepromFileConfig *eepcfg, + uint32_t offset, uint8_t *data, size_t len) { + + msg_t status = MSG_RESET; + systime_t tmo = calc_timeout(eepcfg->i2cp, 2, len); + + osalDbgAssert(((len <= eepcfg->size) && ((offset + len) <= eepcfg->size)), + "out of device bounds"); + + eeprom_split_addr(eepcfg->write_buf, (offset + eepcfg->barrier_low)); + +#if I2C_USE_MUTUAL_EXCLUSION + i2cAcquireBus(eepcfg->i2cp); +#endif + + status = i2cMasterTransmitTimeout(eepcfg->i2cp, eepcfg->addr, + eepcfg->write_buf, 2, data, len, tmo); + +#if I2C_USE_MUTUAL_EXCLUSION + i2cReleaseBus(eepcfg->i2cp); +#endif + + return status; +} + +/** + * @brief EEPROM write routine. + * @details Function writes data to EEPROM. + * @pre Data must be fit to single EEPROM page. + * + * @param[in] eepcfg pointer to configuration structure of eeprom file + * @param[in] offset addres of 1-st byte to be write + * @param[in] data pointer to buffer with data to be written + * @param[in] len number of bytes to be written + */ +static msg_t eeprom_write(const I2CEepromFileConfig *eepcfg, uint32_t offset, + const uint8_t *data, size_t len) { + msg_t status = MSG_RESET; + systime_t tmo = calc_timeout(eepcfg->i2cp, (len + 2), 0); + + osalDbgAssert(((len <= eepcfg->size) && ((offset + len) <= eepcfg->size)), + "out of device bounds"); + osalDbgAssert((((offset + eepcfg->barrier_low) / eepcfg->pagesize) == + (((offset + eepcfg->barrier_low) + len - 1) / eepcfg->pagesize)), + "data can not be fitted in single page"); + + /* write address bytes */ + eeprom_split_addr(eepcfg->write_buf, (offset + eepcfg->barrier_low)); + /* write data bytes */ + memcpy(&(eepcfg->write_buf[2]), data, len); + +#if I2C_USE_MUTUAL_EXCLUSION + i2cAcquireBus(eepcfg->i2cp); +#endif + + status = i2cMasterTransmitTimeout(eepcfg->i2cp, eepcfg->addr, + eepcfg->write_buf, (len + 2), NULL, 0, tmo); + +#if I2C_USE_MUTUAL_EXCLUSION + i2cReleaseBus(eepcfg->i2cp); +#endif + + /* wait until EEPROM process data */ + chThdSleep(eepcfg->write_time); + + return status; +} + +/** + * @brief Determines and returns size of data that can be processed + */ +static size_t __clamp_size(void *ip, size_t n) { + + if (((size_t)eepfs_getposition(ip) + n) > (size_t)eepfs_getsize(ip)) + return eepfs_getsize(ip) - eepfs_getposition(ip); + else + return n; +} + +/** + * @brief Write data that can be fitted in one page boundary + */ +static void __fitted_write(void *ip, const uint8_t *data, size_t len, uint32_t *written) { + + msg_t status = MSG_RESET; + + osalDbgAssert(len != 0, "something broken in hi level part"); + + status = eeprom_write(((I2CEepromFileStream *)ip)->cfg, + eepfs_getposition(ip), data, len); + if (status == MSG_OK) { + *written += len; + eepfs_lseek(ip, eepfs_getposition(ip) + len); + } +} + +/** + * @brief Write data to EEPROM. + * @details Only one EEPROM page can be written at once. So fucntion + * splits large data chunks in small EEPROM transactions if needed. + * @note To achieve the maximum effectivity use write operations + * aligned to EEPROM page boundaries. + */ +static size_t write(void *ip, const uint8_t *bp, size_t n) { + + size_t len = 0; /* bytes to be written at one trasaction */ + uint32_t written; /* total bytes successfully written */ + uint16_t pagesize; + uint32_t firstpage; + uint32_t lastpage; + + osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL)); + + if (n == 0) + return 0; + + n = __clamp_size(ip, n); + if (n == 0) + return 0; + + pagesize = ((EepromFileStream *)ip)->cfg->pagesize; + firstpage = (((EepromFileStream *)ip)->cfg->barrier_low + + eepfs_getposition(ip)) / pagesize; + lastpage = (((EepromFileStream *)ip)->cfg->barrier_low + + eepfs_getposition(ip) + n - 1) / pagesize; + + written = 0; + /* data fitted in single page */ + if (firstpage == lastpage) { + len = n; + __fitted_write(ip, bp, len, &written); + bp += len; + return written; + } + + else { + /* write first piece of data to first page boundary */ + len = ((firstpage + 1) * pagesize) - eepfs_getposition(ip); + len -= ((EepromFileStream *)ip)->cfg->barrier_low; + __fitted_write(ip, bp, len, &written); + bp += len; + + /* now writes blocks at a size of pages (may be no one) */ + while ((n - written) > pagesize) { + len = pagesize; + __fitted_write(ip, bp, len, &written); + bp += len; + } + + /* wrtie tail */ + len = n - written; + if (len == 0) + return written; + else { + __fitted_write(ip, bp, len, &written); + } + } + + return written; +} + +/** + * Read some bytes from current position in file. After successful + * read operation the position pointer will be increased by the number + * of read bytes. + */ +static size_t read(void *ip, uint8_t *bp, size_t n) { + msg_t status = MSG_OK; + + osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL)); + + if (n == 0) + return 0; + + n = __clamp_size(ip, n); + if (n == 0) + return 0; + + /* Stupid I2C cell in STM32F1x does not allow to read single byte. + So we must read 2 bytes and return needed one. */ +#if defined(STM32F1XX_I2C) + if (n == 1) { + uint8_t __buf[2]; + /* if NOT last byte of file requested */ + if ((eepfs_getposition(ip) + 1) < eepfs_getsize(ip)) { + if (read(ip, __buf, 2) == 2) { + eepfs_lseek(ip, (eepfs_getposition(ip) + 1)); + bp[0] = __buf[0]; + return 1; + } + else + return 0; + } + else { + eepfs_lseek(ip, (eepfs_getposition(ip) - 1)); + if (read(ip, __buf, 2) == 2) { + eepfs_lseek(ip, (eepfs_getposition(ip) + 2)); + bp[0] = __buf[1]; + return 1; + } + else + return 0; + } + } +#endif /* defined(STM32F1XX_I2C) */ + + /* call low level function */ + status = eeprom_read(((I2CEepromFileStream *)ip)->cfg, + eepfs_getposition(ip), bp, n); + if (status != MSG_OK) + return 0; + else { + eepfs_lseek(ip, (eepfs_getposition(ip) + n)); + return n; + } +} + +static const struct EepromFileStreamVMT vmt = { + write, + read, + eepfs_put, + eepfs_get, + eepfs_close, + eepfs_geterror, + eepfs_getsize, + eepfs_getposition, + eepfs_lseek, +}; + +EepromDevice eepdev_24xx = { + EEPROM_DEV_24XX, + &vmt +}; + +#endif /* EEPROM_USE_EE24XX */ diff --git a/os/hal/src/ee25xx.c b/os/hal/src/ee25xx.c new file mode 100644 index 0000000..07f4373 --- /dev/null +++ b/os/hal/src/ee25xx.c @@ -0,0 +1,404 @@ +/* + Copyright (c) 2013 Timon Wong + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* + Copyright 2012 Uladzimir Pylinski aka barthess. + You may use this work without restrictions, as long as this notice is included. + The work is provided "as is" without warranty of any kind, neither express nor implied. +*/ + +/***************************************************************************** + * DATASHEET NOTES + ***************************************************************************** +Write cycle time (byte or page) - 5 ms + +Note: + Page write operations are limited to writing bytes within a single physical + page, regardless of the number of bytes actually being written. Physical page + boundaries start at addresses that are integer multiples of the page buffer + size (or page size and end at addresses that are integer multiples of + [page size]. If a Page Write command attempts to write across a physical + page boundary, the result is that the data wraps around to the beginning of + the current page (overwriting data previously stored there), instead of + being written to the next page as might be expected. +*********************************************************************/ + +#include "ee25xx.h" +#include <string.h> + +#if (defined(HAL_USE_EEPROM) && HAL_USE_EEPROM && EEPROM_USE_EE25XX) || defined(__DOXYGEN__) + +/** + * @name Commands of 25XX chip. + * @{ + */ +#define CMD_READ 0x03 /**< @brief Read data from memory array beginning at + selected address. */ +#define CMD_WRITE 0x02 /**< @brief Write data to memory array beginning at + selected address. */ +#define CMD_WRDI 0x04 /**< Reset the write enable latch (disable write + operations). */ +#define CMD_WREN 0x06 /**< Set the write enable latch (enable write + operations). */ +#define CMD_RDSR 0x05 /**< Read STATUS register. */ +#define CMD_WRSR 0x01 /**< Write STATUS register. */ + +/** @} */ + +/** + * @name Status of 25XX chip. + * @{} + */ +#define STAT_BP1 0x08 /**< @brief Block protection (high). */ +#define STAT_BP0 0x04 /**< @brief Block protection (low). */ +#define STAT_WEL 0x02 /**< @brief Write enable latch. */ +#define STAT_WIP 0x01 /**< @brief Write-In-Progress. */ + +/** @} */ + +/** + * @brief 25XX low level write then read rountine. + * + * @param[in] eepcfg pointer to configuration structure of eeprom file. + * @param[in] txbuf pointer to buffer to be transfered. + * @param[in] txlen number of bytes to be transfered. + * @param[out] rxbuf pointer to buffer to be received. + * @param[in] rxlen number of bytes to be received. + */ +static void ll_25xx_transmit_receive(const SPIEepromFileConfig *eepcfg, + const uint8_t *txbuf, size_t txlen, + uint8_t *rxbuf, size_t rxlen) { + +#if SPI_USE_MUTUAL_EXCLUSION + spiAcquireBus(eepcfg->spip); +#endif + spiSelect(eepcfg->spip); + spiSend(eepcfg->spip, txlen, txbuf); + if (rxlen) /* Check if receive is needed. */ + spiReceive(eepcfg->spip, rxlen, rxbuf); + spiUnselect(eepcfg->spip); + +#if SPI_USE_MUTUAL_EXCLUSION + spiReleaseBus(eepcfg->spip); +#endif +} + +/** + * @brief Check whether the device is busy (writing in progress). + * + * @param[in] eepcfg pointer to configuration structure of eeprom file. + * @return @p true on busy. + */ +static bool ll_eeprom_is_busy(const SPIEepromFileConfig *eepcfg) { + + uint8_t cmd = CMD_RDSR; + uint8_t stat; + ll_25xx_transmit_receive(eepcfg, &cmd, 1, &stat, 1); + if (stat & STAT_WIP) + return TRUE; + return FALSE; +} + +/** + * @brief Lock device. + * + * @param[in] eepcfg pointer to configuration structure of eeprom file. + */ +static void ll_eeprom_lock(const SPIEepromFileConfig *eepcfg) { + + uint8_t cmd = CMD_WRDI; + ll_25xx_transmit_receive(eepcfg, &cmd, 1, NULL, 0); +} + +/** + * @brief Unlock device. + * + * @param[in] eepcfg pointer to configuration structure of eeprom file. + */ +static void ll_eeprom_unlock(const SPIEepromFileConfig *eepcfg) { + + uint8_t cmd = CMD_WREN; + ll_25xx_transmit_receive(eepcfg, &cmd, 1, NULL, 0); +} + +/** + * @brief Prepare byte sequence for command and address + * + * @param[in] seq pointer to first 3byte sequence + * @param[in] size size of the eeprom device + * @param[in] cmd command + * @param[in] addr address + * @return number of bytes of this sequence + */ +static uint8_t ll_eeprom_prepare_seq(uint8_t *seq, uint32_t size, uint8_t cmd, + uint32_t addr) { + + seq[0] = ((uint8_t)cmd & 0xff); + + if (size > 0xffffUL) { + /* High density, 24bit address. */ + seq[1] = (uint8_t)((addr >> 16) & 0xff); + seq[2] = (uint8_t)((addr >> 8) & 0xff); + seq[3] = (uint8_t)(addr & 0xff); + return 4; + } + else if (size > 0x00ffUL) { + /* Medium density, 16bit address. */ + seq[1] = (uint8_t)((addr >> 8) & 0xff); + seq[2] = (uint8_t)(addr & 0xff); + return 3; + } + + /* Low density, 8bit address. */ + seq[1] = (uint8_t)(addr & 0xff); + return 2; +} + +/** + * @brief EEPROM read routine. + * + * @param[in] eepcfg pointer to configuration structure of eeprom file. + * @param[in] offset addres of 1-st byte to be read. + * @param[out] data pointer to buffer with data to be written. + * @param[in] len number of bytes to be red. + */ +static msg_t ll_eeprom_read(const SPIEepromFileConfig *eepcfg, uint32_t offset, + uint8_t *data, size_t len) { + + uint8_t txbuff[4]; + uint8_t txlen; + + osalDbgAssert(((len <= eepcfg->size) && ((offset + len) <= eepcfg->size)), + "out of device bounds"); + + if (eepcfg->spip->state != SPI_READY) + return MSG_RESET; + + txlen = ll_eeprom_prepare_seq(txbuff, eepcfg->size, CMD_READ, + (offset + eepcfg->barrier_low)); + ll_25xx_transmit_receive(eepcfg, txbuff, txlen, data, len); + + return MSG_OK; +} + +/** + * @brief EEPROM write routine. + * @details Function writes data to EEPROM. + * @pre Data must be fit to single EEPROM page. + * + * @param[in] eepcfg pointer to configuration structure of eeprom file. + * @param[in] offset addres of 1-st byte to be writen. + * @param[in] data pointer to buffer with data to be written. + * @param[in] len number of bytes to be written. + */ +static msg_t ll_eeprom_write(const SPIEepromFileConfig *eepcfg, uint32_t offset, + const uint8_t *data, size_t len) { + + uint8_t txbuff[4]; + uint8_t txlen; + systime_t now; + + osalDbgAssert(((len <= eepcfg->size) && ((offset + len) <= eepcfg->size)), + "out of device bounds"); + osalDbgAssert((((offset + eepcfg->barrier_low) / eepcfg->pagesize) == + (((offset + eepcfg->barrier_low) + len - 1) / eepcfg->pagesize)), + "data can not be fitted in single page"); + + if (eepcfg->spip->state != SPI_READY) + return MSG_RESET; + + /* Unlock array for writting. */ + ll_eeprom_unlock(eepcfg); + +#if SPI_USE_MUTUAL_EXCLUSION + spiAcquireBus(eepcfg->spip); +#endif + + spiSelect(eepcfg->spip); + txlen = ll_eeprom_prepare_seq(txbuff, eepcfg->size, CMD_WRITE, + (offset + eepcfg->barrier_low)); + spiSend(eepcfg->spip, txlen, txbuff); + spiSend(eepcfg->spip, len, data); + spiUnselect(eepcfg->spip); + +#if SPI_USE_MUTUAL_EXCLUSION + spiReleaseBus(eepcfg->spip); +#endif + + /* Wait until EEPROM process data. */ + now = chVTGetSystemTimeX(); + while (ll_eeprom_is_busy(eepcfg)) { + if ((chVTGetSystemTimeX() - now) > eepcfg->write_time) { + return MSG_TIMEOUT; + } + + chThdYield(); + } + + /* Lock array preventing unexpected access */ + ll_eeprom_lock(eepcfg); + return MSG_OK; +} + +/** + * @brief Determines and returns size of data that can be processed + */ +static size_t __clamp_size(void *ip, size_t n) { + + if (((size_t)eepfs_getposition(ip) + n) > (size_t)eepfs_getsize(ip)) + return eepfs_getsize(ip) - eepfs_getposition(ip); + else + return n; +} + +/** + * @brief Write data that can be fitted in one page boundary + */ +static msg_t __fitted_write(void *ip, const uint8_t *data, size_t len, uint32_t *written) { + + msg_t status = MSG_RESET; + + osalDbgAssert(len != 0, "something broken in hi level part"); + + status = ll_eeprom_write(((SPIEepromFileStream *)ip)->cfg, + eepfs_getposition(ip), data, len); + if (status == MSG_OK) { + *written += len; + eepfs_lseek(ip, eepfs_getposition(ip) + len); + } + return status; +} + +/** + * @brief Write data to EEPROM. + * @details Only one EEPROM page can be written at once. So fucntion + * splits large data chunks in small EEPROM transactions if needed. + * @note To achieve the maximum effectivity use write operations + * aligned to EEPROM page boundaries. + */ +static size_t write(void *ip, const uint8_t *bp, size_t n) { + + size_t len = 0; /* bytes to be written at one trasaction */ + uint32_t written; /* total bytes successfully written */ + uint16_t pagesize; + uint32_t firstpage; + uint32_t lastpage; + + volatile const SPIEepromFileConfig *cfg = ((SPIEepromFileStream *)ip)->cfg; + + osalDbgCheck((ip != NULL) && (((SPIEepromFileStream *)ip)->vmt != NULL)); + + if (n == 0) + return 0; + + n = __clamp_size(ip, n); + if (n == 0) + return 0; + + pagesize = cfg->pagesize; + firstpage = (cfg->barrier_low + eepfs_getposition(ip)) / pagesize; + lastpage = ((cfg->barrier_low + eepfs_getposition(ip) + n) - 1) / pagesize; + + written = 0; + /* data fitted in single page */ + if (firstpage == lastpage) { + len = n; + __fitted_write(ip, bp, len, &written); + bp += len; + return written; + } + else { + /* write first piece of data to first page boundary */ + len = ((firstpage + 1) * pagesize) - eepfs_getposition(ip); + len -= cfg->barrier_low; + __fitted_write(ip, bp, len, &written); + bp += len; + + /* now writes blocks at a size of pages (may be no one) */ + while ((n - written) > pagesize) { + len = pagesize; + if (__fitted_write(ip, bp, len, &written) != MSG_OK) // Fixed: Would increase bp forever and crash in case of timeouts... + return written; + + bp += len; + } + + + /* wrtie tail */ + len = n - written; + if (len == 0) + return written; + else { + __fitted_write(ip, bp, len, &written); + } + } + + return written; +} + +/** + * Read some bytes from current position in file. After successful + * read operation the position pointer will be increased by the number + * of read bytes. + */ +static size_t read(void *ip, uint8_t *bp, size_t n) { + + msg_t status = MSG_OK; + + osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL)); + + if (n == 0) + return 0; + + n = __clamp_size(ip, n); + if (n == 0) + return 0; + + /* call low level function */ + status = ll_eeprom_read(((SPIEepromFileStream *)ip)->cfg, + eepfs_getposition(ip), bp, n); + if (status != MSG_OK) + return 0; + else { + eepfs_lseek(ip, (eepfs_getposition(ip) + n)); + return n; + } +} + +static const struct EepromFileStreamVMT vmt = { + write, + read, + eepfs_put, + eepfs_get, + eepfs_close, + eepfs_geterror, + eepfs_getsize, + eepfs_getposition, + eepfs_lseek, +}; + +EepromDevice eepdev_25xx = { + EEPROM_DEV_25XX, + &vmt +}; + +#endif /* EEPROM_USE_EE25XX */ diff --git a/os/hal/src/eeprom.c b/os/hal/src/eeprom.c new file mode 100644 index 0000000..60d90ed --- /dev/null +++ b/os/hal/src/eeprom.c @@ -0,0 +1,197 @@ +/* + Copyright (c) 2013 Timon Wong + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* + Copyright 2012 Uladzimir Pylinski aka barthess. + You may use this work without restrictions, as long as this notice is included. + The work is provided "as is" without warranty of any kind, neither express nor implied. +*/ + +#include "eeprom.h" +#include <string.h> + +#if defined(HAL_USE_EEPROM) && HAL_USE_EEPROM + +extern EepromDevice eepdev_24xx; +extern EepromDevice eepdev_25xx; + +EepromDevice *__eeprom_drv_table[] = { + /* I2C related. */ +#if HAL_USE_I2C + +# if EEPROM_USE_EE24XX + &eepdev_24xx, +# endif + +#endif /* HAL_USE_I2C */ + + /* SPI related. */ +#if HAL_USE_SPI + +# if EEPROM_USE_EE25XX + &eepdev_25xx, +# endif + +#endif /* HAL_USE_SPI */ +}; + + +/** + * @breif Find low level EEPROM device by id. + */ +const EepromDevice *EepromFindDevice(uint8_t id) { + + uint8_t i; + const EepromDevice *drv; + + for (i = 0; i < EEPROM_TABLE_SIZE; i++) { + drv = __eeprom_drv_table[i]; + if (drv->id == id) { + return drv; + } + } + + return NULL; +} + +/** + * Open EEPROM IC as file and return pointer to the file stream object + * @note Fucntion allways successfully open file. All checking makes + * in read/write functions. + */ +EepromFileStream *EepromFileOpen(EepromFileStream *efs, + const EepromFileConfig *eepcfg, + const EepromDevice *eepdev) { + + osalDbgAssert((efs != NULL) && (eepcfg != NULL) && (eepdev != NULL) && + (eepdev->efsvmt != NULL), "EepromFileOpen"); + osalDbgAssert(efs->vmt != eepdev->efsvmt, "File allready opened"); + osalDbgAssert(eepcfg->barrier_hi > eepcfg->barrier_low, "Low barrier exceeds High barrier"); + osalDbgAssert(eepcfg->pagesize < eepcfg->size, "Pagesize cannot be lager than EEPROM size"); + osalDbgAssert(eepcfg->barrier_hi <= eepcfg->size, "Barrier exceeds EEPROM size"); + + efs->vmt = eepdev->efsvmt; + efs->cfg = eepcfg; + efs->errors = FILE_OK; + efs->position = 0; + return (EepromFileStream *)efs; +} + +uint8_t EepromReadByte(EepromFileStream *efs) { + + uint8_t buf; + fileStreamRead(efs, &buf, sizeof(buf)); + return buf; +} + +uint16_t EepromReadHalfword(EepromFileStream *efs) { + + uint16_t buf; + fileStreamRead(efs, (uint8_t *)&buf, sizeof(buf)); + return buf; +} + +uint32_t EepromReadWord(EepromFileStream *efs) { + + uint32_t buf; + fileStreamRead(efs, (uint8_t *)&buf, sizeof(buf)); + return buf; +} + +size_t EepromWriteByte(EepromFileStream *efs, uint8_t data) { + + return fileStreamWrite(efs, &data, sizeof(data)); +} + +size_t EepromWriteHalfword(EepromFileStream *efs, uint16_t data) { + + return fileStreamWrite(efs, (uint8_t *)&data, sizeof(data)); +} + +size_t EepromWriteWord(EepromFileStream *efs, uint32_t data) { + + return fileStreamWrite(efs, (uint8_t *)&data, sizeof(data)); +} + +msg_t eepfs_getsize(void *ip) { + + uint32_t h, l; + + osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL) && + (((EepromFileStream *)ip)->cfg != NULL)); + + h = ((EepromFileStream *)ip)->cfg->barrier_hi; + l = ((EepromFileStream *)ip)->cfg->barrier_low; + return h - l; +} + +msg_t eepfs_getposition(void *ip) { + + osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL)); + + return ((EepromFileStream *)ip)->position; +} + +msg_t eepfs_lseek(void *ip, fileoffset_t offset) { + + uint32_t size; + + osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL)); + + size = eepfs_getsize(ip); + if (offset > size) + offset = size; + ((EepromFileStream *)ip)->position = offset; + return offset; +} + +msg_t eepfs_close(void *ip) { + + osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL)); + + ((EepromFileStream *)ip)->errors = FILE_OK; + ((EepromFileStream *)ip)->position = 0; + ((EepromFileStream *)ip)->vmt = NULL; + ((EepromFileStream *)ip)->cfg = NULL; + return FILE_OK; +} + +msg_t eepfs_geterror(void *ip) { + + osalDbgCheck((ip != NULL) && (((EepromFileStream *)ip)->vmt != NULL)); + return ((EepromFileStream *)ip)->errors; +} + +msg_t eepfs_put(void *ip, uint8_t b) { + + (void)ip; + (void)b; + return 0; +} + +msg_t eepfs_get(void *ip) { + + (void)ip; + return 0; +} + +#endif /* #if defined(HAL_USE_EEPROM) && HAL_USE_EEPROM */ |