1. 点亮LED
任务目标
在SA8155平台上,通过直接操作GPIO控制器寄存器,将目标引脚配置为输出模式并控制其电平,实现LED的点亮与熄灭。本案例聚焦于GPIO输出模式配置与电平控制的寄存器操作流程,不涉及Linux内核GPIO子系统或设备树。
硬件连接与前置条件
- SA8155 SoC的GPIO控制器基地址:
0xF1000000 - 目标引脚:GPIO_12(对应寄存器偏移需查阅SA8155 TRM,本案例以GPIO_12为例)
- LED正极经限流电阻连接至GPIO_12,负极接地
- 开发环境:交叉编译工具链(aarch64-linux-gnu-gcc),目标板可通过串口或网络访问
GPIO寄存器操作原理
SA8155 GPIO控制器每个引脚由两组寄存器控制:GPIO_CFG_n(配置寄存器,偏移 0x100 + n*8)和 GPIO_IN_OUT_n(数据寄存器,偏移 0x104 + n*8)。配置寄存器bit[1:0]设为0b01使引脚工作在GPIO模式,bit[5:4]设为0b00选择输出方向;数据寄存器bit[0]写1输出高电平,写0输出低电平。
关键完整代码
// led_gpio.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#define GPIO_BASE 0xF1000000
#define MAP_SIZE 0x1000
#define GPIO_CFG_OFFSET(n) (0x100 + (n)*8)
#define GPIO_IN_OUT_OFFSET(n) (0x104 + (n)*8)
#define GPIO_NUM 12
static volatile uint32_t *gpio_base;
static int gpio_init(void) {
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) {
perror("open /dev/mem");
return -1;
}
gpio_base = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, GPIO_BASE);
close(fd);
if (gpio_base == MAP_FAILED) {
perror("mmap");
return -1;
}
return 0;
}
static void gpio_set_output(uint32_t pin) {
uint32_t cfg_offset = GPIO_CFG_OFFSET(pin) / 4;
uint32_t cfg = gpio_base[cfg_offset];
cfg &= ~(0x3 << 0); // 清bit[1:0]
cfg |= (0x1 << 0); // GPIO模式
cfg &= ~(0x3 << 4); // 清bit[5:4]
cfg |= (0x0 << 4); // 输出方向
gpio_base[cfg_offset] = cfg;
}
static void gpio_set_level(uint32_t pin, uint32_t level) {
uint32_t data_offset = GPIO_IN_OUT_OFFSET(pin) / 4;
uint32_t val = gpio_base[data_offset];
if (level)
val |= (1 << 0);
else
val &= ~(1 << 0);
gpio_base[data_offset] = val;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <on|off>\n", argv[0]);
return 1;
}
if (gpio_init() != 0)
return 1;
gpio_set_output(GPIO_NUM);
if (strcmp(argv[1], "on") == 0) {
gpio_set_level(GPIO_NUM, 1);
printf("LED ON\n");
} else if (strcmp(argv[1], "off") == 0) {
gpio_set_level(GPIO_NUM, 0);
printf("LED OFF\n");
} else {
printf("Invalid argument. Use 'on' or 'off'.\n");
munmap((void *)gpio_base, MAP_SIZE);
return 1;
}
munmap((void *)gpio_base, MAP_SIZE);
return 0;
}
编译与运行
aarch64-linux-gnu-gcc -o led_gpio led_gpio.c
# 将led_gpio拷贝至目标板后执行:
./led_gpio on # 点亮LED
./led_gpio off # 熄灭LED
验证方法
执行./led_gpio on后,观察LED是否点亮;执行./led_gpio off后,LED应熄灭。若LED状态未变化,可通过以下步骤排查:
- 确认引脚编号是否正确(GPIO_12对应n=12)
- 使用万用表测量GPIO_12引脚电压,点亮时应接近3.3V,熄灭时应接近0V
- 检查LED与限流电阻连接是否正确
判定通过条件:程序执行无报错,且LED亮灭状态与命令行参数一致。
注意事项
- 操作
/dev/mem需要root权限,请使用sudo执行 - 不同SA8155板卡的GPIO编号可能不同,请参照对应原理图确认
- 若引脚已被其他外设占用,需先在内核设备树中将其配置为GPIO功能并释放