Zephyr printk输出分析

1. 说明
参考的单板: stm32f429i_disc1
zephyr版本: zephyr v1.10.0

2. 编译过程追踪
cd $ZEPHYR_BASE
source ./zephyr-env.sh
cd samples/hello_world
make BOARD=stm32f429i_disc1
注意以下输出
CC  drivers/console/uart_console.o

CC  drivers/serial/uart_stm32.o

3. uart驱动
1) uart pinmux驱动加载: 参考Zephyr uart驱动及使用方法
2) uart 驱动加载: 参考Zephyr uart驱动及使用方法
3) uart console驱动加载
// uart_console.c (drivers\console)
static struct device *uart_console_dev;
/* console输出函数,调用uart进行输出,该函数将用作 _vprintk 输出回调函数,将字符输出至console  */
static int console_out(int c)
{
if ('\n' == c) {
uart_poll_out(uart_console_dev, '\r');
}
uart_poll_out(uart_console_dev, c);
return c;
}

/* 为UART控制台输出安装printk/stdout钩子 */
void uart_console_hook_install(void)
{
__stdout_hook_install(console_out);
_stdout_hook = hook; // stdout_console.c (lib\libc\minimal\source\stdout)
__printk_hook_install(console_out);
_char_out = fn;  //printk.c (misc)
}

/* 初始化一个uart用作console/debug端口  */
int uart_console_init(struct device *arg)
{
ARG_UNUSED(arg);
#ifndef CONFIG_IS_ROM
/* 获取uart设备 */
uart_console_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
#else
uart_console_dev = device_get_binding(uart_console_on_dev_name);
#endif

#if defined(CONFIG_USB_UART_CONSOLE) && defined(CONFIG_USB_UART_DTR_WAIT)
while (1) {
u32_t dtr = 0;
uart_line_ctrl_get(uart_console_dev, LINE_CTRL_DTR, &dtr);
if (dtr) {
break;
}
}
k_busy_wait(1000000);
#endif
/* 安装console钩子 */
uart_console_hook_install();
return 0;
}

#ifndef CONFIG_IS_ROM
/* UART console initializes after the UART device itself */
SYS_INIT(uart_console_init,
#if defined(CONFIG_USB_UART_CONSOLE)
APPLICATION,
#elif defined(CONFIG_EARLY_CONSOLE)
PRE_KERNEL_1,
#else
POST_KERNEL,
#endif
CONFIG_UART_CONSOLE_INIT_PRIORITY);
#endif

4. printk输出过程
// printk.c (misc)
int printk(const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = vprintk(fmt, ap);
va_end(ap);
return ret;
}

typedef int (*out_func_t)(int c, void *ctx);
static int _nop_char_out(int c)
{
ARG_UNUSED(c);
return 0;
}
static int (*_char_out)(int) = _nop_char_out;  // 在__printk_hook_install中被重新赋值为 console_out,该函数会调用 uart_poll_out

int vprintk(const char *fmt, va_list ap)
{
struct out_context ctx = { 0 };
_vprintk((out_func_t)char_out, &ctx, fmt, ap);
return ctx.count;
}

static int char_out(int c, struct out_context *ctx)
{
ctx->count++;
return _char_out(c);
}
/* 为printk安装字符输出例程,该函数在uart_console.c (drivers\console)的初始化函数中, 
_char_out被调用被重新赋值为 console_out,该函数会调用 uart_poll_out */
void __printk_hook_install(int (*fn)(int))
{
_char_out = fn;
}

5. stdout输出过程
// stdout_console.c (lib\libc\minimal\source\stdout)
// 输出过程同printk,都为先安装钩子函数,再调用该函数进行输出。
static int (*_stdout_hook)(int) = _stdout_hook_default;
/* 该函数在uart_console.c (drivers\console)的初始化函数中被调用, 
_stdout_hook被重新赋值为 console_out,该函数会调用 uart_poll_out */
void __stdout_hook_install(int (*hook)(int))  
{
_stdout_hook = hook;
}
int fputc(int c, FILE *stream)
{
return (stdout == stream) ? _stdout_hook(c) : EOF;
}
// 以下三个函数都为直接调用_stdout_hook进行输出
int fputs(const char *_MLIBC_RESTRICT string, FILE *_MLIBC_RESTRICT stream)
size_t fwrite(const void *_MLIBC_RESTRICT ptr, size_t size, size_t nitems,
  FILE *_MLIBC_RESTRICT stream)
int puts(const char *string)

猜你喜欢

转载自blog.csdn.net/u012408797/article/details/80266798