本文记录了使用纯汇编语言在AT32F423芯片上实现流水灯的完整过程,总结了开发中遇到的坑点和调试经验,为后续Zephyr RTOS学习打下基础。 一、背景介绍在嵌入式开发中,使用纯汇编语言直接操作硬件寄存器是理解MCU内部运行机制的最佳途径。本文使用雅特力(AT)公司的AT32F423芯片,该芯片基于ARM Cortex-M4内核,与ST的STM32系列相比,在GPIO寄存器上有较大差异。
开发目标:使用纯汇编(不依赖任何HAL库)实现LED流水灯深入理解AT32F423的启动流程和寄存器配置为后续学习Zephyr RTOS打下基础
二、失败经历与问题总结
2.1 第一次尝试:寄存器映射错误
问题现象:下载程序后LED不亮,调试发现GPIOD寄存器全为0x00
根本原因: 使用了STM32的GPIO寄存器映射
教训: AT32与STM32的GPIO寄存器完全不同,不能照搬STM32代码
2.2 第二次尝试:时钟未使能
问题现象: GPIO寄存器配置正确,但仍不工作
根本原因:未使能GPIOD时钟
解决: 通过CRM_AHBEN1寄存器使能GPIOD时钟
2.3 第三次尝试:LED极性搞反
问题现象: 代码运行但LED不亮
根本原因: LED是低电平点亮,错误地使用高电平
解决: 输出低电平(0)点亮LED
2.4 第四次尝试:SystemInit函数问题
问题现象: 调试器无法继续运行,程序停留在初始化阶段
根本原因: SystemInit函数实现过于复杂
解决: 简化SystemInit,AT32复位后默认使用HICK 8MHz,GPIO时钟默认使能,直接返回即可
三、AT32F423 关键寄存器映射
3.1 外设基地址
4.1 启动文件 startup_asm.S
使用OpenOCD下载:
openocd.exe -f scripts/interface/atlink.cfg -f scripts/target/at32f423xx.cfg -c "program build/at32f423_asm_led.hex verify reset exit"
验证结果: LED2 → LED3 → LED4 循环点亮流水灯效果正常
七、与Zephyr的关系
7.1为什么学习寄存器级别开发?
Zephyr RTOS虽然提供了丰富的驱动抽象,但:深入调试时需要理解底层硬件移植新板级支持 需要掌握寄存器操作-性能优化 需要了解外设工作原理
7.2 汇编技能在Zephyr中的应用
1. 启动代码:Zephyr的arch层使用汇编实现中断向量表、堆栈初始化2. 上下文切换:任务切换需要汇编实现寄存器保存/恢复3. 临界区保护:CPS指令操作需要在汇编级别理解
7.3 下一步学习路径
1. 学习Zephyr的AT32F423板级支持代码2. 分析Zephyr的GPIO子系统实现3. 在Zephyr中添加自定义外设驱动
八、总结
本文通过纯汇编实现AT32F423流水灯,详细记录了:
1. AT32与STM32 GPIO寄存器的差异2. 调试过程中的关键发现3. 完整的代码实现
掌握这些底层技能,将为后续学习Zephyr RTOS打下坚实基础。理解硬件的工作原理,才能更好地使用操作系统提供的抽象层。
开发目标:使用纯汇编(不依赖任何HAL库)实现LED流水灯深入理解AT32F423的启动流程和寄存器配置为后续学习Zephyr RTOS打下基础
二、失败经历与问题总结
2.1 第一次尝试:寄存器映射错误
问题现象:下载程序后LED不亮,调试发现GPIOD寄存器全为0x00
根本原因: 使用了STM32的GPIO寄存器映射
| 功能 | STM32寄存器 | AT32寄存器 |
| 模式配置 | MODER | CFGR |
| 输出速度 | OSPEEDR | ODRVR |
| 输出数据 | ODR | ODT |
| 置位/清零 | BSRR | SCR |
教训: AT32与STM32的GPIO寄存器完全不同,不能照搬STM32代码
2.2 第二次尝试:时钟未使能
问题现象: GPIO寄存器配置正确,但仍不工作
根本原因:未使能GPIOD时钟
解决: 通过CRM_AHBEN1寄存器使能GPIOD时钟
ldr r1, [r0, #CRM_AHBEN1] orr.w r1, r1, #(1 << 3) @ GPIODEN str r1, [r0, #CRM_AHBEN1]
2.3 第三次尝试:LED极性搞反
问题现象: 代码运行但LED不亮
根本原因: LED是低电平点亮,错误地使用高电平
解决: 输出低电平(0)点亮LED
2.4 第四次尝试:SystemInit函数问题
问题现象: 调试器无法继续运行,程序停留在初始化阶段
根本原因: SystemInit函数实现过于复杂
解决: 简化SystemInit,AT32复位后默认使用HICK 8MHz,GPIO时钟默认使能,直接返回即可
三、AT32F423 关键寄存器映射
3.1 外设基地址
.equ PERIPH_BASE, 0x40000000 .equ AHBPERIPH1, PERIPH_BASE + 0x20000 @ 0x40020000 .equ GPIOA_BASE, AHBPERIPH1 + 0x0000 @ 0x40020000 .equ GPIOB_BASE, AHBPERIPH1 + 0x0400 @ 0x40020400 .equ GPIOC_BASE, AHBPERIPH1 + 0x0800 @ 0x40020800 .equ GPIOD_BASE, AHBPERIPH1 + 0x0C00 @ 0x40020C00 .equ CRM_BASE, PERIPH_BASE + 0x23800 @ 0x400238003.2 GPIO寄存器偏移(AT32特有)
.equ GPIO_CFGR, 0x00 @ 模式配置寄存器(每引脚2位) .equ GPIO_OMODE, 0x04 @ 输出类型寄存器 .equ GPIO_ODRVR, 0x08 @ 驱动强度寄存器 .equ GPIO_PULL, 0x0C @ 上下拉寄存器 .equ GPIO_ODT, 0x14 @ 输出数据寄存器 .equ GPIO_SCR, 0x18 @ 置位/清零寄存器3.3 CRM寄存器偏移
.equ CRM_CTRL, 0x00 @ 控制寄存器 .equ CRM_CFG, 0x04 @ 时钟配置寄存器 .equ CRM_AHBEN1, 0x30 @ AHB1外设时钟使能寄存器四、完整代码实现
4.1 启动文件 startup_asm.S
/** ****************************************************************************** * @file startup_asm.S * @brief AT32F423 纯汇编启动文件 - 流水灯 * 不使用任何 HAL 库,直接操作寄存器 ****************************************************************************** */ .syntax unified .cpu cortex-m4 .fpu softvfp .thumb /*============================================================================== 寄存器地址定义 ==============================================================================*/ .equ PERIPH_BASE, 0x40000000 .equ AHBPERIPH1, PERIPH_BASE + 0x20000 .equ GPIOA_BASE, AHBPERIPH1 + 0x0000 .equ GPIOB_BASE, AHBPERIPH1 + 0x0400 .equ GPIOC_BASE, AHBPERIPH1 + 0x0800 .equ GPIOD_BASE, AHBPERIPH1 + 0x0C00 .equ GPIOE_BASE, AHBPERIPH1 + 0x1000 .equ GPIOF_BASE, AHBPERIPH1 + 0x1400 .equ CRM_BASE, PERIPH_BASE + 0x23800 /* CRM 寄存器偏移 */ .equ CRM_CTRL, 0x00 .equ CRM_CFG, 0x04 .equ CRM_AHBEN1, 0x30 /* GPIO 寄存器偏移(AT32特有) */ .equ GPIO_CFGR, 0x00 .equ GPIO_OMODE, 0x04 .equ GPIO_ODRVR, 0x08 .equ GPIO_PULL, 0x0C .equ GPIO_ODT, 0x14 .equ GPIO_SCR, 0x18 /* LED 引脚定义 */ .equ LED2_PIN, 0x2000 @ GPIO_PINS_13 .equ LED3_PIN, 0x4000 @ GPIO_PINS_14 .equ LED4_PIN, 0x8000 @ GPIO_PINS_15 .equ LED_ALL, 0xE000 /* 内存定义 */ .equ FLASH_BASE, 0x08000000 .equ RAM_BASE, 0x20000000 .equ RAM_SIZE, 0xC000 @ 48KB .equ _estack, RAM_BASE + RAM_SIZE /*============================================================================== 向量表 ==============================================================================*/ .section .isr_vector,"a",%progbits .type g_pfnVectors, %object .size g_pfnVectors, .-g_pfnVectors g_pfnVectors: .word _estack .word Reset_Handler .word NMI_Handler .word HardFault_Handler /*============================================================================== 启动代码 ==============================================================================*/ .section .text.Reset_Handler .weak Reset_Handler .type Reset_Handler, %function Reset_Handler: /* 复制 .data 段 */ movs r1, #0 b LoopCopyData CopyData: ldr r3, =_sidata ldr r3, [r3, r1] str r3, [r0, r1] adds r1, r1, #4 LoopCopyData: ldr r0, =_sdata ldr r3, =_edata adds r2, r0, r1 cmp r2, r3 bcc CopyData /* 清零 .bss 段 */ ldr r2, =_sbss b LoopFillZerobss FillZerobss: movs r3, #0 str r3, [r2], #4 LoopFillZerobss: ldr r3, =_ebss cmp r2, r3 bcc FillZerobss /* 初始化系统时钟 */ bl SystemInit /* 初始化 LED */ bl LedInit /* 跳转到 main */ bl main Loop: b Loop /*============================================================================== SystemInit - 最小化版本 ==============================================================================*/ .section .text.SystemInit .type SystemInit, %function SystemInit: bx lr .size SystemInit, .-SystemInit /*============================================================================== LedInit - 初始化 LED (GPIOD pin 13/14/15) ==============================================================================*/ .section .text.LedInit .type LedInit, %function LedInit: ldr r0, =CRM_BASE /* 使能 GPIOD 时钟 (AHB1ENR bit 3) */ ldr r1, [r0, #CRM_AHBEN1] orr.w r1, r1, #(1 << 3) str r1, [r0, #CRM_AHBEN1] /* 配置 GPIOD 引脚为输出 */ ldr r0, =GPIOD_BASE /* CFGR: 设置 Pin 13/14/15 为输出模式 */ ldr r1, [r0, #GPIO_CFGR] bic.w r1, r1, #(0x03 << 26) orr.w r1, r1, #(0x01 << 26) bic.w r1, r1, #(0x03 << 28) orr.w r1, r1, #(0x01 << 28) bic.w r1, r1, #(0x03 << 30) orr.w r1, r1, #(0x01 << 30) str r1, [r0, #GPIO_CFGR] /* ODRVR: 驱动强度 - 最强 */ ldr r1, [r0, #GPIO_ODRVR] orr.w r1, r1, #(0x03 << 26) orr.w r1, r1, #(0x03 << 28) orr.w r1, r1, #(0x03 << 30) str r1, [r0, #GPIO_ODRVR] /* 初始状态: LED 关闭 */ ldr r1, =LED_ALL str r1, [r0, #GPIO_ODT] bx lr .size LedInit, .-LedInit /*============================================================================== main 函数 - 流水灯 ==============================================================================*/ .section .text.main .type main, %function main: ldr r0, =GPIOD_BASE led_loop: /* LED2 点亮 - 低电平点亮 */ ldr r1, =LED2_PIN lsl r1, r1, #16 @ SCR 高16位 = 清零 ODT 位 str r1, [r0, #GPIO_SCR] bl delay /* LED2 关闭 */ ldr r1, =LED2_PIN str r1, [r0, #GPIO_ODT] /* LED3 点亮 */ ldr r1, =LED3_PIN lsl r1, r1, #16 str r1, [r0, #GPIO_SCR] bl delay /* LED3 关闭 */ ldr r1, =LED3_PIN str r1, [r0, #GPIO_ODT] /* LED4 点亮 */ ldr r1, =LED4_PIN lsl r1, r1, #16 str r1, [r0, #GPIO_SCR] bl delay /* LED4 关闭 */ ldr r1, =LED4_PIN str r1, [r0, #GPIO_ODT] b led_loop /* 延时函数 */ delay: movs r5, #0 delay_loop: adds r5, r5, #1 cmp r5, #0x80000 bne delay_loop bx lr .size main, .-main /*============================================================================== 默认中断处理 ==============================================================================*/ .section .text.Default_Handler,"ax",%progbits Default_Handler: Infinite_Loop: b Infinite_Loop .size Default_Handler, .-Default_Handler .weak NMI_Handler .thumb_set NMI_Handler,Default_Handler .weak HardFault_Handler .thumb_set HardFault_Handler,Default_Handler /*============================================================================== 外部符号 ==============================================================================*/ .section .data .align 2 .global _sidata .global _sdata .global _edata .global _sbss .global _ebss _sidata: .word 0 .section .bss .align 2 _sdata: .word 0 _edata: .word 0 _sbss: .word 0 _ebss: .word 0 ```五、Makefile配置
TARGET = at32f423_asm_led DEBUG = 1 OPT = -Og BUILD_DIR = build C_SOURCES = ASM_SOURCES = ./project/src/startup_asm.S PREFIX = arm-none-eabi- CC = $(PREFIX)gcc AS = $(PREFIX)gcc -x assembler-with-cpp CP = $(PREFIX)objcopy SZ = $(PREFIX)size CPU = -mcpu=cortex-m4 FPU = -mfpu=fpv4-sp-d16 FLOAT-ABI = -mfloat-abi=soft MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI) ASFLAGS = $(MCU) $(OPT) -Wall -fdata-sections -ffunction-sections CFLAGS = $(MCU) $(OPT) -Wall -fdata-sections -ffunction-sections ifeq ($(DEBUG), 1) CFLAGS += -g -gdwarf-2 endif LDSCRIPT = project/misc/AT32F423xC_FLASH.ld LIBS = LIBDIR = LDFLAGS = $(MCU) -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.S=.o))) vpath %.S $(sort $(dir $(ASM_SOURCES))) $(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR) $(AS) -c $(ASFLAGS) $< -o $@ $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile $(CC) $(OBJECTS) $(LDFLAGS) -o $@ $(SZ) $@ $(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR) $(CP) -O ihex $< $@ $(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR) $(CP) -O binary -S $< $@ $(BUILD_DIR): mkdir $@ clean: rm -rf $(BUILD_DIR)六、下载验证
使用OpenOCD下载:
openocd.exe -f scripts/interface/atlink.cfg -f scripts/target/at32f423xx.cfg -c "program build/at32f423_asm_led.hex verify reset exit"
验证结果: LED2 → LED3 → LED4 循环点亮流水灯效果正常
七、与Zephyr的关系
7.1为什么学习寄存器级别开发?
Zephyr RTOS虽然提供了丰富的驱动抽象,但:深入调试时需要理解底层硬件移植新板级支持 需要掌握寄存器操作-性能优化 需要了解外设工作原理
7.2 汇编技能在Zephyr中的应用
1. 启动代码:Zephyr的arch层使用汇编实现中断向量表、堆栈初始化2. 上下文切换:任务切换需要汇编实现寄存器保存/恢复3. 临界区保护:CPS指令操作需要在汇编级别理解
7.3 下一步学习路径
1. 学习Zephyr的AT32F423板级支持代码2. 分析Zephyr的GPIO子系统实现3. 在Zephyr中添加自定义外设驱动
八、总结
本文通过纯汇编实现AT32F423流水灯,详细记录了:
1. AT32与STM32 GPIO寄存器的差异2. 调试过程中的关键发现3. 完整的代码实现
掌握这些底层技能,将为后续学习Zephyr RTOS打下坚实基础。理解硬件的工作原理,才能更好地使用操作系统提供的抽象层。
我要赚赏金
