【前言】在基于 AT32F423 微控制器和 Zephyr RTOS 的开发项目中,需要实现串口调试输出功能。本文记录了从零开始配置 USART1 串口输出的完整过程,包括遇到的问题及解决方案。 硬件环境MCU: AT32F423调试器: DAPLink (AT-Link)串口引脚: PA9 (USART1_TX)波特率: 115200
设备树配置
首先在开发板的设备树文件 `boards/at32/at32f423_start/at32f423_start.dts` 中添加 USART1 节点配置:
寄存器定义
通过设备树获取 USART1 基地址:
问题 1: 时钟使能错误现象: 程序卡死在等待 TDBE 标志原因: 最初将 USART1 时钟使能位配置错误。USART1 位于 APB2 总线,时钟使能位在 APB2EN 寄存器的 bit 4,而不是 AHBEN2 寄存器。解决:// 错误: CRM_AHBEN2 |= BIT(14);
问题 3: USART 使能位错误现象: 串口无输出原因: 查错 SVD 文件后发现,USART 使能位是 CTRL1 寄存器的 bit 13 (UEN),而非 bit 0。解决:
为 Zephyr 标准 UART 驱动做准备本文的工作是为后续编写 Zephyr 标准 UART 驱动做准备。通过本次调试,已掌握以下关键信息:设备树配置设备树中已定义 USART1 节点,包括:- 基地址: 0x40011000- 中断号: 37- 波特率配置- 引脚复用配置 (pinctrl)驱动开发要点1. 时钟使能: 需要在 CRM 模块中使能 APB2EN bit 42. GPIO 配置: PA9 需要设置为 MUX 模式 (MODE 寄存器) + AF7 功能 (MUXH 寄存器)3. 波特率计算: BAUDR = APB2时钟 / 波特率4. 控制寄存器: CTRL1 bit 13 (UEN) 使能 USART,bit 3 (TEN) 使能发送器后续工作可以参考 Zephyr 其他 UART 驱动 (如 stm32, nxp lpuart) 的实现方式,编写符合 Zephyr uart_api 接口标准的 AT32 USART 驱动。 【总结】通过本文的调试过程,成功在 AT32F423 + Zephyr RTOS 环境下实现了 USART1 串口输出。由于 Zephyr 尚未包含 AT32 的 USART 驱动,因此采用寄存器级编程方式。设备树中已配置 USART1 节点,为后续添加完整的 Zephyr UART 驱动奠定了基础。
设备树配置
首先在开发板的设备树文件 `boards/at32/at32f423_start/at32f423_start.dts` 中添加 USART1 节点配置:
&usart1 {
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&usart1_tx_pins>;
pinctrl-names = "default";
};
&pinctrl {
usart1_tx_pins: usart1_tx {
/* PA9 as USART1_TX, AF7 */
at32af = <7>; /* AF7 */
at32pp = <0>; /* Push-pull */
at32mode = <2>; /* MUX mode */
};
}; 代码实现寄存器定义
通过设备树获取 USART1 基地址:
#define USART1_BASE DT_REG_ADDR(DT_NODELABEL(usart1)) /* USART1 寄存器 */ #define USART1_STS (*(volatile uint32_t *)(USART1_BASE + 0x00)) #define USART1_DT (*(volatile uint32_t *)(USART1_BASE + 0x04)) #define USART1_BAUDR (*(volatile uint32_t *)(USART1_BASE + 0x08)) #define USART1_CTRL1 (*(volatile uint32_t *)(USART1_BASE + 0x0C)) /* CRM 寄存器 */ #define CRM_BASE 0x40023800 #define CRM_AHBEN1 (*(volatile uint32_t *)(CRM_BASE + 0x30)) #define CRM_APB2EN (*(volatile uint32_t *)(CRM_BASE + 0x44)) /* GPIOA 寄存器 */ #define GPIOA_BASE 0x40020000 #define GPIOA_MODE (*(volatile uint32_t *)(GPIOA_BASE + 0x00)) #define GPIOA_MUXH (*(volatile uint32_t *)(GPIOA_BASE + 0x24))USART 初始化
static void usart1_init(void)
{
volatile uint32_t temp;
/* 使能 GPIOA 时钟 - AHBEN1 bit 0 */
temp = CRM_AHBEN1;
temp |= BIT(0);
CRM_AHBEN1 = temp;
/* 使能 USART1 时钟 - APB2EN bit 4 */
temp = CRM_APB2EN;
temp |= BIT(4);
CRM_APB2EN = temp;
/* 配置 PA9 为 USART1_TX (AF7) */
/* MODE 寄存器: 设置为 MUX 模式 */
temp = GPIOA_MODE;
temp &= ~0x000C0000; /* 清除 bits 19:18 */
temp |= 0x00080000; /* 设置为 MUX 模式 */
GPIOA_MODE = temp;
/* MUXH 寄存器: 设置为 AF7 */
temp = GPIOA_MUXH;
temp &= ~0x000000F0; /* 清除 bits 7:4 */
temp |= 0x00000070; /* 设置为 AF7 */
GPIOA_MUXH = temp;
/* 波特率: 115200 @ 8MHz -> BAUDR = 8000000 / 115200 = 69 */
USART1_BAUDR = 69;
/* 使能 USART 和发送器 (bit 13 = UEN, bit 3 = TEN) */
USART1_CTRL1 = BIT(13) | BIT(3);
} 发送函数static void usart1_putchar(char c)
{
/* 等待发送缓冲区空 (TDBE flag, bit 7) */
while ((USART1_STS & BIT(7)) == 0) {}
USART1_DT = c;
}
static void print_str(const char *str)
{
while (*str) {
if (*str == '\n') {
usart1_putchar('\r');
}
usart1_putchar(*str++);
}
} 关键问题与解决方案问题 1: 时钟使能错误现象: 程序卡死在等待 TDBE 标志原因: 最初将 USART1 时钟使能位配置错误。USART1 位于 APB2 总线,时钟使能位在 APB2EN 寄存器的 bit 4,而不是 AHBEN2 寄存器。解决:// 错误: CRM_AHBEN2 |= BIT(14);
// 正确: CRM_APB2EN |= BIT(4);问题 2: GPIO 引脚配置错误现象: 串口无输出原因: 之前使用了错误的寄存器 (CFGH),且配置的是 PA10 引脚而非 PA9。解决:使用正确的 MUXH 寄存器 (偏移 0x24)配置 PA9 的 MODE 和 MUX 寄存器
问题 3: USART 使能位错误现象: 串口无输出原因: 查错 SVD 文件后发现,USART 使能位是 CTRL1 寄存器的 bit 13 (UEN),而非 bit 0。解决:
// 错误: USART1_CTRL1 = BIT(0) | BIT(3); // 正确: USART1_CTRL1 = BIT(13) | BIT(3); // UEN | TEN问题 4: 波特率不匹配现象: 输出乱码原因: 系统时钟为 8MHz,而非之前假设的 96MHz 或 144MHz。解决:
// 115200 @ 8MHz -> BAUDR = 8000000 / 115200 = 69 USART1_BAUDR = 69;AT32F423 USART 寄存器总结
| 寄存器 | 偏移 | 功能 |
| STS | 0x00 | 状态寄存器 (TDBE: bit 7) |
| DT | 0x04 | 数据寄存器 |
| BAUDR | 0x08 | 波特率寄存器 |
| CTRL1 | 0x0C | 控制寄存器1 (UEN: bit 13, TEN: bit 3) |
| CRM 寄存器 | 偏移 | 功能 |
| AHBEN1 | 0x30 | AHB1 时钟使能 (GPIOA: bit 0) |
| APB2EN | 0x44 | APB2 时钟使能 (USART1: bit 4) |
我要赚赏金
