字符设备驱动分步注册实现LED驱动的编写

news2025/4/21 20:57:28

头文件

#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 RCC            0x50000A28
#define LED1_ADDR      0x50006000
#define LED2_ADDR      0x50007000
#define LED3_ADDR      0x50006000

#define LED_ON  _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)
#endif

应用层文件

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

驱动文件

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>

#include<linux/slab.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>

#include"head.h"

struct cdev *cdev;
unsigned int major=0;//主设备号
unsigned int minor=0;//次设备号

char kbuf[128]={0};

struct class *cls;
struct device *dev;

unsigned int *vir_rcc;
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;


dev_t devno;

int mycdev_open(struct inode *inode,struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
   printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
   unsigned long ret;
   if(size>sizeof(kbuf))
      size=sizeof(kbuf);
   ret=copy_to_user(ubuf,kbuf,size);
   if(ret)
   {
    printk("copy_to_user filed\n");
    return ret;
   }
     return 0;

}

ssize_t mycdev_write(struct file *file, const char  *ubuf, size_t size, loff_t *lof)
{
   unsigned long ret;
   if(size>sizeof(kbuf))
      size=sizeof(kbuf);
   ret=copy_from_user(kbuf,ubuf,size);
   if(ret)
   {
    printk("copy_to_user filed\n");
    return ret;
   }
     return 0;
}
long mycdev_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    int wh;
    int ret=copy_from_user(&wh,(void*)arg,4);
    if(ret)
    {
        printk("copy_from_user filed\n");
        return ret;
    }
    switch(cmd)
    {
        case LED_ON:
        switch(wh)
        {
            case 1:
             vir_led1->ODR |= (1<<10);
             break;
            case 2:
             vir_led2->ODR |= (1<<10);
             break;
            case 3:
             vir_led3->ODR |= (1<<8);
             break;
        } 
            break;
        case LED_OFF:
            switch(wh)
        {
            case 1:
              vir_led1->ODR &= (~(1<<10));
             break;
            case 2:
              vir_led2->ODR &= (~(1<<10));
             break;
            case 3:
              vir_led3->ODR &= (~(1<<8));
             break;
        } 
           
            break;
    }
    return 0;
}
int mycdev_close(struct inode *inode,struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
int all_led_init(void)
{
    vir_led1=ioremap(LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("led1内存映射失败");
        return -ENOMEM;
    }
    printk("led1内存映射成功");

    vir_led2=ioremap(LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("led2内存映射失败");
        return -ENOMEM;
    }
    printk("led2内存映射成功");

    vir_led3=ioremap(LED3_ADDR,sizeof(gpio_t));
    if(vir_led3==NULL)
    {
        printk("led3内存映射失败");
    }
    printk("led3内存映射成功");

    
    vir_rcc=ioremap(RCC,4);
    if(vir_rcc==NULL)
    {
        printk("rcc内存映射失败");
    }
    printk("rcc内存映射成功");
  
    //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_led3->MODER |= (1<<16);
    vir_led3->ODR   &= (~(1<<8));

    printk("寄存器初始化成功\n");
    return 0;
}
struct file_operations fops=
{
  .open=mycdev_open,
  .read=mycdev_read,
  .write=mycdev_write,
  .unlocked_ioctl=mycdev_ioctl,
  .release=mycdev_close,
};
static int __init mycdev_init(void)
{
    int ret;
    //申请字符设备的驱动空间
    cdev = cdev_alloc();
   if (cdev == NULL)
    {
        return -EFAULT;
    }
    printk("字符设备驱动对象申请成功\n");
    //初始化驱动对象
    cdev_init(cdev,&fops);

    
    //动态申请
    if(major==0)
    {
        ret=alloc_chrdev_region(&devno,minor, 3,"myled");
        if(ret)
        {
            printk("动态申请设备号失败\n");
            goto out1;
        }
        major=MAJOR(devno);
        minor=MAJOR(devno);
    }
    //静态指定
    else
    {
      ret=register_chrdev_region(MKDEV(major,minor), 3,"myled");
      if(ret)
      {
        printk("静态申请设备号失败\n");
        goto out1;
      }
    }
    printk("设备号申请成功\n");
    
    //注册驱动
    ret=cdev_add(cdev,MKDEV(major,minor),3);
    if(ret)
    {
        printk("注册驱动失败\n");
        goto out2;
    }
    printk("注册驱动成功\n");
   
    //向上提交目录
    cls=class_create(THIS_MODULE,"led");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret=-PTR_ERR(cls);
        goto out3;
    }
    printk("向上提交目录成功\n");
   
   //向上提交设备节点

 int i;
 for(i=0;i<3;i++)
 {
   dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
    if(IS_ERR(dev))
  {
    printk("向上提交设备信息失败\n");
    ret=-PTR_ERR(dev);
    goto out4;
  }
 }
 printk("向上提交设备信息成功\n");
   //寄存器映射以及初始化
   all_led_init();
    return 0;
out4:
//销毁提交设备成功的设备信息
   for ( --i; i>=0; i--)
   {
     device_destroy(cls,MKDEV(major,i));
    /* code */
   }
    class_destroy(cls);
out3:
   cdev_del(cdev);
out2:
   unregister_chrdev_region(MKDEV(major,minor),3);
out1:
   kfree(cdev);
   return ret;
   
}
static void __exit mycdev_exit(void)
{
    
  //取消地址映射
  iounmap(vir_rcc); 
  iounmap(vir_led1);
  iounmap(vir_led2);
  iounmap(vir_led3);

  //销毁设备节点
  int i;
  for ( i = 0; i < 3; i++)
  {
    device_destroy(cls,MKDEV(major,i));
  }
  //销毁目录
  class_destroy(cls);
  //注销驱动对象
   cdev_del(cdev); 
   //释放设备号
    unregister_chrdev_region(MKDEV(major,minor),3);
  //释放空间
  kfree(cdev);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

makefile

arch?=arm
modname?=register_dev_c
ifeq ($(arch),arm)
KERNELDIR:=/home/ubuntu/linux-5.10.61
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
endif
PWD:=$(shell pwd)

all:
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
	make -C $(KERNELDIR) M=$(PWD) clean 
obj-m:= $(modname).o

执行效果

开发板效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1454871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity求物体关于平面镜像对称后坐标以及旋转

前言&#xff1a;如题&#xff0c;我在已知一个平面L和物体A&#xff0c;我希望得到镜像后的物体B的位置和旋转。 效果&#xff1a; 推导&#xff1a; 首先我们需要知道物体的对称坐标A&#xff0c;我们现在能已知A坐标以及平面L的法线&#xff0c;如果我们能得到B的坐标&…

芯品荟|吉他屏驱应用介绍

PART ONE 市场简介 - Market Profile - 古典吉他与小提琴、钢琴并列为世界著名三大乐器。 目前&#xff0c;带屏成为吉他产品的新发展趋势。 核心应用 调音器、节拍器、录音器、效果、练习、循环乐段。 特色应用 4.3寸以下TFT屏 分辨率800*480以下 不带音弦按键替代&…

java项目的构建流程

1.创建项目 2.创建模块 创建时要注意组ID的命名 通常包含以下模块: 项目的pom文件中,依赖如下(web模块不需要依赖,也不需要main文件夹): 3.配置pom文件 1),主pom文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://mav…

2.18通过字符设备驱动分步注册过程实现LED驱动的编写,编写应用程序测试

应用程序&#xff1a; #include<stdlib.h> #include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> #include<string.h> #include<sys/ioctl.h> #include"myled.h&quo…

ubuntu22.04-磁盘管理-虚拟机动态扩容-系统monitor

文章目录 1.虚拟机2.ubuntu设置3.命令查看4.系统资源管理器1.虚拟机 关闭ubuntu22.04,然后修改虚拟机设置,如下图所示: 修改容量 2.ubuntu设置 搜索打开disks,如下图所示: 选择目标磁盘,选择调整大小到目标大小即可。

vmware-17虚拟机安装教程及版本密钥(保姆级,包含图文讲解,不需注册账户)

文章目录 vmware安装教程一、下载vmware二、安装三、破解密匙 vmware安装教程 一、下载vmware 1.进入VMware官网&#xff1a;https://www.vmware.com/sg/products/workstation-pro.html 2.向下翻找到&#xff0c;如下界面并点击“现在安装” 3.稍事等待以下直到出现以下界面…

医疗器械企业融资排行榜,敷尔佳、心脉医疗、安杰思医学、西山科技、康诺思腾融资总额超70亿元!

21世纪以来&#xff0c;中国医疗器械市场经历多年高速增长&#xff0c;产业集聚度、全球化发展进程不断提升&#xff0c;医疗器械行业竞争加剧。对于医疗器械新兴企业而言&#xff0c;获得融资不仅是支持其技术创新和产品研发生产的重要资金来源&#xff0c;更是推动企业扩张发…

怎么恢复电脑重装前的数据?介绍几种有效的方法

在日常生活和工作中&#xff0c;电脑已成为我们不可或缺的工具。然而&#xff0c;有时候我们会遇到一些突发情况&#xff0c;比如电脑系统崩溃需要重新安装系统。在这个过程中&#xff0c;我们可能会失去一些重要的数据&#xff0c;比如照片、文档、视频等。这些数据可能包含着…

YOLOv8改进 | Conv篇 | 利用FasterBlock二次创新C2f提出一种全新的结构(全网独家首发,参数量下降70W)

一、本文介绍 本文给大家带来的改进机制是利用FasterNet的FasterBlock改进特征提取网络,将其用来改进ResNet网络,其旨在提高计算速度而不牺牲准确性,特别是在视觉任务中。它通过一种称为部分卷积(PConv)的新技术来减少冗余计算和内存访问。这种方法使得FasterNet在多种设…

速看!2024年泰国国际电力能源展10月16-18日

2024年泰国&#xff08;亚洲&#xff09;国际电力能源展暨电工技术设备展 展会时间&#xff1a;2024年10月16-18日 展会地点&#xff1a;泰国.曼谷BITEC会展中心 主办单位&#xff1a;新加坡Fireworks展览集团 组织单位&#xff1a;武汉柏翰展览有限公司(Fireworks China) …

vue框架-vue-cli

vue-cli Vue CLI是一个官方的脚手架工具,用于快速搭建基于Vue.js的项目。Vue CLI提供了一整套可配置的脚手架,可以帮助开发人员快速构建现代化的Web应用程序。 Vue CLI通过提供预先配置好的Webpack模板和插件,使得开发人员可以在不需要手动编写Webpack配置的情况下快速创建…

15.一种坍缩式的简单——组合模式详解

当曾经的孩子们慢慢步入社会才知道&#xff0c;那年味渐淡的春节就像是疾驰在人生路上的暂停键。 它允许你在隆隆的鞭炮声中静下心来&#xff0c;瞻前顾后&#xff0c;怅然若失。 也允许你在寂静的街道上屏气凝神&#xff0c;倾听自己胸腔里的那团人声鼎沸。 孩子们会明白的&am…

信息安全认证 | CISP证书怎么样?值得考吗?

HCIE考证研究所的朋友们&#xff0c;新年快乐&#xff01; 今天给大家说说CISP证书&#xff0c;新的一年祝大家逢考必过啊~ 01 考注册信息安全工程师证书的用处 CISP证书可作为学识和技能证明&#xff1b;求职、任职、晋升、加薪的资格凭证&#xff1b;用人单位招聘、录用劳动…

论文精读--对比学习论文综述

InstDisc 提出了个体判别任务&#xff0c;而且利用这个代理任务与NCE Loss去做对比学习从而得到了不错的无监督表征学习的结果&#xff1b;同时提出了别的数据结构——Memory Bank来存储大量负样本&#xff1b;解决如何对特征进行动量式的更新 翻译&#xff1a; 有监督学习的…

Instagram 账号被封如何申诉?ins账号解封经验分享

不知道各位在玩转海外社媒平台时有没有遇到过Instagram账号异常的情况&#xff0c;比如会出现账号受限、帖子发不出去、账号被封号等情况?Instagram账号如果被封不用马上弃用&#xff0c;我们可以先尝试一下申诉&#xff0c;看看能不能把账号解封。所以今天将会出一篇Instagra…

19.Qt 组合框的实现和应用

目录 前言&#xff1a; 技能&#xff1a; 内容&#xff1a; 1. 界面 2.槽 3.样式表 参考&#xff1a; 前言&#xff1a; 学习QCombox控件的使用 技能&#xff1a; 简单实现组合框效果 内容&#xff1a; 1. 界面 在ui编辑界面找到input widget里面的comboBox&#xff…

如何使用HTTP隧道在Linux环境下构建内网穿透解决方案

你是否曾经遇到过这样的尴尬场景&#xff1a;身处内网环境&#xff0c;却想要让外部世界的朋友访问你的某个服务&#xff0c;却发现那堵墙——防火墙&#xff0c;如同一座不可逾越的山峰&#xff0c;挡住了你的去路&#xff1f;别担心&#xff0c;今天我们就来聊聊如何在Linux环…

ad18学习笔记十八:如何单独设置某一铺铜与导线的间距

网上找的很多内容都是ad18之前的旧版本&#xff0c;ad18对应的介绍特别少。 直接设置全局的铺铜规格比较容易&#xff1a; Altium Designer教程系列&#xff1a;深入学习铺铜操作 (baidu.com) Altium Designer规则及覆铜设计小技巧 (baidu.com) 单独给某一片铺铜区域设置规则…

中国传媒网CEO徐晓艺:第六届世界布商大会启幕 共探全球纺织业转型与合作

日前,2023国际纺织制造商联合会中国绍兴柯桥大会、2023第六届世界布商大会在浙江绍兴柯桥启幕,来自全球55个国家和地区的纺织行业代表围绕“绿色、循环、数字化——纺织工业新动源”主题,共探全球纺织业转型与合作。 “当前,纺织服装行业进入变革期,以数字、绿色为特征的产业变…

day 20 (标准IO 与 文件IO)

标准IO与文件IO的区别 标准IO和文件IO的区别&#xff1a;1.标准IO是库函数,是对系统调用的封装2.文件IO是系统调用,是Linux内核中的函数接口3.标准IO是有缓存的4.文件IO是没有缓存的IO:b cd - 标准IOl s p文件IO:1.操作步骤&#xff1a;打开 -> 读/写 -> 关闭2.打开文件…