通过字符设备驱动点亮板子上的led灯

通过字符设备驱动点亮板子上的led灯

app:      test.c  char buf[3]

                       1 0 0

   0 1 0

   0 0 1

------------------|------------------------

kernel:   led_driver.c

-------------------|------------------------

hardware:  RGB_led

  • 应用程序如何将数据传递给驱动读写的方向是站在用户的角度来说的

函数

1)从用户空间拷贝数据到内核空间(用户需要写数据的时候)

#include <linux/uaccess.h>
int copy_from_user(void *to, const void __user *from, int n)
	功能:从用户空间拷贝数据到内核空间(用户需要写数据的时候)
	参数:
		@to  :内核中内存的首地址
		@from:用户空间的首地址
		@n   :拷贝数据的长度(字节)
	返回值:成功返回0,失败返回未拷贝的字节的个数

2)从内核空间拷贝数据到用户空间(用户开始读数据

int copy_to_user(void __user *to, const void *from, int n)
	功能:从内核空间拷贝数据到用户空间(用户开始读数据)
	参数:@to  :用户空间内存的首地址@from:内核空间的首地址@n   :拷贝数据的长度(字节)
	返回值:成功返回0,失败返回未拷贝的字节的个数

驱动如何操作寄存器

rgb_led灯的寄存器是物理地址,在linux内核启动之后,在使用地址的时候,操作的全是虚拟地址需要将物理地址转化为虚拟地址。在驱动代码中操作的虚拟地址就相当于操作实际的物理地址

物理地址<---转化--->虚拟地址

3)将物理地址映射成虚拟地址

虚拟首地址 = ioremap(物理基地址,字节大小)
void * ioremap(phys_addr_t offset, unsigned long size)
(当__iomen告诉编译器,取的时候是一个字节大小)
功能:将物理地址映射成虚拟地址
参数:
	@offset :要映射的物理的首地址
	@size   :大小(字节)(映射是以业为单位,一页为4K,就是当你小于4k的时候映射的区域都为4k)
返回值:成功返回虚拟地址,失败返回NULL((void *)0); 

4)取消映射

void iounmap(void  *addr)
		功能:取消映射
		参数:	
			@addr :虚拟地址
		返回值:无
#define	ENOMEM		12	/* Out of memory */

操作步骤

1、//通过虚拟地址对寄存器更改设置值,实行对硬件的初始化

用户使用发送对硬件控制的指令给底层驱动,底层驱动驱动硬件做对应动作:

 应用层write,驱动对应write获取到传送的指令:

copy_from_user(内核空间首地址,用户空间地址,拷贝字节数);

2 //将用户空间数据拷贝到内核空间

应用层read,驱动对应read需要将内核空间数据拷贝用户空间:

copy_to_user(用户空间地址,内核空间首地址,拷贝字节数);、

Eg:点灯

  • 软件编程控制硬件的思想:

只需要向控制寄存器中写值或者读值,就可以让我们处理器完成一定的功能。

RGB_led 

1》GPIOxOUT:控制引脚输出高低电平

RED_LED--->GPIOA28

GPIOAOUT --->  0xC001A000

GPIOA28输出高电平:

GPIOAOUT[28]  <--写--  1

GPIOA28输出低电平:

GPIOAOUT[28]  <--写--  0

2》GPIOxOUTENB:控制引脚的输入输出模式

GPIOAOUTENB  ---> 0xC001A004

设置GPIOA28引脚为输出模式:

GPIOAOUTENB[28] <--写-- 1

3》GPIOxALTFN:控制引脚功能的选择

GPIOAALTFN1  ---> 0xC001A024

设置GPIOA28引脚为GPIO功能:

GPIOAALTFN1[25:24]  <--写-- 0b00

00 = ALT Function0   

01 = ALT Function1 

10 = ALT Function2   

11 = ALT Function3 

GPIO引脚功能的选择:每两位控制一个GPIO引脚

red  :gpioa28

GPIOXOUT   :控制高低电平的   0xC001A000

GPIOxOUTENB:输入输出模式    0xC001A004

GPIOxALTFN1:function寄存器  0xC001A024

一个寄存器36个字节

green:gpioe13

0xC001e000

blue :gpiob12

0xC001b000

练习:

1.字符设备驱动实现流水灯(30分钟)

//读,改,写

writel(v,c)

功能:向地址中写一个值

参数:

@ v :写的值

@ c :地址

readl(c)

功能:读一个地址,将地址中的值给返回

参数:

@c :地址

七色LED流水灯驱动

内核层 led驱动函数

#include <linux/module.h>
#include <linux/init.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#define NAME "chrdev_devled"
//定义宏保存物理地址基地址
#define RED_BASE 0xc001a000
#define GRE_BASE 0xc001e000
#define BLU_BASE 0xc001b000
//定义指针保存映像后的虚拟地址首地址
unsigned int *red_addr = NULL;
//定义指针保存映像后的虚拟地址首地址
unsigned int *gre_addr = NULL;
//定义指针保存映像后的虚拟地址首地址
unsigned int *blu_addr = NULL;
int size=32;
int major = 0; //保存主设备号
char kbuf[32];//
char kbuf_r[32]="welcome to hqyj";
// open read write release 初始化
int myopen(struct inode *node, struct file *file_t)
{printk("%s %s %d\n", __FILE__, __func__, __LINE__);return 0;
}
ssize_t myread(struct file *file_t, char __user *ubuf, size_t n, loff_t *off_t)
{printk("%s %s %d\n", __FILE__, __func__, __LINE__);//将内核空间数据拷贝到用户空间if(sizeof(kbuf_r)<size)
    size=sizeof(kbuf_r);if(copy_to_user(ubuf,kbuf_r,size)!=0){printk("copy_to_user err.");return -EINVAL;}// printk("kbuf=%s\n",kbuf);return 0;
}
ssize_t mywrite(struct file *file_t, const char __user *ubuf, size_t n, loff_t *off_t)
{printk("%s %s %d\n", __FILE__, __func__, __LINE__);//将用户空间—(ubuf) 的数据拷贝到内核空间(kbuf)if(sizeof(kbuf)<size)
    size=sizeof(kbuf);if(copy_from_user(kbuf,ubuf,size)!=0){printk("copy_from_user err.");return -EINVAL;}printk("kbuf=%s\n",kbuf);if(kbuf[0]==1){//红灯开*red_addr |= (1<<28);}else if(kbuf[0]==0){//红灯关*red_addr &= (~(1<<28));}if(kbuf[1]==1){//红灯开*gre_addr |= (1<<13);}else if(kbuf[1]==0){//红灯关*gre_addr &= (~(1<<13));}if(kbuf[2]==1){//蓝灯开*blu_addr |= (1<<12);}else if(kbuf[2]==0){//蓝灯关*blu_addr &= (~(1<<12));}return 0;
}
int myclose(struct inode *node, struct file *file_t)
{printk("%s %s %d\n", __FILE__, __func__, __LINE__);return 0;
}
struct file_operations fops = {.open = myopen,.read = myread,.write = mywrite,.release = myclose,
};
//入口函数
static int __init chrdev_init(void)
{printk("%s %s %d\n", __FILE__, __func__, __LINE__);//注册字符设备驱动: 主设备号 驱动名 结构体
    major=register_chrdev(major, NAME, &fops);//容错判断if(major < 0){printk("chrdev_register err.\n");return -EINVAL;}//建立虚拟地址和物理地址之间的映射关系——控制红灯
    red_addr = (unsigned int *)ioremap(RED_BASE,40);if(red_addr == NULL){printk("ioremap red err.\n");return -EINVAL;}//初始化红灯*(red_addr+9) &=(~(3<<24));//选择GPIOA28功能*(red_addr+1) |=(1<<28);//选择输出使能*red_addr &=(~(1<<28));//红灯关闭//建立虚拟地址和物理地址之间的映射关系——控制绿灯
    gre_addr = (unsigned int *)ioremap(GRE_BASE,40);if(red_addr == NULL){printk("ioremap red err.\n");return -EINVAL;}//初始化绿灯*(gre_addr+8) &=(~(3<<26));//选择GPIOE13功能*(gre_addr+1) |=(1<<13);//选择输出使能*gre_addr &=(~(1<<13));//绿灯关闭//建立虚拟地址和物理地址之间的映射关系——控制蓝灯
    blu_addr = (unsigned int *)ioremap(BLU_BASE,40);if(blu_addr == NULL){printk("ioremap red err.\n");return -EINVAL;}//初始化蓝灯*(blu_addr+8) &=(~(0<<24));//选择GPIOB12功能*(blu_addr+8) |=(1<<25);//选择GPIOB12功能*(blu_addr+1) |=(1<<12);//选择输出使能*blu_addr &=(~(1<<12));//蓝灯关闭return 0;
}
//出口函数
static void __exit chrdev_exit(void)
{printk(KERN_ERR "%s %s %d\n", __FILE__, __func__, __LINE__);//取消映射iounmap(red_addr);//注销字符设备驱动unregister_chrdev(major, NAME);
}
module_init(chrdev_init);//入口
module_exit(chrdev_exit);//出口
MODULE_LICENSE("GPL");//协议

Makefile

KERNEL_PATH=/home/hq/kernel/kernel-3.4.39  #开发板的路径
#KERNEL_PATH=/lib/modules/$(shell uname -r)/build #虚拟机路径PWD=$(shell pwd)  #将shell命令pwd执行的结果赋值给PWD变量
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	# -C 路径:指定到切换到那个路径,执行make modules命令
	#  M=路径:指定需要编译的驱动代码所在的路径.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) cleanobj-m += chrdev.o#要编译生成驱动模块的目标程序

应用层

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>#include <stdio.h>void delay()
{int i;while(i<100){
        i++;}
}
int main(int argc,char *argv[])
{int fd = open(argv[1],O_RDWR);if(fd < 0){printf("open %s failed\n",argv[1]);return 2;}char buf[32] = {1,0,0};int i=0;int j=0;int z=0;while(1){write(fd,buf,sizeof(buf));// for(;i<3;i++)// {//     buf[0]=buf[0]?0:1;//     delay();//     for(;j<2;j++)//     {//         buf[1]=buf[1]?0:1;//         delay();//         for(;z<1;z++)//         {//             buf[2]=buf[2]?0:1;//             delay();//         }//     }// }sleep(1);
        buf[0]=1;
        buf[1]=0;
        buf[2]=0;write(fd,buf,sizeof(buf));sleep(1);
        buf[0]=0;
        buf[1]=1;
        buf[2]=0;write(fd,buf,sizeof(buf));sleep(1);
        buf[0]=0;
        buf[1]=0;
        buf[2]=1;write(fd,buf,sizeof(buf));sleep(1);
        buf[0]=1;
        buf[1]=1;
        buf[2]=0;write(fd,buf,sizeof(buf));sleep(1);
        buf[0]=1;
        buf[1]=0;
        buf[2]=1;write(fd,buf,sizeof(buf));sleep(1);
        buf[0]=0;
        buf[1]=1;
        buf[2]=1;write(fd,buf,sizeof(buf));sleep(1);
        buf[0]=1;
        buf[1]=1;
        buf[2]=1;write(fd,buf,sizeof(buf));// sleep(1);// // buf[2]=buf[2]?0:1;// buf[0]=buf[0]?0:1;// // buf[1]=buf[1]?0:1;// write(fd,buf,sizeof(buf));// sleep(1);// // buf[0]=buf[0]?0:1;// buf[1]=buf[1]?0:1;// // buf[2]=buf[2]?0:1;//  write(fd,buf,sizeof(buf));// sleep(1);// // buf[1]=buf[1]?0:1;// buf[2]=buf[2]?0:1;// write(fd,buf,sizeof(buf));}close(fd);return 0;
}

设备节点创建问题(udev/mdev)

(mknod hello  c 243 0,手动创建设备节点hello)

宏有返回值:为最后一句话执行的结果)

mknod 设备节点名 c/b 主设备号 次设备号

1、设置自动创建设备节点:

struct class *cls=class_create(THIS_MODULE,名字); 

struct device *dev=device_create(cls,NULL,\

MKDEV(主设备号,次设备号),NULL,设备节点的名字);

2、卸载驱动:设置自动销毁设备节点

device_destroy(cls,MKDEV(major,0));

class_destroy(cls);

1)自动创建设备节点:

struct class *cls;
#include <linux/device.h>
cls = class_create(owner, name)	
void class_destroy(struct class *cls)//销毁
	功能:向用户空间提交目录信息(内核目录的创建)
	参数:
		@owner :THIS_MODULE(看到owner就添THIS_MODULE)
		@name  :目录名字
	返回值:成功返回struct class *指针
			失败返回错误码指针 int (-5)	
if(IS_ERR(cls))
{	
	return PTR_ERR(cls);(PTR_ERR:把错误码指针转换成错误码)	
}	

IS_ERR(): 返回值为0,不在错误码地址范围,非0,在错误码地址范围,内核从0xffffffff 地址开始往地址减少的方向,预留了4K空间用来作为错误码的地址。

2)向用户空间提交文件信息

struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
//(内核文件的创建),每个文件对应一个外设(硬件设备)
/void device_destroy(struct class *class, dev_t devt)//销毁
	功能:向用户空间提交文件信息
	参数:@class :目录名字@parent:NULL@devt  :设备号 (major<<12 |0  < = > MKDEV(major,0)@drvdata :NULL@fmt   :文件的名字
	返回值:成功返回struct device *指针
			失败返回错误码指针 int (-5)

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

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

相关文章

Kafka日志

位置 server.properties配置文件中通过log.dir指定日志存储目录 log.dir/{topic}-{partition} 核心文件 .log 存储消息的日志文件&#xff0c;固定大小为1G&#xff0c;写满后会新增一个文件&#xff0c;文件名表示当前日志文件记录的第一条消息的偏移量。 .index 以偏移…

计算机视觉基础(13)——深度估计

前言 本节是计算机视觉的最后一节&#xff0c;我们将学习深度估计。从深度的概念和度量入手&#xff0c;依次学习单目深度估计和双目/多目深度估计&#xff0c;需要知道深度估计的经典方法&#xff0c;掌握深度估计的评价标准&#xff0c;注意结合对极几何进行分析和思考。 一、…

医疗影像中DR的骨抑制

1 背景 在DR的拍摄中&#xff0c;根据肺部和脊肋骨两种组织&#xff0c;在不同能量X射线的照射下&#xff0c;衰减的系数不同的特点&#xff0c;可以通过两次不同剂量的曝光后&#xff0c;通过算法&#xff0c;得到一张骨骼的图像和一张肺部图像。 通过一些机构的统计&#x…

MATLAB - 读取双摆杆上的 IMU 数据

系列文章目录 前言 本示例展示了如何从安装在双摆杆上的两个 IMU 传感器生成惯性测量单元 (IMU) 读数。双摆使用 Simscape Multibody™ 进行建模。有关使用 Simscape Multibody™ 构建简易摆的分步示例&#xff0c;请参阅简易摆建模&#xff08;Simscape Multibody&#xff09…

互联网+建筑工地源码,基于微服务+Java+Spring Cloud +Vue+UniApp开发

一、智慧工地概念 智慧工地就是互联网建筑工地&#xff0c;是将互联网的理念和技术引入建筑工地&#xff0c;然后以物联网、移动互联网技术为基础&#xff0c;充分应用BIM、大数据、人工智能、移动通讯、云计算、物联网等信息技术&#xff0c;通过人机交互、感知、决策、执行和…

Nginx优化(重点)与防盗链(新版)

Nginx优化(重点)与防盗链 Nginx优化(重点)与防盗链一、隐藏Nginx版本号1、修改配置文件2、修改源代码 二、修改Nginx用户与组1、编译安装时指定用户与组2、修改配置文件指定用户与组 三、配置Nginx网页的缓存时间四、实现Nginx的日志切割1、data的用法2、编写脚本进行日志切割的…

IntelliJ IDEA Community(社区版)下载及安装自用版

IntelliJ IDEA Community&#xff08;社区版&#xff09;下载及安装自用版 估计是个开发都逃脱不了用IDEA的命运吧&#xff0c;这么好的软件&#xff0c;白嫖了好多年。感恩。 现在很多公司已经不让用商业版的破解版了&#xff0c;所以这里讲的是社区版。 区别&#xff1a; 商…

【单调栈】LeetCode:2818操作使得分最大

作者推荐 map|动态规划|单调栈|LeetCode975:奇偶跳 本文涉及的基础知识点 单调栈分类、封装和总结 题目 给你一个长度为 n 的正整数数组 nums 和一个整数 k 。 一开始&#xff0c;你的分数为 1 。你可以进行以下操作至多 k 次&#xff0c;目标是使你的分数最大&#xff1a…

零基础入门网络安全必看的5本书籍(附PDF)

书中自有黄金屋&#xff0c;书中自有颜如玉。很多人学习一门技术都会看大量的书籍&#xff0c;经常也有朋友询问&#xff1a;零基础刚入门&#xff0c;应该看哪些书&#xff1f;应该怎么学&#xff1f;等等问题。今天就整理了5本零基础入门网络安全必看书籍&#xff0c;希望能帮…

hyper-v ubuntu 3节点 k8s集群搭建

前奏 搭建一主二从的k8s集群&#xff0c;如图所示&#xff0c;准备3台虚拟机。 不会创建的同学&#xff0c;可以看我上上篇博客&#xff1a;https://blog.csdn.net/dawnto/article/details/135086252 和上篇博客&#xff1a;https://blog.csdn.net/dawnto/article/details/135…

Python 数据分析 Matplotlib篇 plot设置线条样式(第2讲)

Python 数据分析 Matplotlib篇 plot设置线条样式(第2讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

Django(二)

1.django框架 1.1 安装 pip install django3.21.2 命令行 创建项目 cd 指定目录 django-admin startproject 项目名mysite ├── manage.py [项目的管理工具] └── mysite├── __init__.py├── settings.py 【配置文件&#xff0c;只有一部分…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

【毕业快刊】IF12分,中科院2区,仅50天录用,17天见刊!国人占比第一!

计算机类 • 好刊解读 今天小编带来Springer旗下计算机领域高分快刊&#xff0c;如您有投稿需求&#xff0c;可作为重点关注&#xff01;后文有相关领域真实发表案例&#xff0c;供您投稿参考~ 01 期刊简介 Artificial Intelligence Review ✅出版社&#xff1a;Springer ✅…

Wireshark网络工具来了

Wireshark是网络包分析工具。网络包分析工具的主要作用是尝试捕获网络包&#xff0c;并尝试显示包的尽可能详细的情况。 Wireshark是一个免费开源软件&#xff0c;不需要付费&#xff0c;免费使用&#xff0c;可以直接登陆到Wireshark的官网下载安装。 在windows环境中&#x…

一个利用摸鱼时间背单词的软件

大家好&#xff0c;我是 Java陈序员。 最近进入了考试季&#xff0c;各种考试&#xff0c;英语四六级、考研、期末考等。不知道大家的英语四六级成绩怎么样呢&#xff1f; 记得大学时&#xff0c;英语四级都是靠高中学习积累的老本才勉强过关。 而六级则是考了多次&#xff…

50 个具有挑战性的概率问题 [第 5 部分]:方形硬币

一、说明 你好&#xff0c;我最近对与概率相关的问题产生了兴趣。我偶然发现了 Frederick Mosteller 所著的《五十个具有挑战性的概率问题及其解决方案》这本书。我认为创建一个系列来讨论这些可能作为面试问题出现的迷人问题会很有趣。每篇文章仅包含 1 个问题&#xff0c;使其…

css学习笔记6(盒子模型)

CSS盒子模型 五、CSS盒子模型1.CSS长度单位2.元素的显示模式3.总结各元素的显示模式4.修改元素显示模式5.盒子模型的组成6.盒子内容区&#xff08;content&#xff09;7.关于默认宽度8.盒子内边距&#xff08;padding&#xff09;9.盒子边框&#xff08;border&#xff09;10.盒…

在Jetpack Compose中使用ExoPlayer实现直播流和音频均衡器

在Jetpack Compose中使用ExoPlayer实现直播流和音频均衡器 背景 ExoPlayer与Media3的能力结合&#xff0c;为Android应用程序播放多媒体内容提供了强大的解决方案。在本教程中&#xff0c;我们将介绍如何设置带有Media3的ExoPlayer来支持使用M3U8 URL进行直播流。此外&#x…

LeetCode刷题--- 字母大小写全排列

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 http://t.csdnimg.cn/6AbpV 数据结构与算法 http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述递归递归、搜索与回…