SPI mode bug fixed, SPI endianness bugs fixed, new spi_transfer()
This commit is contained in:
parent
9dc565ff7c
commit
b4554b5806
2 changed files with 102 additions and 57 deletions
117
core/esp_spi.c
117
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)
|
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
|
// CPHA
|
||||||
if ((uint8_t)mode & 1)
|
if (cpha)
|
||||||
SPI(bus).USER0 |= SPI_USER0_CLOCK_OUT_EDGE;
|
SPI(bus).USER0 |= SPI_USER0_CLOCK_OUT_EDGE;
|
||||||
else
|
else
|
||||||
SPI(bus).USER0 &= ~SPI_USER0_CLOCK_OUT_EDGE;
|
SPI(bus).USER0 &= ~SPI_USER0_CLOCK_OUT_EDGE;
|
||||||
|
|
||||||
// CPOL - see http://bbs.espressif.com/viewtopic.php?t=342#p5384
|
// 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;
|
SPI(bus).PIN |= SPI_PIN_IDLE_EDGE;
|
||||||
else
|
else
|
||||||
SPI(bus).PIN &= ~SPI_PIN_IDLE_EDGE;
|
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)
|
void spi_set_msb(uint8_t bus, bool msb)
|
||||||
{
|
{
|
||||||
if (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)
|
void spi_set_frequency_div(uint8_t bus, uint32_t divider)
|
||||||
{
|
{
|
||||||
uint32_t predivider = divider & 0xffff;
|
uint32_t predivider = (divider & 0xffff) - 1;
|
||||||
uint32_t count = divider >> 16;
|
uint32_t count = (divider >> 16) - 1;
|
||||||
if (count > 1 || divider > 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);
|
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) |
|
SPI(bus).CLOCK = VAL2FIELD_M(SPI_CLOCK_DIV_PRE, predivider) |
|
||||||
(((count - 1) & SPI_CLOCK_COUNT_NUM_M) << SPI_CLOCK_COUNT_NUM_S) |
|
VAL2FIELD_M(SPI_CLOCK_COUNT_NUM, count) |
|
||||||
(((count / 2 - 1) & SPI_CLOCK_COUNT_HIGH_M) << SPI_CLOCK_COUNT_HIGH_S) |
|
VAL2FIELD_M(SPI_CLOCK_COUNT_HIGH, count / 2) |
|
||||||
(((count - 1) & SPI_CLOCK_COUNT_LOW_M) << SPI_CLOCK_COUNT_LOW_S);
|
VAL2FIELD_M(SPI_CLOCK_COUNT_LOW, count);
|
||||||
}
|
}
|
||||||
else
|
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)
|
inline static void _set_size(uint8_t bus, uint8_t bytes)
|
||||||
{
|
{
|
||||||
uint16_t bits = ((uint16_t)bytes << 3) - 1;
|
uint32_t bits = ((uint32_t)bytes << 3) - 1;
|
||||||
const uint32_t mask = ~((SPI_USER1_MOSI_BITLEN_M << SPI_USER1_MOSI_BITLEN_S) |
|
SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_MISO_BITLEN, bits);
|
||||||
(SPI_USER1_MISO_BITLEN_M << SPI_USER1_MISO_BITLEN_S));
|
SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_MOSI_BITLEN, bits);
|
||||||
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)
|
inline static void _wait(uint8_t bus)
|
||||||
|
@ -142,71 +151,95 @@ inline static void _start(uint8_t bus)
|
||||||
SPI(bus).CMD |= SPI_CMD_USR;
|
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);
|
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);
|
return (value << 16) | (value >> 16);
|
||||||
_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 _prepare_buffer(uint8_t bus, size_t len, spi_endianness_t e, spi_word_size_t word_size)
|
||||||
static void _spi_buf_transfer (uint8_t bus, const uint8_t *out_data, uint8_t *in_data, size_t len)
|
{
|
||||||
|
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);
|
_wait(bus);
|
||||||
_set_size(bus, len);
|
size_t bytes = len * (uint8_t)word_size;
|
||||||
memcpy((void *)&SPI(bus).W0, out_data, len);
|
_set_size(bus, bytes);
|
||||||
|
memcpy((void *)&SPI(bus).W0, out_data, bytes);
|
||||||
|
_prepare_buffer(bus, len, e, word_size);
|
||||||
_start(bus);
|
_start(bus);
|
||||||
_wait(bus);
|
_wait(bus);
|
||||||
if (in_data)
|
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)
|
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)
|
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)
|
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_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++)
|
for (size_t i = 0; i < blocks; i++)
|
||||||
{
|
{
|
||||||
size_t offset = i * _SPI_BUF_SIZE;
|
size_t offset = i * _SPI_BUF_SIZE;
|
||||||
_spi_buf_transfer(bus, (const uint8_t *)out_data + offset,
|
_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)
|
if (tail)
|
||||||
{
|
{
|
||||||
_spi_buf_transfer(bus, (const uint8_t *)out_data + blocks * _SPI_BUF_SIZE,
|
_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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,16 @@ typedef enum _spi_mode_t {
|
||||||
} spi_mode_t;
|
} spi_mode_t;
|
||||||
|
|
||||||
typedef enum _spi_endianness_t {
|
typedef enum _spi_endianness_t {
|
||||||
SPI_LITTLE_ENDIAN,
|
SPI_LITTLE_ENDIAN = 0,
|
||||||
SPI_BIG_ENDIAN
|
SPI_BIG_ENDIAN
|
||||||
} 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_word_size_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Initalize SPI bus
|
* \brief Initalize SPI bus
|
||||||
* Initalize specified SPI bus and setup appropriate pins:
|
* 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
|
* \param bus Bus ID: 0 - system, 1 - user
|
||||||
* \return Bus mode
|
* \return Bus mode
|
||||||
*/
|
*/
|
||||||
inline spi_mode_t spi_get_mode(uint8_t bus)
|
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
|
* \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)
|
inline uint32_t spi_get_frequency_hz(uint8_t bus)
|
||||||
{
|
{
|
||||||
return APB_CLK_FREQ /
|
return APB_CLK_FREQ /
|
||||||
(((SPI(bus).CLOCK >> SPI_CLOCK_DIV_PRE_S) & SPI_CLOCK_DIV_PRE_M) + 1) /
|
(FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
|
||||||
(((SPI(bus).CLOCK >> SPI_CLOCK_COUNT_NUM_S) & SPI_CLOCK_COUNT_NUM_M) + 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)
|
inline spi_endianness_t spi_get_endianness(uint8_t bus)
|
||||||
{
|
{
|
||||||
return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER)
|
return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER)
|
||||||
? SPI_BIG_ENDIAN
|
? SPI_BIG_ENDIAN
|
||||||
: SPI_LITTLE_ENDIAN;
|
: SPI_LITTLE_ENDIAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Transfer byte over SPI
|
* \brief Transfer 8 bits over SPI
|
||||||
* \param bus Bus ID: 0 - system, 1 - user
|
* \param bus Bus ID: 0 - system, 1 - user
|
||||||
* \param data Byte to send
|
* \param data Byte to send
|
||||||
* \return Received byte
|
* \return Received byte
|
||||||
*/
|
*/
|
||||||
uint8_t spi_transfer_8(uint8_t bus, uint8_t data);
|
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 bus Bus ID: 0 - system, 1 - user
|
||||||
* \param data Word to send
|
* \param data Word to send
|
||||||
* \return Received word
|
* \return Received word
|
||||||
*/
|
*/
|
||||||
uint16_t spi_transfer_16(uint8_t bus, uint16_t data);
|
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 bus Bus ID: 0 - system, 1 - user
|
||||||
* \param data dword to send
|
* \param data dword to send
|
||||||
* \return Received dword
|
* \return Received dword
|
||||||
*/
|
*/
|
||||||
uint32_t spi_transfer_32(uint8_t bus, uint32_t data);
|
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 bus Bus ID: 0 - system, 1 - user
|
||||||
* \param out_data Data to send.
|
* \param out_data Data to send.
|
||||||
* \param in_data Receive buffer. If NULL, received data will be lost.
|
* \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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue