Merge pull request #117 from UncleRus/core/spi_bugfix
SPI big endian swap bug fix, spi_get_settings()/spi_set_settings()
This commit is contained in:
		
						commit
						cf3f811af1
					
				
					 2 changed files with 86 additions and 15 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue