Неблокируемый класс HardwareSerial.
Собственно все началось с того, что мне нужно было проверять датчики, подключенные к arduino, даже тогда, когда я вывожу информацию в терминал.
Проверяя длину одного цикла loop() обнаружил, что при выводе информации в порт, время цикла резко возрастает.
Начал выяснять, и обнаружил, что класс HardwareSerial при выводе данных в COM порт переходит в состояние ожидания опустошения буфера обмена.
Вот этот код
// If the output buffer is full, there's nothing for it other than to // wait for the interrupt handler to empty it a bit // ???: return 0 here instead? while (i == _tx_buffer->tail) ;
Это все хорошо для разработки простых однопоточных программ (собственно Arduino первоначально для этого и создавалась). Но уж очень хочется использовать все прелести микроконтроллера и его прерываний. Для этого была, например, разработана библиотека TimerOne.
Так вот…
Пришлось немного покопаться во внутренностях библиотеки, которая идет в комплекте со средой Arduino.
Поскольку раньше никогда с AVR дела не имел, пришлось пару дней поискать информацию по внутренностям микроконтроллера и работой с прерываниями.
Все оказалось достаточно просто.
Для совместимости ввел признак Serial.blocked (по умолчанию имеет значение true) так что все программы написанные ранее должны работать без изменений в коде.
Если вы хотите использовать неблокируемую передачу, то вы должны явно перейти в неблокируемый режим Serial.blocked = false;
Теперь об изменениях…
Изменения коснулись нескольких функций.
Функция begin
void HardwareSerial::begin(unsigned long baud) { uint16_t baud_setting; bool use_u2x = true; blocked = true; // Set blocked tag for compatibility
Добавил инициализацию признака blocked для совместимости.
Функция store_char
inline int store_char(unsigned char c, ring_buffer *buffer) { int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE; // if we should be storing the received character into the location // just before the tail (meaning that the head would advance to the // current location of the tail), we're about to overflow the buffer // and so we don't write the character or advance the head. if (i != buffer->tail) { buffer->buffer[buffer->head] = c; buffer->head = i; return 1; } else{ return 0; } }
Теперь функция возвращает 1 при записи символа в порт, и 0 если буфер заполнен.
Новая функция tx_available, возвращает количество свободных байт в буфере передачи.
int HardwareSerial::tx_available(void) { return (unsigned int)(SERIAL_BUFFER_SIZE - (SERIAL_BUFFER_SIZE + _tx_buffer->head - _tx_buffer->tail) % SERIAL_BUFFER_SIZE); }
И главное изменение в функции write
size_t HardwareSerial::write(uint8_t c) { if(blocked){ int i = (_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE; // If the output buffer is full, there's nothing for it other than to // wait for the interrupt handler to empty it a bit // ???: return 0 here instead? while (i == _tx_buffer->tail) ; _tx_buffer->buffer[_tx_buffer->head] = c; _tx_buffer->head = i; sbi(*_ucsrb, _udrie); return 1; } else{ if (tx_buffer.head == tx_buffer.tail && ( UCSR0A & (1<Собственно всё...
При использовании неблокируемого вывода программист сам должен позаботиться о том, чтобы все данные попали в буфер передачи.
Перед выводом данных необходимо проверить, есть ли место в буфере передачи, иначе более новые данные перезапишут еще не переданную из буфера информацию.
Режим вывода можно переключать в любое время (Serial.blocked = true/false;)Пример кода
#include <arduino.h> unsigned long loop_counter; void setup() { // initialize serial: Serial.begin(9600); //Serial.blocked = true; // Blocked operation for Arduino compatibility mode. This tag set to true as default value //Serial.blocked = false; // Uncomment this line for new nonblocked operations. } void loop() { bool send_tag; loop_counter++; if(Serial.available()>0){ if(Serial.read() == ' '){ digitalWrite(13,1); Serial.blocked = false; } else{ digitalWrite(13,0); Serial.blocked = true; } } if(Serial.blocked == false) send_tag = Serial.tx_available() > 60; else send_tag = true; if(send_tag){ Serial.println("1234567890"); Serial.println("1234567890"); Serial.println("1234567890"); Serial.println("1234567890"); Serial.println(loop_counter); // Look this counter in terminal for blocked and for nonblocked operations loop_counter = 0; } }Возможно, продолжение следует...
Google Code
http://code.google.com/p/nb-hardware-serial/Ссылки
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
http://www.multiwii.com
0 комментариев на «“Неблокируемый класс HardwareSerial”»
Спасибо! Немногие решаются лезть внутрь «кубиков», из которых строится мир 🙂
Когда полез в код, сначала тоже было страшновато.
Не хотелось запороть единственный контроллер.
После 2 дней копания понял, что не все так страшно.
Скорее всего код еще немного измениться, поэтому «Возможно продолжение…» 🙂
Но этот код работоспособен.
Только архив с кодом не знаю куда лучше выложить. 🙁
лучше всего исходники на гитхаб залить или гугл-код 😉
Добавил ссылку на Google code.
А можно перенести статью в раздел «Коммуникации»?
А то я что-то не понял как это сделать.
Или мне пока нельзя туда писать?
на блоги есть ограничение по рейтингу.
Перенёс в Arduino.
Спасибо! Тоже всегда боялася неведомого)