Linux驱动开发(二)--字符设备驱动开发提升 LED驱动开发实验

news2024/10/6 8:36:50

1、地址映射

在编写驱动之前,需要知道MMU,也就是内存管理单元,在老版本的 Linux 中要求处理器必须有 MMU,但是现在Linux 内核已经支持无 MMU 的处理器了。
MMU的功能如下:
  • 完成虚拟空间到物理空间的映射
  • 内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性

 我们重点来讲第一点,物理空间映射,虚拟空间到物理空间的映射,也叫做地址映射。
先了解虚拟地址(VA,Virtual Address)物理地址(PA,PhyscicalAddress)这两个概念,对于 32 位 的处理器来说,虚拟地址范围是 2^32=4GB,我们的开发板上有 512MB DDR3,这 512MB 的 内存就是物理内存,经过 MMU 可以将其映射到整个 4GB 的虚拟空间。

所以后续我们访问相关引脚进行操作的时候,如果没有开启 MMU 的话 直接向 0X020E0068 这个寄存器地址写入数据就可以配置 GPIO1_IO03 的复用功能。现在开启 了 MMU,并且设置了内存映射,因此就不能直接0X020E0068 这个地址写入数据了。我们必须得到 0X020E0068 这个物理地址在 Linux 系统里面对应的虚拟地址,这里就涉及到了物理内 存和虚拟内存之间的转换,需要用到两个函数:ioremap iounmap
总得来说也就是上面的步骤多加了一个IO映射的一个过程,仅此而已。

 

2、引脚操作回顾

2.1 STM32 GPIO回顾

主要是通过流程来作为思维的引导。
我们一般拿到一款全新的芯片,第一个要做的事情的就是驱动其 GPIO,控制其 GPIO 输出高低电平,我们学习 I.MX6U 也一样的,先来学习一下 I.MX6U GPIO。在学习 I.MX6U 的 GPIO 之前,我们先来回顾一下 STM32 GPIO 初始化(如果没有学过 STM32 就不用回顾了),我们以最常见的 STM32F103 为例来看一下 STM32 GPIO 初始化,示例代码如下:

void LED_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能 PB 端口时钟

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PB5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO 口速度
 GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化 GPIOB.5
 
 GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
}

看不止是看代码,要看流程,1.使能时钟  2.初始化GPIO,比如上下拉、速度  3.io口是否需要复用 4.GPIO输出高电平还是低电平 这是流程。

我们还需要了解相关位操作,具体如下所示:

分别是写(设置),读(清除)

val = data_reg;
val = val | (1<<n);
data_reg = val;

上述为写操作,具体涉及 “ | ”,不影响其他位。

val = data_reg;
val = val & ~(1<<n);
data_reg = val;

上述为读操作,具体涉及 “ ~  “  ” & ”,不影响其他位。

3、编译驱动程序

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define LED_MAJOR 200 /* 主设备号 */
#define LED_NAME "led" /* 设备名字 */

#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */

#define CCM_CCGR1_BASE (0X020C406C) 
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)

static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

/*
 * @description : LED 打开/关闭
 * @param - sta : LEDON(0) 打开 LED,LEDOFF(1) 关闭 LED
 * @return : 无
 */
void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3); 
		writel(val, GPIO1_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO1_DR);
		val |= (1 << 3);
		writel(val, GPIO1_DR);
	} 
}


static int led_open (struct inode *inodp, struct file *filp)
{
	return 0;
}

static ssize_t led_read (struct file *filp, char __user *buf, 
									     size_t cnt, loff_t *offt)
{
	return 0;
}

/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write (struct file *filp, const char __user *buf, 
											    size_t cnt, loff_t *offt)
{
	int retvalue = 0;
	unsigned char databuf[1];
	unsigned char ledstat;
	retvalue = copy_from_user(databuf,buf,cnt);
	if(retvalue < 0){
		printk("kernel write failed\r\n");
		return -EFAULT;
	}
	ledstat = databuf[0];
	if(ledstat == LEDON){
		led_switch(LEDON);
	}
	else if(ledstat == LEDOFF){
		led_switch(LEDOFF);
	}
	return 0;
}

/*
 * @description : 关闭/释放设备
 * @param - filp : 要关闭的设备文件(文件描述符)
 * @return : 0 成功;其他 失败
 */
static int led_release (struct inode *inodp, struct file *filp)
{
	return 0;
}

static struct file_operations led_fops = {
	.owner   = THIS_MODULE,
	.open    = led_open,
	.read    = led_read,
	.write   = led_write,
	.release = led_release,
};

static int __init led_init(void){
	int retvalue = 0;
	u32 val = 0;

	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4);
	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4);
	GPIO1_DR = ioremap(GPIO1_DR_BASE,4);
	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4);

	/* 2、使能 GPIO1 时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26); /* 清除以前的设置 */
	val |= (3 << 26); /* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);

    /* 3、设置 GPIO1_IO03 的复用功能,将其复用为
   	 * GPIO1_IO03,最后设置 IO 属性。
   	 */
   	writel(5, SW_MUX_GPIO1_IO03);
   
   	/* 寄存器 SW_PAD_GPIO1_IO03 设置 IO 属性 */
   	writel(0x10B0, SW_PAD_GPIO1_IO03);
   
   	/* 4、设置 GPIO1_IO03 为输出功能 */
   	val = readl(GPIO1_GDIR);
   	val &= ~(1 << 3); /* 清除以前的设置 */
   	val |= (1 << 3); /* 设置为输出 */

	writel(val, GPIO1_GDIR);

	/* 5、默认关闭 LED */
	val = readl(GPIO1_DR);
	val |= (1 << 3); 
	writel(val, GPIO1_DR);
	
	//设备号 名字 字符模型驱动的一个结构体
	retvalue = register_chrdev(LED_MAJOR,LED_NAME,&led_fops);
	if(retvalue < 0){
	// exit
	}
	return 0;
}

static void __exit led_exit(void){
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);
	unregister_chrdev(LED_MAJOR,LED_NAME);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("7yewh");

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

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

相关文章

安卓手机删除的照片怎么恢复?3个方法,小技巧大作用

你是否曾经不小心删除了手机里的珍贵照片&#xff0c;却不知道怎么恢复&#xff1f;别担心&#xff0c;今天我们就来分享几个简单的小技巧&#xff0c;帮助你轻松找回那些丢失的照片。这些技巧虽然简单&#xff0c;但却能发挥大作用&#xff0c;让你不再为丢失照片而烦恼。手机…

delphi 如何使用TEdgeBrowser组件以及打包环境在其他主机上运行

不管开发环境还是第三方环境先安装运行时库&#xff1a;Microsoft Edge WebView2 | Microsoft Edge Developer 开发环境可以直接通过&#xff1a; delphi IDE安装 安装完毕后进入到指定路径&#xff0c;复制里面的WebView2Loader.dll到你要开发的程序根目录&#xff1a; 大致路…

南开大学漏洞报送证书【文尾有福利】

证书介绍 获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;教育漏洞报告平台(EDUSRC) 兑换价格&#xff1a;30金币​ 获取条件&#xff1a;南开大学任意中危或以上级别漏洞 证书规格&#xff1a;证书做了木框装裱&#xff0c;显得很高…

C++设计模式——Flyweight享元模式

一&#xff0c;享元模式简介 享元模式是一种结构型设计模式&#xff0c;它将每个对象中各自保存一份数据的方式改为多个对象共享同一份数据&#xff0c;该模式可以有效减少应用程序的内存占用。 享元模式的核心思想是共享和复用&#xff0c;通过设置共享资源来避免创建过多的实…

反激开关电源输入电解电容选型及计算

输入电解电容波形如下 计算 1、输入电解电容经验选取法 当输入电压为220V20%&#xff08;AC176-264V&#xff09;时 按照输出功率选取&#xff1a;W/μF 耐压&#xff08;400V&#xff0c;输入176-264V最高DC峰值373V&#xff09; 当输入电压为11020%时&#xff08;AC88-132V…

进销存系统哪个好?2024最新进销存系统推荐(持续更新中)

现在市面上的进销存系统太多了&#xff0c;企业的选择越多&#xff0c;越容易踩坑。那企业在选择进销存系统时&#xff0c;需要考虑哪些因素&#xff1f; 1、系统的适配性 在进行选择之前&#xff0c;就应该先清楚自己的企业是重财务流管理、还是生产业务流管理、还是销售订单…

NSSCTF-Web题目8

目录 [LitCTF 2023]Http pro max plus 1、题目 2、知识点 3、思路 [NSSCTF 2022 Spring Recruit]babyphp 1、题目 2、知识点 3、思路 [LitCTF 2023]Http pro max plus 1、题目 2、知识点 http数据包头部字段 3、思路 打开题目 第一想法就是加上X-forwarded-for字段 …

ABAP程序示例:交互式ALV演示程序

这个程序是一个用于显示和刷新事务代码&#xff08;TCode&#xff09;列表的报表程序。它使用ALV&#xff08;ABAP List Viewer&#xff09;来显示数据&#xff0c;并提供了一个刷新功能&#xff0c;以便用户可以查看新的事务代码。以下是程序的主要部分及其功能&#xff1a; 数…

拉普拉斯矩阵对称归一化理解,通过一个简单的例子进行说明

拉普拉斯矩阵&#xff08;Laplacian Matrix&#xff09;是一个与图相关的矩阵&#xff0c;通常用于图分析、机器学习和信号处理等领域。它是由图的邻接矩阵或关联矩阵计算得出的。 对于一个无向图 G ( V , E ) G(V,E) G(V,E)&#xff0c;它的拉普拉斯矩阵 L L L 可以表示为…

springBoot高校宿舍交电费系统-计算机毕业设计源码031552

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作…

大数据-数据分析师利用excel绘图

你会用excel&#xff0c;统计数据吗&#xff1f;我是大数据工程师&#xff0c;但是我不会excel。那咋办&#xff1f; 用sql&#xff0c;统计&#xff0c;导出到excel&#xff0c;在用excel统计。本文主要讨论的是导出到excel后&#xff0c;画图。 图是什么&#xff1f; x和y…

视频画面大小裁剪怎么弄?这3种剪辑方法轻松解决

视频画面大小裁剪怎么弄&#xff1f;视频画面大小裁剪&#xff0c;是视频编辑中常见且实用的操作之一。它不仅能帮助我们精准地去除视频中的多余内容&#xff0c;还能根据实际需求调整画面大小&#xff0c;使之适应不同的播放平台和场景需求。通过裁剪&#xff0c;我们可以让视…

WordPress主题 酱茄免费主题

酱茄free主题由酱茄开发的一款免费开源的WordPress主题&#xff0c;主题专为WordPress博客、资讯、自媒体网站而设计&#xff0c;遵循GPL V2.0开源协议发布。 运行环境 酱茄Free主题当前版本&#xff1a;2020.11.25 V1.0.0 支持WordPress版本&#xff1a;5.4 兼容Chrome、Fire…

git知识域

知识体系 FAQ git clone与git pull区别 git clone&#xff1a;从无到有&#xff0c;包含分支信息&#xff0c;日志信息。 git pull&#xff1a;在已有项目基础上操作&#xff0c;针对特定分支。

React实现H5手势密码

监测应用进入前后台 在JavaScript中,监听H5页面是否在前台或后台运行,主要依赖于Page Visibility API。这个API在大多数现代浏览器中都是支持的,包括苹果的Safari和谷歌的Chrome(也就基本覆盖了Android和iOS平台)。下面是一个简单的示例代码,展示如何使用这个API来判断页…

PFA托盘400*300*42mm耐酸碱透明聚四氟乙烯方盘方槽耐高温厂家供

PFA方盘又称托盘&#xff1a;耐高温、耐腐蚀。 进口透明可溶性聚四氟乙烯方盘。可应用于成膜实验&#xff0c;样品液体脱漏等。能放在电热板上直接加热使用&#xff0c;也可以用于烘箱烘干&#xff0c;实验室腐蚀性样品的转移和搬运&#xff0c;防止腐蚀性液体洒落。 产品特性…

【LLM之RAG】Self-RAG论文阅读笔记

研究背景 尽管大型语言模型&#xff08;LLM&#xff09;展示出了显著的能力&#xff0c;但它们在生成回答时经常包含事实错误&#xff0c;因为它们仅依赖于封装在模型中的参数知识。增强型检索生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;是一种方法&…

队列(数据结构篇)

数据结构之队列 队列(queue) 概念&#xff1a; 队列也是一种线性表&#xff0c;使用队列时插入在一端进行而删除则在另一端进行&#xff0c;队列的基本操作是入队&#xff0c;它是在表的末端(也叫做队尾)插入一个元素&#xff0c;出队&#xff0c;它是删除在**表的开头(**队…

SOLIDWORKS 如何通过motion绘制凸轮结构

SOLIDWORKS motion是SOLIDWORKS中一个高性能的插件&#xff0c;一个能够帮助完成虚拟样机的仿真分析工具&#xff0c;motion既可以对众多的机械结构进行运动学和动力学仿真&#xff0c;同时也可以反馈机械设备的速度、加速度、作用力等&#xff0c;今天我们就通过motion来合成凸…

IPython大师课:提升数据科学工作效率的终极工具

IPython是一个增强的Python交互式shell&#xff0c;它提供了丰富的功能和易用性改进&#xff0c;特别适合进行数据分析、科学计算和一般的Python开发。本文将全面介绍IPython的基本概念、使用方法、主要作用以及注意事项。 一、IPython简介 1. IPython的起源 IPython最初由Fe…