当前位置: 首页 > news >正文

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

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.5GPIO_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.lryc.cn/news/378169.html

相关文章:

  • 钡铼BL101网关助力智慧城市路灯远程智能管控
  • 如何优雅的使用Github Action服务来将Hexo部署到Github Pages
  • After Effects 2024 mac/win版:创意视效,梦想起航
  • 信息打点web篇----web后端源码专项收集
  • ArcGIS批量投影转换的妙用(地理坐标系转换为平面坐标系)
  • YOLOv10训练自己的数据集(图像目标检测)
  • 解决不能拉取 docker 镜像
  • 44、基于深度学习的癌症检测(matlab)
  • Vue3 【仿 react 的 hook】封装 useTitle
  • CSS 计数器
  • 磁力搜索器,解读新一代的搜索引擎方式,磁力王、磁力猫等引擎的异同及原理
  • Apache Doris 全新分区策略 Auto Partition 应用场景与功能详解 | Deep Dive系列
  • 【Linux】关于在华为云中开放了端口后仍然无法访问的问题
  • Linux系统ubuntu20.04 无人机PX4 开发环境搭建(失败率很低)
  • 中间件(express)
  • 【代码随想录算法训练Day44】LeetCode 322.零钱兑换、LeetCode 279.完全平方数、LeetCode139.单词拆分
  • ChatGLM2-6B 部署
  • 武汉工程大学24计算机考研数据,有学硕招收调剂,而专硕不招收调剂!
  • python爬虫之selenium自动化操作
  • 【漏洞复现】红帆iOffice.net wssRtSyn接口处存在SQL注入
  • 云计算【第一阶段(17)】账号和权限管理
  • 环境配置02:CUDA安装
  • Ranger配置图片及json文件预览
  • C语言 | Leetcode C语言题解之第169题多数元素
  • 常说的云VR是什么意思?与传统vr的区别
  • 华为云CodeArts API:API管理一体化平台 5月新特性上线啦!
  • ubuntu16因swap分区uuid错误启动慢排查
  • [保姆级]uniapp自定义导航栏
  • Java 桥接模式(Bridge Pattern)是设计模式中的一种结构型设计模式,桥接模式的核心思想是将抽象与实现解耦
  • 入门Ansible常用模块