1. 移植背景与意义1.1 为什么需要移植 printk
在嵌入式开发中,`printk` 是最常用的调试和日志输出工具。在 Zephyr RTOS 中:标准 printk 默认行为:printk 默认将输出发送到系统日志(syslog),需要通过其他机制(如 RTT、UART)才能实际看到输出串口输出需求:在嵌入式调试场景中,最常用的方式是通过 UART 串口将调试信息输出到 PC 终端DAPLink 串口支持:用户的调试器 DAPLink 已连接到 AT32F423 的 USART1,因此需要将 printk 输出重定向到 USART1
1.2 移植的意义
1. 简化调试:无需添加额外的调试库,直接使用标准的 `printk()` 函数输出调试信息2. 标准化接口:与 Linux/Zephyr 生态系统保持一致的调试接口3. 易于使用:开发者只需包含 `<zephyr/sys/printk.h>` 头文件即可使用,无需了解底层实现4. 代码复用:未来的 Zephyr 项目可以直接复用此 UART 驱动
2. 移植方案2.1 实现原理Zephyr 提供了 `__printk_hook_install()` 函数,用于安装自定义的字符输出回调。通过该机制,可以将 printk 的输出重定向到任意字符设备。
核心实现思路:1. 实现 `poll_out` 函数用于单字符输出2. 实现 `putchar` 函数作为 printk 钩子,处理换行符(\n → \r\n)3. 在 UART 驱动初始化时安装钩子
2.2 驱动文件文件位置: `src/drivers/uart_at32.c`关键代码段:
3.3 串口参数波特率:115200数据位:8 停止位:1校验位:无流控制:无 4. 使用方法 4.1 基础使用
在代码中直接使用 `printk` 函数:
4.3 注意事项
1. 换行符自动处理:驱动程序已自动处理 `\n` 到 `\r\n` 的转换,无需手动添加 `\r`2. 设备就绪检查:`putchar` 函数中已包含设备就绪检查,确保安全调用3. 性能考虑:printk 采用轮询方式输出,在中断上下文或实时性要求高的场景需注意
5. 文件结构
Q1: printk 输出没有反应
检查项:1. 确认 CONFIG_PRINTK=y 已配置2. 确认硬件连接正确(PA9 → DAPLink RX)3. 确认 PC 端串口参数正确(115200, 8N1)4. 检查是否有其他错误导致程序未正常运行
Q2: 输出乱码
检查项:1. 确认波特率匹配(115200)2. 确认串口终端编码为 UTF-8 或 ASCII
Q3: 如何关闭 printk 输出
在 `prj.conf` 中设置:
在嵌入式开发中,`printk` 是最常用的调试和日志输出工具。在 Zephyr RTOS 中:标准 printk 默认行为:printk 默认将输出发送到系统日志(syslog),需要通过其他机制(如 RTT、UART)才能实际看到输出串口输出需求:在嵌入式调试场景中,最常用的方式是通过 UART 串口将调试信息输出到 PC 终端DAPLink 串口支持:用户的调试器 DAPLink 已连接到 AT32F423 的 USART1,因此需要将 printk 输出重定向到 USART1
1.2 移植的意义
1. 简化调试:无需添加额外的调试库,直接使用标准的 `printk()` 函数输出调试信息2. 标准化接口:与 Linux/Zephyr 生态系统保持一致的调试接口3. 易于使用:开发者只需包含 `<zephyr/sys/printk.h>` 头文件即可使用,无需了解底层实现4. 代码复用:未来的 Zephyr 项目可以直接复用此 UART 驱动
2. 移植方案2.1 实现原理Zephyr 提供了 `__printk_hook_install()` 函数,用于安装自定义的字符输出回调。通过该机制,可以将 printk 的输出重定向到任意字符设备。
核心实现思路:1. 实现 `poll_out` 函数用于单字符输出2. 实现 `putchar` 函数作为 printk 钩子,处理换行符(\n → \r\n)3. 在 UART 驱动初始化时安装钩子
2.2 驱动文件文件位置: `src/drivers/uart_at32.c`关键代码段:
/* printk putchar 函数 - 用于控制台输出 */
static int at32_uart_putchar(int c)
{
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(usart1));
if (!device_is_ready(dev)) {
return c;
}
/* 处理换行符:\n → \r\n */
if (c == '\n') {
uart_poll_out(dev, '\r');
}
uart_poll_out(dev, c);
return c;
}
static int at32_uart_init(const struct device *dev)
{
// ... UART 初始化代码 ...
/* 安装 printk 钩子 */
__printk_hook_install(at32_uart_putchar);
return 0;
}3. 配置说明 3.1 必须配置在 `prj.conf` 中添加以下配置:# 启用 printk 支持 CONFIG_PRINTK=y # 启用控制台输出 CONFIG_CONSOLE=y # 启用 UART 控制台 CONFIG_UART_CONSOLE=y # 启用串口驱动 CONFIG_SERIAL=y3.2 硬件连接
| 信号 | MCU 引脚 | 说明 |
| TX | PA9 | USART1 发送脚,连接到 DAPLink 的 RX |
在代码中直接使用 `printk` 函数:
#include <zephyr/sys/printk.h>
int main(void)
{
printk("AT32F423 Demo Start\n");
int value = 42;
printk("Value = %d\n", value);
printk("Hex: 0x%08x\n", value);
return 0;
} 4.2 支持的格式说明符| 格式符 | 说明 | 示例 |
| %d | 十进制整数 | printk("%d\n", 123); |
| %u | 无符号十进制 | printk("%u\n", 456); |
| %x | 十六进制(小写) | printk("%x\n", 255); |
| %X | 十六进制(大写) | printk("%X\n", 255); |
| %p | 指针 | printk("%p\n", ptr); |
| %c | 字符 | printk("%c\n", 'A'); |
| %s | 字符串 | printk("%s\n", "hello"); |
| %f | 浮点数 | printk("%f\n", 3.14); |
1. 换行符自动处理:驱动程序已自动处理 `\n` 到 `\r\n` 的转换,无需手动添加 `\r`2. 设备就绪检查:`putchar` 函数中已包含设备就绪检查,确保安全调用3. 性能考虑:printk 采用轮询方式输出,在中断上下文或实时性要求高的场景需注意
5. 文件结构
test_at32/ ├── CMakeLists.txt # 构建配置 ├── prj.conf # Kconfig 配置 ├── src/ │ └── drivers/ │ └── uart_at32.c # UART 驱动 + printk 钩子 ├── main.c # 应用代码 └── boards/ └── at32f423_start.dts # 设备树配置6. 常见问题
Q1: printk 输出没有反应
检查项:1. 确认 CONFIG_PRINTK=y 已配置2. 确认硬件连接正确(PA9 → DAPLink RX)3. 确认 PC 端串口参数正确(115200, 8N1)4. 检查是否有其他错误导致程序未正常运行
Q2: 输出乱码
检查项:1. 确认波特率匹配(115200)2. 确认串口终端编码为 UTF-8 或 ASCII
Q3: 如何关闭 printk 输出
在 `prj.conf` 中设置:
CONFIG_PRINTK=n
实现效果:

我要赚赏金
