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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章