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

头文件

#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

执行效果

开发板效果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

序列发生器

一开始想直接FSM&#xff0c;划分出6状态依次输出对应的。但其实只要6比特的移位寄存器&#xff0c;每次输出高位。复位后的默认值时6’b001_011。这样就可以实现循环&#xff0c;这种移位寄存器也叫barrel_shifter。循环移位。也可以使用循环计数器&#xff0c;然后case计数器…

MATLAB知识点:meshgrid函数(★★★★☆)返回二维网格坐标(在MATLAB中经常用于生成绘制三维图的数据)

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…

从 AGP 4.1.2 到 7.5.1——XmlParser、GPathResult、QName 过时

新年首发&#xff0c; 去年的问题&#xff0c;今年解决~ 问题 & 排查 1: Task failed with an exception. ----------- * What went wrong: Execution failed for task :app:processCommonReleaseManifest. > org.xml.sax.SAXParseException; lineNumber: 1; columnNu…

Java学习--黑马SpringBoot3课程个人总结-2024-02-15

1.未登录统一处理 2.添加文章分类 //控制添加分类弹窗 const dialogVisible ref(false)//添加分类数据模型 const categoryModel ref({categoryName: ,categoryAlias: }) //添加分类表单校验 const rules {categoryName: [{ required: true, message: 请输入分类名称, tri…

element 表单提交图片(表单上传图片)

文章目录 使用场景页面效果前端代码 使用场景 vue2 element 表单提交图片   1.点击【上传图片】按钮择本地图片&#xff08;只能选择一张图片&#xff09;后。   2.点击图片&#xff0c;支持放大查看。   3.点击【保存】按钮&#xff0c;提交表单。 页面效果 前端代码…

OBD部署OceanBase集群-配置文件方式

前一篇文章介绍了OBD白屏可视化方式部署OceanBase集群 &#xff0c;其原理是把可视化设置生成为一个配置文件&#xff0c;然后使用OBD命令部署集群 本篇想使用命令行加配置文件方式&#xff0c;只部署OceanBase和ODProxy两个组件 服务器参数配置和 oceanbase-all-in-one-*.ta…

洛谷: P1553 数字反转(升级版)

思路: 没想到什么好办法&#xff0c;一步一步来。整体就是反转&#xff0c;删除前导/后导0&#xff0c;反转&#xff0c;删除前导/后导0。 第一次AC没过去&#xff0c;原因是没考虑到分数的分母前导0的情况&#xff0c;比如1234567890/1234567890这个样例&#xff0c;结果输出…

2024 ICDE第一轮 时空(Spatial-Temporal)和时序(Time Series)论文总结

ICDE 2024目前把第一轮接收的论文已经全部放出&#xff0c;部分挂在了arXiv上&#xff0c;本文总结了ICDE 2024第一轮有关时空和时序的相关文章。 &#x1f31f;【紧跟前沿】“时空探索之旅”与你一起探索时空奥秘&#xff01;&#x1f680; 时空数据&#xff08;Spatial-Tem…

MySQL系列之索引入门(下)

前言 通过上文&#xff0c;我想各位盆友已熟悉MySQL的索引分类及其含义&#xff0c;那么如何合理的使用呢&#xff1f; 请继续围观此文&#xff0c;一探究竟&#xff01; 一、创建索引 首先&#xff0c;我们一起学习索引是如何创建的&#xff0c;又有哪些方式。 1. create t…

104.网游逆向分析与插件开发-网络通信封包解析-接收数据的初步逆向分析

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;网络完成端口模型的流程 下图登录了游戏&#xff0c;此时此刻 WSARecv 已经投递 然后打开x96dbg来到WSARecv函数 然后WSARecv的线程id&#xff1a;5BAC WSASend函数的线程id&#xff1a;主线程 66C0 …

stm32--笔记

一、引脚与变量 ​​​​​​​​​​​​​​ 二、STM32时钟 [STM32-时钟系统详解_stm32时钟_KevinFlyn的博客-CSDN博客] 三、定时器中断实验 1、定时器中断实验 ​ stm32关于通用定时器的周期、频率计算公式_stm32tim频率计算_胶囊咖啡的博客-CSDN博客 ​ 【STM32】通用…

Eclipse - 查看工程或者文件的磁盘路径

Eclipse - 查看工程或者文件的磁盘路径 1. Help -> Eclipse Marketplace -> Find: Explorer -> Eclipse Explorer 4.1.0 -> Install2. right-click -> Open in ExplorerReferences 1. Help -> Eclipse Marketplace -> Find: Explorer -> Eclipse Explo…

svg图片构造QGraphicsSvgItem对象耗时很长的问题解决

目录 1. 问题的提出 2. 问题解决 1. 问题的提出 今天通过一张像素为141 * 214&#xff0c;大小为426KB的svg格式的图片构造QGraphicsSvgItem对象&#xff0c;再通过Qt的Graphics View Framework框架&#xff0c;将QGraphicsSvgItem对象显示到场景视图上&#xff0c;代码如下&…

【Python】【Pycharm】Python Script头文件设置

1、步骤&#xff1a;File->settings->Editor->File and CodeTemplates->Python Script 2、复制粘贴以下代码&#xff0c;应用即可&#xff1a; #!/usr/bin/env python # -*- coding: utf-8 -*-# Time :${DATE} ${TIME} # Author : admin # Site :${SITE} …

Hack The Box-Office

端口扫描&信息收集 使用nmap对靶机进行扫描 nmap -sC -sV 10.10.11.3开放了80端口&#xff0c;并且注意到该ip对应的域名为office.htb&#xff0c;将其加入到hosts文件中访问之 注意到扫描出来的还有robots文件&#xff0c;经过尝试后只有administrator界面是可以访问的 …

基于ArcGIS Pro SDK的MVVM架构

示例结果展示 文件夹创建 相对于原始C#&#xff0c;少了Command文件夹里的类。该文件中的RelayCommand使用 ArcGIS.Desktop.Framework Properties属性配置&#xff0c;主要用于设置执行程序路径&#xff08;自带文件夹&#xff09; DarkImages用于存放深色图片&#xff08;自…

【Kubernetes in Action笔记】1.快速开始

在Kubernetes上运行一个程序 基础运行环境 当前的运行环境为使用虚拟机构建的单master集群。 [rootk8s-master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 109d v1.27.1 k8s-node1 Ready …

正则表达式(Java)

一、初步认识 正则表达式是由一些特定的字符组成&#xff0c;代表一个规则&#xff0c;可以用来检验数据格式是否合法&#xff0c;也可以在一段文本中查找满足要求的内容。 如果使用代码检验数据是否正确&#xff1a; public class RegexTest1 {public static void main(Str…

汇编语言程序设计——基础知识(完整)

文章目录 一、CPU概述:1.1 CPU(中央处理器)和MCU(微处理器 = 单片机)的区别:1.2 CPU是如何工作的:1.2.1 CPU是如何区分内存中的指令和数据的:1.2.1.1 地址总线:1.2.1.2数据总线:1.2.1.3控制总线:二、存储器:2.1 随机存储器(RAM):2.2 只读存储器(ROM):2.3 CPU的…

php基础学习之匿名函数

匿名函数 描述 允许临时创建一个没有指定名称的函数&#xff0c;使用匿名函数可以方便地创建一次性或临时的功能块。 语法 function (){ //函数体 }; 样例 &#xff08;注&#xff1a;匿名函数定义后&#xff0c;需要将其赋值给一个变量&#xff0c;然后通过该变量来调用 这种调…