Linux驱动入门实验班——LED驱动(附百问网视频链接)

目录

一、确定引脚编号

二、编写思路

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_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1XK411D7wK/?p=37&spm_id_from=pageDriver&vd_source=3a9afee9fda50350a1c881b4325e007d

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值