From 97389f00a3ce1bfe23fc772a7a3c08b1df45711c Mon Sep 17 00:00:00 2001 From: Felix Ruess Date: Thu, 21 Feb 2013 12:06:25 +0100 Subject: [PATCH] [stm32][spi] select slave after configuring clock/phase, etc of peripheral Also: - replace tx_length_case by simpler use_dummy_tx_buf - minor comment cleanup while here... --- sw/airborne/arch/stm32/mcu_periph/spi_arch.c | 128 +++++++++---------- 1 file changed, 61 insertions(+), 67 deletions(-) diff --git a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index b7b3503225..60712b40cd 100644 --- a/sw/airborne/arch/stm32/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c @@ -571,34 +571,19 @@ 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; - typedef enum { - TX_LEN_LONGER = 0, - RX_LEN_LONGER_TX_0, - RX_LEN_LONGER_TX_NOT_0 - } spi_tx_length_case; - spi_tx_length_case tx_length_case = TX_LEN_LONGER; + bool_t use_dummy_tx_buf = FALSE; // Store local copy to notify of the results _trans->status = SPITransRunning; periph->status = SPIRunning; - // Select the slave if required - 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); - } - dma = periph->init_struct; - // FIXME this section is at least partially necessary but may block!!! - /* - * Wait until transceive complete. + /* 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)) ; @@ -610,23 +595,40 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) temp_data = SPI_DR((u32)periph->reg_addr); } + /* + * Check if we need to reconfigure the spi peripheral for this transaction + */ sig = get_transaction_signature(_trans); - if (sig != dma->comm_sig) { - dma->comm_sig = sig; - - // A different config is required in this transaction... + /* 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); + } + /* * Clear flag for interrupt order handling * @@ -638,55 +640,43 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) */ dma->other_dma_finished = 0; - /* Determine the length case of the transaction */ - if ((_trans->input_length > _trans->output_length) && (_trans->output_length > 0)) { - /* We are sending something, but less than receiving */ - tx_length_case = RX_LEN_LONGER_TX_NOT_0; - } else if ((_trans->input_length > _trans->output_length) && (_trans->output_length == 0)) { - /* We are not sending anything, but we are receiving */ - tx_length_case = RX_LEN_LONGER_TX_0; - } else { - /* We are sending at least as much as we receive, use output length */ - tx_length_case = TX_LEN_LONGER; - } - /* + /* 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. */ - if (tx_length_case == RX_LEN_LONGER_TX_NOT_0) { - /* - * We are sending something, but less than receiving, we need to pad the tx - * buffer with zeroes - */ + if (_trans->input_length > _trans->output_length) { + /* Receiving more than sending */ max_length = _trans->input_length; - int i; - for (i = _trans->output_length; i < _trans->input_length; i++) { - _trans->output_buf[i] = 0; + /* 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 { + /* pad the tx buffer with zeroes */ + for (int i = _trans->output_length; i < _trans->input_length; i++) { + _trans->output_buf[i] = 0; + } } - } else if (tx_length_case == RX_LEN_LONGER_TX_0) { - /* - * We are not sending anything, but we are receiving, use the dummy buf but - * make sure to run the SPI for the whole receive length - */ - max_length = _trans->input_length; } else { - /* - * We are sending at least as much as we receive, use output length - */ + /* We are sending at least as much as we receive, use output length */ max_length = _trans->output_length; } + + /* + * Receive DMA channel configuration ---------------------------------------- + */ dma_channel_reset(dma->dma, dma->rx_chan); if (_trans->input_length > 0) { - // Rx_DMA_Channel configuration ------------------------------------ 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_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); - // Set the dma transfer size based on SPI transaction DSS + + /* 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); @@ -697,26 +687,30 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) //dma_set_mode(dma->dma, dma->rx_chan, DMA_???_NORMAL); dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_VERY_HIGH); } else { - // There will be no interrupt in this case, i.e. like the interrupt already finished + /* There will be no interrupt in this case, i.e. like the interrupt already finished */ dma->other_dma_finished = 1; } + + /* + * Transmit DMA channel configuration --------------------------------------- + */ dma_channel_reset(dma->dma, dma->tx_chan); - // SPI Tx_DMA_Channel configuration ------------------------------------ dma_set_peripheral_address(dma->dma, dma->tx_chan, (u32)dma->spidr); - // Use the dummy buffer if tx length is zero - if (tx_length_case == RX_LEN_LONGER_TX_0) { + /* 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); } else { dma_set_memory_address(dma->dma, dma->tx_chan, (u32)_trans->output_buf); dma_enable_memory_increment_mode(dma->dma, dma->tx_chan); } - // Here use the max length of rx or tx instead of the actual tx length, as described above + /* 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_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 + + /* 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); @@ -728,26 +722,26 @@ static void spi_rw(struct spi_periph* periph, struct spi_transaction* _trans) dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); - // FIXME do we need to explicitly disable the half transfer interrupt? + /* + * Enable DMA --------------------------------------------------------------- + */ + /* Enable DMA transfer complete interrupts. */ if (_trans->input_length > 0) { - // Enable dma->dma rx Channel Transfer Complete interrupt dma_enable_transfer_complete_interrupt(dma->dma, dma->rx_chan); } - // Enable dma->dma tx Channel Transfer Complete interrupt 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) { - // Enable dma->dma rx channel dma_enable_channel(dma->dma, dma->rx_chan); } - // Enable dma->dma tx Channel dma_enable_channel(dma->dma, dma->tx_chan); + /* enable SPI transfers via DMA */ if (_trans->input_length > 0) { - // Enable SPI Rx request spi_enable_rx_dma((u32)periph->reg_addr); } - // Enable SPI Tx request spi_enable_tx_dma((u32)periph->reg_addr); }