树莓派驱动之GPIO

最简单的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
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值