2015-09-02 12:43:21 +02:00
|
|
|
/*
|
2016-11-08 18:20:38 +01:00
|
|
|
* Copyright (C) 2015-2016 Freie Universität Berlin
|
2015-09-02 12:43:21 +02:00
|
|
|
*
|
|
|
|
* 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 cpu_msp430fxyz
|
2017-06-22 15:43:17 +02:00
|
|
|
* @ingroup drivers_periph_spi
|
2015-09-02 12:43:21 +02:00
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file
|
|
|
|
* @brief Low-level SPI driver implementation
|
|
|
|
*
|
|
|
|
* This SPI driver implementation does only support one single SPI device for
|
|
|
|
* now. This is sufficient, as most MSP430 CPU's only support two serial
|
|
|
|
* devices - one used as UART and one as SPI.
|
|
|
|
*
|
|
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
|
|
*
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cpu.h"
|
|
|
|
#include "mutex.h"
|
2016-09-28 20:05:06 +02:00
|
|
|
#include "assert.h"
|
2015-09-02 12:43:21 +02:00
|
|
|
#include "periph/spi.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Mutex for locking the SPI device
|
|
|
|
*/
|
|
|
|
static mutex_t spi_lock = MUTEX_INIT;
|
|
|
|
|
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
void spi_init(spi_t bus)
|
2015-09-02 12:43:21 +02:00
|
|
|
{
|
2016-11-08 18:20:38 +01:00
|
|
|
assert(bus <= SPI_NUMOF);
|
2016-03-24 09:40:28 +01:00
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
/* we need to differentiate between the legacy SPI device and USCI */
|
|
|
|
#ifndef SPI_USE_USCI
|
|
|
|
/* put SPI device in reset state */
|
|
|
|
SPI_BASE->CTL = USART_CTL_SWRST;
|
|
|
|
SPI_BASE->CTL |= (USART_CTL_CHAR | USART_CTL_SYNC | USART_CTL_MM);
|
|
|
|
SPI_BASE->RCTL = 0;
|
|
|
|
SPI_BASE->MCTL = 0;
|
2015-09-02 12:43:21 +02:00
|
|
|
/* enable SPI mode */
|
|
|
|
SPI_ME |= SPI_ME_BIT;
|
2016-11-08 18:20:38 +01:00
|
|
|
#else
|
|
|
|
/* reset SPI device */
|
|
|
|
SPI_BASE->CTL1 = USCI_SPI_CTL1_SWRST;
|
|
|
|
SPI_BASE->CTL1 |= (USCI_SPI_CTL1_SSEL_SMCLK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* trigger the pin configuration */
|
|
|
|
spi_init_pins(bus);
|
2015-09-02 12:43:21 +02:00
|
|
|
}
|
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
void spi_init_pins(spi_t bus)
|
|
|
|
{
|
|
|
|
gpio_periph_mode(SPI_PIN_MISO, true);
|
|
|
|
gpio_periph_mode(SPI_PIN_MOSI, true);
|
|
|
|
gpio_periph_mode(SPI_PIN_CLK, true);
|
|
|
|
}
|
2015-09-02 12:43:21 +02:00
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
|
2015-09-02 12:43:21 +02:00
|
|
|
{
|
2016-11-08 18:20:38 +01:00
|
|
|
if (clk == SPI_CLK_10MHZ) {
|
|
|
|
return SPI_NOCLK;
|
2015-09-02 12:43:21 +02:00
|
|
|
}
|
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
/* lock the bus */
|
|
|
|
mutex_lock(&spi_lock);
|
2016-03-24 09:40:28 +01:00
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
/* calculate baudrate */
|
|
|
|
uint32_t br = CLOCK_CMCLK / clk;
|
2016-03-24 09:40:28 +01:00
|
|
|
/* make sure the is not smaller then 2 */
|
|
|
|
if (br < 2) {
|
|
|
|
br = 2;
|
|
|
|
}
|
2016-11-08 18:20:38 +01:00
|
|
|
SPI_BASE->BR0 = (uint8_t)br;
|
|
|
|
SPI_BASE->BR1 = (uint8_t)(br >> 8);
|
2016-03-24 09:40:28 +01:00
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
/* configure bus mode */
|
|
|
|
#ifndef SPI_USE_USCI
|
|
|
|
/* configure mode */
|
|
|
|
SPI_BASE->TCTL = (USART_TCTL_SSEL_SMCLK | USART_TCTL_STC | mode);
|
2015-09-02 12:43:21 +02:00
|
|
|
/* release from software reset */
|
2016-11-08 18:20:38 +01:00
|
|
|
SPI_BASE->CTL &= ~(USART_CTL_SWRST);
|
|
|
|
#else
|
|
|
|
/* configure mode */
|
|
|
|
SPI_BASE->CTL0 = (USCI_SPI_CTL0_UCSYNC | USCI_SPI_CTL0_MST|
|
|
|
|
USCI_SPI_CTL0_MODE_0 | USCI_SPI_CTL0_MSB | mode);
|
|
|
|
/* release from software reset */
|
|
|
|
SPI_BASE->CTL1 &= ~(USCI_SPI_CTL1_SWRST);
|
|
|
|
#endif
|
2015-09-02 12:43:21 +02:00
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
return SPI_OK;
|
2015-09-02 12:43:21 +02:00
|
|
|
}
|
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
void spi_release(spi_t dev)
|
2015-09-02 12:43:21 +02:00
|
|
|
{
|
2016-11-08 18:20:38 +01:00
|
|
|
/* put SPI device back in reset state */
|
|
|
|
#ifndef SPI_USE_USCI
|
|
|
|
SPI_BASE->CTL |= (USART_CTL_SWRST);
|
|
|
|
#else
|
|
|
|
SPI_BASE->CTL1 |= (USCI_SPI_CTL1_SWRST);
|
|
|
|
#endif
|
2015-09-02 12:43:21 +02:00
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
/* release the bus */
|
2015-09-02 12:43:21 +02:00
|
|
|
mutex_unlock(&spi_lock);
|
|
|
|
}
|
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
|
|
|
|
const void *out, void *in, size_t len)
|
2015-09-02 12:43:21 +02:00
|
|
|
{
|
2016-11-08 18:20:38 +01:00
|
|
|
uint8_t *out_buf = (uint8_t *)out;
|
|
|
|
uint8_t *in_buf = (uint8_t *)in;
|
|
|
|
|
|
|
|
assert(out_buf || in_buf);
|
2016-09-28 20:05:06 +02:00
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
if (cs != SPI_CS_UNDEF) {
|
|
|
|
gpio_clear((gpio_t)cs);
|
|
|
|
}
|
2016-09-28 20:05:06 +02:00
|
|
|
|
|
|
|
/* if we only send out data, we do this the fast way... */
|
2016-11-08 18:20:38 +01:00
|
|
|
if (!in_buf) {
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
2016-09-28 20:05:06 +02:00
|
|
|
while (!(SPI_IF & SPI_IE_TX_BIT)) {}
|
2016-11-08 18:20:38 +01:00
|
|
|
SPI_BASE->TXBUF = (uint8_t)out_buf[i];
|
2016-09-28 20:05:06 +02:00
|
|
|
}
|
|
|
|
/* finally we need to wait, until all transfers are complete */
|
|
|
|
#ifndef SPI_USE_USCI
|
|
|
|
while (!(SPI_IF & SPI_IE_TX_BIT) || !(SPI_IF & SPI_IE_RX_BIT)) {}
|
|
|
|
#else
|
2016-11-08 18:20:38 +01:00
|
|
|
while (SPI_BASE->STAT & USCI_SPI_STAT_UCBUSY) {}
|
2016-09-28 20:05:06 +02:00
|
|
|
#endif
|
2016-11-08 18:20:38 +01:00
|
|
|
SPI_BASE->RXBUF;
|
2015-09-02 12:43:21 +02:00
|
|
|
}
|
2016-11-08 18:20:38 +01:00
|
|
|
else if (!out_buf) {
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
SPI_BASE->TXBUF = 0;
|
2016-09-28 20:05:06 +02:00
|
|
|
while (!(SPI_IF & SPI_IE_RX_BIT)) {}
|
2016-11-08 18:20:38 +01:00
|
|
|
in_buf[i] = (char)SPI_BASE->RXBUF;
|
2016-09-28 20:05:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2016-11-08 18:20:38 +01:00
|
|
|
for (size_t i = 0; i < len; i++) {
|
2016-09-28 20:05:06 +02:00
|
|
|
while (!(SPI_IF & SPI_IE_TX_BIT)) {}
|
2016-11-08 18:20:38 +01:00
|
|
|
SPI_BASE->TXBUF = out_buf[i];
|
2016-09-28 20:05:06 +02:00
|
|
|
while (!(SPI_IF & SPI_IE_RX_BIT)) {}
|
2016-11-08 18:20:38 +01:00
|
|
|
in_buf[i] = (char)SPI_BASE->RXBUF;
|
2016-09-28 20:05:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-08 18:20:38 +01:00
|
|
|
if ((!cont) && (cs != SPI_CS_UNDEF)) {
|
|
|
|
gpio_set((gpio_t)cs);
|
|
|
|
}
|
2015-09-02 12:43:21 +02:00
|
|
|
}
|