栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

基于树莓派4B的Linux驱动------点亮LED灯

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于树莓派4B的Linux驱动------点亮LED灯

基于树莓派4B的Linux驱动------点亮LED灯

美好的一天从点灯开始
学好Linux驱动从点灯大师做起

本人也是接触Linux不久,可能有些问题也没考虑到,以下仅是个人观点,欢迎留言,共同进步,话不多说,直接步入正题。

一、实验说明

本次实验采用设备树编程
开发板基于树莓派4B
linux内核版本:linux-rpi-5.15.y
开发平台:ubuntu交叉编译
LED为高电平点亮

二、修改设备树文件

为了方便,我是直接在根节点下添加一个ledtest节点,要不然放得太深不好找
首先在linux内核中找到对应的设备树文件
这里我是修改了bcm2711-rpi-4.dts文件
linux内核设备树文件路径在arch/arm/boot/dts目录下,可以在该目录下找到该文件
设备树的语法我这里不细讲,设备树的语法可以自己去搜,网上一搜一大把
在根节点下添加以下节点

ledtest{
	#address-cells = <1>;
    #size-cells = <1>;
	compatible = "ledtest";
	pinctrl-names = "default";
	gpios = <&gpio 26 GPIO_ACTIVE_HIGH>;
    status = "okay";
};

属性#address-cells 和#size-cells 都为 1,表示 reg 属性中起始地址占用一个字长(cell),地址长度也占用一个字长(cell)
属性 compatbile 设置 ledtest节点兼容性为“ledtest”
pinctrl-names 属性,此属性描述 pinctrl 名字为“default”。
gpios 属性值,表示此 LED 所使用的 GPIO 引脚的信息
属性 status 设置状态为“okay”。

本次我们使用的引脚编码是: BCM 26
可以使用gpio readall 命令查看树莓派引脚分布和状态,设置自己想要设置的引脚,不一定要是26
保存然后编译设备树文件,使用命令make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
然后把编译好的.dtb文件放到板子里面

三、编写驱动程序

然后就可以写驱动程序了,我们可以使用of函数来读取设备树里面的内容,以下是我用到的of函数
of_find_node_by_path 用来打开设备节点
of_get_named_gpio 用来获取设备树中的某个属性
然后通过读取设备树属性获取到引脚编码,再通过gpio_direction_output函数配置引脚为输出,gpio_set_value函数来设置引脚输出的电平,后面加上注册设备号,注册字符设备,自动创建设备节点,这样编程的思路就大致理好了。
我这里是把控制LED的程序写在了gpioled_write函数里面,目的是能让应用层来控制LED的亮灭,为了方便我这里把printk的打印级别设置为最高了,即KERN_EMERG。copy_from_user函数的作用从用户空间拷贝数据到内核空间,话不多说,直接上代码。
gpioled.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define GPIOLED_NAME        "gpioled"
#define GPIOLED_COUNT        1
#define LEDOFF               0			
#define LEDON                1			



struct gpioled_dev
{
    struct cdev cdev;         
    dev_t devid;              
    struct class *class;      
    struct device *device;    
    int major;                
    int minor;                
    struct device_node	*nd;  
	int led_gpio;             
};

static struct gpioled_dev gpioled;

static int gpioled_open(struct inode *inode, struct file *filp)
{
    printk(KERN_EMERG "gpioled_openn");
    filp->private_data = &gpioled;
    return 0;
}

static int gpioled_release(struct inode *inode, struct file *filp)
{
    printk(KERN_EMERG "gpioled_releasen");
    return 0;
}

static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;
	struct gpioled_dev *dev = filp->private_data;

    printk(KERN_EMERG "gpioled_writen");
	retvalue = copy_from_user(databuf, buf, count);
	if(retvalue < 0) {
		printk(KERN_EMERG "kernel write failed!rn");
		return -EFAULT;
	}

	ledstat = databuf[0];		

	if(ledstat == LEDON)
    {
        printk(KERN_EMERG "打开led灯n");
		gpio_set_value(dev->led_gpio, 1);	
	}
    else if(ledstat == LEDOFF)
    {
        printk(KERN_EMERG "关闭led灯n");
		gpio_set_value(dev->led_gpio, 0);	
	}

    return 0;
}


static const struct file_operations gpioled_fops = {
    .owner = THIS_MODULE,
    .open = gpioled_open,
    .release = gpioled_release,
    .write = gpioled_write,
};



static int __init gpioled_init(void)
{
    int ret;

    printk(KERN_EMERG "gpioled_initn");
    gpioled.major = 0;

    
	
	gpioled.nd = of_find_node_by_path("/ledtest");
	if(gpioled.nd == NULL)
    {
		printk(KERN_EMERG "ledtest node not find!rn");
		return -EINVAL;
	}

	
	gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "gpios", 0);
	if(gpioled.led_gpio < 0)
    {
		printk(KERN_EMERG "can't get gpios");
		return -EINVAL;
	}
	printk(KERN_EMERG "gpios num = %dn", gpioled.led_gpio);
	ret = gpio_direction_output(gpioled.led_gpio, 1);
	if(ret < 0)
    {
		printk(KERN_EMERG "can't set gpio!n");
	}

    
    if(gpioled.major)
    {
        gpioled.devid = MKDEV(gpioled.major, 0);
        ret = register_chrdev_region(gpioled.devid, GPIOLED_COUNT, GPIOLED_NAME);
    }
    else
    {
        ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_COUNT, GPIOLED_NAME);
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
    }
    if(ret < 0)
    {
        printk(KERN_EMERG "gpioled chrdev_region err!n");
        goto fail_devid;
    }
    printk(KERN_EMERG "gpioled major:%d, minor:%dn", gpioled.major, gpioled.minor);

    
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_fops);
    ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_COUNT);
    if(ret < 0)
    {
        goto fail_cdev;
    }

    
    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if(IS_ERR(gpioled.class))
    {
        ret = PTR_ERR(gpioled.class);
        goto fail_class;
    }
    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if(IS_ERR(gpioled.device))
    {
        ret = PTR_ERR(gpioled.device);
        goto fail_device;
    }

    return 0;

fail_device:
    class_destroy(gpioled.class);
fail_class:
    cdev_del(&gpioled.cdev);
fail_cdev:
    unregister_chrdev_region(gpioled.devid, GPIOLED_COUNT);
fail_devid:
    return ret;

}



static void __exit gpioled_exit(void)
{
    printk(KERN_EMERG "gpioled_exitn");

    
    cdev_del(&gpioled.cdev);

    
    unregister_chrdev_region(gpioled.devid, GPIOLED_COUNT);

    
    device_destroy(gpioled.class, gpioled.devid);

    
    class_destroy(gpioled.class);
}



module_init(gpioled_init);
module_exit(gpioled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
四、编写MakeFile程序

MakeFile

KERNELDIR := /home/pi/linux/pi4/linux-rpi-5.15.y

CURRENT_PATH := $(shell pwd)

obj-m := gpioled.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
五、编写APP程序

gpioledAPP.c

#include 
#include 
#include 
#include 
#include 
#include 


int main(int argc, char *argv[])
{
    char *pathname;
    char data;
    int fd;

    pathname = argv[1];

    if(argc != 3)
    {
        printf("%s 输入错误!n", pathname);
        return -1;
    }
    if(argv[2][1] != '')
    {
        printf("%s 输入错误!n", pathname);
        return -1;
    }

    fd = open(pathname, O_RDWR);
    if(fd < 0)
    {
        printf("open %s error!n", pathname);
    }

    data = (char)atoi(argv[2]);
    if(write(fd, &data, sizeof(data)) < 0)
    {
        printf("write %s error!n", pathname);
    }

    if(close(fd) == -1)
    {
        printf("close %s error!n", pathname);
    }

    return 0;
}

六、编译测试

在工作目录下
命令行输入make编译驱动程序
命令行输入arm-linux-gnueabihf-gcc gpioledAPP.c -o gpioledAPP 编译APP应用程序
传到板子上去,使用insmod命令加载驱动模块
sudo ./gpioledAPP /dev/gpioled 1 能实现亮灯
sudo ./gpioledAPP /dev/gpioled 0 能实现灭灯
好的,实验完成

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/842447.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号