目录
一、确定引脚编号
二、编写思路
2.1驱动层
2.2应用层
三、源码
四、实现
课程链接
一、确定引脚编号
首先,可以在开发板上执行如下命令查看已经在使用的GPIO状态:
cat /sys/kernel/debug/gpio
可以看到每个gpio都有对应的编号,如何查看自己想要的设备对应的gpio编号呢?
最简单的就是可以通过查看原理图 :
我所使用的开发板时IMX6ULL,所以下面也是IMX6ULL的开发板原理图上截取出来的
通过原理图我们可以观察到LED所在的引脚为GPIO5_3,意思是第五组中的第三个
根据上方我们用cat命令查看到的GPIO状态,可知一组GPIO为0-31,也就是三十二个。
GPIO1:0~31
GPIO2:32~63
GPIO5:4 x 32
GPIO5_3 : 4 x 32 + 3
可知LED引脚为 32 x 5 + 3 = 131
得出来引脚编号,就可以进行代码实现了。
二、编写思路
2.1驱动层
1.先构造file_operations结构体。
static struct file_operations led_drv = {
.owner = THIS_MOUDLE,
.read = led_drv_read,
.write = led_drv_write,
};
2.实现write和read函数
根据自己构造出来的的file_operations结构体,去实现具体的函数。
使用到的函数
- opy_to_user()
- copy_from_user()
- gpio_set_value
- gpio_get_value
在上一篇文章有具体介绍这篇文章所使用到的函数的作用和使用方法。
3.编写入口函数
用到的函数
- gpio_request
- gpio_direction_output
- register_chrdev
- class_create
- device_create
4.编写出口函数
- device_destroy
- class_destroy
- unregister_chrdev
- gpio_free
5.声明出口函数和入口函数,声明描述内核模块的许可权限
- module_init
- module_exit
- MODULE_LICENSE("GPL");
2.2应用层
根据传入参数的个数和值,判断用户想对那个引脚进行什么操作
实现选择控制LED亮灭,可读取GPIO电平状态
三、源码
led_drv.c
#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "asm/uaccess.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
struct gpio_desc {
int gpio;
int irq;
char *name;
};
static struct gpio_desc gpios[2] =
{
{131, 0, "led0"},
};
static int major = 0;
static struct class *led_class;
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
char kernel_buf[2];
int err;
if(size != 2)
{
return -EINVAL;
}
err = copy_from_user(kernel_buf, buf, 1);
if(kernel_buf[0] >= (sizeof(gpios)/sizeof(gpios[0])))
{
return -EINVAL;
}
kernel_buf[1] = gpio_get_value(gpios[kernel_buf[0]].gpio);
err = copy_to_user(buf, kernel_buf, 2);
return 2;
}
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
char kernel_buf[2];
int err;
if (size != 2)
{
return -EINVAL;
}
err = copy_from_user(kernel_buf, buf, 1);
if (kernel_buf[0] >= (sizeof(gpios)/sizeof(gpios[0])))
{
return -EINVAL;
}
gpio_set_value(gpios[kernel_buf[0]].gpio,kernel_buf[1]);
return 2;a
}
static struct file_operations led_drv = {
.owner = THIS_MODULE,
.read = led_drv_read,
.write = led_drv_write,
};
static int __init led_drv_init(void)
{
int err;
int i;
int count = sizeof(gpios)/sizeof(gpios[0]);
for (i = 0; i < count; i++)
{
err = gpio_request(gpios[i].gpio, gpios[i].name);
if (err < 0)
{
printk("can not request gpio %s %d\n", gpios[i].name, gpios[i].gpio);
return -ENODEV;
}
gpio_direction_output(gpios[i].gpio, 1);
}
/* 注册file_operations结构体 */
major = register_chrdev(0, "led_drv",&led_drv);
led_class = class_create(THIS_MODULE, "drv_class");
if (IS_ERR(led_class))
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "led_class");
return PTR_ERR(led_class);
}
device_create(led_class, NULL, MKDEV(major, 0), NULL, "led_drv");
return 0;
}
static void __exit led_drv_exit(void)
{
int i;
int count = sizeof(gpios)/sizeof(gpios[0]);
device_destroy(led_class, MKDEV(major, 0));
class_destroy(led_class);
unregister_chrdev(major, "led_drv");
for(i = 0; i< count; i++)
{
gpio_free(gpios[i].gpio);
}
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
led_drv_test.c
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
int main(int argc, char **argv)
{
int ret;
int fd;
char buf[2];
if (argc < 2)
{
printf("Usage : %s <0> [on|off]", argv[0]);
return -1;
}
fd = open("/dev/led_drv", O_RDWR);
if (fd == -1)
{
printf("can not open file /dev/100ask_led\n");
return -1;
}
if (argc == 3)
{
buf[0] = strtol(argv[1],NULL, buf[0]);
if(strcmp(argv[2],"on") == 0)
{
buf[1] = 0;
}
else
{
buf[1] = 1;
}
ret = write(fd, buf, 2);
}
else
{
buf[0] = strtol(argv[1], NULL, 0);
ret = read(fd, buf, 2);
if (ret == 2)
{
printf("led %d status is %s\n", buf[0], buf[1] == 0 ? "on" : "off");
}
}
close(fd);
return 0;
}
四、实现
在开发板上执行以下命令,将生成的.ko文件装载到内核
insmod led_drv.ko
课程链接
36_模板1实战_LED驱动上机_STM32MP157_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1XK411D7wK/?p=37&spm_id_from=pageDriver&vd_source=3a9afee9fda50350a1c881b4325e007d