mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
aba1d9aaf0
The code did not take into account that valid bus numbers are 0...(COUNT-1). This patch corrects this. It also makes use of the newly introduced SOFT_SPI_NUMOF define.
243 lines
6.4 KiB
C
243 lines
6.4 KiB
C
/*
|
|
* 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 <Markus.Blechschmidt@haw-hamburg.de>
|
|
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
|
|
#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);
|
|
}
|
|
}
|
|
}
|