驱动开发--创建设备文件--控制LED灯

news2024/10/7 8:22:27

目录

1、手动创建设备文件

2、应用程序如何将数据传递给驱动

3、控制LED灯:

4、应用层控制灯

         5、自动创建设备节点


1、手动创建设备文件

cat  /proc/devices 查看主设备号

sudo  mknod hello(路径:任意的)   c/b(C代表字符设备 b代表块设备)主设备号  次设备号

生成hello:应用层可以打开的文件

设置驱动层程序:hello.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>  //添加头文件
#define CNAME "hello"
int major=0;
ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
{ 
	printk("this is read\n"); 

    return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
{   
    printk("this is write\n");  

    return 0;
}
int mycdev_open (struct inode *inode, struct file *file)
{   
	printk("this is open\n");   
return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{   
	printk("this is close\n");   
	return 0;
}	
const struct file_operations fops={   

	.open=mycdev_open,   

	.read=mycdev_read,   

	.write=mycdev_write,   

	.release=mycdev_release,
};
static int __init hello_init(void)//入口
{ 
    major=register_chrdev(major,CNAME,&fops); 

    if(major<0)  

    {   

	printk("register chrdev error"); 

    } 
   return 0;
}
static void __exit hello_exit(void)//出口
{  
    unregister_chrdev(major,CNAME);
}
module_init(hello_init);//告诉内核驱动的入口

module_exit(hello_exit);//告诉内核驱动的出口

MODULE_LICENSE("GPL");

 设置应用层程序:test.c

2、应用程序如何将数据传递给驱动

(读写的方向是站在用户的角度来说的)

#include <linux/uaccess.h>

int copy_from_user(void *to, const void __user *from, int n)

功能:从用户空间拷贝数据到内核空间

参数:

@to  :内核中内存的首地址

@from:用户空间的首地址

@n   :拷贝数据的长度(字节)

返回值:成功返回0,失败返回未拷贝的字节的个数

int copy_to_user(void __user *to, const void *from, int n)

功能:从内核空间拷贝数据到用户空间

参数:

@to  :用户空间内存的首地址

@from:内核空间的首地址  __user需要加作用是告诉编译器这是用户空间地址

@n   :拷贝数据的长度(字节)

返回值:成功返回0,失败返回未拷贝的字节的个数

驱动层:hello.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/uaccess.h>   //添加头文件
#include <linux/fs.h>
#define CNAME "hello"
int major=0;
char kbuf[128]={0}; //存储数据
int dev=0;          
ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
{
    //printk("this is read\n");
    if(size>sizeof(kbuf))
    {
      size=sizeof(kbuf);
    }
    dev=copy_to_user(user,kbuf,size);  //从内核空间到用户空间
    if(dev)
    {
        printk("copy to user err");
        return dev;
    }
    return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
{  
    //printk("this is write\n");  
    if(size>sizeof(kbuf))
    {
      size=sizeof(kbuf);
    }
    dev=copy_from_user(kbuf,user,size);   //从用户空间到内核空间
 return 0;
}
int mycdev_open (struct inode *inode, struct file *file)
{  
    printk("this is open\n");  
return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{  
    printk("this is close\n");  
    return 0;
}    
const struct file_operations fops={  
    .open=mycdev_open,  
    .read=mycdev_read,  
    .write=mycdev_write,  
    .release=mycdev_release,
};
static int __init hello_init(void)//入口
{
    major=register_chrdev(major,CNAME,&fops);
    if(major<0)  
  {  
    printk("register chrdev error");
   }  
return 0;
}
static void __exit hello_exit(void)//出口
{  
    unregister_chrdev(major,CNAME);
}
module_init(hello_init);//告诉内核驱动的入口
module_exit(hello_exit);//告诉内核驱动的出口
MODULE_LICENSE("GPL");

应用程序:test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
char buf[128]="hello world!";
int main(int argc, const char *argv[])
{
   int fd;
   fd=open("./hello",O_RDWR);
   if(fd==-1)
{
    perror("open error");
    return -1;
}
    write(fd,buf,sizeof(buf));
    memset(buf,0,sizeof(buf));
    read(fd,buf,sizeof(buf));
    printf("buf is :%s\n",buf);
    close(fd);
    return 0;
}

$make

$sudo insmod hello.ko

$gcc test.c

$./a.out

$dmesg

 

 

3、控制LED灯:

驱动如何操作寄存器

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

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

void * ioremap(phys_addr_t offset, unsigned long size)

功能:将物理地址映射成虚拟地址

参数:@offset :要映射的物理地址

          @size   :大小(字节)

返回值:成功返回虚拟地址,失败返回NULL; 

void iounmap(void  *addr)

功能:取消映射

参数: @addr :虚拟地址

返回值:无

RGB_led 

red  :gpioa28

GPIOXOUT   :控制高低电平的   0xC001A000

GPIOxOUTENB:输入输出模式    0xC001A004

GPIOxALTFN1:function寄存器  0xC001A024

green:gpioe13    0xC001e000

blue :gpiob12     0xC001b000

 R:GPIOA

G: GPIOE

 B: GPIOB

 

宏定义基地址 ,设置虚拟地址

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

 指针类型加1是加的类型大小

 添加头文件:#include <linux/io.h>

取消映射:

 

 将hello.ko拷贝到开发板内核文件夹中指针类型加1是加的类型大小

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/io.h>     //添加头文件
int major=0;
#define CNAME "hello"
char kbuf[128]={0};
int dev=0;
#define RED_BASE  0XC001A000
#define BLUE_BASE  0XC001B000
#define GREEN_BASE 0XC001E000
unsigned int *red_base=NULL;
unsigned int *blue_base=NULL;
unsigned int *green_base=NULL;
ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
{
   //printk("this is read");
   if(size>128){
   	size=128;
	}
    dev=copy_to_user(user,kbuf,size);
	if(dev)
		{
		printk("copy to user errer");
		return dev;
	}
   return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
{
   //printk("this is write");
      if(size>128){
   	size=128;
	}
	dev=copy_from_user(kbuf,user,size);
   return 0;
}
int mycdev_open (struct inode *inode, struct file *file)
{
   printk("this is open");
   return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{
   printk("this is close");
   return 0;
}
const struct file_operations fops={
   .open=mycdev_open,
   .read=mycdev_read,
   .write=mycdev_write,
   .release=mycdev_release,
};
static int __init hello_init(void)//入口
{
  major=register_chrdev(major,CNAME,&fops);
  if(major<0)
  {
   printk("register chrdev error");
  }
  red_base=ioremap(RED_BASE,36);
  if(red_base==NULL)
  	{
	  printk("red ioremap error\n");
	  return -ENOMEM;
  }
  blue_base=ioremap(BLUE_BASE,36);
  if(blue_base==NULL)
  	{
	  printk("blue ioremap error\n");
	  return -ENOMEM;
  }
    green_base=ioremap(GREEN_BASE,36);
  if(green_base==NULL)
  	{
	  printk("green ioremap error\n");
	  return -ENOMEM;
  }

	*red_base &=~(1<<28);
	*(red_base+1) |=1<<28;  //指针类型加1是加的类型大小  int占4字节
	*(red_base+9) &=~(3<<24);
  return 0;
}
static void __exit hello_exit(void)//出口
{
  iounmap(green_base);
  iounmap(blue_base);
  iounmap(red_base);
  unregister_chrdev(major,CNAME);
}
module_init(hello_init);//告诉内核驱动的入口
module_exit(hello_exit);//告诉内核驱动的出口
MODULE_LICENSE("GPL");

Makefile:

 

4、应用层控制灯

 驱动层

判断语句 、宏定义开关

#define RED_ON *red_base |= 1<<28
#define RED_OF *red_base &= ~(1<<28)
ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
{
   //printk("this is write");
      if(size>128){
   	size=128;
	}
	dev=copy_from_user(kbuf,user,size);
	if(kbuf[0]==1)
		{
		RED_ON;
	}
	else{
		RED_OF;
	}
   return 0;
}

应用层 

需要将test.c 在开发板中进行编译,还需要拷贝编译生成的a.out,所以直接在Makefile中添加两行代码:


while(1)
	 {
    	write(fd,buf,sizeof(buf));
		sleep(1);
		buf[0]=buf[0]?0:1;
	 }

 

 

 

 

 

 

5、自动创建设备节点

头文件:#include <linux/device.h>

自动创建设备节点:

struct class *cls;

cls = class_create(owner, name) /void class_destroy(struct class *cls)

功能:向用户空间提交目录信息

参数:

@owner :THIS_MODULE  (宏,固定格式)

@name  :目录名字

返回值:成功返回struct class *指针

失败返回错误码指针 int (-5)

if(IS_ERR(cls)){           //将错误码指针转换为错误码

   printk("class _create error");

   return IS_ERR(cls);

}    

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 :目录名字 cls(向用户空间提交目录信息时产生的返回值)

@parent:NULL

@devt  :设备号    MKDEV(major,0)

@drvdata :NULL

@fmt   :文件的名字

返回值:成功返回struct device *指针

失败返回错误码指针 int (-5)

struct class*cls;

struct device *dev;

//自动创建设备节点

cls = class_create(THIS_MODULE,CNAME);

if(IS_ERR(cls)){

printk("class create error\n");

return PTR_ERR(cls);

}

dev = device_create(cls,NULL,MKDEV(major,0),NULL

,CNAME);

if(IS_ERR(dev)){

printk("device create error\n");

return PTR_ERR(dev);

}

 声明处:

 

 

 入口处:

 

 

 //出口-》这里写了卸载时注销的函数

static void __exit hello_exit(void)//出口

{

device_destroy(devc,MKDEV(major,1));//注销内容

class_destroy(cls);//注销目录

iounmap(green_base);

iounmap(blue_base);

iounmap(red_base);

unregister_chrdev(major,CNAME);

}

注意:MKDEV(major,0)为一个宏用于配置主设备号和次设备号

 

 make

 自动创建设备节点建立在dev里 

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

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

相关文章

华为无线AC双机热备三层组网配置案例

核心交换机: dis current-configuration sysname hx undo info-center enable vlan batch 10 66 88 99 to 100 ip pool vlan10 gateway-list 192.168.10.254 network 192.168.10.0 mask 255.255.255.0 dns-list 8.8.8.8 ip pool vlan100 gateway-list 172.16.100.254 network …

Qt在MySQL中存储音频文件

一、在存储音频视频等大文件时需要以二进制文件进行存储&#xff0c;首先需要了解mysql存储二进制文件的字段类型以及大小&#xff1a; 需要创建数据库中的图片类型为&#xff1a;二进制mediumblob类型&#xff0c;&#xff08; TinyBlob 最大 255 Blob 最大 65K MediumBlob …

苹果发布会,卧槽,卧槽,卧槽

今天跟二哥在群里聊到苹果的发布会&#xff0c;二哥完整的看了发布会&#xff0c;我随口问一句二哥看完后什么感受。 二哥说「苹果的工业设计还是遥遥领先&#xff0c;交互设计也是一流水准&#xff0c;然后价格也是遥遥领先」。 然后&#xff0c;我今天也抽空看了关于苹果新发…

【算法与数据结构】203、LeetCode移除链表元素

文章目录 题目一、解题思路完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 题目 一、解题思路 思路分析&#xff1a;这道题需要注意一个特殊情况&#xff0c;当删除的是头结点时&#xff0c;直接删除就找不到整个链表。因此我…

2023LRC软件、Adobe Lightroom Classic下载、安装教程

最后附下载地址 LRC简介&#xff1a; Adobe Lightroom Classic&#xff08;简称LR&#xff09;是Adobe Creative Cloud大家庭中的一款专业的图片管理和编辑工具&#xff0c;用于专业摄影师、摄影爱好者以及所有不断优化数码影像的人等。其目标是以丰富的功能提供高效、一致的…

03【WebStorm开发工具】

上一篇&#xff1a;02【HTML快速入门】 下一篇&#xff1a;04【】 目录&#xff1a;【HTML5系列教程】 文章目录 三、WebStorm开发工具3.1 WebStorm简介3.2 WebStorm安装3.3 WebStorm基本使用3.3.1 创建项目3.3.2 调整字体大小3.3.3 代码自动补全3.3.4 WebStorm常用快捷键 三…

在Anaconda的虚拟环境中添加环境变量并通过python访问(win/mac/linux)

一、前言 有的时候密码登比较敏感的信息&#xff0c;不方便直接写在代码里有很多变量我想很多project都可以访问到 那这时候使用环境变量是非常合适的了。 二、设置环境变量 以linux为例 直接在internal执行命令 export 变量值通过更改bashc文件 vim ~/.bashrc # 在最后一行加上…

【双向链表】

双向链表 带头双向循环链表的实现1. 函数的声明2. 函数的实现3. 主函数测试 带头双向循环链表的实现 今天我们来实现一下带头双向循环链表&#xff0c;顾名思义&#xff0c;带头就是有哨兵位&#xff0c;哨兵位不是链表的头&#xff0c;它是连接头节点的一个节点&#xff0c;方…

ChatGPT提示词攻略之迭代提示词

当我们在调试程序时&#xff0c;通常很难一次就把程序正常跑起来。这是普遍现象。但我们会借助一些工具和手段&#xff0c;有步骤有流程地去调整程序&#xff0c;最终让程序按照我们想要的样子正常执行。 对于提示词来说也是一样的。当我们向ChatGPT提问时&#xff0c;一开始它…

从操作系统角度了解内存管理

一.内存管理 1.主要功能 内存管理的主要功能有: 内存空间的分配与回收。由操作系统完成主存储器空间的分配和管理&#xff0c;使程序员摆脱存储分配的麻烦&#xff0c;提高编程效率。地址转换。在多道程序环境下&#xff0c;程序中的逻辑地址与内存中的物理地址不可能一致, …

Effective第三版 中英 | 第二章 创建和销毁对象 | 固定资源首选使用依赖注入

文章目录 Effective第三版前言第二章 创建和销毁对象固定资源首选使用依赖注入 Effective第三版 前言 大家好&#xff0c;这里是 Rocky 编程日记 &#xff0c;喜欢后端架构及中间件源码&#xff0c;目前正在阅读 effective-java 书籍。同时也把自己学习该书时的笔记&#xff0…

接招吧!MySQL 10 连问

文章目录 &#x1f349;1. 索引底层采用什么数据结构&#xff1f;为什么不用hash&#x1f349;2. B树与B树区别&#xff1f;为何用B树&#xff1f;&#x1f349;3. 自增主键理解&#xff1f;&#x1f349;4. 为什么自增主键不连续&#x1f349;5. Innodb为什么推荐用自增ID&…

Jetpack Compose 中的基础组件

Button 默认样式 Button的lambda块中可以传入任意的Composable组件&#xff0c;但一般是放一个Text在里面 Button(onClick { println("确认onClick") }) {Text("默认样式") }按钮的宽高 如果想要宽一点或高一点的Button&#xff0c;可以通过Modifier修…

chatgpt赋能python:Python平面图制作教程

Python平面图制作教程 Python是一种高级编程语言&#xff0c;也是数据科学和机器学习领域中使用最广泛的编程语言之一。在数据可视化中&#xff0c;Python语言具有优秀的表现力和灵活性&#xff0c;可以为用户展示各种数据可视化方案。这篇文章将重点介绍Python如何制作平面图…

八字诀 · 十年之约

* * * 原创&#xff1a;刘教链 * * * 号外&#xff1a;今天在“刘教链Pro”发表了两篇文章&#xff0c;《一视同仁&#xff0c;SEC起诉Coinbase》和《SEC起诉币安的官方新闻稿》&#xff0c;欢迎关注“刘教链Pro”并阅读。 * * * 隔夜比特币奋力反弹&#xff0c;重新回升至27k…

激发数学思维:GPT-4实证研究探索挑战性数学问题

深度学习自然语言处理 原创作者&#xff1a;wkk 考虑到自然语言在许多科学和工程领域表达的数学问题的丰富性&#xff0c;使用大语言模型(LLM)来解决数学问题是一项有趣的研究工作。今天给大家介绍一篇微软研究院联合欧美高校关于如何使用GPT-4解决数学问题的研究论文。 之前的…

KCC@深圳 邀你共享『升压手电DIY』的创新之旅!

嘿&#xff0c;亲爱的KCC深圳的社区成员们&#xff01;你们是否曾经梦想过拥有一款强大的手电&#xff0c;能够在黑暗中照亮一切&#xff1f;现在&#xff0c;我将揭开一个让你们眼前一亮的活动——『升压手电DIY』&#xff01; 在这个充满创意的活动中&#xff0c;我们将带你们…

为什么我们是RISC-V的首选 GPU

谈起 GPU&#xff0c;很容易想到 Imagination。毫无疑问&#xff0c;鉴于 Imagination 在图形处理领域的积累&#xff0c;也成为 RISC-V 的首选 GPU。 凭借 30 多年的 PowerVR GPU&#xff0c;Imagination 非常熟悉图形处理器。在过去的时间里&#xff0c;我们不断创新&#xf…

总结893

学习目标&#xff1a; 月目标&#xff1a;6月&#xff08;线性代数强化9讲&#xff0c;背诵15篇短文&#xff0c;考研核心词过三遍&#xff09; 周目标&#xff1a;线性代数强化3讲&#xff0c;英语背3篇文章并回诵&#xff0c;检测 每日必复习&#xff08;5分钟&#xff09;…

java数组and二维数组

文章目录 java一维数组1.数组定义和定义格式1.1 定义1.2格式 2.数组初始化2.1数组动态初始化2.1.1格式 2.2动态数组初始化取值2.2.1格式 2.3 静态数组初始化取值2.3.1格式 3.数组求长度4.数组遍历 java二维数组1.二维数组动态初始化2.二维数组遍历3.二维数组长度 数组赋值1整数…