编写LED驱动,创建三个设备文件,每个设备文件绑定一个设备

驱动代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include "head.h"gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;unsigned int *vir_rcc;struct cdev *cdev;
struct class *cls;
struct device *dev;unsigned int major = 500;
unsigned int minor = 0; //此设备的起始值
dev_t devno;
char kbuf[128] = {0};
// 封装操作方法int mycdev_open(struct inode *inode, struct file *file)
{int a = inode->i_rdev;                 //获取当前设备文件对应的设备号file->private_data = (void *)MINOR(a); //将次设备号保存到当前文件的file结构中printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{unsigned int a = (unsigned int)file->private_data;switch (a){case 0:switch (cmd){case LED_ON:vir_led1->ODR |= (0x1 << 10); // 开灯break;case LED_OFF:vir_led1->ODR &= (~(0x1 << 10));break;}break;case 1:switch (cmd){case LED_ON:vir_led2->ODR |= (0x1 << 10); // 开灯break;case LED_OFF:vir_led2->ODR &= (~(0x1 << 10));break;}break;case 2:switch (cmd){case LED_ON:vir_led3->ODR |= (0x1 << 8); // 开灯break;case LED_OFF:vir_led3->ODR &= (~(0x1 << 8));break;}break;}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__);if (size > sizeof(kbuf)) //用户的需求内核满足不了{size = sizeof(kbuf);}long ret;ret = copy_to_user(ubuf, kbuf, size);if (ret){printk("copy_to_user filed\n");return -EIO;}return 0;
}ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) //用户的需求内核满足不了{size = sizeof(kbuf);}long ret;ret = copy_from_user(kbuf, ubuf, size);if (ret){printk("copy_from_user filed\n");return -EIO;}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,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};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);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_led3->MODER &= (~(3 << 16));vir_led3->MODER |= (1 << 16);vir_led3->ODR &= (~(1 << 8));printk("寄存器初始化成功\n");return 0;
}static int __init mycdev_init(void)
{int ret;// 1.申请对象空间 cdev_alloccdev = cdev_alloc();if (cdev == NULL){printk("申请对象空间失败\n");ret = -EFAULT;goto OUT1;}// 2.初始化对象  cdev_initcdev_init(cdev, &fops);// 3.s申请设备号  register_chrdev_region()/alloc_chrdev_region()if (major == 0){//动态申请ret = alloc_chrdev_region(&devno, minor, 3, "mycdev");if (ret){printk("动态申请设备号失败\n");goto OUT2;}//统一后面的操作major = MAJOR(devno); //根据设备号获取主设备号minor = MINOR(devno); //根据设备号获取次设备号}else{//静态申请ret = register_chrdev_region(MKDEV(major, minor), 3, "mycdev");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;}// 5.向上提交目录  class_create()cls = class_create(THIS_MODULE, "mycdev");if (IS_ERR(cls)){printk("向上提交目录失败\n");ret = -PTR_ERR(cls);goto OUT4;}// 6.向上提交设备信息 device_create()int i;for (i = 0; i < 3; i++){dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);if (IS_ERR(dev)){printk("向上提交设备节点失败\n");ret = -PTR_ERR(dev);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.销毁设备信息  device_destroyint i;for (i = 0; i < 3; i++){device_destroy(cls, MKDEV(major, i));}// 2.销毁目录    class_destroyclass_destroy(cls);// 3.注销驱动对象  cdev_delcdev_del(cdev);// 4.释放设备号   unregister_chrdev_region()unregister_chrdev_region(MKDEV(major, minor), 3);// 5.释放对象空间   kfree()kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用层代码:

#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 a, b, fd0, fd1, fd2;while (1){printf("请输入要控制的灯 1(LED1) 2(LED2) 3(LED3):");scanf("%d", &b);fd0 = open("/dev/mycdev0", O_RDWR);if (fd0 < 0){printf("打开设备文件失败\n");exit(-1);}fd1 = open("/dev/mycdev1", O_RDWR);if (fd1 < 0){printf("打开设备文件失败\n");exit(-1);}fd2 = open("/dev/mycdev2", O_RDWR);if (fd2 < 0){printf("打开设备文件失败\n");exit(-1);}printf("请输入指令\n");printf("0(关灯) 1(开灯)\n");printf("请输入:");scanf("%d", &a);switch (a){case 1:switch (b){case 1:ioctl(fd0, LED_ON); //开灯break;case 2:ioctl(fd1, LED_ON);break;case 3:ioctl(fd2, LED_ON);break;}break;case 0:switch (b){case 1:ioctl(fd0, LED_OFF); //关灯break;case 2:ioctl(fd1, LED_OFF);break;case 3:ioctl(fd2, LED_OFF);break;}break;}}close(fd0);close(fd1);close(fd2);return 0;
}

头文件:

#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

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

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

相关文章

老年公寓人员定位管理系统:提升安全与关怀的智能解决方案

老年公寓作为提供安全居住环境和关怀服务的重要场所&#xff0c;面临着人员管理和安全控制的挑战。为了解决这些问题&#xff0c;老年公寓人员定位管理系统应运而生。基于为提供全面的安全管理和个性化关怀服务&#xff0c;华安联大便通过老年公寓人员定位管理系统的技术原理、…

数字孪生和 GIS 系统融合将为水利领域带来哪些变化?

随着科技的不断进步&#xff0c;数字孪生和 GIS 系统的融合应用逐渐成为了水利领域的新趋势。数字孪生是指通过数字化技术模拟物理实体和过程&#xff0c;将现实世界与虚拟世界相结合的技术&#xff0c;而 GIS 系统则是地理信息系统&#xff0c;用于收集、存储、管理和分析地理…

网工内推 | 售前、售后工程师,IE认证优先

01 广州佳杰科技有限公司 招聘岗位&#xff1a;IT售前工程师 职责描述&#xff1a; 1、负责所在区域 IT 产品的售前技术支持工作,包括客户交流、方案编写、配置报价、投标应标、测试、赋能等; 2、与厂商相关人员建立和保持良好的关系,相互配合,提高项目成功率和厂商满意度; 3、…

Python:使用openpyxl读取Excel文件转为json数据

文档 https://openpyxl.readthedocs.io/en/stable/https://pypi.org/project/openpyxl/ 安装 pip install openpyxl环境 $ python --version Python 3.7.0读取文件示例&#xff1a;将Excel文件读取为json数据 有如下一个文件 data.xlsx 实现代码 # -*- coding: utf-8 -…

如何恢复损坏/删除的 Word 文件

有关如何修复不可读的 Microsoft Word 文件或 Rich Text 文件中的文本的分步说明。这些说明有助于从损坏的*.doc、*.docx、*.dot、*.dotx、*.rtf文件&#xff08;任何版本和大小&#xff09;中提取文本&#xff0c;只需单击几下&#xff1a; 从此处下载奇客数据恢复 &#xff…

【云原生】Kubernetes之Secret

使用 kubectl 管理 Secret 准备开始 你必须拥有一个 Kubernetes 的集群&#xff0c;同时你必须配置 kubectl 命令行工具与你的集群通信 创建 Secret Secret 对象用来存储敏感数据&#xff0c;如 Pod 用于访问服务的凭据。例如&#xff0c;为访问数据库&#xff0c;你可能需…

如何在Linux系统中安装ActiveMQ

1、环境 ActiveMQ是一个纯Java程序&#xff0c;这里安装5.18.2版ActiveMQ&#xff0c;该版MQ运行在JDK 11环境内&#xff0c;为此需要先搭建JDK 11环境&#xff0c;这里安装JDK 15。 1.1、卸载 卸载开源JDK软件包&#xff0c;如下所示&#xff1a; [rootlocalhost ~]# rpm -…

如何将表格中的状态数据转换为Tag标签显示

考虑到系统前端页面的美观程度&#xff0c;通常通过Tag标签来代替某条数据中的状态信息。仅通过一点操作&#xff0c;便能够使得页面美观程度得到较大提升&#xff0c;前后对比如下所示。代码基于Vue以及Element-ui组件实现。 修改前&#xff1a; 修改后&#xff1a; 修改前…

QT信号与槽及常见问题

Qt的信号与槽&#xff08;Signals and Slots&#xff09;: 是一种用于对象间通信的机制&#xff0c;它是Qt框架的核心特性之一。通过信号与槽&#xff0c;一个对象可以在特定事件发生时发射信号&#xff0c;而其他对象则可以在接收到信号时执行对应的槽函数。这种机制实现了对…

1.Flink概述

1.1 技术架构 应用框架层: 在API层之上构建的满足特定应用场景的计算框架&#xff0c;总体上分为流计算和批处理两类应用框架。API 层&#xff1a; Flink对外提供能力的接口 &#xff0c;实现了面向流计算的DataStream API和面向批处理的DataSet API。运行时层&#xff1a;Flin…

Visio文件编辑查看工具Visio Viewer for Mac

Visio Viewer for Mac可以打开和查看Visio文件&#xff08;.vsd、.vdx和.vsdm文件&#xff09;。它具有简单易用的用户界面&#xff0c;可以快速加载和显示Visio文件。此外&#xff0c;它还支持导出文件为PDF、PNG、JPEG等格式&#xff0c;方便用户进行文件转换和共享。 Visio…

Golang Json 编解码

编码 json.NewEncoder(<Writer>).encode(v) json.Marshal(&v) 解码 json.NewDecoder(<Reader>).decode(&v) json.Unmarshal([]byte, &v) 使用示例 type Person struct {Name string json:"name"Age int json:"age" } func ma…

《零基础入门学习Python》第053讲:论一只爬虫的自我修养

0. 请写下这一节课你学习到的内容&#xff1a;格式不限&#xff0c;回忆并复述是加强记忆的好方式&#xff01; 马上我们的教学就要进入最后一个章节&#xff0c;Pygame 嗨爆引爆全场&#xff0c;但由于发生了一个小插曲&#xff0c;所以这里决定追加一个章节&#xff0c;因为…

Qt Core学习日记——第四天QMetaEnum(下)

类定义&#xff1a; 成员变量就只有QMetaObject *mobj和uint handle&#xff0c;handle同样用于计算在qt_meta_stringdata_XTest的位置 成员函数&#xff1a; 接下以test类进行函数讲解 test.h #pragma once #include <qobject.h> #include <QFlags> class X…

有些能力,是工作中学不来的,看看这篇超过90%同行

俗话说:360行&#xff0c;行行转IT。 在就业形势压力巨大的今天&#xff0c;不仅仅是计算机专业的毕业生&#xff0c;很多其他专业的大学生都选择转行从事计算机行业。 尤其是软件测试行业&#xff0c;远远超出其他行业的薪水和广阔的就业前景&#xff0c;吸引了大批应届毕业…

QT DAY1

1.思维导体 2.作业 #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {qDebug()<<this->size();qDebug()<<this->rect().size();qDebug()<<this->geometry().size();qDebug()<<this->frameGeometry().siz…

视频增强技术-对比度增强

在图像处理中&#xff0c;由于获取的图像质量不好&#xff0c;需要通过对比度增强来提升图片质量&#xff0c;主要解决的是由于图像灰度级范围较小造成的对比度较低的问题&#xff0c;作用是使图像的灰度级范围放大&#xff0c;从而让图像更加清晰。主要对比度增强方法包括线性…

macbook 软件iMovie for Mac(专业视频剪辑工具)中文版

iMovie mac中文版是一款针对Mac平台量身定做的视频编辑工具&#xff0c;软件凭借流线型设计和直观的编辑功能&#xff0c;可以让您感受前所未有的方式制作好莱坞风格的预告片和精美电影&#xff0c;并且还可以浏览视频资料库&#xff0c;快速共享挚爱瞬间&#xff0c;创建精美的…

DevOps自动化平台开发之 Shell脚本执行的封装

基础知识 基于如下技术栈开发DevOps平台 Spring Boot Shell Ansible Git Gitlab Docker K8S Vue 1、spring boot starter的封装使用 2、Shell脚本的编写 3、Ansible 脚本的编写 4、Docker 的使用与封装设计 本篇介绍如何使用Java封装Linux命令和Shell脚本的使用 将其设计成…

文件上传漏洞

什么是文件上传漏洞&#xff1f; 攻击者上传了一个可执行文件如木马&#xff0c;病毒&#xff0c;恶意脚本&#xff0c;WebShell等到服务器执行&#xff0c;并最终获得网站控制权限的高危漏洞。 webshell 非法用户可利用上传的恶意脚本文件控制整个网站&#xff0c;甚至控制服…