驱动开发4 使用字符设备驱动的分步实现编写LED驱动(LED亮灯)

一、思维导图

二、通过字符设备驱动的分步实现编写LED驱动,另外实现特备文件和设备的绑定

应用程序 test.c

#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.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;int fd=open("/dev/mycdev0",O_RDWR);if(fd<0){printf("打开设备文件失败\n");exit(-1);}while(1){//从终端读取printf("请输入要实现的功能 ");printf("0(关灯) 1(开灯)\n");printf("请输入>");scanf("%d",&a);switch(a){case 1:ioctl(fd,LED_ON);break;case 0:ioctl(fd,LED_OFF);break;}}close(fd);return 0;
}

头文件 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
// 构建开灯关灯的功能码
#define LED_ON _IO('l', 1)
#define LED_OFF _IO('l', 0)
#endif

驱动程序 mycdev.c

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include "head.h"struct cdev *cdev;
unsigned int major=0;
unsigned int minor=0;
dev_t devno;char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
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: // 开灯switch (cmd){case LED_ON: // LED1vir_led1->ODR |= (0X1 << 10); //LED开break;case LED_OFF:vir_led1->ODR &= (~(0X1 << 10));break;}break;case 1: // 关灯switch (cmd){case LED_ON: // LED1vir_led2->ODR |= (0X1 << 10);break;case LED_OFF:vir_led2->ODR &= (~(0X1 << 10));break;}break;case 2:switch (cmd){case LED_ON: // LED1vir_led3->ODR |= (0X1 << 8);break;case LED_OFF:vir_led3->ODR &= (~(0X1 << 8));break;}break;}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);// 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_led1->MODER |= (1 << 16);vir_led1->ODR &= (~(1 << 8));printk("寄存器初始化成功\n");return 0;
}
static int __init mycdev_init(void)
{//1.申请一个对象空间cdev_allocint ret;cdev = cdev_alloc();if (NULL == cdev){printk("申请字符设备驱动对象失败\n");ret = -EFAULT;goto out1;}printk("字符设备驱动对象申请成功\n");//2.初始化对象cdev_inicdev_init(cdev,&fops);//3.申请设备号register_chrdev_region()/alloc_chrdev_region()if(0 == major){ret = alloc_chrdev_region(&devno,minor,3,"mychrdev");if(ret){printk("动态申请设备号失败\n");goto out2;}major=MAJOR(devno); //根据设备号获取主设备号minor=MINOR(devno); //根据设备号获取次设备号}else //静态制定设备号{ret=register_chrdev_region(MKDEV(major,minor),3,"mychrdev");if(ret){printk("静态指定设备号失败\n");goto out2;}}printk("设备号申请成功\n");//4.注册驱动对象 cdev_addret = cdev_add(cdev,MKDEV(major,minor),3);if(ret){printk("注册字符设备驱动对象失败\n");goto out3;}printk("注册字符设备驱动对象成功\n");//5.向上提交目录 class_createcls = class_create(THIS_MODULE,"mychrdev");if(IS_ERR(cls)){printk("向上提交目录失败\n");goto out4;}printk("向上提交目录成功\n");//6.向上提交设备节点信息 device_createint 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");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");

Makefile

modname ?= demo
arch ?= arm
ifeq ($(arch),arm)  #通过命令行传过来的架构决定怎么编译
#KERBELDIR保存开发板内核源码路径
KERNELDIR := /home/ubuntu/FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61
else
#保存UBUNTU内核源码路径
KERNELDIR := /lib/modules/$(shell uname -r)/build
endif#PWD保存当前内核模块的路径
PWD := $(shell pwd)
all:
#make modules是模块化编译命令
#make -C $(KERNLEDIR) 执行make之前先切换到KERNELDIR对应的路径
#M=$(PWD)表示进行模块化编译的路径是PWD保存的路径make -C $(KERNELDIR) M=$(PWD) modules
clean:
#编译清除make -C $(KERNELDIR) M=$(PWD) clean
#将obj-m保存的文件单独链接为内核模块
obj-m :=  $(modname).o

效果实现

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

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

相关文章

Jenkins部署失败:JDK ‘jdk1.8.0_381‘ not supported to run Maven projects

Jenkins部署报错&#xff1a;JDK ‘jdk1.8.0_381’ not supported to run Maven projects提示使用的jdk有问题&#xff0c;启动的jdk版本不能满足项目启动。 登录Jenkins管理页面&#xff0c;系统管理——全局工具配置——JDK安装配置满足条件的JDK版本&#xff0c;保存配置&…

Parallels Client for Mac:改变您远程控制体验的革命性软件

在当今数字化的世界中&#xff0c;远程控制软件已经成为我们日常生活和工作中不可或缺的一部分。在众多远程控制软件中&#xff0c;Parallels Client for Mac以其独特的功能和出色的性能脱颖而出&#xff0c;让远程控制变得更加简单、高效和灵活。 Parallels Client for Mac是…

08数据结构——排序

8.2 插入排序 8.2.1 直接插入排序 直接插入排序&#xff08;用哨兵&#xff09;代码如下&#xff1a; void InsertSort(ElemType A[],int n){int i,j;for(i2;i<n;i) //依次将A[2]~A[n]插入前面已排序序列if(A[i]<A[i-1]){ //若A[i]关键码小于其前驱…

成都瀚网科技有限公司抖音小店:创新营销引领电商潮流

在当今数字化时代&#xff0c;抖音作为一款备受欢迎的短视频平台&#xff0c;不仅吸引了大量用户的关注&#xff0c;还为众多电商企业提供了新的销售渠道。成都瀚网科技有限公司抖音小店便是其中之一&#xff0c;凭借其独特的营销策略和优质的产品&#xff0c;成为了抖音电商领…

PyCharm改变代码背景图片的使用教程

一个好的集成环境是学习和使用一门编程语言的重中之重&#xff0c;这次我给大家分享如何改变PyCharm软件的代码背景图片。 说明&#xff1a;本教程使用的是汉化版PyCharm软件。 打开PyCharm软件。 点击软件最上方导航栏的文件&#xff0c;然后找到设置。 打开设置然后点击外观…

总结使用React做过的一些优化

一、修改css模拟v-show {!flag && <MyComponent style{{display: none}} />} {flag && <MyComponent />}<MyComponent style{{ display: flag ? block : none }} />二、循环使用key const todosList todos.map(item > {<li key{it…

Web APIs——焦点事件以及小米搜索框

一、事件类型 二、焦点事件 <body><input type"text"><script>const input document.querySelector(input)input.addEventListener(focus,function(){console.log(有焦点触发)})input.addEventListener(blur,function(){console.log(失去焦点触…

如何使用 JMeter 进行 HTTPS 请求测试?

本文将介绍如何使用 JMeter 测试 HTTPS 请求&#xff0c;并提供相关的技巧和注意事项。 在进行性能测试时&#xff0c;很多网站都采用了 HTTPS 协议。当我们测试 HTTPS 请求&#xff0c;如果服务端开启了双向认证&#xff0c;则需要客户端发送请求时带上证书。本文介绍如何在 …

【Axure视频教程】曲线图

今天教大家在Axure制作可视化曲线图的原型模板&#xff0c;鼠标移入曲线图后&#xff0c;会显示弹窗并回显对应折点的具体数据&#xff0c;该模板是用Axure原生元件制作的&#xff0c;所以使用方便&#xff0c;可以任意修改对应样式或者交互效果。该原型模板的具体效果可以参考…

Pytorch--3.使用CNN和LSTM对数据进行预测

这个系列前面的文章我们学会了使用全连接层来做简单的回归任务&#xff0c;但是在现实情况里&#xff0c;我们不仅需要做回归&#xff0c;可能还需要做预测工作。同时&#xff0c;我们的数据可能在时空上有着联系&#xff0c;但是简单的全连接层并不能满足我们的需求&#xff0…

【Docker】Docker学习之一:离线安装Docker步骤

前言&#xff1a;基于Ubuntu Jammy 22.04 (LTS)版本安装和测试 1、Docker安装 1.1、离线安装 步骤一&#xff1a;官网下载 docker 安装包 wget https://download.docker.com/linux/static/stable/x86_64/docker-24.0.6.tgz步骤二&#xff1a;解压安装包; tar -zxvf docker…

谈谈Net-SNMP软件

Net-SNMP是一个开源的SNMP软件套件&#xff0c;它提供了SNMP代理&#xff08;snmpd&#xff09;和SNMP工具&#xff08;如snmpget、snmpwalk等&#xff09;&#xff0c;可以用于监控和管理网络设备。 Net-SNMP最初是从UC Davis的SNMP软件衍生而来&#xff0c;现在已经成为广泛…

小程序设计基本微信小程序的校园生活助手系统

项目介绍 通篇文章的撰写基础是实际的应用需要&#xff0c;然后在架构系统之前全面复习大学所修习的相关知识以及网络提供的技术应用教程&#xff0c;以校园生活助手系统的实际应用需要出发&#xff0c;架构系统来改善现校园生活助手系统工作流程繁琐等问题。不仅如此以操作者…

纺织工厂数字孪生3D可视化管理平台,推动纺织产业数字化转型

近年来&#xff0c;我国加快数字化发展战略部署&#xff0c;全面推进制造业数字化转型&#xff0c;促进数字经济与实体经济深度融合。以数字孪生、物联网、云计算、人工智能为代表的数字技术发挥重要作用。聚焦数字孪生智能工厂可视化平台&#xff0c;推动纺织制造业数字化转型…

【Java集合类面试十八】、ConcurrentHashMap是怎么分段分组的?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;ConcurrentHashMap是怎么…

手把手教你在项目中引入Excel报表组件

摘要&#xff1a;本文由葡萄城技术团队原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 GrapeCity Documents for Excel&#xff08;以下简称GcExcel&#xff09;是葡萄城公司的…

【TES641】基于VU13P FPGA的4路FMC接口基带信号处理平台

板卡概述 TES641是一款基于Virtex UltraScale系列FPGA的高性能4路FMC接口基带信号处理平台&#xff0c;该平台采用1片Xilinx的Virtex UltraScale系列FPGA XCVU13P作为信号实时处理单元&#xff0c;该板卡具有4个FMC子卡接口&#xff08;其中有2个为FMC接口&#xff09;&#x…

Vue3.3指北(二)

Vue3.3指北 Vue31、组件基础1.1、全局组件1.2、局部组件1.3、组件的命名1.4、组件的数据存放1.5、组件标签化 2、父组件向子组件传递数据2.1、props2.2、动态props2.3、props传数组2.4、props传对象2.4.1、默认值和必传值 3、子组件向父组件传递数据4、父子组件互相访问4.1、父…

03初始Docker

一、初始Docker 1.什么是Docker 问题 ①大型项目组件复杂&#xff0c;运行环境复杂&#xff0c;部署时依赖复杂&#xff0c;出现兼容性问题。 ②开发&#xff0c;测试&#xff0c;生产环境有差异。不同的环境操作系统不同 解决 ①Docket将应用、依赖、函数库、配置一起打…

聚观早报 | vivo Y100官宣;极氪001 FR将上市

【聚观365】10月25日消息 vivo Y100官宣 一极氪001 FR将上市 特斯拉加速扩张 苹果扩大招聘力度 小米澎湃OS实现历史性跨越 vivo Y100官宣 vivo Y系列是vivo存在比较久的入门系列&#xff0c;主打千元价位的线下市场&#xff0c;在消费者中有着不错的口碑。而不久前一款型…