Я программирую на ручном микропроцессоре и пытаюсь выполнить отладку, используя операторы печати через UART. Я не хочу добавлять stdlibs
только для отладки. Есть ли способ печати на консоли без stdio.h
/iostream.h
? Могу ли я написать свой собственный printf()
?
В качестве альтернативы я могу сделать это с помощью контроллера DMA и напрямую писать в UART. Однако я бы хотел избежать этого. Используя встроенную тестовую функцию «эхо» или
«удаленный шлейф» Я знаю, что UART настроен правильно.
Короткий ответ: Да, вполне возможно сделать оба ваших решения.
Функция printf довольно сложна, если вы хотите поддерживать все типы данных и форматы. Но это не так сложно написать что-то, что может выводить строку или целое число в нескольких разных базах (большинству людей нужны только десятичные и шестнадцатеричные, но восьмеричные, вероятно, только добавляют еще 3-4 строки кода, если у вас есть десятичные и шестнадцатеричные).
Обычно printf пишется так:
int printf(const char *fmt, ...)
{
int ret;
va_list args;
va_start(args, fmt)
ret = do_xprintf(outputfunc, NULL, fmt, args);
va_end(args);
return ret;
}
А потом do_xprintf()
выполняет всю тяжелую работу для всех вариантов (printf, sprintf, fprintf и т. д.)
int do_xprintf(void (*outputfunc)(void *extra, char c), void *extra, const char *fmt, va_list args)
{
char *ptr = fmt;
while(1)
{
char c = *ptr++;
if (c == '%')
{
c = *ptr++; // Get next character from format string.
switch(c)
{
case 's':
char *str = va_arg(args, const char *);
while(*str)
{
count++;
outputfunc(extra, *str);
str++;
}
break;
case 'x':
base = 16;
goto output_number;
case 'd':
base = 10;
output_number:
int i = va_arg(args, int);
// magical code to output 'i' in 'base'.
break;
default:
count++;
outputfunc(extra, c);
break;
}
else
count++;
outputfunc(extra, c);
}
return count;
}
Теперь все, что вам нужно сделать, это заполнить несколько битов из приведенного выше кода и записать outputfunc (), который выводит на ваш последовательный порт.
Обратите внимание, что это грубый набросок, и я уверен, что в коде есть некоторые ошибки — и если вы хотите поддерживать с плавающей точкой или «ширины», вам придется работать над этим немного больше …
(Обратите внимание на дополнительный параметр — для вывода на FILE *
это будет указатель файла, для sprintf
Вы можете передать структуру для буфера и положение в буфере, или что-то в этом роде)
Понятие «консоль» не имеет большого значения вне контекста конкретной системы, которую вы используете. Вообще во встроенной программе нет реальной концепции консоли.
То, что вы ищете, — это способ получить данные из вашей системы. Если вы хотите использовать UART и не используете высокоуровневую ОС, такую как GNU / linux, вам нужно написать свои собственные драйверы ввода / вывода. Как правило, это означает, что сначала нужно настроить UART для требуемого управления скоростью потока / четности / потока посредством записи в регистр. Для любого типа надежного ввода-вывода вы захотите, чтобы он управлялся прерываниями, поэтому вам нужно будет написать ISR для tx и rx, которые используют циклические буферы.
После того, как вы это сделаете, вы можете написать свой собственный printf, как указано Mats.
Я обнаружил, что для фоновой отладки мой метод выбора заключается в постановке символов в циклический буфер, который затем сливается с помощью процедуры опроса в регистре передачи UART.
Процедуры постановки в очередь основаны на символе, строке и переменном размере (в шестнадцатеричном или фиксированном десятичном формате). И процедура буфера делюкс может указывать на переполнение зарезервированным символом.
Этот подход имеет минимальные издержки / влияние на целевую операцию, его можно использовать (с осторожностью) в процедуре прерывания, и идея легко переносится, поэтому я проигнорировал отладчик на всех системах, которые я использовал.
Поскольку распечатка информации через последовательный порт во встроенной системе изменяет время основной программы, лучшее решение, которое я нашел, — это отправить небольшое сообщение, закодированное в 2 байта (иногда 1 байт работает нормально), а затем использовать программу в ПК для декодирования этих сообщений и предоставления необходимой информации, которая может включать статистику и все, что вам может понадобиться.
Таким образом, я добавляю немного служебной информации в основную программу и позволяю ПК выполнять тяжелую работу по обработке сообщений.
Может быть, что-то вроде этого:
1-байтовое сообщение: биты 7: 4 = идентификатор модуля, биты 3: 0 = информация об отладке.
2 байта сообщения: биты 15:12 = идентификатор модуля, биты 11: 8 = информация об отладке, биты 7: 0 = данные.
Затем в программном обеспечении для ПК необходимо объявить таблицу с сообщениями, которые сопоставляются с данной парой идентификатора модуля / отладочной информации, и использовать их для вывода на экран.
Может быть, она не такая гибкая, как функция pseudo-printf, поскольку для декодирования вам необходим фиксированный набор сообщений на ПК, но это не приводит к дополнительным накладным расходам, как я уже упоминал ранее.
Надеюсь, поможет.
Фернандо