这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【雅特力|Zephyr】2、AT32F423纯汇编开发实战-从零开始点流水灯

共1条 1/1 1 跳转至

【雅特力|Zephyr】2、AT32F423纯汇编开发实战-从零开始点流水灯

高工
2026-02-28 23:57:13     打赏
本文记录了使用纯汇编语言在AT32F423芯片上实现流水灯的完整过程,总结了开发中遇到的坑点和调试经验,为后续Zephyr RTOS学习打下基础。 一、背景介绍在嵌入式开发中,使用纯汇编语言直接操作硬件寄存器是理解MCU内部运行机制的最佳途径。本文使用雅特力(AT)公司的AT32F423芯片,该芯片基于ARM Cortex-M4内核,与ST的STM32系列相比,在GPIO寄存器上有较大差异。
开发目标:使用纯汇编(不依赖任何HAL库)实现LED流水灯深入理解AT32F423的启动流程和寄存器配置为后续学习Zephyr RTOS打下基础

二、失败经历与问题总结
2.1 第一次尝试:寄存器映射错误
问题现象:下载程序后LED不亮,调试发现GPIOD寄存器全为0x00
根本原因: 使用了STM32的GPIO寄存器映射
功能 STM32寄存器AT32寄存器
模式配置MODERCFGR
输出速度OSPEEDRODRVR
输出数据ODRODT
置位/清零BSRRSCR

教训: 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    @ 0x40023800
3.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打下坚实基础。理解硬件的工作原理,才能更好地使用操作系统提供的抽象层。





关键词: AT32F423     汇编    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]