Here is a scenario I'm facing right now, I have an interrupt(thread) UART that is reading to a ringbuffer from the values that I get from the serial port, and also writing the values from the serial port to the ring buffer. I have a main loop that access that ringbuffer for reading the values from it, while writing an AT Command, and also writing to the ring buffer those AT Commands. Do I need the ringbuffer to be lock free or surround the shared data with a semaphore or a mutex ? I don't have an OS for getting a mutex or semaphore working. I have read alot about the subject and it seems I need a lock free ringbuffer. On ARM I would use a compare and swap instruction. The ringbuffer is implemented as an array so I wouldn't run into ABA problem
Declaration of buffers:
#define MAX_CHANNEL_COUNT 5
#define UART_BUFSIZE 512
char buffers[2][MAX_CHANNEL_COUNT][UART_BUFSIZE];
char* writeBuffers[MAX_CHANNEL_COUNT];
char* readBuffers[MAX_CHANNEL_COUNT];
volatile int readPos[MAX_CHANNEL_COUNT] = { 0 };
volatile int writePos[MAX_CHANNEL_COUNT] = { 0 };
here is the interrupt code
void USART_IRQHandler(char Channel, USART_TypeDef *USARTx)
{
volatile unsigned int IIR;
int c = 0;
IIR = USARTx->SR;
if (IIR & USART_FLAG_RXNE)
{ // read interrupt
USARTx->SR &= ~USART_FLAG_RXNE; // clear interrupt
c = USART_ReceiveData(USARTx);
writeBuffers[Channel][writePos[Channel]] = c;
writePos[Channel]++;
if(writePos[Channel]>=UART_BUFSIZE) writePos[Channel]=0;
}
if (IIR & USART_FLAG_TXE)
{
USARTx->SR &= ~USART_FLAG_TXE; // clear interrupt
}
}
code for initializing and swapping the buffers:
void initializeBuffers(void) {
int i = 0;
for (i = 0; i < MAX_CHANNEL_COUNT; ++i)
{
writeBuffers[i] = buffers[0][i];
readBuffers[i] = buffers[1][i];
}
}
void swapBuffers(int channel) {
int i;
char * buf = writeBuffers[channel];
__disable_irq();
writeBuffers[channel] = readBuffers[channel];
readBuffers[channel] = buf;
if ( readPos[channel] == UART_BUFSIZE)
readPos[channel] = 0;
for (i =0; i < UART_BUFSIZE; i++)
{
buf[i] = 0;
}
__enable_irq();
}
here I use this function to get a char from a specific channel and from a specific UART
int GetCharUART (char Channel)
{
int c = readBuffers[Channel][readPos[Channel]++];
if (c == 0 || readPos[Channel] == UART_BUFSIZE)
{
swapBuffers(Channel); // Make this clear your read buffer.
return EMPTY;
}
return c; // Note, your code that calls this should handle the case where c == 0
}
usage of GetCharUart
PutStringUART(UART_GSM, "AT");
PutStringUART(UART_GSM, pCommand);
PutCharUART(UART_GSM, '\r');
count = 0;
timer_100ms = 0;
while (timer_100ms <= timeout)
{
zeichen = GetCharUART(UART_GSM);
}
You need synchronization between interrupt handlers and the "main-thread", so yes a sort of mutex is needed. The usual method without OS, is to just disable interrupts before "entering the critical section" and re-enable afterwards. That ensures the critical section is "atomic" (i.e. no interrupts fire in the meantime).
On ARM I would use a compare and swap instruction
What compiler are you using?
GCC and Clang has intrinsics for atomic compare-and-swap.
See e.g.
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
https://llvm.org/docs/Atomics.html
C11 supports it via <stdatomic.h>
I believe: https://en.cppreference.com/w/c/atomic/atomic_compare_exchange