esp-open-rtos/core/include/esp/spi.h
2016-03-13 02:25:15 +05:00

208 lines
6.4 KiB
C

/**
* \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 <stdbool.h>
#include <stdint.h>
#include "esp/spi_regs.h"
#include "esp/clocks.h"
/**
* 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 = 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:
* 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
*/
spi_mode_t spi_get_mode(uint8_t bus);
/**
* \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 APB_CLK_FREQ /
(FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 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 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 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 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 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 in words
* \param word_size Size of the word
* \return Transmitted/received words count
*/
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
}
#endif
#endif /* _ESP_SPI_H_ */