详解U-Boot中printf函数的实现

本文详细解析了U-Boot环境下printf函数的实现机制,包括fputc、putc、serial_printf、vprintf、printf等函数的调用关系及工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

详解U-Boot中printf函数的实现

一、printf函数调用关系


1.1fputc和srial_putc的关系

/* * Output a single byte to the serial port. */void serial_putc (const char c)//发送数据{ S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);#ifdef CONFIG_MODEM_SUPPORT if (be_quiet)  return;#endif /* wait for room in the tx FIFO */ while (!(uart->UTRSTAT & 0x2));#ifdef CONFIG_HWFLOW /* Wait for CTS up */ while(hwflow && !(uart->UMSTAT & 0x1))  ;#endif uart->UTXH = c; /* If \n, also do \r */ if (c == '\n')  serial_putc ('\r');}
serial_putc函数是直接和控制相关的,通过UTXH寄存器发送数据。

void fputc (int file, const char c){ if (file < MAX_FILES)  stdio_devices[file]->putc (c);}

这是在console_init_r中设置stdio_devices[]后才有的,其他的是类似的。

1.2putc和fputc的关系

void putc (const char c){#ifdef CONFIG_SILENT_CONSOLE if (gd->flags & GD_FLG_SILENT)  return;#endif if (gd->flags & GD_FLG_DEVINIT) {  /* Send to the standard output */  fputc (stdout, c); } else {  /* Send directly to the handler */  serial_putc (c); }}

这是console_init_r中设置gd->flags & GD_FLG_DEVINIT,也就是串口设备完全初始化之后才有这种关系,其他的函数是类似的。

二、serial_printf (const char *fmt, ...)

 serial_printf函数是通过一个宏va_start把所有的可变参数放到了由args指向的一块内存中,vsprintf将变参列表args中的变量按照fmt中规定的格式保存到临时缓冲printbuffer中,最后调用serial_puts函数将临时缓冲中的字符串数据打印到终端中去。
void serial_printf (const char *fmt, ...){ va_list args; uint i; char printbuffer[CFG_PBSIZE]; va_start (args, fmt); /* For this to work, printbuffer must be larger than  * anything we ever want to print.  */ i = vsprintf (printbuffer, fmt, args); va_end (args); serial_puts (printbuffer);}

三、fprintf(int file, const char *fmt, ...)

 fprintf函数是通过一个宏va_start把所有的可变参数放到了由args指向的一块内存中,vsprintf将变参列表args中的变量按照fmt中规定的格式保存到临时缓冲printbuffer中,最后调用fputs函数将临时缓冲中的字符串数据打印到终端中去。

void fprintf (int file, const char *fmt, ...){ va_list args; uint i; char printbuffer[CFG_PBSIZE]; va_start (args, fmt); /* For this to work, printbuffer must be larger than  * anything we ever want to print.  */ i = vsprintf (printbuffer, fmt, args); va_end (args); /* Send to desired file */ fputs (file, printbuffer);}

四、vprintf(const char *fmt, va_list args)

 vprintf函数是通过一个宏va_start把所有的可变参数放到了由args指向的一块内存中,vsprintf将变参列表args中的变量按照fmt中规定的格式保存到临时缓冲printbuffer中,最后调用puts函数将临时缓冲中的字符串数据打印到终端中去。

void vprintf (const char *fmt, va_list args){ uint i; char printbuffer[CFG_PBSIZE]; /* For this to work, printbuffer must be larger than  * anything we ever want to print.  */ i = vsprintf (printbuffer, fmt, args); /* Print the string */ puts (printbuffer);}

五、printf(const char *fmt, ...)

 printf函数是通过一个宏va_start把所有的可变参数放到了由args指向的一块内存中,vsprintf将变参列表args中的变量按照fmt中规定的格式保存到临时缓冲printbuffer中,最后调用puts函数将临时缓冲中的字符串数据打印到终端中去。

void printf (const char *fmt, ...){ va_list args; uint i; char printbuffer[CFG_PBSIZE]; va_start (args, fmt); /* For this to work, printbuffer must be larger than  * anything we ever want to print.  */ i = vsprintf (printbuffer, fmt, args); va_end (args); /* Print the string */ puts (printbuffer);}

六、小结

 serial_printf、vprintf和pringf函数基本相同,都是通过一个宏va_start把所有的可变参数放到了由args指向的一块内存中,vsprintf将变参列表args中的变量按照fmt中规定的格式保存到临时缓冲printbuffer中,最后调用不同的串口输出函数将临时缓冲中的字符串数据打印到终端中去。下面我们就来分析一下va_start、va_eng和vsprintf。

           

给我老师的人工智能教程打call!http://blog.youkuaiyun.com/jiangjunshow
这里写图片描述
你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block var foo = 'bar'; 

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t &ThinSpace; . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

gantt
        dateFormat  YYYY-MM-DD
        title Adding GANTT diagram functionality to mermaid
        section 现有任务
        已完成               :done,    des1, 2014-01-06,2014-01-08
        进行中               :active,  des2, 2014-01-09, 3d
        计划一               :         des3, after des2, 5d
        计划二               :         des4, after des3, 5d
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

<think>好的,我现在需要回答用户关于U-Boot中spi_flash_probe()函数的问题。首先,用户之前询问过如何在U-Boot中读取W25Q64BV的Unique ID,现在他们想了解这个函数的具体细节。 我应该先回顾一下之前的对话,确保回答的一致性。用户可能已经对U-Boot的SPI Flash驱动有一定了解,因此需要详细但不过于基础的解释。 首先,确定spi_flash_probe()的作用。这个函数用于探测并初始化SPI Flash设备,返回一个struct spi_flash指针。需要解释它的参数:总线号、片选号、最大频率和SPI模式。例如,在之前的示例中,用户使用了spi_flash_probe(0, 0, 1000000, SPI_MODE_3),我需要解释每个参数的意义。 然后,需要说明函数的工作流程:SPI总线初始化、设备探测、返回句柄。可能还要提到它内部如何调用厂商特定的驱动,比如Winbond的W25Q64BV。 用户可能想知道如何使用这个函数,以及常见的问题,比如探测失败的原因,如配置错误、硬件连接问题或频率设置不当。在之前的回答中提到了调试关键点,这里可以呼应一下,比如检查SPI模式、频率和CS引脚。 此外,是否需要给出示例代码?用户之前的示例中调用了这个函数,可能可以再次引用,但需要避免重复,而是补充更多细节。例如,解释如何根据硬件调整参数,或者处理不同的SPI控制器。 还应该提到返回值的处理,如果返回NULL如何处理错误,比如打印错误信息并返回。可能还需要指出在U-Boot命令行中使用sf命令时,这个函数可能已经被封装,但自定义代码中需要显式调用。 还要注意U-Boot的配置选项,比如CONFIG_CMD_SF和CONFIG_SPI_FLASH_WINBOND是否启用,这些在之前的回答中已经涉及,这里可能需要简要提及以确保驱动支持。 最后,是否需要对比其他函数,比如spi_get_bus_and_cs(),或者说明spi_flash_probe()的高级用法?可能用户不需要太深入,但简要说明其在整个SPI Flash操作中的角色是有帮助的。 总结,回答结构需要包括函数作用、参数详解、工作流程、示例代码、常见问题及处理,以及相关配置注意事项。需要保持清晰,分点说明,帮助用户逐步理解。</think>--- ### **`spi_flash_probe()` 函数详解** `spi_flash_probe()` 是 U-Boot 中用于初始化和访问 SPI Flash 设备的核心函数,其功能是探测并建立与 SPI Flash 的通信通道,返回一个操作句柄。以下是其详细说明: --- #### **1. 函数原型** ```c struct spi_flash *spi_flash_probe( unsigned int bus, // SPI 总线编号(如 0, 1) unsigned int cs, // SPI 片选信号编号(如 0, 1) unsigned int max_hz, // 最大通信频率(单位:Hz) unsigned int spi_mode // SPI 模式(如 SPI_MODE_0~3) ); ``` --- #### **2. 参数详解** - **`bus`** SPI 控制器总线编号,取决于硬件设计。例如,单 SPI 控制器通常为 `0`,多总线系统需根据电路图确定。 - **`cs`** 片选信号编号,对应 Flash 芯片连接的 SPI 控制器的片选引脚(如 CS0、CS1)。例如,若 Flash 接在控制器的 CS0,则 `cs=0`。 - **`max_hz`** SPI 通信的最大时钟频率。需参考 Flash 芯片数据手册设置(如 W25Q64BV 最高支持 104MHz)。 **调试建议**:初始调试时可降低频率(如 1MHz)以确保稳定性。 - **`spi_mode`** SPI 传输模式,定义时钟极性和相位,需与 Flash 芯片匹配: - `SPI_MODE_0`:CPOL=0,CPHA=0 - `SPI_MODE_3`:CPOL=1,CPHA=1(W25Q64BV 常用此模式) **注意**:模式错误会导致数据读写失败。 --- #### **3. 返回值** - **成功**:返回 `struct spi_flash*` 指针,包含 Flash 的容量、厂商、操作函数等元数据。 - **失败**:返回 `NULL`,可能原因包括: - SPI 总线或片选配置错误 - Flash 芯片未响应(硬件连接问题) - 驱动未启用(如未配置 `CONFIG_SPI_FLASH_WINBOND`) --- #### **4. 内部工作流程** 1. **SPI 总线初始化** 根据 `bus` 和 `cs` 绑定 SPI 控制器和片选引脚。 2. **Flash 探测** 发送 JEDEC ID 命令(`0x9F`)读取厂商和容量信息,匹配驱动支持的芯片列表。 3. **驱动绑定** 调用厂商专用驱动(如 `spi_flash_w25q64bv_init()`)初始化读写擦除函数指针。 --- #### **5. 使用示例** ```c // 初始化 SPI Flash(总线0,片选0,频率1MHz,SPI模式3) struct spi_flash *flash = spi_flash_probe(0, 0, 1000000, SPI_MODE_3); if (!flash) { printf("Error: SPI Flash not detected!\n"); return -1; } // 读取 Flash 信息 printf("Manufacturer ID: 0x%02X\n", flash->manufacturer_id); printf("Memory Size: %d MiB\n", flash->size >> 20); // 转换为 MiB ``` --- #### **6. 关键依赖配置** 在 U-Boot 的配置文件(如 `include/configs/board_name.h`)中需启用以下选项: ```c #define CONFIG_SPI // 启用 SPI 子系统 #define CONFIG_CMD_SF // 启用 SPI Flash 命令(如 sf read/write) #define CONFIG_SPI_FLASH_WINBOND // 启用 Winbond 芯片驱动 ``` --- #### **7. 常见问题与调试** 1. **探测失败** - 检查硬件连接(CS、CLK、MOSI、MISO 是否短路/断路)。 - 确认 `spi_mode` 和 `max_hz` 与 Flash 芯片匹配。 - 使用示波器或逻辑分析仪捕获 SPI 波形,验证时序。 2. **读写异常** - 确保 `struct spi_flash` 中的 `read()/write()` 函数已正确绑定。 - 检查 Flash 的写保护(WP)引脚是否被拉高。 --- #### **8. 扩展应用** - **多 Flash 芯片支持**:通过不同 `bus` 和 `cs` 参数初始化多个设备。 - **自定义驱动**:若 Flash 芯片未在 U-Boot 驱动列表中,需实现 `spi_flash_ops` 结构体并注册。 --- 通过 `spi_flash_probe()`,开发者可以快速集成 SPI Flash 功能,为固件更新、环境变量存储等场景提供基础支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值