IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 字符设备驱动——(应用控制) -> 正文阅读

[嵌入式]字符设备驱动——(应用控制)

上次我们搭了一个驱动的框架,并且使用了文件接口,去控制驱动。但是我们只是搭了一个框架,申明都没有做。

一、应用控制内核(与内核实现数据交互)
1)交互原理
我们将实现在应用层的用户空间去控制内核空间,并且通过内核空间去控制硬件。这就是一个简单的驱动
我们将做一个led点灯的实验(学软硬件结合(单片机,嵌入式,物联网),点灯实验就跟helloworld一样,虽然很基础,但是你可以有无数个方法去实现)。
我们在创建设备号的时候,的这个函数就为我们提供了一个用文件控制的接口。
在这里插入图片描述
在这里插入图片描述
这个my_opes是一个文件控制结构体,一切皆文件,当我们创建了一个设备节点的时候,其实就是在磁盘的DEV目录下挂载了一个文件,那么,它挂载了这个文件它就有一个独有的文件描述符,
在应用层我们就可以用OPEN 去打开对应的/dev/“XXX”,你创建节点的时候起的什么名字,它也会创建的挂载一个一样名字的文件XXX。open的时候就会得到它独有的文件描述符,当你对通过文件描述符去文件操作的时候,文件结构控制结构体的对应函数就会操作。
EG:我在应用程序写了一个READ操作,那么,内核就会运行,结构体的write,并执行这个函数。
所以我们可以在通过,文件从应用层控制内核,实现数据交互。
2)交互实现
在这里插入图片描述
通过copy_to_user(函数把内核数据传递给用户) read操作
通过copy_from_user(把数据从用户写打内核)write操作 //利用文件描述符,和各自的buff缓冲区完成/(缓冲区可以之定义,随便用数组,指针,。。。。都可以)。

 应用程序需要传递数据给驱动
int copy_to_user(void __user * to, const void * from, unsigned long n)
//将数据从内核空间拷贝到用户空间,一般是在驱动中chr_drv_read()用
参数1:应用驱动中的一个buffer
参数2:内核空间到一个buffer
参数3:个数
返回值:大于0,表示出错,剩下多少个没有拷贝成功
		等于0,表示正确

int copy_from_user(void * to, const void __user * from, unsigned long n)
//将数据从用户空间拷贝到内核空间,一般是在驱动中chr_drv_write()用
参数1:内核驱动中的一个buffer
参数2:应用空间到一个buffer
参数3:个数
——————————————————————————————————————————————————————

二、内核控制底层硬件。
之前我们学过ARM的裸机驱动。
我们通过芯片手册通过物理地址,去控制寄存器然后实现我们想要的操作。
我们用内核控制底层硬件其实也差不多,但是我们控制的不是物理地址,而是虚拟地址。
这个虚拟地址是实时的,你想把一段物理地址赋给虚拟地址的时候就赋,不想的时候呢,就解除。
在这里插入图片描述

所以我们要用内核去控制LED灯
就先要知道你的物理地址
然后用函数把物理地址转化成虚拟地址,进行控制
控制外设,其实就是控制地址,内核驱动中是通过虚拟地址操作

	void *ioremap(cookie, size)
	参数1: 物理地址
	参数2: 长度
	返回值: 虚拟地址

去映射--解除映射
		void iounmap(void __iomem *addr)
		参数1: 映射之后到虚拟地址
——————————————————————————————————————
代码:
内核模块代码
/*********** 头文件***********/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<asm/uaccess.h>
#include<asm/io.h>

static unsigned int  kernel_val = 0;


//物理地址
#define GPX2_CON 0x11000C40
#define GPX2_SIZE 8

//虚拟地址指针
volatile unsigned long *gpx2conf;
volatile unsigned long *gpx2dat;




static unsigned int my_dev = 256;  		//主设备号
static struct class *devcls;   			//返回的类指针 DEVCLS = DEV CLASS
static struct device *dev; 				//返回的设备指针



ssize_t chr_dev_read (struct file *filp, char __user *buff, size_t count, loff_t *fops)
{
		int ret;
		printk("-------%s-------",__FUNCTION__);
		if((ret = copy_to_user(buff, &kernel_val, count)) > 0){
			printk("travel failed\n");
			return -EFAULT;}

		return 0;

}

ssize_t chr_dev_write (struct file *filp,char __user *buff, size_t count, loff_t *fops)
{
	

		int ret;
		printk("-------%s-------",__FUNCTION__);
		if((ret = copy_from_user(&kernel_val, buff, count)) == 0){
				printk("user read:%d\n",kernel_val);}
		else{
			printk("travel failed\n");
			return -EFAULT;}
		if(kernel_val > 0){
			*gpx2dat = *gpx2dat | (0x1 << 7);
		}else{
		*gpx2dat = *gpx2dat & (~(1 << 7));}

		return 0;

}
int chr_dev_open (struct inode *inode, struct file *filp)
{
	printk("-------%s-------",__FUNCTION__);

		return 0;
}

int chr_dev_close(struct inode *inode, struct file *filp)
{
	printk("-------%s-------",__FUNCTION__);

		return 0;
}
//文件操作结构体
const struct file_operations my_fops ={
	.read = chr_dev_read,
	.write = chr_dev_write,
	.open = chr_dev_open,
	.release = chr_dev_close,
};


/**********模块装载和卸载入口的实现********/


static int chr_drv_init (void){

	int ret;
//申请设备号
	ret = register_chrdev(my_dev, "led",&my_fops);
	if( ret == 0){
			printk("resgister ok!!\n");}
	else{
			printk("resgister failed!!\n");
			return -EFAULT;}//申请设备节点
	//创建类
		devcls = class_create(THIS_MODULE,"chr_clas");
	//申请节点
		dev = device_create(devcls, NULL,MKDEV(my_dev, 0), NULL, "LED");
//对地址进行映射 
	gpx2conf = ioremap(GPX2_CON,GPX2_SIZE);
	gpx2dat = gpx2conf + 1;           //DTA寄存器首地址
	//GPX1 INIT 设置为输出功能
	*gpx2conf = *gpx2conf & (~(0xf << 28)) | (0x1 << 28);
	
	return 0;
}



static void chr_drv_exit(void){

	device_destroy(devcls,MKDEV(my_dev, 0));
	class_destroy(devcls); 					//销毁类
	unregister_chrdev(my_dev, "led"); 		//销毁设备号
}

/********模块装载和卸载入口的申明*************/
module_init(chr_drv_init);
module_exit(chr_drv_exit);

/***********GPL申明**********************/
	MODULE_LICENSE("GPL");

————————————————(应用层代码)

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>


int main(int argc, const char *argv[])
{
	int fd;
	int vaule = 0;
 	if((fd = open("/dev/LED",O_RDWR)) < 0){
		perror("open");
		exit(0);}
	while(1){

		printf("input 0 or 1\n");
		scanf("%d",&vaule);
		write(fd,&vaule,4);
		read(fd,&vaule,4);
		printf("vaule = %d\n",vaule);
		//sleep(5);
/*		
		vaule = 0;
		write(fd, &vaule , 4);
		sleep(2);

		vaule = 1;
		write(fd, &vaule , 4);
		sleep(2);
		*/
	}

	close(fd);
	return 0;
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-14 13:30:52  更:2021-09-14 13:31:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:46:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码