From 42018f03152bef6f8f5eb263679e85d829c0a4e7 Mon Sep 17 00:00:00 2001 From: UncleRus Date: Fri, 18 Mar 2016 03:27:36 +0500 Subject: [PATCH] SPI big endian swap bug fix, spi_get_settings()/spi_set_settings() --- core/esp_spi.c | 28 +++++++++------- core/include/esp/spi.h | 73 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/core/esp_spi.c b/core/esp_spi.c index d91e1b2..c6c0546 100644 --- a/core/esp_spi.c +++ b/core/esp_spi.c @@ -28,6 +28,8 @@ #define _SPI_BUF_SIZE 64 +static bool _minimal_pins[2] = {false, false}; + inline static void _set_pin_function(uint8_t pin, uint32_t function) { iomux_set_function(gpio_to_iomux(pin), function); @@ -59,6 +61,7 @@ bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi return false; } + _minimal_pins[bus] = minimal_pins; SPI(bus).USER0 = SPI_USER0_MOSI | SPI_USER0_CLOCK_IN_EDGE | SPI_USER0_DUPLEX | (minimal_pins ? 0 : (SPI_USER0_CS_HOLD | SPI_USER0_CS_SETUP)); @@ -70,6 +73,15 @@ bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi return true; } +void spi_get_settings(uint8_t bus, spi_settings_t *s) +{ + s->mode = spi_get_mode(bus); + s->freq_divider = spi_get_frequency_div(bus); + s->msb = spi_get_msb(bus); + s->endianness = spi_get_endianness(bus); + s->minimal_pins = _minimal_pins[bus]; +} + void spi_set_mode(uint8_t bus, spi_mode_t mode) { bool cpha = (uint8_t)mode & 1; @@ -161,19 +173,13 @@ inline static uint32_t _swap_words(uint32_t value) return (value << 16) | (value >> 16); } -static void _prepare_buffer(uint8_t bus, size_t len, spi_endianness_t e, spi_word_size_t word_size) +static void _spi_buf_prepare(uint8_t bus, size_t len, spi_endianness_t e, spi_word_size_t word_size) { if (e == SPI_LITTLE_ENDIAN || word_size == SPI_32BIT) return; - if (word_size == SPI_16BIT) - { - if (len % 2) - len ++; - len /= 2; - } - + size_t count = word_size == SPI_16BIT ? (len + 1) / 2 : (len + 3) / 4; uint32_t *data = (uint32_t *)&SPI(bus).W0; - for (size_t i = 0; i < len; i ++) + for (size_t i = 0; i < count; i ++) { data[i] = word_size == SPI_16BIT ? _swap_words(data[i]) @@ -188,12 +194,12 @@ static void _spi_buf_transfer(uint8_t bus, const void *out_data, void *in_data, size_t bytes = len * (uint8_t)word_size; _set_size(bus, bytes); memcpy((void *)&SPI(bus).W0, out_data, bytes); - _prepare_buffer(bus, len, e, word_size); + _spi_buf_prepare(bus, len, e, word_size); _start(bus); _wait(bus); if (in_data) { - _prepare_buffer(bus, len, e, word_size); + _spi_buf_prepare(bus, len, e, word_size); memcpy(in_data, (void *)&SPI(bus).W0, bytes); } } diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h index a2403bf..b3bc629 100644 --- a/core/include/esp/spi.h +++ b/core/include/esp/spi.h @@ -19,7 +19,7 @@ * SPI frequency = 80000000 / divider / count * dvider must be in 1..8192 and count in 1..64 */ -#define SPI_GET_FREQ_DIV(divider, count) (((count) << 16) | (divider)) +#define SPI_GET_FREQ_DIV(divider, count) (((count) << 16) | ((divider) & 0xffff)) /** * Predefinded SPI frequency dividers @@ -59,6 +59,18 @@ typedef enum _spi_word_size_t { SPI_32BIT = 4 ///< 4 bytes } spi_word_size_t; +/** + * SPI bus settings + */ +typedef struct +{ + spi_mode_t mode; ///< Bus mode + uint32_t freq_divider; ///< Bus frequency as a divider. See spi_init() + bool msb; ///< MSB first if true + spi_endianness_t endianness; ///< Bus byte order + bool minimal_pins; ///< Minimal set of pins if true. Spee spi_init() +} spi_settings_t; + /** * \brief Initalize SPI bus * Initalize specified SPI bus and setup appropriate pins: @@ -84,11 +96,47 @@ typedef enum _spi_word_size_t { * \return false when error */ bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi_endianness_t endianness, bool minimal_pins); +/** + * \brief Initalize SPI bus + * spi_init() wrapper. + * Example: + * + * const spi_settings_t my_settings = { + * .mode = SPI_MODE0, + * .freq_divider = SPI_FREQ_DIV_4M, + * .msb = true, + * .endianness = SPI_LITTLE_ENDIAN, + * .minimal_pins = true + * } + * .... + * spi_settings_t old; + * spi_get_settings(1, &old); // save current settings + * //spi_init(1, SPI_MODE0, SPI_FREQ_DIV_4M, true, SPI_LITTLE_ENDIAN, true); // use own settings + * // or + * spi_set_settings(1, &my_settings); + * // some work with spi here + * .... + * spi_set_settings(1, &old); // restore saved settings + * + * \param s Pointer to the settings structure + * \return false when error + */ +static inline bool spi_set_settings(uint8_t bus, const spi_settings_t *s) +{ + return spi_init(bus, s->mode, s->freq_divider, s->msb, s->endianness, s->minimal_pins); +} +/** + * \brief Get current settings of the SPI bus + * See spi_set_settings(). + * \param bus Bus ID: 0 - system, 1 - user + * \param s Pointer to the structure that receives SPI bus settings + */ +void spi_get_settings(uint8_t bus, spi_settings_t *s); /** * \brief Set SPI bus mode * \param bus Bus ID: 0 - system, 1 - user - * \param mode Bus mode. + * \param mode Bus mode */ void spi_set_mode(uint8_t bus, spi_mode_t mode); /** @@ -115,8 +163,24 @@ spi_mode_t spi_get_mode(uint8_t bus); */ void spi_set_frequency_div(uint8_t bus, uint32_t divider); /** - * \brief Get SPI bus frequency - * Note the result is in Hz. + * \brief Get SPI bus frequency as a divider + * Example: + * + * uint32_t old_freq = spi_get_frequency_div(1); + * spi_set_frequency_div(1, SPI_FREQ_DIV_8M); + * ... + * spi_set_frequency_div(1, old_freq); + * + * \param bus Bus ID: 0 - system, 1 - user + * \return SPI frequency, as divider. + */ +inline uint32_t spi_get_frequency_div(uint8_t bus) +{ + return (FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) | + (FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1); +} +/** + * \brief Get SPI bus frequency in Hz * \param bus Bus ID: 0 - system, 1 - user * \return SPI frequency, Hz */ @@ -186,6 +250,7 @@ uint32_t spi_transfer_32(uint8_t bus, uint32_t data); * \brief Transfer buffer of words over SPI * Please note that the buffer size is in words, not in bytes! * Example: + * * const uint16_t out_buf[] = { 0xa0b0, 0xa1b1, 0xa2b2, 0xa3b3 }; * uint16_t in_buf[sizeof(out_buf)]; * spi_init(1, SPI_MODE1, SPI_FREQ_DIV_4M, true, SPI_BIG_ENDIAN, true);