В Newlib вы не реализуете printf()
, который включен в библиотеку. Вы просто реализуете минимальный набор системных вызовов для поддержки библиотеки. API sycalls потокового устройства включает в себя open
, close
, read
и write
(или реентерабельные версии с суффиксом _r
) - реентерабельность в этом полезна, если вы используете многопоточность и вам требуется на поток errno
(среди любых требований повторного входа для конкретной реализации).
Если все, что вы реализуете, это 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 .