Неблокируемый класс 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.
Спасибо! Тоже всегда боялася неведомого)