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

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

请添加图片描述

mychrdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include "head.h"
unsigned int major; // 保存申请的主设备号
unsigned int minor; // 保存申请的次设备号
char kbuf[128]={0};
dev_t devno;
struct class *cls;
struct device *dev;
struct cdev *cdev;

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

// 封装常用的操作方法
int mychrdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mychrdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *lof)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
     unsigned int ret;
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)
    {
        printk("copy_to_user err\n");
        return -ret;
    }
    return 0;
}
ssize_t mychrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lof)
{
    //ubuf就是应用程序中write函数第二个参数传过来的值
    //size就是应用程序中write函数第三个参数传过来的值
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    unsigned int ret;
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("copy_from_user err\n");
        return -ret;
    }
    if(kbuf[0]=='0')//关灯
    {
        vir_led1->ODR &=(~(0x1<<10));
        vir_led2->ODR &=(~(0x1<<10));
        vir_led3->ODR &=(~(0x1<<8));
    }
    else if(kbuf[0]=='1')//开led1灯
    {
        vir_led1->ODR|=(0x1<<10);
    }
    else if(kbuf[0]=='2')//开led2灯
    {
        vir_led2->ODR|=(0x1<<10);
    }
    else if(kbuf[0]=='3')//开led3灯
    {
        vir_led3->ODR|=(0x1<<8);
    }
    return 0;
}
int mychrdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
struct file_operations fops = {
    .open=mychrdev_open,
    .release=mychrdev_close,
    .read=mychrdev_read,
    .write=mychrdev_write,
};

void all_led_init(void)
{
    // 注册字符设备驱动
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("注册字符设备驱动失败%d\n", __LINE__);
    }
    printk("注册字符设备驱动成功 major=%d\n", major);
    //进行LED寄存器的地址映射
    vir_led1 =ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("地址映射失败%d\n",__LINE__);
        return 0;
    }
    vir_led2 =ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("地址映射失败%d\n",__LINE__);
        return 0;
    }
    vir_led3 =ioremap(PHY_LED3_ADDR,sizeof(gpio_t));
    if(vir_led3==NULL)
    {
        printk("地址映射失败%d\n",__LINE__);
        return 0;
    }
    //GPIOF
    vir_rcc =ioremap(PHY_RCC_ADDR,sizeof(gpio_t));
    if(vir_rcc ==NULL)
    {
        printk("地址映射失败%d\n",__LINE__);
        return 0;
    }
    printk("地址映射成功\n");
    //硬件初始化

    //使能时钟
    (*vir_rcc) |= (0X3<<4);
    //设置PE10为输出
    vir_led1->MODER &= (~(0X3<<20));
    vir_led1->MODER |= (0X1<<20);
    //设置PF10为输出
    vir_led2->MODER &= (~(0X3<<20));
    vir_led2->MODER |= (0X1<<20);
    //设置PE8为输出
    vir_led3->MODER &= (~(0X3<<16));
    vir_led3->MODER |= (0X1<<16);
    //默认输出低电平
    vir_led1->ODR &=(~(0x1<<10));
    vir_led2->ODR &=(~(0x1<<10));
    vir_led3->ODR &=(~(0x1<<8));
    return 0;
}

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=MINOR(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;          
    }
    
    //向上提交目录
    cls = class_create(THIS_MODULE,"led");
    if(ret)
    {
        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 out3;          
        }
    }
    all_led_init();
    return 0;
    out4:
        //销毁提交成功的设备信息
        for(i=0;i<3;i++)
        {
            device_destroy(cls,MKDEV(major,i));
        }
        //销毁目录
        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_led1);
    iounmap(vir_led2);
    iounmap(vir_led3);
    iounmap(vir_rcc);
    //销毁提交成功的设备信息
    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");

head.h

#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

#endif

led.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    char buf[128] = {};
    int fd = open("/dev/myled0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        return -1;
    }
    while (1)
    {
        printf("请输入控制码:1(led1开灯) 2(led2开灯) 3(led3开灯) 0(关灯)>");
        fgets(buf, sizeof(buf), stdin); // 从终端输入一个数据
        buf[strlen(buf) - 1] = '\0';
        write(fd, buf, sizeof(buf));
    }
    close(fd);
    return 0;
}

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

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

相关文章

相机图像质量研究(34)常见问题总结:图像处理对成像的影响--拖影

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

【鸿蒙手机】获取UDID,并添加签名认证

一、打开开发者模式 1、手机型号华为nova 10 pro , HarmonyOS版本 4.0&#xff0c;路径&#xff1a;设置-> 关于本机-> 多次连续点击”软件版本“ 这一行&#xff0c;一般是是5到7次&#xff08;我是点击了5次&#xff09;&#xff0c;第一次会弹出输入密码&#xff0c;验…

猪圈Pigsty-PG私有RDS集群搭建教程

博客 https://songxwn.com/Pigsty-PG-RDS/ 简介 Pigsty 是一个更好的本地自建且开源 RDS for PostgreSQL 替代&#xff0c;具有以下特点&#xff1a; 开箱即用的 PostgreSQL 发行版&#xff0c;深度整合地理、时序、分布式、图、向量、分词、AI等 150 余个扩展插件&#xff…

作业2024/2/18

1.思维导图 2.定义一个基类Animal&#xff0c;其中有一个虚函数perform ()&#xff0c;用于在子类中实现不同的表演行为。 #include <iostream>using namespace std; class Animal { private:public:void virtual perform() 0;}; class Tiger:public Animal { private:…

C#使用迭代器实现文字的动态效果

目录 一、涉及到的知识点 1.GDI 2.Thread类 3.使用IEnumerable()迭代器 二、实例 1.源码 2.生成效果&#xff1a; 一、涉及到的知识点 1.GDI GDI主要用于在窗体上绘制各种图形图像。 GDI的核心是Graphics类&#xff0c;该类表示GDI绘图表面&#xff0c;它提供将对象绘制…

LeetCode.107. 二叉树的层序遍历 II

题目 107. 二叉树的层序遍历 II 分析 这个题目考查的是二叉树的层序遍历&#xff0c;对于二叉树的层序遍历&#xff0c;我们需要借助 队列 这种数据结构。再来回归本题 &#xff0c;我们只需要将 二叉树的层序遍历的结果逆序&#xff0c;就可以得到这道题我们要求的答案了。…

C++学习Day05之强化训练---字符串类封装

目录 一、程序及输出1.1 头文件1.2 cpp文件1.3 主程序 二、分析与总结 一、程序及输出 1.1 头文件 myString.h #pragma once #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; class MyString {//左移运算符友元friend ostream& operat…

2024.2.18 C++QT 作业

思维导图 练习题 1>定义一个基类 Animal&#xff0c;其中有一个虛函数perform&#xff08;)&#xff0c;用于在子类中实现不同的表演行为。 #include <iostream>using namespace std;class Animal { public:virtual void perform() {cout << "这是一个动…

【springboot+vue项目(十五)】基于Oauth2的SSO单点登录(二)vue-element-admin框架改造整合Oauth2.0

Vue-element-admin 是一个基于 Vue.js 和 Element UI 的后台管理系统框架&#xff0c;提供了丰富的组件和功能&#xff0c;可以帮助开发者快速搭建现代化的后台管理系统。 一、基本知识 &#xff08;一&#xff09;Vue-element-admin 的主要文件和目录 vue-element-admin/ |…

一个很牛逼的保存页面为html的插件

目录 安装 使用 今天突然发现一个牛逼的插件 直接上链接 https://github.com/gildas-lormeau/SingleFile 安装 SingleFile can be installed on: Firefox: SingleFile – Get this Extension for &#x1f98a; Firefox (en-US)Firefox for Android: SingleFile – Get thi…

C++11新特性(深度剖析+总结)

文章目录 1. 前言1. C11简介2. 统一的列表初始化2.1 {} 初始化2.2 std::initializer_list 3. 声明3.1 auto3.2 decltype3.3 nullptr 4. 范围for循环5. STL中的一些变化6. 右值引用和移动语义6.1 左值引用和右值引用6.2 左值引用与右值引用比较6.3 右值引用使用场景和意义6.4 右…

【Java多线程】线程中几个常见的属性以及状态

目录 Thread的几个常见属性 1、Id 2、Name名称 3、State状态 4、Priority优先级 5、Daemon后台线程 6、Alive存活 Thread的几个常见属性 1、Id ID 是线程的唯一标识&#xff0c;由系统自动分配&#xff0c;不同线程不会重复。 2、Name名称 用户定义的名称。该名称在各种…

Mac软件打开提示:已损坏,无法打开。您应该将它移到废纸娄 怎么解决?

新入手的苹果电脑打开软件出现&#xff1a;“已损坏&#xff0c;无法打开。您应该将它移到废纸娄” 或 “已损坏&#xff0c;打不开。推出磁盘映像”。这个怎么解决&#xff1f; 第一部分&#xff1a;&#xff08;注意&#xff1a;任何来源打开过了的&#xff0c;就直接去看下…

医用软管用双轴测径仪 外径与椭圆度的双重检测!

摘要&#xff1a;软管的一大特点就是容易产生形变&#xff0c;接触式测量稍施压力可能导致测量不准&#xff0c;因此非接触式的高精高速测径仪被广泛的应用于生产中。 关键词&#xff1a;双轴测径仪,医用软管测径仪,软管测径仪,测径仪,软管外径测量仪 引言 非接触式的外径测量仪…

山西电力市场日前价格预测【2024-02-18】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-18&#xff09;山西电力市场全天平均日前电价为347.21元/MWh。其中&#xff0c;最高日前电价为535.89元/MWh&#xff0c;预计出现在07:45。最低日前电价为165.05元/MWh&#xff0c;预计…

数据分析 — 动画图 pyecharts

目录 一、概念二、安装和导入三、绘图逻辑四、绘图1、柱状图2、折线图3、散点图4、饼图5、南丁格尔图6、Geo() 地理坐标第7、Map() 绘制区域8、词云图9、层叠图10、3D 图11、仪表板 一、概念 Pyecharts 是一个基于 Echarts 的 Python 可视化库&#xff0c;它通过 Python 生成 …

【动态规划】【C++算法】2742. 给墙壁刷油漆

作者推荐 【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 本文涉及知识点 动态规划汇总 LeetCode2742. 给墙壁刷油漆 给你两个长度为 n 下标从 0 开始的整数数组 cost 和 time &#xff0c;分别表示给 n 堵不同的墙刷油漆需要的开销和时间。你有…

差异表达分析和PPI网络图构建

原文链接&#xff1a;差异分析和PPI网路图绘制教程 写在前面 在原文中&#xff0c;作者获得285个DEG&#xff0c;在此推文中共获得601个DEG。小杜的猜想是标准化的水段不同的原因吧&#xff0c;或是其他的原因。此外&#xff0c;惊奇的发现发表医学类的文章在附件中都不提供相…

【MySQL】学习多表查询和笛卡尔积

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-N8PeTKG6uLu4bJuM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

vue自定义指令(图文示例)

第085个 查看专栏目录: VUE 本文章目录 示例效果图示例源代码API 参考网址 Vue 自定义指令是一种用于扩展 Vue 模板功能的强大工具。通过自定义指令&#xff0c;你可以在 Vue 模板中添加自定义的行为和逻辑&#xff0c;使模板更加灵活和可定制。 以下是对 Vue 自定义指令的详细…
最新文章