24468

Printf через UART

Дата последнего изменения: 06.08.2021 13:13:46

В статье Printf через ITM был рассмотрен способ вывода информации стандартного потока ввода/вывода stdio в ПК с помощью отладчика по интерфейсу ITM. Однако, такой подход применим только для МК с ядром Cortex M3 и выше.

МК на Cortex M0 и M1, такие как 1986ВЕ1Т, 1986ВЕВ3Т, блока отладки ITM не имеют, поэтому вывести отладочную информацию с помощью отладчика нельзя. В данной статье рассмотрим, как реализовать ввод/вывод сообщений с помощью интерфейса UART. Получившийся пример программы с использованием ввода/вывода через UART для МК 1986ВЕ1Т можно скачать по ссылке в конце статьи.

Стандартные функции ввод/вывод

Ввод и вывод информации осуществляется через функции стандартной библиотеки. Прототипы рассматриваемых функций находятся в файле 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 в различных МК (некоторые стандартные примеры его успешно используют), который содержится в файле «MDR32F9Qx_config.h» и активируется с помощью макроопределения _USE_DEBUG_UART_. Этими параметрами мы воспользуемся при написании функции инициализации интерфейса UART.

После установки Pack'a файл «MDR32F9Qx_config.h» по умолчанию расположен по пути "Диск:\Keil\ARM\PACK\Keil\MDR1986BExx\1.5\Config". Если макроопределение _USE_DEBUG_UART_ не раскомментировать, то проект будет собираться с ошибками.

Листинг функции инициализации UART приведён в фрагменте кода 1.

Фрагмент кода 1 - Функция инициализации 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. Листинг данных функций приведён в фрагменте кода 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

Файлы для скачивания

Теги

Была ли статья полезной?