树莓派Pi 3B设备驱动程序开发_LED点灯程序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/windsnow1/article/details/88123144

树莓派Pi 3B设备驱动程序开发_LED点灯程序

摘要:

从开发基于Linux的树莓派设备驱动开始,学习近一个星期,从一开始理解驱动程序与内核、应用程序的关系,到驱动程序基本框架,到树莓派的本地编译和交叉编译。今天,终于完成了驱动程序入门的第一个程序,实现了LED的亮灭。虽然这个程序很简单,但是基本包括开发的基本流程。

硬件准备:

我在树莓派Raspberry Pi 3 Model B的GPIO2引脚与GND之间接了一个LED灯,带保护电阻。如果GPIO2输出高电平则LED亮起,否则熄灭。

直接贴上源码:

led_drv.c

#include <linux/fs.h>                        //file_operations声明
#include <linux/module.h>              //module_init module_exit声明
#include <linux/init.h>                      //__init __exit 宏定义声明
#include <linux/device.h>                //class device声明
#include <linux/uaccess.h>             //copy_from_user的头文件
#include <linux/types.h>                 //设备号 dev_t 类型声明
#include <asm/io.h>                       // ioremap iounmap的头文件

static struct class *leddrv_class;
static struct device *leddrv_class_dev;

static dev_t devno;                 //设备号
static int major = 231;           //主设备号
static int minor = 0;               //次设备号
static char *module_name = "led_drv";    //模块名

volatile unsigned long *gpfsel0 = NULL;     //功能选择寄存器指针
volatile unsigned long *gpset0  = NULL;    //设置为高电平寄存器指针
volatile unsigned long *gpclr0  = NULL;     //设置为低电平寄存器指针

//led_open函数
static int led_open(struct inode *inode, struct file *file)
{
	//printk("led_open\n");
	//CONFIGURE GPIO 2 OUTPUT   001 GPIO Pin 2 is an output
	*gpfsel0 &= ( ~( 0x6 << ( 2 * 3 ) ) );
	*gpfsel0 |= ( 0x1 << ( 2 * 3 ) );
	return 0;
}

//led_write函数
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	int val = 0;
	copy_from_user(&val, buf, count);
	if( val == 1)
	{
		//led on
		printk("led on\n");
		*gpset0 |= (0x1 << 2);
	}
	else
	{
		//led off
		printk("led off\n");
		*gpclr0 |= (0x1 << 2);
	}
	return 0;
}

static struct file_operations leds_fops = {
	.owner = THIS_MODULE,
	.open  = led_open,
	.write = led_write,
};

int __init led_drv_init(void)
{
	int ret;	
	devno = MKDEV(major,minor);
	ret = register_chrdev( major,  module_name, &leds_fops);                      //注册驱动 告诉内核

	leddrv_class = class_create( THIS_MODULE, "myfirstdemo" );

	leddrv_class_dev = device_create( leddrv_class, NULL, devno, NULL, module_name );  //创建设备文件

	gpfsel0 = (volatile unsigned long *)ioremap( 0x3f200000, 4);
	gpset0  = (volatile unsigned long *)ioremap( 0x3f20001C, 4);
	gpclr0  = (volatile unsigned long *)ioremap( 0x3f200028, 4);
	return 0;
}

void __exit led_drv_exit(void)
{
	device_destroy(leddrv_class,devno);
	class_destroy(leddrv_class);

	unregister_chrdev( major,  module_name);              //卸载驱动
	iounmap(gpfsel0);
	iounmap(gpset0);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL v2");

Makefile

KERN_DIR=/home/dr/raspberry_src/linux-rpi-4.14.y
all:
	make -C $(KERN_DIR) M=`pwd` modules ARCH=arm CROSS_COMPILE=/home/dr/raspberry_src/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_drv.o

测试程序
led_test.c

#include <stdio.h>
#include <fcntl.h>     //open


/*
 * led_test on
 * led_test off
 */

int main(int argc, char **argv)
{
	int fd;
	int val = 0;
	if(argc != 2)
	{
		printf("Usage: \n");
		printf("%s: <on|off>\n", argv[0]);     //<> must have on | off
		return 0;
	}
	
	fd = open("/dev/led_drv",O_RDWR);
	if( fd < 0 )
	{
		printf("can not open\n");
	}
	
	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);
}

欢迎大家有问题留言一起讨论,我也是磕磕碰碰很久才把这个东西搞懂。
不如不想复制粘贴源码,提供下载链接:https://download.csdn.net/download/windsnow1/10992900 。若有问题,欢迎指正。

猜你喜欢

转载自blog.csdn.net/windsnow1/article/details/88123144