DMA SPI-Receive during ISR Event

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
User13410
Level 1
Level 1
Hi,

I am using XMC4500 Relax Kit to readout 128 Byte of ADC data at 1 kHz and save to an SDHC-Card. ADC-Units are 8x Texas Instruments AD8568 with each 8 channels, in total 64 channels. The are read by the XMC device by Quad SPI interface.

To get the 1 kHz sampling rate I set up a Timer-App interrupting every 1000 us, calling a function to perform the ADC readout via SPI-Master-APP. Data is stored to the SD-Card using FATfs and the 4-Bit SD interface.

The issue is as follows:

When I place the SPI_Receive command inside the main loop and let it wait for a Timer-App triggered flag to be executed, data readout works fine. A problem occurs when I want to save the data to the SD-Card.
The accessing and internal housekeeping delay of the card blocks the main loop for more than 10 ms sometimes. The result is that I miss some trigger events of the Timer-App and therefore do not sample meanwhile. As I the SD block size is
512 bytes I put the 128 ADC bytes + 4 bytes timestamp into such a 512 byte array and write it down in one move.

When I place the SPI_Receive inside the Timer's callback function, it does not receive any data anymore. Code is below. I now write the data into a 8kb ring buffer which is not the problem as the variable ADC_data remains empty after SPI_Receive.
Data is written to the SD-Card, but all values are 0 (as expected when no SPI data is written, bc AD_data is initialized as global variable and initially is all-zero).
I tried to change the interrupt preemption priorities of the mentioned components as follows:

SDMMC_BLOCK_0 ->63 (can be interrupted by all other events)
INTERRUPT_2 (related to AD_Timer @ 1000 us) -> 62 (can not be interrupted by SD-write, SD-write is inside the main waiting for enough data in the ring buffer)
GLOBAL_DMA_0 (related to SPI_MASTER_0) -> 61 (can not be interrupted by INTERRUPT_2, because INTERRUPT_2 calls it)

Please, if anyone can give me an advice how I can setup the SPI_Receive command inside the ISR of the TIMER_APP.




Here is the code:


The function called by the ADTimer:

void WaitForCapture(void){
/* ISR to perform single ADC capture and write the resulting
* 132 bytes into the ring buffer */
DIGITAL_IO_SetOutputHigh(&pin_conversion); // Start conversion
capture_events++; // inc sample number
while(DIGITAL_IO_GetInput(&pin_busy)==1); // wait until capture complete
int i=0;
for (i=0; i<8; i++){} // wait a little longer
SPI_MASTER_EnableSlaveSelectSignal(&SPI_MASTER_0, SPI_MASTER_0.config->slave_select_pin_config[0]->slave_select_ch); // set up the SPI interface
SPI_MASTER_ClearFlag(&SPI_MASTER_0,XMC_SPI_CH_STATUS_FLAG_ALTERNATIVE_RECEIVE_INDICATION); // of the ADC, clear the
SPI_MASTER_ClearFlag(&SPI_MASTER_0,XMC_SPI_CH_STATUS_FLAG_RECEIVE_INDICATION); // receive-ackn.-flag
SPI_MASTER_0.runtime->spi_master_mode = XMC_SPI_CH_MODE_QUAD; // Quad-mode
SPI_MASTER_Receive(&SPI_MASTER_0, *ADU_data, 128); // read 128 bytes
while(SPI_MASTER_0.runtime->rx_busy); // wait until finished
SPI_MASTER_DisableSlaveSelectSignal(&SPI_MASTER_0); // pull up CS line
DIGITAL_IO_SetOutputLow(&pin_conversion); // reset conv pin
ADU_data[128] = (uint8_t)((capture_events & 0xFF000000) >> 24); // add time stamp
ADU_data[129] = (uint8_t)((capture_events & 0x00FF0000) >> 16);
ADU_data[130] = (uint8_t)((capture_events & 0x0000FF00) >> 8);
ADU_data[131] = (uint8_t)(capture_events & 0x000000FF);
for (i=0; i<132; i++){ // put data into buffer
circBufPut(&ringbuffer,ADU_data);
}
TIMER_ClearEvent(&ADTimer); // clear event to re-arm timer
//DIGITAL_IO_ToggleOutput(&LED2);
}


The part of the main loop where the data is written:

TIMER_Start(&ADTimer);
while (capture_events < n_samples){
l=ADU_data[35]; // check existence of data in the ADU_data variable
if (ringbuffer.head < ringbuffer.tail){
n = (ringbuffer.maxLen + ringbuffer.head) - ringbuffer.tail;
}
else{
n = ringbuffer.head - ringbuffer.tail;
}
if (n>512){
for (k=0; k<512; k++){
circBufGet(&ringbuffer,&SD_data);
}
res = f_write(&fil2, SD_data, (sizeof SD_data), &bw);
}
}
TIMER_Stop(&ADTimer);
DIGITAL_IO_SetOutputLow(&LED1);
RELAY_Reset(0);
res = f_close(&fil2);


Thank you for your help in advance. Kind regards,

VN
0 Likes
1 Reply
User13410
Level 1
Level 1
I now got it to work as intended 🙂

The SPI transfer hanged at the point "while(SPI_MASTER_0.runtime->rx_busy);". The program had waited there for the SPI_Receive to complete, which was obviously not acknowledged correctly.

The solution was to set GLOBAL_DMA_0 Interrupt Priority to a lower value than INTERRUPT_2 priority and to place that "while(SPI_MASTER_0.runtime->rx_busy);" inside the Timer-Apps callback function.

Now I am able to sample accurately in ms intervals via ISR and save the data in the ring buffer. Ring buffer data is written in chunks of 512 byte inside the main loop when there is enough data.


Below the code for other newbies like me struggling to get similar stuff to work 😉


//____________________________________________________________
// Capture and get ADC data
void ADU_Capture(uint8_t *ADU_data_p){
while(DIGITAL_IO_GetInput(&pin_busy)==1);
int i=0;
for (i=0; i<8; i++);
SPI_MASTER_EnableSlaveSelectSignal(&SPI_MASTER_0, SPI_MASTER_0.config->slave_select_pin_config[0]->slave_select_ch);
SPI_MASTER_ClearFlag(&SPI_MASTER_0,XMC_SPI_CH_STATUS_FLAG_ALTERNATIVE_RECEIVE_INDICATION);
SPI_MASTER_ClearFlag(&SPI_MASTER_0,XMC_SPI_CH_STATUS_FLAG_RECEIVE_INDICATION);
SPI_MASTER_0.runtime->spi_master_mode = XMC_SPI_CH_MODE_QUAD;
SPI_MASTER_Receive(&SPI_MASTER_0, ADU_data_p, 128);
}
//============================================================

//____________________________________________________________
// ISR to perform single ADC capture and write the resulting 132 bytes into the ring buffer
void WaitForCapture(void){
DIGITAL_IO_SetOutputHigh(&pin_conversion); // Start conversion
capture_events++; // inc sample number
uint8_t k;
ADU_Capture(&ADU_data[0]);
while(SPI_MASTER_0.runtime->rx_busy);
SPI_MASTER_DisableSlaveSelectSignal(&SPI_MASTER_0);
DIGITAL_IO_SetOutputLow(&pin_conversion);
ADU_data[128] = (uint8_t)((capture_events & 0xFF000000) >> 24); // add time stamp
ADU_data[129] = (uint8_t)((capture_events & 0x00FF0000) >> 16);
ADU_data[130] = (uint8_t)((capture_events & 0x0000FF00) >> 8);
ADU_data[131] = (uint8_t)(capture_events & 0x000000FF);
for (k=0; k<132; k++){ // put data into buffer
circBufPut(&ringbuffer,ADU_data);
}
TIMER_ClearEvent(&ADTimer); // clear event to re-arm timer
}
//============================================================


inside the main:


		// Start recording
TIMER_Start(&ADTimer);
while (capture_events < n_samples){
n_elements = ringbuffer.elements;
if (n_elements > 512){
for (k=0; k<512; k++){
circBufGet(&ringbuffer,&SD_data);
}
res = f_write(&fil2, &SD_data[0], 512, &bw);
// DIGITAL_IO_ToggleOutput(&LED2);
}

}
TIMER_Stop(&ADTimer);

0 Likes