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));