ESP8266 Урок 23. FreeRTOS. UART. Приём данных

 

 

 

Продолжаем учиться писать код для микроконтроллера ESP8266.

На данном занятии мы продолжим работу с интерфейсом UART и попробуем принять данные по данной шине.

Такую работу мы уже проделывали в уроке 11, только операционную систему FreeRTOS мы не использовали, а с её использованием на приём данных по шине UART налагаются некоторые свои требования. Нам нежелательно уже будет использовать глобальные переменные, буферы, а использовать мы будем очередь, с которой мы уже знакомы, правда отправка данных в очередь из обработчиков прерываний незначительно отличается.

Надеюсь, что во всём этом мы сейчас разберёмся.

Схему мы возьмём из урока 20, так как мы принятые данные из UART будем отображать на дисплее

 

 

И проект за основу мы также возьмём из этого же урока с именем I2C_LCD2004_RTOS и присвоим ему новое имя UART_RX_RTOS.

Откроем наш проект в Eclipse и в файле main.c немного переделаем объявление глобального типа структуры qData. Мы переименуем первое поле номера задачи в номер строки, а второе поле сделаем не переменной для счётчика, а указателем на символьный массив, в котором будет находиться строка, отображаемая впоследствии на дисплее. После этого функция приёма данных их очереди и вывода их на дисплей станет более универсальной

 

 

Объявим ещё одну очередь, посредством которой данные будут передаваться в задачу обработки принятых символов из обработчиков прерываний

 

 

Для данной очереди никакая структура будет не нужна, так как передавать через данную очередь мы будем обычные байты.

Добавим функцию, которая будет служить обработчиком прерываний от UART, в теле которой для начала объявим переменные для длины буфера, счётчика элементов буфера, символьный массив для временного буфера, переменную состояния задачи, в которая будет принимать байты из очереди, и переменную для хранения символа

 

 

Прочитаем регистр с флагами прерываний

 

 

Добавим цикл, который будет работать до тех пор, пока хотя бы один флаг прерываний будет установлен

 

 

В теле данного цикла сбросим флаг прочих ошибок, если будет установлен

 

 

Если буфер приёма полон, то сначала узнаем его длину и установим указатель на ноль

 

 

В теле той же ветки условия добавим цикл, в котором будем, двигаясь по элементам буфера, считывать их значения и отправлять в очередь, отдавая затем управление задаче-приёмнику из данной очереди (если у неё приоритет выше, чем у задачи, вызвавшей прерывание, по крайней мере так написано в документации к FreeRTOS)

 

 

Выйдя из тела цикла (но не из ветки условия), сбросим флаг

 

 

Выйдя из ветки условия, проделаем то же самое, если у нас будет установлен флаг окончания тайм-аута приёма, только с той разницей, что мы в конце тела ветки условия сбросим уже соответствующий флаг

 

 

В следующей ветке условия обработаем событие опустошение буфера передачи путём сброса соответствующего флага, а также сбросим аналогичный бит в регистре разрешения прерываний

 

 

Выйдя из тела данной ветки условия, не забудем прочитать заново регистр в переменную, иначе мы будем крутиться в нашем цикле вечно

 

 

Ниже функции vRecvTask добавим функцию, которая будет принимать байты из очереди, в которую мы их послали в обработчике, обрабатывать их, склеивая в строку и отправлять указатель на данную строку с помощью другой очереди в задачу, которая после приёма из очереди будет отображать наши строки на дисплее. В данной задаче мы для начала объявим переменную типа структуры для очереди на отправку, переменную для хранения текущей позиции в строке, символьный массив, проинициализируем поля переменной структуры и объявим ещё переменную для хранения символа

 

 

Добавим бесконечный цикл, в котором попытаемся прочитать байт из очереди в переменную символа

 

 

Здесь наша задача будет в блокированном состоянии, до тех пор, пока в очереди не появится элемент либо до тех пор, пока не истечёт тайм-аут, а он вряд ли истечёт, так как максимальный. Если байт в очереди появился и он считан, то добавим ещё одно условие, в котором узнаем, что это не символ возврата каретки или перевода строки. Если это так, то запишем символ в массив и увеличим указатель на 1, а если это всё же символ возврата каретки или перевода строки, то забьём пробелами оставшиеся символы до 19-го (у нас такая величина строки в дисплее), закончим строку нулём и попытаемся передать указатель на нашу строку, а также позицию в функцию вывода строк на дисплей, ну и, конечно же, обнулим счётчик элементов массива

 

 

Теперь нам надо немного поработать с функцией задачи, выводящей на дисплей строки – vRecvTask. В теле данной функции сначала удалим объявление символьного массива, так как у нас будет указатель на такой массив, и нам дополнительный локальный будет не нужен

 

char str01[20];

 

Удалим вот это условие, мы не будем проверять очередь, так как это ёмкая операция, а нам тормоза не нужны

 

if (uxQueueMessagesWaiting(xQueue) != 0)

{

  os_printf("Queue should have been empty!\n");

}

 

Удалим также содержимое тела следующего условия

 

snprintf(str01, sizeof(str01), "%7lu", xReceivedData.cnt);

LCD_SetPos(7,xReceivedData.num_task – 1);

LCD_String(str01);

 

Вместо этого мы просто установим позицию, взяв её из переменной структуры и выведем строку на дисплей, воспользовавшись указателем из поля той же переменной типа структуры

 

 

Противный случай также удалим

 

else

{

  os_printf("Could not receive from the queue.\n");

}

 

 

Задачу task1 мы оставим, чтобы убедиться, что приём данных у нас работает даже при условии работы других задач, только код её тела нам надо будет также несколько переработать в свете требований задачи, которая будет выводить данные на дисплей. Сначала мы в данном теле удалим вот эту инициализацию

 

xReceivedData.cnt = 0;

 

Вместо этого мы объявим и проинициализируем обычную переменную

 

uint32 cnt = 0;

 

Удалим следующую строку

 

xReceivedData.num_task = pdt->num_task;

 

Вместо неё объявим символьный массив и проинициализируем поля переменной структуры

 

 

В бесконечном цикле мы вначале сформируем нашу строку

 

 

А вместо вот этих строк

 

xReceivedData.cnt++;

if(xReceivedData.cnt>=10000000) xReceivedData.cnt=0;

 

мы добавим подобные, но использующие обычную переменную, а не поле

 

cnt++;

if(cnt>=10000000) cnt=0;

 

Переходим в тело функции user_init, в котором сначала дождёмся опустошения буферов передачи обоих модулей UART

 

 

Сконфигурируем модуль UART0, для чего сначала объявим переменную типа соответствующей структуры, присвоим её полям нужные значения и применим данные настройки при помощи вызова соответствующей функции SDK

 

 

У нас не будет 4 задачи, оставим только одну, поэтому вывод соответствующих строк на дисплей удалим

 

LCD_SetPos(0,1);

LCD_String("Task2:");

LCD_SetPos(0,2);

LCD_String("Task3:");

LCD_SetPos(0,3);

LCD_String("Task4:");

 

Удалим также и инициализацию соответствующих полей

 

dt2.del = 400; dt2.num_task = 2;

dt3.del = 500; dt3.num_task = 3;

dt4.del = 600; dt4.num_task = 4;

 

Удалим создание задач

 

xTaskCreate(task1, "task2", 256, (void *) &dt2, 1, NULL);

xTaskCreate(task1, "task3", 256, (void *) &dt3, 1, NULL);

xTaskCreate(task1, "task4", 256, (void *) &dt4, 1, NULL);

 

Разрешим нужные прерывания от UART и зарегистрируем их обработчик

 

 

Создадим очередь и задачу

 

 

Соберём код, прошьём контроллер. Наша задача со счётчиком работает

 

 

Запустим терминальную программу, не забыв настроить там передачу данных из неё

 

 

Попытаемся что-нибудь передать из терминальной программы в наш модуль, стараясь не превысить максимальный размер строки в 20 символов

 

 

Данные из PC успешно пришли в модуль и отображаются на дисплее

 

 

Передадим теперь строку покороче, чтобы проверить стирание старого текста

 

 

Всё успешно принимается

 

 

При этом задача task1 также работает без сбоев.

Итак, на данном уроке мы научились принимать данные по шине UART, при этом используя механизм прерываний и операционную систему FreeRTOS.

Всем спасибо за внимание!

 

Предыдущий урок Программирование МК ESP8266 Следующий урок

 

Исходный код

 

 

Модуль ESP NodeMCU можно купить здесь: Модуль ESP NodeMCU

Различные модули ЕSP8266 можно приобрести здесь Модули ЕSP8266

Дисплей LCD 20×4 можно приобрести здесь Дисплей LCD 20×4

Дисплей LCD 16×2

Переходник I2C to LCD можно приобрести здесьI2C to LCD1602 2004

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

ESP8266 FreeRTOS. UART. Приём данных

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*