Как вы реализуете printf в GCC от Newlib?

Я изо всех сил пытаюсь правильно внедрить printf из newlib в свой esp32, используя GCC.

Я просмотрел документацию newlib, и она дает мне общую информацию о том, как вызывается printf, но не объясняет мне внутреннюю реализацию.

Основываясь на моем текущем исследовании, я определил, что printf выводит форматированную строку в STDOUT. На ПК мне это было проще понять, потому что есть окно консоли, в котором будет отображаться форматированный вывод из printf, однако во встроенной системе я понимаю, что вы должны указать библиотеке, куда перенаправить форматированный вывод printf, и вот что Я пытаюсь выяснить.

Опять же, основываясь на своих исследованиях, я пришел к выводу, что для этого требуются некоторые функции, в частности функция _write.

Мне очень сложно преодолеть разрыв между printf и использованием функции _write. Я надеюсь, что кто-то здесь может помочь мне понять, как правильно реализовать printf.

И если я пропустил какую-то документацию, которая ясно объясняет это, то, пожалуйста, перенаправьте меня на нее. Я пробовал читать документацию по newlib, а также документацию, связанную с GCC, но на самом деле нигде не упоминается, как использовать printf, но есть много документации о том, как вызывать printf и форматировать строку, но эта часть проста. Мне нужно знать, как получить отформатированную строку из STDOUT MCU.

Спасибо всем!


person cDreamer    schedule 06.03.2019    source источник
comment
Вы можете найти реализации printf() и его родственников большей или меньшей сложности в Интернете. Если вы найдете int printf(const char * restrict fmt, ...) { va_list args; va_start(args, fmt); int rc = vfprintf(stdout, fmt, args); va_end(args); return rc; }, то у вас есть функция реле, и вместо этого вам нужно найти реализацию vfprintf(). Но и это доступно…   -  person Jonathan Leffler    schedule 06.03.2019
comment
@JonathanLeffler: за исключением случаев, когда используется Newlib, уже существует реализация printf(). Я думаю, что вопрос должен быть о том, как перенастроить Newlib так, чтобы ее printf() работало, а не о том, как реализовать printf   -  person Clifford    schedule 06.03.2019


Ответы (1)


В Newlib вы не реализуете printf(), включенный в библиотеку. Вы просто реализуете минимальный набор системных вызовов для поддержки библиотеки. API системных вызовов потокового устройства состоит из open, close, read и write (или реентерабельных версий с суффиксом _r). вступительные требования).

Если вы реализуете только stdout (поток, используемый printf(), putchar(), puts() и т. д.) и поддерживаете только одно устройство (обычно UART) и не беспокоитесь о возможности перенаправления или повторного входа, тогда open, close и read может быть пустым, а write может просто вывести предоставленный буфер непосредственно в ваш низкоуровневый API последовательного ввода-вывода:

int _write(int handle, char *data, int size ) 
{
    int count ;

    handle = handle ; // unused

    for( count = 0; count < size; count++) 
    {
        outputByte( data[count] ) ;  // Your low-level output function here.
    }

    return count;
}

Обратите внимание, что handle здесь не используется. Для stdout будет 1 (stdin = 0 и stderr = 2). Аргумент handle будет использоваться, если вам нужны отдельные устройства вывода для stdout и stderr или если вы поддерживаете дополнительные устройства или файловую систему и перенаправление fopen или stdout. Он используется для идентификации пара, открытого open. Игнорируя это, весь вывод потока (например, fprintf() будет обрабатываться одинаково и выводиться на одно и то же устройство); во многих случаях (где printf() - это просто средство получения отладочного вывода, или ваше приложение не имеет файловой системы, которая вас не волнует.

Учитывая эту функцию write, printf() будет "просто работать" (самым простым способом), потому что под капотом все функции вывода stdio вызывают write). Рекомендуется функция низкоуровневого вывода, буферизованная и неблокируемая (например, драйвер UART, управляемый прерываниями).

Очевидно, что если вы хотите принимать ввод и для stdin, вы должны реализовать аналогичную функцию read.

Если вам нужна куча (malloc() и т. д.), вам также потребуется реализовать sbrk/sbrk_r. Я бы посоветовал вам, по крайней мере, реализовать это, если ничего другого в системных вызовах Newlib.

Билл Гейтлифф обсуждает более сложную реализацию системных вызовов в Перенос и использование Newlib во встроенных системах, а базовая реализация обсуждается на странице здесь, а примеры заготовок с минимальной реализацией, аналогичные приведенным выше, приведены в документация Newlib.

person Clifford    schedule 06.03.2019