======Printf через UART====== В статье [[https://startmilandr.ru/doku.php/prog:debug:printfitm|Printf через ITM]] был рассмотрен способ вывода информации стандартного потока ввода/вывода stdio в ПК с помощью отладчика по интерфейсу ITM. Однако такой подход применим только для МК с ядром Cortex M3 и выше. МК на Cortex M0 и M1, такие как 1986ВЕ1Т, 1986ВЕВ3Т, блока отладки ITM не имеют, поэтому вывести отладочную информацию с помощью отладчика нельзя. В данной статье мы рассмотрим, как реализовать ввод/вывод сообщений с помощью интерфейса UART. Получившийся пример программы с использованием ввода/вывода через UART для МК 1986ВЕ1Т можно скачать по [[https://drive.google.com/uc?authuser=0&id=1iLfvBN7jwvduZ45cTfCB5T1EoGjxFTKi&export=download|ссылке]]. =====Стандартные функции ввод/вывод===== Ввод и вывод информации осуществляется через функции стандартной библиотеки. Прототипы рассматриваемых функций находятся в файле stdio.h. Эта библиотека содержит функции: - printf() — для вывода информации; - scanf() — для ввода информации. Чтобы понять, как настроить ввод/вывод информации рассмотрим данные функции поподробнее. Структура построения стандартных функций ввода/вывода в Keil выглядит следующим образом: {{prog:debug:pritfuart:struct_func.png}} Функции, которые доступны пользователю, находятся в самом верху данной структуры и называются высокоуровневыми (High-Level Functions). К таким функциям как раз и относятся printf() и scanf(). При их вызове обработка вводимой информации реализуется с помощью вызова низкоуровневых функций (Low-Level Functions), к ним относятся такие функции как fputc() и fgetc(). Данные функции в свою очередь вызывают системные функции, которые напрямую работают с периферией МК (System I/O Functions), например, с UART или CAN, перенаправляя на них поток данных. Наша задача будет реализовать эти системные функции для ввода/вывода информации по UART. Для встраивания системных функций в структуру стандартных функций ввода/вывода Keil предоставляет шаблон специального файла [[http://www.keil.com/support/man/docs/gsac/gsac_retargetcortex.htm|Retarget.c]]. В нём цепочка вывода информации от fputc() перенаправляется в системную функцию sendchar(), а при вводе функция fgetc() ожидает информацию от getkey(). Две эти системные функции sendchar() и getkey() мы и опишем для работы с интерфейсом UART. При желании можно не использовать Retarget.c от Keil, а самостоятельно объявить функции fputc() и fgetc(), в которых реализовать ввод/вывод по интересующему интерфейсу МК. Однако, в дополнение к функциям fputc() и fgetc() необходимо также объявить структуры: struct __FILE { int handle; }; FILE __stdout; FILE __stdin; Если этого не сделать, то низкоуровневые библиотеки Си будут реализовывать механизм semihosting’a, и в начале программы будет вызвана инструкция BKPT, переводящая процессор в режим debug. Выполнение программы при этом останавливается. =====Реализация функций для работы с UART===== Прежде чем приступить к описанию системных функций sendchar() и getkey() необходимо настроить интерфейс UART. В SPL уже сделан специальный набор параметров для отладки с помощью UART в различных МК (некоторые стандартные примеры его успешно используют), который содержится в файле «MDR32F9Qx_config.h» и активируется с помощью макроопределения _USE_DEBUG_UART_. Этими параметрами мы воспользуемся при написании функции инициализации интерфейса UART. После установки Pack'a файл «MDR32F9Qx_config.h» по умолчанию расположен по пути //"Диск:\Keil\ARM\PACK\Keil\MDR1986BExx\1.5\Config"//. Если макроопределение _USE_DEBUG_UART_ не раскомментировать, то проект будет собираться с ошибками. Листинг функции инициализации UART void DebugUARTInit() { UART_InitTypeDef UART_InitStructure; PORT_InitTypeDef PORT_InitStructure; uint32_t BaudRateStatus; #if defined (USE_MDR1986VE3) RST_CLK_PCLKcmd((RST_CLK_PCLK_PORTD | RST_CLK_PCLK_UART2), ENABLE); #elif defined (USE_MDR1986VE1T) RST_CLK_PCLKcmd((RST_CLK_PCLK_PORTC | RST_CLK_PCLK_UART1), ENABLE); #elif defined (USE_MDR1986VE9x) RST_CLK_PCLKcmd((RST_CLK_PCLK_PORTF | RST_CLK_PCLK_UART2), ENABLE); #elif defined (USE_MDR1901VC1T) RST_CLK_PCLKcmd((RST_CLK_PCLK_PORTF | RST_CLK_PCLK_UART3), ENABLE); #endif /* Инициализация структуры параметров порта ввода/вывода */ PORT_InitStructure.PORT_Pin = DEBUG_UART_PINS; PORT_InitStructure.PORT_FUNC = DEBUG_UART_PINS_FUNCTION; PORT_InitStructure.PORT_MODE = PORT_MODE_DIGITAL; PORT_InitStructure.PORT_SPEED = PORT_SPEED_MAXFAST; PORT_Init(DEBUG_UART_PORT, &PORT_InitStructure); UART_DeInit(DEBUG_UART); /* Инициализация структуры параметров UART */ UART_InitStructure.UART_BaudRate = DEBUG_BAUD_RATE; UART_InitStructure.UART_WordLength = UART_WordLength8b; UART_InitStructure.UART_StopBits = UART_StopBits1; UART_InitStructure.UART_Parity = UART_Parity_No; UART_InitStructure.UART_FIFOMode = UART_FIFO_ON; UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_RXE | UART_HardwareFlowControl_TXE; /* ----- Инициализация UART ----- */ UART_BRGInit(DEBUG_UART, UART_HCLKdiv1); BaudRateStatus = UART_Init(DEBUG_UART, &UART_InitStructure); if(BaudRateStatus == BaudRateValid){ UART_Cmd(DEBUG_UART,ENABLE); } else{ while(1); } printf("========System startup========\n\r"); printf("Init Debug UART ... Ok\r\n"); } Все основные параметры настройки UART берутся из файла «MDR32F9Qx_config.h», выбор МК определяется в файле «MDR32F9Qx_board.h». В случае успешной инициализации по UART будет отправлено соответствующее сообщение. Теперь перейдём к описанию функции sendchar() и getkey() для работы с UART. Они получились небольшие, листинг ниже int sendchar(int c) { UART_SendData(DEBUG_UART, (uint8_t) c); // Ожидать, пока не закончится передача while (UART_GetFlagStatus(DEBUG_UART, UART_FLAG_TXFF) == SET); return (c); } int getkey () { // Ожидать, пока не начнётся передача while (UART_GetFlagStatus(DEBUG_UART, UART_FLAG_RXFE) == SET); return ( UART_ReceiveData(DEBUG_UART) ); } Теперь осталось описать прототипы получившихся функций в заголовочном файле и подключить всё в наш проект. Пример использования printf() и scanf() для МК 1986ВЕ1Т со всеми описанными функциями можно скачать по ссылке [[#Printf через UART|в начале статьи]]. Работа программы с иcпользованием терминала Putty {{prog:debug:pritfuart:putty.png}}