I am using SPI DMA to read 18 bytes from an external IC. The IC generates a negative edge interrupt that is connected to a port IRQ. In the PORT IRQ handler, I assert Chip Select ( CS ) low and then enable the DMA. In the DMA handler, I de-assert the CS high and then clear flags and copy the received data. The main symptom is that SPI DMA gets triggered only once successfully... and then when the next port IRQ interrupt arrives, SPI-DMA does not get started: no SPI transactions occur. I know that the PORT IRQ was executed (because the CS line goes low), however the DMA did not start the SPI transactions. While debugging, I noticed that the UCTXIFG bit in the UCBxIFG register is 1 when the SPI successfully completes and UCTXIFG is set to 0, when the SPI DMA fails to start. I looked at UCTXIFG after the execution of the following lines of code in the PORT IRQ handler MAP_DMA_enableChannel(DMA_CHANNEL_1); MAP_DMA_enableChannel(DMA_CHANNEL_0); So it looks like the DMA did write to the eUSCI_B peripheral's TUCxxTXBUF, but for some reason the SPI peripheral did not start the transmission of the 18 bytes. Also after reading the TRM, I know that the DMA_INT0 is a master interrupt and is different from the DMA_INT1/2/3 interrupts. The problem is that the DMA_INT1/2/3 are mapped to other channels and I cannot use them, so I am left with DMA_INT0. I also observed that when the SPI DMA fails and the DMA_INT0_IRQHandler gets called, then the value of INT0_SRCFLG is zeros. This is puzzling: how could the DMA_INT0_IRQHandler be called and yet have all the bits in the source flag be reset to 0 ? I would think that at least 1 channel caused the interrupt handler to be called. Another observation regarding DMA_INT0 is that if I do not disable the DMA_INT0 interrupt in the DMA_INT0_IRQHandler then the IRQ gets called again *even if I clear the DMA interrupt flags* . This reminds me of the DMA12 bug in the Rev B. silicon. But I think that was fixed in the production Rev C. silicon. Also there is no way to assign DMA_INT0 to any specific trigger like a SPI RX or TX channel... basically it is illegal to do this MAP_DMA_assignInterrupt(DMA_INT0, DMA_CHANNEL_1) because DMA_INT0 maps to all completion sources except those mapped to DMA_INT1/2/3 Ideally I would just want to map a SPI recieve (RX) DMA completion to DMA_INT0, but I guess that is not allowed. (1) any suggestions to why the SPI DMA freezes (2) Why am I seeing spurious interrupts on DMA_INT0 where none of the source bits are set? here is a screen shot showing that the first DMA runs successfully , but the second one fails. SPI CLK = 6MHz SMCLK = 12MHz, Clock is set to DCO = 12MHz Here are the relevant code pieces :DMA configuration routine and Port and DMA IRQ handlers const eUSCI_SPI_MasterConfig spiMasterConfig = { EUSCI_B_SPI_CLOCKSOURCE_SMCLK, // SMCLK Clock Source 6000000, // SMCLK = DCO/2 = 12MHz/2 = 6MHz 6000000, // SPICLK = 6MHz EUSCI_B_SPI_MSB_FIRST, // MSB First EUSCI_B_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT, // Phase EUSCI_B_SPI_CLOCKPOLARITY_INACTIVITY_HIGH, // High polarity EUSCI_B_SPI_3PIN // 3Wire SPI Mode }; /* DMA Control Table */ #pragma DATA_ALIGN(1024) static DMA_ControlTable controlTableInertialSensor[32]; ---------------------------------------------------------------------------------------------------------------------------------------------- void Sensor::ConfigureDMA(void) { /* Configuring DMA module */ MAP_DMA_enableModule(); MAP_DMA_setControlBase(controlTableInertialSensor); /* Assign DMA channel 0 to EUSCI_B0_TX0, channel 1 to EUSCI_B0_RX0 */ MAP_DMA_assignChannel(DMA_CH0_EUSCIB0TX0); MAP_DMA_assignChannel(DMA_CH1_EUSCIB0RX0); /* Setup the TX transfer characteristics & buffers */ MAP_DMA_setChannelControl(DMA_CH0_EUSCIB0TX0 | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1); MAP_DMA_setChannelTransfer(DMA_CH0_EUSCIB0TX0 | UDMA_PRI_SELECT, UDMA_MODE_BASIC, mTXData, (void *) MAP_SPI_getTransmitBufferAddressForDMA(EUSCI_B0_BASE), INERTIAL_NUM_BYTES_TO_READ_PER_SAMPLE); /* Setup the RX transfer characteristics & buffers */ MAP_DMA_setChannelControl(DMA_CH1_EUSCIB0RX0 | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1); MAP_DMA_setChannelTransfer(DMA_CH1_EUSCIB0RX0 | UDMA_PRI_SELECT, UDMA_MODE_BASIC, (void *) MAP_SPI_getReceiveBufferAddressForDMA(EUSCI_B0_BASE), mRXData, INERTIAL_NUM_BYTES_TO_READ_PER_SAMPLE); // Enable DMA interrupt and clear any previously set completion flags MAP_DMA_enableInterrupt(INT_DMA_INT0); MAP_Interrupt_enableInterrupt(INT_DMA_INT0); MAP_DMA_clearInterruptFlag(DMA_CH0_EUSCIB0TX0); MAP_DMA_clearInterruptFlag(DMA_CH1_EUSCIB0RX0); } ---------------------------------------------------------------------------------------------------------------------------------------------- void InertialSensor::PORT2_IRQHandler(void) { /* * disable the interrupt on the I2C DRDY port * DMA operation on SPI and I2C should be atomic. * Disable the other interrupts so that the current operation * completes atomically before starting the other peripheral */ MAP_Interrupt_disableInterrupt(INT_PORT3); uint32_t status; status = MAP_GPIO_getEnabledInterruptStatus(GPIO_PORT_P2); MAP_GPIO_clearInterruptFlag(GPIO_PORT_P2, 0xFF); /* * LSM6DS3 is signaling data is ready. * Trigger the SPI read operation using DMA */ // reset the DMA transfer complete flag mDMAXferComplete = false; /* first assert the Chip Select (CS) to signal the start of the SPI transmission */ MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN4); // Enabling DMA interrupts MAP_DMA_enableInterrupt(INT_DMA_INT0); MAP_Interrupt_enableInterrupt(INT_DMA_INT0); //enable DMA RX and TX channel MAP_DMA_enableChannel(DMA_CHANNEL_1); MAP_DMA_enableChannel(DMA_CHANNEL_0); /* * at this point DMA will take over and complete reception of rest of the data from the * inertial sensor */ } ---------------------------------------------------------------------------------------------------------------------------------------------- void InertialSensor::DMA_INT0_IRQHandler(void) { uint32_t int0_srcflg_dma = DMA_Channel->INT0_SRCFLG; if ( !(DMA_Channel->INT0_SRCFLG & (DMA_INT0_SRCFLG_CH0 | DMA_INT0_SRCFLG_CH1)) ) { return; } // clear all the interrupt flag bits in the "DMA_INT0_SRCFLG" register // that correspond to the DMA channels 0 and 1 MAP_DMA_clearInterruptFlag(DMA_CH0_EUSCIB0TX0); MAP_DMA_clearInterruptFlag(DMA_CH1_EUSCIB0RX0); // Disable the DMA interrupts MAP_Interrupt_disableInterrupt(INT_DMA_INT0); MAP_DMA_disableInterrupt(INT_DMA_INT0); } ---------------------------------------------------------------------------------------------------------------------------------------------- Details of tool chain: MSPWARE is version 3.50.0.2 Driverlib is version 3_21_00_05 Code Composer Studio Version: 6.1.3.00033 Compiler Version is V15.12.3 LTS ----------------------------------------------------------------------------------------------------------------------------------------------
↧