版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/windsnow1/article/details/88123144
摘要:
从开发基于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 。若有问题,欢迎指正。
扫描二维码关注公众号,回复:
5451367 查看本文章