работа Serial.flush()
- Войдите на сайт для отправки комментариев
Ср, 18/12/2019 - 22:12
Коллеги.
Всегда считал, что Serial.flush() работает некорректно. Но закрались сомнения в собственной компетентности. Я не эксперт в AVR, однако вот мои соображения.
Текст Serial.flush() (выглядит слегка обфусцированным почему-то):
void HardwareSerial::flush() { // If we have never written a byte, no need to flush. This special // case is needed since there is no way to force the TXC (transmit // complete) bit to 1 during initialization if (!_written) return; while (bit_is_set(*_ucsrb, UDRIE0) || bit_is_clear(*_ucsra, TXC0)) { if (bit_is_clear(SREG, SREG_I) && bit_is_set(*_ucsrb, UDRIE0)) // Interrupts are globally disabled, but the DR empty // interrupt should be enabled, so poll the DR empty flag to // prevent deadlock if (bit_is_set(*_ucsra, UDRE0)) _tx_udr_empty_irq(); } // If we get here, nothing is queued anymore (DRIE is disabled) and // the hardware finished tranmission (TXC is set). }
Текст прерывания по опустошению UDR (другие прерывания по концу передачи в HardwareSerial не используются):
void HardwareSerial::_tx_udr_empty_irq(void) { // If interrupts are enabled, there must be more data in the output // buffer. Send the next byte unsigned char c = _tx_buffer[_tx_buffer_tail]; _tx_buffer_tail = (_tx_buffer_tail + 1) % SERIAL_TX_BUFFER_SIZE; *_udr = c; // clear the TXC bit -- "can be cleared by writing a one to its bit // location". This makes sure flush() won't return until the bytes // actually got written. Other r/w bits are preserved, and zeroes // written to the rest. #ifdef MPCM0 *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0); #else *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << TXC0))); #endif if (_tx_buffer_head == _tx_buffer_tail) { // Buffer empty, so disable interrupts cbi(*_ucsrb, UDRIE0); } }
Я вижу так, что Serial.flush() выйдет тогда, когда уйдёт последний байт из UDR, но не когда уйдёт последний байт из сдвигового регистра (по TXC == 1). То есть Serial.flush() работает некорректно. Разубедите, если это не так.
Всегда считал, что Serial.flush() работает некорректно. Но закрались сомнения в собственной компетентности. Я не эксперт в AVR, однако вот мои соображения.
Текст Serial.flush() (выглядит слегка обфусцированным почему-то):
Я вижу так, что Serial.flush() выйдет тогда, когда уйдёт последний байт из UDR, но не когда уйдёт последний байт из сдвигового регистра (по TXC == 1). То есть Serial.flush() работает некорректно. Разубедите, если это не так.
Насколько я понял именно когда уйдет последний бит из сдвигового регистра.
Из даташита
TXCn: USART Transmit Complete This flag bit is set when the entire frame in the Transmit Shift Register has been shifted out and there are no new data currently present in the transmit buffer (UDRn).
А в чем некорректность работы serial.flash()?
flush выйдет тогда, когда освободится буфер отправки, (_tx_buffer приравняется _tx_buffer_tail), что вполне понятно из приведенного кода.
flush выйдет тогда, когда освободится буфер отправки, (_tx_buffer приравняется _tx_buffer_tail), что вполне понятно из приведенного кода.
Это не так. Точнее - именно так. И это неправильно, если не понимаете, почему - не ко мне.
Насколько я понял именно когда уйдет последний бит из сдвигового регистра.
Из даташита
TXCn: USART Transmit Complete This flag bit is set when the entire frame in the Transmit Shift Register has been shifted out and there are no new data currently present in the transmit buffer (UDRn).
А в чем некорректность работы serial.flash()?
В том, что flush выйдет, когда сработает последнее прерывание по UDR. Дело не в даташите, а в логике тех, кто писал HardwareSerial. Я долго на это смотрел, но вот я не вижу выхода flush по окончании передачи, а вижу выход по последнему прерыванию по опустошению UDR.
В том, что flush выйдет, когда сработает последнее прерывание по UDR. Дело не в даташите, а в логике тех, кто писал HardwareSerial. Я долго на это смотрел, но вот я не вижу выхода flush по окончании передачи, а вижу выход по последнему прерыванию по опустошению UDR.
Выход из flush происходит когда выставляетс флаг TXC0. А происходит это когда the entire frame in the Transmit Shift Register has been shifted out and there are no new data currently present in the transmit buffer (UDRn).
Выход из flush происходит когда выставляетс флаг TXC0. А происходит это когда the entire frame in the Transmit Shift Register has been shifted out and there are no new data currently present in the transmit buffer (UDRn).
Вы посмотрите, какое условие там первое по или. Ну и что произойдёт раньше? UDRIE сбросится или TXC встанет? Ага?
Вы посмотрите, какое условие там первое по или. Ну и что произойдёт раньше? UDRIE сбросится или TXC встанет? Ага?
UDRIE это просто разрешение прерыванией по опустошении.Ты путаешь с UDRE
UDRIEn: USART Data Register Empty Interrupt Enable
UDREn: USART Data Register Empty
Там же в комментарии сказано
// interrupt should be enabled, so poll the DR empty flag to
prevent deadlock
UDRIE это просто разрешение прерыванией по опустошении.Ты путаешь с UDRE
UDRIEn: USART Data Register Empty Interrupt Enable
UDREn: USART Data Register Empty
Вот это не смущает? Я же не зря привёл текст прерывания.
if
(_tx_buffer_head == _tx_buffer_tail) {
// Buffer empty, so disable interrupts
cbi(*_ucsrb, UDRIE0);
И, конечно я читал комментарии. Я не просто так сказал, что давно смотрю на эту шнягу.
UDRIE это просто разрешение прерыванией по опустошении.Ты путаешь с UDRE
UDRIEn: USART Data Register Empty Interrupt Enable
UDREn: USART Data Register Empty
Вам сидеть и смотреть пару дней, пытаясь понять логику работы этой библиотеки. Когда точно сможете сказать в чём я неправ - тогда пишите. Не забудьте логику библиотеки увязать с работой контроллера, которая описана чуть более, чем конкретно производителем.
Ну да, согласен. Выйдет по опустошению data register. Для меня это вообще загадочная функция. По названию должна буфер очищать, а не ждать пока все передастся.
Ну да, согласен. Выйдет по опустошению data register. Для меня это вообще загадочная функция. По названию должна буфер очищать, а не ждать пока все передастся.
Так я именно так и думал, но во многих библиотеках она используется для переключения RS-485 на приём. А это рано.
Факт. Было бы логичнее добавить для этих случаев функцию вроде WaitForEndOfTransmission и ждать пока освободится буфер и выставится TXCn
flush() в библиотеке для эзернета тоже используется для выталкивания в сетевой чип всего, что еще болтается в памяти МК. Так что это, видать, устоявшаяся практика - считать, что буфер "почищен", когда избавились от данных на своей стороне.
Это не так. Точнее - именно так. И это неправильно, если не понимаете, почему - не ко мне.
Пришел арбитр правильности и решил, что общепринятое поведение теперь неправильно, и идите все нафиг если не согласны.
Пришел арбитр правильности и решил, что общепринятое поведение теперь неправильно, и идите все нафиг если не согласны.
Если бы не эти строчки в HardwareSerial.cpp я бы ничего не имел против такого поведения:
Ну и описание с оф. сайта:
Собирался спросить "а что не так?", но увидел
И это неправильно, если не понимаете, почему - не ко мне.
и понял, что это не к Вам.
Пришел арбитр правильности и решил, что общепринятое поведение теперь неправильно, и идите все нафиг если не согласны.
Если бы не эти строчки в HardwareSerial.cpp я бы ничего не имел против такого поведения:
Ну и описание с оф. сайта:
Всё верно. Комментарий подходит к строчке, которую он комментирует, и поведение процедуры соответствует описанному.
Всё верно. Комментарий подходит к строчке, которую он комментирует, и поведение процедуры соответствует описанному.
Не соответствует. Одному вроде уже объяснил, тут второй нарисовался.
.
Собирался спросить "а что не так?", но увидел
И это неправильно, если не понимаете, почему - не ко мне.
и понял, что это не к Вам.
Не ко мне. Вам это просто не нужно.
Короче говоря, товарищи. Если кто хочет доказать, что Serial.flush() корректно выходит по окончании передачи, как написано в описании на сайте ардуино.цц и в комментариях в библиотеке - добро пожаловать. Кто хочет пивным авторитетом пугать неокрепших юношей - проходите мимо.
А кто хочет сказать, что от Serial.flush() нечего было ожидать иного, кроме ожидания очистки буфера передачи - те уже сказали, и я в том числе.
"Serial.flush(); // ждем окончания передачи"
Тоже не работает, как описано!
Программа (Ардуино) отсылала на скорости 115200 другому устройству пакеты по 256 байт (большой массив, поделённый на такой размер). После отсылки пакета ( читаем с SD в буфер и далее из него Serial.write(); ) ставилось Serial.flush(); На втором пакете сбивалась синхронизация на той стороне. Заработало только после того, как Serial.flush(); заменил на банальную паузу delay(200); между пакетами.
"Serial.flush(); // ждем окончания передачи"
Тоже не работает, как описано!
Кашмар!
И куда смотрит правительство?