aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/ports/MSP430X/hal_spi_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/ports/MSP430X/hal_spi_lld.c')
-rw-r--r--os/hal/ports/MSP430X/hal_spi_lld.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/os/hal/ports/MSP430X/hal_spi_lld.c b/os/hal/ports/MSP430X/hal_spi_lld.c
new file mode 100644
index 0000000..70a357e
--- /dev/null
+++ b/os/hal/ports/MSP430X/hal_spi_lld.c
@@ -0,0 +1,578 @@
+/*
+ ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file hal_spi_lld.c
+ * @brief MSP430X SPI subsystem low level driver source.
+ *
+ * @addtogroup SPI
+ * @{
+ */
+
+#include "hal.h"
+
+#if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief SPIA0 driver identifier.
+ */
+#if (MSP430X_SPI_USE_SPIA0 == TRUE) || defined(__DOXYGEN__)
+SPIDriver SPIDA0;
+#endif
+
+/**
+ * @brief SPIA1 driver identifier.
+ */
+#if (MSP430X_SPI_USE_SPIA1 == TRUE) || defined(__DOXYGEN__)
+SPIDriver SPIDA1;
+#endif
+
+/**
+ * @brief SPIA2 driver identifier.
+ */
+#if (MSP430X_SPI_USE_SPIA2 == TRUE) || defined(__DOXYGEN__)
+SPIDriver SPIDA2;
+#endif
+
+/**
+ * @brief SPIA3 driver identifier.
+ */
+#if (MSP430X_SPI_USE_SPIA3 == TRUE) || defined(__DOXYGEN__)
+SPIDriver SPIDA3;
+#endif
+
+/**
+ * @brief SPIB0 driver identifier.
+ */
+#if (MSP430X_SPI_USE_SPIB0 == TRUE) || defined(__DOXYGEN__)
+SPIDriver SPIDB0;
+#endif
+
+/**
+ * @brief SPIB1 driver identifier.
+ */
+#if (MSP430X_SPI_USE_SPIB1 == TRUE) || defined(__DOXYGEN__)
+SPIDriver SPIDB1;
+#endif
+
+/**
+ * @brief SPIB2 driver identifier.
+ */
+#if (MSP430X_SPI_USE_SPIB2 == TRUE) || defined(__DOXYGEN__)
+SPIDriver SPIDB2;
+#endif
+
+/**
+ * @brief SPIB3 driver identifier.
+ */
+#if (MSP430X_SPI_USE_SPIB3 == TRUE) || defined(__DOXYGEN__)
+SPIDriver SPIDB3;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+static const uint16_t dummytx = 0xFFFFU;
+static uint16_t dummyrx;
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static void init_transfer(SPIDriver * spip) {
+
+#if MSP430X_SPI_EXCLUSIVE_DMA == TRUE || defined(__DOXYGEN__)
+ if (spip->config->dmarx_index > MSP430X_DMA_CHANNELS) {
+ dmaRequest(&(spip->rx_req), TIME_INFINITE);
+ }
+ else {
+ dmaTransfer(&(spip->dmarx), &(spip->rx_req));
+ }
+ if (spip->config->dmatx_index > MSP430X_DMA_CHANNELS) {
+ dmaRequest(&(spip->tx_req), TIME_INFINITE);
+ }
+ else {
+ dmaTransfer(&(spip->dmatx), &(spip->tx_req));
+ }
+#else
+ dmaRequest(&(spip->rx_req), TIME_INFINITE);
+ dmaRequest(&(spip->tx_req), TIME_INFINITE);
+#endif
+
+ *(spip->ifg) |= UCTXIFG;
+}
+
+/**
+ * @brief Shared end-of-transfer callback.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @note This function is called in ISR context by the DMA code.
+ */
+static void spi_lld_end_of_transfer(void * spip) {
+
+ /* So that future transfers will actually work */
+ *(((SPIDriver *)spip)->ifg) &= ~(UCTXIFG);
+ /* NOTE to future me - this macro sets the driver state and calls the
+ * configured callback end_cb, if applicable. That callback doesn't seem to
+ * be modifiable without reconfiguring the whole driver. */
+ _spi_isr_code((SPIDriver *)spip);
+}
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level SPI driver initialization.
+ *
+ * @notapi
+ */
+void spi_lld_init(void) {
+
+#if MSP430X_SPI_USE_SPIA0 == TRUE
+ /* Driver initialization.*/
+ spiObjectInit(&SPIDA0);
+ SPIDA0.regs = (msp430x_spi_reg_t *)(&UCA0CTLW0);
+ SPIDA0.ifg = (volatile uint16_t *)&UCA0IFG;
+ SPIDA0.tx_req.trigger = DMA_TRIGGER_MNEM(UCA0TXIFG);
+ SPIDA0.rx_req.trigger = DMA_TRIGGER_MNEM(UCA0RXIFG);
+ SPIDA0.tx_req.dest_addr = &(SPIDA0.regs->txbuf);
+ SPIDA0.rx_req.source_addr = &(SPIDA0.regs->rxbuf);
+ SPIDA0.tx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDA0.rx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDA0.tx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDA0.rx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDA0.tx_req.callback.callback = NULL;
+ SPIDA0.tx_req.callback.args = NULL;
+ SPIDA0.rx_req.callback.callback = spi_lld_end_of_transfer;
+ SPIDA0.rx_req.callback.args = &SPIDA0;
+/* NOTE to my future self - this must be SINGLE because BLOCK and BURST
+ * don't wait for triggers and would overflow both buffers. Don't worry, it
+ * still works - the transfer isn't complete until SZ bytes are transferred */
+#endif
+
+#if MSP430X_SPI_USE_SPIA1 == TRUE
+ /* Driver initialization.*/
+ spiObjectInit(&SPIDA1);
+ SPIDA1.regs = (msp430x_spi_reg_t *)(&UCA1CTLW0);
+ SPIDA1.ifg = (volatile uint16_t *)&UCA1IFG;
+ SPIDA1.tx_req.trigger = DMA_TRIGGER_MNEM(UCA1TXIFG);
+ SPIDA1.rx_req.trigger = DMA_TRIGGER_MNEM(UCA1RXIFG);
+ SPIDA1.tx_req.dest_addr = &(SPIDA1.regs->txbuf);
+ SPIDA1.rx_req.source_addr = &(SPIDA1.regs->rxbuf);
+ SPIDA1.tx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDA1.rx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDA1.tx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDA1.rx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDA1.tx_req.callback.callback = NULL;
+ SPIDA1.tx_req.callback.args = NULL;
+ SPIDA1.rx_req.callback.callback = spi_lld_end_of_transfer;
+ SPIDA1.rx_req.callback.args = &SPIDA1;
+#endif
+
+#if MSP430X_SPI_USE_SPIA2 == TRUE
+ /* Driver initialization.*/
+ spiObjectInit(&SPIDA2);
+ SPIDA2.regs = (msp430x_spi_reg_t *)(&UCA2CTLW0);
+ SPIDA2.ifg = (volatile uint16_t *)&UCA2IFG;
+ SPIDA2.tx_req.trigger = DMA_TRIGGER_MNEM(UCA2TXIFG);
+ SPIDA2.rx_req.trigger = DMA_TRIGGER_MNEM(UCA2RXIFG);
+ SPIDA2.tx_req.dest_addr = &(SPIDA2.regs->txbuf);
+ SPIDA2.rx_req.source_addr = &(SPIDA2.regs->rxbuf);
+ SPIDA2.tx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDA2.rx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDA2.tx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDA2.rx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDA2.tx_req.callback.callback = NULL;
+ SPIDA2.tx_req.callback.args = NULL;
+ SPIDA2.rx_req.callback.callback = spi_lld_end_of_transfer;
+ SPIDA2.rx_req.callback.args = &SPIDA2;
+#endif
+
+#if MSP430X_SPI_USE_SPIA3 == TRUE
+ /* Driver initialization.*/
+ spiObjectInit(&SPIDA3);
+ SPIDA3.regs = (msp430x_spi_reg_t *)(&UCA3CTLW0);
+ SPIDA3.ifg = (volatile uint16_t *)&UCA3IFG;
+ SPIDA3.tx_req.trigger = DMA_TRIGGER_MNEM(UCA3TXIFG);
+ SPIDA3.rx_req.trigger = DMA_TRIGGER_MNEM(UCA3RXIFG);
+ SPIDA3.tx_req.dest_addr = &(SPIDA3.regs->txbuf);
+ SPIDA3.rx_req.source_addr = &(SPIDA3.regs->rxbuf);
+ SPIDA3.tx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDA3.rx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDA3.tx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDA3.rx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDA3.tx_req.callback.callback = NULL;
+ SPIDA3.tx_req.callback.args = NULL;
+ SPIDA3.rx_req.callback.callback = spi_lld_end_of_transfer;
+ SPIDA3.rx_req.callback.args = &SPIDA3;
+#endif
+
+#if MSP430X_SPI_USE_SPIB0 == TRUE
+ /* Driver initialization.*/
+ spiObjectInit(&SPIDB0);
+ SPIDB0.regs = (msp430x_spi_reg_t *)(&UCB0CTLW0);
+ SPIDB0.ifg = (volatile uint16_t *)&UCB0IFG;
+ SPIDB0.tx_req.trigger = DMA_TRIGGER_MNEM(UCB0TXIFG0);
+ SPIDB0.rx_req.trigger = DMA_TRIGGER_MNEM(UCB0RXIFG0);
+ SPIDB0.tx_req.dest_addr = &(SPIDB0.regs->txbuf);
+ SPIDB0.rx_req.source_addr = &(SPIDB0.regs->rxbuf);
+ SPIDB0.tx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDB0.rx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDB0.tx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDB0.rx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDB0.tx_req.callback.callback = NULL;
+ SPIDB0.tx_req.callback.args = NULL;
+ SPIDB0.rx_req.callback.callback = spi_lld_end_of_transfer;
+ SPIDB0.rx_req.callback.args = &SPIDB0;
+#endif
+
+#if MSP430X_SPI_USE_SPIB1 == TRUE
+ /* Driver initialization.*/
+ spiObjectInit(&SPIDB1);
+ SPIDB1.regs = (msp430x_spi_reg_t *)(&UCB1CTLW0);
+ SPIDB1.ifg = (volatile uint16_t *)&UCB1IFG;
+ SPIDB1.tx_req.trigger = DMA_TRIGGER_MNEM(UCB1TXIFG0);
+ SPIDB1.rx_req.trigger = DMA_TRIGGER_MNEM(UCB1RXIFG0);
+ SPIDB1.tx_req.dest_addr = &(SPIDB1.regs->txbuf);
+ SPIDB1.rx_req.source_addr = &(SPIDB1.regs->rxbuf);
+ SPIDB1.tx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDB1.rx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDB1.tx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDB1.rx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDB1.tx_req.callback.callback = NULL;
+ SPIDB1.tx_req.callback.args = NULL;
+ SPIDB1.rx_req.callback.callback = spi_lld_end_of_transfer;
+ SPIDB1.rx_req.callback.args = &SPIDB1;
+#endif
+
+#if MSP430X_SPI_USE_SPIB2 == TRUE
+ /* Driver initialization.*/
+ spiObjectInit(&SPIDB2);
+ SPIDB2.regs = (msp430x_spi_reg_t *)(&UCB2CTLW0);
+ SPIDB2.ifg = (volatile uint16_t *)&UCB2IFG;
+ SPIDB2.tx_req.trigger = DMA_TRIGGER_MNEM(UCB2TXIFG0);
+ SPIDB2.rx_req.trigger = DMA_TRIGGER_MNEM(UCB2RXIFG0);
+ SPIDB2.tx_req.dest_addr = &(SPIDB2.regs->txbuf);
+ SPIDB2.rx_req.source_addr = &(SPIDB2.regs->rxbuf);
+ SPIDB2.tx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDB2.rx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDB2.tx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDB2.rx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDB2.tx_req.callback.callback = NULL;
+ SPIDB2.tx_req.callback.args = NULL;
+ SPIDB2.rx_req.callback.callback = spi_lld_end_of_transfer;
+ SPIDB2.rx_req.callback.args = &SPIDB2;
+#endif
+
+#if MSP430X_SPI_USE_SPIB3 == TRUE
+ /* Driver initialization.*/
+ spiObjectInit(&SPIDB3);
+ SPIDB3.regs = (msp430x_spi_reg_t *)(&UCB3CTLW0);
+ SPIDB3.ifg = (volatile uint16_t *)&UCB3IFG;
+ SPIDB3.tx_req.trigger = DMA_TRIGGER_MNEM(UCB3TXIFG0);
+ SPIDB3.rx_req.trigger = DMA_TRIGGER_MNEM(UCB3RXIFG0);
+ SPIDB3.tx_req.dest_addr = &(SPIDB3.regs->txbuf);
+ SPIDB3.rx_req.source_addr = &(SPIDB3.regs->rxbuf);
+ SPIDB3.tx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDB3.rx_req.data_mode = MSP430X_DMA_SRCBYTE | MSP430X_DMA_DSTBYTE;
+ SPIDB3.tx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDB3.rx_req.transfer_mode = MSP430X_DMA_SINGLE;
+ SPIDB3.tx_req.callback.callback = NULL;
+ SPIDB3.tx_req.callback.args = NULL;
+ SPIDB3.rx_req.callback.callback = spi_lld_end_of_transfer;
+ SPIDB3.rx_req.callback.args = &SPIDB3;
+#endif
+}
+
+/**
+ * @brief Configures and activates the SPI peripheral.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_start(SPIDriver * spip) {
+
+ if (spip->state == SPI_STOP) {
+/* Enables the peripheral.*/
+#if MSP430X_SPI_EXCLUSIVE_DMA == TRUE
+ /* Claim DMA streams here */
+ bool b;
+ if (spip->config->dmatx_index < MSP430X_DMA_CHANNELS) {
+ b = dmaAcquire(&(spip->dmatx), spip->config->dmatx_index);
+ osalDbgAssert(!b, "stream already allocated");
+ }
+ if (spip->config->dmarx_index < MSP430X_DMA_CHANNELS) {
+ b = dmaAcquire(&(spip->dmarx), spip->config->dmarx_index);
+ osalDbgAssert(!b, "stream already allocated");
+ }
+#endif /* MSP430X_SPI_EXCLUSIVE_DMA */
+ }
+ uint16_t brw = 0;
+ uint8_t ssel = 0;
+#if MSP430X_SPI_USE_SPIA0
+ if (spip == &SPIDA0) {
+ brw = MSP430X_SPIA0_CLK_FREQ / spip->config->bit_rate;
+ ssel = MSP430X_SPIA0_UCSSEL;
+ }
+#endif
+#if MSP430X_SPI_USE_SPIA1
+ if (spip == &SPIDA1) {
+ brw = MSP430X_SPIA1_CLK_FREQ / spip->config->bit_rate;
+ ssel = MSP430X_SPIA1_UCSSEL;
+ }
+#endif
+#if MSP430X_SPI_USE_SPIA2
+ if (spip == &SPIDA2) {
+ brw = MSP430X_SPIA2_CLK_FREQ / spip->config->bit_rate;
+ ssel = MSP430X_SPIA2_UCSSEL;
+ }
+#endif
+#if MSP430X_SPI_USE_SPIA3
+ if (spip == &SPIDA3) {
+ brw = MSP430X_SPIA3_CLK_FREQ / spip->config->bit_rate;
+ ssel = MSP430X_SPIA3_UCSSEL;
+ }
+#endif
+#if MSP430X_SPI_USE_SPIB0
+ if (spip == &SPIDB0) {
+ brw = MSP430X_SPIB0_CLK_FREQ / spip->config->bit_rate;
+ ssel = MSP430X_SPIB0_UCSSEL;
+ }
+#endif
+#if MSP430X_SPI_USE_SPIB1
+ if (spip == &SPIDB1) {
+ brw = MSP430X_SPIB1_CLK_FREQ / spip->config->bit_rate;
+ ssel = MSP430X_SPIB1_UCSSEL;
+ }
+#endif
+#if MSP430X_SPI_USE_SPIB2
+ if (spip == &SPIDB2) {
+ brw = MSP430X_SPIB2_CLK_FREQ / spip->config->bit_rate;
+ ssel = MSP430X_SPIB2_UCSSEL;
+ }
+#endif
+#if MSP430X_SPI_USE_SPIB3
+ if (spip == &SPIDB3) {
+ brw = MSP430X_SPIB3_CLK_FREQ / spip->config->bit_rate;
+ ssel = MSP430X_SPIB3_UCSSEL;
+ }
+#endif
+ /* Configures the peripheral.*/
+ spip->regs->ctlw0 = UCSWRST;
+ spip->regs->brw = brw;
+ spip->regs->ctlw0 =
+ (spip->config->spi_mode << 14) | (spip->config->bit_order << 13) |
+ (spip->config->data_size << 12) | (UCMST) |
+ ((spip->config->ss_line ? 0 : 2) << 9) | (UCSYNC) | (ssel) | (UCSTEM);
+ *(spip->ifg) = 0;
+}
+
+/**
+ * @brief Deactivates the SPI peripheral.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_stop(SPIDriver * spip) {
+
+ if (spip->state == SPI_READY) {
+/* Disables the peripheral.*/
+#if MSP430X_SPI_EXCLUSIVE_DMA == TRUE
+ dmaRelease(&(spip->dmatx));
+ dmaRelease(&(spip->dmarx));
+#endif
+ spip->regs->ctlw0 = UCSWRST;
+ }
+}
+
+/**
+ * @brief Asserts the slave select signal and prepares for transfers.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_select(SPIDriver * spip) {
+
+ if (spip->config->ss_line) {
+ palClearLine(spip->config->ss_line);
+ }
+}
+
+/**
+ * @brief Deasserts the slave select signal.
+ * @details The previously selected peripheral is unselected.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_unselect(SPIDriver * spip) {
+
+ if (spip->config->ss_line) {
+ palSetLine(spip->config->ss_line);
+ }
+}
+
+/**
+ * @brief Ignores data on the SPI bus.
+ * @details This asynchronous function starts the transmission of a series of
+ * idle bytes on the SPI bus and ignores the received data.
+ * @post At the end of the operation the configured callback is invoked.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of bytes to be ignored
+ *
+ * @notapi
+ */
+void spi_lld_ignore(SPIDriver * spip, size_t n) {
+
+ spip->tx_req.source_addr = &dummytx;
+ spip->tx_req.size = n;
+ spip->tx_req.addr_mode = 0;
+
+ spip->rx_req.dest_addr = &dummyrx;
+ spip->rx_req.size = n;
+ spip->rx_req.addr_mode = 0;
+
+ init_transfer(spip);
+}
+
+/**
+ * @brief Exchanges data on the SPI bus.
+ * @details This asynchronous function starts a simultaneous transmit/receive
+ * operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ * @param[in] txbuf the pointer to the transmit buffer
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @notapi
+ */
+void spi_lld_exchange(SPIDriver * spip,
+ size_t n,
+ const void * txbuf,
+ void * rxbuf) {
+
+ spip->tx_req.source_addr = txbuf;
+ spip->tx_req.size = n;
+ spip->tx_req.addr_mode = MSP430X_DMA_SRCINCR;
+
+ spip->rx_req.dest_addr = rxbuf;
+ spip->rx_req.size = n;
+ spip->rx_req.addr_mode = MSP430X_DMA_DSTINCR;
+
+ init_transfer(spip);
+}
+
+/**
+ * @brief Sends data over the SPI bus.
+ * @details This asynchronous function starts a transmit operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to send
+ * @param[in] txbuf the pointer to the transmit buffer
+ *
+ * @notapi
+ */
+void spi_lld_send(SPIDriver * spip, size_t n, const void * txbuf) {
+
+ spip->tx_req.source_addr = txbuf;
+ spip->tx_req.size = n;
+ spip->tx_req.addr_mode = MSP430X_DMA_SRCINCR;
+
+ spip->rx_req.dest_addr = &dummyrx;
+ spip->rx_req.size = n;
+ spip->rx_req.addr_mode = 0;
+
+ init_transfer(spip);
+}
+
+/**
+ * @brief Receives data from the SPI bus.
+ * @details This asynchronous function starts a receive operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to receive
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @notapi
+ */
+void spi_lld_receive(SPIDriver * spip, size_t n, void * rxbuf) {
+
+ spip->tx_req.source_addr = &dummytx;
+ spip->tx_req.size = n;
+ spip->tx_req.addr_mode = 0;
+
+ spip->rx_req.dest_addr = rxbuf;
+ spip->rx_req.size = n;
+ spip->rx_req.addr_mode = MSP430X_DMA_DSTINCR;
+
+ init_transfer(spip);
+}
+
+/**
+ * @brief Exchanges one frame using a polled wait.
+ * @details This synchronous function exchanges one frame using a polled
+ * synchronization method. This function is useful when exchanging
+ * small amount of data on high speed channels, usually in this
+ * situation is much more efficient just wait for completion using
+ * polling than suspending the thread waiting for an interrupt.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] frame the data frame to send over the SPI bus
+ * @return The received data frame from the SPI bus.
+ */
+uint16_t spi_lld_polled_exchange(SPIDriver * spip, uint16_t frame) {
+
+ osalDbgAssert(!(frame & 0xFF00U), "16-bit transfers not supported");
+
+ while (!(*(spip->ifg) & UCTXIFG))
+ ;
+ spip->regs->txbuf = frame;
+ while (!(*(spip->ifg) & UCRXIFG))
+ ;
+ return spip->regs->rxbuf;
+}
+
+#endif /* HAL_USE_SPI == TRUE */
+
+/** @} */