aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal
diff options
context:
space:
mode:
authorAndrew Wygle <awygle@gmail.com>2016-05-07 23:35:10 -0700
committerAndrew Wygle <awygle@gmail.com>2016-05-08 17:59:09 -0700
commitdfd93d512b6703ac45d5c0e322bcf8b5e83f50f1 (patch)
tree7a4c49a2e3852905297b85e1452071b1c876cfaa /os/hal
parentcf02c79b5aa7209542cbf1b0cefe703a2c0c60be (diff)
downloadChibiOS-Contrib-dfd93d512b6703ac45d5c0e322bcf8b5e83f50f1.tar.gz
ChibiOS-Contrib-dfd93d512b6703ac45d5c0e322bcf8b5e83f50f1.tar.bz2
ChibiOS-Contrib-dfd93d512b6703ac45d5c0e322bcf8b5e83f50f1.zip
Added SPI driver and test code to MSP430X port
A DMA-driven SPI driver and fairly comprehensive test code for SPI on the MSP430X port. Required some cleanup to the DMA and Serial drivers as well. Includes some reformatting to be more in line with ChibiOS coding standards.
Diffstat (limited to 'os/hal')
-rw-r--r--os/hal/ports/MSP430X/hal_dma_lld.c253
-rw-r--r--os/hal/ports/MSP430X/hal_dma_lld.h67
-rw-r--r--os/hal/ports/MSP430X/hal_pal_lld.h51
-rw-r--r--os/hal/ports/MSP430X/hal_serial_lld.c483
-rw-r--r--os/hal/ports/MSP430X/hal_spi_lld.c578
-rw-r--r--os/hal/ports/MSP430X/hal_spi_lld.h642
-rw-r--r--os/hal/ports/MSP430X/platform.mk3
7 files changed, 1659 insertions, 418 deletions
diff --git a/os/hal/ports/MSP430X/hal_dma_lld.c b/os/hal/ports/MSP430X/hal_dma_lld.c
index 58293ca..43e1d6c 100644
--- a/os/hal/ports/MSP430X/hal_dma_lld.c
+++ b/os/hal/ports/MSP430X/hal_dma_lld.c
@@ -40,9 +40,8 @@
/* Driver local variables and types. */
/*===========================================================================*/
-/* TODO make sure this is right... */
-static msp430x_dma_ch_reg_t * const dma_channels = (msp430x_dma_ch_reg_t *)&DMA0CTL;
-static uint8_t * const dma_ctls = (uint8_t *)&DMACTL0;
+static msp430x_dma_ch_reg_t * const dma_channels =
+ (msp430x_dma_ch_reg_t *)&DMA0CTL;
static msp430x_dma_cb_t callbacks[MSP430X_DMA_CHANNELS];
#if CH_CFG_USE_SEMAPHORES
@@ -53,16 +52,29 @@ static semaphore_t dma_lock;
/* Driver local functions. */
/*===========================================================================*/
+/**
+ * @brief Set a DMA trigger using an index.
+ *
+ * @param[in] index The index of the DMA channel whose trigger is set.
+ * @param[in] trigger The trigger to use.
+ * @note This is all to get around weird MSP behavior when writing to memory-
+ * mapped registers using bytewise instructions.
+ */
+static void dma_trigger_set(uint8_t index, uint8_t trigger) {
+ uint16_t * ctl = ((uint16_t *)((uintptr_t)(&DMACTL0)) + (index / 2));
+ *ctl &= 0xFF00 >> (8 * (index % 2));
+ *ctl |= trigger << (8 * (index % 2));
+}
static void init_request(const msp430x_dma_req_t * request, uint8_t index) {
-
- dma_ctls[index] = request->trigger;
- callbacks[index] = request->callback;
- msp430x_dma_ch_reg_t * ch = &dma_channels[index];
- ch->sa = (uintptr_t)request->source_addr;
- ch->da = (uintptr_t)request->dest_addr;
- ch->sz = request->size;
- ch->ctl = DMAREQ | DMAIE | DMAEN | request->data_mode | request->addr_mode
- | request->transfer_mode;
+
+ dma_trigger_set(index, request->trigger);
+ callbacks[index] = request->callback;
+ msp430x_dma_ch_reg_t * ch = &dma_channels[index];
+ ch->sa = (uintptr_t)request->source_addr;
+ ch->da = (uintptr_t)request->dest_addr;
+ ch->sz = request->size;
+ ch->ctl = DMAREQ | DMAIE | DMAEN | request->data_mode | request->addr_mode |
+ request->transfer_mode;
}
/*===========================================================================*/
@@ -72,19 +84,22 @@ static void init_request(const msp430x_dma_req_t * request, uint8_t index) {
PORT_IRQ_HANDLER(DMA_VECTOR) {
uint8_t index;
OSAL_IRQ_PROLOGUE();
-
+
index = (DMAIV >> 1) - 1;
-
+
if (index < MSP430X_DMA_CHANNELS) {
+#if CH_CFG_USE_SEMAPHORES
+ chSemSignalI(&dma_lock);
+#endif
+
msp430x_dma_cb_t * cb = &callbacks[index];
-
+
/* WARNING: CALLBACKS ARE CALLED IN AN ISR CONTEXT! */
if (cb->callback != NULL) {
cb->callback(cb->args);
}
-
- }
-
+ }
+
OSAL_IRQ_EPILOGUE();
}
@@ -94,7 +109,7 @@ PORT_IRQ_HANDLER(DMA_VECTOR) {
/**
* @brief Initialize the DMA engine.
- *
+ *
* @init
*/
void dmaInit(void) {
@@ -112,132 +127,132 @@ void dmaInit(void) {
* disabled, the calling thread will busy-wait instead of sleeping.
*/
bool dmaRequest(msp430x_dma_req_t * request, systime_t timeout) {
- /* Check if a DMA channel is available */
+/* Check if a DMA channel is available */
#if CH_CFG_USE_SEMAPHORES
msg_t semresult = chSemWaitTimeout(&dma_lock, timeout);
if (semresult != MSG_OK)
return true;
#endif
-
+
#if !(CH_CFG_USE_SEMAPHORES)
systime_t start = chVTGetSystemTimeX();
-
+
do {
#endif
- /* Grab the correct DMA channel to use */
- int i = 0;
- for (i = 0; i < MSP430X_DMA_CHANNELS; i++) {
- if (!(dma_channels[i].ctl & DMAEN)) {
- break;
+ /* Grab the correct DMA channel to use */
+ int i = 0;
+ for (i = 0; i < MSP430X_DMA_CHANNELS; i++) {
+ if (!(dma_channels[i].ctl & DMAEN)) {
+ break;
+ }
}
- }
#if !(CH_CFG_USE_SEMAPHORES)
- while (chVTTimeElapsedSinceX(start) < timeout);
+ while (chVTTimeElapsedSinceX(start) < timeout)
+ ;
#endif
-
+
#if !(CH_CFG_USE_SEMAPHORES)
- if (i == MSP430X_DMA_CHANNELS) {
- return true;
- }
-#endif
-
- /* Make the request */
- init_request(request, i);
-
-#if CH_CFG_USE_SEMAPHORES
- chSemSignal(&dma_lock);
+ if (i == MSP430X_DMA_CHANNELS) {
+ return true;
+ }
#endif
-
- return false;
-}
-/**
- * @brief Acquires exclusive control of a DMA channel.
- * @pre The channel must not be already acquired or an error is returned.
- * @note If the channel is in use by the DMA engine, blocks until acquired.
- * @post This channel must be interacted with using only the functions
- * defined in this module.
- *
- * @param[out] channel The channel handle. Must be pre-allocated.
- * @param[in] index The index of the channel (< MSP430X_DMA_CHANNELS).
- * @return The operation status.
- * @retval false no error, channel acquired.
- * @retval true error, channel already acquired.
- */
-bool dmaAcquire(msp430x_dma_ch_t * channel, uint8_t index) {
- /* Acquire the channel in an idle mode */
-
- /* Is the channel already acquired? */
- osalDbgAssert(index < MSP430X_DMA_CHANNELS, "invalid channel index");
- if (dma_channels[index].ctl & DMADT_4) {
- return true;
+ /* Make the request */
+ init_request(request, i);
+
+ return false;
}
-
- /* Increment the DMA counter */
+
+ /**
+ * @brief Acquires exclusive control of a DMA channel.
+ * @pre The channel must not be already acquired or an error is returned.
+ * @note If the channel is in use by the DMA engine, blocks until acquired.
+ * @post This channel must be interacted with using only the functions
+ * defined in this module.
+ *
+ * @param[out] channel The channel handle. Must be pre-allocated.
+ * @param[in] index The index of the channel (< MSP430X_DMA_CHANNELS).
+ * @return The operation status.
+ * @retval false no error, channel acquired.
+ * @retval true error, channel already acquired.
+ */
+ bool dmaAcquire(msp430x_dma_ch_t * channel, uint8_t index) {
+ /* Acquire the channel in an idle mode */
+
+ /* Is the channel already acquired? */
+ osalDbgAssert(index < MSP430X_DMA_CHANNELS, "invalid channel index");
+ if (dma_channels[index].ctl & DMADT_4) {
+ return true;
+ }
+
+/* Increment the DMA counter */
#if CH_CFG_USE_SEMAPHORES
- msg_t semresult = chSemWait(&dma_lock);
- if (semresult != MSG_OK)
- return true;
+ msg_t semresult = chSemWait(&dma_lock);
+ if (semresult != MSG_OK)
+ return true;
#endif
-
- while (dma_channels[index].ctl & DMAEN) ;
-
- dma_ctls[index] = DMA_TRIGGER_MNEM(DMAREQ);
- dma_channels[index].sz = 0;
- dma_channels[index].ctl = DMAEN | DMAABORT | DMADT_4;
-
- channel->registers = dma_channels + index;
- channel->ctl = dma_ctls + index;
- channel->cb = callbacks + index;
-
- return false;
-}
-/**
- * @brief Initiates a DMA transfer operation using an acquired channel.
- * @pre The channel must have been acquired using @p dmaAcquire().
- *
- * @param[in] channel pointer to a DMA channel from @p dmaAcquire().
- * @param[in] request pointer to a DMA request object.
- */
-void dmaTransfer(msp430x_dma_ch_t * channel, msp430x_dma_req_t * request) {
-
- *(channel->ctl) = request->trigger;
-
+ while (dma_channels[index].ctl & DMAEN)
+ ;
+
+ dma_trigger_set(index, DMA_TRIGGER_MNEM(DMAREQ));
+ dma_channels[index].sz = 0;
+ dma_channels[index].ctl = DMAEN | DMAABORT | DMADT_4;
+
+ channel->registers = dma_channels + index;
+ channel->index = index;
+ channel->cb = callbacks + index;
+
+ return false;
+ }
+
+ /**
+ * @brief Initiates a DMA transfer operation using an acquired channel.
+ * @pre The channel must have been acquired using @p dmaAcquire().
+ *
+ * @param[in] channel pointer to a DMA channel from @p dmaAcquire().
+ * @param[in] request pointer to a DMA request object.
+ */
+ void dmaTransfer(msp430x_dma_ch_t * channel, msp430x_dma_req_t * request) {
+
+ dma_trigger_set(channel->index, request->trigger);
+ /**(channel->ctl) = request->trigger;*/
+
channel->cb->callback = request->callback.callback;
- channel->cb->args = request->callback.args;
-
+ channel->cb->args = request->callback.args;
+
chSysLock();
channel->registers->ctl &= (~DMAEN);
- channel->registers->sa = (uintptr_t)request->source_addr;
- channel->registers->da = (uintptr_t)request->dest_addr;
- channel->registers->sz = request->size;
- channel->registers->ctl = DMAIE | request->data_mode | request->addr_mode
- | request->transfer_mode | DMADT_4 | DMAEN | DMAREQ; /* repeated transfers */
+ channel->registers->sa = (uintptr_t)request->source_addr;
+ channel->registers->da = (uintptr_t)request->dest_addr;
+ channel->registers->sz = request->size;
+ channel->registers->ctl = DMAIE | request->data_mode | request->addr_mode |
+ request->transfer_mode | DMADT_4 | DMAEN |
+ DMAREQ; /* repeated transfers */
chSysUnlock();
-}
+ }
-/**
- * @brief Releases exclusive control of a DMA channel.
- * @details The channel is released from control and returned to the DMA engine
- * pool. Trying to release an unallocated channel is an illegal
- * operation and is trapped if assertions are enabled.
- * @pre The channel must have been acquired using @p dmaAcquire().
- * @post The channel is returned to the DMA engine pool.
- */
-void dmaRelease(msp430x_dma_ch_t * channel) {
-
- osalDbgCheck(channel != NULL);
- osalDbgAssert(channel->registers->ctl & DMADT_4, "not acquired");
-
- /* Release the channel in an idle mode */
- channel->registers->ctl = DMAABORT;
-
- /* release the DMA counter */
+ /**
+ * @brief Releases exclusive control of a DMA channel.
+ * @details The channel is released from control and returned to the DMA
+ * engine
+ * pool. Trying to release an unallocated channel is an illegal
+ * operation and is trapped if assertions are enabled.
+ * @pre The channel must have been acquired using @p dmaAcquire().
+ * @post The channel is returned to the DMA engine pool.
+ */
+ void dmaRelease(msp430x_dma_ch_t * channel) {
+
+ osalDbgCheck(channel != NULL);
+
+ /* Release the channel in an idle mode */
+ channel->registers->ctl = DMAABORT;
+
+/* release the DMA counter */
#if CH_CFG_USE_SEMAPHORES
- chSemSignal(&dma_lock);
+ chSemSignal(&dma_lock);
#endif
-}
+ }
#endif /* HAL_USE_DMA == TRUE */
diff --git a/os/hal/ports/MSP430X/hal_dma_lld.h b/os/hal/ports/MSP430X/hal_dma_lld.h
index 2cce85d..d1495d2 100644
--- a/os/hal/ports/MSP430X/hal_dma_lld.h
+++ b/os/hal/ports/MSP430X/hal_dma_lld.h
@@ -34,18 +34,18 @@
/*===========================================================================*/
#define MSP430X_DMA_SINGLE DMADT_0
-#define MSP430X_DMA_BLOCK DMADT_1
-#define MSP430X_DMA_BURST DMADT_2
+#define MSP430X_DMA_BLOCK DMADT_1
+#define MSP430X_DMA_BURST DMADT_2
-#define MSP430X_DMA_SRCINCR DMASRCINCR_3
-#define MSP430X_DMA_SRCDECR DMASRCINCR_2
-#define MSP430X_DMA_DSTINCR DMADSTINCR_3
-#define MSP430X_DMA_DSTDECR DMADSTINCR_2
+#define MSP430X_DMA_SRCINCR DMASRCINCR_3
+#define MSP430X_DMA_SRCDECR DMASRCINCR_2
+#define MSP430X_DMA_DSTINCR DMADSTINCR_3
+#define MSP430X_DMA_DSTDECR DMADSTINCR_2
-#define MSP430X_DMA_SRCBYTE DMASRCBYTE
-#define MSP430X_DMA_DSTBYTE DMADSTBYTE
-#define MSP430X_DMA_SRCWORD 0
-#define MSP430X_DMA_DSTWORD 0
+#define MSP430X_DMA_SRCBYTE DMASRCBYTE
+#define MSP430X_DMA_DSTBYTE DMADSTBYTE
+#define MSP430X_DMA_SRCWORD 0
+#define MSP430X_DMA_DSTWORD 0
/*===========================================================================*/
/* Driver pre-compile time settings. */
@@ -56,8 +56,8 @@
/*===========================================================================*/
#if !defined(DMA_BASE) && !defined(MSP430X_DMA_SOFTWARE)
- #error "The MSP430 device in use does not support DMA. Explicitly enable"
- #error "software support by defining MSP430X_DMA_SOFTWARE."
+#error "The MSP430 device in use does not support DMA. Explicitly enable"
+#error "software emulation by defining MSP430X_DMA_SOFTWARE."
#endif
#if defined(__MSP430_HAS_DMAX_1__) || defined(__MSP430X_HAS_DMA_1__)
@@ -67,7 +67,7 @@
#elif defined(__MSP430_HAS_DMAX_6__) || defined(__MSP430X_HAS_DMA_6__)
#define MSP430X_DMA_CHANNELS 6
#else
- #error "Unexpected error - how many DMA channels does your MSP have?"
+#error "Unexpected error - how many DMA channels does your MSP have?"
#endif
/*===========================================================================*/
@@ -77,28 +77,28 @@
/**
* @brief Type of DMA callback function pointer.
*/
-typedef void (*msp430x_dma_cbp_t)(void *args);
+typedef void (*msp430x_dma_cbp_t)(void * args);
/**
* @brief DMA callback, function and argument.
*/
typedef struct {
- msp430x_dma_cbp_t callback; /**< @brief Callback function pointer */
- void * args; /**< @brief Callback function arguments */
+ msp430x_dma_cbp_t callback; /**< @brief Callback function pointer */
+ void * args; /**< @brief Callback function arguments */
} msp430x_dma_cb_t;
/**
* @brief MSP430X DMA request structure.
*/
typedef struct {
- void * source_addr; /**< @brief Source address */
- void * dest_addr; /**< @brief Destination address */
- uint16_t size; /**< @brief Number of values to transfer */
- uint16_t addr_mode; /**< @brief Address manipulation mode */
- uint16_t data_mode; /**< @brief Data sizes (b2b, w2w, b2w, w2b) */
- uint16_t transfer_mode; /**< @brief Transfer mode (single, block, burst) */
- uint16_t trigger; /**< @brief Triggering event (see datasheet) */
- msp430x_dma_cb_t callback;/**< @brief Callback function and arguments */
+ const void * source_addr; /**< @brief Source address */
+ void * dest_addr; /**< @brief Destination address */
+ uint16_t size; /**< @brief Number of values to transfer */
+ uint16_t addr_mode; /**< @brief Address manipulation mode */
+ uint16_t data_mode; /**< @brief Data sizes (b2b, w2w, b2w, w2b) */
+ uint16_t transfer_mode; /**< @brief Transfer mode (single, block, burst) */
+ uint16_t trigger; /**< @brief Triggering event (see datasheet) */
+ msp430x_dma_cb_t callback; /**< @brief Callback function and arguments */
} msp430x_dma_req_t;
/**
@@ -133,7 +133,7 @@ typedef struct {
*/
typedef struct {
msp430x_dma_ch_reg_t * registers; /**< @brief Pointer to channel registers */
- volatile uint8_t * ctl; /**< @brief Pointer to channel control register */
+ uint8_t index; /**< @brief Index of channel trigger control register */
msp430x_dma_cb_t * cb; /**< @brief Pointer to callback function and args */
} msp430x_dma_ch_t;
@@ -143,11 +143,11 @@ typedef struct {
/**
* @brief Identifies a DMA trigger using a mnemonic.
- *
+ *
* @param[in] mnem The mnemonic for the trigger, e.g. UCA0RXIFG to trigger
* on UART receive.
*/
-#define DMA_TRIGGER_MNEM(mnem) DMA0TSEL__ ## mnem
+#define DMA_TRIGGER_MNEM(mnem) DMA0TSEL__##mnem
/** @} */
@@ -158,12 +158,12 @@ typedef struct {
#ifdef __cplusplus
extern "C" {
#endif
- void dmaInit(void);
- bool dmaRequest(msp430x_dma_req_t * request, systime_t timeout);
- bool dmaAcquire(msp430x_dma_ch_t * channel, uint8_t index);
- void dmaTransfer(msp430x_dma_ch_t * channel, msp430x_dma_req_t * request);
- void dmaRelease(msp430x_dma_ch_t * channel);
-
+void dmaInit(void);
+bool dmaRequest(msp430x_dma_req_t * request, systime_t timeout);
+bool dmaAcquire(msp430x_dma_ch_t * channel, uint8_t index);
+void dmaTransfer(msp430x_dma_ch_t * channel, msp430x_dma_req_t * request);
+void dmaRelease(msp430x_dma_ch_t * channel);
+
#ifdef __cplusplus
}
#endif
@@ -171,4 +171,3 @@ extern "C" {
#endif /* HAL_USE_DMA == true */
#endif /* HAL_MSP430X_DMA_H */
-
diff --git a/os/hal/ports/MSP430X/hal_pal_lld.h b/os/hal/ports/MSP430X/hal_pal_lld.h
index 8789ed1..0b6363b 100644
--- a/os/hal/ports/MSP430X/hal_pal_lld.h
+++ b/os/hal/ports/MSP430X/hal_pal_lld.h
@@ -42,25 +42,25 @@
/**
* @brief Alternate mode 1
*/
-#define PAL_MSP430X_ALTERNATE_1 8
+#define PAL_MSP430X_ALTERNATE_1 8
/**
* @brief Alternate mode 2
*/
-#define PAL_MSP430X_ALTERNATE_2 9
+#define PAL_MSP430X_ALTERNATE_2 9
/**
* @brief Alternate mode 3
*/
-#define PAL_MSP430X_ALTERNATE_3 10
+#define PAL_MSP430X_ALTERNATE_3 10
-#define ALTERNATE_HELP(n) (PAL_MSP430X_ALTERNATE_ ## n)
+#define ALTERNATE_HELP(n) (PAL_MSP430X_ALTERNATE_##n)
/**
* @brief Alternate function.
- *
+ *
* @param[in] n alternate function selector - 1 through 3
*/
-#define PAL_MODE_ALTERNATE(n) (ALTERNATE_HELP(n))
+#define PAL_MODE_ALTERNATE(n) (ALTERNATE_HELP(n))
/** @} */
@@ -75,17 +75,16 @@
/**
* @brief Width, in bits, of an I/O port.
*/
-#define PAL_IOPORTS_WIDTH 16U
+#define PAL_IOPORTS_WIDTH 16U
/**
* @brief Whole port mask.
* @details This macro specifies all the valid bits into a port.
*/
-#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFU)
+#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFU)
/** @} */
-
/**
* @name Line handling macros
* @{
@@ -97,25 +96,24 @@
* @note In this driver the pad number is encoded in the upper 4 bits of
* the GPIO address which are guaranteed to be zero.
*/
-#define PAL_LINE(port, pad) \
+#define PAL_LINE(port, pad) \
((ioline_t)((uint16_t)(port)) | (((uint16_t)(pad)) << 12))
/**
* @brief Decodes a port identifier from a line identifier.
*/
-#define PAL_PORT(line) \
+#define PAL_PORT(line) \
((msp430x_gpio_registers_t *)(((uint16_t)(line)) & 0x0FFFU))
/**
* @brief Decodes a pad identifier from a line identifier.
*/
-#define PAL_PAD(line) \
- ((uint16_t)((uint16_t)(line) >> 12))
+#define PAL_PAD(line) ((uint16_t)((uint16_t)(line) >> 12))
/**
* @brief Value identifying an invalid line.
*/
-#define PAL_NOLINE 0U
+#define PAL_NOLINE 0U
/** @} */
/**
@@ -220,35 +218,35 @@ typedef msp430x_gpio_registers_t * ioportid_t;
* @brief GPIO port A identifier.
*/
#if defined(PA_BASE) || defined(__DOXYGEN__)
-#define IOPORT1 ((volatile msp430x_gpio_registers_t *)PA_BASE)
+#define IOPORT1 ((volatile msp430x_gpio_registers_t *)PA_BASE)
#endif
/**
* @brief GPIO port B identifier.
*/
#if defined(PB_BASE) || defined(__DOXYGEN__)
-#define IOPORT2 ((volatile msp430x_gpio_registers_t *)PB_BASE)
+#define IOPORT2 ((volatile msp430x_gpio_registers_t *)PB_BASE)
#endif
/**
* @brief GPIO port C identifier.
*/
#if defined(PC_BASE) || defined(__DOXYGEN__)
-#define IOPORT3 ((volatile msp430x_gpio_registers_t *)PC_BASE)
+#define IOPORT3 ((volatile msp430x_gpio_registers_t *)PC_BASE)
#endif
/**
* @brief GPIO port D identifier.
*/
#if defined(PD_BASE) || defined(__DOXYGEN__)
-#define IOPORT4 ((volatile msp430x_gpio_registers_t *)PD_BASE)
+#define IOPORT4 ((volatile msp430x_gpio_registers_t *)PD_BASE)
#endif
/**
* @brief GPIO port E identifier.
*/
#if defined(PE_BASE) || defined(__DOXYGEN__)
-#define IOPORT5 ((volatile msp430x_gpio_registers_t *)PE_BASE)
+#define IOPORT5 ((volatile msp430x_gpio_registers_t *)PE_BASE)
#endif
/**
@@ -261,7 +259,7 @@ typedef msp430x_gpio_registers_t * ioportid_t;
/**
* @brief GPIO port J identifier.
*/
-#define IOPORT0 ((volatile msp430x_gpio_registers_t *)PJ_BASE)
+#define IOPORT0 ((volatile msp430x_gpio_registers_t *)PJ_BASE)
/*===========================================================================*/
/* Implementation, some of the following macros could be implemented as */
@@ -307,8 +305,7 @@ typedef msp430x_gpio_registers_t * ioportid_t;
*
* @notapi
*/
-#define pal_lld_writeport(port, bits) ((port)->out = (bits))
-
+#define pal_lld_writeport(port, bits) ((port)->out = (bits))
/**
* @brief Sets a bits mask on a I/O port.
@@ -353,7 +350,7 @@ typedef msp430x_gpio_registers_t * ioportid_t;
*
* @notapi
*/
-#define pal_lld_setgroupmode(port, mask, offset, mode) \
+#define pal_lld_setgroupmode(port, mask, offset, mode) \
_pal_lld_setgroupmode(port, mask << offset, mode)
/**
@@ -366,7 +363,7 @@ typedef msp430x_gpio_registers_t * ioportid_t;
*
* @notapi
*/
-#define pal_lld_clearpad(port, pad) ((port)->out &= ~(BIT ## pad))
+#define pal_lld_clearpad(port, pad) ((port)->out &= ~(1 << pad))
#if !defined(__DOXYGEN__)
extern const PALConfig pal_default_config;
@@ -375,10 +372,8 @@ extern const PALConfig pal_default_config;
#ifdef __cplusplus
extern "C" {
#endif
- void _pal_lld_init(const PALConfig *config);
- void _pal_lld_setgroupmode(ioportid_t port,
- ioportmask_t mask,
- iomode_t mode);
+void _pal_lld_init(const PALConfig * config);
+void _pal_lld_setgroupmode(ioportid_t port, ioportmask_t mask, iomode_t mode);
#ifdef __cplusplus
}
#endif
diff --git a/os/hal/ports/MSP430X/hal_serial_lld.c b/os/hal/ports/MSP430X/hal_serial_lld.c
index bc002cb..0d9aa1c 100644
--- a/os/hal/ports/MSP430X/hal_serial_lld.c
+++ b/os/hal/ports/MSP430X/hal_serial_lld.c
@@ -37,33 +37,49 @@
/** @brief USART0 serial driver identifier.*/
#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__)
#ifndef __MSP430_HAS_EUSCI_A0__
- #error "Cannot find USCI module to use for SD0"
+#error "Cannot find USCI module to use for SD0"
+#endif
+#ifdef MSP430X_USCI_A0_USED
+#error "USCI module A0 already in use - USART0 not available"
#endif
SerialDriver SD0;
+#define MSP430X_USCI_A0_USED
#endif
/** @brief USART1 serial driver identifier.*/
#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__)
#ifndef __MSP430_HAS_EUSCI_A1__
- #error "Cannot find USCI module to use for SD1"
+#error "Cannot find USCI module to use for SD1"
+#endif
+#ifdef MSP430X_USCI_A1_USED
+#error "USCI module A1 already in use - USART1 not available"
#endif
SerialDriver SD1;
+#define MSP430X_USCI_A1_USED
#endif
/** @brief USART2 serial driver identifier.*/
#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__)
#ifndef __MSP430_HAS_EUSCI_A2__
- #error "Cannot find USCI module to use for SD2"
+#error "Cannot find USCI module to use for SD2"
+#endif
+#ifdef MSP430X_USCI_A2_USED
+#error "USCI module A2 already in use - USART2 not available"
#endif
SerialDriver SD2;
+#define MSP430X_USCI_A2_USED
#endif
/** @brief USART3 serial driver identifier.*/
#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__)
#ifndef __MSP430_HAS_EUSCI_A3__
- #error "Cannot find USCI module to use for SD3"
+#error "Cannot find USCI module to use for SD3"
+#endif
+#ifdef MSP430X_USCI_A3_USED
+#error "USCI module A3 already in use - USART3 not available"
#endif
SerialDriver SD3;
+#define MSP430X_USCI_A3_USED
#endif
/*===========================================================================*/
@@ -73,9 +89,7 @@ SerialDriver SD3;
/**
* @brief Driver default configuration.
*/
-static const SerialConfig default_config = {
- SERIAL_DEFAULT_BITRATE
-};
+static const SerialConfig default_config = { SERIAL_DEFAULT_BITRATE };
/*===========================================================================*/
/* Driver local functions. */
@@ -85,7 +99,7 @@ static const SerialConfig default_config = {
* @brief UCBRS calculation.
* @details This function calculates the UCBRS value for oversampled baud
* rates.
- *
+ *
* @param[in] frac Fractional part of baud rate division, times 10000.
*/
static uint8_t UCBRS(uint16_t frac) {
@@ -160,7 +174,7 @@ static uint8_t UCBRS(uint16_t frac) {
return 0xFB;
else if (frac < 9288)
return 0xFD;
- else
+ else
return 0xFE;
}
@@ -168,13 +182,13 @@ static uint8_t UCBRS(uint16_t frac) {
* @brief Modulation control word calculator.
* @details This function calculates the modulation control word from the
* input clock frequency and the requested baud rate.
- *
+ *
* @param[in] baud Requested baud rate
* @param[in] freq Frequency of the clock driving the USCI module
*/
static uint16_t UCAxMCTLW(uint32_t baud, uint32_t freq) {
-
- uint16_t n = freq/baud;
+
+ uint16_t n = freq / baud;
/*uint16_t frac = (freq * 10000 / baud) - ((freq / baud) * 10000);*/
uint16_t frac = (freq - (n * baud)) * 10000 / baud;
if (n > 16) {
@@ -190,12 +204,12 @@ static uint16_t UCAxMCTLW(uint32_t baud, uint32_t freq) {
* @brief UCBRW calculation.
* @details This function calculates the UCBRW value for all baud
* rates.
- *
+ *
* @param[in] baud Requested baud rate
* @param[in] freq Frequency of the clock driving the USCI module
*/
static uint16_t UCAxBRW(uint32_t baud, uint32_t freq) {
- uint16_t n = freq/baud;
+ uint16_t n = freq / baud;
if (n > 16) {
return n >> 4;
}
@@ -203,88 +217,88 @@ static uint16_t UCAxBRW(uint32_t baud, uint32_t freq) {
}
#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__)
-static void usart0_init(const SerialConfig *config) {
- UCA0BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART0_CLK_FREQ);
+static void usart0_init(const SerialConfig * config) {
+ UCA0BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART0_CLK_FREQ);
UCA0MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART0_CLK_FREQ);
UCA0STATW = 0;
UCA0ABCTL = 0;
UCA0IRCTL = 0;
- UCA0CTLW0 = (MSP430X_USART0_PARITY << 14) | (MSP430X_USART0_ORDER << 13) | \
- (MSP430X_USART0_SIZE << 12) | (MSP430X_USART0_STOP << 11) | \
+ UCA0CTLW0 = (MSP430X_USART0_PARITY << 14) | (MSP430X_USART0_ORDER << 13) |
+ (MSP430X_USART0_SIZE << 12) | (MSP430X_USART0_STOP << 11) |
(MSP430X_USART0_UCSSEL);
UCA0IE = UCRXIE;
}
#endif
#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__)
-static void usart1_init(const SerialConfig *config) {
- UCA1BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART1_CLK_FREQ);
+static void usart1_init(const SerialConfig * config) {
+ UCA1BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART1_CLK_FREQ);
UCA1MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART1_CLK_FREQ);
UCA1STATW = 0;
UCA1ABCTL = 0;
UCA1IRCTL = 0;
- UCA1CTLW0 = (MSP430X_USART1_PARITY << 14) | (MSP430X_USART1_ORDER << 13) | \
- (MSP430X_USART1_SIZE << 12) | (MSP430X_USART1_STOP << 11) | \
+ UCA1CTLW0 = (MSP430X_USART1_PARITY << 14) | (MSP430X_USART1_ORDER << 13) |
+ (MSP430X_USART1_SIZE << 12) | (MSP430X_USART1_STOP << 11) |
(MSP430X_USART1_UCSSEL);
UCA1IE = UCRXIE;
}
#endif
#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__)
-static void usart2_init(const SerialConfig *config) {
- UCA2BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART2_CLK_FREQ);
+static void usart2_init(const SerialConfig * config) {
+ UCA2BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART2_CLK_FREQ);
UCA2MCTLW = UCAxMCTLW(config->sc_bitrate);
UCA2STATW = 0;
UCA2ABCTL = 0;
UCA2IRCTL = 0;
- UCA2CTLW0 = (MSP430X_USART2_PARITY << 14) | (MSP430X_USART2_ORDER << 13) | \
- (MSP430X_USART2_SIZE << 12) | (MSP430X_USART2_STOP << 11) | \
+ UCA2CTLW0 = (MSP430X_USART2_PARITY << 14) | (MSP430X_USART2_ORDER << 13) |
+ (MSP430X_USART2_SIZE << 12) | (MSP430X_USART2_STOP << 11) |
(MSP430X_USART2_UCSSEL);
UCA2IE = UCRXIE;
}
#endif
#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__)
-static void usart3_init(const SerialConfig *config) {
- UCA3BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART3_CLK_FREQ);
+static void usart3_init(const SerialConfig * config) {
+ UCA3BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART3_CLK_FREQ);
UCA3MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART3_CLK_FREQ);
UCA3STATW = 0;
UCA3ABCTL = 0;
UCA3IRCTL = 0;
- UCA3CTLW0 = (MSP430X_USART3_PARITY << 14) | (MSP430X_USART3_ORDER << 13) | \
- (MSP430X_USART3_SIZE << 12) | (MSP430X_USART3_STOP << 11) | \
+ UCA3CTLW0 = (MSP430X_USART3_PARITY << 14) | (MSP430X_USART3_ORDER << 13) |
+ (MSP430X_USART3_SIZE << 12) | (MSP430X_USART3_STOP << 11) |
(MSP430X_USART3_UCSSEL);
UCA3IE = UCRXIE;
}
#endif
#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__)
-static void notify0(io_queue_t *qp) {
-
+static void notify0(io_queue_t * qp) {
+
(void)qp;
UCA0IE |= UCTXIE;
}
#endif
#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__)
-static void notify1(io_queue_t *qp) {
-
+static void notify1(io_queue_t * qp) {
+
(void)qp;
UCA1IE |= UCTXIE;
}
#endif
#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__)
-static void notify2(io_queue_t *qp) {
-
+static void notify2(io_queue_t * qp) {
+
(void)qp;
UCA2IE |= UCTXIE;
}
#endif
#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__)
-static void notify3(io_queue_t *qp) {
-
+static void notify3(io_queue_t * qp) {
+
(void)qp;
UCA3IE |= UCTXIE;
}
@@ -292,24 +306,23 @@ static void notify3(io_queue_t *qp) {
/**
* @brief Error handling routine.
- *
+ *
* @param[in] sra USCI status register containing errors
* @param[in] sdp pointer to a @p SerialDriver object
*/
-static void set_error(uint16_t sra, SerialDriver *sdp) {
- eventflags_t sts = 0;
-
- if (sra & UCOE)
- sts |= SD_OVERRUN_ERROR;
- if (sra & UCPE)
- sts |= SD_PARITY_ERROR;
- if (sra & UCFE)
- sts |= SD_FRAMING_ERROR;
- osalSysLockFromISR();
- chnAddFlagsI(sdp, sts);
- osalSysUnlockFromISR();
+static void set_error(uint16_t sra, SerialDriver * sdp) {
+ eventflags_t sts = 0;
+
+ if (sra & UCOE)
+ sts |= SD_OVERRUN_ERROR;
+ if (sra & UCPE)
+ sts |= SD_PARITY_ERROR;
+ if (sra & UCFE)
+ sts |= SD_FRAMING_ERROR;
+ osalSysLockFromISR();
+ chnAddFlagsI(sdp, sts);
+ osalSysUnlockFromISR();
}
-
/*===========================================================================*/
/* Driver interrupt handlers. */
@@ -318,236 +331,235 @@ static void set_error(uint16_t sra, SerialDriver *sdp) {
#if MSP430X_SERIAL_USE_USART0 || defined(__DOXYGEN__)
/**
* @brief USART0 interrupt handler.
- *
+ *
* @isr
*/
PORT_IRQ_HANDLER(USCI_A0_VECTOR) {
msg_t b;
-
+
OSAL_IRQ_PROLOGUE();
-
- switch (__even_in_range(UCA0IV,USCI_UART_UCTXCPTIFG)) {
- case USCI_UART_UCRXIFG: /* RX interrupt */
-
- /* Detect errors */
- if (UCA0STATW & UCRXERR)
- set_error(UCA0STATW, &SD0);
-
- /* Data available */
- osalSysLockFromISR();
- sdIncomingDataI(&SD0, UCA0RXBUF);
- osalSysUnlockFromISR();
- break;
-
- case USCI_UART_UCTXIFG: /* TX interrupt */
-
- /* Transmission buffer empty */
- osalSysLockFromISR();
- b = sdRequestDataI(&SD0);
- if (b < Q_OK) {
- chnAddFlagsI(&SD0, CHN_OUTPUT_EMPTY);
- UCA0IE = (UCA0IE & ~UCTXIE) | UCTXCPTIE;
- UCA0IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
- }
- else
- UCA0TXBUF = b;
- osalSysUnlockFromISR();
- break;
-
- case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
-
- /* Physical transmission end */
- osalSysLockFromISR();
- if (oqIsEmptyI(&SD0.oqueue))
- chnAddFlagsI(&SD0, CHN_TRANSMISSION_END);
- UCA0IE &= ~UCTXCPTIE;
- break;
-
- default: /* other interrupts */
- while (1);
- break;
+
+ switch (__even_in_range(UCA0IV, USCI_UART_UCTXCPTIFG)) {
+ case USCI_UART_UCRXIFG: /* RX interrupt */
+
+ /* Detect errors */
+ if (UCA0STATW & UCRXERR)
+ set_error(UCA0STATW, &SD0);
+
+ /* Data available */
+ osalSysLockFromISR();
+ sdIncomingDataI(&SD0, UCA0RXBUF);
+ osalSysUnlockFromISR();
+ break;
+
+ case USCI_UART_UCTXIFG: /* TX interrupt */
+
+ /* Transmission buffer empty */
+ osalSysLockFromISR();
+ b = sdRequestDataI(&SD0);
+ if (b < Q_OK) {
+ chnAddFlagsI(&SD0, CHN_OUTPUT_EMPTY);
+ UCA0IE = (UCA0IE & ~UCTXIE) | UCTXCPTIE;
+ UCA0IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
+ }
+ else
+ UCA0TXBUF = b;
+ osalSysUnlockFromISR();
+ break;
+
+ case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
+
+ /* Physical transmission end */
+ osalSysLockFromISR();
+ if (oqIsEmptyI(&SD0.oqueue))
+ chnAddFlagsI(&SD0, CHN_TRANSMISSION_END);
+ UCA0IE &= ~UCTXCPTIE;
+ break;
+
+ default: /* other interrupts */
+ while (1)
+ ;
+ break;
}
-
+
OSAL_IRQ_EPILOGUE();
-
}
#endif
#if MSP430X_SERIAL_USE_USART1 || defined(__DOXYGEN__)
/**
* @brief USART1 interrupt handler.
- *
+ *
* @isr
*/
PORT_IRQ_HANDLER(USCI_A1_VECTOR) {
msg_t b;
-
+
OSAL_IRQ_PROLOGUE();
-
- switch (__even_in_range(UCA1IV,USCI_UART_UCTXCPTIFG)) {
- case USCI_UART_UCRXIFG: /* RX interrupt */
-
- /* Detect errors */
- if (UCA1STATW & UCRXERR)
- set_error(UCA1STATW, &SD1);
-
- /* Data available */
- osalSysLockFromISR();
- sdIncomingDataI(&SD1, UCA1RXBUF);
- osalSysUnlockFromISR();
- break;
-
- case USCI_UART_UCTXIFG: /* TX interrupt */
-
- /* Transmission buffer empty */
- osalSysLockFromISR();
- b = sdRequestDataI(&SD1);
- if (b < Q_OK) {
- chnAddFlagsI(&SD1, CHN_OUTPUT_EMPTY);
- UCA1IE = (UCA1IE & ~UCTXIE) | UCTXCPTIE;
- UCA1IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
- }
- else
- UCA1TXBUF = b;
- osalSysUnlockFromISR();
- break;
-
- case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
-
- /* Physical transmission end */
- osalSysLockFromISR();
- if (oqIsEmptyI(&SD1.oqueue))
- chnAddFlagsI(&SD1, CHN_TRANSMISSION_END);
- UCA1IE &= ~UCTXCPTIE;
- break;
-
- default: /* other interrupts */
- while (1);
- break;
+
+ switch (__even_in_range(UCA1IV, USCI_UART_UCTXCPTIFG)) {
+ case USCI_UART_UCRXIFG: /* RX interrupt */
+
+ /* Detect errors */
+ if (UCA1STATW & UCRXERR)
+ set_error(UCA1STATW, &SD1);
+
+ /* Data available */
+ osalSysLockFromISR();
+ sdIncomingDataI(&SD1, UCA1RXBUF);
+ osalSysUnlockFromISR();
+ break;
+
+ case USCI_UART_UCTXIFG: /* TX interrupt */
+
+ /* Transmission buffer empty */
+ osalSysLockFromISR();
+ b = sdRequestDataI(&SD1);
+ if (b < Q_OK) {
+ chnAddFlagsI(&SD1, CHN_OUTPUT_EMPTY);
+ UCA1IE = (UCA1IE & ~UCTXIE) | UCTXCPTIE;
+ UCA1IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
+ }
+ else
+ UCA1TXBUF = b;
+ osalSysUnlockFromISR();
+ break;
+
+ case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
+
+ /* Physical transmission end */
+ osalSysLockFromISR();
+ if (oqIsEmptyI(&SD1.oqueue))
+ chnAddFlagsI(&SD1, CHN_TRANSMISSION_END);
+ UCA1IE &= ~UCTXCPTIE;
+ break;
+
+ default: /* other interrupts */
+ while (1)
+ ;
+ break;
}
-
+
OSAL_IRQ_EPILOGUE();
-
}
#endif
#if MSP430X_SERIAL_USE_USART2 || defined(__DOXYGEN__)
/**
* @brief USART2 interrupt handler.
- *
+ *
* @isr
*/
PORT_IRQ_HANDLER(USCI_A2_VECTOR) {
msg_t b;
-
+
OSAL_IRQ_PROLOGUE();
-
- switch (__even_in_range(UCA2IV,USCI_UART_UCTXCPTIFG)) {
- case USCI_UART_UCRXIFG: /* RX interrupt */
-
- /* Detect errors */
- if (UCA2STATW & UCRXERR)
- set_error(UCA2STATW, &SD2);
-
- /* Data available */
- osalSysLockFromISR();
- sdIncomingDataI(&SD2, UCA2RXBUF);
- osalSysUnlockFromISR();
- break;
-
- case USCI_UART_UCTXIFG: /* TX interrupt */
-
- /* Transmission buffer empty */
- osalSysLockFromISR();
- b = sdRequestDataI(&SD2);
- if (b < Q_OK) {
- chnAddFlagsI(&SD2, CHN_OUTPUT_EMPTY);
- UCA2IE = (UCA2IE & ~UCTXIE) | UCTXCPTIE;
- UCA2IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
- }
- else
- UCA2TXBUF = b;
- osalSysUnlockFromISR();
- break;
-
- case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
-
- /* Physical transmission end */
- osalSysLockFromISR();
- if (oqIsEmptyI(&SD2.oqueue))
- chnAddFlagsI(&SD2, CHN_TRANSMISSION_END);
- UCA2IE &= ~UCTXCPTIE;
- break;
-
- default: /* other interrupts */
- while (1);
- break;
+
+ switch (__even_in_range(UCA2IV, USCI_UART_UCTXCPTIFG)) {
+ case USCI_UART_UCRXIFG: /* RX interrupt */
+
+ /* Detect errors */
+ if (UCA2STATW & UCRXERR)
+ set_error(UCA2STATW, &SD2);
+
+ /* Data available */
+ osalSysLockFromISR();
+ sdIncomingDataI(&SD2, UCA2RXBUF);
+ osalSysUnlockFromISR();
+ break;
+
+ case USCI_UART_UCTXIFG: /* TX interrupt */
+
+ /* Transmission buffer empty */
+ osalSysLockFromISR();
+ b = sdRequestDataI(&SD2);
+ if (b < Q_OK) {
+ chnAddFlagsI(&SD2, CHN_OUTPUT_EMPTY);
+ UCA2IE = (UCA2IE & ~UCTXIE) | UCTXCPTIE;
+ UCA2IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
+ }
+ else
+ UCA2TXBUF = b;
+ osalSysUnlockFromISR();
+ break;
+
+ case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
+
+ /* Physical transmission end */
+ osalSysLockFromISR();
+ if (oqIsEmptyI(&SD2.oqueue))
+ chnAddFlagsI(&SD2, CHN_TRANSMISSION_END);
+ UCA2IE &= ~UCTXCPTIE;
+ break;
+
+ default: /* other interrupts */
+ while (1)
+ ;
+ break;
}
-
+
OSAL_IRQ_EPILOGUE();
-
}
#endif
#if MSP430X_SERIAL_USE_USART3 || defined(__DOXYGEN__)
/**
* @brief USART3 interrupt handler.
- *
+ *
* @isr
*/
PORT_IRQ_HANDLER(USCI_A3_VECTOR) {
msg_t b;
-
+
OSAL_IRQ_PROLOGUE();
-
- switch (__even_in_range(UCA3IV,USCI_UART_UCTXCPTIFG)) {
- case USCI_UART_UCRXIFG: /* RX interrupt */
-
- /* Detect errors */
- if (UCA3STATW & UCRXERR)
- set_error(UCA3STATW, &SD3);
-
- /* Data available */
- osalSysLockFromISR();
- sdIncomingDataI(&SD3, UCA3RXBUF);
- osalSysUnlockFromISR();
- break;
-
- case USCI_UART_UCTXIFG: /* TX interrupt */
-
- /* Transmission buffer empty */
- osalSysLockFromISR();
- b = sdRequestDataI(&SD3);
- if (b < Q_OK) {
- chnAddFlagsI(&SD3, CHN_OUTPUT_EMPTY);
- UCA3IE = (UCA3IE & ~UCTXIE) | UCTXCPTIE;
- UCA3IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
- }
- else
- UCA3TXBUF = b;
- osalSysUnlockFromISR();
- break;
-
- case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
-
- /* Physical transmission end */
- osalSysLockFromISR();
- if (oqIsEmptyI(&SD3.oqueue))
- chnAddFlagsI(&SD3, CHN_TRANSMISSION_END);
- UCA3IE &= ~UCTXCPTIE;
- break;
-
- default: /* other interrupts */
- while (1);
- break;
+
+ switch (__even_in_range(UCA3IV, USCI_UART_UCTXCPTIFG)) {
+ case USCI_UART_UCRXIFG: /* RX interrupt */
+
+ /* Detect errors */
+ if (UCA3STATW & UCRXERR)
+ set_error(UCA3STATW, &SD3);
+
+ /* Data available */
+ osalSysLockFromISR();
+ sdIncomingDataI(&SD3, UCA3RXBUF);
+ osalSysUnlockFromISR();
+ break;
+
+ case USCI_UART_UCTXIFG: /* TX interrupt */
+
+ /* Transmission buffer empty */
+ osalSysLockFromISR();
+ b = sdRequestDataI(&SD3);
+ if (b < Q_OK) {
+ chnAddFlagsI(&SD3, CHN_OUTPUT_EMPTY);
+ UCA3IE = (UCA3IE & ~UCTXIE) | UCTXCPTIE;
+ UCA3IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */
+ }
+ else
+ UCA3TXBUF = b;
+ osalSysUnlockFromISR();
+ break;
+
+ case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */
+
+ /* Physical transmission end */
+ osalSysLockFromISR();
+ if (oqIsEmptyI(&SD3.oqueue))
+ chnAddFlagsI(&SD3, CHN_TRANSMISSION_END);
+ UCA3IE &= ~UCTXCPTIE;
+ break;
+
+ default: /* other interrupts */
+ while (1)
+ ;
+ break;
}
-
+
OSAL_IRQ_EPILOGUE();
-
}
#endif
-
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
@@ -586,13 +598,12 @@ void sd_lld_init(void) {
*
* @notapi
*/
-void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) {
+void sd_lld_start(SerialDriver * sdp, const SerialConfig * config) {
if (config == NULL) {
config = &default_config;
}
-
if (sdp->state == SD_STOP) {
#if MSP430X_SERIAL_USE_USART0 == TRUE
if (&SD0 == sdp) {
@@ -626,7 +637,7 @@ void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) {
*
* @notapi
*/
-void sd_lld_stop(SerialDriver *sdp) {
+void sd_lld_stop(SerialDriver * sdp) {
if (sdp->state == SD_READY) {
#if MSP430X_SERIAL_USE_USART0 == TRUE
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 */
+
+/** @} */
diff --git a/os/hal/ports/MSP430X/hal_spi_lld.h b/os/hal/ports/MSP430X/hal_spi_lld.h
new file mode 100644
index 0000000..ebf14c8
--- /dev/null
+++ b/os/hal/ports/MSP430X/hal_spi_lld.h
@@ -0,0 +1,642 @@
+/*
+ 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.h
+ * @brief MSP430X SPI subsystem low level driver header.
+ *
+ * @addtogroup SPI
+ * @{
+ */
+
+#ifndef HAL_SPI_LLD_H
+#define HAL_SPI_LLD_H
+
+#if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__)
+
+#include "hal_dma_lld.h"
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name MSP430X configuration options
+ * @{
+ */
+/**
+ * @brief SPIA0 driver enable switch.
+ * @details If set to @p TRUE the support for SPIA0 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_USE_SPIA0) || defined(__DOXYGEN__)
+#define MSP430X_SPI_USE_SPIA0 FALSE
+#endif
+
+/**
+ * @brief SPIA1 driver enable switch.
+ * @details If set to @p TRUE the support for SPIA1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_USE_SPIA1) || defined(__DOXYGEN__)
+#define MSP430X_SPI_USE_SPIA1 FALSE
+#endif
+
+/**
+ * @brief SPIA2 driver enable switch.
+ * @details If set to @p TRUE the support for SPIA2 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_USE_SPIA2) || defined(__DOXYGEN__)
+#define MSP430X_SPI_USE_SPIA2 FALSE
+#endif
+
+/**
+ * @brief SPIA3 driver enable switch.
+ * @details If set to @p TRUE the support for SPIA3 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_USE_SPIA3) || defined(__DOXYGEN__)
+#define MSP430X_SPI_USE_SPIA3 FALSE
+#endif
+
+/**
+ * @brief SPIB0 driver enable switch.
+ * @details If set to @p TRUE the support for SPIB0 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_USE_SPIB0) || defined(__DOXYGEN__)
+#define MSP430X_SPI_USE_SPIB0 FALSE
+#endif
+
+/**
+ * @brief SPIB1 driver enable switch.
+ * @details If set to @p TRUE the support for SPIB1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_USE_SPIB1) || defined(__DOXYGEN__)
+#define MSP430X_SPI_USE_SPIB1 FALSE
+#endif
+
+/**
+ * @brief SPIB2 driver enable switch.
+ * @details If set to @p TRUE the support for SPIB2 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_USE_SPIB2) || defined(__DOXYGEN__)
+#define MSP430X_SPI_USE_SPIB2 FALSE
+#endif
+
+/**
+ * @brief SPIB3 driver enable switch.
+ * @details If set to @p TRUE the support for SPIB3 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_USE_SPIB3) || defined(__DOXYGEN__)
+#define MSP430X_SPI_USE_SPIB3 FALSE
+#endif
+
+/**
+ * @brief Exclusive DMA enable switch.
+ * @details If set to @p TRUE the support for exclusive DMA is included.
+ * @note This increases the size of the compiled executable somewhat.
+ * @note The default is @p FALSE.
+ */
+#if !defined(MSP430X_SPI_EXCLUSIVE_DMA) | defined(__DOXYGEN__)
+#define MSP430X_SPI_EXCLUSIVE_DMA FALSE
+#endif
+
+/**
+ * @brief SPIA0 clock source switch.
+ * @details Sets the clock source for SPIA0.
+ * @note Legal values are @p MSP430X_SMCLK_SRC or @p MSP430X_ACLK_SRC.
+ * @note The default is @p MSP430X_SMCLK_SRC.
+ */
+#if !defined(MSP430X_SPIA0_CLK_SRC)
+ #define MSP430X_SPIA0_CLK_SRC MSP430X_SMCLK_SRC
+#endif
+
+/**
+ * @brief SPIA1 clock source switch.
+ * @details Sets the clock source for SPIA1.
+ * @note Legal values are @p MSP430X_SMCLK_SRC or @p MSP430X_ACLK_SRC.
+ * @note The default is @p MSP430X_SMCLK_SRC.
+ */
+#if !defined(MSP430X_SPIA1_CLK_SRC)
+ #define MSP430X_SPIA1_CLK_SRC MSP430X_SMCLK_SRC
+#endif
+
+/**
+ * @brief SPIA2 clock source switch.
+ * @details Sets the clock source for SPIA2.
+ * @note Legal values are @p MSP430X_SMCLK_SRC or @p MSP430X_ACLK_SRC.
+ * @note The default is @p MSP430X_SMCLK_SRC.
+ */
+#if !defined(MSP430X_SPIA2_CLK_SRC)
+ #define MSP430X_SPIA2_CLK_SRC MSP430X_SMCLK_SRC
+#endif
+
+/**
+ * @brief SPIA3 clock source switch.
+ * @details Sets the clock source for SPIA3.
+ * @note Legal values are @p MSP430X_SMCLK_SRC or @p MSP430X_ACLK_SRC.
+ * @note The default is @p MSP430X_SMCLK_SRC.
+ */
+#if !defined(MSP430X_SPIA3_CLK_SRC)
+ #define MSP430X_SPIA3_CLK_SRC MSP430X_SMCLK_SRC
+#endif
+
+/**
+ * @brief SPIB0 clock source switch.
+ * @details Sets the clock source for SPIB0.
+ * @note Legal values are @p MSP430X_SMCLK_SRC or @p MSP430X_ACLK_SRC.
+ * @note The default is @p MSP430X_SMCLK_SRC.
+ */
+#if !defined(MSP430X_SPIB0_CLK_SRC)
+ #define MSP430X_SPIB0_CLK_SRC MSP430X_SMCLK_SRC
+#endif
+
+/**
+ * @brief SPIB1 clock source switch.
+ * @details Sets the clock source for SPIB1.
+ * @note Legal values are @p MSP430X_SMCLK_SRC or @p MSP430X_ACLK_SRC.
+ * @note The default is @p MSP430X_SMCLK_SRC.
+ */
+#if !defined(MSP430X_SPIB1_CLK_SRC)
+ #define MSP430X_SPIB1_CLK_SRC MSP430X_SMCLK_SRC
+#endif
+
+/**
+ * @brief SPIB2 clock source switch.
+ * @details Sets the clock source for SPIB2.
+ * @note Legal values are @p MSP430X_SMCLK_SRC or @p MSP430X_ACLK_SRC.
+ * @note The default is @p MSP430X_SMCLK_SRC.
+ */
+#if !defined(MSP430X_SPIB2_CLK_SRC)
+ #define MSP430X_SPIB2_CLK_SRC MSP430X_SMCLK_SRC
+#endif
+
+/**
+ * @brief SPIB3 clock source switch.
+ * @details Sets the clock source for SPIB3.
+ * @note Legal values are @p MSP430X_SMCLK_SRC or @p MSP430X_ACLK_SRC.
+ * @note The default is @p MSP430X_SMCLK_SRC.
+ */
+#if !defined(MSP430X_SPIB3_CLK_SRC)
+ #define MSP430X_SPIB3_CLK_SRC MSP430X_SMCLK_SRC
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if MSP430X_SPI_USE_SPIA0 && !defined(__MSP430_HAS_EUSCI_A0__)
+ #error "Cannot find MSP430X_USCI module to use for SPIA0"
+#endif
+
+#if MSP430X_SPI_USE_SPIA1 && !defined(__MSP430_HAS_EUSCI_A1__)
+ #error "Cannot find MSP430X_USCI module to use for SPIA1"
+#endif
+
+#if MSP430X_SPI_USE_SPIA2 && !defined(__MSP430_HAS_EUSCI_A2__)
+ #error "Cannot find MSP430X_USCI module to use for SPIA2"
+#endif
+
+#if MSP430X_SPI_USE_SPIA3 && !defined(__MSP430_HAS_EUSCI_A3__)
+ #error "Cannot find MSP430X_USCI module to use for SPIA3"
+#endif
+
+#if MSP430X_SPI_USE_SPIB0 && !defined(__MSP430_HAS_EUSCI_B0__)
+ #error "Cannot find MSP430X_USCI module to use for SPIB0"
+#endif
+
+#if MSP430X_SPI_USE_SPIB1 && !defined(__MSP430_HAS_EUSCI_B1__)
+ #error "Cannot find MSP430X_USCI module to use for SPIB1"
+#endif
+
+#if MSP430X_SPI_USE_SPIB2 && !defined(__MSP430_HAS_EUSCI_B2__)
+ #error "Cannot find MSP430X_USCI module to use for SPIB2"
+#endif
+
+#if MSP430X_SPI_USE_SPIB3 && !defined(__MSP430_HAS_EUSCI_B3__)
+ #error "Cannot find MSP430X_USCI module to use for SPIB3"
+#endif
+
+#if MSP430X_SPI_USE_SPIA0
+ #ifdef MSP430X_USCI_A0_USED
+ #error "USCI module A0 already in use - SPIA0 not available"
+ #else
+ #define MSP430X_USCI_A0_USED
+ #endif
+#endif
+
+#if MSP430X_SPI_USE_SPIA1
+ #ifdef MSP430X_USCI_A1_USED
+ #error "USCI module A1 already in use - SPIA1 not available"
+ #else
+ #define MSP430X_USCI_A1_USED
+ #endif
+#endif
+
+#if MSP430X_SPI_USE_SPIA2
+ #ifdef MSP430X_USCI_A2_USED
+ #error "USCI module A2 already in use - SPIA2 not available"
+ #else
+ #define MSP430X_USCI_A2_USED
+ #endif
+#endif
+
+#if MSP430X_SPI_USE_SPIA3
+ #ifdef MSP430X_USCI_A3_USED
+ #error "USCI module A3 already in use - SPIA3 not available"
+ #else
+ #define MSP430X_USCI_A3_USED
+ #endif
+#endif
+
+#if MSP430X_SPI_USE_SPIB0
+ #ifdef MSP430X_USCI_B0_USED
+ #error "USCI module B0 already in use - SPIB0 not available"
+ #else
+ #define MSP430X_USCI_B0_USED
+ #endif
+#endif
+
+#if MSP430X_SPI_USE_SPIB1
+ #ifdef MSP430X_USCI_B1_USED
+ #error "USCI module B1 already in use - SPIB1 not available"
+ #else
+ #define MSP430X_USCI_B1_USED
+ #endif
+#endif
+
+#if MSP430X_SPI_USE_SPIB2
+ #ifdef MSP430X_USCI_B2_USED
+ #error "USCI module B2 already in use - SPIB2 not available"
+ #else
+ #define MSP430X_USCI_B2_USED
+ #endif
+#endif
+
+#if MSP430X_SPI_USE_SPIB3
+ #ifdef MSP430X_USCI_B3_USED
+ #error "USCI module B3 already in use - SPIB3 not available"
+ #else
+ #define MSP430X_USCI_B3_USED
+ #endif
+#endif
+
+#if defined(MSP430X_SPIA0_TX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIA0 TX, but requested index is invalid"
+#endif
+#if defined(MSP430X_SPIA0_RX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIA0 RX, but requested index is invalid"
+#endif
+
+#if defined(MSP430X_SPIA1_TX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIA1 TX, but requested index is invalid"
+#endif
+#if defined(MSP430X_SPIA1_RX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIA1 RX, but requested index is invalid"
+#endif
+
+#if defined(MSP430X_SPIA2_TX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIA2 TX, but requested index is invalid"
+#endif
+#if defined(MSP430X_SPIA2_RX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIA2 RX, but requested index is invalid"
+#endif
+
+#if defined(MSP430X_SPIA3_TX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIA3 TX, but requested index is invalid"
+#endif
+#if defined(MSP430X_SPIA3_RX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIA3 RX, but requested index is invalid"
+#endif
+
+#if defined(MSP430X_SPIB0_TX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIB0 TX, but requested index is invalid"
+#endif
+#if defined(MSP430X_SPIB0_RX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIB0 RX, but requested index is invalid"
+#endif
+
+#if defined(MSP430X_SPIB1_TX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIB1 TX, but requested index is invalid"
+#endif
+#if defined(MSP430X_SPIB1_RX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIB1 RX, but requested index is invalid"
+#endif
+
+#if defined(MSP430X_SPIB2_TX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIB2 TX, but requested index is invalid"
+#endif
+#if defined(MSP430X_SPIB2_RX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIB2 RX, but requested index is invalid"
+#endif
+
+#if defined(MSP430X_SPIB3_TX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIB3 TX, but requested index is invalid"
+#endif
+#if defined(MSP430X_SPIB3_RX_DMA) && (MSP430X_SPI_DMA >= MSP430X_DMA_CHANNELS)
+ #error "Requested DMA for SPIB3 RX, but requested index is invalid"
+#endif
+
+/* TODO figure out a way to check for conflicting DMA channels */
+
+#if MSP430X_SPIA0_CLK_SRC == MSP430X_ACLK_SRC
+ #define MSP430X_SPIA0_CLK_FREQ MSP430X_ACLK_FREQ
+ #define MSP430X_SPIA0_UCSSEL UCSSEL__ACLK
+#elif MSP430X_SPIA0_CLK_SRC == MSP430X_SMCLK_SRC
+ #define MSP430X_SPIA0_CLK_FREQ MSP430X_SMCLK_FREQ
+ #define MSP430X_SPIA0_UCSSEL UCSSEL__SMCLK
+#endif
+
+#if MSP430X_SPIA1_CLK_SRC == MSP430X_ACLK_SRC
+ #define MSP430X_SPIA1_CLK_FREQ MSP430X_ACLK_FREQ
+ #define MSP430X_SPIA1_UCSSEL UCSSEL__ACLK
+#elif MSP430X_SPIA1_CLK_SRC == MSP430X_SMCLK_SRC
+ #define MSP430X_SPIA1_CLK_FREQ MSP430X_SMCLK_FREQ
+ #define MSP430X_SPIA1_UCSSEL UCSSEL__SMCLK
+#endif
+
+#if MSP430X_SPIA2_CLK_SRC == MSP430X_ACLK_SRC
+ #define MSP430X_SPIA2_CLK_FREQ MSP430X_ACLK_FREQ
+ #define MSP430X_SPIA2_UCSSEL UCSSEL__ACLK
+#elif MSP430X_SPIA2_CLK_SRC == MSP430X_SMCLK_SRC
+ #define MSP430X_SPIA2_CLK_FREQ MSP430X_SMCLK_FREQ
+ #define MSP430X_SPIA2_UCSSEL UCSSEL__SMCLK
+#endif
+
+#if MSP430X_SPIA3_CLK_SRC == MSP430X_ACLK_SRC
+ #define MSP430X_SPIA3_CLK_FREQ MSP430X_ACLK_FREQ
+ #define MSP430X_SPIA3_UCSSEL UCSSEL__ACLK
+#elif MSP430X_SPIA3_CLK_SRC == MSP430X_SMCLK_SRC
+ #define MSP430X_SPIA3_CLK_FREQ MSP430X_SMCLK_FREQ
+ #define MSP430X_SPIA3_UCSSEL UCSSEL__SMCLK
+#endif
+
+#if MSP430X_SPIB0_CLK_SRC == MSP430X_ACLK_SRC
+ #define MSP430X_SPIB0_CLK_FREQ MSP430X_ACLK_FREQ
+ #define MSP430X_SPIB0_UCSSEL UCSSEL__ACLK
+#elif MSP430X_SPIB0_CLK_SRC == MSP430X_SMCLK_SRC
+ #define MSP430X_SPIB0_CLK_FREQ MSP430X_SMCLK_FREQ
+ #define MSP430X_SPIB0_UCSSEL UCSSEL__SMCLK
+#endif
+
+#if MSP430X_SPIB1_CLK_SRC == MSP430X_ACLK_SRC
+ #define MSP430X_SPIB1_CLK_FREQ MSP430X_ACLK_FREQ
+ #define MSP430X_SPIB1_UCSSEL UCSSEL__ACLK
+#elif MSP430X_SPIB1_CLK_SRC == MSP430X_SMCLK_SRC
+ #define MSP430X_SPIB1_CLK_FREQ MSP430X_SMCLK_FREQ
+ #define MSP430X_SPIB1_UCSSEL UCSSEL__SMCLK
+#endif
+
+#if MSP430X_SPIB2_CLK_SRC == MSP430X_ACLK_SRC
+ #define MSP430X_SPIB2_CLK_FREQ MSP430X_ACLK_FREQ
+ #define MSP430X_SPIB2_UCSSEL UCSSEL__ACLK
+#elif MSP430X_SPIB2_CLK_SRC == MSP430X_SMCLK_SRC
+ #define MSP430X_SPIB2_CLK_FREQ MSP430X_SMCLK_FREQ
+ #define MSP430X_SPIB2_UCSSEL UCSSEL__SMCLK
+#endif
+
+#if MSP430X_SPIB3_CLK_SRC == MSP430X_ACLK_SRC
+ #define MSP430X_SPIB3_CLK_FREQ MSP430X_ACLK_FREQ
+ #define MSP430X_SPIB3_UCSSEL UCSSEL__ACLK
+#elif MSP430X_SPIB3_CLK_SRC == MSP430X_SMCLK_SRC
+ #define MSP430X_SPIB3_CLK_FREQ MSP430X_SMCLK_FREQ
+ #define MSP430X_SPIB3_UCSSEL UCSSEL__SMCLK
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type of a structure representing an SPI driver.
+ */
+typedef struct SPIDriver SPIDriver;
+
+/**
+ * @brief SPI notification callback type.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object triggering the
+ * callback
+ */
+typedef void (*spicallback_t)(SPIDriver *spip);
+
+/**
+ * @brief Enumerated type for SPI bit order.
+ */
+typedef enum {
+ MSP430X_SPI_BO_LSB = 0,
+ MSP430X_SPI_BO_MSB = 1
+} msp430x_spi_bit_order_t;
+
+/**
+ * @brief Enumerated type for SPI data size.
+ */
+typedef enum {
+ MSP430X_SPI_DS_EIGHT = 0,
+ MSP430X_SPI_DS_SEVEN = 1
+} msp430x_spi_data_size_t;
+
+/**
+ * @brief Driver configuration structure.
+ * @note Implementations may extend this structure to contain more,
+ * architecture dependent, fields.
+ */
+typedef struct {
+ /**
+ * @brief Operation complete callback or @p NULL.
+ */
+ spicallback_t end_cb;
+ /* End of the mandatory fields.*/
+ /**
+ * @brief The chip select line.
+ * @note This may be PAL_NOLINE to indicate that hardware chip select is used.
+ */
+ ioline_t ss_line;
+ /**
+ * @brief The bit rate of the SPI interface.
+ * @note Nearest available rate is used.
+ */
+ uint32_t bit_rate;
+ /**
+ * @brief The bit order of the peripheral - LSB or MSB first.
+ */
+ msp430x_spi_bit_order_t bit_order;
+ /**
+ * @brief The data size of the peripheral - 7 or 8 bits.
+ */
+ msp430x_spi_data_size_t data_size;
+ /**
+ * @brief The SPI mode to use - 0 through 3.
+ */
+ uint8_t spi_mode;
+#if MSP430X_SPI_EXCLUSIVE_DMA == TRUE || defined(__DOXYGEN__)
+ /**
+ * @brief The index of the TX DMA channel.
+ * @note This may be >MSP430X_DMA_CHANNELS to indicate that exclusive DMA is not used.
+ */
+ uint8_t dmatx_index;
+ /**
+ * @brief The index of the RX DMA channel.
+ * @note This may be >MSP430X_DMA_CHANNELS to indicate that exclusive DMA is not used.
+ */
+ uint8_t dmarx_index;
+#endif
+} SPIConfig;
+
+/**
+ * @brief MSP430X SPI register structure.
+ */
+typedef struct {
+ uint16_t ctlw0;
+ uint16_t _padding0;
+ uint16_t _padding1;
+ uint16_t brw;
+ uint16_t statw_b;
+ uint16_t statw_a;
+ uint16_t rxbuf;
+ uint16_t txbuf;
+} msp430x_spi_reg_t;
+
+/**
+ * @brief Structure representing an SPI driver.
+ * @note Implementations may extend this structure to contain more,
+ * architecture dependent, fields.
+ */
+struct SPIDriver {
+ /**
+ * @brief Driver state.
+ */
+ spistate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const SPIConfig *config;
+#if (SPI_USE_WAIT == TRUE) || defined(__DOXYGEN__)
+ /**
+ * @brief Waiting thread.
+ */
+ thread_reference_t thread;
+#endif /* SPI_USE_WAIT */
+#if (SPI_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
+ /**
+ * @brief Mutex protecting the peripheral.
+ */
+ mutex_t mutex;
+#endif
+#if defined(SPI_DRIVER_EXT_FIELDS)
+ SPI_DRIVER_EXT_FIELDS
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Configuration registers.
+ */
+ msp430x_spi_reg_t * regs;
+ /**
+ * @brief Interrupt flag register.
+ */
+ volatile uint16_t * ifg;
+ /**
+ * @brief TX DMA request.
+ */
+ msp430x_dma_req_t tx_req;
+ /**
+ * @brief RX DMA request.
+ */
+ msp430x_dma_req_t rx_req;
+#if MSP430X_SPI_EXCLUSIVE_DMA == TRUE || defined(__DOXYGEN__)
+ /**
+ * @brief TX DMA stream.
+ */
+ msp430x_dma_ch_t dmatx;
+ /**
+ * @brief RX DMA stream.
+ */
+ msp430x_dma_ch_t dmarx;
+#endif
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if (MSP430X_SPI_USE_SPIA0 == TRUE) && !defined(__DOXYGEN__)
+extern SPIDriver SPIDA0;
+#endif
+
+#if (MSP430X_SPI_USE_SPIA1 == TRUE) && !defined(__DOXYGEN__)
+extern SPIDriver SPIDA1;
+#endif
+
+#if (MSP430X_SPI_USE_SPIA2 == TRUE) && !defined(__DOXYGEN__)
+extern SPIDriver SPIDA2;
+#endif
+
+#if (MSP430X_SPI_USE_SPIA3 == TRUE) && !defined(__DOXYGEN__)
+extern SPIDriver SPIDA3;
+#endif
+
+#if (MSP430X_SPI_USE_SPIB0 == TRUE) && !defined(__DOXYGEN__)
+extern SPIDriver SPIDB0;
+#endif
+
+#if (MSP430X_SPI_USE_SPIB1 == TRUE) && !defined(__DOXYGEN__)
+extern SPIDriver SPIDB1;
+#endif
+
+#if (MSP430X_SPI_USE_SPIB2 == TRUE) && !defined(__DOXYGEN__)
+extern SPIDriver SPIDB2;
+#endif
+
+#if (MSP430X_SPI_USE_SPIB3 == TRUE) && !defined(__DOXYGEN__)
+extern SPIDriver SPIDB3;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void spi_lld_init(void);
+ void spi_lld_start(SPIDriver *spip);
+ void spi_lld_stop(SPIDriver *spip);
+ void spi_lld_select(SPIDriver *spip);
+ void spi_lld_unselect(SPIDriver *spip);
+ void spi_lld_ignore(SPIDriver *spip, size_t n);
+ void spi_lld_exchange(SPIDriver *spip, size_t n,
+ const void *txbuf, void *rxbuf);
+ void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf);
+ void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf);
+ uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_SPI == TRUE */
+
+#endif /* HAL_SPI_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/MSP430X/platform.mk b/os/hal/ports/MSP430X/platform.mk
index 6947785..832814b 100644
--- a/os/hal/ports/MSP430X/platform.mk
+++ b/os/hal/ports/MSP430X/platform.mk
@@ -3,7 +3,8 @@ PLATFORMSRC = ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_lld.c \
${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_st_lld.c \
${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_serial_lld.c \
${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_pal_lld.c \
- ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_dma_lld.c
+ ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_dma_lld.c \
+ ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_spi_lld.c
# Required include directories
PLATFORMINC = ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X