The setup:
SPI is driven by the DMA. DMA provides 16 bit data stream from a memory array to SPI which just outputs serial data on the MOSI pin.
The DMA is configured to generate an interrupt after it finished to send all the data to SPI.
The flow:
The DMA IRQ is correctly generated after data transmission finished, but before all 16 bits are pulsed out by SPI.
That’s why it is needed to check the SPI status before sending new data.
The problem:
In the DMA ISR, checking the TXE and BSY flags of the SPI interface using this code doesn’t wok:
while (spi_is_tx_empty(_currentSetting->spi_d) == 0); // "5. Wait until TXE=1 ..."
/* and */
while (spi_is_busy(_currentSetting->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI."
I can’t see anything obviously wrong with your code (so you may have encountered a bug)… but is true=1 is false=0 what exactly does spi_is_busy return?! Are we being fooled by comparing a boolean with an int or something daft?
uint8 spi_is_busy(spi_dev * dev)
Determine whether the device’s peripheral busy (SPI_SR_BSY) flag is set.
Parameters:
dev –
SPI device
Return:
true, iff dev’s BSY flag is set.
… appears to be a valid way to see if you are ready to fire off the next transmission. At least that is how I am reading http://docs.leaflabs.com/static.leaflab … i/spi.html
I am probably barking up the wrong tree (or perhaps not even barking in the right forest).
I also tried your suggestion, but as expected, same “bug”, it is something else there.
Btw, same code snippet works flawlessly if is executed on the main level, not within the ISR.
I also tried your suggestion, but as expected, same “bug”, it is something else there.
Btw, same code snippet works flawlessly if is executed on the main level, not within the ISR.
This is getting weird…Even disabling the SPI peripheral breaks the whole system…
Calling
spi_peripheral_disable(_currentSetting->spi_d);
/*****************************************************************************/
void SPIClass::dmaSendIrq(void)
{
digitalWrite(DEB, LOW);
//chan_regs->CCR &= (DMA_CCR_EN_BIT|DMA_CCR_TCIE)^0xFFFFFFFF;
bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0); //dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);//
bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_TCIE_BIT, 0);//dma_disable_irq(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);//
//spi_tx_dma_disable(_currentSetting->spi_d);
dmaIrqCount++;
if ( dmaIrqCount>1 ) {
digitalWrite(DEB, HIGH);
digitalWrite(DEB, LOW);
spi_peripheral_disable(_currentSetting->spi_d); // here we have the freeze, the CPU will not execute the following lines...
digitalWrite(DEB, HIGH);
digitalWrite(DEB, LOW);
}
digitalWrite(DEB, HIGH);
while ( !spi_is_tx_empty(_currentSetting->spi_d) ); // "5. Wait until TXE=1 ..."
digitalWrite(DEB, LOW);
while ( spi_is_busy(_currentSetting->spi_d) ); // "... and then wait until BSY=0 before disabling the SPI."
digitalWrite(DEB, HIGH);
//uint16 x = spi_rx_reg(_currentSetting->spi_d); // dummy read, needed, don't remove!
uint8 tail = bufTail;
// check if there is buffer available to send
if ( tail!=bufHead ) { // data to be written available?
// write window address
writeWinAddr( dmaBuffer[tail].winAddr );
// update length and memory to write from
chan_regs->CMAR = (uint32)(&dmaBuffer[tail].color);//dma_set_mem_addr(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &dmaBuffer[bufTail].color);//
chan_regs->CNDTR = dmaBuffer[tail].length;//dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, dmaBuffer[bufTail].length);//
bufTail = (tail+1) & BUFF_SIZE_MASK;
dmaSending = 1;
writeCSActive(); // activate CS
}
else
{ //nothing to send
dmaSending = 0;
writeCSIdle(); // release chip select
}
digitalWrite(DEB, LOW);
/**/
dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
if (dmaSending) {
spi_tx_dma_enable(_currentSetting->spi_d);
bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_TCIE_BIT, 1); // enable irq//dma_enable_irq(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);//
bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1);//dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); // enable transmit
}
digitalWrite(DEB, HIGH);
}
Do you have an Blue Pill you could turn into a BlackMagic Probe, as If the code has had an exception, its fairly easy to find out the what has caused the exception (which line of code) using GDB.
The code line causing the trouble is actually known. In fact, any SPI register access seems to cause this effect.
It must have to do with accessing within ISR, but I really cannot figure out what exactly can disturb so heavily…
To avoid any of those effects, it is recommended to respect the following procedure when disabling the SPI:
http://stm32info.com/en/disabling-the-spi/
set up a timer (with a silly limit) to force it out, just for debug only
hangs are so indeterminate
srp
Thanks, I am well aware of those information, but none of them discusses the system hangup by SPI register accesses within DMA ISR! And exactly this is what happens in my case. I cannot get the result of checking BSY flag, the system suddenly hangs…
@zmemw16
I cannot image why should the DMA trigger 2 interrupts at the same time.
But, anyway, even if it would so, why freezes the system when I access any SPI register within the ISR?
What kind of severe exception could occur?
If I manage to debug with STLink, how exactly can I detect the exception cause?
3.3: Interrupts not handled by libmaple (isrs.S)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Though libmaple does provide some IRQ handlers, it doesn’t define one
for every available interrupt. This is true for various reasons: maybe
the peripheral or interrupt isn’t supported yet, maybe there’s no
useful default behavior, etc.
In this case, it would be wasteful to have a separate function for
each unhandled interrupt. To handle this, there’s a single file that
deals with all unhandled interrupts. Its name is isrs.S, and it lives
in the same directory as the corresponding vector_table.S. For
example, for Maple, the file is libmaple/stm32f1/performance/isrs.S.
These aren’t complicated; read the source to see how they work.
4. Adding your own interrupt handlers
————————————-
When adding an interrupt handler (or overriding a default one), you
need to decide whether you want it for a particular program, or if
what you’re writing is general-purpose enough that it should live in
libmaple itself.
…
What kind of severe exception could occur?
In the error case, I used the following line to attach the interrupt for DMA:
dma_attach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, (void(*)())(&SPIClass::dmaSendIrq));
I came across the same problem myself ( but I can remember what code I was tryng to write)
From what I remember, its not possible to assign a pointer to a function to call a function inside a class.
It may be possible to use a static ( global ) callback function inside the SPI class, but it would be common to all instances of the class. ( but you would need to double check this)
There are probably other work-arounds to this, as its a well known problem. But I dont think there is an ideal solution.