Для работы с UART и со стандартными библиотечными функциями потокового ввода/вывода printf() и scanf() создаются 2 очереди:
· Очередь stdin_queue предназначена для приема данных через UART.
· Очередь stdout_queue предназначена для передачи данных через UART.
Работа с очередью stdin_queue для приема данных через UART.
Запись элемента в очередь stdin_queue.
Обработчик прерывания по приему по UART - UART2_IRQHandler() помещает принятый байт данных в локальную переменную
ReceiveByte = UART_ReceiveData(UART_IO).
Значение переменной ReceiveByte отправляется в конец очереди stdin_queue: xQueueSendToBackFromISR(stdin_queue, &ReceiveByte, &xHigherPriorityTaskWoken).
Чтение элемента из очереди stdin_queue.
Функция scanf() является стандартной библиотечной функцией потокового ввода. Функция fgetc(), находящаяся в файле uart_io.c, читает один байт из потокового ввода и вызывается из функции scanf(). Функция fgetc() должна быть переопределена для чтение одного байта из очереди stdin_queue. Вызов функции scanf() из любой задачи, по сути, будет обращаться к данным очереди stdin_queue.
Работа с очередью stdout_queue для передачи данных через UART.
Запись элемента в очередь stdout_queue.
Функция printf() является стандартной библиотечной функцией потокового вывода. Функция fputc(), находящаяся в файле uart_io.c, передает один байт из потокового вывода и вызывается из функции printf(). Функция fputc() должна быть переопределена для передачи одного байта в очередь stdout_queue. Вызов функции printf() из любой задачи, по сути, будет формировать данные очереди stdout_queue.
Чтение элемента из очереди stdout_queue.
Вывод данных по UART организован в отдельной низкоприоритетной задачи: Task_Output. В этой задаче происходит чтение элемента из очереди stdout_queue в переменную buffer и передача значения этой переменной через UART с помощью функции UART_SendData(UART_IO, buffer). Передача будет производиться пока задача не передаст все символы из очереди. Если задача блокируется планировщиком из-за вызова более высокоприоритетной задачи, то передача символов продолжится после блокировки более высокоприоритетной задачи.
|
Для работы со стандартными библиотечными функциями потокового ввода/вывода printf() и scanf() были переопределены функции посимвольного ввода вывода fputc() и fgetc(). Данные функции осуществляли по символьный ввод/вывод в 2 очереди для ввода и вывода:
Функции fputc() и fgetc():
1. extern xQueueHandle stdin_queue;
2. extern xQueueHandle stdout_queue;
3.
4. /**
5. * @brief Retargets the C library printf function to the UART.
6. */
7. int fputc(int ch, FILE *f)
8. {
9. char item = (char)ch;
10.
11. xQueueSendToBack(stdout_queue, &item, 0);
12. return ch;
13. }
14.
15. /**
16. * @brief Retargets the C library scanf function to the UART.
17. */
18. int fgetc(FILE *f)
19. {
20. uint8_t ch;
21.
22. if (xQueueReceive(stdin_queue, &ch, portMAX_DELAY) == pdPASS)
23. {
24. return((int)ch);
25. }
26. else
27. {
28. return ((int)'\0');
29. }
30. }
По приему данных по UART разрешены прерывания, для этого в функции инициализации UART uart_io_init() необходимо добавить 2 строки:
1. /*Разрешение прерывания для UART*/
2. NVIC_EnableIRQ(UART_IO_IRQ);
3. /*Разрешение прерывания по приему*/
4. UART_ITConfig(UART_IO,UART_IT_RX, ENABLE);
Подпрограмма обработчик прерывания по приему данных по UART:
1. void UART2_IRQHandler(void)
2. {
3. static uint8_t ReceiveByte=0x00; // данные для приема
4. static BaseType_t xHigherPriorityTaskWoken;
5. //проверка установки флага прерывания по окончании приема данных
6. if (UART_GetITStatusMasked(MDR_UART2, UART_IT_RX) == SET)
7. {
8. //очистка флага прерывания по приему данных
9. UART_ClearITPendingBit(MDR_UART2, UART_IT_RX);
10. }
11. // записать принятый данный в статическую переменную
12. ReceiveByte=UART_ReceiveData(UART_IO);
|
13. //Отправить принятый байт в очередь приема данных
14. xQueueSendToBackFromISR(stdin_queue, &ReceiveByte, &xHigherPriorityTaskWoken);
15. // Принудительно переключить контекст, вызов планировщика для передачи управления задаче с более высоким приоритетом, если она разблокировалась
16. portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
17. }
Значение *pxHigherPriorityTaskWoken необходимо отслеживать для того, чтобы выполнить переключение контекста задачи в конце обработчика прерывания, если в результате отправки в очередь была разблокирована более высокоприоритетная задача. Если этого не сделать, то после выполнения обработчика прерывания выполнение продолжит та задача, выполнение которой были прервано этим прерыванием. Ничего «страшного» в этом случае не произойдет: текущая задача будет выполняться до истечения текущего кванта времени, после чего планировщик выполнит переключение контекста (которое он выполняет каждый системный квант), и управление получит более высокоприоритетная задача. Единственное, что пострадает, — это время реакции системы на прерывание, которое может составлять до одного системного кванта.
Основная функция программы main.c:
1. /* Объявить переменную-дескриптор очереди. Эта переменная
2. будет использоваться для ссылки на очередь после ее создания. */
3. xQueueHandle stdin_queue,stdout_queue;
4.
5. // Создание очередей для потока ввода/вывода
6. stdin_queue = xQueueCreate(STDIN_QUEUE_SIZE, sizeof(char));
7. stdout_queue = xQueueCreate(STDOUT_QUEUE_SIZE, sizeof(char));
8.
9. //Создание задачи по работе с меню
10. xTaskCreate(U_MENU_Task_Function,
11. (char *) "Task1",
12. configMINIMAL_STACK_SIZE,
13. NULL,
14. tskIDLE_PRIORITY + 2,
15. NULL);
16. //Создание задачи по выводу бегущей строки на ЖКИ
|
17. xTaskCreate(U_MENU_Running_String_Task_Function,
18. (char *) "Task2",
19. configMINIMAL_STACK_SIZE,
20. NULL,
21. tskIDLE_PRIORITY + 2,
22. NULL);
23. //Создание задачи по выводу на ЖКИ информации полученной с терминала по UART
24. xTaskCreate(U_MENU_Output,
25. (char *) "Task3",
26. configMINIMAL_STACK_SIZE,
27. NULL,
28. tskIDLE_PRIORITY + 2,
29. NULL);
30. //Создание задачи приветствия
31. xTaskCreate(U_Privet,
32. (char *) "Task4",
33. configMINIMAL_STACK_SIZE,
34. NULL,
35. tskIDLE_PRIORITY + 3,
36. NULL);
37. //Создание задачи для работы с выводом по UART – самая низкоприоритетная задача
38. xTaskCreate(Task_output,
39. (char *) "Task5",
40. configMINIMAL_STACK_SIZE,
41. NULL,
42. tskIDLE_PRIORITY + 1,
43. NULL);
44.
45. // Запуск планировщика задач
46. vTaskStartScheduler();
47. }
Добавленные задачи:
1. //Задача по выводу на ЖКИ информации полученной с терминала по UART
2. void U_MENU_Output (void)
3. {
4. /* Буфер для полученного сообщения по UART */
5. char Message_UART[16 + 1];
6. while(1)
7. {
8. // Ожидание ввода сообщения по UART
9. scanf("%16s", Message_UART);
10. // Передача полученного ссобщения на дисплей ЖКИ в 4-ю строку
11. U_MLT_Put_String (Message_UART, 4);
12. // Передача в стандартный поток вывода, что сообщение успешно передано на ЖКИ
13. printf("Transmit Message To LCD - Success\r\n");
14. }
15. }
16.
17. // Задача - приветствие
18. void U_Privet(void)
19. {
20. // Отправка сообщения приветствия
21. printf("Work with Queues\r\n");
22. // Удаление задачи
23. vTaskDelete(NULL);
24. }
25.
26. // Задача для работы с выводом по UART - Самая низкоприоритетная задача
27. void Task_output(void)
28. {
29. static uint8_t buffer;
30. while(1)
31. {
32. if (xQueueReceive(stdout_queue, &buffer, portMAX_DELAY) == pdPASS)
33. {
34. //Ожидание флага освобождения буфера передачи (TXFE) */
35. while (UART_GetFlagStatus(UART_IO, UART_FLAG_TXFE)!= SET);
36. // Отправка одного символа по UART
37. UART_SendData(UART_IO, buffer);
38. }
39.
40. }
41. }