当前位置: 首页 > news >正文

i.MX6ULL移植内核6.6(二)GPIO子系统和LED子系统

本篇主要内容:

    • 一、应用层操作GPIO的几种方式
      • 1. 通过sysfs系统操作GPIO
      • 2. 通过/dev/mem内存映射直接操作GPIO
      • 3. 通过/dev/gpiochipx操作GPIO(linux4.8新增)
      • 4. 设备树分析
    • 二、LED子系统
      • 1.设备树添加LED节点
      • 2.应用层控制LED
      • 3.内核LED驱动

一、应用层操作GPIO的几种方式

1. 通过sysfs系统操作GPIO

📌以i.IMX6ULL为例,在前一篇移植的6.6内核,GPIO相关的引脚定义和声明均在设备树中已写好,驱动也默认使能。可以直接通过sysfs操作GPIO,在阿尔法开发板上,GPIO1_IO3接了LED,这里可以通过以下命令操作GPIO。

echo 3 > /sys/class/gpio/export		# 导出GPIO1_IO03
echo "out" > /sys/class/gpio/gpio3/direction	# 设置GPIO1_IO03为输出
echo 0 > /sys/class/gpio/gpio3/value		# GPIO1_IO03输出低电平(此时LED亮)
echo 1 > /sys/class/gpio/gpio3/value		# GPIO1_IO03输出高电平(此时LED灭)

2. 通过/dev/mem内存映射直接操作GPIO

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>// IMX6ULL GPIO1寄存器基地址
#define GPIO1_BASE 0x0209C000// GPIO寄存器偏移量
#define GPIO_DR     0x00    // 数据寄存器
#define GPIO_GDIR   0x04    // 方向寄存器
#define GPIO_PSR    0x08    // 引脚状态寄存器
#define GPIO_ICR1   0x0C    // 中断配置寄存器1
#define GPIO_ICR2   0x10    // 中断配置寄存器2
#define GPIO_IMR    0x14    // 中断屏蔽寄存器
#define GPIO_ISR    0x18    // 中断状态寄存器
#define GPIO_EDGE_SEL 0x1C // 边沿选择寄存器// 内存映射大小
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)		//0xFFF,4K对齐用int main(int argc, char *argv[]) {if (argc != 2) {fprintf(stderr, "Usage: %s <0|1>\n", argv[0]);fprintf(stderr, "  0: set GPIO1_IO03 low\n");fprintf(stderr, "  1: set GPIO1_IO03 high\n");return -1;}int value = atoi(argv[1]);if (value != 0 && value != 1) {fprintf(stderr, "Invalid value: %s. Must be 0 or 1.\n", argv[1]);return EXIT_FAILURE;}int fd;void *map_base;volatile unsigned long *gpio_dr;volatile unsigned long *gpio_gdir;// 打开/dev/mem 设备if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) {perror("open /dev/mem");return EXIT_FAILURE;}// 对齐后的GPIO1基地址映射到用户空间map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_BASE & ~MAP_MASK);if (map_base == (void *)-1) {perror("mmap");close(fd);return EXIT_FAILURE;}// 计算寄存器地址off_t gpio_addr = GPIO1_BASE & MAP_MASK;	// 0x0209C000 & 0xfff得到页偏移值,这里偏移是0gpio_gdir = (volatile unsigned long *)((char *)map_base + gpio_addr + GPIO_GDIR);gpio_dr = (volatile unsigned long *)((char *)map_base + gpio_addr + GPIO_DR);// 设置 GPIO1_IO03 为输出模式 (bit3 = 1)*gpio_gdir |= (1 << 3);// 设置输出电平if (value) {*gpio_dr |= (1 << 3);  // 设置高电平printf("Set GPIO1_IO03 HIGH\n");} else {*gpio_dr &= ~(1 << 3); // 设置低电平printf("Set GPIO1_IO03 LOW\n");}// 释放内存、关闭设备if (munmap(map_base, MAP_SIZE) == -1) {perror("munmap");}close(fd);return 0;
}

在这里插入图片描述

3. 通过/dev/gpiochipx操作GPIO(linux4.8新增)

📌这种方式是linux4.8以后新增的方式,也是GPIO子系统一部分,是当前比较新和推荐的方式,毕竟sysfs在2020年被标记为deprecated(废弃),同时,也引入了libgpiod这个库供应用层来操作GPIO,以及相应的命令行测试工具。上一篇使用的根文件系统是不带gpiod相关的命令,这里重新使用buildroot制作了一个根文件系统,并添加了gpiod相关的命令。

在这里插入图片描述

📌命令操作GPIO如下(其它命令略):

root@ATK-IMX6ULL:~# gpioset /dev/gpiochip0 3=1		# 操作GPIO1_IO3输出高电平
root@ATK-IMX6ULL:~# gpioset /dev/gpiochip0 3=0		# 操作GPIO1_IO3输出低电平

📌libgpiod操作GPIO代码如下:

#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>int main() {const char *chipname = "gpiochip0";struct gpiod_chip *chip;struct gpiod_line *line;int ret;// 打开GPIO控制器chip = gpiod_chip_open_by_name(chipname);if (!chip) {perror("Open chip failed");return 1;}// 获取GPIO3(IMX6ULL GPIO1_IO03 = 偏移量 3)line = gpiod_chip_get_line(chip, 3);if (!line) {perror("Get line failed");gpiod_chip_close(chip);return 1;}// 配置为输出模式,初始低电平ret = gpiod_line_request_output(line, "example", 0);if (ret < 0) {perror("Request line as output failed");goto cleanup;}// 设置高电平gpiod_line_set_value(line, 1);printf("GPIO3 has been set HIGH\n");sleep(1);// 设置低电平gpiod_line_set_value(line, 0);printf("GPIO3 has been set LOW\n");cleanup:// 释放资源gpiod_line_release(line);gpiod_chip_close(chip);return 0;
}

在这里插入图片描述

📌编译命令如图所示,需要提前交叉编译好对应的库,最后的运行结果,LED先熄灭1s后亮起

4. 设备树分析

gpio1: gpio@209c000 {		/* GPIO1_IOX基地址0x209c000 *//* 匹配该GPIO控制器兼容的驱动:*/compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";reg = <0x0209c000 0x4000>;	/* <寄存器基地址 大小16KB>*/interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,		/* <中断控制器 中断号 触发方式> */<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_GPIO1>;		/* 时钟信号 */gpio-controller;		/* gpio控制器 */#gpio-cells = <2>;		/* <&gpio1 3 0> */interrupt-controller;#interrupt-cells = <2>;	/* <引脚复用控制器 GPIO起始编号 iomux控制器起始编号 GPIO数量> *//* 0-9、10-15、16-31 一共32个GPIO */gpio-ranges = <&iomuxc  0 23 10>, <&iomuxc 10 17 6>,<&iomuxc 16 33 16>;
};

📌源码中默认已经开启了GPIO子系统,如果没有可以添加以下配置到.config

CONFIG_GPIOLIB=y //启用GPIO核心库
CONFIG_OF_GPIO=y //允许通过设备树配置GPIO
CONFIG_GPIOLIB_IRQCHIP=y //允许GPIO引脚用于中断请求
CONFIG_GPIO_CDEV=y //启用字符设备(cdev)接口来访问GPIO
CONFIG_GPIO_CDEV_V1=y //使用GPIO字符设备接口的版本V1
CONFIG_GPIO_GENERIC=y //启用通用GPIO框架,提供了标准的GPIO实现

二、LED子系统

1.设备树添加LED节点

gpio-leds {compatible = "gpio-leds";status = "okay";led0 {function = LED_FUNCTION_HEARTBEAT;color = <LED_COLOR_ID_RED>;gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;linux,default-trigger = "heartbeat";};
};

📌关于LED内核所提供的驱动配置路径如下,在设备树中添加以上节点,替换到开发板重启,可以看到LED闪烁

在这里插入图片描述

2.应用层控制LED

root@ATK-IMX6ULL:~# ls /sys/class/leds/*
mmc0::/         mmc1::/         red:heartbeat/
root@ATK-IMX6ULL:~# echo none > /sys/class/leds/red:heartbeat/trigger
root@ATK-IMX6ULL:~# echo 1 > /sys/class/leds/red:heartbeat/brightness
root@ATK-IMX6ULL:~# echo 0 > /sys/class/leds/red:heartbeat/brightness

3.内核LED驱动

📌源码路径:

📂linux-imx-lf-6.6.y/drivers/leds/led-core.c
📂linux-imx-lf-6.6.y/drivers/leds/led-class.c
📂linux-imx-lf-6.6.y/drivers/leds/leds-gpio.c
📂linux-imx-lf-6.6.y/drivers/leds/leds-pwm.c
📂linux-imx-lf-6.6.y/drivers/leds/leds.h

📌楼了一眼代码,有点小复杂看不懂,此处略过。大致的关系如下

源文件层次主要功能
led-class.c核心抽象层定义核心数据结构 struct led_classdev,通过它注册具体的LED设备
led-core.c核心基础实现层提供了LED子系统的核心实现,如sysfs 接口、全局初始化等
leds-gpio.c硬件驱动层控制GPIO引脚上的LED,实现probe函数初始化GPIO并注册LED设备
leds-pwm.c其它功能控制PWM信号驱动的LED,通过调节PWM占空比实现亮度控制
led-triggers.c其它功能实现了LED触发器功能(如 heartbeat、timer)

在这里插入图片描述

http://www.dtcms.com/a/394843.html

相关文章:

  • VLC Media取消视频文件名前置显示
  • 在unity urp项目中 通过图片创建材质(透明光晕)
  • OneSignal PHP SDK v2 官方资源
  • 如何透过批次模拟加速3D模型建立
  • PDF清晰度提升工具,让模糊文档变清晰
  • 设计模式六大原则
  • QML 多路 RTSP 视频流实时预览实现
  • glTF/glb:现在和未来
  • 构建以ERP为核心的智能制造运营中心(MOM)
  • Java:toArray(new String[0])
  • Trilium Notes+cpolar:打造随身个人知识库的智能中枢
  • 无人机图传技术详解:为何云望图传信号传输能力远超WiFi?,无人机wifi图传是什么意思
  • 水题记录2.1
  • 企业智能工作流的无界解决方案由CherryStudio+cpolar解决
  • Nginx高级用法案例汇总
  • Python开发:使用FastAPI创建后端服务
  • Nginx配置中location和proxy_pass指令尾部是否带斜杠的区别
  • Nginx核心配置
  • 医院不良事件管理系统:提升医疗安全的智能化解决方案
  • 【$.post回调函数未被执行的原因分析】,第048篇
  • 远程连接服务器的远程重启办法shutdown -r -t 0
  • 【js】关于JWT的前端存储新思路
  • Unity官方Dots范例工程学习——Jobs101
  • 如何在SQLite中实现事务处理?
  • 广东省省考备考(第一百零四天9.22)——判断推理(强化训练)
  • k8s 常用命令
  • windows远程桌面服务安全加固的配置指南
  • datawhale玩转通义四大新模型 202509 第4次作业
  • MySQL 表约束实战指南:从概念到落地,守护数据完整性
  • 64位整型变量错误使用int类型对应的格式化符%d导致软件崩溃问题的排查与分析(借助deepseek辅助分析)