[i] Printf через UART
В статье Printf через ITM был рассмотрен способ вывода информации стандартного потока ввода/вывода stdio в ПК с помощью отладчика по интерфейсу ITM. Однако, такой подход применим только для МК с ядром Cortex M3 и выше.
МК К1986ВЕ1QI с RISC-ядром, функционально аналогичным Cortex M1, блока отладки ITM нет, поэтому вывести отладочную информацию с помощью отладчика нельзя. В данной статье рассмотрим, как реализовать ввод/вывод сообщений с помощью интерфейса UART. Получившийся пример программы с использованием ввода/вывода через UART для МК К1986ВЕ1QI можно скачать по ссылке в конце статьи.
Стандартные функции ввод/вывод
Ввод и вывод информации осуществляется через функции стандартной библиотеки. Прототипы рассматриваемых функций находятся в файле stdio.h. Эта библиотека содержит функции:
- printf() — для вывода информации;
- scanf() — для ввода информации.
Чтобы понять, как настроить ввод/вывод информации рассмотрим подробнее данные функции. Структура стандартных функций ввода/вывода в Keil показана на рисунке 1.
Рисунок 1 - Структура стандартных функций ввода/вывода в Keil
Функции, которые доступны пользователю, находятся в самом верху данной структуры и называются высокоуровневыми (High-Level Functions). К таким функциям как раз и относятся printf() и scanf(). При их вызове обработка вводимой информации реализуется с помощью вызова низкоуровневых функций (Low-Level Functions), к ним относятся такие функции как fputc() и fgetc(). Данные функции, в свою очередь, вызывают системные функции, которые напрямую работают с периферией МК (System I/O Functions), например, с UART или CAN, перенаправляя на них поток данных. Для ввода/вывода информации по UART необходимо будет реализовать эти системные функции.
Для встраивания системных функций в структуру стандартных функций ввода/вывода Keil предоставляет шаблон специального файла 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 в различных МК (некоторые стандартные примеры его успешно используют), который содержится в файле «MDR32FxQI_config.h» и активируется с помощью макроопределения _USE_DEBUG_UART_. Этими параметрами мы воспользуемся при написании функции инициализации интерфейса UART.
Листинг функции инициализации UART приведён в фрагменте кода 1.
Фрагмент кода 1 - Функция инициализации UART
void DebugUARTInit()
{
UART_InitTypeDef UART_InitStructure;
PORT_InitTypeDef PORT_InitStructure;
uint32_t BaudRateStatus;
#if defined (USE_MDR32F1QI)
RST_CLK_PCLKcmd((RST_CLK_PCLK_PORTC | RST_CLK_PCLK_UART1), ENABLE);
#elif defined (USE_MDR32F9Q2I)
RST_CLK_PCLKcmd((RST_CLK_PCLK_PORTF | RST_CLK_PCLK_UART2), ENABLE);
#elif defined (USE_MDR32FG16S1QI)
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, а также выбор МК, определяются в файле «MDR32FxQI_config.h». В случае успешной инициализации по UART будет отправлено соответствующее сообщение.
Теперь перейдём к описанию функции sendchar() и getkey() для работы с UART. Листинг данных функций приведён в фрагменте кода 2.
Фрагмент кода 2 - Функции 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) );
}
Теперь осталось описать прототипы получившихся функций в заголовочном файле и подключить всё в проект.
Работа программы с иcпользованием терминала Putty приведена на рисунке 2.
Рисунок 2 - Вывод отладочной информации в терминал Putty через UART
Сохранить статью в PDF