ARM不同层次开发
文章目录
- 摘要
- 一、纯寄存器操作( Bare-Metal / 裸机 )
- 二、中间层:使用标准外设库/硬件抽象层(HAL)
- 三、最上层:操作系统下的开发(如 Linux on ARM)
- 四、总结
摘要
ARM开发的核心就是操作寄存器。越是底层,越是直接操作寄存器;越是上层,越是远离寄存器。
一、纯寄存器操作( Bare-Metal / 裸机 )
地址访问:每个寄存器在内存中都有一个特定的地址。通过读写这些地址来配置和控制硬件。
使用指针:在C语言中,通过定义指针变量指向这些特定地址,然后通过指针进行读写。
示例: 点亮一个LED(假设LED由GPIOA的第5引脚控制):
// 1. 定义寄存器地址(以STM32为例,地址来自芯片参考手册)
#define RCC_AHB1ENR (*(volatile unsigned int*)0x40023830) // RCC 时钟使能寄存器地址
//**将整数地址 0x40023830 强制转换为 指向 32 位无符号整数的指针,然后对指针解引用。**
#define GPIOA_MODER (*(volatile unsigned int*)0x40020000) // GPIOA 模式寄存器地址
#define GPIOA_ODR (*(volatile unsigned int*)0x40020014) // GPIOA 输出数据寄存器地址// 2. 主函数
int main() {// a. 使能GPIOA的时钟(打开GPIOA的电源开关)RCC_AHB1ENR |= (1 << 0); // 将第0位设置为1// 等价于RCC_AHB1ENR = RCC_AHB1ENR | (1 << 0)// b. 配置GPIOA的第5引脚为输出模式// MODER5[1:0] = 0b01 (输出模式)GPIOA_MODER &= ~(0x3 << 10); // 先清空第10和11位GPIOA_MODER |= (0x1 << 10); // 再将第10位设置为1while(1) {// c. 设置GPIOA第5引脚为高电平,点亮LEDGPIOA_ODR |= (1 << 5);delay(500000); // 简单延时// d. 设置GPIOA第5引脚为低电平,熄灭LEDGPIOA_ODR &= ~(1 << 5);delay(500000);}return 0;
}
二、中间层:使用标准外设库/硬件抽象层(HAL)
为了简化开发,芯片厂商(如ST、NXP)提供了封装好的函数库(如ST的Standard Peripheral Library、HAL库)。
优点: 开发效率高,代码可读性好,可移植性较强。
本质: 仍然是操作寄存器,只是由厂商的库函数代劳了。
库函数底层仍然是在操作寄存器,但开发者不再直接面对地址和比特位。
通过调用直观的函数和结构体来完成配置。
示例: 同样点亮一个LED(使用HAL库)
#include "stm32f4xx_hal.h"int main() {// 1. 初始化HAL库、系统时钟等(库函数帮你完成了寄存器配置)HAL_Init();SystemClock_Config();// 2. 定义一个GPIO初始化结构体并配置它GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_5; // 选择第5引脚GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;// 3. 调用库函数初始化GPIOA// 这个函数内部最终会写入GPIOA相关的MODER、OTYPER等寄存器HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);while (1) {// 4. 调用库函数控制引脚电平// 这些函数内部会读写ODR或BSRR寄存器HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 亮HAL_Delay(500);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 灭HAL_Delay(500);}
}
三、最上层:操作系统下的开发(如 Linux on ARM)
开发者几乎完全不直接操作寄存器。
驱动开发:由专门的驱动工程师编写内核驱动模块。驱动代码通过操作系统提供的接口(如ioremap、readl/writel)来间接地、安全地访问物理寄存器。
应用开发:绝大多数应用程序员只进行上层开发。他们通过操作系统提供的API(如打开设备文件/dev/xxx,然后使用read, write, ioctl等系统调用)来与硬件交互。
示例: 一个应用程序想控制LED
// 应用层代码,完全看不到寄存器
int main() {// 1. 打开Linux系统内核驱动暴露出来的设备文件int fd = open("/dev/led0", O_WRONLY);// 2. 通过写文件的方式控制硬件write(fd, "1", 1); // 亮sleep(1);write(fd, "0", 1); // 灭sleep(1);close(fd);return 0;
}
操作寄存器的工作,实际上是在/dev/led0对应的驱动程序中完成的。