1. SA8155 BSP开发环境搭建与交叉编译工具链验证

SA8155 BSP开发环境搭建与交叉编译工具链验证 学习卡片

任务目标

在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状态未变化,可通过以下步骤排查:

  1. 确认引脚编号是否正确(GPIO_12对应n=12)
  2. 使用万用表测量GPIO_12引脚电压,点亮时应接近3.3V,熄灭时应接近0V
  3. 检查LED与限流电阻连接是否正确

判定通过条件:程序执行无报错,且LED亮灭状态与命令行参数一致。

注意事项

  • 操作/dev/mem需要root权限,请使用sudo执行
  • 不同SA8155板卡的GPIO编号可能不同,请参照对应原理图确认
  • 若引脚已被其他外设占用,需先在内核设备树中将其配置为GPIO功能并释放