前言
这是前3篇:
Linux 中的三大类驱动:字符设备驱动、块设备驱动和网络设备驱动. LED, GPIO属于字符设备, 驱动在买的板子上一般厂家都已改好, 当然如果扩展或者自己画新板子的话可以改设备树. 本篇介绍下GPIO, 以LED, GPIO子系统为例, 采用板子出厂配置的系统, 暂不涉及设备树的改动.
LED
终端
# 查看LED
root@mys6ull14x14:~# ls /sys/class/leds
cpu mmc0:: user
# 米尔的MYS-6ULX板子上有3颗LED, 分别命名为cpu, mmc0::, user
# user是闲置的LED, 用这个
# 查看LED设备属性
root@mys6ull14x14:~# ls /sys/class/leds/user
brightness max_brightness subsystem uevent
device power trigger
# 这里主要用brightness, 取值范围0~brightness
# 如果不支持多级亮度, brightness取0灯灭, 非0灯亮(如1, 255)
# 点亮LED, 对应米尔MYS-6ULX板子上的D12
root@mys6ull14x14:~# echo 1 > /sys/class/leds/user/brightness
# 熄灭LED
root@mys6ull14x14:~# echo 0 > /sys/class/leds/user/brightness
脚本
此处可有脚本gpio_led.sh:
#!/bin/bash
echo "GPIO LED Test"
LED=/sys/class/leds/user/brightness
while (true)
do
echo 1 > $LED
sleep 1
echo 0 > $LED
sleep 1
done
加权限sudo chmod 777 gpio_led.sh
, 执行./gpio_led.sh
, 可以看到板子上LED间隔1s闪烁.
C语言
直接用系统调用的方式, 新建文件main.c:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define USER_LED_DEV_PATH "/sys/class/leds/user/brightness"
int main()
{
int led_fd;
char cmd[128] = {0};
printf("user led demo\r\n");
led_fd = open(USER_LED_DEV_PATH, O_WRONLY);
if (led_fd < 0)
{
printf("Fail to open %s device\n", USER_LED_DEV_PATH);
exit(1);
}
while (1)
{
sprintf(cmd, "echo 1 > %s", USER_LED_DEV_PATH);
system(cmd);
sleep(1);
sprintf(cmd, "echo 0 > %s", USER_LED_DEV_PATH);
system(cmd);
usleep(1000 * 300);
}
close(led_fd);
return 0;
}
交叉编译arm-linux-gnueabihf-gcc -o led1 led1.c
, 执行./led1
, 可以看到板子user LED亮1s灭300ms循环…
while中也可以改成write的方式:
while (1)
{
write(led_fd, "255", 3); //3: 3个字节
sleep(1);
write(led_fd, "0", 1);
usleep(1000 * 300);
}
交叉编译执行, 效果是一样的.
GPIO子系统
与LED子系统类似,Linux提供了GPIO子系统驱动框架,使用该驱动框架可以把CPU的GPIO引脚导出到用户空间,用户通过访问/sys文件系统进行控制,GPIO子系统支持把引脚用于基本的输入输出功能,其中输入功能还支持中断检测。
米尔的MYS-6ULX板子上留出了两个gpio, gpio5和gpio9:
这里以37引脚的GPIO_9为例, 接上万用表:
root@mys6ull14x14:~# ls /sys/class/gpio
export gpiochip0 gpiochip32 gpiochip96
gpio131 gpiochip128 gpiochip64 unexport
# 向export文件写入GPIO编号可以向内核申请将该编号的GPIO导出到用户空间
# 注意: 板子复位后设置无效
# 反操作是unexport
root@mys6ull14x14:~# echo 9 > /sys/class/gpio/export
# 查看导出的gpio, 发现多了gpio9
root@mys6ull14x14:~# ls /sys/class/gpio
export gpio9 gpiochip128 gpiochip64 unexport
gpio131 gpiochip0 gpiochip32 gpiochip96
# 查看gpio9属性
root@mys6ull14x14:~# ls /sys/class/gpio/gpio9
active_low direction power uevent
device edge subsystem value
# 使用GPIO子系统的设备则可以在用户空间灵活配置作为输入、输出或中断模式
# 查看gpio9的方向, 下面显示是输入
root@mys6ull14x14:~# cat /sys/class/gpio/gpio9/direction
in
# 设置gpio9为输出
root@mys6ull14x14:~# echo out > /sys/class/gpio/gpio9/direction
# gpio9输出高电平, 可用万用表检测米尔MYS-6ULX板子J2的37引脚, 应为3.3V
root@mys6ull14x14:~# echo 1 > /sys/class/gpio/gpio9/value
# gpio9输出低电平
root@mys6ull14x14:~# echo 0 > /sys/class/gpio/gpio9/value
# 取消gpio9的导出
root@mys6ull14x14:~# echo 9 > /sys/class/gpio/unexport
再回头看下米尔MYS-6ULX板子GPIO_9的定义, 实际引脚是GPIO1_IO09:
引用下 控制蜂鸣器(GPIO子系统)中的介绍:
i.MX6ULL芯片GPIO引脚名格式通常为GPIOn_IOx,如GPIO1_IO09,其 中n是端口号,x为该组端口的引脚号,开发板采用的芯片有1-5组端口,每组端口包含的引脚从0-31不等。
export文件使用的编号index与GPIO引脚名的转换关系:index = GPIOn_IOx = (n-1)*32 + x
, 所以GPIO1_IO09对应的index是(1-1)*32+9 = 9
, 直接在原理图中命名为GPIO_9.
用C语言编写文件gpio.c:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#define GPIO_INDEX "9"
int main()
{
int fd;
//echo 9 > /sys/class/gpio/export
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0)
{
return 1;
}
write(fd, GPIO_INDEX, strlen(GPIO_INDEX));
close(fd);
//echo out > /sys/class/gpio/gpio9/direction
fd = open("/sys/class/gpio/gpio" GPIO_INDEX "/direction", O_WRONLY);
if (fd < 0)
{
return 2;
}
write(fd, "out", strlen("out"));
close(fd);
fd = open("/sys/class/gpio/gpio" GPIO_INDEX "/value", O_WRONLY);
if (fd < 0)
{
return 3;
}
while (1)
{
write(fd, "1", 1);
sleep(2);
write(fd, "0", 1);
sleep(2);
}
close(fd);
return 0;
}
交叉编译: arm-linux-gnueabihf-gcc -o gpio1 gpio.c
, 嵌入式板子上执行./gpio1
, 可以看到万用表3.3V和0V之间以2s间隔切换.
微信公众号
欢迎扫描关注我的微信公众号, 及时获取最新文章: