Неблокируемый класс HardwareSerial


Неблокируемый класс 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

AVR. Учебный курс. Передача данных через UART


http://www.multiwii.com


0 комментариев на «“Неблокируемый класс HardwareSerial”»

    • Когда полез в код, сначала тоже было страшновато.
      Не хотелось запороть единственный контроллер.
      После 2 дней копания понял, что не все так страшно.
      Скорее всего код еще немного измениться, поэтому «Возможно продолжение…» 🙂
      Но этот код работоспособен.
      Только архив с кодом не знаю куда лучше выложить. 🙁

    • лучше всего исходники на гитхаб залить или гугл-код 😉

    • Добавил ссылку на Google code.

      А можно перенести статью в раздел «Коммуникации»?
      А то я что-то не понял как это сделать.
      Или мне пока нельзя туда писать?

    • на блоги есть ограничение по рейтингу.
      Перенёс в Arduino.

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение