当u-boot的启动执行到_main函数处,将在_main函数中执行板级的前初始化和后初始化操作,即函数board_init_f和board_init_r。
串口的初始化以及相关stdio、console操作穿插在这两个函数的执行过程中。下面将分别详细讨论这两阶段中涉及的串口及stdio、
console设备操作。
这里所使用的u-boot版本为2015.7,硬件为I.MX6 boundary nitrogen6q开发平台。
一.board_init_f阶段串口操作相关函数
下面以执行顺序来逐个分析board_init_f阶段所涉及的函数:
1.init_baud_rate
文件common/board_f.c中的函数init_baud_rate首先尝试从环境变量中获取波特率的设定值,如果找不到波特率相关的环境变量,
则使用默认的波特率配置115200。
最后将环境变量中波特率设定值或波特率默认值赋值给全局变量gd->baudrate。
gd->flags |= GD_FLG_SERIAL_READY
GD_FLG_SERIAL_READY为串口可用标志。然后调用get_current()->start()。
get_current是静态函数,定义如下:
标志GD_FLG_RELOC在后续的board_r阶段初始化函数initr_reloc中设置,gd->flags相应的标志位此时还未置标,则将执行
dev=default_serial_console();
default_serial_console函数在drivers/serial/serial_mxc.c中的定义为: 而其前变量mxc_serial_drv定义时被初始化为: 那么,get_current()->start()中的get_current()调用将返回上述结构体的地址,即默认的串口设备指针。
接下来就执行mxc_serial_drv.start函数,即mxc_serial_init。
mxc_serial_init主要执行SOC层 i.mx芯片串口硬件配置的初始化操作:使能串口模块时钟,设置波特率和数据位长度等。
注意,UART的IOMUX设置已在前面的函数board_early_init_f中完成,详见该函数说明。
我们跟踪common/console.c中的printf可以看到: gd->have_console在上面的函数console_init_f中被置为1,那么接下来就可以使用puts函数进行信息的输出,
我们继续跟踪puts,其在common/console.c中实现为: 此时gd->flags中的GD_FLG_DEVINIT还未置标,那么puts函数将使用drivers/serial/serial.c中的serial_puts(s)
进行数据信息输出: 这样又回到了和上面2小节相仿的get_current()函数,那么,最终将使用mxc_serial_drv结构体中的成员函数puts进行信息输出。
综上所述,结构体struct serial_device是在一般serial层中对串口设备的描述,可以看做串口设备的抽象,struct serial_device对下层,即SOC层的串口属性(如名称)和设备操作函数进行了封装。
定义一个struct serial_device变量,并填充结构体中的操作函数和属性,则代表一个具体的串口设备在serial层中的实现。
在serial层,提供了诸如serial_puts,serial_getc的外部访问接口。而这些函数都将调用其私有get_current函数获取一个serial的实例。并通过调用该serial设备的操作函数puts,getc,最终定位到最底层的SOC serial操作实现。
SOC serial操作函数对struct serial_device结构体相应成员变量的填充,可看作是SOC serial到serial的注册。
我们知道标准输出的printf函数是包括在控制台(console)的stdio设备中, 在board_init_f阶段中,stdio设备并未准备好,也未完成控制台注册,那么,作为替代,这里提供了一种非标准输入输出函数printf(其实是调用了上述的serial_puts)的实现。此后的很长一段代码中将使用这里的printf执行流程完成信息输出,直至stdio设备注册到console中。
串口的初始化以及相关stdio、console操作穿插在这两个函数的执行过程中。下面将分别详细讨论这两阶段中涉及的串口及stdio、
console设备操作。
这里所使用的u-boot版本为2015.7,硬件为I.MX6 boundary nitrogen6q开发平台。
一.board_init_f阶段串口操作相关函数
下面以执行顺序来逐个分析board_init_f阶段所涉及的函数:
1.init_baud_rate
文件common/board_f.c中的函数init_baud_rate首先尝试从环境变量中获取波特率的设定值,如果找不到波特率相关的环境变量,
则使用默认的波特率配置115200。
最后将环境变量中波特率设定值或波特率默认值赋值给全局变量gd->baudrate。
2.serial_init
该函数在文件drivers/serial/serial.c中定义,先执行gd->flags |= GD_FLG_SERIAL_READY
GD_FLG_SERIAL_READY为串口可用标志。然后调用get_current()->start()。
get_current是静态函数,定义如下:
标志GD_FLG_RELOC在后续的board_r阶段初始化函数initr_reloc中设置,gd->flags相应的标志位此时还未置标,则将执行
dev=default_serial_console();
default_serial_console函数在drivers/serial/serial_mxc.c中的定义为: 而其前变量mxc_serial_drv定义时被初始化为: 那么,get_current()->start()中的get_current()调用将返回上述结构体的地址,即默认的串口设备指针。
接下来就执行mxc_serial_drv.start函数,即mxc_serial_init。
mxc_serial_init主要执行SOC层 i.mx芯片串口硬件配置的初始化操作:使能串口模块时钟,设置波特率和数据位长度等。
注意,UART的IOMUX设置已在前面的函数board_early_init_f中完成,详见该函数说明。
3.console_init_f
该函数在console层的文件common/console.c中实现:
我们跟踪common/console.c中的printf可以看到: gd->have_console在上面的函数console_init_f中被置为1,那么接下来就可以使用puts函数进行信息的输出,
我们继续跟踪puts,其在common/console.c中实现为: 此时gd->flags中的GD_FLG_DEVINIT还未置标,那么puts函数将使用drivers/serial/serial.c中的serial_puts(s)
进行数据信息输出: 这样又回到了和上面2小节相仿的get_current()函数,那么,最终将使用mxc_serial_drv结构体中的成员函数puts进行信息输出。
综上所述,结构体struct serial_device是在一般serial层中对串口设备的描述,可以看做串口设备的抽象,struct serial_device对下层,即SOC层的串口属性(如名称)和设备操作函数进行了封装。
定义一个struct serial_device变量,并填充结构体中的操作函数和属性,则代表一个具体的串口设备在serial层中的实现。
在serial层,提供了诸如serial_puts,serial_getc的外部访问接口。而这些函数都将调用其私有get_current函数获取一个serial的实例。并通过调用该serial设备的操作函数puts,getc,最终定位到最底层的SOC serial操作实现。
SOC serial操作函数对struct serial_device结构体相应成员变量的填充,可看作是SOC serial到serial的注册。
我们知道标准输出的printf函数是包括在控制台(console)的stdio设备中, 在board_init_f阶段中,stdio设备并未准备好,也未完成控制台注册,那么,作为替代,这里提供了一种非标准输入输出函数printf(其实是调用了上述的serial_puts)的实现。此后的很长一段代码中将使用这里的printf执行流程完成信息输出,直至stdio设备注册到console中。