I.MX6U 裸机开发6. 结构体编程风格使用寄存器
一、STM32 库函数的开发风格
STM32库函数通过将寄存器定义为结构体的方式,提供了一种高效且易于维护的编程风格。
在STM32库中,给每个外设会对应定义一个结构体,结构体的每个成员变量对应一个寄存器。例如,GPIO外设的寄存器定义如下:
typedef struct
{
__IO uint32_t MODER; /*!< GPIO 端口模式寄存器, 地址偏移: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO 端口输出类型寄存器, 地址偏移: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO 端口输出速度寄存器, 地址偏移: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO 端口上拉/下拉寄存器, 地址偏移: 0x0C */
__IO uint32_t IDR; /*!< GPIO 端口输入数据寄存器, 地址偏移: 0x10 */
__IO uint32_t ODR; /*!< GPIO 端口输出数据寄存器, 地址偏移: 0x14 */
__IO uint32_t BSRR; /*!< GPIO 端口位设置/重置寄存器, 地址偏移: 0x18 */
__IO uint32_t LCKR; /*!< GPIO 端口配置锁定寄存器, 地址偏移: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO 端口复用功能寄存器, 地址偏移: 0x20-0x24 */
} GPIO_TypeDef;
然后,通过定义指向这些结构体的指针,可以方便地访问和操作寄存器。例如:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
这样做有以下好处:
-
代码可读性高:使用结构体定义寄存器后,代码更加直观。例如,设置GPIOA的模式寄存器可以写成
GPIOA->MODER = 0x00000000;
,一目了然。 -
易于维护:当寄存器地址或结构发生变化时,只需修改结构体定义或基地址定义,其他代码无需改动,极大地提高了代码的可维护性。
-
减少错误:通过结构体成员访问寄存器,可以避免直接使用魔法数(硬编码的地址),减少了出错的可能性。
-
便于调试:结构体的使用使得调试工具能够更好地解析和显示寄存器的内容,方便开发人员进行调试。
-
代码复用性高:结构体定义的寄存器可以在不同的项目中复用,减少了重复代码的编写,提高了开发效率。
二、代码编写
1. 清除 bss 段
BSS 段用于存储程序中未初始化的全局变量和静态变量。
下面定义两个全局符号 _bss_start 和 _bss_end,并将它们分别与 __bss_start 和 __bss_end 关联起来。
这些符号通常用于标识 BSS 段的起始和结束位置。
.global _bss_start
_bs_start:
.word __bss_start
.global _bss_end
_bss_end:
.word __bss_end
下面是清除的过程 :
/** 清除 bss 段 */
LDR R0, =__bss_start /** 获取 bss 段的起始地址 */
LDR R1, =__bss_end /** 获取 bss 段的结束地址 */
MOV R2, #0 /** 设置清零的值 */
B bss_loop /** 跳转到 bss_loop */
bss_loop:
STMIA R0!, {R2} /** 将 R2 的值存储到 R0 指向的地址,然后 R0 += 4 */
CMP R0, R1 /** 比较 R0 和 R1 的值, 即有没有到结尾 */
BLE bss_loop /** 如果 R0 <= R1,跳转到 bss_loop */
bss_end:
2. 完整启动文件
.global _start
.global _bss_start
_bs_start:
.word __bss_start
.global _bss_end
_bss_end:
.word __bss_end
_start:
/** 设置处理进入 SVC 模式 */
MRS R0, CPSR /** 读取当前的 CPSR *