最简单的gpio
Linux对各种资源的调用是有相关API的,要尽量使用内核接口编写驱动程序,一能保证底层代码的质量,二能提高代码的移植性。关于GPIO资源的调用,要熟悉以下接口:
#include <linux/gpio.h>
struct gpio {
trueunsigned gpio; // GPIO编号
trueunsigned long flags; // GPIO复用功能配置
trueconst char *label; // GPIO标签名
};
// 单个GPIO资源申请/释放
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
void gpio_free(unsigned gpio);
// 多个GPIO资源申请/释放
int gpio_request_array(const struct gpio *array, size_t num);
void gpio_free_array(const struct gpio *array, size_t num);
// GPIO状态读写
int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value);
实现代码
下载源码
一、驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/gpio.h>
//设计一个类型,描述一个设备的信息
struct led_desc{
unsigned int dev_major; //设备号
struct class *cls;
struct device *dev; //创建设备文件
};
//定义gpio引脚功能
struct gpio led0 = {
.gpio = 17,
.flags = GPIOF_OUT_INIT_LOW, //设置为输出且初始值为低
.label = "led"
};
//定义一个全局的设备对象
struct led_desc *led_dev;
int open_led_dev(struct inode *ino, struct file *filp)
{
printk("open led_dev\n");
return 0;
}
ssize_t write_led_dev(struct file *filp, const char __user *buf, size_t count, loff_t *loff)
{
int value;
int ret;
ret = copy_from_user(&value, buf, count);
if(ret > 0)
{
printk("copy_from_user error\n");
return -EFAULT;
}
if(value == 1){
printk("led on!\n");
gpio_set_value(led0.gpio, 1);
}else{
printk("led off!\n");
gpio_set_value(led0.gpio, 0);
}
return 0;
}
struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = open_led_dev,
.write = write_led_dev
};
static int led_dev_init(void)
{
int ret;
//实例化全局的设备对象-->分配空间
led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
if(led_dev == NULL)
{
printk(KERN_ERR"kmalloc error\n");
return -ENOMEM;
}
//一般都是申请设备资源
//申请设备号(设置为0表示有内核自动分配可用设备号)
led_dev->dev_major = register_chrdev(0, "led_dev", &my_fops);
if(led_dev->dev_major < 0)
{
printk(KERN_ERR"register_chrdev error\n");
ret = -ENODEV;
goto err_0;
}
//创建设备文件
led_dev->cls = class_create(THIS_MODULE, "led_cls");
if(IS_ERR(led_dev->cls))
{
printk(KERN_ERR"class_create error\n");
ret = PTR_ERR(led_dev->cls);
goto err_1;
}
// /dev/led0
led_dev->dev = device_create(led_dev->cls, NULL,
MKDEV(led_dev->dev_major, 0), NULL, "led%d", 0);
if(IS_ERR(led_dev->dev))
{
printk(KERN_ERR"device_create error\n");
ret = PTR_ERR(led_dev->dev);
goto err_2;
}
//向内核申请gpio
ret = gpio_request_one(led0.gpio, led0.flags, led0.label);
if(ret < 0)
{
printk(KERN_ERR"gpio_request error\n");
goto err_3;
}
return 0;
err_3:
device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));
err_2:
class_destroy(led_dev->cls);
err_1:
unregister_chrdev(led_dev->dev_major, "led_dev");
err_0:
kfree(led_dev);
return ret;
}
static void led_dev_exit(void)
{
//一般都是释放资源
kfree(led_dev);
unregister_chrdev(led_dev->dev_major, "led_dev");
class_destroy(led_dev->cls);
device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));
gpio_free(led0.gpio);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
Makefile:
KERN_DIR=/home/yang/linux-rpi-4.19.y
all:
make -C $(KERN_DIR) M=`pwd` modules ARCH=arm CROSS_COMPILE=/home/yang/tools-master/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers modul* modules.order
obj-m += led_gpio.o
二、测试代码
#include "stdio.h"
#include "fcntl.h"
#include "unistd.h"
int main(int argc, char *argv[])
{
int ret;
int fd;
int val = 0;
if(argc != 2)
{
printf("error, Please input 1 argument\n");
return -1;
}
fd = open("/dev/led0", O_WRONLY);
if(fd < 0)
{
printf("open failed\n");
return -1;
}
if(strcmp(argv[1], "on") == 0)
{
val = 1;
printf("led on\n");
}
else if( strcmp(argv[1],"off") == 0 )
{
val = 0;
printf("led off\n");
}
write(fd, &val, 4);
close(fd);
return 0;
}
三、实现过程
yang@yang-Lenovo:~/my_cdev/LED/led_gpio$ ls
led_gpio.c led_gpio_test.c Makefile
#执行make
yang@yang-Lenovo:~/my_cdev/LED/led_gpio$ make
......
#执行后结果
yang@yang-Lenovo:~/my_cdev/LED/led_gpio$ ls
led_gpio.c led_gpio.ko led_gpio.mod.c led_gpio.mod.o led_gpio.o led_gpio_test led_gpio_test.c Makefile modules.order Module.symvers
#将文件拷贝到树莓派下
pi@raspberrypi:~/my_cdev/LED/led_gpio $ ls
led_gpio.ko led_gpio_test
#先清空系统消息
pi@raspberrypi:~/my_cdev/LED/led_gpio $ sudo dmesg -c
#注册设备号
pi@raspberrypi:~/my_cdev/LED/led_gpio $ sudo insmod led_gpio.ko
#查看注册结果
pi@raspberrypi:~/my_cdev/LED/led_gpio $ cat /proc/devices
...
236 led_dev
...
#查看设备文件并修改权限
pi@raspberrypi:~/my_cdev/LED/led_gpio $ ls -l /dev/led0
crw------- 1 root root 236, 0 11月 26 17:38 /dev/led0
pi@raspberrypi:~/my_cdev/LED/led_gpio $ sudo chmod 777 /dev/led0
pi@raspberrypi:~/my_cdev/LED/led_gpio $ ls -l /dev/led0
crwxrwxrwx 1 root root 236, 0 11月 26 17:38 /dev/led0
#测试成功
pi@raspberrypi:~/my_cdev/LED/led_gpio $ ./led_gpio_test on
led on
pi@raspberrypi:~/my_cdev/LED/led_gpio $ dmesg
[ 3778.450394] led_gpio: loading out-of-tree module taints kernel.
[ 3871.141005] open led_dev
[ 3871.141302] led on!
pi@raspberrypi:~/my_cdev/LED/led_gpio $ ./led_gpio_test off
led off
pi@raspberrypi:~/my_cdev/LED/led_gpio $ dmesg
[ 3778.450394] led_gpio: loading out-of-tree module taints kernel.
[ 3871.141005] open led_dev
[ 3871.141302] led on!
[ 3881.245663] open led_dev
[ 3881.245959] led off!
#卸载设备及删除设备文件
sudo rm /dev/led0
sudo rmmod led_gpio