From 9a4cd531a8eb99198f0a5a921545d3c39b8ab8dd Mon Sep 17 00:00:00 2001 From: Stephen Dwyer Date: Tue, 26 Feb 2013 14:30:24 -0700 Subject: [PATCH 01/12] [spi][stm32] updated handling of different transaction lengths - now tx buffer sizes are independent of rx buffer sizes - uses a second dummy dma transmit transfer triggered of the first transmit dma ISR when 0 < tx_len < rx_len --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 60 ++++++++++++++++---- sw/airborne/mcu_periph/spi.h | 3 - 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index 7fc9f1e4d81..46fa28bfd46 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -46,8 +46,8 @@ * It is assumed that the transmit output_length and receive input_length will never be 0 at the same time. * In this case, spi_submit will just return false. * - * IMPORTANT: At this point, you MUST MAKE THE TRANSACTION TRANSMIT BUFFER AT LEAST AS LONG AS THE RECEIVE BUFFER, or the - * transmit buffer memory will overrun to the length of the receive buffer with zeroes. + * The case where tx length is less than rx length, a second dummy transmit dma transfer is started after the + * first transmit dma interrupt completes, handled in the ISR directly. */ #include @@ -90,6 +90,7 @@ struct spi_periph_dma { u8 tx_nvic_irq; ///< transmit interrupt u8 other_dma_finished; u16 tx_dummy_buf; ///< dummy tx buffer for receive only cases + u8 use_dummy_dma; ///< second dma transmit with dummy buffer for tx_len < rx_len struct locm3_spi_comm comm; ///< current communication paramters u8 comm_sig; ///< comm config signature used to check for changes: cdiv, cpol, cpha, dss, bo }; @@ -317,7 +318,7 @@ static inline void SpiSlaveSelect(uint8_t slave) } } -/// Enable DMA rx channel interrupt +/// Enable DMA channel interrupts // FIXME fix priority levels if necessary static void spi_arch_int_enable( struct spi_periph *spi ) { if (spi->trans[spi->trans_extract_idx]->input_length != 0) { @@ -332,7 +333,7 @@ static void spi_arch_int_enable( struct spi_periph *spi ) { } } -/// Disable DMA rx channel interrupt +/// Disable DMA channel interrupts static void spi_arch_int_disable( struct spi_periph *spi ) { nvic_disable_irq( ((struct spi_periph_dma *)spi->init_struct)->rx_nvic_irq ); nvic_disable_irq( ((struct spi_periph_dma *)spi->init_struct)->tx_nvic_irq ); @@ -351,6 +352,7 @@ void spi1_arch_init(void) { spi1_dma.tx_nvic_irq = NVIC_DMA1_CHANNEL3_IRQ; spi1_dma.other_dma_finished = 0; spi1_dma.tx_dummy_buf = 0; + spi1_dma.use_dummy_dma = 0; // set the default configuration set_default_comm_config(&spi1_dma.comm); @@ -419,6 +421,7 @@ void spi2_arch_init(void) { spi2_dma.tx_nvic_irq = NVIC_DMA1_CHANNEL5_IRQ; spi2_dma.other_dma_finished = 0; spi2_dma.tx_dummy_buf = 0; + spi2_dma.use_dummy_dma = 0; // set the default configuration set_default_comm_config(&spi2_dma.comm); @@ -488,6 +491,7 @@ void spi3_arch_init(void) { spi3_dma.tx_nvic_irq = NVIC_DMA2_CHANNEL2_IRQ; spi3_dma.other_dma_finished = 0; spi3_dma.tx_dummy_buf = 0; + spi3_dma.use_dummy_dma = 0; // set the default configuration set_default_comm_config(&spi3_dma.comm); @@ -622,7 +626,7 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) /* Determine the maximum length of the transaction. * To receive data, the clock must run, which means something has to be transmitted. - * This should be zeroed data if the transaction rx length > tx length. + * This is done by enabling a second, dummy dma transmit transfer with the dummy buffer. */ if (_trans->input_length > _trans->output_length) { /* Receiving more than sending */ @@ -632,10 +636,8 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) /* Special case: use dummy buffer */ use_dummy_tx_buf = TRUE; } else { - /* pad the tx buffer with zeroes */ - for (int i = _trans->output_length; i < _trans->input_length; i++) { - _trans->output_buf[i] = 0; - } + /* Enable use of second dma transfer with dummy buffer */ + dma->use_dummy_dma = 1; } } else { /* We are sending at least as much as we receive, use output length */ @@ -650,7 +652,7 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) if (_trans->input_length > 0) { dma_set_peripheral_address(dma->dma, dma->rx_chan, (u32)dma->spidr); dma_set_memory_address(dma->dma, dma->rx_chan, (u32)_trans->input_buf); - dma_set_number_of_data(dma->dma, dma->rx_chan, _trans->input_length); + dma_set_number_of_data(dma->dma, dma->rx_chan, (u16)(_trans->input_length)); dma_set_read_from_peripheral(dma->dma, dma->rx_chan); //dma_disable_peripheral_increment_mode(dma->dma, dma->rx_chan); dma_enable_memory_increment_mode(dma->dma, dma->rx_chan); @@ -678,14 +680,14 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) dma_set_peripheral_address(dma->dma, dma->tx_chan, (u32)dma->spidr); /* Use the dummy buffer if tx length is zero */ if (use_dummy_tx_buf) { - dma_set_memory_address(dma->dma, dma->tx_chan, (u32)dma->tx_dummy_buf); + dma_set_memory_address(dma->dma, dma->tx_chan, (u32)&(dma->tx_dummy_buf)); dma_disable_memory_increment_mode(dma->dma, dma->tx_chan); } else { dma_set_memory_address(dma->dma, dma->tx_chan, (u32)_trans->output_buf); dma_enable_memory_increment_mode(dma->dma, dma->tx_chan); } /* Use the max length of rx or tx instead of actual tx length as described above */ - dma_set_number_of_data(dma->dma, dma->tx_chan, max_length); + dma_set_number_of_data(dma->dma, dma->tx_chan, (u16)(max_length)); dma_set_read_from_memory(dma->dma, dma->tx_chan); //dma_disable_peripheral_increment_mode(dma->dma, dma->tx_chan); @@ -983,6 +985,40 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { periph->status = SPIIdle; else spi_rw(periph, periph->trans[periph->trans_extract_idx]); + } else if (dma->use_dummy_dma == 1) { + /* + * We are finished the first part of the transmit with real data, but still need + * to clock in the rest of the receive data. Set up a dummy dma transmit transfer + * to accomplish this. + */ + + // reset the flag so this only happens once in a transaction + dma->use_dummy_dma = 0; + + dma_channel_reset(dma->dma, dma->tx_chan); + dma_set_peripheral_address(dma->dma, dma->tx_chan, (u32)dma->spidr); + /* Use the dummy buffer if tx length is zero */ + dma_set_memory_address(dma->dma, dma->tx_chan, (u32)&(dma->tx_dummy_buf)); + dma_disable_memory_increment_mode(dma->dma, dma->tx_chan); + /* Use the difference in length between rx and tx */ + dma_set_number_of_data(dma->dma, dma->tx_chan, (u16)(trans->input_length - trans->output_length)); + dma_set_read_from_memory(dma->dma, dma->tx_chan); + /* Set the DMA transfer size based on SPI transaction DSS */ + if (trans->dss == SPIDss8bit) { + dma_set_peripheral_size(dma->dma, dma->tx_chan, DMA_CCR_PSIZE_8BIT); + dma_set_memory_size(dma->dma, dma->tx_chan, DMA_CCR_MSIZE_8BIT); + } else { + dma_set_peripheral_size(dma->dma, dma->tx_chan, DMA_CCR_PSIZE_16BIT); + dma_set_memory_size(dma->dma, dma->tx_chan, DMA_CCR_MSIZE_16BIT); + } + dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); + /* Enable DMA transfer complete interrupts. */ + dma_enable_transfer_complete_interrupt(dma->dma, dma->tx_chan); + /* enable DMA channels */ + dma_enable_channel(dma->dma, dma->tx_chan); + /* enable SPI transfers via DMA */ + spi_enable_tx_dma((u32)periph->reg_addr); + } else { // if this is not the last part of the transaction, set finished flag dma->other_dma_finished = 1; diff --git a/sw/airborne/mcu_periph/spi.h b/sw/airborne/mcu_periph/spi.h index 294e3492938..94993f7a6cc 100644 --- a/sw/airborne/mcu_periph/spi.h +++ b/sw/airborne/mcu_periph/spi.h @@ -138,9 +138,6 @@ typedef void (*SPICallback)( struct spi_transaction *trans ); * of the two specifies the toal number of exchanged bytes, * - if input_length is larger than output length, * 0 is sent for the remaining bytes - * WARNING: For STM32 only, the output_buf size MUST be greater than or equal - * to the input_buf size. This is only required in the event any transaction - * has (0 < output_length < input_length). */ struct spi_transaction { volatile uint8_t* input_buf; ///< pointer to receive buffer for DMA From 1a4ba9690d9a7643f53bbbf813ca0a977a23858f Mon Sep 17 00:00:00 2001 From: Stephen Dwyer Date: Thu, 28 Feb 2013 15:04:52 -0700 Subject: [PATCH 02/12] [spi][stm32] Added second dummy buffer dma handling for rx too - handled in the same way as tx - simplifies completion interrupt timing, clearing registers and running callback and deselecting slave at the right time, AFTER the last byte is fully transmitted. - Also updated comments. --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 216 +++++++++++-------- 1 file changed, 130 insertions(+), 86 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index 46fa28bfd46..0dfe1848b85 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -40,14 +40,14 @@ * - Each interrupt does some basic cleanup necessary to finish off each dma transfer * - The after_cb callback, slave unselect, status changes and further transactions only occur after both dma * transfers are complete, using a state flag. Note that the callback happens BEFORE the slave unselect - * - If the receive input_length is 0, the dma transfer is not even initialized, and no interrupt will occur - * - The state flag handles this as a case where the rx dma transfer is already complete + * - If the output_len != input_len, a dummy dma transfer is triggered for the remainder so the same amount + * of data is moved in and out. This simplifies keeping the clock going if output_len is greater and allows + * the rx dma interrupt to represent that the transaction has fully completed. The second dummy transfer is + * initiated at the transaction setup if either length is 0, otherwise after the first dma interrupt completes + * in the ISR directly * * It is assumed that the transmit output_length and receive input_length will never be 0 at the same time. * In this case, spi_submit will just return false. - * - * The case where tx length is less than rx length, a second dummy transmit dma transfer is started after the - * first transmit dma interrupt completes, handled in the ISR directly. */ #include @@ -90,7 +90,9 @@ struct spi_periph_dma { u8 tx_nvic_irq; ///< transmit interrupt u8 other_dma_finished; u16 tx_dummy_buf; ///< dummy tx buffer for receive only cases - u8 use_dummy_dma; ///< second dma transmit with dummy buffer for tx_len < rx_len + u8 tx_use_dummy_dma; ///< second dma transmit with dummy buffer for tx_len < rx_len + u16 rx_dummy_buf; ///< dummy rx buffer for receive only cases + u8 rx_use_dummy_dma; ///< second dma transmit with dummy buffer for tx_len > rx_len struct locm3_spi_comm comm; ///< current communication paramters u8 comm_sig; ///< comm config signature used to check for changes: cdiv, cpol, cpha, dss, bo }; @@ -352,7 +354,9 @@ void spi1_arch_init(void) { spi1_dma.tx_nvic_irq = NVIC_DMA1_CHANNEL3_IRQ; spi1_dma.other_dma_finished = 0; spi1_dma.tx_dummy_buf = 0; - spi1_dma.use_dummy_dma = 0; + spi1_dma.tx_use_dummy_dma = 0; + spi1_dma.rx_dummy_buf = 0; + spi1_dma.rx_use_dummy_dma = 0; // set the default configuration set_default_comm_config(&spi1_dma.comm); @@ -421,7 +425,9 @@ void spi2_arch_init(void) { spi2_dma.tx_nvic_irq = NVIC_DMA1_CHANNEL5_IRQ; spi2_dma.other_dma_finished = 0; spi2_dma.tx_dummy_buf = 0; - spi2_dma.use_dummy_dma = 0; + spi2_dma.tx_use_dummy_dma = 0; + spi2_dma.rx_dummy_buf = 0; + spi2_dma.rx_use_dummy_dma = 0; // set the default configuration set_default_comm_config(&spi2_dma.comm); @@ -491,7 +497,9 @@ void spi3_arch_init(void) { spi3_dma.tx_nvic_irq = NVIC_DMA2_CHANNEL2_IRQ; spi3_dma.other_dma_finished = 0; spi3_dma.tx_dummy_buf = 0; - spi3_dma.use_dummy_dma = 0; + spi3_dma.tx_use_dummy_dma = 0; + spi3_dma.rx_dummy_buf = 0; + spi3_dma.rx_use_dummy_dma = 0; // set the default configuration set_default_comm_config(&spi3_dma.comm); @@ -555,29 +563,14 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) uint8_t sig = 0x00; uint8_t max_length = 0; bool_t use_dummy_tx_buf = FALSE; + bool_t use_dummy_rx_buf = FALSE; - // Store local copy to notify of the results + /* Store local copy to notify of the results */ _trans->status = SPITransRunning; periph->status = SPIRunning; dma = periph->init_struct; - /* Wait until transceive complete. - * This follows the procedure on the Reference Manual (RM0008 rev 14 - * Section 25.3.9 page 692, the note.) - * - * FIXME this section is at least partially necessary but may block!!! - */ - while (!(SPI_SR((u32)periph->reg_addr) & SPI_SR_TXE)) - ; - while (SPI_SR((u32)periph->reg_addr) & SPI_SR_BSY) - ; - /* Reset SPI data and status registers */ - volatile u16 temp_data __attribute__ ((unused)); - while (SPI_SR((u32)periph->reg_addr) & (SPI_SR_RXNE | SPI_SR_OVR)) { - temp_data = SPI_DR((u32)periph->reg_addr); - } - /* * Check if we need to reconfigure the spi peripheral for this transaction */ @@ -624,23 +617,39 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) dma->other_dma_finished = 0; - /* Determine the maximum length of the transaction. + /* + * Determine the maximum length of the transaction. + * * To receive data, the clock must run, which means something has to be transmitted. * This is done by enabling a second, dummy dma transmit transfer with the dummy buffer. + * Also, best to run receive to the very end to deal with timing issues in cleanup (i.e. + * the dma for tx will trigger complete interrupt before all data is sent, meaning slave + * is deselected and callback run BEFORE data is fully sent) */ if (_trans->input_length > _trans->output_length) { /* Receiving more than sending */ max_length = _trans->input_length; - /* make sure we send zeroed data while we are actually only receiving */ + /* Make sure we send zeroed data while we are actually only receiving */ if (_trans->output_length == 0) { /* Special case: use dummy buffer */ use_dummy_tx_buf = TRUE; } else { - /* Enable use of second dma transfer with dummy buffer */ - dma->use_dummy_dma = 1; + /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ + dma->tx_use_dummy_dma = 1; + } + } else if (_trans->output_length > _trans->input_length) { + /* Transmitting more than receiving */ + max_length = _trans->output_length; + /* Make sure we ignore junk data while we are actually only transmitting */ + if (_trans->input_length == 0) { + /* Special case: use dummy buffer */ + use_dummy_rx_buf = TRUE; + } else { + /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ + dma->rx_use_dummy_dma = 1; } } else { - /* We are sending at least as much as we receive, use output length */ + /* We are sending equal amounts, use output length */ max_length = _trans->output_length; } @@ -649,28 +658,29 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) * Receive DMA channel configuration ---------------------------------------- */ dma_channel_reset(dma->dma, dma->rx_chan); - if (_trans->input_length > 0) { - dma_set_peripheral_address(dma->dma, dma->rx_chan, (u32)dma->spidr); + dma_set_peripheral_address(dma->dma, dma->rx_chan, (u32)dma->spidr); + /* Use the dummy buffer if rx length is zero */ + if (use_dummy_rx_buf) { + dma_set_memory_address(dma->dma, dma->rx_chan, (u32)&(dma->rx_dummy_buf)); + dma_disable_memory_increment_mode(dma->dma, dma->rx_chan); + /* Use the max length of rx or tx instead of actual rx length as described above */ + dma_set_number_of_data(dma->dma, dma->rx_chan, (u16)(max_length)); + } else { dma_set_memory_address(dma->dma, dma->rx_chan, (u32)_trans->input_buf); - dma_set_number_of_data(dma->dma, dma->rx_chan, (u16)(_trans->input_length)); - dma_set_read_from_peripheral(dma->dma, dma->rx_chan); - //dma_disable_peripheral_increment_mode(dma->dma, dma->rx_chan); dma_enable_memory_increment_mode(dma->dma, dma->rx_chan); + dma_set_number_of_data(dma->dma, dma->rx_chan, (u16)(_trans->input_length)); + } + dma_set_read_from_peripheral(dma->dma, dma->rx_chan); - /* Set the dma transfer size based on SPI transaction DSS */ - if (_trans->dss == SPIDss8bit) { - dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_8BIT); - dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_8BIT); - } else { - dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_16BIT); - dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_16BIT); - } - //dma_set_mode(dma->dma, dma->rx_chan, DMA_???_NORMAL); - dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_VERY_HIGH); + /* Set the dma transfer size based on SPI transaction DSS */ + if (_trans->dss == SPIDss8bit) { + dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_8BIT); + dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_8BIT); } else { - /* There will be no interrupt in this case, i.e. like the interrupt already finished */ - dma->other_dma_finished = 1; + dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_16BIT); + dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_16BIT); } + dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_VERY_HIGH); /* @@ -682,14 +692,14 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) if (use_dummy_tx_buf) { dma_set_memory_address(dma->dma, dma->tx_chan, (u32)&(dma->tx_dummy_buf)); dma_disable_memory_increment_mode(dma->dma, dma->tx_chan); + /* Use the max length of rx or tx instead of actual tx length as described above */ + dma_set_number_of_data(dma->dma, dma->tx_chan, (u16)(max_length)); } else { dma_set_memory_address(dma->dma, dma->tx_chan, (u32)_trans->output_buf); dma_enable_memory_increment_mode(dma->dma, dma->tx_chan); + dma_set_number_of_data(dma->dma, dma->tx_chan, (u16)(_trans->output_length)); } - /* Use the max length of rx or tx instead of actual tx length as described above */ - dma_set_number_of_data(dma->dma, dma->tx_chan, (u16)(max_length)); dma_set_read_from_memory(dma->dma, dma->tx_chan); - //dma_disable_peripheral_increment_mode(dma->dma, dma->tx_chan); /* Set the DMA transfer size based on SPI transaction DSS */ if (_trans->dss == SPIDss8bit) { @@ -699,7 +709,6 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) dma_set_peripheral_size(dma->dma, dma->tx_chan, DMA_CCR_PSIZE_16BIT); dma_set_memory_size(dma->dma, dma->tx_chan, DMA_CCR_MSIZE_16BIT); } - //dma_set_mode(dma->dma, dma->tx_chan, DMA_???_NORMAL); dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); @@ -707,22 +716,16 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) * Enable DMA --------------------------------------------------------------- */ /* Enable DMA transfer complete interrupts. */ - if (_trans->input_length > 0) { - dma_enable_transfer_complete_interrupt(dma->dma, dma->rx_chan); - } + dma_enable_transfer_complete_interrupt(dma->dma, dma->rx_chan); dma_enable_transfer_complete_interrupt(dma->dma, dma->tx_chan); /* FIXME do we need to explicitly disable the half transfer interrupt? */ - /* enable DMA channels */ - if (_trans->input_length > 0) { - dma_enable_channel(dma->dma, dma->rx_chan); - } + /* Enable DMA channels */ + dma_enable_channel(dma->dma, dma->rx_chan); dma_enable_channel(dma->dma, dma->tx_chan); - /* enable SPI transfers via DMA */ - if (_trans->input_length > 0) { - spi_enable_rx_dma((u32)periph->reg_addr); - } + /* Enable SPI transfers via DMA */ + spi_enable_rx_dma((u32)periph->reg_addr); spi_enable_tx_dma((u32)periph->reg_addr); } @@ -910,40 +913,80 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { struct spi_periph_dma *dma = periph->init_struct; struct spi_transaction *trans = periph->trans[periph->trans_extract_idx]; - // disable DMA Channel + /* Disable DMA Channel */ dma_disable_transfer_complete_interrupt(dma->dma, dma->rx_chan); - // Disable SPI Rx request + /* Disable SPI Rx request */ spi_disable_rx_dma((u32)periph->reg_addr); - // Disable DMA rx channel + /* Disable DMA rx channel */ dma_disable_channel(dma->dma, dma->rx_chan); if (dma->other_dma_finished != 0) { - // this transaction is finished - // run the callback + /* This transaction is finished */ + /* + * In theory, the RXNE flag will be cleared by the rx dma reading the SPI_DR + * since we are receiving the same amount as transmitting, and also because of + * this, we should always being entering this if clause, and never the equivalent + * in process_tx_dma_interrupt(), since the rx dma will finish after the last data + * while the tx will fire just before the last word is sent. + */ + /* Run the callback */ trans->status = SPITransSuccess; if (trans->after_cb != 0) { trans->after_cb(trans); } - // AFTER the callback, then unselect the slave if required + /* AFTER the callback, then unselect the slave if required */ if (trans->select == SPISelectUnselect || trans->select == SPIUnselect) { SpiSlaveUnselect(trans->slave_idx); } - // increment the transaction to handle + /* Increment the transaction to handle */ periph->trans_extract_idx++; - // Check if there is another pending SPI transaction + /* Check if there is another pending SPI transaction */ if (periph->trans_extract_idx >= SPI_TRANSACTION_QUEUE_LEN) periph->trans_extract_idx = 0; if (periph->trans_extract_idx == periph->trans_insert_idx || periph->suspend) periph->status = SPIIdle; else spi_rw(periph, periph->trans[periph->trans_extract_idx]); + } else if (dma->rx_use_dummy_dma == 1) { + /* + * We are finished the first part of the receive with real data, but still need + * to run the dma to get a fully complete interrupt. Set up a dummy dma receive transfer + * to accomplish this. + */ + + /* Reset the flag so this only happens once in a transaction */ + dma->rx_use_dummy_dma = 0; + + dma_channel_reset(dma->dma, dma->rx_chan); + dma_set_peripheral_address(dma->dma, dma->rx_chan, (u32)dma->spidr); + /* Use the dummy buffer if rx length is zero */ + dma_set_memory_address(dma->dma, dma->rx_chan, (u32)&(dma->rx_dummy_buf)); + dma_disable_memory_increment_mode(dma->dma, dma->rx_chan); + /* Use the difference in length between rx and tx */ + dma_set_number_of_data(dma->dma, dma->rx_chan, (u16)(trans->output_length - trans->input_length)); + dma_set_read_from_memory(dma->dma, dma->rx_chan); + /* Set the DMA transfer size based on SPI transaction DSS */ + if (trans->dss == SPIDss8bit) { + dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_8BIT); + dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_8BIT); + } else { + dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_16BIT); + dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_16BIT); + } + dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_HIGH); + /* Enable DMA transfer complete interrupts. */ + dma_enable_transfer_complete_interrupt(dma->dma, dma->rx_chan); + /* Enable DMA channels */ + dma_enable_channel(dma->dma, dma->rx_chan); + /* Enable SPI transfers via DMA */ + spi_enable_rx_dma((u32)periph->reg_addr); } else { - // if this is not the last part of the transaction, set finished flag + /* If this is not the last part of the transaction, set finished flag */ dma->other_dma_finished = 1; } } @@ -953,47 +996,48 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { struct spi_periph_dma *dma = periph->init_struct; struct spi_transaction *trans = periph->trans[periph->trans_extract_idx]; - // disable DMA Channel + /* Disable DMA Channel */ dma_disable_transfer_complete_interrupt(dma->dma, dma->tx_chan); - // Disable SPI TX request + /* Disable SPI TX request */ spi_disable_tx_dma((u32)periph->reg_addr); - // Disable DMA tx channel + /* Disable DMA tx channel */ dma_disable_channel(dma->dma, dma->tx_chan); if (dma->other_dma_finished != 0) { - // this transaction is finished - // run the callback + /* This transaction is finished */ + /* Theoretically this should never actually run... */ + /* Run the callback */ trans->status = SPITransSuccess; if (trans->after_cb != 0) { trans->after_cb(trans); } - // AFTER the callback, then unselect the slave if required + /* AFTER the callback, then unselect the slave if required */ if (trans->select == SPISelectUnselect || trans->select == SPIUnselect) { SpiSlaveUnselect(trans->slave_idx); } - // increment the transaction to handle + /* Increment the transaction to handle */ periph->trans_extract_idx++; - // Check if there is another pending SPI transaction + /* Check if there is another pending SPI transaction */ if (periph->trans_extract_idx >= SPI_TRANSACTION_QUEUE_LEN) periph->trans_extract_idx = 0; if (periph->trans_extract_idx == periph->trans_insert_idx || periph->suspend) periph->status = SPIIdle; else spi_rw(periph, periph->trans[periph->trans_extract_idx]); - } else if (dma->use_dummy_dma == 1) { + } else if (dma->tx_use_dummy_dma == 1) { /* * We are finished the first part of the transmit with real data, but still need * to clock in the rest of the receive data. Set up a dummy dma transmit transfer * to accomplish this. */ - // reset the flag so this only happens once in a transaction - dma->use_dummy_dma = 0; + /* Reset the flag so this only happens once in a transaction */ + dma->tx_use_dummy_dma = 0; dma_channel_reset(dma->dma, dma->tx_chan); dma_set_peripheral_address(dma->dma, dma->tx_chan, (u32)dma->spidr); @@ -1014,13 +1058,13 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); /* Enable DMA transfer complete interrupts. */ dma_enable_transfer_complete_interrupt(dma->dma, dma->tx_chan); - /* enable DMA channels */ + /* Enable DMA channels */ dma_enable_channel(dma->dma, dma->tx_chan); - /* enable SPI transfers via DMA */ + /* Enable SPI transfers via DMA */ spi_enable_tx_dma((u32)periph->reg_addr); } else { - // if this is not the last part of the transaction, set finished flag + /* If this is not the last part of the transaction, set finished flag */ dma->other_dma_finished = 1; } } From 594183f987b302ea5dcce8bef962ea939bd16a7e Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Fri, 1 Mar 2013 16:37:20 +0100 Subject: [PATCH 03/12] [stm32][spi] refactor: spi_next_transaction function --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 41 ++++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index 0dfe1848b85..dfff5ad52e3 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -61,9 +61,10 @@ #ifdef SPI_MASTER -static void spi_rw(struct spi_periph* p, struct spi_transaction * _trans); -static void process_rx_dma_interrupt( struct spi_periph *spi ); -static void process_tx_dma_interrupt( struct spi_periph *spi ); +static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans); +static void spi_next_transaction(struct spi_periph* periph); +static void process_rx_dma_interrupt(struct spi_periph* periph); +static void process_tx_dma_interrupt(struct spi_periph* periph); /** * Libopencm3 specifc communication parameters for a SPI peripheral in master mode. @@ -838,6 +839,20 @@ bool_t spi_resume(struct spi_periph* p, uint8_t slave) { return FALSE; } +/** start next transaction if there is one in the queue */ +static void spi_next_transaction(struct spi_periph* periph) { + /* Increment the transaction to handle */ + periph->trans_extract_idx++; + + /* Check if there is another pending SPI transaction */ + if (periph->trans_extract_idx >= SPI_TRANSACTION_QUEUE_LEN) + periph->trans_extract_idx = 0; + if ((periph->trans_extract_idx == periph->trans_insert_idx) || periph->suspend) + periph->status = SPIIdle; + else + spi_rw(periph, periph->trans[periph->trans_extract_idx]); +} + #ifdef USE_SPI1 /// receive transferred over DMA @@ -942,16 +957,8 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { SpiSlaveUnselect(trans->slave_idx); } - /* Increment the transaction to handle */ - periph->trans_extract_idx++; + spi_next_transaction(periph); - /* Check if there is another pending SPI transaction */ - if (periph->trans_extract_idx >= SPI_TRANSACTION_QUEUE_LEN) - periph->trans_extract_idx = 0; - if (periph->trans_extract_idx == periph->trans_insert_idx || periph->suspend) - periph->status = SPIIdle; - else - spi_rw(periph, periph->trans[periph->trans_extract_idx]); } else if (dma->rx_use_dummy_dma == 1) { /* * We are finished the first part of the receive with real data, but still need @@ -1019,16 +1026,8 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { SpiSlaveUnselect(trans->slave_idx); } - /* Increment the transaction to handle */ - periph->trans_extract_idx++; + spi_next_transaction(periph); - /* Check if there is another pending SPI transaction */ - if (periph->trans_extract_idx >= SPI_TRANSACTION_QUEUE_LEN) - periph->trans_extract_idx = 0; - if (periph->trans_extract_idx == periph->trans_insert_idx || periph->suspend) - periph->status = SPIIdle; - else - spi_rw(periph, periph->trans[periph->trans_extract_idx]); } else if (dma->tx_use_dummy_dma == 1) { /* * We are finished the first part of the transmit with real data, but still need From 761bd03aaa5d6e212e854b8400bc0e9515799f1e Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Fri, 1 Mar 2013 17:46:42 +0100 Subject: [PATCH 04/12] [stm32][spi] refactor: spi_configure_dma function --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 178 +++++++------------ 1 file changed, 68 insertions(+), 110 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index dfff5ad52e3..9d1c985d6c5 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -63,6 +63,7 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans); static void spi_next_transaction(struct spi_periph* periph); +static void spi_configure_dma(u32 dma, u8 chan, u32 periph_addr, u32 buf_addr, u16 len, enum SPIDataSizeSelect dss, bool_t increment); static void process_rx_dma_interrupt(struct spi_periph* periph); static void process_tx_dma_interrupt(struct spi_periph* periph); @@ -371,16 +372,16 @@ void spi1_arch_init(void) { spi1.status = SPIIdle; - // Enable SPI1 Periph and gpio clocks ------------------------------------------------- + // Enable SPI1 Periph and gpio clocks --------------------------------------- rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_SPI1EN); // Configure GPIOs: SCK, MISO and MOSI -------------------------------- gpio_set_mode(GPIO_BANK_SPI1_SCK, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_SPI1_SCK | - GPIO_SPI1_MOSI); + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, + GPIO_SPI1_SCK | GPIO_SPI1_MOSI); gpio_set_mode(GPIO_BANK_SPI1_MISO, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, - GPIO_SPI1_MISO); + GPIO_SPI1_MISO); // reset SPI spi_reset(SPI1); @@ -392,7 +393,8 @@ void spi1_arch_init(void) { SPI1_I2SCFGR = 0; // configure master SPI. - spi_init_master(SPI1, spi1_dma.comm.br, spi1_dma.comm.cpol, spi1_dma.comm.cpha, spi1_dma.comm.dff, spi1_dma.comm.lsbfirst); + spi_init_master(SPI1, spi1_dma.comm.br, spi1_dma.comm.cpol, spi1_dma.comm.cpha, + spi1_dma.comm.dff, spi1_dma.comm.lsbfirst); /* * Set NSS management to software. * @@ -442,16 +444,16 @@ void spi2_arch_init(void) { spi2.status = SPIIdle; - // Enable SPI2 Periph and gpio clocks ------------------------------------------------- + // Enable SPI2 Periph and gpio clocks --------------------------------------- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_SPI2EN); // Configure GPIOs: SCK, MISO and MOSI -------------------------------- gpio_set_mode(GPIO_BANK_SPI2_SCK, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_SPI2_SCK | - GPIO_SPI2_MOSI); + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, + GPIO_SPI2_SCK | GPIO_SPI2_MOSI); gpio_set_mode(GPIO_BANK_SPI2_MISO, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, - GPIO_SPI2_MISO); + GPIO_SPI2_MISO); // reset SPI spi_reset(SPI2); @@ -463,7 +465,8 @@ void spi2_arch_init(void) { SPI2_I2SCFGR = 0; // configure master SPI. - spi_init_master(SPI2, spi2_dma.comm.br, spi2_dma.comm.cpol, spi2_dma.comm.cpha, spi2_dma.comm.dff, spi2_dma.comm.lsbfirst); + spi_init_master(SPI2, spi2_dma.comm.br, spi2_dma.comm.cpol, spi2_dma.comm.cpha, + spi2_dma.comm.dff, spi2_dma.comm.lsbfirst); /* * Set NSS management to software. @@ -514,13 +517,13 @@ void spi3_arch_init(void) { spi3.status = SPIIdle; - // Enable SPI3 Periph and gpio clocks ------------------------------------------------- + // Enable SPI3 Periph and gpio clocks --------------------------------------- rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_SPI3EN); // Configure GPIOs: SCK, MISO and MOSI -------------------------------- gpio_set_mode(GPIO_BANK_SPI3_SCK, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_SPI3_SCK | - GPIO_SPI3_MOSI); + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, + GPIO_SPI3_SCK | GPIO_SPI3_MOSI); gpio_set_mode(GPIO_BANK_SPI3_MISO, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_SPI3_MISO); @@ -535,7 +538,8 @@ void spi3_arch_init(void) { SPI3_I2SCFGR = 0; // configure master SPI. - spi_init_master(SPI3, spi3_dma.comm.br, spi3_dma.comm.cpol, spi3_dma.comm.cpha, spi3_dma.comm.dff, spi3_dma.comm.lsbfirst); + spi_init_master(SPI3, spi3_dma.comm.br, spi3_dma.comm.cpol, spi3_dma.comm.cpha, + spi3_dma.comm.dff, spi3_dma.comm.lsbfirst); /* * Set NSS management to software. @@ -562,9 +566,6 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) { struct spi_periph_dma *dma; uint8_t sig = 0x00; - uint8_t max_length = 0; - bool_t use_dummy_tx_buf = FALSE; - bool_t use_dummy_rx_buf = FALSE; /* Store local copy to notify of the results */ _trans->status = SPITransRunning; @@ -585,7 +586,8 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) /* apply the new configuration */ spi_disable((u32)periph->reg_addr); - spi_init_master((u32)periph->reg_addr, dma->comm.br, dma->comm.cpol, dma->comm.cpha, dma->comm.dff, dma->comm.lsbfirst); + spi_init_master((u32)periph->reg_addr, dma->comm.br, dma->comm.cpol, + dma->comm.cpha, dma->comm.dff, dma->comm.lsbfirst); spi_enable_software_slave_management((u32)periph->reg_addr); spi_set_nss_high((u32)periph->reg_addr); spi_enable((u32)periph->reg_addr); @@ -619,97 +621,48 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) /* - * Determine the maximum length of the transaction. - * * To receive data, the clock must run, which means something has to be transmitted. * This is done by enabling a second, dummy dma transmit transfer with the dummy buffer. * Also, best to run receive to the very end to deal with timing issues in cleanup (i.e. * the dma for tx will trigger complete interrupt before all data is sent, meaning slave * is deselected and callback run BEFORE data is fully sent) */ - if (_trans->input_length > _trans->output_length) { - /* Receiving more than sending */ - max_length = _trans->input_length; - /* Make sure we send zeroed data while we are actually only receiving */ - if (_trans->output_length == 0) { - /* Special case: use dummy buffer */ - use_dummy_tx_buf = TRUE; - } else { + if (_trans->input_length > _trans->output_length && _trans->output_length > 0) { /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ dma->tx_use_dummy_dma = 1; - } - } else if (_trans->output_length > _trans->input_length) { - /* Transmitting more than receiving */ - max_length = _trans->output_length; - /* Make sure we ignore junk data while we are actually only transmitting */ - if (_trans->input_length == 0) { - /* Special case: use dummy buffer */ - use_dummy_rx_buf = TRUE; - } else { + } else if (_trans->output_length > _trans->input_length && _trans->input_length > 0) { /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ dma->rx_use_dummy_dma = 1; - } - } else { - /* We are sending equal amounts, use output length */ - max_length = _trans->output_length; } /* * Receive DMA channel configuration ---------------------------------------- */ - dma_channel_reset(dma->dma, dma->rx_chan); - dma_set_peripheral_address(dma->dma, dma->rx_chan, (u32)dma->spidr); /* Use the dummy buffer if rx length is zero */ - if (use_dummy_rx_buf) { - dma_set_memory_address(dma->dma, dma->rx_chan, (u32)&(dma->rx_dummy_buf)); - dma_disable_memory_increment_mode(dma->dma, dma->rx_chan); - /* Use the max length of rx or tx instead of actual rx length as described above */ - dma_set_number_of_data(dma->dma, dma->rx_chan, (u16)(max_length)); + if (_trans->input_length == 0) { + spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, + (u32)&(dma->rx_dummy_buf), _trans->output_length, _trans->dss, FALSE); } else { - dma_set_memory_address(dma->dma, dma->rx_chan, (u32)_trans->input_buf); - dma_enable_memory_increment_mode(dma->dma, dma->rx_chan); - dma_set_number_of_data(dma->dma, dma->rx_chan, (u16)(_trans->input_length)); + spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, + (u32)_trans->input_buf, _trans->input_length, _trans->dss, TRUE); } dma_set_read_from_peripheral(dma->dma, dma->rx_chan); - - /* Set the dma transfer size based on SPI transaction DSS */ - if (_trans->dss == SPIDss8bit) { - dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_8BIT); - dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_8BIT); - } else { - dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_16BIT); - dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_16BIT); - } dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_VERY_HIGH); /* * Transmit DMA channel configuration --------------------------------------- */ - dma_channel_reset(dma->dma, dma->tx_chan); - dma_set_peripheral_address(dma->dma, dma->tx_chan, (u32)dma->spidr); /* Use the dummy buffer if tx length is zero */ - if (use_dummy_tx_buf) { - dma_set_memory_address(dma->dma, dma->tx_chan, (u32)&(dma->tx_dummy_buf)); - dma_disable_memory_increment_mode(dma->dma, dma->tx_chan); - /* Use the max length of rx or tx instead of actual tx length as described above */ - dma_set_number_of_data(dma->dma, dma->tx_chan, (u16)(max_length)); + if (_trans->output_length == 0) { + spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, + (u32)&(dma->tx_dummy_buf), _trans->input_length, _trans->dss, FALSE); } else { - dma_set_memory_address(dma->dma, dma->tx_chan, (u32)_trans->output_buf); - dma_enable_memory_increment_mode(dma->dma, dma->tx_chan); - dma_set_number_of_data(dma->dma, dma->tx_chan, (u16)(_trans->output_length)); + spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, + (u32)_trans->output_buf, _trans->output_length, _trans->dss, TRUE); } dma_set_read_from_memory(dma->dma, dma->tx_chan); - - /* Set the DMA transfer size based on SPI transaction DSS */ - if (_trans->dss == SPIDss8bit) { - dma_set_peripheral_size(dma->dma, dma->tx_chan, DMA_CCR_PSIZE_8BIT); - dma_set_memory_size(dma->dma, dma->tx_chan, DMA_CCR_MSIZE_8BIT); - } else { - dma_set_peripheral_size(dma->dma, dma->tx_chan, DMA_CCR_PSIZE_16BIT); - dma_set_memory_size(dma->dma, dma->tx_chan, DMA_CCR_MSIZE_16BIT); - } dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); @@ -844,15 +797,38 @@ static void spi_next_transaction(struct spi_periph* periph) { /* Increment the transaction to handle */ periph->trans_extract_idx++; - /* Check if there is another pending SPI transaction */ + /* wrap read index of circular buffer */ if (periph->trans_extract_idx >= SPI_TRANSACTION_QUEUE_LEN) periph->trans_extract_idx = 0; + + /* Check if there is another pending SPI transaction */ if ((periph->trans_extract_idx == periph->trans_insert_idx) || periph->suspend) periph->status = SPIIdle; else spi_rw(periph, periph->trans[periph->trans_extract_idx]); } +static void spi_configure_dma(u32 dma, u8 chan, u32 periph_addr, u32 buf_addr, u16 len, enum SPIDataSizeSelect dss, bool_t increment) { + dma_channel_reset(dma, chan); + dma_set_peripheral_address(dma, chan, periph_addr); + dma_set_memory_address(dma, chan, buf_addr); + dma_set_number_of_data(dma, chan, len); + + /* Set the dma transfer size based on SPI transaction DSS */ + if (dss == SPIDss8bit) { + dma_set_peripheral_size(dma, chan, DMA_CCR_PSIZE_8BIT); + dma_set_memory_size(dma, chan, DMA_CCR_MSIZE_8BIT); + } else { + dma_set_peripheral_size(dma, chan, DMA_CCR_PSIZE_16BIT); + dma_set_memory_size(dma, chan, DMA_CCR_MSIZE_16BIT); + } + + if (increment) + dma_enable_memory_increment_mode(dma, chan); + else + dma_disable_memory_increment_mode(dma, chan); +} + #ifdef USE_SPI1 /// receive transferred over DMA @@ -969,23 +945,14 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { /* Reset the flag so this only happens once in a transaction */ dma->rx_use_dummy_dma = 0; - dma_channel_reset(dma->dma, dma->rx_chan); - dma_set_peripheral_address(dma->dma, dma->rx_chan, (u32)dma->spidr); - /* Use the dummy buffer if rx length is zero */ - dma_set_memory_address(dma->dma, dma->rx_chan, (u32)&(dma->rx_dummy_buf)); - dma_disable_memory_increment_mode(dma->dma, dma->rx_chan); /* Use the difference in length between rx and tx */ - dma_set_number_of_data(dma->dma, dma->rx_chan, (u16)(trans->output_length - trans->input_length)); - dma_set_read_from_memory(dma->dma, dma->rx_chan); - /* Set the DMA transfer size based on SPI transaction DSS */ - if (trans->dss == SPIDss8bit) { - dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_8BIT); - dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_8BIT); - } else { - dma_set_peripheral_size(dma->dma, dma->rx_chan, DMA_CCR_PSIZE_16BIT); - dma_set_memory_size(dma->dma, dma->rx_chan, DMA_CCR_MSIZE_16BIT); - } + u16 len_remaining = trans->output_length - trans->input_length; + + spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, + (u32)&(dma->rx_dummy_buf), len_remaining, trans->dss, FALSE); + dma_set_read_from_peripheral(dma->dma, dma->rx_chan); dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_HIGH); + /* Enable DMA transfer complete interrupts. */ dma_enable_transfer_complete_interrupt(dma->dma, dma->rx_chan); /* Enable DMA channels */ @@ -1038,23 +1005,14 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { /* Reset the flag so this only happens once in a transaction */ dma->tx_use_dummy_dma = 0; - dma_channel_reset(dma->dma, dma->tx_chan); - dma_set_peripheral_address(dma->dma, dma->tx_chan, (u32)dma->spidr); - /* Use the dummy buffer if tx length is zero */ - dma_set_memory_address(dma->dma, dma->tx_chan, (u32)&(dma->tx_dummy_buf)); - dma_disable_memory_increment_mode(dma->dma, dma->tx_chan); - /* Use the difference in length between rx and tx */ - dma_set_number_of_data(dma->dma, dma->tx_chan, (u16)(trans->input_length - trans->output_length)); + /* Use the difference in length between tx and rx */ + u16 len_remaining = trans->input_length - trans->output_length; + + spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, + (u32)&(dma->tx_dummy_buf), len_remaining, trans->dss, FALSE); dma_set_read_from_memory(dma->dma, dma->tx_chan); - /* Set the DMA transfer size based on SPI transaction DSS */ - if (trans->dss == SPIDss8bit) { - dma_set_peripheral_size(dma->dma, dma->tx_chan, DMA_CCR_PSIZE_8BIT); - dma_set_memory_size(dma->dma, dma->tx_chan, DMA_CCR_MSIZE_8BIT); - } else { - dma_set_peripheral_size(dma->dma, dma->tx_chan, DMA_CCR_PSIZE_16BIT); - dma_set_memory_size(dma->dma, dma->tx_chan, DMA_CCR_MSIZE_16BIT); - } dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); + /* Enable DMA transfer complete interrupts. */ dma_enable_transfer_complete_interrupt(dma->dma, dma->tx_chan); /* Enable DMA channels */ From e5cfe24255611c1fe3bfc0776cbf334c3b24c73a Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Fri, 1 Mar 2013 18:13:06 +0100 Subject: [PATCH 05/12] [stm32][spi] style --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index 9d1c985d6c5..8a01e8336f2 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -562,13 +562,13 @@ void spi3_arch_init(void) { } #endif -static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) +static void spi_rw(struct spi_periph* periph, struct spi_transaction* trans) { struct spi_periph_dma *dma; uint8_t sig = 0x00; /* Store local copy to notify of the results */ - _trans->status = SPITransRunning; + trans->status = SPITransRunning; periph->status = SPIRunning; dma = periph->init_struct; @@ -576,10 +576,10 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) /* * Check if we need to reconfigure the spi peripheral for this transaction */ - sig = get_transaction_signature(_trans); + sig = get_transaction_signature(trans); if (sig != dma->comm_sig) { /* A different config is required in this transaction... */ - set_comm_from_transaction(&(dma->comm), _trans); + set_comm_from_transaction(&(dma->comm), trans); /* remember the new conf signature */ dma->comm_sig = sig; @@ -599,13 +599,13 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) /* * Select the slave after reconfiguration of the peripheral */ - if (_trans->select == SPISelectUnselect || _trans->select == SPISelect) { - SpiSlaveSelect(_trans->slave_idx); + if (trans->select == SPISelectUnselect || trans->select == SPISelect) { + SpiSlaveSelect(trans->slave_idx); } /* Run the callback AFTER selecting the slave */ - if (_trans->before_cb != 0) { - _trans->before_cb(_trans); + if (trans->before_cb != 0) { + trans->before_cb(trans); } /* @@ -627,10 +627,10 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) * the dma for tx will trigger complete interrupt before all data is sent, meaning slave * is deselected and callback run BEFORE data is fully sent) */ - if (_trans->input_length > _trans->output_length && _trans->output_length > 0) { + if (trans->input_length > trans->output_length && trans->output_length > 0) { /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ dma->tx_use_dummy_dma = 1; - } else if (_trans->output_length > _trans->input_length && _trans->input_length > 0) { + } else if (trans->output_length > trans->input_length && trans->input_length > 0) { /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ dma->rx_use_dummy_dma = 1; } @@ -640,12 +640,12 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) * Receive DMA channel configuration ---------------------------------------- */ /* Use the dummy buffer if rx length is zero */ - if (_trans->input_length == 0) { + if (trans->input_length == 0) { spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, - (u32)&(dma->rx_dummy_buf), _trans->output_length, _trans->dss, FALSE); + (u32)&(dma->rx_dummy_buf), trans->output_length, trans->dss, FALSE); } else { spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, - (u32)_trans->input_buf, _trans->input_length, _trans->dss, TRUE); + (u32)trans->input_buf, trans->input_length, trans->dss, TRUE); } dma_set_read_from_peripheral(dma->dma, dma->rx_chan); dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_VERY_HIGH); @@ -655,12 +655,12 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) * Transmit DMA channel configuration --------------------------------------- */ /* Use the dummy buffer if tx length is zero */ - if (_trans->output_length == 0) { + if (trans->output_length == 0) { spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, - (u32)&(dma->tx_dummy_buf), _trans->input_length, _trans->dss, FALSE); + (u32)&(dma->tx_dummy_buf), trans->input_length, trans->dss, FALSE); } else { spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, - (u32)_trans->output_buf, _trans->output_length, _trans->dss, TRUE); + (u32)trans->output_buf, trans->output_length, trans->dss, TRUE); } dma_set_read_from_memory(dma->dma, dma->tx_chan); dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); From bb7a04fede16fd488ee8d78906a179b45dceec0d Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Fri, 1 Mar 2013 18:56:39 +0100 Subject: [PATCH 06/12] [stm32][spi] removed other_dma_finished, always run rx dma till the end of transaction --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 128 ++++++++----------- 1 file changed, 50 insertions(+), 78 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index 8a01e8336f2..a582a57110f 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -90,7 +90,6 @@ struct spi_periph_dma { u8 tx_chan; ///< transmit DMA channel number u8 rx_nvic_irq; ///< receive interrupt u8 tx_nvic_irq; ///< transmit interrupt - u8 other_dma_finished; u16 tx_dummy_buf; ///< dummy tx buffer for receive only cases u8 tx_use_dummy_dma; ///< second dma transmit with dummy buffer for tx_len < rx_len u16 rx_dummy_buf; ///< dummy rx buffer for receive only cases @@ -354,7 +353,6 @@ void spi1_arch_init(void) { spi1_dma.tx_chan = DMA_CHANNEL3; spi1_dma.rx_nvic_irq = NVIC_DMA1_CHANNEL2_IRQ; spi1_dma.tx_nvic_irq = NVIC_DMA1_CHANNEL3_IRQ; - spi1_dma.other_dma_finished = 0; spi1_dma.tx_dummy_buf = 0; spi1_dma.tx_use_dummy_dma = 0; spi1_dma.rx_dummy_buf = 0; @@ -426,7 +424,6 @@ void spi2_arch_init(void) { spi2_dma.tx_chan = DMA_CHANNEL5; spi2_dma.rx_nvic_irq = NVIC_DMA1_CHANNEL4_IRQ; spi2_dma.tx_nvic_irq = NVIC_DMA1_CHANNEL5_IRQ; - spi2_dma.other_dma_finished = 0; spi2_dma.tx_dummy_buf = 0; spi2_dma.tx_use_dummy_dma = 0; spi2_dma.rx_dummy_buf = 0; @@ -499,7 +496,6 @@ void spi3_arch_init(void) { spi3_dma.tx_chan = DMA_CHANNEL2; spi3_dma.rx_nvic_irq = NVIC_DMA2_CHANNEL1_IRQ; spi3_dma.tx_nvic_irq = NVIC_DMA2_CHANNEL2_IRQ; - spi3_dma.other_dma_finished = 0; spi3_dma.tx_dummy_buf = 0; spi3_dma.tx_use_dummy_dma = 0; spi3_dma.rx_dummy_buf = 0; @@ -608,44 +604,32 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* trans) trans->before_cb(trans); } - /* - * Clear flag for interrupt order handling - * - * Note: If one of the transaction lengths is 0, it won't trigger an interrupt. - * This is like the interrupt has already finished, so you specify that the other - * dma has already finished, and everything is cleaned up after the one interrupt - * that actually runs. The case that both lengths are zero is guarded against in - * spi_submit. - */ - dma->other_dma_finished = 0; - - - /* - * To receive data, the clock must run, which means something has to be transmitted. - * This is done by enabling a second, dummy dma transmit transfer with the dummy buffer. - * Also, best to run receive to the very end to deal with timing issues in cleanup (i.e. - * the dma for tx will trigger complete interrupt before all data is sent, meaning slave - * is deselected and callback run BEFORE data is fully sent) - */ - if (trans->input_length > trans->output_length && trans->output_length > 0) { - /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ - dma->tx_use_dummy_dma = 1; - } else if (trans->output_length > trans->input_length && trans->input_length > 0) { - /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ - dma->rx_use_dummy_dma = 1; - } - - /* * Receive DMA channel configuration ---------------------------------------- + * + * We always run the receive DMA until the very end! + * This is done so we can use the transfer complete interrupt + * of the RX DMA to signal the end of the transaction. + * + * If we want to receive less than we transmit, a dummy buffer + * for the rx DMA is used after for the remaining data. + * + * In the transmit only case (input_length == 0), + * the dummy is used right from the start. */ - /* Use the dummy buffer if rx length is zero */ if (trans->input_length == 0) { + /* run the dummy rx dma for the complete transaction length */ spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, (u32)&(dma->rx_dummy_buf), trans->output_length, trans->dss, FALSE); } else { + /* run the real rx dma for input_length */ spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, (u32)trans->input_buf, trans->input_length, trans->dss, TRUE); + /* use dummy rx dma for the rest */ + if (trans->output_length > trans->input_length) { + /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ + dma->rx_use_dummy_dma = 1; + } } dma_set_read_from_peripheral(dma->dma, dma->rx_chan); dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_VERY_HIGH); @@ -653,6 +637,10 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* trans) /* * Transmit DMA channel configuration --------------------------------------- + * + * We always run the transmit DMA! + * To receive data, the clock must run, which means something has to be transmitted. + * This is done by enabling a second, dummy dma transmit transfer with the dummy buffer. */ /* Use the dummy buffer if tx length is zero */ if (trans->output_length == 0) { @@ -661,11 +649,16 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* trans) } else { spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, (u32)trans->output_buf, trans->output_length, trans->dss, TRUE); + if (trans->input_length > trans->output_length) { + /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ + dma->tx_use_dummy_dma = 1; + } } dma_set_read_from_memory(dma->dma, dma->tx_chan); dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); + /* * Enable DMA --------------------------------------------------------------- */ @@ -913,29 +906,8 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { /* Disable DMA rx channel */ dma_disable_channel(dma->dma, dma->rx_chan); - if (dma->other_dma_finished != 0) { - /* This transaction is finished */ - /* - * In theory, the RXNE flag will be cleared by the rx dma reading the SPI_DR - * since we are receiving the same amount as transmitting, and also because of - * this, we should always being entering this if clause, and never the equivalent - * in process_tx_dma_interrupt(), since the rx dma will finish after the last data - * while the tx will fire just before the last word is sent. - */ - /* Run the callback */ - trans->status = SPITransSuccess; - if (trans->after_cb != 0) { - trans->after_cb(trans); - } - - /* AFTER the callback, then unselect the slave if required */ - if (trans->select == SPISelectUnselect || trans->select == SPIUnselect) { - SpiSlaveUnselect(trans->slave_idx); - } - - spi_next_transaction(periph); - } else if (dma->rx_use_dummy_dma == 1) { + if (dma->rx_use_dummy_dma == 1) { /* * We are finished the first part of the receive with real data, but still need * to run the dma to get a fully complete interrupt. Set up a dummy dma receive transfer @@ -959,9 +931,28 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { dma_enable_channel(dma->dma, dma->rx_chan); /* Enable SPI transfers via DMA */ spi_enable_rx_dma((u32)periph->reg_addr); - } else { - /* If this is not the last part of the transaction, set finished flag */ - dma->other_dma_finished = 1; + } + else { + /* This transaction is finished */ + /* + * In theory, the RXNE flag will be cleared by the rx dma reading the SPI_DR + * since we are receiving the same amount as transmitting, and also because of + * this, we should always being entering this if clause, and never the equivalent + * in process_tx_dma_interrupt(), since the rx dma will finish after the last data + * while the tx will fire just before the last word is sent. + */ + /* Run the callback */ + trans->status = SPITransSuccess; + if (trans->after_cb != 0) { + trans->after_cb(trans); + } + + /* AFTER the callback, then unselect the slave if required */ + if (trans->select == SPISelectUnselect || trans->select == SPIUnselect) { + SpiSlaveUnselect(trans->slave_idx); + } + + spi_next_transaction(periph); } } @@ -979,23 +970,7 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { /* Disable DMA tx channel */ dma_disable_channel(dma->dma, dma->tx_chan); - if (dma->other_dma_finished != 0) { - /* This transaction is finished */ - /* Theoretically this should never actually run... */ - /* Run the callback */ - trans->status = SPITransSuccess; - if (trans->after_cb != 0) { - trans->after_cb(trans); - } - - /* AFTER the callback, then unselect the slave if required */ - if (trans->select == SPISelectUnselect || trans->select == SPIUnselect) { - SpiSlaveUnselect(trans->slave_idx); - } - - spi_next_transaction(periph); - - } else if (dma->tx_use_dummy_dma == 1) { + if (dma->tx_use_dummy_dma == 1) { /* * We are finished the first part of the transmit with real data, but still need * to clock in the rest of the receive data. Set up a dummy dma transmit transfer @@ -1020,9 +995,6 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { /* Enable SPI transfers via DMA */ spi_enable_tx_dma((u32)periph->reg_addr); - } else { - /* If this is not the last part of the transaction, set finished flag */ - dma->other_dma_finished = 1; } } From ab783897da5ef2547282f90f093c03bedb8b129b Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Fri, 1 Mar 2013 19:32:30 +0100 Subject: [PATCH 07/12] [stm32][spi] rename x_use_dummy_dma to x_extra_dummy_dma --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index a582a57110f..1ca293e51dc 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -91,9 +91,9 @@ struct spi_periph_dma { u8 rx_nvic_irq; ///< receive interrupt u8 tx_nvic_irq; ///< transmit interrupt u16 tx_dummy_buf; ///< dummy tx buffer for receive only cases - u8 tx_use_dummy_dma; ///< second dma transmit with dummy buffer for tx_len < rx_len + bool_t tx_extra_dummy_dma; ///< second dma transmit with dummy buffer for tx_len < rx_len u16 rx_dummy_buf; ///< dummy rx buffer for receive only cases - u8 rx_use_dummy_dma; ///< second dma transmit with dummy buffer for tx_len > rx_len + bool_t rx_extra_dummy_dma; ///< second dma receive with dummy buffer for tx_len > rx_len struct locm3_spi_comm comm; ///< current communication paramters u8 comm_sig; ///< comm config signature used to check for changes: cdiv, cpol, cpha, dss, bo }; @@ -354,9 +354,9 @@ void spi1_arch_init(void) { spi1_dma.rx_nvic_irq = NVIC_DMA1_CHANNEL2_IRQ; spi1_dma.tx_nvic_irq = NVIC_DMA1_CHANNEL3_IRQ; spi1_dma.tx_dummy_buf = 0; - spi1_dma.tx_use_dummy_dma = 0; + spi1_dma.tx_extra_dummy_dma = FALSE; spi1_dma.rx_dummy_buf = 0; - spi1_dma.rx_use_dummy_dma = 0; + spi1_dma.rx_extra_dummy_dma = FALSE; // set the default configuration set_default_comm_config(&spi1_dma.comm); @@ -425,9 +425,9 @@ void spi2_arch_init(void) { spi2_dma.rx_nvic_irq = NVIC_DMA1_CHANNEL4_IRQ; spi2_dma.tx_nvic_irq = NVIC_DMA1_CHANNEL5_IRQ; spi2_dma.tx_dummy_buf = 0; - spi2_dma.tx_use_dummy_dma = 0; + spi2_dma.tx_extra_dummy_dma = FALSE; spi2_dma.rx_dummy_buf = 0; - spi2_dma.rx_use_dummy_dma = 0; + spi2_dma.rx_extra_dummy_dma = FALSE; // set the default configuration set_default_comm_config(&spi2_dma.comm); @@ -497,9 +497,9 @@ void spi3_arch_init(void) { spi3_dma.rx_nvic_irq = NVIC_DMA2_CHANNEL1_IRQ; spi3_dma.tx_nvic_irq = NVIC_DMA2_CHANNEL2_IRQ; spi3_dma.tx_dummy_buf = 0; - spi3_dma.tx_use_dummy_dma = 0; + spi3_dma.tx_extra_dummy_dma = FALSE; spi3_dma.rx_dummy_buf = 0; - spi3_dma.rx_use_dummy_dma = 0; + spi3_dma.rx_extra_dummy_dma = FALSE; // set the default configuration set_default_comm_config(&spi3_dma.comm); @@ -628,7 +628,7 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* trans) /* use dummy rx dma for the rest */ if (trans->output_length > trans->input_length) { /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ - dma->rx_use_dummy_dma = 1; + dma->rx_extra_dummy_dma = TRUE; } } dma_set_read_from_peripheral(dma->dma, dma->rx_chan); @@ -651,7 +651,7 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* trans) (u32)trans->output_buf, trans->output_length, trans->dss, TRUE); if (trans->input_length > trans->output_length) { /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ - dma->tx_use_dummy_dma = 1; + dma->tx_extra_dummy_dma = TRUE; } } dma_set_read_from_memory(dma->dma, dma->tx_chan); @@ -907,7 +907,7 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { dma_disable_channel(dma->dma, dma->rx_chan); - if (dma->rx_use_dummy_dma == 1) { + if (dma->rx_extra_dummy_dma) { /* * We are finished the first part of the receive with real data, but still need * to run the dma to get a fully complete interrupt. Set up a dummy dma receive transfer @@ -915,7 +915,7 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { */ /* Reset the flag so this only happens once in a transaction */ - dma->rx_use_dummy_dma = 0; + dma->rx_extra_dummy_dma = FALSE; /* Use the difference in length between rx and tx */ u16 len_remaining = trans->output_length - trans->input_length; @@ -970,7 +970,7 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { /* Disable DMA tx channel */ dma_disable_channel(dma->dma, dma->tx_chan); - if (dma->tx_use_dummy_dma == 1) { + if (dma->tx_extra_dummy_dma) { /* * We are finished the first part of the transmit with real data, but still need * to clock in the rest of the receive data. Set up a dummy dma transmit transfer @@ -978,7 +978,7 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { */ /* Reset the flag so this only happens once in a transaction */ - dma->tx_use_dummy_dma = 0; + dma->tx_extra_dummy_dma = FALSE; /* Use the difference in length between tx and rx */ u16 len_remaining = trans->input_length - trans->output_length; From 3ecd088f344f8a8f41cb246e852838cdf000610c Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Fri, 22 Feb 2013 20:47:22 +0100 Subject: [PATCH 08/12] [peripherals] adxl345: enable measurement mode AFTER other configuration is done --- sw/airborne/lisa/test/lisa_test_adxl345_dma.c | 4 ++-- sw/airborne/peripherals/adxl345.h | 6 +++--- sw/airborne/peripherals/adxl345_i2c.c | 10 +++++----- sw/airborne/peripherals/adxl345_spi.c | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/sw/airborne/lisa/test/lisa_test_adxl345_dma.c b/sw/airborne/lisa/test/lisa_test_adxl345_dma.c index fbf46d2397d..efaeb095e90 100644 --- a/sw/airborne/lisa/test/lisa_test_adxl345_dma.c +++ b/sw/airborne/lisa/test/lisa_test_adxl345_dma.c @@ -90,13 +90,13 @@ static inline void main_periodic_task( void ) { if (acc_status != CONFIGURED) { /* set data rate to 800Hz */ write_to_reg(ADXL345_REG_BW_RATE, 0x0D); - /* switch to measurememnt mode */ - write_to_reg(ADXL345_REG_POWER_CTL, 1<<3); /* enable data ready interrupt */ write_to_reg(ADXL345_REG_INT_ENABLE, 1<<7); /* Enable full res and interrupt active low */ write_to_reg(ADXL345_REG_DATA_FORMAT, 1<<3|1<<5); /* reads data once to bring interrupt line up */ + /* switch to measurememnt mode */ + write_to_reg(ADXL345_REG_POWER_CTL, 1<<3); read_data(); acc_status = CONFIGURED; } diff --git a/sw/airborne/peripherals/adxl345.h b/sw/airborne/peripherals/adxl345.h index 0d11d5e23fe..cfedad8bd86 100644 --- a/sw/airborne/peripherals/adxl345.h +++ b/sw/airborne/peripherals/adxl345.h @@ -36,9 +36,9 @@ enum Adxl345ConfStatus { ADXL_CONF_UNINIT = 0, ADXL_CONF_RATE = 1, - ADXL_CONF_POWER = 2, - ADXL_CONF_INT = 3, - ADXL_CONF_FORMAT = 4, + ADXL_CONF_INT = 2, + ADXL_CONF_FORMAT = 3, + ADXL_CONF_ENABLE = 4, ADXL_CONF_DONE = 5 }; diff --git a/sw/airborne/peripherals/adxl345_i2c.c b/sw/airborne/peripherals/adxl345_i2c.c index fea916fab3b..be3dcca5a78 100644 --- a/sw/airborne/peripherals/adxl345_i2c.c +++ b/sw/airborne/peripherals/adxl345_i2c.c @@ -64,11 +64,6 @@ static void adxl345_i2c_send_config(struct Adxl345_I2c *adxl) adxl345_i2c_tx_reg(adxl, ADXL345_REG_BW_RATE, adxl->config.rate); adxl->init_status++; break; - case ADXL_CONF_POWER: - /* enable measurement, is in standby after power up */ - adxl345_i2c_tx_reg(adxl, ADXL345_REG_POWER_CTL, (0x1<<3)); - adxl->init_status++; - break; case ADXL_CONF_INT: adxl345_i2c_tx_reg(adxl, ADXL345_REG_INT_ENABLE, adxl->config.drdy_int_enable); adxl->init_status++; @@ -77,6 +72,11 @@ static void adxl345_i2c_send_config(struct Adxl345_I2c *adxl) adxl345_i2c_tx_reg(adxl, ADXL345_REG_DATA_FORMAT, ADXL345_DATA_FORMAT); adxl->init_status++; break; + case ADXL_CONF_ENABLE: + /* enable measurement, is in standby after power up */ + adxl345_i2c_tx_reg(adxl, ADXL345_REG_POWER_CTL, (0x1<<3)); + adxl->init_status++; + break; case ADXL_CONF_DONE: adxl->initialized = TRUE; adxl->i2c_trans.status = I2CTransDone; diff --git a/sw/airborne/peripherals/adxl345_spi.c b/sw/airborne/peripherals/adxl345_spi.c index 762550b3366..a5f3caa5d86 100644 --- a/sw/airborne/peripherals/adxl345_spi.c +++ b/sw/airborne/peripherals/adxl345_spi.c @@ -82,11 +82,6 @@ static void adxl345_spi_send_config(struct Adxl345_Spi *adxl) adxl345_spi_write_to_reg(adxl, ADXL345_REG_BW_RATE, adxl->config.rate); adxl->init_status++; break; - case ADXL_CONF_POWER: - /* enable measurement, is in standby after power up */ - adxl345_spi_write_to_reg(adxl, ADXL345_REG_POWER_CTL, (0x1<<3)); - adxl->init_status++; - break; case ADXL_CONF_INT: adxl345_spi_write_to_reg(adxl, ADXL345_REG_INT_ENABLE, (adxl->config.drdy_int_enable << 7)); adxl->init_status++; @@ -95,6 +90,11 @@ static void adxl345_spi_send_config(struct Adxl345_Spi *adxl) adxl345_spi_write_to_reg(adxl, ADXL345_REG_DATA_FORMAT, adxl345_data_format(&adxl->config)); adxl->init_status++; break; + case ADXL_CONF_ENABLE: + /* enable measurement, is in standby after power up */ + adxl345_spi_write_to_reg(adxl, ADXL345_REG_POWER_CTL, (0x1<<3)); + adxl->init_status++; + break; case ADXL_CONF_DONE: adxl->initialized = TRUE; adxl->spi_trans.status = SPITransDone; From 0ce07331ea7edebdd549ffd76e0f3931a42f6641 Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Tue, 26 Feb 2013 23:37:26 +0100 Subject: [PATCH 09/12] [peripherals] adxl345_spi: set input/output length of spi transaction to actual lenghts --- sw/airborne/lisa/test/lisa_test_adxl345_dma.c | 9 ++++----- sw/airborne/peripherals/adxl345_spi.c | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/sw/airborne/lisa/test/lisa_test_adxl345_dma.c b/sw/airborne/lisa/test/lisa_test_adxl345_dma.c index efaeb095e90..ff2ce230e83 100644 --- a/sw/airborne/lisa/test/lisa_test_adxl345_dma.c +++ b/sw/airborne/lisa/test/lisa_test_adxl345_dma.c @@ -114,9 +114,6 @@ static inline void main_event_task( void ) { } if (acc_status >= CONFIGURED && acc_data_available) { acc_data_available = FALSE; - //int16_t ax = dma_rx_buf[1] | (dma_rx_buf[2]<<8); - //int16_t ay = dma_rx_buf[3] | (dma_rx_buf[4]<<8); - //int16_t az = dma_rx_buf[5] | (dma_rx_buf[6]<<8); int16_t ax = Int16FromBuf(dma_rx_buf, 1); int16_t ay = Int16FromBuf(dma_rx_buf, 3); int16_t az = Int16FromBuf(dma_rx_buf, 5); @@ -128,7 +125,8 @@ static inline void main_event_task( void ) { } static void write_to_reg(uint8_t addr, uint8_t val) { - //adxl345_spi_trans.output_length = 2; + adxl345_spi_trans.output_length = 2; + adxl345_spi_trans.input_length = 0; dma_tx_buf[0] = addr; dma_tx_buf[1] = val; spi_submit(&(ADXL345_SPI_DEV), &adxl345_spi_trans); @@ -137,7 +135,8 @@ static void write_to_reg(uint8_t addr, uint8_t val) { } static void read_data(void) { - //adxl345_spi_trans.output_length = 1; + adxl345_spi_trans.output_length = 1; + adxl345_spi_trans.input_length = 7; dma_tx_buf[0] = (1<<7|1<<6|ADXL345_REG_DATA_X0); spi_submit(&(ADXL345_SPI_DEV), &adxl345_spi_trans); } diff --git a/sw/airborne/peripherals/adxl345_spi.c b/sw/airborne/peripherals/adxl345_spi.c index a5f3caa5d86..9f488eb67fc 100644 --- a/sw/airborne/peripherals/adxl345_spi.c +++ b/sw/airborne/peripherals/adxl345_spi.c @@ -67,8 +67,8 @@ void adxl345_spi_init(struct Adxl345_Spi *adxl, struct spi_periph *spi_p, uint8_ static void adxl345_spi_write_to_reg(struct Adxl345_Spi *adxl, uint8_t _reg, uint8_t _val) { - //adxl->spi_trans.output_length = 2; - //adxl->spi_trans.input_length = 0; + adxl->spi_trans.output_length = 2; + adxl->spi_trans.input_length = 0; adxl->tx_buf[0] = _reg; adxl->tx_buf[1] = _val; spi_submit(adxl->spi_p, &(adxl->spi_trans)); @@ -117,8 +117,8 @@ void adxl345_spi_start_configure(struct Adxl345_Spi *adxl) void adxl345_spi_read(struct Adxl345_Spi *adxl) { if (adxl->initialized && adxl->spi_trans.status == SPITransDone) { - //adxl->spi_trans.output_length = 1; - //adxl->spi_trans.input_length = 7; + adxl->spi_trans.output_length = 1; + adxl->spi_trans.input_length = 7; /* set read bit and multiple byte bit, then address */ adxl->tx_buf[0] = (1<<7|1<<6|ADXL345_REG_DATA_X0); spi_submit(adxl->spi_p, &(adxl->spi_trans)); From bae344bfd1dbfb48dd8bf28d45047ece4f7acb7e Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Fri, 1 Mar 2013 23:01:22 +0100 Subject: [PATCH 10/12] [stm32][spi] finally fix zero length cases... should fix #348 --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index 1ca293e51dc..91ebeacc423 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -324,16 +324,12 @@ static inline void SpiSlaveSelect(uint8_t slave) /// Enable DMA channel interrupts // FIXME fix priority levels if necessary static void spi_arch_int_enable( struct spi_periph *spi ) { - if (spi->trans[spi->trans_extract_idx]->input_length != 0) { - // only enable the receive interrupt if we want to receive something - nvic_set_priority( ((struct spi_periph_dma *)spi->init_struct)->rx_nvic_irq, 0); - nvic_enable_irq( ((struct spi_periph_dma *)spi->init_struct)->rx_nvic_irq ); - } - if (spi->trans[spi->trans_extract_idx]->output_length != 0) { - // only enable the transmit interrupt if we want to transmit something - nvic_set_priority( ((struct spi_periph_dma *)spi->init_struct)->tx_nvic_irq, 0); - nvic_enable_irq( ((struct spi_periph_dma *)spi->init_struct)->tx_nvic_irq ); - } + // enable receive interrupt + nvic_set_priority( ((struct spi_periph_dma *)spi->init_struct)->rx_nvic_irq, 0); + nvic_enable_irq( ((struct spi_periph_dma *)spi->init_struct)->rx_nvic_irq ); + // enable transmit interrupt + nvic_set_priority( ((struct spi_periph_dma *)spi->init_struct)->tx_nvic_irq, 0); + nvic_enable_irq( ((struct spi_periph_dma *)spi->init_struct)->tx_nvic_irq ); } /// Disable DMA channel interrupts From 4b4bc2ed9d4581b039634ac63e60ea955b5e75b1 Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Sat, 2 Mar 2013 14:57:21 +0100 Subject: [PATCH 11/12] [tests] update imu_b2_2 test to use hmc58xx driver --- conf/airframes/TestHardware/LisaL_v1.1_b2_v1.2_rc.xml | 6 ++++-- conf/firmwares/lisa_test_progs.makefile | 7 +++---- sw/airborne/subsystems/imu/imu_b2.c | 11 +++++++---- sw/airborne/test/subsystems/test_imu.c | 9 ++++----- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/conf/airframes/TestHardware/LisaL_v1.1_b2_v1.2_rc.xml b/conf/airframes/TestHardware/LisaL_v1.1_b2_v1.2_rc.xml index 8dcdcb8f963..589699bda38 100644 --- a/conf/airframes/TestHardware/LisaL_v1.1_b2_v1.2_rc.xml +++ b/conf/airframes/TestHardware/LisaL_v1.1_b2_v1.2_rc.xml @@ -33,17 +33,19 @@ - + + - + + diff --git a/conf/firmwares/lisa_test_progs.makefile b/conf/firmwares/lisa_test_progs.makefile index f06a93fcea1..eecffbceafd 100644 --- a/conf/firmwares/lisa_test_progs.makefile +++ b/conf/firmwares/lisa_test_progs.makefile @@ -361,10 +361,10 @@ test_imu_b2.srcs += $(IMU_B2_SRCS) # IMU_B2_2_CFLAGS = -DIMU_B2_VERSION_1_2 # mag stuff -IMU_B2_2_CFLAGS += -DUSE_I2C2 +IMU_B2_2_CFLAGS += -DIMU_B2_I2C_DEV=i2c2 -DUSE_I2C2 IMU_B2_2_SRCS = mcu_periph/i2c.c $(SRC_ARCH)/mcu_periph/i2c_arch.c -IMU_B2_2_CFLAGS += -DIMU_B2_MAG_TYPE=IMU_B2_MAG_HMC5843 -IMU_B2_2_SRCS += peripherals/hmc5843.c $(SRC_ARCH)/peripherals/hmc5843_arch.c +IMU_B2_2_CFLAGS += -DIMU_B2_MAG_TYPE=IMU_B2_MAG_HMC58XX +IMU_B2_2_SRCS += peripherals/hmc58xx.c test_imu_b2_2.ARCHDIR = $(ARCH) test_imu_b2_2.srcs = test/subsystems/test_imu.c @@ -498,7 +498,6 @@ test_hmc5843.srcs += lisa/test/lisa_test_hmc5843.c test_hmc5843.CFLAGS += -DUSE_I2C2 test_hmc5843.srcs += mcu_periph/i2c.c $(SRC_ARCH)/mcu_periph/i2c_arch.c - # # test ITG3200 # diff --git a/sw/airborne/subsystems/imu/imu_b2.c b/sw/airborne/subsystems/imu/imu_b2.c index 2c573f97d82..37dd29b6249 100644 --- a/sw/airborne/subsystems/imu/imu_b2.c +++ b/sw/airborne/subsystems/imu/imu_b2.c @@ -24,12 +24,17 @@ * @file subsystems/imu/imu_b2.c * * Driver for the Booz2 IMUs. + * + * Analog gyros and accelerometers are read via MAX1168 16-bit SPI ADC. + * Depending on version, different I2C or SPI magnetometers are used. */ #include "subsystems/imu.h" struct ImuBooz2 imu_b2; +PRINT_CONFIG_VAR(IMU_B2_MAG_TYPE) + void imu_impl_init(void) { max1168_init(); @@ -53,11 +58,9 @@ void imu_periodic(void) { // read mag #if defined IMU_B2_MAG_TYPE && IMU_B2_MAG_TYPE == IMU_B2_MAG_MS2100 ms2100_periodic(&ms2100); -#endif -#if defined IMU_B2_MAG_TYPE && IMU_B2_MAG_TYPE == IMU_B2_MAG_AMI601 +#elif defined IMU_B2_MAG_TYPE && IMU_B2_MAG_TYPE == IMU_B2_MAG_AMI601 RunOnceEvery(10, { ami601_read(); }); -#endif -#if defined IMU_B2_MAG_TYPE && IMU_B2_MAG_TYPE == IMU_B2_MAG_HMC58XX +#elif defined IMU_B2_MAG_TYPE && IMU_B2_MAG_TYPE == IMU_B2_MAG_HMC58XX RunOnceEvery(5, hmc58xx_periodic(&imu_b2.mag_hmc)); #endif diff --git a/sw/airborne/test/subsystems/test_imu.c b/sw/airborne/test/subsystems/test_imu.c index 7d543d57b46..332cbba003f 100644 --- a/sw/airborne/test/subsystems/test_imu.c +++ b/sw/airborne/test/subsystems/test_imu.c @@ -41,7 +41,7 @@ static inline void main_init( void ); static inline void main_periodic_task( void ); static inline void main_event_task( void ); -static inline void on_gyro_accel_event(void); +static inline void on_gyro_event(void); static inline void on_accel_event(void); static inline void on_mag_event(void); @@ -69,7 +69,7 @@ static inline void main_init( void ) { static inline void led_toggle ( void ) { #ifdef BOARD_LISA_L - LED_TOGGLE(3); + LED_TOGGLE(7); #endif } @@ -109,8 +109,7 @@ static inline void main_periodic_task( void ) { static inline void main_event_task( void ) { - ImuEvent(on_gyro_accel_event, on_accel_event, on_mag_event); - + ImuEvent(on_gyro_event, on_accel_event, on_mag_event); } @@ -135,7 +134,7 @@ static inline void on_accel_event(void) { } } -static inline void on_gyro_accel_event(void) { +static inline void on_gyro_event(void) { ImuScaleGyro(imu); RunOnceEvery(50, LED_TOGGLE(2)); From f1b565693267f6989633395a346e8adae1d9f375 Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Sat, 2 Mar 2013 15:22:57 +0100 Subject: [PATCH 12/12] [stm32][spi] no functional changes, only readability/comments/dox --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 797 ++++++++++--------- sw/airborne/mcu_periph/spi.h | 4 +- sw/airborne/peripherals/max1168.c | 11 +- 3 files changed, 420 insertions(+), 392 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index 91ebeacc423..5edb1152e2c 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -28,26 +28,27 @@ * SPI Master code. * * When a transaction is submitted: - * - The transaction is added to the queue if there is space, otherwise it returns false + * - The transaction is added to the queue if there is space, + * otherwise it returns false * - The pending state is set - * - SPI Interrupts (in this case the dma interrupts) are disabled to prevent race conditions - * - The slave is selected if required, AFTER which the before_cb callback is run - * - The spi and dma registers are set up appropriately for the specific transaction - * - Spi and dma are enabled, interrupts are reenabled and the transaction starts + * - SPI Interrupts (in this case the DMA interrupts) are disabled + * to prevent race conditions + * - The slave is selected if required, then the before_cb callback is run + * - The spi and dma registers are set up for the specific transaction + * - Spi, DMA and interrupts are enabled and the transaction starts * - * For the dma and interrupts: - * - For each transaction, an interrupt is called after the dma transfer is complete for the rx AND the tx (i.e. two) - * - Each interrupt does some basic cleanup necessary to finish off each dma transfer - * - The after_cb callback, slave unselect, status changes and further transactions only occur after both dma - * transfers are complete, using a state flag. Note that the callback happens BEFORE the slave unselect - * - If the output_len != input_len, a dummy dma transfer is triggered for the remainder so the same amount - * of data is moved in and out. This simplifies keeping the clock going if output_len is greater and allows - * the rx dma interrupt to represent that the transaction has fully completed. The second dummy transfer is - * initiated at the transaction setup if either length is 0, otherwise after the first dma interrupt completes - * in the ISR directly - * - * It is assumed that the transmit output_length and receive input_length will never be 0 at the same time. + * Obviously output_length and input_length will never both be 0 at the same time. * In this case, spi_submit will just return false. + * + * For the DMA and interrupts: + * - If the output_len != input_len, a dummy DMA transfer is triggered for + * the remainder so the same amount of data is moved in and out. + * This simplifies keeping the clock going if output_len is greater and allows + * the rx dma interrupt to represent that the transaction has fully completed. + * - The dummy DMA transfer is initiated at the transaction setup if length is 0, + * otherwise after the first dma interrupt completes in the ISR directly. + * - The rx DMA transfer completed interrupt marks the end of a complete transaction. + * - The after_cb callback happens BEFORE the slave is unselected as configured. */ #include @@ -61,12 +62,6 @@ #ifdef SPI_MASTER -static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans); -static void spi_next_transaction(struct spi_periph* periph); -static void spi_configure_dma(u32 dma, u8 chan, u32 periph_addr, u32 buf_addr, u16 len, enum SPIDataSizeSelect dss, bool_t increment); -static void process_rx_dma_interrupt(struct spi_periph* periph); -static void process_tx_dma_interrupt(struct spi_periph* periph); - /** * Libopencm3 specifc communication parameters for a SPI peripheral in master mode. */ @@ -91,11 +86,11 @@ struct spi_periph_dma { u8 rx_nvic_irq; ///< receive interrupt u8 tx_nvic_irq; ///< transmit interrupt u16 tx_dummy_buf; ///< dummy tx buffer for receive only cases - bool_t tx_extra_dummy_dma; ///< second dma transmit with dummy buffer for tx_len < rx_len + bool_t tx_extra_dummy_dma; ///< extra tx dummy dma flag for tx_len < rx_len u16 rx_dummy_buf; ///< dummy rx buffer for receive only cases - bool_t rx_extra_dummy_dma; ///< second dma receive with dummy buffer for tx_len > rx_len + bool_t rx_extra_dummy_dma; ///< extra rx dummy dma flag for tx_len > rx_len struct locm3_spi_comm comm; ///< current communication paramters - u8 comm_sig; ///< comm config signature used to check for changes: cdiv, cpol, cpha, dss, bo + u8 comm_sig; ///< comm config signature used to check for changes }; @@ -112,7 +107,22 @@ static struct spi_periph_dma spi2_dma; static struct spi_periph_dma spi3_dma; #endif +static void spi_start_dma_transaction(struct spi_periph* periph, struct spi_transaction* _trans); +static void spi_next_transaction(struct spi_periph* periph); +static void spi_configure_dma(u32 dma, u8 chan, u32 periph_addr, u32 buf_addr, + u16 len, enum SPIDataSizeSelect dss, bool_t increment); +static void process_rx_dma_interrupt(struct spi_periph* periph); +static void process_tx_dma_interrupt(struct spi_periph* periph); +static void spi_arch_int_enable(struct spi_periph *spi); +static void spi_arch_int_disable(struct spi_periph *spi); + +/****************************************************************************** + * + * Handling of Slave Select outputs + * + *****************************************************************************/ +/// @todo move the SS gpio defines to the board files #define SPI_SELECT_SLAVE0_PERIPH RCC_APB2ENR_IOPAEN #define SPI_SELECT_SLAVE0_PORT GPIOA #define SPI_SELECT_SLAVE0_PIN GPIO15 @@ -133,7 +143,194 @@ static struct spi_periph_dma spi3_dma; #define SPI_SELECT_SLAVE4_PORT GPIOC #define SPI_SELECT_SLAVE4_PIN GPIO12 +static inline void SpiSlaveUnselect(uint8_t slave) { + switch(slave) { +#if USE_SPI_SLAVE0 + case 0: + GPIO_BSRR(SPI_SELECT_SLAVE0_PORT) = SPI_SELECT_SLAVE0_PIN; + break; +#endif // USE_SPI_SLAVE0 +#if USE_SPI_SLAVE1 + case 1: + GPIO_BSRR(SPI_SELECT_SLAVE1_PORT) = SPI_SELECT_SLAVE1_PIN; + break; +#endif //USE_SPI_SLAVE1 +#if USE_SPI_SLAVE2 + case 2: + GPIO_BSRR(SPI_SELECT_SLAVE2_PORT) = SPI_SELECT_SLAVE2_PIN; + break; +#endif //USE_SPI_SLAVE2 +#if USE_SPI_SLAVE3 + case 3: + GPIO_BSRR(SPI_SELECT_SLAVE3_PORT) = SPI_SELECT_SLAVE3_PIN; + break; +#endif //USE_SPI_SLAVE3 +#if USE_SPI_SLAVE4 + case 4: + GPIO_BSRR(SPI_SELECT_SLAVE4_PORT) = SPI_SELECT_SLAVE4_PIN; + break; +#endif //USE_SPI_SLAVE4 + default: + break; + } +} + +static inline void SpiSlaveSelect(uint8_t slave) { + switch(slave) { +#if USE_SPI_SLAVE0 + case 0: + GPIO_BRR(SPI_SELECT_SLAVE0_PORT) = SPI_SELECT_SLAVE0_PIN; + break; +#endif // USE_SPI_SLAVE0 +#if USE_SPI_SLAVE1 + case 1: + GPIO_BRR(SPI_SELECT_SLAVE1_PORT) = SPI_SELECT_SLAVE1_PIN; + break; +#endif //USE_SPI_SLAVE1 +#if USE_SPI_SLAVE2 + case 2: + GPIO_BRR(SPI_SELECT_SLAVE2_PORT) = SPI_SELECT_SLAVE2_PIN; + break; +#endif //USE_SPI_SLAVE2 +#if USE_SPI_SLAVE3 + case 3: + GPIO_BRR(SPI_SELECT_SLAVE3_PORT) = SPI_SELECT_SLAVE3_PIN; + break; +#endif //USE_SPI_SLAVE3 +#if USE_SPI_SLAVE4 + case 4: + GPIO_BRR(SPI_SELECT_SLAVE4_PORT) = SPI_SELECT_SLAVE4_PIN; + break; +#endif //USE_SPI_SLAVE4 + default: + break; + } +} + +void spi_slave_select(uint8_t slave) { + SpiSlaveSelect(slave); +} + +void spi_slave_unselect(uint8_t slave) { + SpiSlaveUnselect(slave); +} + +void spi_init_slaves(void) { + +#if USE_SPI_SLAVE0 + rcc_peripheral_enable_clock(&RCC_APB2ENR, + SPI_SELECT_SLAVE0_PERIPH | RCC_APB2ENR_AFIOEN); + SpiSlaveUnselect(0); + gpio_set(SPI_SELECT_SLAVE0_PORT, SPI_SELECT_SLAVE0_PIN); + gpio_set_mode(SPI_SELECT_SLAVE0_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE0_PIN); +#endif +#if USE_SPI_SLAVE1 + rcc_peripheral_enable_clock(&RCC_APB2ENR, + SPI_SELECT_SLAVE1_PERIPH | RCC_APB2ENR_AFIOEN); + SpiSlaveUnselect(1); + gpio_set(SPI_SELECT_SLAVE1_PORT, SPI_SELECT_SLAVE1_PIN); + gpio_set_mode(SPI_SELECT_SLAVE1_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE1_PIN); +#endif + +#if USE_SPI_SLAVE2 + rcc_peripheral_enable_clock(&RCC_APB2ENR, + SPI_SELECT_SLAVE2_PERIPH | RCC_APB2ENR_AFIOEN); + SpiSlaveUnselect(2); + gpio_set(SPI_SELECT_SLAVE2_PORT, SPI_SELECT_SLAVE2_PIN); + gpio_set_mode(SPI_SELECT_SLAVE2_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE2_PIN); +#endif + +#if USE_SPI_SLAVE3 + rcc_peripheral_enable_clock(&RCC_APB2ENR, + SPI_SELECT_SLAVE3_PERIPH | RCC_APB2ENR_AFIOEN); + SpiSlaveUnselect(3); + gpio_set(SPI_SELECT_SLAVE3_PORT, SPI_SELECT_SLAVE3_PIN); + gpio_set_mode(SPI_SELECT_SLAVE3_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE3_PIN); +#endif + +#if USE_SPI_SLAVE4 + rcc_peripheral_enable_clock(&RCC_APB2ENR, + SPI_SELECT_SLAVE4_PERIPH | RCC_APB2ENR_AFIOEN); + SpiSlaveUnselect(4); + gpio_set(SPI_SELECT_SLAVE4_PORT, SPI_SELECT_SLAVE4_PIN); + gpio_set_mode(SPI_SELECT_SLAVE4_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE4_PIN); +#endif +} + + +/****************************************************************************** + * + * Implementation of the generic SPI functions + * + *****************************************************************************/ +bool_t spi_submit(struct spi_periph* p, struct spi_transaction* t) +{ + uint8_t idx; + idx = p->trans_insert_idx + 1; + if (idx >= SPI_TRANSACTION_QUEUE_LEN) idx = 0; + if ((idx == p->trans_extract_idx) || ((t->input_length == 0) && (t->output_length == 0))) { + t->status = SPITransFailed; + return FALSE; /* queue full or input_length and output_length both 0 */ + // TODO can't tell why it failed here if it does + } + + t->status = SPITransPending; + + //Disable interrupts to avoid race conflict with end of DMA transfer interrupt + //FIXME + spi_arch_int_disable(p); + + // GT: no copy? There's a queue implying a copy here... + p->trans[p->trans_insert_idx] = t; + p->trans_insert_idx = idx; + + /* if peripheral is idle, start the transaction */ + if (p->status == SPIIdle && !p->suspend) { + spi_start_dma_transaction(p, p->trans[p->trans_extract_idx]); + } + //FIXME + spi_arch_int_enable(p); + return TRUE; +} + +bool_t spi_lock(struct spi_periph* p, uint8_t slave) { + spi_arch_int_disable(p); + if (slave < 254 && p->suspend == 0) { + p->suspend = slave + 1; // 0 is reserved for unlock state + spi_arch_int_enable(p); + return TRUE; + } + spi_arch_int_enable(p); + return FALSE; +} + +bool_t spi_resume(struct spi_periph* p, uint8_t slave) { + spi_arch_int_disable( p ); + if (p->suspend == slave + 1) { + // restart fifo + p->suspend = 0; + if (p->trans_extract_idx != p->trans_insert_idx && p->status == SPIIdle) { + spi_start_dma_transaction(p, p->trans[p->trans_extract_idx]); + } + spi_arch_int_enable(p); + return TRUE; + } + spi_arch_int_enable(p); + return FALSE; +} + + +/****************************************************************************** + * + * Transaction configuration helper functions + * + *****************************************************************************/ static void set_default_comm_config(struct locm3_spi_comm* c) { c->br = SPI_CR1_BAUDRATE_FPCLK_DIV_64; c->cpol = SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLE; @@ -143,7 +340,8 @@ static void set_default_comm_config(struct locm3_spi_comm* c) { } static inline uint8_t get_transaction_signature(struct spi_transaction* t) { - return ((t->dss << 6) | (t->cdiv << 3) | (t->bitorder << 2) | (t->cpha << 1) | (t->cpol)); + return ((t->dss << 6) | (t->cdiv << 3) | (t->bitorder << 2) | + (t->cpha << 1) | (t->cpol)); } static uint8_t get_comm_signature(struct locm3_spi_comm* c) { @@ -254,76 +452,38 @@ static void set_comm_from_transaction(struct locm3_spi_comm* c, struct spi_trans } } -static inline void SpiSlaveUnselect(uint8_t slave) -{ - switch(slave) { -#if USE_SPI_SLAVE0 - case 0: - GPIO_BSRR(SPI_SELECT_SLAVE0_PORT) = SPI_SELECT_SLAVE0_PIN; - break; -#endif // USE_SPI_SLAVE0 -#if USE_SPI_SLAVE1 - case 1: - GPIO_BSRR(SPI_SELECT_SLAVE1_PORT) = SPI_SELECT_SLAVE1_PIN; - break; -#endif //USE_SPI_SLAVE1 -#if USE_SPI_SLAVE2 - case 2: - GPIO_BSRR(SPI_SELECT_SLAVE2_PORT) = SPI_SELECT_SLAVE2_PIN; - break; -#endif //USE_SPI_SLAVE2 -#if USE_SPI_SLAVE3 - case 3: - GPIO_BSRR(SPI_SELECT_SLAVE3_PORT) = SPI_SELECT_SLAVE3_PIN; - break; -#endif //USE_SPI_SLAVE3 -#if USE_SPI_SLAVE4 - case 4: - GPIO_BSRR(SPI_SELECT_SLAVE4_PORT) = SPI_SELECT_SLAVE4_PIN; - break; -#endif //USE_SPI_SLAVE4 - default: - break; - } -} - -static inline void SpiSlaveSelect(uint8_t slave) +/****************************************************************************** + * + * Helpers for SPI transactions with DMA + * + *****************************************************************************/ +static void spi_configure_dma(u32 dma, u8 chan, u32 periph_addr, u32 buf_addr, + u16 len, enum SPIDataSizeSelect dss, bool_t increment) { - switch(slave) { -#if USE_SPI_SLAVE0 - case 0: - GPIO_BRR(SPI_SELECT_SLAVE0_PORT) = SPI_SELECT_SLAVE0_PIN; - break; -#endif // USE_SPI_SLAVE0 -#if USE_SPI_SLAVE1 - case 1: - GPIO_BRR(SPI_SELECT_SLAVE1_PORT) = SPI_SELECT_SLAVE1_PIN; - break; -#endif //USE_SPI_SLAVE1 -#if USE_SPI_SLAVE2 - case 2: - GPIO_BRR(SPI_SELECT_SLAVE2_PORT) = SPI_SELECT_SLAVE2_PIN; - break; -#endif //USE_SPI_SLAVE2 -#if USE_SPI_SLAVE3 - case 3: - GPIO_BRR(SPI_SELECT_SLAVE3_PORT) = SPI_SELECT_SLAVE3_PIN; - break; -#endif //USE_SPI_SLAVE3 -#if USE_SPI_SLAVE4 - case 4: - GPIO_BRR(SPI_SELECT_SLAVE4_PORT) = SPI_SELECT_SLAVE4_PIN; - break; -#endif //USE_SPI_SLAVE4 - default: - break; + dma_channel_reset(dma, chan); + dma_set_peripheral_address(dma, chan, periph_addr); + dma_set_memory_address(dma, chan, buf_addr); + dma_set_number_of_data(dma, chan, len); + + /* Set the dma transfer size based on SPI transaction DSS */ + if (dss == SPIDss8bit) { + dma_set_peripheral_size(dma, chan, DMA_CCR_PSIZE_8BIT); + dma_set_memory_size(dma, chan, DMA_CCR_MSIZE_8BIT); + } else { + dma_set_peripheral_size(dma, chan, DMA_CCR_PSIZE_16BIT); + dma_set_memory_size(dma, chan, DMA_CCR_MSIZE_16BIT); } + + if (increment) + dma_enable_memory_increment_mode(dma, chan); + else + dma_disable_memory_increment_mode(dma, chan); } /// Enable DMA channel interrupts -// FIXME fix priority levels if necessary -static void spi_arch_int_enable( struct spi_periph *spi ) { +static void spi_arch_int_enable(struct spi_periph *spi) { + /// @todo fix priority levels if necessary // enable receive interrupt nvic_set_priority( ((struct spi_periph_dma *)spi->init_struct)->rx_nvic_irq, 0); nvic_enable_irq( ((struct spi_periph_dma *)spi->init_struct)->rx_nvic_irq ); @@ -333,12 +493,150 @@ static void spi_arch_int_enable( struct spi_periph *spi ) { } /// Disable DMA channel interrupts -static void spi_arch_int_disable( struct spi_periph *spi ) { +static void spi_arch_int_disable(struct spi_periph *spi) { nvic_disable_irq( ((struct spi_periph_dma *)spi->init_struct)->rx_nvic_irq ); nvic_disable_irq( ((struct spi_periph_dma *)spi->init_struct)->tx_nvic_irq ); } +/// start next transaction if there is one in the queue +static void spi_next_transaction(struct spi_periph* periph) { + /* Increment the transaction to handle */ + periph->trans_extract_idx++; + + /* wrap read index of circular buffer */ + if (periph->trans_extract_idx >= SPI_TRANSACTION_QUEUE_LEN) + periph->trans_extract_idx = 0; + + /* Check if there is another pending SPI transaction */ + if ((periph->trans_extract_idx == periph->trans_insert_idx) || periph->suspend) + periph->status = SPIIdle; + else + spi_start_dma_transaction(periph, periph->trans[periph->trans_extract_idx]); +} + + +/** + * Start a new transaction with DMA. + */ +static void spi_start_dma_transaction(struct spi_periph* periph, struct spi_transaction* trans) +{ + struct spi_periph_dma *dma; + uint8_t sig = 0x00; + + /* Store local copy to notify of the results */ + trans->status = SPITransRunning; + periph->status = SPIRunning; + + dma = periph->init_struct; + + /* + * Check if we need to reconfigure the spi peripheral for this transaction + */ + sig = get_transaction_signature(trans); + if (sig != dma->comm_sig) { + /* A different config is required in this transaction... */ + set_comm_from_transaction(&(dma->comm), trans); + + /* remember the new conf signature */ + dma->comm_sig = sig; + + /* apply the new configuration */ + spi_disable((u32)periph->reg_addr); + spi_init_master((u32)periph->reg_addr, dma->comm.br, dma->comm.cpol, + dma->comm.cpha, dma->comm.dff, dma->comm.lsbfirst); + spi_enable_software_slave_management((u32)periph->reg_addr); + spi_set_nss_high((u32)periph->reg_addr); + spi_enable((u32)periph->reg_addr); + } + + /* + * Select the slave after reconfiguration of the peripheral + */ + if (trans->select == SPISelectUnselect || trans->select == SPISelect) { + SpiSlaveSelect(trans->slave_idx); + } + + /* Run the callback AFTER selecting the slave */ + if (trans->before_cb != 0) { + trans->before_cb(trans); + } + + /* + * Receive DMA channel configuration ---------------------------------------- + * + * We always run the receive DMA until the very end! + * This is done so we can use the transfer complete interrupt + * of the RX DMA to signal the end of the transaction. + * + * If we want to receive less than we transmit, a dummy buffer + * for the rx DMA is used after for the remaining data. + * + * In the transmit only case (input_length == 0), + * the dummy is used right from the start. + */ + if (trans->input_length == 0) { + /* run the dummy rx dma for the complete transaction length */ + spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, + (u32)&(dma->rx_dummy_buf), trans->output_length, trans->dss, FALSE); + } else { + /* run the real rx dma for input_length */ + spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, + (u32)trans->input_buf, trans->input_length, trans->dss, TRUE); + /* use dummy rx dma for the rest */ + if (trans->output_length > trans->input_length) { + /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ + dma->rx_extra_dummy_dma = TRUE; + } + } + dma_set_read_from_peripheral(dma->dma, dma->rx_chan); + dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_VERY_HIGH); + + + /* + * Transmit DMA channel configuration --------------------------------------- + * + * We always run the transmit DMA! + * To receive data, the clock must run, so something has to be transmitted. + * If needed, use a dummy DMA transmitting zeros for the remaining length. + * + * In the reveive only case (output_length == 0), + * the dummy is used right from the start. + */ + if (trans->output_length == 0) { + spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, + (u32)&(dma->tx_dummy_buf), trans->input_length, trans->dss, FALSE); + } else { + spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, + (u32)trans->output_buf, trans->output_length, trans->dss, TRUE); + if (trans->input_length > trans->output_length) { + /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ + dma->tx_extra_dummy_dma = TRUE; + } + } + dma_set_read_from_memory(dma->dma, dma->tx_chan); + dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); + + + /* Enable DMA transfer complete interrupts. */ + dma_enable_transfer_complete_interrupt(dma->dma, dma->rx_chan); + dma_enable_transfer_complete_interrupt(dma->dma, dma->tx_chan); + + /* Enable DMA channels */ + dma_enable_channel(dma->dma, dma->rx_chan); + dma_enable_channel(dma->dma, dma->tx_chan); + + /* Enable SPI transfers via DMA */ + spi_enable_rx_dma((u32)periph->reg_addr); + spi_enable_tx_dma((u32)periph->reg_addr); +} + + +/****************************************************************************** + * + * Initialization of each SPI peripheral + * + *****************************************************************************/ #if USE_SPI1 void spi1_arch_init(void) { @@ -366,10 +664,10 @@ void spi1_arch_init(void) { spi1.status = SPIIdle; - // Enable SPI1 Periph and gpio clocks --------------------------------------- + // Enable SPI1 Periph and gpio clocks rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_SPI1EN); - // Configure GPIOs: SCK, MISO and MOSI -------------------------------- + // Configure GPIOs: SCK, MISO and MOSI gpio_set_mode(GPIO_BANK_SPI1_SCK, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_SPI1_SCK | GPIO_SPI1_MOSI); @@ -400,7 +698,7 @@ void spi1_arch_init(void) { spi_enable_software_slave_management(SPI1); spi_set_nss_high(SPI1); - // Enable SPI_1 DMA clock --------------------------------------------------- + // Enable SPI_1 DMA clock rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_DMA1EN); // Enable SPI1 periph. @@ -437,10 +735,10 @@ void spi2_arch_init(void) { spi2.status = SPIIdle; - // Enable SPI2 Periph and gpio clocks --------------------------------------- + // Enable SPI2 Periph and gpio clocks rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_SPI2EN); - // Configure GPIOs: SCK, MISO and MOSI -------------------------------- + // Configure GPIOs: SCK, MISO and MOSI gpio_set_mode(GPIO_BANK_SPI2_SCK, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_SPI2_SCK | GPIO_SPI2_MOSI); @@ -463,8 +761,6 @@ void spi2_arch_init(void) { /* * Set NSS management to software. - * - * Note: * Setting nss high is very important, even if we are controlling the GPIO * ourselves this bit needs to be at least set to 1, otherwise the spi * peripheral will not send any data out. @@ -472,7 +768,7 @@ void spi2_arch_init(void) { spi_enable_software_slave_management(SPI2); spi_set_nss_high(SPI2); - // Enable SPI_2 DMA clock --------------------------------------------------- + // Enable SPI_2 DMA clock rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_DMA1EN); // Enable SPI2 periph. @@ -509,10 +805,10 @@ void spi3_arch_init(void) { spi3.status = SPIIdle; - // Enable SPI3 Periph and gpio clocks --------------------------------------- + // Enable SPI3 Periph and gpio clocks rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_SPI3EN); - // Configure GPIOs: SCK, MISO and MOSI -------------------------------- + // Configure GPIOs: SCK, MISO and MOSI gpio_set_mode(GPIO_BANK_SPI3_SCK, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_SPI3_SCK | GPIO_SPI3_MOSI); @@ -520,6 +816,8 @@ void spi3_arch_init(void) { gpio_set_mode(GPIO_BANK_SPI3_MISO, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_SPI3_MISO); + /// @todo disable JTAG so the pins can be used? + // reset SPI spi_reset(SPI3); @@ -535,8 +833,6 @@ void spi3_arch_init(void) { /* * Set NSS management to software. - * - * Note: * Setting nss high is very important, even if we are controlling the GPIO * ourselves this bit needs to be at least set to 1, otherwise the spi * peripheral will not send any data out. @@ -544,281 +840,24 @@ void spi3_arch_init(void) { spi_enable_software_slave_management(SPI3); spi_set_nss_high(SPI3); - // Enable SPI_3 DMA clock --------------------------------------------------- + // Enable SPI_3 DMA clock rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_DMA2EN); // Enable SPI3 periph. spi_enable(SPI3); - spi_arch_int_enable( &spi3 ); -} -#endif - -static void spi_rw(struct spi_periph* periph, struct spi_transaction* trans) -{ - struct spi_periph_dma *dma; - uint8_t sig = 0x00; - - /* Store local copy to notify of the results */ - trans->status = SPITransRunning; - periph->status = SPIRunning; - - dma = periph->init_struct; - - /* - * Check if we need to reconfigure the spi peripheral for this transaction - */ - sig = get_transaction_signature(trans); - if (sig != dma->comm_sig) { - /* A different config is required in this transaction... */ - set_comm_from_transaction(&(dma->comm), trans); - - /* remember the new conf signature */ - dma->comm_sig = sig; - - /* apply the new configuration */ - spi_disable((u32)periph->reg_addr); - spi_init_master((u32)periph->reg_addr, dma->comm.br, dma->comm.cpol, - dma->comm.cpha, dma->comm.dff, dma->comm.lsbfirst); - spi_enable_software_slave_management((u32)periph->reg_addr); - spi_set_nss_high((u32)periph->reg_addr); - spi_enable((u32)periph->reg_addr); - - // FIXME this is also called immediately after spi_rw in spi_submit is this needed? - //spi_arch_int_enable( p ); - } - - /* - * Select the slave after reconfiguration of the peripheral - */ - if (trans->select == SPISelectUnselect || trans->select == SPISelect) { - SpiSlaveSelect(trans->slave_idx); - } - - /* Run the callback AFTER selecting the slave */ - if (trans->before_cb != 0) { - trans->before_cb(trans); - } - - /* - * Receive DMA channel configuration ---------------------------------------- - * - * We always run the receive DMA until the very end! - * This is done so we can use the transfer complete interrupt - * of the RX DMA to signal the end of the transaction. - * - * If we want to receive less than we transmit, a dummy buffer - * for the rx DMA is used after for the remaining data. - * - * In the transmit only case (input_length == 0), - * the dummy is used right from the start. - */ - if (trans->input_length == 0) { - /* run the dummy rx dma for the complete transaction length */ - spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, - (u32)&(dma->rx_dummy_buf), trans->output_length, trans->dss, FALSE); - } else { - /* run the real rx dma for input_length */ - spi_configure_dma(dma->dma, dma->rx_chan, (u32)dma->spidr, - (u32)trans->input_buf, trans->input_length, trans->dss, TRUE); - /* use dummy rx dma for the rest */ - if (trans->output_length > trans->input_length) { - /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ - dma->rx_extra_dummy_dma = TRUE; - } - } - dma_set_read_from_peripheral(dma->dma, dma->rx_chan); - dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_VERY_HIGH); - - - /* - * Transmit DMA channel configuration --------------------------------------- - * - * We always run the transmit DMA! - * To receive data, the clock must run, which means something has to be transmitted. - * This is done by enabling a second, dummy dma transmit transfer with the dummy buffer. - */ - /* Use the dummy buffer if tx length is zero */ - if (trans->output_length == 0) { - spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, - (u32)&(dma->tx_dummy_buf), trans->input_length, trans->dss, FALSE); - } else { - spi_configure_dma(dma->dma, dma->tx_chan, (u32)dma->spidr, - (u32)trans->output_buf, trans->output_length, trans->dss, TRUE); - if (trans->input_length > trans->output_length) { - /* Enable use of second dma transfer with dummy buffer (cleared in ISR) */ - dma->tx_extra_dummy_dma = TRUE; - } - } - dma_set_read_from_memory(dma->dma, dma->tx_chan); - dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); - - - - /* - * Enable DMA --------------------------------------------------------------- - */ - /* Enable DMA transfer complete interrupts. */ - dma_enable_transfer_complete_interrupt(dma->dma, dma->rx_chan); - dma_enable_transfer_complete_interrupt(dma->dma, dma->tx_chan); - /* FIXME do we need to explicitly disable the half transfer interrupt? */ - - /* Enable DMA channels */ - dma_enable_channel(dma->dma, dma->rx_chan); - dma_enable_channel(dma->dma, dma->tx_chan); - - /* Enable SPI transfers via DMA */ - spi_enable_rx_dma((u32)periph->reg_addr); - spi_enable_tx_dma((u32)periph->reg_addr); - -} - -bool_t spi_submit(struct spi_periph* p, struct spi_transaction* t) -{ - uint8_t idx; - idx = p->trans_insert_idx + 1; - if (idx >= SPI_TRANSACTION_QUEUE_LEN) idx = 0; - if ((idx == p->trans_extract_idx) || ((t->input_length == 0) && (t->output_length == 0))) { - t->status = SPITransFailed; - return FALSE; /* queue full or input_length and output_length both 0 */ - // TODO can't tell why it failed here if it does - } - - t->status = SPITransPending; - - //Disable interrupts to avoid race conflict with end of DMA transfer interrupt - //FIXME - spi_arch_int_disable(p); - - // GT: no copy? There's a queue implying a copy here... - p->trans[p->trans_insert_idx] = t; - p->trans_insert_idx = idx; - - /* if peripheral is idle, start the transaction */ - if (p->status == SPIIdle && !p->suspend) { - spi_rw(p, p->trans[p->trans_extract_idx]); - } - //FIXME - spi_arch_int_enable(p); - return TRUE; + spi_arch_int_enable(&spi3); } - -void spi_init_slaves(void) { - -#if USE_SPI_SLAVE0 - rcc_peripheral_enable_clock(&RCC_APB2ENR, SPI_SELECT_SLAVE0_PERIPH | RCC_APB2ENR_AFIOEN); - SpiSlaveUnselect(0); - gpio_set(SPI_SELECT_SLAVE0_PORT, SPI_SELECT_SLAVE0_PIN); - gpio_set_mode(SPI_SELECT_SLAVE0_PORT, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE0_PIN); -#endif - -#if USE_SPI_SLAVE1 - rcc_peripheral_enable_clock(&RCC_APB2ENR, SPI_SELECT_SLAVE1_PERIPH | RCC_APB2ENR_AFIOEN); - SpiSlaveUnselect(1); - gpio_set(SPI_SELECT_SLAVE1_PORT, SPI_SELECT_SLAVE1_PIN); - gpio_set_mode(SPI_SELECT_SLAVE1_PORT, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE1_PIN); -#endif - -#if USE_SPI_SLAVE2 - rcc_peripheral_enable_clock(&RCC_APB2ENR, SPI_SELECT_SLAVE2_PERIPH | RCC_APB2ENR_AFIOEN); - SpiSlaveUnselect(2); - gpio_set(SPI_SELECT_SLAVE2_PORT, SPI_SELECT_SLAVE2_PIN); - gpio_set_mode(SPI_SELECT_SLAVE2_PORT, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE2_PIN); -#endif - -#if USE_SPI_SLAVE3 - rcc_peripheral_enable_clock(&RCC_APB2ENR, SPI_SELECT_SLAVE3_PERIPH | RCC_APB2ENR_AFIOEN); - SpiSlaveUnselect(3); - gpio_set(SPI_SELECT_SLAVE3_PORT, SPI_SELECT_SLAVE3_PIN); - gpio_set_mode(SPI_SELECT_SLAVE3_PORT, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE3_PIN); -#endif - -#if USE_SPI_SLAVE4 - rcc_peripheral_enable_clock(&RCC_APB2ENR, SPI_SELECT_SLAVE4_PERIPH | RCC_APB2ENR_AFIOEN); - SpiSlaveUnselect(4); - gpio_set(SPI_SELECT_SLAVE4_PORT, SPI_SELECT_SLAVE4_PIN); - gpio_set_mode(SPI_SELECT_SLAVE4_PORT, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_PUSHPULL, SPI_SELECT_SLAVE4_PIN); #endif -} - -void spi_slave_select(uint8_t slave) { - SpiSlaveSelect(slave); -} - -void spi_slave_unselect(uint8_t slave) { - SpiSlaveUnselect(slave); -} - -bool_t spi_lock(struct spi_periph* p, uint8_t slave) { - spi_arch_int_disable(p); - if (slave < 254 && p->suspend == 0) { - p->suspend = slave + 1; // 0 is reserved for unlock state - spi_arch_int_enable(p); - return TRUE; - } - spi_arch_int_enable(p); - return FALSE; -} - -bool_t spi_resume(struct spi_periph* p, uint8_t slave) { - spi_arch_int_disable( p ); - if (p->suspend == slave + 1) { - // restart fifo - p->suspend = 0; - if (p->trans_extract_idx != p->trans_insert_idx && p->status == SPIIdle) { - spi_rw(p, p->trans[p->trans_extract_idx]); - } - spi_arch_int_enable(p); - return TRUE; - } - spi_arch_int_enable(p); - return FALSE; -} - -/** start next transaction if there is one in the queue */ -static void spi_next_transaction(struct spi_periph* periph) { - /* Increment the transaction to handle */ - periph->trans_extract_idx++; - /* wrap read index of circular buffer */ - if (periph->trans_extract_idx >= SPI_TRANSACTION_QUEUE_LEN) - periph->trans_extract_idx = 0; - - /* Check if there is another pending SPI transaction */ - if ((periph->trans_extract_idx == periph->trans_insert_idx) || periph->suspend) - periph->status = SPIIdle; - else - spi_rw(periph, periph->trans[periph->trans_extract_idx]); -} - -static void spi_configure_dma(u32 dma, u8 chan, u32 periph_addr, u32 buf_addr, u16 len, enum SPIDataSizeSelect dss, bool_t increment) { - dma_channel_reset(dma, chan); - dma_set_peripheral_address(dma, chan, periph_addr); - dma_set_memory_address(dma, chan, buf_addr); - dma_set_number_of_data(dma, chan, len); - - /* Set the dma transfer size based on SPI transaction DSS */ - if (dss == SPIDss8bit) { - dma_set_peripheral_size(dma, chan, DMA_CCR_PSIZE_8BIT); - dma_set_memory_size(dma, chan, DMA_CCR_MSIZE_8BIT); - } else { - dma_set_peripheral_size(dma, chan, DMA_CCR_PSIZE_16BIT); - dma_set_memory_size(dma, chan, DMA_CCR_MSIZE_16BIT); - } - - if (increment) - dma_enable_memory_increment_mode(dma, chan); - else - dma_disable_memory_increment_mode(dma, chan); -} +/****************************************************************************** + * + * DMA Interrupt service routines + * + *****************************************************************************/ #ifdef USE_SPI1 /// receive transferred over DMA void dma1_channel2_isr(void) @@ -905,9 +944,9 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { if (dma->rx_extra_dummy_dma) { /* - * We are finished the first part of the receive with real data, but still need - * to run the dma to get a fully complete interrupt. Set up a dummy dma receive transfer - * to accomplish this. + * We are finished the first part of the receive with real data, + * but still need to run the dummy to get a transfer complete interrupt + * after the complete transaction is done. */ /* Reset the flag so this only happens once in a transaction */ @@ -929,14 +968,12 @@ void process_rx_dma_interrupt(struct spi_periph *periph) { spi_enable_rx_dma((u32)periph->reg_addr); } else { - /* This transaction is finished */ /* - * In theory, the RXNE flag will be cleared by the rx dma reading the SPI_DR - * since we are receiving the same amount as transmitting, and also because of - * this, we should always being entering this if clause, and never the equivalent - * in process_tx_dma_interrupt(), since the rx dma will finish after the last data - * while the tx will fire just before the last word is sent. + * Since the receive DMA is always run until the very end + * and this interrupt is triggered after the last data word was read, + * we now know that this transaction is finished. */ + /* Run the callback */ trans->status = SPITransSuccess; if (trans->after_cb != 0) { @@ -968,9 +1005,9 @@ void process_tx_dma_interrupt(struct spi_periph *periph) { if (dma->tx_extra_dummy_dma) { /* - * We are finished the first part of the transmit with real data, but still need - * to clock in the rest of the receive data. Set up a dummy dma transmit transfer - * to accomplish this. + * We are finished the first part of the transmit with real data, + * but still need to clock in the rest of the receive data. + * Set up a dummy dma transmit transfer to accomplish this. */ /* Reset the flag so this only happens once in a transaction */ diff --git a/sw/airborne/mcu_periph/spi.h b/sw/airborne/mcu_periph/spi.h index 94993f7a6cc..50c17d2010e 100644 --- a/sw/airborne/mcu_periph/spi.h +++ b/sw/airborne/mcu_periph/spi.h @@ -135,9 +135,9 @@ typedef void (*SPICallback)( struct spi_transaction *trans ); * - The input/output buffers needs to be created separately * - Take care of pointing input_buf/ouput_buf correctly * - input_length and output_length can be different, the larger number - * of the two specifies the toal number of exchanged bytes, + * of the two specifies the toal number of exchanged words, * - if input_length is larger than output length, - * 0 is sent for the remaining bytes + * 0 is sent for the remaining words */ struct spi_transaction { volatile uint8_t* input_buf; ///< pointer to receive buffer for DMA diff --git a/sw/airborne/peripherals/max1168.c b/sw/airborne/peripherals/max1168.c index 4914ac331e2..1a560345b0c 100644 --- a/sw/airborne/peripherals/max1168.c +++ b/sw/airborne/peripherals/max1168.c @@ -93,7 +93,6 @@ void max1168_read( void ) { spi_submit(&(MAX1168_SPI_DEV),&max1168_read_trans); max1168_status = MAX1168_SENDING_REQ; - } void max1168_event( void ) { @@ -111,15 +110,7 @@ void max1168_event( void ) { // handle reading transaction if (max1168_read_trans.status == SPITransSuccess) { if (max1168_status == MAX1168_READING_RES) { - // store values - //max1168_values[0] = max1168_read_trans.input_buf[0]; - //max1168_values[1] = max1168_read_trans.input_buf[1]; - //max1168_values[2] = max1168_read_trans.input_buf[2]; - //max1168_values[3] = max1168_read_trans.input_buf[3]; - //max1168_values[4] = max1168_read_trans.input_buf[4]; - //max1168_values[5] = max1168_read_trans.input_buf[5]; - //max1168_values[6] = max1168_read_trans.input_buf[6]; - //max1168_values[7] = max1168_read_trans.input_buf[7]; + // result was already written to max1168_values by DMA max1168_status = MAX1168_DATA_AVAILABLE; max1168_read_trans.status = SPITransDone; }