From a1db002da5d4bd3fdc0c7fcc0f2aa466de803d4f Mon Sep 17 00:00:00 2001 From: gdisirio Date: Mon, 26 Oct 2009 16:02:29 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1256 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/io/platforms/STM32/spi_lld.c | 210 ++++++++++++++++++++++++++++++++-------- os/io/platforms/STM32/spi_lld.h | 14 ++- os/io/spi.c | 63 +++++++++++- os/io/spi.h | 4 +- os/io/templates/spi_lld.c | 56 ++++++++--- os/io/templates/spi_lld.h | 6 +- 6 files changed, 292 insertions(+), 61 deletions(-) (limited to 'os/io') 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); } /** @} */ diff --git a/os/io/platforms/STM32/spi_lld.h b/os/io/platforms/STM32/spi_lld.h index 5e5c26106..5775b67db 100644 --- a/os/io/platforms/STM32/spi_lld.h +++ b/os/io/platforms/STM32/spi_lld.h @@ -58,7 +58,7 @@ #endif /** - * @brief SPI1 DMA priority (0..3). + * @brief SPI1 DMA priority (0..3|lowest..highest). * @note The priority level is used for both the TX and RX DMA channels but * because of the channels ordering the RX channel has always priority * over the TX channel. @@ -68,7 +68,7 @@ #endif /** - * @brief SPI2 DMA priority (0..3). + * @brief SPI2 DMA priority (0..3|lowest..highest). * @note The priority level is used for both the TX and RX DMA channels but * because of the channels ordering the RX channel has always priority * over the TX channel. @@ -97,7 +97,7 @@ typedef struct { /** * @brief Clock pulses to be generated after initialization. */ - cnt_t spc_clkpulses; + cnt_t spc_initcnt; /* End of the mandatory fields.*/ /** * @brief The chip select line port. @@ -136,6 +136,10 @@ typedef struct { */ const SPIConfig *spd_config; /* End of the mandatory fields.*/ + /** + * @brief Thread waiting for I/O completion. + */ + Thread *spd_thread; /** * @brief Pointer to the SPIx registers block. */ @@ -174,7 +178,9 @@ extern "C" { void spi_lld_setup(SPIDriver *spip); void spi_lld_select(SPIDriver *spip); void spi_lld_unselect(SPIDriver *spip); - void spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf); + msg_t spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf); + msg_t spi_lld_send(SPIDriver *spip, size_t n, void *txbuf); + msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); #ifdef __cplusplus } #endif diff --git a/os/io/spi.c b/os/io/spi.c index 7c08fe413..be34c469d 100644 --- a/os/io/spi.c +++ b/os/io/spi.c @@ -59,6 +59,7 @@ void spiObjectInit(SPIDriver *spip) { */ void spiSetup(SPIDriver *spip, const SPIConfig *config) { + chDbgCheck((spip != NULL) && (config != NULL), "spiSetup"); chDbgAssert(spip->spd_state == SPI_IDLE, "spiSetup(), #1", "not idle"); @@ -74,6 +75,8 @@ void spiSetup(SPIDriver *spip, const SPIConfig *config) { */ void spiSelect(SPIDriver *spip) { + chDbgCheck(spip != NULL, "spiSelect"); + chSysLock(); chDbgAssert(spip->spd_state == SPI_IDLE, @@ -93,6 +96,8 @@ void spiSelect(SPIDriver *spip) { */ void spiUnselect(SPIDriver *spip) { + chDbgCheck(spip != NULL, "spiUnselect"); + chSysLock(); chDbgAssert(spip->spd_state == SPI_ACTIVE, @@ -121,13 +126,63 @@ void spiUnselect(SPIDriver *spip) { * data sizes below or equal to 8 bits else it is organized as * an uint16_t array. */ -void spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) { +msg_t spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) { + chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL) && (txbuf != NULL), + "spiExchange"); chDbgAssert(spip->spd_state == SPI_ACTIVE, "spiExchange(), #1", "not active"); - spi_lld_exchange(spip, n, rxbuf, txbuf); + return spi_lld_exchange(spip, n, rxbuf, txbuf); +} + +/** + * @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 spiSend(SPIDriver *spip, size_t n, void *txbuf) { + + chDbgCheck((spip != NULL) && (n > 0) && (txbuf != NULL), + "spiSend"); + chDbgAssert(spip->spd_state == SPI_ACTIVE, + "spiSend(), #1", + "not active"); + + return spi_lld_send(spip, n, txbuf); +} + +/** + * @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 spiReceive(SPIDriver *spip, size_t n, void *rxbuf) { + + chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL), + "spiReceive"); + chDbgAssert(spip->spd_state == SPI_ACTIVE, + "spiReceive(), #1", + "not active"); + + return spi_lld_receive(spip, n, rxbuf); } #if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) @@ -143,6 +198,8 @@ void spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) { */ void spiAcquireBus(SPIDriver *spip) { + chDbgCheck(spip != NULL, "spiAcquireBus"); + #if CH_USE_MUTEXES chMtxLock(&spip->spd_mutex); #elif CH_USE_SEMAPHORES @@ -160,6 +217,8 @@ void spiAcquireBus(SPIDriver *spip) { */ void spiReleaseBus(SPIDriver *spip) { + chDbgCheck(spip != NULL, "spiReleaseBus"); + #if CH_USE_MUTEXES (void)spip; chMtxUnlock(); diff --git a/os/io/spi.h b/os/io/spi.h index 54409fe76..816e9f6d4 100644 --- a/os/io/spi.h +++ b/os/io/spi.h @@ -48,7 +48,9 @@ extern "C" { void spiSetup(SPIDriver *spip, const SPIConfig *config); void spiSelect(SPIDriver *spip); void spiUnselect(SPIDriver *spip); - void spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf); + msg_t spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf); + msg_t spiSend(SPIDriver *spip, size_t n, void *txbuf); + msg_t spiReceive(SPIDriver *spip, size_t n, void *rxbuf); #if SPI_USE_MUTUAL_EXCLUSION void spiAcquireBus(SPIDriver *spip); void spiReleaseBus(SPIDriver *spip); diff --git a/os/io/templates/spi_lld.c b/os/io/templates/spi_lld.c index ca938fe7b..e31dffc4b 100644 --- a/os/io/templates/spi_lld.c +++ b/os/io/templates/spi_lld.c @@ -79,19 +79,51 @@ 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. + */ +msg_t spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) { + +} + +/** + * @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) { + +} + +/** + * @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. */ -void spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) { +msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { } diff --git a/os/io/templates/spi_lld.h b/os/io/templates/spi_lld.h index 37f14781e..a8479c008 100644 --- a/os/io/templates/spi_lld.h +++ b/os/io/templates/spi_lld.h @@ -62,7 +62,7 @@ typedef struct { /** * @brief Clock pulses to be generated after initialization. */ - cnt_t spc_clkpulses; + cnt_t spc_initcnt; /* End of the mandatory fields.*/ } SPIConfig; @@ -102,7 +102,9 @@ extern "C" { void spi_lld_setup(SPIDriver *spip); void spi_lld_select(SPIDriver *spip); void spi_lld_unselect(SPIDriver *spip); - void spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf); + msg_t spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf); + msg_t spi_lld_send(SPIDriver *spip, size_t n, void *txbuf); + msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); #ifdef __cplusplus } #endif -- cgit v1.2.3