aboutsummaryrefslogtreecommitdiffstats
path: root/os/io/platforms/STM32/spi_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/io/platforms/STM32/spi_lld.c')
-rw-r--r--os/io/platforms/STM32/spi_lld.c210
1 files changed, 170 insertions, 40 deletions
diff --git a/os/io/platforms/STM32/spi_lld.c b/os/io/platforms/STM32/spi_lld.c
index 3ee0abba6..d0a84ddf7 100644
--- a/os/io/platforms/STM32/spi_lld.c
+++ b/os/io/platforms/STM32/spi_lld.c
@@ -47,24 +47,116 @@ static uint16_t dummytx;
/* Low Level Driver local functions. */
/*===========================================================================*/
+static void spi_stop(SPIDriver *spip, msg_t msg) {
+
+ /* Stops RX and TX DMA channels.*/
+ spip->spd_dmarx->CCR = 0;
+ spip->spd_dmatx->CCR = 0;
+
+ /* Stops SPI operations.*/
+ spip->spd_spi->CR1 &= ~SPI_CR1_SPE;
+
+ chSchReadyI(spip->spd_thread)->p_msg = msg;
+}
+
+static void dma_start(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
+ uint32_t ccr;
+
+ /* Common DMA setup.*/
+ ccr = spip->spd_dmaprio;
+ if ((spip->spd_config->spc_cr1 & SPI_CR1_DFF) != 0)
+ ccr |= DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0; /* 16 bits transfer.*/
+
+ /* RX DMA setup.*/
+ spip->spd_dmarx->CMAR = (uint32_t)rxbuf;
+ spip->spd_dmarx->CNDTR = (uint32_t)n;
+ spip->spd_dmarx->CCR |= ccr;
+
+ /* TX DMA setup.*/
+ spip->spd_dmatx->CMAR = (uint32_t)txbuf;
+ spip->spd_dmatx->CNDTR = (uint32_t)n;
+ spip->spd_dmatx->CCR |= ccr;
+}
+
+static msg_t spi_start_wait(SPIDriver *spip) {
+ msg_t msg;
+
+ chSysLock();
+ spip->spd_spi->CR1 |= SPI_CR1_SPE; /* SPI enable.*/
+ spip->spd_thread = currp;
+ chSchGoSleepS(PRSUSPENDED); /* Wait for completion event.*/
+ spip->spd_thread = NULL;
+ msg = currp->p_rdymsg;
+ chSysUnlock();
+ return msg;
+}
+
/*===========================================================================*/
/* Low Level Driver interrupt handlers. */
/*===========================================================================*/
#if USE_STM32_SPI1 || defined(__DOXYGEN__)
+/**
+ * @brief SPI1 RX DMA interrupt handler (channel 2).
+ */
CH_IRQ_HANDLER(Vector70) {
CH_IRQ_PROLOGUE();
+ if ((DMA1->ISR & DMA_ISR_TCIF2) != 0)
+ spi_stop(&SPID1, RDY_OK);
+ else
+ spi_stop(&SPID1, RDY_RESET);
+ DMA1->IFCR |= DMA_IFCR_CGIF2 | DMA_IFCR_CTCIF2 |
+ DMA_IFCR_CHTIF2 | DMA_IFCR_CTEIF2;
+
+ CH_IRQ_EPILOGUE();
+}
+
+/**
+ * @brief SPI1 TX DMA interrupt handler (channel 3).
+ */
+CH_IRQ_HANDLER(Vector74) {
+
+ CH_IRQ_PROLOGUE();
+
+ spi_stop(&SPID1, RDY_RESET);
+ DMA1->IFCR |= DMA_IFCR_CGIF3 | DMA_IFCR_CTCIF3 |
+ DMA_IFCR_CHTIF3 | DMA_IFCR_CTEIF3;
+
CH_IRQ_EPILOGUE();
}
#endif
#if USE_STM32_SPI2 || defined(__DOXYGEN__)
+/**
+ * @brief SPI2 RX DMA interrupt handler (channel 4).
+ */
CH_IRQ_HANDLER(Vector78) {
CH_IRQ_PROLOGUE();
+ if ((DMA1->ISR & DMA_ISR_TCIF2) != 0)
+ spi_stop(&SPID2, RDY_OK);
+ else
+ spi_stop(&SPID2, RDY_RESET);
+ DMA2->IFCR |= DMA_IFCR_CGIF4 | DMA_IFCR_CTCIF4 |
+ DMA_IFCR_CHTIF4 | DMA_IFCR_CTEIF4;
+
+ CH_IRQ_EPILOGUE();
+}
+
+/**
+ * @brief SPI2 TX DMA interrupt handler (channel 5).
+ */
+CH_IRQ_HANDLER(Vector7C) {
+
+ CH_IRQ_PROLOGUE();
+
+ spi_stop(&SPID2, RDY_RESET);
+ DMA2->IFCR |= DMA_IFCR_CGIF5 | DMA_IFCR_CTCIF5 |
+ DMA_IFCR_CHTIF5 | DMA_IFCR_CTEIF5;
+
CH_IRQ_EPILOGUE();
}
#endif
@@ -78,10 +170,11 @@ CH_IRQ_HANDLER(Vector78) {
*/
void spi_lld_init(void) {
- dummyrx = dummytx = 0xFFFF;
+ dummytx = 0xFFFF;
#if USE_STM32_SPI1
spiObjectInit(&SPID1);
+ SPID1.spd_thread = NULL;
SPID1.spd_spi = SPI1;
SPID1.spd_dmarx = DMA1_Channel2;
SPID1.spd_dmatx = DMA1_Channel3;
@@ -92,6 +185,7 @@ void spi_lld_init(void) {
#if USE_STM32_SPI2
spiObjectInit(&SPID2);
+ SPID2.spd_thread = NULL;
SPID2.spd_spi = SPI2;
SPID2.spd_dmarx = DMA1_Channel4;
SPID2.spd_dmatx = DMA1_Channel5;
@@ -115,6 +209,20 @@ void spi_lld_setup(SPIDriver *spip) {
/* DMA setup.*/
spip->spd_dmarx->CPAR = (uint32_t)&spip->spd_spi->DR;
spip->spd_dmatx->CPAR = (uint32_t)&spip->spd_spi->DR;
+
+ /*
+ * If specified in the configuration then emits a pulses train on
+ * the SPI clock line without asserting any slave.
+ */
+ if (spip->spd_config->spc_initcnt > 0) {
+ spip->spd_dmarx->CCR = DMA_CCR1_TCIE |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ spip->spd_dmatx->CCR = DMA_CCR1_DIR |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ dma_start(spip, (size_t)spip->spd_config->spc_initcnt,
+ &dummyrx, &dummytx);
+ (void) spi_start_wait(spip);
+ }
}
/**
@@ -143,48 +251,70 @@ void spi_lld_unselect(SPIDriver *spip) {
* @details This function performs a simultaneous transmit/receive operation.
*
* @param[in] spip pointer to the @p SPIDriver object
- * @param n number of words to be exchanged
- * @param rxbuf the pointer to the receive buffer, if @p NULL is specified then
- * the input data is discarded.
- * Note that the buffer is organized as an uint8_t array for
- * data sizes below or equal to 8 bits else it is organized as
- * an uint16_t array.
- * @param txbuf the pointer to the transmit buffer, if @p NULL is specified all
- * ones are transmitted.
- * Note that the buffer is organized as an uint8_t array for
- * data sizes below or equal to 8 bits else it is organized as
- * an uint16_t array.
+ * @param n number of words to exchange
+ * @param rxbuf the pointer to the receive buffer
+ * @param txbuf the pointer to the transmit buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @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.
*/
-void spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
- uint32_t baseccr, ccr;
-
- /* Common DMA setup.*/
- baseccr = spip->spd_dmaprio;
- if ((spip->spd_config->spc_cr1 & SPI_CR1_DFF) != 0)
- baseccr |= DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0; /* 16 bits transfer.*/
-
- /* RX DMA setup.*/
- ccr = baseccr | DMA_CCR1_TCIE | DMA_CCR1_TEIE | DMA_CCR1_EN;
- if (rxbuf == NULL)
- rxbuf = &dummyrx;
- else
- ccr |= DMA_CCR1_MINC;
- spip->spd_dmarx->CMAR = (uint32_t)rxbuf;
- spip->spd_dmarx->CNDTR = (uint32_t)n;
- spip->spd_dmarx->CCR = ccr;
+msg_t spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
+
+ spip->spd_dmarx->CCR = DMA_CCR1_TCIE | DMA_CCR1_MINC |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ spip->spd_dmatx->CCR = DMA_CCR1_DIR | DMA_CCR1_MINC |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ dma_start(spip, n, rxbuf, txbuf);
+ return spi_start_wait(spip);
+}
- /* TX DMA setup.*/
- ccr = baseccr | DMA_CCR1_DIR | DMA_CCR1_TEIE | DMA_CCR1_EN;
- if (txbuf == NULL)
- txbuf = &dummytx;
- else
- ccr |= DMA_CCR1_PINC;
- spip->spd_dmatx->CMAR = (uint32_t)txbuf;
- spip->spd_dmatx->CNDTR = (uint32_t)n;
- spip->spd_dmatx->CCR = ccr;
+/**
+ * @brief Sends data ever the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param n number of words to send
+ * @param txbuf the pointer to the transmit buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @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.
+ */
+msg_t spi_lld_send(SPIDriver *spip, size_t n, void *txbuf) {
+
+ spip->spd_dmarx->CCR = DMA_CCR1_TCIE |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ spip->spd_dmatx->CCR = DMA_CCR1_DIR | DMA_CCR1_MINC |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ dma_start(spip, n, &dummyrx, txbuf);
+ return spi_start_wait(spip);
+}
- /* SPI enable.*/
- spip->spd_spi->CR1 != SPI_CR1_SPE;
+/**
+ * @brief Receives data from the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param n number of words to receive
+ * @param rxbuf the pointer to the receive buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @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.
+ */
+msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
+
+ spip->spd_dmarx->CCR = DMA_CCR1_TCIE | DMA_CCR1_MINC |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ spip->spd_dmatx->CCR = DMA_CCR1_DIR |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ dma_start(spip, n, rxbuf, &dummytx);
+ return spi_start_wait(spip);
}
/** @} */