/* * Copyright (C) 2017 Hamburg University of Applied Sciences * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup drivers_soft_spi * @{ * * @file * @brief Software SPI implementation * * @author Markus Blechschmidt * @author Peter Kietzmann */ #include #include #include "mutex.h" #include "periph/gpio.h" #include "xtimer.h" #include "soft_spi.h" #include "soft_spi_params.h" #define ENABLE_DEBUG 0 #include "debug.h" #define READ_PADDING_BYTE (0x00) /** * @brief Allocate one lock per SPI device */ static mutex_t locks[SOFT_SPI_NUMOF]; static inline bool soft_spi_bus_is_valid(soft_spi_t bus) { unsigned int soft_spi_num = (unsigned int) bus; if (SOFT_SPI_NUMOF <= soft_spi_num) { return false; } return true; } void soft_spi_init(soft_spi_t bus) { DEBUG("Soft SPI init\n"); assert(soft_spi_bus_is_valid(bus)); /* initialize device lock */ mutex_init(&locks[bus]); soft_spi_init_pins(bus); } void soft_spi_init_pins(soft_spi_t bus) { DEBUG("Soft SPI soft_spi_init_pins\n"); assert(soft_spi_bus_is_valid(bus)); /* check that miso is not mosi is not clk*/ assert(!gpio_is_equal(soft_spi_config[bus].mosi_pin, soft_spi_config[bus].miso_pin)); assert(!gpio_is_equal(soft_spi_config[bus].mosi_pin, soft_spi_config[bus].clk_pin)); assert(!gpio_is_equal(soft_spi_config[bus].miso_pin, soft_spi_config[bus].clk_pin)); /* mandatory pins */ assert(gpio_is_valid(soft_spi_config[bus].mosi_pin) || gpio_is_valid(soft_spi_config[bus].miso_pin)); assert(gpio_is_valid(soft_spi_config[bus].clk_pin)); /* initialize clock pin */ gpio_init(soft_spi_config[bus].clk_pin, GPIO_OUT); /* initialize optional pins */ if (gpio_is_valid(soft_spi_config[bus].mosi_pin)) { gpio_init(soft_spi_config[bus].mosi_pin, GPIO_OUT); gpio_clear(soft_spi_config[bus].mosi_pin); } if (gpio_is_valid(soft_spi_config[bus].miso_pin)) { gpio_init(soft_spi_config[bus].miso_pin, GPIO_IN); } } int soft_spi_init_cs(soft_spi_t bus, soft_spi_cs_t cs) { DEBUG("Soft SPI init CS\n"); if (!soft_spi_bus_is_valid(bus)) { DEBUG("Soft SPI bus not valid\n"); return SOFT_SPI_NODEV; } if (gpio_is_valid(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) { DEBUG("Soft SPI set user CS line\n"); gpio_init(cs, GPIO_OUT); gpio_set(cs); } return SOFT_SPI_OK; } static inline int soft_spi_mode_is_valid(soft_spi_mode_t mode) { if ((mode != SOFT_SPI_MODE_0) && (mode != SOFT_SPI_MODE_1) && (mode != SOFT_SPI_MODE_2) && (mode != SOFT_SPI_MODE_3)) { return 0; } return 1; } void soft_spi_acquire(soft_spi_t bus, soft_spi_cs_t cs, soft_spi_mode_t mode, soft_spi_clk_t clk) { (void)cs; assert(soft_spi_bus_is_valid(bus)); assert(soft_spi_mode_is_valid(mode)); /* lock bus */ mutex_lock(&locks[bus]); soft_spi_config[bus].soft_spi_mode = mode; switch (mode) { case SOFT_SPI_MODE_0: case SOFT_SPI_MODE_1: /* CPOL=0 */ gpio_clear(soft_spi_config[bus].clk_pin); break; case SOFT_SPI_MODE_2: case SOFT_SPI_MODE_3: /* CPOL=1 */ gpio_set(soft_spi_config[bus].clk_pin); break; } soft_spi_config[bus].soft_spi_clk = clk; } void soft_spi_release(soft_spi_t bus) { assert(soft_spi_bus_is_valid(bus)); mutex_unlock(&locks[bus]); } static inline uint8_t _transfer_one_byte(soft_spi_t bus, uint8_t out) { uint8_t i = 8; if (SOFT_SPI_MODE_1 == soft_spi_config[bus].soft_spi_mode || SOFT_SPI_MODE_3 == soft_spi_config[bus].soft_spi_mode) { /* CPHA = 1*/ gpio_toggle(soft_spi_config[bus].clk_pin); } do { uint8_t bit = out >> 7; gpio_write(soft_spi_config[bus].mosi_pin, bit); xtimer_usleep(soft_spi_config[bus].soft_spi_clk); gpio_toggle(soft_spi_config[bus].clk_pin); out <<= 1; /*shift transfer register*/ bit = gpio_read(soft_spi_config[bus].miso_pin); out = bit ? (out | 0x01) : (out & 0xfe); /*set or delete bit 0*/ xtimer_usleep(soft_spi_config[bus].soft_spi_clk); --i; if (i > 0) { gpio_toggle(soft_spi_config[bus].clk_pin); } } while (i > 0); if (SOFT_SPI_MODE_0 == soft_spi_config[bus].soft_spi_mode || SOFT_SPI_MODE_2 == soft_spi_config[bus].soft_spi_mode) { /* CPHA = 0 */ xtimer_usleep(soft_spi_config[bus].soft_spi_clk); gpio_toggle(soft_spi_config[bus].clk_pin); } return out; } uint8_t soft_spi_transfer_byte(soft_spi_t bus, soft_spi_cs_t cs, bool cont, uint8_t out) { DEBUG("Soft SPI soft_spi_transfer_bytes\n"); assert(soft_spi_bus_is_valid(bus)); uint8_t retval = 0; /* activate the given chip select line */ if (gpio_is_valid(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) { gpio_clear((gpio_t)cs); } retval = _transfer_one_byte(bus, out); if (!cont) { if (gpio_is_valid(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) { gpio_set((gpio_t)cs); } } return retval; } void soft_spi_transfer_bytes(soft_spi_t bus, soft_spi_cs_t cs, bool cont, const void *out, void *in, size_t len) { DEBUG("Soft SPI soft_spi_transfer_bytes\n"); assert(soft_spi_bus_is_valid(bus)); /* make sure at least one input or one output buffer is given */ assert(out || in); const uint8_t *outbuf = out; uint8_t *inbuf = in; /* activate the given chip select line */ if ((cs != GPIO_UNDEF) && (cs != SOFT_SPI_CS_UNDEF)) { gpio_clear((gpio_t)cs); } if (!inbuf) { for (size_t i = 0; i < len; i++) { _transfer_one_byte(bus, outbuf[i]); } } else if (!outbuf) { for (size_t i = 0; i < len; i++) { inbuf[i] = _transfer_one_byte(bus, READ_PADDING_BYTE); } } else { for (size_t i = 0; i < len; i++) { inbuf[i] = _transfer_one_byte(bus, outbuf[i]); } } if (!cont) { if ((cs != GPIO_UNDEF) && (cs != SOFT_SPI_CS_UNDEF)) { gpio_set((gpio_t)cs); } } }