驱动开发LED灯绑定设备文件

头文件

cs 复制代码
#ifndef __HEAD_H__
#define __HEAD_H__

typedef struct 
{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;


#define PHY_LED1_ADDR 0x50006000
#define PHY_LED2_ADDR 0x50007000
#define PHY_LED3_ADDR 0x50006000
#define PHY_RCC_ADDR 0x50000A28
#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)

#endif

应用程序

cs 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "head.h"
int main(int argc,char const *argv[])
{
    char buf[128]={0};
    int a,b;
    int fd=open("/dev/demo0",O_RDWR);
    if(fd<0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while(1)
    {
        //从终端读取
        printf("请输入要实现的功能 ");
        printf("0(关灯) 1(开灯)\n");
        printf("请输入>");
        scanf("%d",&a);
        printf("请选择要控制的灯:1(LED1)2(LED2) 3(LED3)\n");
        printf("请输入>");
        scanf("%d",&b);
        switch(a)
        {
            case 1:
                ioctl(fd,LED_ON,&b);
                break;
            case 0:
                ioctl(fd,LED_OFF,&b);
                break;
        }
    }

    
    close(fd);

    return 0;
}

驱动程序

cs 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include "head.h"
unsigned int major=0;
unsigned int minor=0;
char kbuf[128]={};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct cdev *cdev;
dev_t devno;
struct class *cls;
struct device *dev;

int mycdev_open(struct inode *inode, struct file *file)
{
    int min=MINOR(inode->i_rdev);
    file->private_data=(void *)min;
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    int min=(int)file->private_data;
    switch (min)
    {
    case 0: // 操作LED1
        switch (cmd)
        {
        case LED_ON: //开灯
            vir_led1->ODR |= (0x1<<10);
            break;
        case LED_OFF://关灯
            vir_led1->ODR &= (~(0x1<<10));
            break;
        }
        break;
    case 1: // 操作LED2
        switch (cmd)
        {
        case LED_ON: //开灯
            vir_led2->ODR |= (0x1<<10);
            break;
        case LED_OFF://关灯
            vir_led2->ODR &= (~(0x1<<10));
            break;
        }
        break;
    case 2: // 操作LED3
        switch (cmd)
        {
        case LED_ON: //开灯
            vir_led3->ODR |= (0x1<<8);
            break;
        case LED_OFF://关灯
            vir_led3->ODR &= (~(0x1<<8));
            break;
        }
        break;
    }
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

struct file_operations fops =
{
    .open=mycdev_open,
    .unlocked_ioctl=mycdev_ioctl,
    .release=mycdev_close,
};

int all_led_init(void)
{
    //寄存器地址的映射
    vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led3=vir_led1;
    vir_rcc=ioremap(PHY_RCC_ADDR,4);
    if(vir_rcc==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    //寄存器的初始化
    //rcc
    (*vir_rcc) |= (3<<4);
    //led1
    vir_led1->MODER &= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR &= (~(1<<10));
    //led2
    vir_led2->MODER &= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR &= (~(1<<10));
    //led3
    vir_led3->MODER &= (~(3<<16));
    vir_led1->MODER |= (1<<16);
    vir_led1->ODR &= (~(1<<8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
        //1.申请一个对象空间cdev_alloc
    int ret;
    cdev= cdev_alloc();
    if(cdev==NULL)
    {
        printk("申请字符设备驱动对象失败\n");
        ret=-EFAULT;
        goto out1;

    }
    printk("字符设备驱动对象申请成功\n");
    //2.初始化对象cdev_init
    cdev_init(cdev,&fops);
    //3.申请设备号  register_chrdev_region()/alloc_chrdev_region()
    if(major==0)//动态申请
    {
        ret=alloc_chrdev_region(&devno,minor,3,"demo");
        if(ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major=MAJOR(devno);//根据设备号获取主设备号
        minor=MINOR(devno);//根据设备号获取次设备号
    }
    else
    {
        ret=register_chrdev_region(MKDEV(major,minor),3,"demo");
        if(ret)
        {
            printk("静态指定设备号失败\n");
            goto out2;
        }
    }
    printk("设备号申请成功\n");
    //4.注册驱动对象  cdev_add
    ret=cdev_add(cdev,MKDEV(major,minor),3);
    if(ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    //5.向上提交目录  class_create
    cls=class_create(THIS_MODULE,"demo");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        goto out4;
    }
    printk("向上提交目录成功\n");
    //6.向上提交设备节点信息  device_create
    int i;
    for(i=0;i<3;i++)
    {
        dev=device_create(cls,NULL,MKDEV(major,i),NULL,"demo%d",i);
        if(IS_ERR(dev))
        {
            printk("向上提交设备节点失败\n");
            goto out5;
        }
    }
    printk("向上提交设备节点信息成功\n");
    
    all_led_init();
    return 0;
out5:
//将提交成功的节点信息释放
    for(--i;i>=0;i--)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //销毁目录
    class_destroy(cls);
out4:
    cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major,minor),3);
out2:
    kfree(cdev);
out1:
    return ret;

}
static void __exit mycdev_exit(void)
{
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);

//1.销毁设备节点信息
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //2.销毁目录
    class_destroy(cls);
    //3.注销字符设备驱动对象
    cdev_del(cdev);
    //4.释放设备号
    unregister_chrdev_region(MKDEV(major,minor),3);
    //5.释放申请到的字符设备驱动对象空间
    kfree(cdev);
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
相关推荐
cxr8281 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot1 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶2 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot3 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot3 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday3 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8285 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday5 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发