cancel
Showing results for 
Search instead for 
Did you mean: 

How to manage stack access by Interrupts (STM32L100) ?

jogerh
Associate II
Posted on May 20, 2015 at 22:51

Dear community,

I want to send some data via UART directly to DMA without interruption. To solve this problem I use a custom FIFO stack from which the DMA feeds itself using its DMA interrupt-routine.

The stack is filled with data by the UART interrupt routine (if data arrives via uart). Unfortunately sometimes both interrupts DMA and UART occur simultaneously which means one interrupt routine has not finished its stack operation while the other routine starts a new one.

I was thinking to define a kind of USER – INTERRUPT of higher priority than UART and DMA to execute all stack operations if requested.  Does anybody know how to implement an user interrupt, which can be fired from user code ?

6 REPLIES 6
jpeacock
Associate II
Posted on May 20, 2015 at 22:57

Look at the HT half transfer flag in DMA.  That allows you to use a ''Ping-Pong'' buffer where one half is receiving and the other half sending via DMA.  If your data rates aren't synchronized then you need some buffer management.

  Jack Peacock

jogerh
Associate II
Posted on May 21, 2015 at 22:48

Thank you very much for responding.

Maybe I should add some code for better understanding…

bool Push(struct DAC_Stack * S, char* loc_data) {
if(S->size < 
MAX_STACK_SIZE
) {
memcpy(S->data + (S->size*BLOCK_SIZE), loc_data, BLOCK_SIZE);
S->size ++;
return true;
} else return false;
}
bool Pull(struct DAC_Stack * S, char* loc_data) {
if(S->size > 0) {
memcpy(loc_data, S->data, BLOCK_SIZE);
memcpy(S->data,S->data + BLOCK_SIZE, BLOCK_SIZE * (S->size - 1) );
S->size--;
return true;
} else return false; 
} 
void DMA_Manager() {
Pull(&newStack,DMA_buffer + DMA_offset);
}; 
void DMA1_Channel2_IRQHandler() {
if(DMA_GetFlagStatus(DMA1_IT_TC2) != RESET) {
DMA_offset = BLOCK_SIZE;
}
if(DMA_GetFlagStatus(DMA1_IT_HT2) != RESET) {
DMA_offset = 0;
}
DMA_Manager();
DMA_ClearITPendingBit(DMA1_IT_GL2); 
}
void USART3_IRQHandler (void) { 
if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {
//Do some other stuff like collecting data in LoadBuffer
//If LoadBuffer is full then push it into the stack
if(!Push(&newStack, LoadBuffer)) GPIOC->ODR ^= GPIO_Pin_9;
}
}; 

DMA has higher priority than USART which means while PUSHing data to the STACK the DMA interrupt might request the PULLout of data from the bottom of the stack (FIFO) which causes usually STACK chaos... What might be the best solution (if I want to stick on a STACK) OPTION A: Using an Eventhandler which is triggered by an interrupt of even higher ordere.g. TIM. OPTION B: Adding some progressinformation to the stack to check where the stack operation was interrupted to allow for e.g. repairing the stack. OPTION C: Please add some text here.
jpeacock
Associate II
Posted on May 22, 2015 at 14:04

That's what's known as a ''priority inversion'', where a low priority task blocks a higher priority.  If you model this as two tasks, UART and DMA, instead of using FIFO pointers the UART task sends a message that a block is ready to the message queue for the DMA task.  When a DMA transfer ends the DMA task removes the next message from the message queue, or blocks until a message arrives.  The presence of a message in the queue guarantees there is sufficient data in the FIFO to start a DMA transfer and that the removal pointers in the FIFO can safely be updated independently of the UART insertion pointers.

This is a common design pattern that's easily handled by an RTOS.  You see this frequently in network stacks where large numbers of outgoing messages from multiple tasks have to be merged into a single output stream over the net interface.

  Jack Peacock
jogerh
Associate II
Posted on May 28, 2015 at 15:55

Thank you very much for replying.

If I understand your recommendation correctly then I need to add a Message Queue (e.g. another FIFO Stack) which is handled by the interrupt of higher priority.  

I was already thinking about such an approach however I also doubt that much more memcpy operations executed by the DMA Handler would slow down the system significantly or might cause gaps in the DAC output feed by the DMA.

Another option would be to skip Interrupts and use a state machine instead which rely on polling  -  maybe this is what you have suggested in your reply. I would be very happy if you or someone else would add a suitable link to some source-code which might solve the given problem. In the mean time I have added some control flags which allow for stack repair if a stack operation was interrupted by another interrupt.

Posted on May 28, 2015 at 19:16

I think you need to revisit how to implement a FIFO (ring buffer). There is no good reason to move the content around in the buffer.

Your ping-pong implementation also looks a bit dubious.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jogerh
Associate II
Posted on May 29, 2015 at 19:03

Thank you for replying.

I was also thinking to use a ring buffer

e.g.  

//USART Interrupt

USART_Buffer[USART_Address % MAX_USART_ADRESS] = received data;

USART_address++;

//DMA_Interupt

memcpy(DMA_Buffer + (DMA_Address % MAX_DMA_ADRESS), USART_Buffer +(USART_Adress % MAX_USART_ADRESS), HALFSIZE);

DMA_Address += HALFSIZE; //of DMA_Buffer

which only works, if  DMA_adress < USART_adress. However this would mean incrementing the addresses infinitely. If one of both the addresses reaches 8589934592+1 then it will start again with zero which than results in  DMA_adress > USART_adress which might causes a problem.

I didn’t found a suitable solution how to avoid the 8589934592+1 problem which resulted in the FIFO implementation as shown above.