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 8dcdcb8f96..589699bda3 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 f06a93fcea..eecffbceaf 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/arch/stm32/mcu_periph/spi_arch.c b/sw/airborne/arch/stm32/mcu_periph/spi_arch.c index 7fc9f1e4d8..5edb1152e2 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 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 - * - * 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. * - * 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. + * 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,10 +62,6 @@ #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 ); - /** * Libopencm3 specifc communication parameters for a SPI peripheral in master mode. */ @@ -88,10 +85,12 @@ 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 + 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; ///< 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 }; @@ -108,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 @@ -129,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; @@ -139,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) { @@ -250,95 +452,191 @@ static void set_comm_from_transaction(struct locm3_spi_comm* c, struct spi_trans } } -static inline void SpiSlaveUnselect(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_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; + 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); } - -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; - } +/// Enable DMA channel interrupts +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 ); + // 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 ); } -/// Enable DMA rx channel interrupt -// 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 ); - } -} - -/// Disable DMA rx channel interrupt -static void spi_arch_int_disable( struct spi_periph *spi ) { +/// 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 ); } +/// 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) { @@ -349,8 +647,10 @@ 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_extra_dummy_dma = FALSE; + spi1_dma.rx_dummy_buf = 0; + spi1_dma.rx_extra_dummy_dma = FALSE; // set the default configuration set_default_comm_config(&spi1_dma.comm); @@ -364,16 +664,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 -------------------------------- + // 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); @@ -385,7 +685,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. * @@ -397,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. @@ -417,8 +718,10 @@ 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_extra_dummy_dma = FALSE; + spi2_dma.rx_dummy_buf = 0; + spi2_dma.rx_extra_dummy_dma = FALSE; // set the default configuration set_default_comm_config(&spi2_dma.comm); @@ -432,16 +735,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 -------------------------------- + // 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); @@ -453,12 +756,11 @@ 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. - * - * 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. @@ -466,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. @@ -486,8 +788,10 @@ 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_extra_dummy_dma = FALSE; + spi3_dma.rx_dummy_buf = 0; + spi3_dma.rx_extra_dummy_dma = FALSE; // set the default configuration set_default_comm_config(&spi3_dma.comm); @@ -501,17 +805,19 @@ 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); + 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); + /// @todo disable JTAG so the pins can be used? + // reset SPI spi_reset(SPI3); @@ -522,12 +828,11 @@ 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. - * - * 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. @@ -535,305 +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 ); + 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; - uint8_t max_length = 0; - bool_t use_dummy_tx_buf = FALSE; - - // 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 - */ - 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); - } - - /* - * 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; - /* 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 (_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 { - /* pad the tx buffer with zeroes */ - for (int i = _trans->output_length; i < _trans->input_length; i++) { - _trans->output_buf[i] = 0; - } - } - } else { - /* 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) { - 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 */ - 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); - } else { - /* 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); - 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); - } 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_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) { - 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_mode(dma->dma, dma->tx_chan, DMA_???_NORMAL); - dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM); - - - /* - * 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->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); - } - 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); - } - 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; -} - -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; -} - +/****************************************************************************** + * + * DMA Interrupt service routines + * + *****************************************************************************/ #ifdef USE_SPI1 /// receive transferred over DMA void dma1_channel2_isr(void) @@ -908,41 +932,60 @@ 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 + + if (dma->rx_extra_dummy_dma) { + /* + * 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 */ + dma->rx_extra_dummy_dma = FALSE; + + /* Use the difference in length between rx and tx */ + 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 */ + dma_enable_channel(dma->dma, dma->rx_chan); + /* Enable SPI transfers via DMA */ + spi_enable_rx_dma((u32)periph->reg_addr); + } + else { + /* + * 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) { 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 - 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]); - } else { - // if this is not the last part of the transaction, set finished flag - dma->other_dma_finished = 1; + spi_next_transaction(periph); } } @@ -951,41 +994,40 @@ 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 - trans->status = SPITransSuccess; - if (trans->after_cb != 0) { - trans->after_cb(trans); - } + 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. + */ - // AFTER the callback, then unselect the slave if required - if (trans->select == SPISelectUnselect || trans->select == SPIUnselect) { - SpiSlaveUnselect(trans->slave_idx); - } + /* Reset the flag so this only happens once in a transaction */ + dma->tx_extra_dummy_dma = FALSE; - // increment the transaction to handle - periph->trans_extract_idx++; + /* 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); + 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); - // 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 this is not the last part of the transaction, set finished flag - dma->other_dma_finished = 1; } } diff --git a/sw/airborne/lisa/test/lisa_test_adxl345_dma.c b/sw/airborne/lisa/test/lisa_test_adxl345_dma.c index fbf46d2397..ff2ce230e8 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; } @@ -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/mcu_periph/spi.h b/sw/airborne/mcu_periph/spi.h index 294e349293..50c17d2010 100644 --- a/sw/airborne/mcu_periph/spi.h +++ b/sw/airborne/mcu_periph/spi.h @@ -135,12 +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 - * 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). + * 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/adxl345.h b/sw/airborne/peripherals/adxl345.h index 0d11d5e23f..cfedad8bd8 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 fea916fab3..be3dcca5a7 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 762550b336..9f488eb67f 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)); @@ -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; @@ -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)); diff --git a/sw/airborne/peripherals/max1168.c b/sw/airborne/peripherals/max1168.c index 4914ac331e..1a560345b0 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; } diff --git a/sw/airborne/subsystems/imu/imu_b2.c b/sw/airborne/subsystems/imu/imu_b2.c index 2c573f97d8..37dd29b624 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 7d543d57b4..332cbba003 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));