|
|
  |
Вопрос по usr::ring_buffer, как правильно пользоваться |
|
|
|
Jan 21 2012, 18:54
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Попытался использовать usr::ring_buffer для выдачи отладочного лога в свободный УАРТ. Алгоритм такой: 1. Задача заполняет его данными 2. В прерывании таймера проверяется не пуст ли буфер, если не пуст, то разрешается прерывание по пустому буферу передачика уарт. 3. В прерывании DRE посылается след.байт и запрещаются прерывания если байтов больше нет. CODE class TExtTxBuffer { usr::ring_buffer<unsigned char, EXTERNAL_UART_TX_BUFFER, unsigned short> fifo; public: void WriteByte(unsigned char new_byte) { fifo.push(new_byte); } unsigned char ReadByte(void) { return fifo.pop(); } void WriteAnsiString(unsigned char __flash *data) { while (*data) { fifo.push(*data++); } } void WriteAnsiString(unsigned char *data) { while (*data) { fifo.push(*data++); } } unsigned char BufferNotEmpty(void) { if (fifo.get_count()) return 1; return 0; } };
void StopExtUartTx() { UCSR0B&=~(1<<UDRIE0); } #pragma vector=USART0_UDRE_vect __interrupt void ExtUartTx() { OS::TISRW_SS ISRW; unsigned char i=ExtTxPtr.ReadByte(); UDR0=i; if (ExtTxPtr.BufferNotEmpty()==0) StopExtUartTx(); }
В какой-то момент обнаружил что передача данных прекратилась. Остановил программу. Увидел следующее (цифры примерные): Код First=10; Last=9; Count=0; B соответственно код Код if (ExtTxPtr.BufferNotEmpty()) StartExtUartTx(); прерывание USART0_UDRE_vect запускать не стал. Что неправильно делаю? Неправильно объявил (не использовал volatile)? Код usr::ring_buffer<unsigned char, EXTERNAL_UART_TX_BUFFER, unsigned short> fifo; Нужны ли критические секции перед вызовом BufferNotEmpty и WriteByte? Спасибо.
|
|
|
|
|
Jan 21 2012, 21:04
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(AHTOXA @ Jan 21 2012, 21:30)  ring_buffer не обеспечивает безопасного многопоточного доступа. Спасибо, AHTOXA. Чувствовал когда писал что граблю закладываю. Предчуствия меня не обманули. Попробую выйти из положения пока так: Код usr::ring_buffer<unsigned char, EXTERNAL_UART_TX_BUFFER, volatile unsigned short> fifo; void WriteByte(unsigned char new_byte) { TCritSect cs; fifo.push(new_byte); } void WriteAnsiString(unsigned char __flash *data) { TCritSect cs; while (*data) { fifo.push(*data++); } } void WriteAnsiString(unsigned char *data) { TCritSect cs; while (*data) { fifo.push(*data++); } } Цитата(AHTOXA @ Jan 21 2012, 21:30)  Используйте OS::channel, он специально предназначен для таких целей Понял, channel подходящее будет. Цитата(AHTOXA @ Jan 21 2012, 21:30)  Это, на самом деле, тот же самый ring_buffer, но с механизмами блокировки. Зачем тогда ring_buffer нужен в чистом виде? Только как основа для того же OS::channel? Где его использовать?
|
|
|
|
|
Jan 22 2012, 11:03
|

Шаман
     
Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221

|
Цитата(_Артём_ @ Jan 21 2012, 23:04)  Попробую выйти из положения пока так: Вторая и третья функции - плохие, блокируют прерывания и, как следствие, перепланирование процессов на неопределённое время, особенно в случае длинных стрингов. Кроме того, они отличаются только типом параметра, можно было бы сделать шаблоном. Цитата(_Артём_ @ Jan 21 2012, 23:04)  Зачем тогда ring_buffer нужен в чистом виде? Только как основа для того же OS::channel? В документации и в комментариях сказано, что он нужен для служебных операций, в частности, для OS::channel. Цитата(_Артём_ @ Jan 21 2012, 23:04)  Где его использовать? Его можно применять там, где не критично, т. е. если синхронизация между процессами (наполняющим и выгребающим) делается другими способами.
|
|
|
|
|
Jan 22 2012, 14:50
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(IgorKossak @ Jan 22 2012, 13:03)  Вторая и третья функции - плохие, блокируют прерывания и, как следствие, перепланирование процессов на неопределённое время, особенно в случае длинных стрингов. Согласен, когда в программу вписывал - понял. Сделал так: Код void WriteAnsiString(unsigned char *data) { TCritSect cs; while (*data) { { TCritSect cs; fifo.push(*data++); } } } Цитата(IgorKossak @ Jan 22 2012, 13:03)  Кроме того, они отличаются только типом параметра, можно было бы сделать шаблоном. Хорошая мысль, осталось шаблоны освоить. Цитата(IgorKossak @ Jan 22 2012, 13:03)  В документации и в комментариях сказано, что он нужен для служебных операций, в частности, для OS::channel. Проглядел. Спасибо. Цитата(IgorKossak @ Jan 22 2012, 13:03)  Его можно применять там, где не критично, т. е. если синхронизация между процессами (наполняющим и выгребающим) делается другими способами. Какими? Не представил....
|
|
|
|
|
Jan 22 2012, 17:13
|

Шаман
     
Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221

|
Цитата(_Артём_ @ Jan 22 2012, 16:50)  Сделал так: Если первую строку с TCritSect cs; выкинуть, то получится ещё лучше. Цитата(_Артём_ @ Jan 22 2012, 16:50)  Хорошая мысль, осталось шаблоны освоить. Это быстрее, чем Вы писали эту фразу. Хотя бы по примерам из той же scmRTOS. Цитата(_Артём_ @ Jan 22 2012, 16:50)  Какими? Не представил.... Сигналами, семафорами или, на худой конец, так как в результате у Вас получилось с TCritSect cs. Но это всё не так красиво, как с каналами.
|
|
|
|
|
Jan 22 2012, 17:50
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(IgorKossak @ Jan 22 2012, 19:13)  Если первую строку с TCritSect cs; выкинуть, то получится ещё лучше. Да, это я хорошо исправил...поспешил. Цитата(IgorKossak @ Jan 22 2012, 19:13)  Это быстрее, чем Вы писали эту фразу. Хотя бы по примерам из той же scmRTOS. Будем посмотреть Цитата(IgorKossak @ Jan 22 2012, 19:13)  Сигналами, семафорами или, на худой конец, так как в результате у Вас получилось с TCritSect cs. Но это всё не так красиво, как с каналами. Ясно. Спасибо.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|