diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2009-10-17 16:11:55 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2009-10-17 16:11:55 +0000 |
commit | 2f3269b1362a9588ed3b34c1044b85ff1e4147e2 (patch) | |
tree | 561534c555a9f307eb583949c7304666979377e7 | |
parent | 1482de823cddb881777b57e76cafb47a9bf4c3b2 (diff) | |
download | px4-nuttx-2f3269b1362a9588ed3b34c1044b85ff1e4147e2.tar.gz px4-nuttx-2f3269b1362a9588ed3b34c1044b85ff1e4147e2.tar.bz2 px4-nuttx-2f3269b1362a9588ed3b34c1044b85ff1e4147e2.zip |
SPI DMA design simplification
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2148 42af7a65-404d-4744-a932-0658087f49c3
-rwxr-xr-x | nuttx/arch/arm/src/stm32/stm32_dma.c | 33 | ||||
-rwxr-xr-x | nuttx/arch/arm/src/stm32/stm32_internal.h | 37 | ||||
-rwxr-xr-x | nuttx/arch/arm/src/stm32/stm32_spi.c | 301 |
3 files changed, 88 insertions, 283 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_dma.c b/nuttx/arch/arm/src/stm32/stm32_dma.c index d76a4373b..adf651236 100755 --- a/nuttx/arch/arm/src/stm32/stm32_dma.c +++ b/nuttx/arch/arm/src/stm32/stm32_dma.c @@ -301,10 +301,31 @@ void weak_function stm32_dmainitialize(void) * Name: stm32_dmachannel * * Description: - * Allocate a DMA channel + * Allocate a DMA channel. This function gives the caller mutually + * exclusive access to the DMA channel specified by the 'chan' argument. + * DMA channels are shared on the STM32: Devices sharing the same DMA + * channel cannot do DMA concurrently! See the DMACHAN_* definitions in + * stm32_dma.h. + * + * If the DMA channel is not available, then stm32_dmachannel() will wait + * until the holder of the channel relinquishes the channel by calling + * stm32_dmafree(). WARNING: If you have two devices sharing a DMA + * channel and the code never releases the channel, the stm32_dmachannel + * call for the other will hang forever in this function! Don't let your + * design do that! + * + * Hmm.. I suppose this interface could be extended to make a non-blocking + * version. Feel free to do that if that is what you need. * * Returned Value: - * On success, a void* DMA channel handle; NULL on failure + * Provided that 'chan' is valid, this function ALWAYS returns a non-NULL, + * void* DMA channel handle. (If 'chan' is invalid, the function will + * assert if debug is enabled or do something ignorant otherwise). + * + * Assumptions: + * - The caller does not hold he DMA channel. + * - The caller can wait for the DMA channel to be freed if it is no + * available. * ****************************************************************************/ @@ -326,10 +347,14 @@ DMA_HANDLE stm32_dmachannel(int chan) } /**************************************************************************** - * Name: stm32_dmarelease + * Name: stm32_dmafree * * Description: - * Release a DMA channel + * Release a DMA channel. If another thread is waiting for this DMA channel + * in a call to stm32_dmachannel, then this function will re-assign the + * DMA channel to that thread and wake it up. NOTE: The 'handle' used + * in this argument must NEVER be used again until stm32_dmachannel() is + * called again to re-gain access to the channel. * * Returned Value: * None diff --git a/nuttx/arch/arm/src/stm32/stm32_internal.h b/nuttx/arch/arm/src/stm32/stm32_internal.h index 7d58d736b..b558ce2a5 100755 --- a/nuttx/arch/arm/src/stm32/stm32_internal.h +++ b/nuttx/arch/arm/src/stm32/stm32_internal.h @@ -494,24 +494,53 @@ EXTERN void weak_function stm32_dmainitialize(void); * Name: stm32_dmachannel * * Description: - * Allocate a DMA channel + * Allocate a DMA channel. This function gives the caller mutually + * exclusive access to the DMA channel specified by the 'chan' argument. + * DMA channels are shared on the STM32: Devices sharing the same DMA + * channel cannot do DMA concurrently! See the DMACHAN_* definitions in + * stm32_dma.h. + * + * If the DMA channel is not available, then stm32_dmachannel() will wait + * until the holder of the channel relinquishes the channel by calling + * stm32_dmafree(). WARNING: If you have two devices sharing a DMA + * channel and the code never releases the channel, the stm32_dmachannel + * call for the other will hang forever in this function! Don't let your + * design do that! + * + * Hmm.. I suppose this interface could be extended to make a non-blocking + * version. Feel free to do that if that is what you need. * * Returned Value: - * On success, a void* DMA channel handle; NULL on failure + * Provided that 'chan' is valid, this function ALWAYS returns a non-NULL, + * void* DMA channel handle. (If 'chan' is invalid, the function will + * assert if debug is enabled or do something ignorant otherwise). + * + * Assumptions: + * - The caller does not hold he DMA channel. + * - The caller can wait for the DMA channel to be freed if it is no + * available. * ****************************************************************************/ EXTERN DMA_HANDLE stm32_dmachannel(int chan); /**************************************************************************** - * Name: stm32_dmarelease + * Name: stm32_dmafree * * Description: - * Release a DMA channel + * Release a DMA channel. If another thread is waiting for this DMA channel + * in a call to stm32_dmachannel, then this function will re-assign the + * DMA channel to that thread and wake it up. NOTE: The 'handle' used + * in this argument must NEVER be used again until stm32_dmachannel() is + * called again to re-gain access to the channel. * * Returned Value: * None * + * Assumptions: + * - The caller holds the DMA channel. + * - There is no DMA in progress + * ****************************************************************************/ EXTERN void stm32_dmafree(DMA_HANDLE handle); diff --git a/nuttx/arch/arm/src/stm32/stm32_spi.c b/nuttx/arch/arm/src/stm32/stm32_spi.c index 49341e6ed..68f3f729b 100755 --- a/nuttx/arch/arm/src/stm32/stm32_spi.c +++ b/nuttx/arch/arm/src/stm32/stm32_spi.c @@ -163,14 +163,6 @@ static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv, FAR const void *txbuffer, FAR const void *txdummy, size_t nwords); static inline void spi_dmarxstart(FAR struct stm32_spidev_s *priv); static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv); -static inline void spi_dmaexchange(FAR struct stm32_spidev_s *priv, - FAR const void *txbuffer, FAR void *rxbuffer, - size_t nwords); -static inline void spi_dmatxexchange(FAR struct stm32_spidev_s *priv, - FAR const void *txbuffer, FAR void *rxbuffer, - size_t nwords); -static inline void spi_dmarxexchange(FAR struct stm32_spidev_s *priv, FAR const void *txbuffer, - FAR void *rxbuffer, size_t nwords); #endif /* SPI methods */ @@ -179,13 +171,8 @@ static uint32 spi_setfrequency(FAR struct spi_dev_s *dev, uint32 frequen static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode); static void spi_setbits(FAR struct spi_dev_s *dev, int nbits); static uint16 spi_send(FAR struct spi_dev_s *dev, uint16 wd); -#ifdef CONFIG_STM32_SPI_DMA -static void spi_copyexchange(FAR struct spi_dev_s *dev, - FAR const void *txbuffer, FAR void *rxbuffer, - size_t nwords); -#endif -static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, - FAR void *rxbuffer, size_t nwords); +static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords); #ifndef CONFIG_SPI_EXCHANGE static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, size_t nwords); @@ -653,226 +640,6 @@ static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv) #endif /************************************************************************************ - * Name: spi_dmaexchange - * - * Description: - * Perform concurrent RX and TX DMA transfers - * - * priv - Device-specific state data - * txbuffer - A pointer to the buffer of data to be sent - * rxbuffer - A pointer to a buffer in which to receive data - * nwords - the length of data to be exchaned in units of words. - * The wordsize is determined by the number of bits-per-word - * selected for the SPI interface. If nbits <= 8, the data is - * packed into ubytes; if nbits >8, the data is packed into uint16's - * - * Returned Value: - * None - * - ************************************************************************************/ - -#ifdef CONFIG_STM32_SPI_DMA -static inline void spi_dmaexchange(FAR struct stm32_spidev_s *priv, - FAR const void *txbuffer, FAR void *rxbuffer, - size_t nwords) -{ - uint16 rxdummy = 0xffff; - uint16 txdummy; - - /* Setup DMAs */ - - spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords); - spi_dmatxsetup(priv, txbuffer, &txdummy, nwords); - - /* Start the DMAs */ - - spi_dmarxstart(priv); - spi_dmatxstart(priv); - - /* Then wait for each to complete */ - - spi_dmarxwait(priv); - spi_dmatxwait(priv); -} -#endif - -/************************************************************************************ - * Name: spi_dmatxexchange - * - * Description: - * Perform SPI exchange using DMA on the TX side only - * - * priv - Device-specific state data - * txbuffer - A pointer to the buffer of data to be sent - * rxbuffer - A pointer to a buffer in which to receive data - * nwords - the length of data to be exchaned in units of words. - * The wordsize is determined by the number of bits-per-word - * selected for the SPI interface. If nbits <= 8, the data is - * packed into ubytes; if nbits >8, the data is packed into uint16's - * - * Returned Value: - * None - * - ************************************************************************************/ - -#ifdef CONFIG_STM32_SPI_DMA -static inline void spi_dmatxexchange(FAR struct stm32_spidev_s *priv, - FAR const void *txbuffer, FAR void *rxbuffer, - size_t nwords) -{ - uint16 txdummy; - - /* Setup and start the TX DMA */ - - spi_dmatxsetup(priv, txbuffer, &txdummy, nwords); - spi_dmatxstart(priv); - - /* Then read the RX data via a polling loop */ - - /* 8- or 16-bit mode? */ - - if (spi_16bitmode(priv)) - { - /* 16-bit mode */ - - uint16 *dest = (uint16*)rxbuffer; - uint16 word; - - while (nwords-- > 0) - { - /* Read one word */ - - word = spi_readword(priv); - - /* Is there a buffer to receive the return value? */ - - if (dest) - { - *dest++ = word; - } - } - } - else - { - /* 8-bit mode */ - - ubyte *dest = (ubyte*)rxbuffer; - ubyte word; - - while (nwords-- > 0) - { - /* Read one word */ - - word = (ubyte)spi_readword(priv); - - /* Is there a buffer to receive the return value? */ - - if (dest) - { - *dest++ = word; - } - } - } - - /* Then wait for the TX DMA to complete (should already be finished) */ - - spi_dmatxwait(priv); -} -#endif - -/************************************************************************************ - * Name: spi_dmarxexchange - * - * Description: - * Perform SPI exchange using DMA on the RX side only - * - * priv - Device-specific state data - * txbuffer - A pointer to the buffer of data to be sent - * rxbuffer - A pointer to a buffer in which to receive data - * nwords - the length of data to be exchaned in units of words. - * The wordsize is determined by the number of bits-per-word - * selected for the SPI interface. If nbits <= 8, the data is - * packed into ubytes; if nbits >8, the data is packed into uint16's - * - * Returned Value: - * None - * - ************************************************************************************/ - -#ifdef CONFIG_STM32_SPI_DMA -static inline void spi_dmarxexchange(FAR struct stm32_spidev_s *priv, - FAR const void *txbuffer, FAR void *rxbuffer, - size_t nwords) -{ - uint16 rxdummy = 0xffff; - - /* Setup and start the RX DMA */ - - spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords); - spi_dmarxstart(priv); - - /* Then send all of the TX data via a copy loop */ - - /* 8- or 16-bit mode? */ - - if (spi_16bitmode(priv)) - { - /* 16-bit mode */ - - const uint16 *src = (const uint16*)txbuffer;; - uint16 word; - - while (nwords-- > 0) - { - /* Get the next word to write. Is there a source buffer? */ - - if (src) - { - word = *src++; - } - else - { - word = 0xffff; - } - - /* Write one word */ - - spi_writeword(priv, word); - } - } - else - { - /* 8-bit mode */ - - const ubyte *src = (const ubyte*)txbuffer;; - ubyte word; - - while (nwords-- > 0) - { - /* Get the next word to write. Is there a source buffer? */ - - if (src) - { - word = *src++; - } - else - { - word = 0xff; - } - - /* Write one word */ - - spi_writeword(priv, (uint16)word); - } - } - - /* Then wait for the RX DMA to complete */ - - spi_dmarxwait(priv); -} -#endif - -/************************************************************************************ * Name: spi_modifycr1 * * Description: @@ -1098,10 +865,10 @@ static uint16 spi_send(FAR struct spi_dev_s *dev, uint16 wd) } /************************************************************************* - * Name: spi_exchange (no DMA) or spi_copyexchange (with DMA capability) + * Name: spi_exchange (no DMA) * * Description: - * Exchange a block of data on SPI + * Exchange a block of data on SPI without using DMA * * Input Parameters: * dev - Device-specific state data @@ -1117,14 +884,9 @@ static uint16 spi_send(FAR struct spi_dev_s *dev, uint16 wd) * ************************************************************************************/ -#ifdef CONFIG_STM32_SPI_DMA -static void spi_copyexchange(FAR struct spi_dev_s *dev, - FAR const void *txbuffer, FAR void *rxbuffer, - size_t nwords) -#else +#ifndef CONFIG_STM32_SPI_DMA static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords) -#endif { FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; DEBUGASSERT(priv && priv->spibase); @@ -1198,12 +960,13 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, } } } +#endif /************************************************************************* * Name: spi_exchange (with DMA capability) * * Description: - * Exchange a block of data on SPI + * Exchange a block of data on SPI using DMA * * Input Parameters: * dev - Device-specific state data @@ -1224,42 +987,25 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords) { FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; - DEBUGASSERT(priv && priv->spibase); - - /* Do we have a TX dma channel? */ - - if (priv->txdma) - { - /* Yes.. Do we have an RX dma channel too? */ + uint16 rxdummy = 0xffff; + uint16 txdummy; - if (priv->rxdma) - { - /* Yes.. do the full DMA exchange */ + DEBUGASSERT(priv && priv->spibase); - spi_dmaexchange(priv, txbuffer, rxbuffer, nwords); - } - else - { - /* No... do the exchange with only TX DMA */ + /* Setup DMAs */ - spi_dmatxexchange(priv, txbuffer, rxbuffer, nwords); - } - } + spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords); + spi_dmatxsetup(priv, txbuffer, &txdummy, nwords); - /* Do we have an RX dma channel? */ + /* Start the DMAs */ - else if (priv->rxdma) - { - /* Yes... do the exchange with only RX DMA */ + spi_dmarxstart(priv); + spi_dmatxstart(priv); - spi_dmarxexchange(priv, txbuffer, rxbuffer, nwords); - } - else - { - /* No... do the exchange with no DMA */ + /* Then wait for each to complete */ - spi_copyexchange(dev, txbuffer, rxbuffer, nwords); - } + spi_dmarxwait(priv); + spi_dmatxwait(priv); } #endif @@ -1366,12 +1112,17 @@ static void spi_portinitialize(FAR struct stm32_spidev_s *priv) sem_init(&priv->rxsem, 0, 0); sem_init(&priv->txsem, 0, 0); - /* Get DMA channels. Note that if we fail to get a DMA channel, we will just - * fall back to dumb I/O. + /* Get DMA channels. NOTE: stm32_dmachannel() will always assign the DMA channel. + * if the channel is not available, then stm32_dmachannel() will block and wait + * until the channel becomes available. WARNING: If you have another device sharing + * a DMA channel with SPI and the code never releases that channel, then the call + * to stm32_dmachannel() will hang forever in this function! Don't let your + * design do that! */ priv->rxdma = stm32_dmachannel(priv->rxch); priv->txdma = stm32_dmachannel(priv->txch); + DEBUGASSERT(priv->rxdma && priv->txdma); #endif /* Enable spi */ |