InsolentS 0 29 октября, 2012 Опубликовано 29 октября, 2012 · Жалоба Добрый день! В моём проекте не используется ОС, но, думаю, что актуальнее всего будет задать вопрос именно в этом форуме. Есть драйвер (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; } } в драйвере SPI метод Write вызывается из основного потока программы: void SPI_WriteByte(uint8 _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); // записываем байт из очереди в регистр передатчика } ... } и всё работает хорошо при не очень интенсивном обмене данными, а если данных много, бывает, что прерывание по передаче возникает в момент, когда идёт выполнение функции Write, т.е. QUEUE_Read вызывается во время выполнения QUEUE_Write, данные в структуре очереди портятся и наступает жвах! В данный момент приходится запрещать прерывания от SPI в начале функции SPI_Write и заного разрешать их в конце - но такой подход мне не очень нравится, т.к. 1) Нужно помнить и следить за всеми вызовами функций очереди и обрамлять их в подобные критическии секции, очень велик риск забыть и получить труднообнаруживаемый глюк при эксплуатации. 2) Много времени система проводит при запрещённых прерываниях, т.е. увеличивается время реакции на прерывание -> велик риск пропустить входящий байт (если SPI в слейве). Может быть есть способ красиво разрулить эту ситуацию, внеся какие-то изменения в функции QUEUE_Read и QUEUE_Write, чтобы они не мешали друг-другу, будучи вызванными одновременно из разных потоков? Извиняюсь за "многабуков" :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Артём__ 0 29 октября, 2012 Опубликовано 29 октября, 2012 · Жалоба В данный момент приходится запрещать прерывания от SPI в начале функции SPI_Write и заного разрешать их в конце - но такой подход мне не очень нравится Запрет прерываний в таком случае - вполне приемлимый способ. 1) Нужно помнить и следить за всеми вызовами функций очереди и обрамлять их в подобные критическии секции, очень велик риск забыть и получить труднообнаруживаемый глюк при эксплуатации. Вставте критическую секцию в функцию SPI_WriteByte. На выходе из функции разрешайте прерывания. 2) Много времени система проводит при запрещённых прерываниях, т.е. увеличивается время реакции на прерывание -> велик риск пропустить входящий байт (если SPI в слейве). Если запрещать прырывания только на запись байта, то запрет недолгий. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xemul 0 29 октября, 2012 Опубликовано 29 октября, 2012 · Жалоба Если операции с _queue->Count атомарны, то достаточно объявить его volatile. Если нет, то придётся или взводить флаг на время операций с _queue->Count вне прерываний, или добавлять критические секции там же. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vshemm 0 29 октября, 2012 Опубликовано 29 октября, 2012 · Жалоба Если у процессора нет атомарных операций (или LL/SC, или HTM и т.д.), то без запрещения прерываний на время работы с _queue->Count не обойтись. С флагом не получится, т.к. ждать в прерывании пока отработает QUEUE_Write() можно до бесконечности. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xemul 0 29 октября, 2012 Опубликовано 29 октября, 2012 · Жалоба С флагом не получится, т.к. ждать в прерывании пока отработает QUEUE_Write() можно до бесконечности. Разве я сказал, что в прерывании нужно ждать сброса флага? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
InsolentS 0 29 октября, 2012 Опубликовано 29 октября, 2012 · Жалоба Если операции с _queue->Count атомарны, то достаточно объявить его volatile. Операция инкремента (INC) - атомарна, а разыменование указателя - нет :( QUEUE_Write: CALL ?push_w4 LDW ?b4, X LD ?b6, A if(_queue->Count < _queue->Size) // If buffer not full ADDW X, #?b4 LDW ?b2, X LDW X, ?b4 ADDW X, #?b5 LDW 0x00, X LD A, [0x00.w] CP A, [?b2.w] JRNC ??QUEUE_Write_0 _queue->Buff[_queue->WriteIndex++] = _data; // Write next byte to buffer LDW X, ?b4 ADDW X, #?b3 LDW Y, X LD A, (Y) CLRW X LD XL, A LDW ?b8, X LDW X, [?b4.w] ADDW X, ?b8 LD A, ?b6 LD (X), A LD A, (Y) INC A LD (Y), A _queue->WriteIndex &= _queue->Size - 1; // If buffer ends, jump to begin LD A, [?b2.w] DEC A AND A, (Y) LD (Y), A _queue->Count++; LD A, [0x00.w] INC A LD [0x00.w], A JP ?epilogue_w4 Процессор - stm8 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vshemm 0 29 октября, 2012 Опубликовано 29 октября, 2012 (изменено) · Жалоба Разве я сказал, что в прерывании нужно ждать сброса флага? А что вы предлагаете делать? Код ошибки вернуть нельзя, вызвать UnderflowFunc() тоже... UPD: Нужны атомарные чтение-запись + инкремент. Разыменование это тоже чтение. Правда, с stm8 я не работал. Изменено 29 октября, 2012 пользователем vshemm Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xemul 0 29 октября, 2012 Опубликовано 29 октября, 2012 · Жалоба А что вы предлагаете делать? Код ошибки вернуть нельзя, вызвать UnderflowFunc() тоже... По-моему, очевидно - ничего не делать. __interrupt void SPI_Isr(void) { ... if(!TxQueue.Busy) if(TxQueue.Count) // Если в очереди что-то есть { SPI_DR = QUEUE_Read(&TxQueue); // записываем байт из очереди в регистр передатчика }; ... } Содержимого ... не знаю, может потребуются уточнения. _queue->Count++; LD A, [0x00.w] INC A LD [0x00.w], A Как-то не похоже оно на атомарность. С stm8 тоже не работал, но конструкция для современного проца и компилятора выглядит чудесато. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 14 29 октября, 2012 Опубликовано 29 октября, 2012 · Жалоба Есть же неблокирующий вариант очереди с read_ptr и write_ptr, без count-а. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
InsolentS 0 30 октября, 2012 Опубликовано 30 октября, 2012 · Жалоба Есть же неблокирующий вариант очереди с read_ptr и write_ptr, без count-а. Очень интересно, не могли бы Вы дать ссылку или привести пример? Count, по большому счёту, нужен только чтобы отслеживать переполнение и опустошение очереди, не хотелось бы потерять эту функцию. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 14 30 октября, 2012 Опубликовано 30 октября, 2012 · Жалоба Да там всё элементарно. Есть два индекса - put и get. Буфер пуст, если put == get. Буфер полон, если put + 1 == get. запись в буфер: temp = (put + 1) % SIZE; if (temp == get) return -1; // buffer full buffer[put] = ch; put = temp; чтение из буфера: if (put == get) return -1; // empty ch = buffer[get++]; get %= SIZE; // limit the pointer return ch При условии: один писатель, один читатель - прерывания можно не запрещать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ReAl 0 30 октября, 2012 Опубликовано 30 октября, 2012 · Жалоба При условии: один писатель, один читатель - прерывания можно не запрещать.для случая sizeof(put) <= sizeof(sig_atomic_t) Иначе надо подумать о возможных эффектах при прерывании, например, в середине чтения основной програмой индекса, изменяемого в прерывании. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 14 30 октября, 2012 Опубликовано 30 октября, 2012 · Жалоба для случая sizeof(put) <= sizeof(sig_atomic_t)Да, точно. Я не придумал, как это сформулировать:) Иначе надо подумать о возможных эффектах при прерывании, например, в середине чтения основной програмой индекса, изменяемого в прерывании.В этом случае можно читать индекс до совпадения результатов двух последовательных чтений. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mbr 0 20 ноября, 2012 Опубликовано 20 ноября, 2012 · Жалоба Фьютексы же. Можно использовать два буфера (что, собственно, реализуется большинством ОС) - один на чтение, второй на запись. Буфер на запись можно использовать свободно. Дальше используем две дополнительные переменные - lockFlag и readed. в основной программе: lockFlag = 1; <read data here> readed += <readed size> lockFlag = 0; в обработчике: if (!lockFlag) { real_size -= readed; readed = 0; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться