cancel
Showing results for 
Search instead for 
Did you mean: 

masking USART interrupt without disabling

bruzzim
Associate II
Posted on February 11, 2011 at 17:50

masking USART interrupt without disabling

7 REPLIES 7
ColdWeather
Senior
Posted on May 17, 2011 at 14:24

Hello,

 

I want to mask USART interrupt without disabling it in order not to miss a char during the time it is disabled.

 

You don't miss a char: first, the receiption works also while an interrupt is disabled, second, the UART normally needs a lot of time to receive the next character.

To solve the problem of the access to the resource ''counter'' you can use SWI to change its value because SWI cannot be interrupted. As soon as you leave SWI, pending UART interrupt can be processed again.

picguy2
Associate II
Posted on May 17, 2011 at 14:24

Why do you want to disable the Rx interrupt?  Interrupts work especially well on Cortex machines.

Okay, I looked at your code.  Is this in your ISR?  Any character that comes in during your ISR (perhaps due to a long running higher priority interrupt) will get you another interrupt as soon as you exit.  

Try it by forcing the condition.  At 9600 baud a 10-bit frame arrives every 1.0417 milliseconds.  Setup a higher priority ISR that burns the CPU  for 1.5 milliseconds every 3 milliseconds.  (50% of the time in the higher priority ISR.)  Stream simple pattern data saving it all to RAM.  You won’t miss anything.

Here is why

The USART has a shifter and USART_DR.  As soon as your frame has completely shifted in it’s copied to the data register.  The shifter becomes empty and ready to accept another byte.

I’ve posted http://www.hmtown.com/fifo.htm before.  It focuses more on why and a general purpose what rather than implementation details.  ColdWeather mentioned a way to use a single counter.  My approach uses a pair of pointers.  It works nicely even if the source and sink processes are in separate processors coupled only by RAM.
ColdWeather
Senior
Posted on May 17, 2011 at 14:24

Is this in your ISR? 

I think, the piece of code of the main poster is not an ISR (''source'') but the ''sink'' process reading from the queue (watch pQueue->Count--). Meanwhile I looked at my code from some test firmware. An easy way is to use a ''critical section'' flag:

The ''sink/reader'' process:

if ( pQueue->Count) {

  // read an item out

      ...

  // decrement the counter within the critical section

  csflag = 1; // critical section begin

  pQueue->Count--;

  csflag = 0; // critical section end

  // reenable/confirm interrupt

  USART_ITConfig(UsartAddress, USART_IT_RXNE, ENABLE);

}

ISR:

if (csflag == 0)

{

  c = USART_ReceiveData(usart);

  // Put a char ''c'' into the queue

  ...

 // increment the counter

  pQueue->Count++;

} else { // the critical section has been interrupted!

  USART_ITConfig(UsartAddress, USART_IT_RXNE, DISABLE);

  // leave the interrupt.

  // RXNE is still set because no read from DR done yet

}

The idea: if the ISR encounters that the critical section has been interrupted, it disables itself and leaves. The interrupt is still pending, a char stays in DR. As soon as the ''sink'' process leaves the critical section (csflag = 0) and reenables the interrupt, it shots again immediately, and the first part of the ISR is executed placing the char into the queue.

trevor23
Associate III
Posted on May 17, 2011 at 14:24

The concept is very common indeed - one might even say that it's the standard  way

 

Indeed. Which is why the vendor (ST) should provide an example for this. This is always one of the first things I implement on a new micro and almost everyone else does also. Lots of parallel effort that could be saved with one common, efficient and well documented example. In fairness to ST many other vendors do not provide this either. An open source example library would make sense to me, that way we all contribute to something that grows and improves over time. Won't be holding my breath for ST to embrace this tough.

Andrew Neil
Evangelist III
Posted on May 17, 2011 at 14:24

''I’ve posted http://www.hmtown.com/fifo.htm before.''

 

 

Good link!

The technigque is also often called a Ring buffer or a Circular buffer - because of the way the pointers chase around & around the buffer in a ring or circle.

The concept is very common indeed - one might even say that it's the standard  way to handle interrupt-driven serial comms!

Googling these terms will find you many references & examples.

But I think there's a mistake:

The four ''pointers'' are described as follows

First  First byte address of buffer

In     Address within buffer where next byte goes IN to the buffer

 

Out    Address within buffer where next byte comes OUT from the buffer

 

Limit  The last byte address+1 of the buffer

(note that they don't actually all need to be ''pointers'' - they could be indexes  within the buffer)

The mistake, I think, lies in the getData() function:

char getData() {

  char temp;

  if (in == out) {whatever;}

  temp = *in;  // The order here

  in+++;       // is important

  return temp;

Apart from the obvious typo with the closing brace, surely this should be using the Out  pointer - since it is taking data out  from the buffer?

bruzzim
Associate II
Posted on May 17, 2011 at 14:24

Thanks all, guys for your comments.

This is my actual code for push/pop the usart queue and the usart routine itself. I have three queues for the three usart I need. The usart ISR pushes the data into queue, main loop gets the data (pop).

Thanks and best regards.

typedef struct tagQUEUE {

// Top

u16 Top;

// Bottom

u16 Bottom;

// Count

        u16 Count;

        // queue overflow

        u16 Overflow;

        // Errors

        uint32_t ErrOverrun;

        uint32_t ErrFrame;

        uint32_t ErrParity;

        uint32_t ErrNoise;

// Buffer

u8 pBuffer[__RX_QUEUE_SIZE__];

} QUEUE;

//---------------------------------------------------------------------------

// Queue Push

//---------------------------------------------------------------------------

void QueuePush(QUEUE* pQueue,BYTE Data)

{

  if (pQueue->Count == __RX_QUEUE_SIZE__) { pQueue->Overflow++; return; }

  pQueue->pBuffer[pQueue->Top]=Data;

  ++pQueue->Top;

  if (pQueue->Top == __RX_QUEUE_SIZE__) pQueue->Top = 0;

  pQueue->Count++;

}

//---------------------------------------------------------------------------

// Queue Pop

//---------------------------------------------------------------------------

BYTE QueuePop(QUEUE* pQueue, BYTE Channel)

{

  BYTE Data;

  USART_TypeDef *UsartAddress;

  

  switch(Channel)

  {

  case 0:

        UsartAddress = USART1;

        break;  

  case 1:

        UsartAddress = USART2;

        break;  

  case 2:

        UsartAddress = USART3;

        break;  

  }

  if (pQueue->Count == 0) return(0);

  Data=pQueue->pBuffer[pQueue->Bottom];

  ++pQueue->Bottom;

  if (pQueue->Bottom == __RX_QUEUE_SIZE__) pQueue->Bottom = 0;

    USART_ITConfig(UsartAddress, USART_IT_RXNE, DISABLE); 

  pQueue->Count--;

    USART_ITConfig(UsartAddress, USART_IT_RXNE, ENABLE); 

  return(Data);

}

void USART3_IRQHandler(void) {

  uint8_t ch;

  

  if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) 

    { 

      ch = (uint8_t)USART_ReceiveData(USART3);

      QueuePush(&Queues[CHANNEL_SERIAL_3], ch);

    } 

bruzzim
Associate II
Posted on May 17, 2011 at 14:24

yes, the code reported was from the sink routine. I attached my actual, I will switch to your since It seems faster than mine.

Thanks