From bd40f75d370cca1933fdae164751c71e197d363b Mon Sep 17 00:00:00 2001 From: UncleRus Date: Fri, 4 Mar 2016 01:10:06 +0500 Subject: [PATCH 01/10] Hardware SPI driver --- core/esp_spi.c | 206 ++++++++++++++++++++++++++++++++++++ core/include/esp/spi.h | 197 ++++++++++++++++++++++++++++++++++ core/include/esp/spi_regs.h | 2 + 3 files changed, 405 insertions(+) create mode 100644 core/esp_spi.c create mode 100644 core/include/esp/spi.h diff --git a/core/esp_spi.c b/core/esp_spi.c new file mode 100644 index 0000000..ac9b0b1 --- /dev/null +++ b/core/esp_spi.c @@ -0,0 +1,206 @@ +/* + * ESP hardware SPI master driver + * + * Part of esp-open-rtos + * Copyright (c) Ruslan V. Uss, 2016 + * BSD Licensed as described in the file LICENSE + */ +#include "esp/spi.h" + +#include "esp/iomux.h" +#include "esp/gpio.h" +#include + +//#include + +#define _SPI0_SCK_GPIO 6 +#define _SPI0_MISO_GPIO 7 +#define _SPI0_MOSI_GPIO 8 +#define _SPI0_HD_GPIO 9 +#define _SPI0_WP_GPIO 10 +#define _SPI0_CS0_GPIO 11 + +#define _SPI1_MISO_GPIO 12 +#define _SPI1_MOSI_GPIO 13 +#define _SPI1_SCK_GPIO 14 +#define _SPI1_CS0_GPIO 15 + +#define _SPI0_FUNC 1 +#define _SPI1_FUNC 2 + +#define _SPI_BUF_SIZE 64 + +inline static void _set_pin_function(uint8_t pin, uint32_t function) +{ + iomux_set_function(gpio_to_iomux(pin), function); +} + +bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi_endianness_t endianness, bool minimal_pins) +{ + switch (bus) + { + case 0: + _set_pin_function(_SPI0_MISO_GPIO, _SPI0_FUNC); + _set_pin_function(_SPI0_MOSI_GPIO, _SPI0_FUNC); + _set_pin_function(_SPI0_SCK_GPIO, _SPI0_FUNC); + if (!minimal_pins) + { + _set_pin_function(_SPI0_HD_GPIO, _SPI0_FUNC); + _set_pin_function(_SPI0_WP_GPIO, _SPI0_FUNC); + _set_pin_function(_SPI0_CS0_GPIO, _SPI0_FUNC); + } + break; + case 1: + _set_pin_function(_SPI1_MISO_GPIO, _SPI1_FUNC); + _set_pin_function(_SPI1_MOSI_GPIO, _SPI1_FUNC); + _set_pin_function(_SPI1_SCK_GPIO, _SPI1_FUNC); + if (!minimal_pins) + _set_pin_function(_SPI1_CS0_GPIO, _SPI1_FUNC); + break; + default: + return false; + } + + 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)); + + spi_set_frequency_div(bus, freq_divider); + spi_set_mode(bus, mode); + spi_set_msb(bus, msb); + spi_set_endianness(bus, endianness); + + return true; +} + +void spi_set_mode(uint8_t bus, spi_mode_t mode) +{ + // CPHA + if ((uint8_t)mode & 1) + SPI(bus).USER0 |= SPI_USER0_CLOCK_OUT_EDGE; + else + SPI(bus).USER0 &= ~SPI_USER0_CLOCK_OUT_EDGE; + + // CPOL - see http://bbs.espressif.com/viewtopic.php?t=342#p5384 + if ((uint8_t)mode & 2) + SPI(bus).PIN |= SPI_PIN_IDLE_EDGE; + else + SPI(bus).PIN &= ~SPI_PIN_IDLE_EDGE; +} + +void spi_set_msb(uint8_t bus, bool msb) +{ + if (msb) + SPI(bus).CTRL0 &= ~(SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER); + else + SPI(bus).CTRL0 |= (SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER); +} + +void spi_set_endianness(uint8_t bus, spi_endianness_t endianness) +{ + if (endianness == SPI_BIG_ENDIAN) + SPI(bus).USER0 |= (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER); + else + SPI(bus).USER0 &= ~(SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER); +} + +void spi_set_frequency_div(uint8_t bus, uint32_t divider) +{ + uint32_t predivider = divider & 0xffff; + uint32_t count = divider >> 16; + if (count > 1 && divider > 1) + { + predivider = predivider > SPI_CLOCK_DIV_PRE_M + 1 ? SPI_CLOCK_DIV_PRE_M + 1 : predivider; + count = count > SPI_CLOCK_COUNT_NUM_M + 1 ? SPI_CLOCK_COUNT_NUM_M + 1 : count; + IOMUX.CONF &= ~(bus == 0 ? IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK : IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK); + SPI(bus).CLOCK = (((predivider - 1) & SPI_CLOCK_DIV_PRE_M) << SPI_CLOCK_DIV_PRE_S) | + (((count - 1) & SPI_CLOCK_COUNT_NUM_M) << SPI_CLOCK_COUNT_NUM_S) | + (((count / 2 - 1) & SPI_CLOCK_COUNT_HIGH_M) << SPI_CLOCK_COUNT_HIGH_S) | + (((count - 1) & SPI_CLOCK_COUNT_LOW_M) << SPI_CLOCK_COUNT_LOW_S); + } + else + { + IOMUX.CONF |= bus == 0 ? IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK : IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK; + SPI(bus).CLOCK = SPI_CLOCK_EQU_SYS_CLOCK; + } +} + +inline static void _set_size(uint8_t bus, uint8_t bytes) +{ + uint16_t bits = ((uint16_t)bytes << 3) - 1; + const uint32_t mask = ~((SPI_USER1_MOSI_BITLEN_M << SPI_USER1_MOSI_BITLEN_S) | + (SPI_USER1_MISO_BITLEN_M << SPI_USER1_MISO_BITLEN_S)); + SPI(bus).USER1 = (SPI(bus).USER1 & mask) | (bits << SPI_USER1_MOSI_BITLEN_S) | + (bits << SPI_USER1_MISO_BITLEN_S); +} + +inline static void _wait(uint8_t bus) +{ + while (SPI(bus).CMD & SPI_CMD_USR) + ; +} + +inline static void _start(uint8_t bus) +{ + SPI(bus).CMD |= SPI_CMD_USR; +} + +inline static uint32_t _reverse_bytes(uint32_t value) +{ + return (value << 24) | ((value << 8) & 0x00ff0000) | ((value >> 8) & 0x0000ff00) | (value >> 24); +} + +static uint32_t _spi_single_transfer (uint8_t bus, uint32_t data, uint8_t len) +{ + _wait(bus); + _set_size(bus, len); + spi_endianness_t e = spi_get_endianness(bus); + SPI(bus).W0 = e == SPI_BIG_ENDIAN ? _reverse_bytes(data) : data; + _start(bus); + _wait(bus); + return e == SPI_BIG_ENDIAN ? _reverse_bytes(SPI(bus).W0) : SPI(bus).W0; +} + +// works properly only with little endian byte order +static void _spi_buf_transfer (uint8_t bus, uint8_t *data, size_t len) +{ + _wait(bus); + _set_size(bus, len); + memcpy((void *)&SPI(bus).W0, data, len); + _start(bus); + _wait(bus); + memcpy(data, (void *)&SPI(bus).W0, len); +} + +uint8_t spi_transfer_8(uint8_t bus, uint8_t data) +{ + return _spi_single_transfer(bus, data, sizeof(data)); +} + +uint16_t spi_transfer_16(uint8_t bus, uint16_t data) +{ + return _spi_single_transfer(bus, data, sizeof(data)); +} + +uint32_t spi_transfer_32(uint8_t bus, uint32_t data) +{ + return _spi_single_transfer(bus, data, sizeof(data)); +} + +void spi_transfer(uint8_t bus, void *data, size_t len) +{ + if (!data || !len) return; + + _wait(bus); + spi_endianness_t e = spi_get_endianness(bus); + spi_set_endianness(bus, SPI_LITTLE_ENDIAN); + + size_t counts = len / _SPI_BUF_SIZE; + for (uint8_t i = 0; i < counts; i++) + _spi_buf_transfer(bus, data + i * _SPI_BUF_SIZE, _SPI_BUF_SIZE); + + uint8_t tail = len % _SPI_BUF_SIZE; + if (tail) + _spi_buf_transfer(bus, data + counts * _SPI_BUF_SIZE, tail); + + spi_set_endianness(bus, e); +} diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h new file mode 100644 index 0000000..6b2b0e2 --- /dev/null +++ b/core/include/esp/spi.h @@ -0,0 +1,197 @@ +/** + * \file Hardware SPI master driver + * + * Part of esp-open-rtos + * + * \copyright Ruslan V. Uss, 2016 + * BSD Licensed as described in the file LICENSE + */ +#ifndef _ESP_SPI_H_ +#define _ESP_SPI_H_ + +#include +#include +#include "esp/spi_regs.h" + +// FIXME Better to define it somewhere else. This is not the CPU frequency! +#define SYSTEM_BUS_FREQ 80000000UL + +/** + * Macro for use with spi_init and spi_set_frequency_div. + * 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)) + +/** + * Predefinded SPI frequency dividers + */ +#define SPI_FREQ_DIV_125K SPI_GET_FREQ_DIV(64, 10) ///< 125kHz +#define SPI_FREQ_DIV_250K SPI_GET_FREQ_DIV(32, 10) ///< 250kHz +#define SPI_FREQ_DIV_500K SPI_GET_FREQ_DIV(16, 10) ///< 500kHz +#define SPI_FREQ_DIV_1M SPI_GET_FREQ_DIV(8, 10) ///< 1MHz +#define SPI_FREQ_DIV_2M SPI_GET_FREQ_DIV(4, 10) ///< 2MHz +#define SPI_FREQ_DIV_4M SPI_GET_FREQ_DIV(2, 10) ///< 4MHz +#define SPI_FREQ_DIV_8M SPI_GET_FREQ_DIV(5, 2) ///< 8MHz +#define SPI_FREQ_DIV_10M SPI_GET_FREQ_DIV(4, 2) ///< 10MHz +#define SPI_FREQ_DIV_20M SPI_GET_FREQ_DIV(2, 2) ///< 20MHz +#define SPI_FREQ_DIV_40M SPI_GET_FREQ_DIV(1, 2) ///< 40MHz +#define SPI_FREQ_DIV_80M SPI_GET_FREQ_DIV(1, 1) ///< 80MHz + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum _spi_mode_t { + SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0 + SPI_MODE1, ///< CPOL = 0, CPHA = 1 + SPI_MODE2, ///< CPOL = 1, CPHA = 0 + SPI_MODE3 ///< CPOL = 1, CPHA = 1 +} spi_mode_t; + +typedef enum _spi_endianness_t { + SPI_LITTLE_ENDIAN, + SPI_BIG_ENDIAN +} spi_endianness_t; + +/** + * \brief Initalize SPI bus + * Initalize specified SPI bus and setup appropriate pins: + * Bus 0: + * - MISO = GPIO 7 + * - MOSI = GPIO 8 + * - SCK = GPIO 6 + * - CS0 = GPIO 11 (if minimal_pins is false) + * - HD = GPIO 9 (if minimal_pins is false) + * - WP = GPIO 10 (if minimal_pins is false) + * Bus 1: + * - MISO = GPIO 12 + * - MOSI = GPIO 13 + * - SCK = GPIO 14 + * - CS0 = GPIO 15 (if minimal_pins is false) + * Note that system flash memory is on the bus 0! + * \param bus Bus ID: 0 - system, 1 - user + * \param mode Bus mode + * \param freq_divider SPI bus frequency divider, use SPI_GET_FREQ_DIV() or predefined value + * \param msb Bit order, MSB first if true + * \param endianness Byte order + * \param minimal_pins If true use the minimal set of pins: MISO, MOSI and SCK. + * \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 Set SPI bus mode + * \param bus Bus ID: 0 - system, 1 - user + * \param mode Bus mode. + */ +void spi_set_mode(uint8_t bus, spi_mode_t mode); +/** + * \brief Get mode of the SPI bus + * \param bus Bus ID: 0 - system, 1 - user + * \return Bus mode + */ +inline spi_mode_t spi_get_mode(uint8_t bus) +{ + return (spi_mode_t)((SPI(bus).PIN & SPI_PIN_IDLE_EDGE ? 2 : 0) | (SPI(bus).USER0 & SPI_USER0_CLOCK_OUT_EDGE ? 1 : 0)); +} + +/** + * \brief Set SPI bus frequency + * Examples: + * + * spi_set_frequency_div(1, SPI_FREQ_DIV_8M); // 8 MHz, predefined value + * ... + * spi_set_frequency_div(1, SPI_GET_FREQ_DIV(8, 10)); // divider = 8, count = 10, + * // frequency = 80000000 Hz / 8 / 10 = 1000000 Hz + * + * \param bus Bus ID: 0 - system, 1 - user + * \param divider Predivider of the system bus frequency (80MHz) in the 2 low + * bytes and period pulses count in the third byte. Please note that + * divider must be be in range 1..8192 and count in range 2..64. Use the + * macro SPI_GET_FREQ_DIV(divider, count) to get the correct parameter value. + */ +void spi_set_frequency_div(uint8_t bus, uint32_t divider); +/** + * \brief Get SPI bus frequency + * Note the result is in Hz. + * \param bus Bus ID: 0 - system, 1 - user + * \return SPI frequency, Hz + */ +inline uint32_t spi_get_frequency_hz(uint8_t bus) +{ + return SYSTEM_BUS_FREQ / + (((SPI(bus).CLOCK >> SPI_CLOCK_DIV_PRE_S) & SPI_CLOCK_DIV_PRE_M) + 1) / + (((SPI(bus).CLOCK >> SPI_CLOCK_COUNT_NUM_S) & SPI_CLOCK_COUNT_NUM_M) + 1); +} + +/** + * \brief Set SPI bus bit order + * \param bus Bus ID: 0 - system, 1 - user + * \param msb Bit order, MSB first if true + */ +void spi_set_msb(uint8_t bus, bool msb); +/** + * \brief Get SPI bus bit order + * \param bus Bus ID: 0 - system, 1 - user + * \return msb Bit order, MSB first if true + */ +inline bool spi_get_msb(uint8_t bus) +{ + return !(SPI(bus).CTRL0 & (SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER)); +} + +/** + * \brief Set SPI bus byte order + * This value is ignored when transferring buffer with spi_transfer() + * \param bus Bus ID: 0 - system, 1 - user + * \param endianness Byte order + */ +void spi_set_endianness(uint8_t bus, spi_endianness_t endianness); +/** + * \brief Get SPI bus byte order + * \param bus Bus ID: 0 - system, 1 - user + * \return endianness Byte order + */ +inline spi_endianness_t spi_get_endianness(uint8_t bus) +{ + return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER) + ? SPI_BIG_ENDIAN + : SPI_LITTLE_ENDIAN; +} + +/** + * \brief Transfer byte over SPI + * \param bus Bus ID: 0 - system, 1 - user + * \param data Byte to send + * \return Received byte + */ +uint8_t spi_transfer_8(uint8_t bus, uint8_t data); +/** + * \brief Transfer word over SPI + * \param bus Bus ID: 0 - system, 1 - user + * \param data Word to send + * \return Received word + */ +uint16_t spi_transfer_16(uint8_t bus, uint16_t data); +/** + * \brief Transfer dword over SPI + * \param bus Bus ID: 0 - system, 1 - user + * \param data dword to send + * \return Received dword + */ +uint32_t spi_transfer_32(uint8_t bus, uint32_t data); +/** + * \brief Transfer buffer over SPI + * \param bus Bus ID: 0 - system, 1 - user + * \param data Data to send. Buffer contents will be replaced with received data + * \param len Buffer size + */ +void spi_transfer(uint8_t bus, void *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP_SPI_H_ */ diff --git a/core/include/esp/spi_regs.h b/core/include/esp/spi_regs.h index dfcf998..37eb113 100644 --- a/core/include/esp/spi_regs.h +++ b/core/include/esp/spi_regs.h @@ -152,6 +152,7 @@ _Static_assert(sizeof(struct SPI_REGS) == 0x100, "SPI_REGS is the wrong size"); #define SPI_USER0_CS_SETUP BIT(5) #define SPI_USER0_CS_HOLD BIT(4) #define SPI_USER0_FLASH_MODE BIT(2) +#define SPI_USER0_DUPLEX BIT(0) /* Details for USER1 register */ @@ -173,6 +174,7 @@ _Static_assert(sizeof(struct SPI_REGS) == 0x100, "SPI_REGS is the wrong size"); /* Details for PIN register */ +#define SPI_PIN_IDLE_EDGE BIT(29) ///< CPOL #define SPI_PIN_CS2_DISABLE BIT(2) #define SPI_PIN_CS1_DISABLE BIT(1) #define SPI_PIN_CS0_DISABLE BIT(0) From 0e3f3bb2ca36766bc3e759fcce5348d8d6073a58 Mon Sep 17 00:00:00 2001 From: UncleRus Date: Sat, 5 Mar 2016 03:07:57 +0500 Subject: [PATCH 02/10] Frequency divider bug --- core/esp_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/esp_spi.c b/core/esp_spi.c index ac9b0b1..56ed196 100644 --- a/core/esp_spi.c +++ b/core/esp_spi.c @@ -107,7 +107,7 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider) { uint32_t predivider = divider & 0xffff; uint32_t count = divider >> 16; - if (count > 1 && divider > 1) + if (count > 1 || divider > 1) { predivider = predivider > SPI_CLOCK_DIV_PRE_M + 1 ? SPI_CLOCK_DIV_PRE_M + 1 : predivider; count = count > SPI_CLOCK_COUNT_NUM_M + 1 ? SPI_CLOCK_COUNT_NUM_M + 1 : count; From b5c2120efce06895fb0dcba53f7b4f2dd67b7231 Mon Sep 17 00:00:00 2001 From: UncleRus Date: Sun, 6 Mar 2016 01:24:59 +0500 Subject: [PATCH 03/10] Removed redeclaration of peripheral bus freq --- core/esp_spi.c | 6 +++--- core/include/esp/spi.h | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/core/esp_spi.c b/core/esp_spi.c index 56ed196..02dae76 100644 --- a/core/esp_spi.c +++ b/core/esp_spi.c @@ -194,13 +194,13 @@ void spi_transfer(uint8_t bus, void *data, size_t len) spi_endianness_t e = spi_get_endianness(bus); spi_set_endianness(bus, SPI_LITTLE_ENDIAN); - size_t counts = len / _SPI_BUF_SIZE; - for (uint8_t i = 0; i < counts; i++) + size_t blocks = len / _SPI_BUF_SIZE; + for (uint8_t i = 0; i < blocks; i++) _spi_buf_transfer(bus, data + i * _SPI_BUF_SIZE, _SPI_BUF_SIZE); uint8_t tail = len % _SPI_BUF_SIZE; if (tail) - _spi_buf_transfer(bus, data + counts * _SPI_BUF_SIZE, tail); + _spi_buf_transfer(bus, data + blocks * _SPI_BUF_SIZE, tail); spi_set_endianness(bus, e); } diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h index 6b2b0e2..55b9d14 100644 --- a/core/include/esp/spi.h +++ b/core/include/esp/spi.h @@ -12,9 +12,7 @@ #include #include #include "esp/spi_regs.h" - -// FIXME Better to define it somewhere else. This is not the CPU frequency! -#define SYSTEM_BUS_FREQ 80000000UL +#include "esp/clocks.h" /** * Macro for use with spi_init and spi_set_frequency_div. @@ -121,7 +119,7 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider); */ inline uint32_t spi_get_frequency_hz(uint8_t bus) { - return SYSTEM_BUS_FREQ / + return APB_CLK_FREQ / (((SPI(bus).CLOCK >> SPI_CLOCK_DIV_PRE_S) & SPI_CLOCK_DIV_PRE_M) + 1) / (((SPI(bus).CLOCK >> SPI_CLOCK_COUNT_NUM_S) & SPI_CLOCK_COUNT_NUM_M) + 1); } From 9dc565ff7cc613363a6ea69d8b7e1e234455bafc Mon Sep 17 00:00:00 2001 From: UncleRus Date: Tue, 8 Mar 2016 04:00:11 +0500 Subject: [PATCH 04/10] Separate send/receive buffers --- core/esp_spi.c | 26 ++++++++++++++++---------- core/include/esp/spi.h | 5 +++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/core/esp_spi.c b/core/esp_spi.c index 02dae76..320716c 100644 --- a/core/esp_spi.c +++ b/core/esp_spi.c @@ -11,8 +11,6 @@ #include "esp/gpio.h" #include -//#include - #define _SPI0_SCK_GPIO 6 #define _SPI0_MISO_GPIO 7 #define _SPI0_MOSI_GPIO 8 @@ -161,14 +159,15 @@ static uint32_t _spi_single_transfer (uint8_t bus, uint32_t data, uint8_t len) } // works properly only with little endian byte order -static void _spi_buf_transfer (uint8_t bus, uint8_t *data, size_t len) +static void _spi_buf_transfer (uint8_t bus, const uint8_t *out_data, uint8_t *in_data, size_t len) { _wait(bus); _set_size(bus, len); - memcpy((void *)&SPI(bus).W0, data, len); + memcpy((void *)&SPI(bus).W0, out_data, len); _start(bus); _wait(bus); - memcpy(data, (void *)&SPI(bus).W0, len); + if (in_data) + memcpy(in_data, (void *)&SPI(bus).W0, len); } uint8_t spi_transfer_8(uint8_t bus, uint8_t data) @@ -186,21 +185,28 @@ uint32_t spi_transfer_32(uint8_t bus, uint32_t data) return _spi_single_transfer(bus, data, sizeof(data)); } -void spi_transfer(uint8_t bus, void *data, size_t len) +void spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len) { - if (!data || !len) return; + if (!out_data || !len) return; _wait(bus); spi_endianness_t e = spi_get_endianness(bus); spi_set_endianness(bus, SPI_LITTLE_ENDIAN); size_t blocks = len / _SPI_BUF_SIZE; - for (uint8_t i = 0; i < blocks; i++) - _spi_buf_transfer(bus, data + i * _SPI_BUF_SIZE, _SPI_BUF_SIZE); + for (size_t i = 0; i < blocks; i++) + { + size_t offset = i * _SPI_BUF_SIZE; + _spi_buf_transfer(bus, (const uint8_t *)out_data + offset, + in_data ? (uint8_t *)in_data + offset : NULL, _SPI_BUF_SIZE); + } uint8_t tail = len % _SPI_BUF_SIZE; if (tail) - _spi_buf_transfer(bus, data + blocks * _SPI_BUF_SIZE, tail); + { + _spi_buf_transfer(bus, (const uint8_t *)out_data + blocks * _SPI_BUF_SIZE, + in_data ? (uint8_t *)in_data + blocks * _SPI_BUF_SIZE : NULL, tail); + } spi_set_endianness(bus, e); } diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h index 55b9d14..5de966c 100644 --- a/core/include/esp/spi.h +++ b/core/include/esp/spi.h @@ -183,10 +183,11 @@ uint32_t spi_transfer_32(uint8_t bus, uint32_t data); /** * \brief Transfer buffer over SPI * \param bus Bus ID: 0 - system, 1 - user - * \param data Data to send. Buffer contents will be replaced with received data + * \param out_data Data to send. + * \param in_data Receive buffer. If NULL, received data will be lost. * \param len Buffer size */ -void spi_transfer(uint8_t bus, void *data, size_t len); +void spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len); #ifdef __cplusplus } From 41e28717f9c68c0102ba8ac1f1081a3788968c17 Mon Sep 17 00:00:00 2001 From: Kazuyoshi Kato Date: Fri, 4 Mar 2016 22:18:29 -0800 Subject: [PATCH 05/10] howmyssl.com's root certificate has been updated Now the site is using Let's Encrypt's root certificate. --- examples/http_get_mbedtls/cert.c | 52 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/examples/http_get_mbedtls/cert.c b/examples/http_get_mbedtls/cert.c index a0050a6..f8ab11f 100644 --- a/examples/http_get_mbedtls/cert.c +++ b/examples/http_get_mbedtls/cert.c @@ -10,30 +10,34 @@ #include const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n" -"MIIEWTCCA0GgAwIBAgIDAjpjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\r\n" -"MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\r\n" -"YWwgQ0EwHhcNMTIwODI3MjA0MDQwWhcNMjIwNTIwMjA0MDQwWjBEMQswCQYDVQQG\r\n" -"EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg\r\n" -"U1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5J/lP\r\n" -"2Pa3FT+Pzc7WjRxr/X/aVCFOA9jK0HJSFbjJgltYeYT/JHJv8ml/vJbZmnrDPqnP\r\n" -"UCITDoYZ2+hJ74vm1kfy/XNFCK6PrF62+J589xD/kkNm7xzU7qFGiBGJSXl6Jc5L\r\n" -"avDXHHYaKTzJ5P0ehdzgMWUFRxasCgdLLnBeawanazpsrwUSxLIRJdY+lynwg2xX\r\n" -"HNil78zs/dYS8T/bQLSuDxjTxa9Akl0HXk7+Yhc3iemLdCai7bgK52wVWzWQct3Y\r\n" -"TSHUQCNcj+6AMRaraFX0DjtU6QRN8MxOgV7pb1JpTr6mFm1C9VH/4AtWPJhPc48O\r\n" -"bxoj8cnI2d+87FLXAgMBAAGjggFUMIIBUDAfBgNVHSMEGDAWgBTAephojYn7qwVk\r\n" -"DBF9qn1luMrMTjAdBgNVHQ4EFgQUEUrQcznVW2kIXLo9v2SaqIscVbwwEgYDVR0T\r\n" -"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2gK4Yp\r\n" -"aHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwNAYIKwYB\r\n" -"BQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nZW90cnVzdC5jb20w\r\n" -"TAYDVR0gBEUwQzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93\r\n" -"d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwKgYDVR0RBCMwIaQfMB0xGzAZ\r\n" -"BgNVBAMTElZlcmlTaWduTVBLSS0yLTI1NDANBgkqhkiG9w0BAQUFAAOCAQEAPOU9\r\n" -"WhuiNyrjRs82lhg8e/GExVeGd0CdNfAS8HgY+yKk3phLeIHmTYbjkQ9C47ncoNb/\r\n" -"qfixeZeZ0cNsQqWSlOBdDDMYJckrlVPg5akMfUf+f1ExRF73Kh41opQy98nuwLbG\r\n" -"mqzemSFqI6A4ZO6jxIhzMjtQzr+t03UepvTp+UJrYLLdRf1dVwjOLVDmEjIWE4ry\r\n" -"lKKbR6iGf9mY5ffldnRk2JG8hBYo2CVEMH6C2Kyx5MDkFWzbtiQnAioBEoW6MYhY\r\n" -"R3TjuNJkpsMyWS4pS0XxW4lJLoKaxhgVRNAuZAEVaDj59vlmAwxVG52/AECu8Egn\r\n" -"TOCAXi25KhV6vGb4NQ==\r\n" +"MIIFNzCCBB+gAwIBAgISAfl7TPw6Mf/F6VCBc5eaHA7kMA0GCSqGSIb3DQEBCwUA\r\n" +"MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD\r\n" +"ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMTAeFw0xNTEyMzAwNzQ0MDBaFw0x\r\n" +"NjAzMjkwNzQ0MDBaMBwxGjAYBgNVBAMTEXd3dy5ob3dzbXlzc2wuY29tMIIBIjAN\r\n" +"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKF7WzSrDPinQhd9mVfoW5u46/TC\r\n" +"fbYKR3MEryetUSeQuwuXFj2xO6+a/JQ99UC3Eq+9s8uFdx1zFFS6qfW+HXBwGWVf\r\n" +"ajKEyIcXUJZCRn7aWpTWZq6cuv4bZSv1QklGViQs8UCZifcN0A/mYrH7zHG2WDn2\r\n" +"fxNgA+nJBqbZPr1gP9hqGFCnX+dPR5WxtC9+Dv9Sx+wiOWVz/obVTCygdqqcpa5I\r\n" +"3/U9REVgO2VfT+xMty6NZTMCjTJ+GXZuB/BrMe9+ZmgWk0grJyqdrCxOCyK6B4g+\r\n" +"Fvs8WFRNTHdQnP3/NT5hPtreZ3nuY2YY7RbGFwUaBcvJwbbIqalpiQ1X7QIDAQAB\r\n" +"o4ICQzCCAj8wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr\r\n" +"BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQMAe087CSp3PjgqyIYyWTR\r\n" +"a2aMnTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBwBggrBgEFBQcB\r\n" +"AQRkMGIwLwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmludC14MS5sZXRzZW5jcnlw\r\n" +"dC5vcmcvMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDEubGV0c2VuY3J5\r\n" +"cHQub3JnLzBNBgNVHREERjBEgg1ob3dzbXlzc2wuY29tgg1ob3dzbXl0bHMuY29t\r\n" +"ghF3d3cuaG93c215dGxzLmNvbYIRd3d3Lmhvd3NteXNzbC5jb20wgf4GA1UdIASB\r\n" +"9jCB8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpo\r\n" +"dHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlz\r\n" +"IENlcnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcg\r\n" +"UGFydGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmlj\r\n" +"YXRlIFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBv\r\n" +"c2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEALB2QrIrcxxFr81b6khy9LwVBpthL\r\n" +"2LSs0xWhA09gxHmPnVrqim3Wa9HFYRApSqtTQWSx58TO+MERXQ7eDX50k53oem+Q\r\n" +"gXn90HVDkR0jbV1CsAD5qL00kKOofAyGkUhFlO2hcRU0CIj7Z9HZB8xpYdZWLUSZ\r\n" +"BpgiXdf/YYRIwgx29GRQjhfl/H30fHyawY5SvquJuvAeEr7lxqAmEEg3a7J6ibHL\r\n" +"90zf5KMkXGyVsXqxLqrEXQKgTvpUMeP5iYAxE45R5GNmYS2jajyTi4XkWw4nEu4Q\r\n" +"dMTLuwxGz87KOwSSFOLU903hO3IIPvFIIMY6aVK2kAgQPNIqk9nFurTF9A==\r\n" "-----END CERTIFICATE-----\r\n"; From b4554b5806a0af098ee60175ba7e3cf9607f2f35 Mon Sep 17 00:00:00 2001 From: UncleRus Date: Sun, 13 Mar 2016 01:59:52 +0500 Subject: [PATCH 06/10] SPI mode bug fixed, SPI endianness bugs fixed, new spi_transfer() --- core/esp_spi.c | 117 ++++++++++++++++++++++++++--------------- core/include/esp/spi.h | 42 +++++++++------ 2 files changed, 102 insertions(+), 57 deletions(-) diff --git a/core/esp_spi.c b/core/esp_spi.c index 320716c..d91e1b2 100644 --- a/core/esp_spi.c +++ b/core/esp_spi.c @@ -72,19 +72,32 @@ bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi void spi_set_mode(uint8_t bus, spi_mode_t mode) { + bool cpha = (uint8_t)mode & 1; + bool cpol = (uint8_t)mode & 2; + if (cpol) + cpha = !cpha; // CPHA must be inverted when CPOL = 1, I have no idea why + // CPHA - if ((uint8_t)mode & 1) + if (cpha) SPI(bus).USER0 |= SPI_USER0_CLOCK_OUT_EDGE; else SPI(bus).USER0 &= ~SPI_USER0_CLOCK_OUT_EDGE; // CPOL - see http://bbs.espressif.com/viewtopic.php?t=342#p5384 - if ((uint8_t)mode & 2) + if (cpol) SPI(bus).PIN |= SPI_PIN_IDLE_EDGE; else SPI(bus).PIN &= ~SPI_PIN_IDLE_EDGE; } +spi_mode_t spi_get_mode(uint8_t bus) +{ + uint8_t cpha = SPI(bus).USER0 & SPI_USER0_CLOCK_OUT_EDGE ? 1 : 0; + uint8_t cpol = SPI(bus).PIN & SPI_PIN_IDLE_EDGE ? 2 : 0; + + return (spi_mode_t)(cpol | (cpol ? 1 - cpha : cpha)); // see spi_set_mode +} + void spi_set_msb(uint8_t bus, bool msb) { if (msb) @@ -103,17 +116,15 @@ void spi_set_endianness(uint8_t bus, spi_endianness_t endianness) void spi_set_frequency_div(uint8_t bus, uint32_t divider) { - uint32_t predivider = divider & 0xffff; - uint32_t count = divider >> 16; - if (count > 1 || divider > 1) + uint32_t predivider = (divider & 0xffff) - 1; + uint32_t count = (divider >> 16) - 1; + if (count || predivider) { - predivider = predivider > SPI_CLOCK_DIV_PRE_M + 1 ? SPI_CLOCK_DIV_PRE_M + 1 : predivider; - count = count > SPI_CLOCK_COUNT_NUM_M + 1 ? SPI_CLOCK_COUNT_NUM_M + 1 : count; IOMUX.CONF &= ~(bus == 0 ? IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK : IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK); - SPI(bus).CLOCK = (((predivider - 1) & SPI_CLOCK_DIV_PRE_M) << SPI_CLOCK_DIV_PRE_S) | - (((count - 1) & SPI_CLOCK_COUNT_NUM_M) << SPI_CLOCK_COUNT_NUM_S) | - (((count / 2 - 1) & SPI_CLOCK_COUNT_HIGH_M) << SPI_CLOCK_COUNT_HIGH_S) | - (((count - 1) & SPI_CLOCK_COUNT_LOW_M) << SPI_CLOCK_COUNT_LOW_S); + SPI(bus).CLOCK = VAL2FIELD_M(SPI_CLOCK_DIV_PRE, predivider) | + VAL2FIELD_M(SPI_CLOCK_COUNT_NUM, count) | + VAL2FIELD_M(SPI_CLOCK_COUNT_HIGH, count / 2) | + VAL2FIELD_M(SPI_CLOCK_COUNT_LOW, count); } else { @@ -124,11 +135,9 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider) inline static void _set_size(uint8_t bus, uint8_t bytes) { - uint16_t bits = ((uint16_t)bytes << 3) - 1; - const uint32_t mask = ~((SPI_USER1_MOSI_BITLEN_M << SPI_USER1_MOSI_BITLEN_S) | - (SPI_USER1_MISO_BITLEN_M << SPI_USER1_MISO_BITLEN_S)); - SPI(bus).USER1 = (SPI(bus).USER1 & mask) | (bits << SPI_USER1_MOSI_BITLEN_S) | - (bits << SPI_USER1_MISO_BITLEN_S); + uint32_t bits = ((uint32_t)bytes << 3) - 1; + SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_MISO_BITLEN, bits); + SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_MOSI_BITLEN, bits); } inline static void _wait(uint8_t bus) @@ -142,71 +151,95 @@ inline static void _start(uint8_t bus) SPI(bus).CMD |= SPI_CMD_USR; } -inline static uint32_t _reverse_bytes(uint32_t value) +inline static uint32_t _swap_bytes(uint32_t value) { return (value << 24) | ((value << 8) & 0x00ff0000) | ((value >> 8) & 0x0000ff00) | (value >> 24); } -static uint32_t _spi_single_transfer (uint8_t bus, uint32_t data, uint8_t len) +inline static uint32_t _swap_words(uint32_t value) { - _wait(bus); - _set_size(bus, len); - spi_endianness_t e = spi_get_endianness(bus); - SPI(bus).W0 = e == SPI_BIG_ENDIAN ? _reverse_bytes(data) : data; - _start(bus); - _wait(bus); - return e == SPI_BIG_ENDIAN ? _reverse_bytes(SPI(bus).W0) : SPI(bus).W0; + return (value << 16) | (value >> 16); } -// works properly only with little endian byte order -static void _spi_buf_transfer (uint8_t bus, const uint8_t *out_data, uint8_t *in_data, size_t len) +static void _prepare_buffer(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; + } + + uint32_t *data = (uint32_t *)&SPI(bus).W0; + for (size_t i = 0; i < len; i ++) + { + data[i] = word_size == SPI_16BIT + ? _swap_words(data[i]) + : _swap_bytes(data[i]); + } +} + +static void _spi_buf_transfer(uint8_t bus, const void *out_data, void *in_data, + size_t len, spi_endianness_t e, spi_word_size_t word_size) { _wait(bus); - _set_size(bus, len); - memcpy((void *)&SPI(bus).W0, out_data, len); + 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); _start(bus); _wait(bus); if (in_data) - memcpy(in_data, (void *)&SPI(bus).W0, len); + { + _prepare_buffer(bus, len, e, word_size); + memcpy(in_data, (void *)&SPI(bus).W0, bytes); + } } uint8_t spi_transfer_8(uint8_t bus, uint8_t data) { - return _spi_single_transfer(bus, data, sizeof(data)); + uint8_t res; + _spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_8BIT); + return res; } uint16_t spi_transfer_16(uint8_t bus, uint16_t data) { - return _spi_single_transfer(bus, data, sizeof(data)); + uint16_t res; + _spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_16BIT); + return res; } uint32_t spi_transfer_32(uint8_t bus, uint32_t data) { - return _spi_single_transfer(bus, data, sizeof(data)); + uint32_t res; + _spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_32BIT); + return res; } -void spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len) +size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len, spi_word_size_t word_size) { - if (!out_data || !len) return; + if (!out_data || !len) return 0; - _wait(bus); spi_endianness_t e = spi_get_endianness(bus); - spi_set_endianness(bus, SPI_LITTLE_ENDIAN); + uint8_t buf_size = _SPI_BUF_SIZE / (uint8_t)word_size; - size_t blocks = len / _SPI_BUF_SIZE; + size_t blocks = len / buf_size; for (size_t i = 0; i < blocks; i++) { size_t offset = i * _SPI_BUF_SIZE; _spi_buf_transfer(bus, (const uint8_t *)out_data + offset, - in_data ? (uint8_t *)in_data + offset : NULL, _SPI_BUF_SIZE); + in_data ? (uint8_t *)in_data + offset : NULL, buf_size, e, word_size); } - uint8_t tail = len % _SPI_BUF_SIZE; + uint8_t tail = len % buf_size; if (tail) { _spi_buf_transfer(bus, (const uint8_t *)out_data + blocks * _SPI_BUF_SIZE, - in_data ? (uint8_t *)in_data + blocks * _SPI_BUF_SIZE : NULL, tail); + in_data ? (uint8_t *)in_data + blocks * _SPI_BUF_SIZE : NULL, tail, e, word_size); } - spi_set_endianness(bus, e); + return len; } diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h index 5de966c..ec86e9d 100644 --- a/core/include/esp/spi.h +++ b/core/include/esp/spi.h @@ -49,10 +49,16 @@ typedef enum _spi_mode_t { } spi_mode_t; typedef enum _spi_endianness_t { - SPI_LITTLE_ENDIAN, + SPI_LITTLE_ENDIAN = 0, SPI_BIG_ENDIAN } spi_endianness_t; +typedef enum _spi_word_size_t { + SPI_8BIT = 1, ///< 1 byte, no endian swapping + SPI_16BIT = 2, ///< 2 bytes, swap 16-bit values in SPI_BIG_ENDIAN mode + SPI_32BIT = 4 ///< 4 bytes, swap 32-bit values in SPI_BIG_ENDIAN mode +} spi_word_size_t; + /** * \brief Initalize SPI bus * Initalize specified SPI bus and setup appropriate pins: @@ -90,10 +96,7 @@ void spi_set_mode(uint8_t bus, spi_mode_t mode); * \param bus Bus ID: 0 - system, 1 - user * \return Bus mode */ -inline spi_mode_t spi_get_mode(uint8_t bus) -{ - return (spi_mode_t)((SPI(bus).PIN & SPI_PIN_IDLE_EDGE ? 2 : 0) | (SPI(bus).USER0 & SPI_USER0_CLOCK_OUT_EDGE ? 1 : 0)); -} +spi_mode_t spi_get_mode(uint8_t bus); /** * \brief Set SPI bus frequency @@ -120,8 +123,8 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider); inline uint32_t spi_get_frequency_hz(uint8_t bus) { return APB_CLK_FREQ / - (((SPI(bus).CLOCK >> SPI_CLOCK_DIV_PRE_S) & SPI_CLOCK_DIV_PRE_M) + 1) / - (((SPI(bus).CLOCK >> SPI_CLOCK_COUNT_NUM_S) & SPI_CLOCK_COUNT_NUM_M) + 1); + (FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) / + (FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1); } /** @@ -155,39 +158,48 @@ void spi_set_endianness(uint8_t bus, spi_endianness_t endianness); inline spi_endianness_t spi_get_endianness(uint8_t bus) { return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER) - ? SPI_BIG_ENDIAN - : SPI_LITTLE_ENDIAN; + ? SPI_BIG_ENDIAN + : SPI_LITTLE_ENDIAN; } /** - * \brief Transfer byte over SPI + * \brief Transfer 8 bits over SPI * \param bus Bus ID: 0 - system, 1 - user * \param data Byte to send * \return Received byte */ uint8_t spi_transfer_8(uint8_t bus, uint8_t data); /** - * \brief Transfer word over SPI + * \brief Transfer 16 bits over SPI * \param bus Bus ID: 0 - system, 1 - user * \param data Word to send * \return Received word */ uint16_t spi_transfer_16(uint8_t bus, uint16_t data); /** - * \brief Transfer dword over SPI + * \brief Transfer 32 bits over SPI * \param bus Bus ID: 0 - system, 1 - user * \param data dword to send * \return Received dword */ uint32_t spi_transfer_32(uint8_t bus, uint32_t data); /** - * \brief Transfer buffer over SPI + * \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[4] = { 0xa0b0, 0xa1b1, 0xa2b2, 0xa3b3 }; + * uint16_t in_buf[4]; + * spi_init(1, SPI_MODE1, SPI_FREQ_DIV_4M, true, SPI_BIG_ENDIAN, true); + * spi_transfer(1, buf, in_buf, 4, SPI_16BIT); // len = 4 words = 8 bytes + * * \param bus Bus ID: 0 - system, 1 - user * \param out_data Data to send. * \param in_data Receive buffer. If NULL, received data will be lost. - * \param len Buffer size + * \param len Buffer size in words + * \param word_size Size of the word + * \return Transmitted/received words count */ -void spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len); +size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len, spi_word_size_t word_size); #ifdef __cplusplus } From 22ceb08b49b55ab2da408982a2aa062a7796e262 Mon Sep 17 00:00:00 2001 From: UncleRus Date: Sun, 13 Mar 2016 02:25:15 +0500 Subject: [PATCH 07/10] Tabs to spaces. Again. --- core/include/esp/spi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h index ec86e9d..0b8e053 100644 --- a/core/include/esp/spi.h +++ b/core/include/esp/spi.h @@ -123,8 +123,8 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider); inline uint32_t spi_get_frequency_hz(uint8_t bus) { return APB_CLK_FREQ / - (FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) / - (FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1); + (FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) / + (FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1); } /** From b0fb8736a8a64c2f38f7eef875c965a1823f1011 Mon Sep 17 00:00:00 2001 From: UncleRus Date: Sun, 13 Mar 2016 16:48:05 +0500 Subject: [PATCH 08/10] Comments updated --- core/include/esp/spi.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h index 0b8e053..a2403bf 100644 --- a/core/include/esp/spi.h +++ b/core/include/esp/spi.h @@ -54,9 +54,9 @@ typedef enum _spi_endianness_t { } spi_endianness_t; typedef enum _spi_word_size_t { - SPI_8BIT = 1, ///< 1 byte, no endian swapping - SPI_16BIT = 2, ///< 2 bytes, swap 16-bit values in SPI_BIG_ENDIAN mode - SPI_32BIT = 4 ///< 4 bytes, swap 32-bit values in SPI_BIG_ENDIAN mode + SPI_8BIT = 1, ///< 1 byte + SPI_16BIT = 2, ///< 2 bytes + SPI_32BIT = 4 ///< 4 bytes } spi_word_size_t; /** @@ -145,7 +145,6 @@ inline bool spi_get_msb(uint8_t bus) /** * \brief Set SPI bus byte order - * This value is ignored when transferring buffer with spi_transfer() * \param bus Bus ID: 0 - system, 1 - user * \param endianness Byte order */ @@ -187,10 +186,10 @@ 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[4] = { 0xa0b0, 0xa1b1, 0xa2b2, 0xa3b3 }; - * uint16_t in_buf[4]; + * 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); - * spi_transfer(1, buf, in_buf, 4, SPI_16BIT); // len = 4 words = 8 bytes + * spi_transfer(1, out_buf, in_buf, sizeof(out_buf), SPI_16BIT); // len = 4 words * 2 bytes = 8 bytes * * \param bus Bus ID: 0 - system, 1 - user * \param out_data Data to send. From 42018f03152bef6f8f5eb263679e85d829c0a4e7 Mon Sep 17 00:00:00 2001 From: UncleRus Date: Fri, 18 Mar 2016 03:27:36 +0500 Subject: [PATCH 09/10] 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); From 3a62a0af0c882b6ba60661d197dd46fe24e8b2f8 Mon Sep 17 00:00:00 2001 From: UncleRus Date: Tue, 22 Mar 2016 01:51:50 +0500 Subject: [PATCH 10/10] esp/gpio.h c++ compatibility --- core/include/esp/gpio.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/include/esp/gpio.h b/core/include/esp/gpio.h index 01fe144..c6fa42f 100644 --- a/core/include/esp/gpio.h +++ b/core/include/esp/gpio.h @@ -13,6 +13,10 @@ #include "esp/iomux.h" #include "esp/interrupts.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { GPIO_INPUT, GPIO_OUTPUT, /* "Standard" push-pull output */ @@ -139,4 +143,8 @@ static inline gpio_inttype_t gpio_get_interrupt(const uint8_t gpio_num) return (gpio_inttype_t)FIELD2VAL(GPIO_CONF_INTTYPE, GPIO.CONF[gpio_num]); } +#ifdef __cplusplus +} +#endif + #endif