В моём проекте не используется ОС, но, думаю, что актуальнее всего будет задать вопрос именно в этом форуме.
Есть драйвер (SPI), который использует кольцевую очередь.
У очереди есть "методы" Write и Read
Код
void QUEUE_Write(Queue *_queue, uint8 _data)
{
if(_queue->Count < _queue->Size) // If buffer not full
{
_queue->Buff[_queue->WriteIndex++] = _data; // Write next byte to buffer
_queue->WriteIndex &= _queue->Size - 1; // If buffer ends, jump to begin
_queue->Count++;
}
else OverflowFunc(); // Call overflow error handler
}
uint8 QUEUE_Read(Queue *_queue)
{
uint8 data;
if(_queue->Count) // If queue has a data
{
data = _queue->Buff[_queue->ReadIndex++]; // Read next byte from buffer
_queue->ReadIndex &= _queue->Size - 1; // If buffer ends, jump to begin
_queue->Count--;
return data;
}
else
{
UnderflowFunc(); // Call underflow error handler
return 0;
}
}
{
if(_queue->Count < _queue->Size) // If buffer not full
{
_queue->Buff[_queue->WriteIndex++] = _data; // Write next byte to buffer
_queue->WriteIndex &= _queue->Size - 1; // If buffer ends, jump to begin
_queue->Count++;
}
else OverflowFunc(); // Call overflow error handler
}
uint8 QUEUE_Read(Queue *_queue)
{
uint8 data;
if(_queue->Count) // If queue has a data
{
data = _queue->Buff[_queue->ReadIndex++]; // Read next byte from buffer
_queue->ReadIndex &= _queue->Size - 1; // If buffer ends, jump to begin
_queue->Count--;
return data;
}
else
{
UnderflowFunc(); // Call underflow error handler
return 0;
}
}
в драйвере SPI метод Write вызывается из основного потока программы:
Код
void SPI_WriteByte(uint8 _val)
{
while(TxQueue.Count == TxQueue.Size) asm("nop"); // Если в очереди нет места, ждём пока освободится
QUEUE_Write(&TxQueue, _val); // Записываем байт в очередь
}
{
while(TxQueue.Count == TxQueue.Size) asm("nop"); // Если в очереди нет места, ждём пока освободится
QUEUE_Write(&TxQueue, _val); // Записываем байт в очередь
}
а метод Read вызывается из прерывания:
Код
#pragma vector = SPI_TXE_vector
__interrupt void SPI_Isr(void)
{
...
if(TxQueue.Count) // Если в очереди что-то есть
{
SPI_DR = QUEUE_Read(&TxQueue); // записываем байт из очереди в регистр передатчика
}
...
}
__interrupt void SPI_Isr(void)
{
...
if(TxQueue.Count) // Если в очереди что-то есть
{
SPI_DR = QUEUE_Read(&TxQueue); // записываем байт из очереди в регистр передатчика
}
...
}
и всё работает хорошо при не очень интенсивном обмене данными, а если данных много, бывает, что прерывание по передаче возникает в момент, когда идёт выполнение функции Write, т.е. QUEUE_Read вызывается во время выполнения QUEUE_Write, данные в структуре очереди портятся и наступает жвах!
В данный момент приходится запрещать прерывания от SPI в начале функции SPI_Write и заного разрешать их в конце - но такой подход мне не очень нравится, т.к.
1) Нужно помнить и следить за всеми вызовами функций очереди и обрамлять их в подобные критическии секции, очень велик риск забыть и получить труднообнаруживаемый глюк при эксплуатации.
2) Много времени система проводит при запрещённых прерываниях, т.е. увеличивается время реакции на прерывание -> велик риск пропустить входящий байт (если SPI в слейве).
Может быть есть способ красиво разрулить эту ситуацию, внеся какие-то изменения в функции QUEUE_Read и QUEUE_Write, чтобы они не мешали друг-другу, будучи вызванными одновременно из разных потоков?
Извиняюсь за "многабуков"
