/* * Copyright (C) 2015 Freie Universität Berlin * * 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 * @{ * * @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 * * @} */ #include "cpu.h" #include "mutex.h" #include "assert.h" #include "periph_cpu.h" #include "periph_conf.h" #include "periph/spi.h" /** * @brief Mutex for locking the SPI device */ static mutex_t spi_lock = MUTEX_INIT; /* per default, we use the legacy MSP430 USART module for UART functionality */ #ifndef SPI_USE_USCI int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed) { if (dev != 0) { return -2; } /* reset SPI device */ SPI_DEV->CTL = USART_CTL_SWRST; /* configure pins */ spi_conf_pins(dev); /* configure USART to SPI mode with SMCLK driving it */ SPI_DEV->CTL |= (USART_CTL_CHAR | USART_CTL_SYNC | USART_CTL_MM); SPI_DEV->RCTL = 0; SPI_DEV->TCTL = (USART_TCTL_SSEL_SMCLK | USART_TCTL_STC); /* set polarity and phase */ switch (conf) { case SPI_CONF_FIRST_RISING: SPI_DEV->TCTL |= USART_TCTL_CKPH; break; case SPI_CONF_SECOND_RISING: /* nothing to be done here */ break; case SPI_CONF_FIRST_FALLING: SPI_DEV->TCTL |= (USART_TCTL_CKPH & USART_TCTL_CKPL); break; case SPI_CONF_SECOND_FALLING: SPI_DEV->TCTL |= USART_TCTL_CKPL; break; default: /* invalid clock setting */ return -2; } /* configure clock - we use no modulation for now */ uint32_t br = CLOCK_CMCLK; switch (speed) { case SPI_SPEED_100KHZ: br /= 100000; break; case SPI_SPEED_400KHZ: br /= 400000; break; case SPI_SPEED_1MHZ: br /= 1000000; break; case SPI_SPEED_5MHZ: br /= 5000000; break; default: /* other clock speeds are not supported */ return -1; } /* make sure the is not smaller then 2 */ if (br < 2) { br = 2; } SPI_DEV->BR0 = (uint8_t)br; SPI_DEV->BR1 = (uint8_t)(br >> 8); SPI_DEV->MCTL = 0; /* enable SPI mode */ SPI_ME |= SPI_ME_BIT; /* release from software reset */ SPI_DEV->CTL &= ~(USART_CTL_SWRST); return 0; } /* we use alternative SPI code in case the board used the USCI module for SPI * instead of the (older) USART module */ #else /* SPI_USE_USCI */ int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed) { if (dev != 0) { return -2; } /* reset SPI device */ SPI_DEV->CTL1 |= USCI_SPI_CTL1_SWRST; /* configure pins */ spi_conf_pins(dev); /* configure USART to SPI mode with SMCLK driving it */ SPI_DEV->CTL0 |= (USCI_SPI_CTL0_UCSYNC | USCI_SPI_CTL0_MST | USCI_SPI_CTL0_MODE_0 | USCI_SPI_CTL0_MSB); SPI_DEV->CTL1 |= (USCI_SPI_CTL1_SSEL_SMCLK); /* set polarity and phase */ switch (conf) { case SPI_CONF_FIRST_RISING: SPI_DEV->CTL0 |= USCI_SPI_CTL0_CKPH; break; case SPI_CONF_SECOND_RISING: /* nothing to be done here */ break; case SPI_CONF_FIRST_FALLING: SPI_DEV->CTL0 |= (USCI_SPI_CTL0_CKPH & USCI_SPI_CTL0_CKPL); break; case SPI_CONF_SECOND_FALLING: SPI_DEV->CTL0 |= USCI_SPI_CTL0_CKPL; break; default: /* invalid clock setting */ return -2; } /* configure clock - we use no modulation for now */ uint32_t br = CLOCK_CMCLK; switch (speed) { case SPI_SPEED_100KHZ: br /= 100000; break; case SPI_SPEED_400KHZ: br /= 400000; break; case SPI_SPEED_1MHZ: br /= 1000000; break; case SPI_SPEED_5MHZ: br /= 5000000; break; default: /* other clock speeds are not supported */ return -1; } /* make sure the is not smaller then 2 */ if (br < 2) { br = 2; } SPI_DEV->BR0 = (uint8_t)br; SPI_DEV->BR1 = (uint8_t)(br >> 8); /* release from software reset */ SPI_DEV->CTL1 &= ~(USCI_SPI_CTL1_SWRST); return 0; } #endif /* SPI_USE_USCI */ int spi_init_slave(spi_t dev, spi_conf_t conf, char (*cb)(char data)) { /* not supported so far */ (void)dev; (void)conf; (void)cb; return -1; } void spi_transmission_begin(spi_t dev, char reset_val) { /* not supported so far */ (void)dev; (void)reset_val; } int spi_conf_pins(spi_t dev) { (void)dev; gpio_periph_mode(SPI_PIN_MISO, true); gpio_periph_mode(SPI_PIN_MOSI, true); gpio_periph_mode(SPI_PIN_CLK, true); return 0; } int spi_acquire(spi_t dev) { (void)dev; mutex_lock(&spi_lock); return 0; } int spi_release(spi_t dev) { (void)dev; mutex_unlock(&spi_lock); return 0; } int spi_transfer_bytes(spi_t dev, char *out, char *in, unsigned int length) { (void)dev; assert(out || in); /* if we only send out data, we do this the fast way... */ if (!in) { for (unsigned i = 0; i < length; i++) { while (!(SPI_IF & SPI_IE_TX_BIT)) {} SPI_DEV->TXBUF = (uint8_t)out[i]; } /* 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 while (SPI_DEV->STAT & USCI_SPI_STAT_UCBUSY) {} #endif SPI_DEV->RXBUF; } else if (!out) { for (unsigned i = 0; i < length; i++) { SPI_DEV->TXBUF = 0; while (!(SPI_IF & SPI_IE_RX_BIT)) {} in[i] = (char)SPI_DEV->RXBUF; } } else { for (unsigned i = 0; i < length; i++) { while (!(SPI_IF & SPI_IE_TX_BIT)) {} SPI_DEV->TXBUF = out[i]; while (!(SPI_IF & SPI_IE_RX_BIT)) {} in[i] = (char)SPI_DEV->RXBUF; } } return length; } void spi_poweron(spi_t dev) { /* not supported so far */ (void)dev; } void spi_poweroff(spi_t dev) { /* not supported so far */ (void)dev; }