Linux驱动开发学习笔记2《LED驱动开发试验》

news2025/1/12 18:17:56

目录

一、Linux下LED灯驱动原理

1.地址映射

二、硬件原理图分析

三、实验程序编写

1.LED 灯驱动程序编写

2.编写测试APP

四、运行测试

1.编译驱动程序和测试APP

(1)编译驱动程序

(2)编译测试APP

2.运行测试


一、Linux下LED灯驱动原理

        Linux 下的任何外设驱动,最终都是要配置相应的硬件寄存器。所以本章的LED 灯驱动最终也是对I.MX6ULL 的IO口进行配置,与裸机实验不同的是,在Linux 下编写驱动要符合Linux的驱动框架。I.MX6U-ALPHA 开发板上的LED 连接到I.MX6ULL 的GPIO1_IO03 这个引脚上,因此本章实验的重点就是编写Linux 下I.MX6UL引脚控制驱动。

1.地址映射

        MMU 全称叫做Memory Manage Unit,也就是内存管理单元。在老版本的Linux 中要求处理器必须有MMU,但是现在Linux 内核已经支持无MMU的处理器了。MMU主要完成的功能如下:

(1)完成虚拟空间到物理空间的映射

(2)内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性

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

        物理内存只有512MB,虚拟内存有4GB,那么肯定存在多个虚拟地址映射到同一个物理地址上去,虚拟地址范围比物理地址范围大的问题处理器自会处理。

        Linux 内核启动的时候会初始化MMU,设置好内存映射,设置好以后CPU 访问的都是虚拟地址。比如I.MX6ULL 的GPIO1_IO03 引脚的复用寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的地址为0X020E0068。如果没有开启MMU 的话直接向0X020E0068 这个寄存器地址写入数据就可以配置GPIO1_IO03 的复用功能。现在开启了MMU,并且设置了内存映射,因此就不能直接向0X020E0068 这个地址写入数据了。我们必须得到0X020E0068 这个物理地址在Linux 系统里面对应的虚拟地址,这里就涉及到了物理内存和虚拟内存之间的转换,需要用到两个函数:ioremap 和iounmap

二、硬件原理图分析

        从上图可以看出,LED0 接到了GPIO_3 上,GPIO_3 就是GPIO1_IO03,当GPIO1_IO03输出低电平(0)的时候发光二极管LED0 就会导通点亮,当GPIO1_IO03 输出高电平(1)的时候发光二极管LED0 不会导通,因此LED0也就不会点亮。所以LED0 的亮灭取决于GPIO1_IO03的输出电平,输出0 就亮,输出1 就灭

三、实验程序编写

1.LED 灯驱动程序编写

        新建名为“2_led”文件夹,然后在2_led 文件夹里面创建VSCode 工程,工作区命名为“led”。工程创建好以后新建led.c 文件,此文件就是led 的驱动文件,在led.c 里面输入如下内容:

#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/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;

//LED打开/关闭
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 *inode, struct file *filp)
{
    return 0;
}
//从设备读取数据
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}
//从设备写取数据
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int retvalue;
    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;
}
//关闭/释放设备
static int led_release(struct inode *inode, 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;

    //初始化LED
    //1.寄存器地址映射
    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); //清楚以前的设置bit26, 27
    val |= (3 << 26); //设置新值bit26,27置1
    writel(val, IMX6U_CCM_CCGR1);

    //3.设置GPIO1_IO03的复用功能,将其复用为GPIO1_IO03,最后设置IO属性
    writel(5, SW_MUX_GPIO1_IO03);

    //寄存器SW_PAD_GPIO1_IO03 设置电气属性
    writel(0x10B0, SW_PAD_GPIO1_IO03);

    //4.设置GPIO_IO03为输出功能,因为用的IO03,所以左移3位
    val = readl(GPIO1_GDIR);
    val &= ~(1 << 3); //清楚以前的设置
    val |= (1 << 3); //bit3置1,设置为输出
    writel(val, GPIO1_GDIR);

    //5.默认关闭LED,设置第三位为1就是高点平关闭LED
    val = readl(GPIO1_DR);
    val |= (1 << 3);      
    writel(val, GPIO1_DR);

    //6.注册字符设备驱动
    retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
    if(retvalue < 0)
    {
        printk("register chrdev failed!\r\n");
        return -EIO;
    }
    return 0;
}
//驱动出口函数
static int __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("ssz");

2.编写测试APP

        编写测试APP,led 驱动加载成功以后手动创建/dev/led 节点,应用APP 通过操作/dev/led文件来完成对LED 设备的控制。向/dev/led 文件写0 表示关闭LED 灯,写1 表示打开LED 灯。新建ledApp.c 文件,在里面输入如下内容:

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

#define LEDOFF 0
#define LEDON 1

int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename;
    unsigned char databuf[1];

    if(argc != 3)
    {
        printf("Error Usage!\r\n");
        return -1;
    }
    filename = argv[1];

    //打开led驱动
    fd = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("filr %s open failed!\r\n", argv[1]);
        return -1;
    }
    databuf[0] = atoi(argv[2]);

    //向/dev/led文件写入数据
    retvalue = write(fd, databuf, sizeof(databuf));
    if(retvalue < 0)
    {
        printf("LED Control Failed!\r\n");
        close(fd);
        return -1;
    }
    retvalue = close(fd); //关闭文件
    if(retvalue < 0)
    {
        printf("fail %s close failed!\r\n",argv[1]);
        return -1;
    }
    return 0;
}

四、运行测试

1.编译驱动程序和测试APP

(1)编译驱动程序

编写Makefile 文件如下:

KERNELDIR := /home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)
obj-m := led.o

build : kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

输入如下命令编译出驱动模块文件:

(2)编译测试APP
arm-linux-gnueabihf-gcc ledApp.c -o ledApp

2.运行测试

        将上一小节编译出来的led.ko 和ledApp 这两个文件拷贝到rootfs/lib/modules/4.1.15 目录中,重启开发板,进入到目录lib/modules/4.1.15 中,输入如下命令加载led.ko 驱动模块:

        驱动加载成功以后创建“/dev/led”设备节点,命令如下:

        驱动节点创建成功以后就可以使用ledApp 软件来测试驱动是否工作正常,输入如下命令打开LED 灯:

        输入上述命令以后观察I.MX6U-ALPHA 开发板上的红色LED 灯是否点亮,如果点亮的话说明驱动工作正常。在输入如下命令关闭LED 灯:

        如果要卸载驱动的话输入如下命令即可:

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

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

相关文章

分享81个节日PPT,总有一款适合您

分享81个节日PPT&#xff0c;总有一款适合您 81个节日PPT下载链接&#xff1a;https://pan.baidu.com/s/1V0feg5pZ8C1Szycy40CrUw?pwd6666 提取码&#xff1a;6666 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

Android CardView基础使用

目录 一、CardView 1.1 导入material库 1.2 属性 二、使用(效果) 2.1 圆角卡片效果 2.2 阴影卡片效果 2.3 背景 2.3.1 设置卡片背景(app:cardBackgroundColor) 2.3.2 内嵌布局&#xff0c;给布局设置背景色 2.4 进阶版 2.4.1 带透明度 2.4.2 无透明度 一、CardView 顾名…

【编码魔法师系列_构建型1.3 】抽象工厂模式(Abstract Factory)

学会设计模式&#xff0c;你就可以像拥有魔法一样&#xff0c;在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们&#xff08;GoF&#xff09;凝聚出来的最佳实践&#xff0c;可以提高代码的可读性、可维护性和可重用性&#xff0c;从而让我们的开发效率更高。通…

vs 安装 qt qt扩展 改迅雷下载qt

Qt5.14.2安装教程和VS2019中的qt环境配置-CSDN博客 1 安装qt 社区版 免费 Download Qt OSS: Get Qt Online Installer 2 vs安装 qt vs tools 3 vs添加 qt添加 bin/cmake.exe 路径 3.1 扩展 -> qt versions 3.2 4 新版要源码安装 需要自己安装 安装独立安装的旧版 官网…

pygame时序模块time

文章目录 简介时钟对象平抛运动 pygame系列&#xff1a;初步&#x1f48e;加载图像&#x1f48e;图像变换&#x1f48e;直线绘制 简介 之前在更新图形的时候&#xff0c;为了调控死循环的响应时间&#xff0c;用到了time.sleep。而实际上&#xff0c;我们并不需要额外导入其他…

最强Node js 后端框架学习看这一篇文章就够

距离上次认真花时间写作&#xff0c;似乎已经过了许久许久&#xff0c;前端讲了一个新框架 &#xff0c;叫 Nest.js 下方是课件&#xff0c;有过一定开发经验可跟随视频学习 B站 地址 &#xff1a; https://www.bilibili.com/video/BV1Lg4y197u1/?vd_sourcead427ffaf8a5c8344…

【计算机网络笔记】物理层——数据通信基础

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

【1】基于多设计模式下的同步异步日志系统-项目介绍

1. 项目介绍 本项⽬主要实现⼀个日志系统&#xff0c; 其主要支持以下功能: • 支持多级别日志消息 • 支持同步日志和异步日志 • 支持可靠写⼊日志到控制台、文件以及滚动文件中 • 支持多线程程序并发写日志 • 支持扩展不同的日志落地⽬标地 2. 开发环境 • CentOS 7 • vs…

Node版本管理nvm工具安装及使用问题

安装和配置 下载地址 nvm官方下载window环境直接下nvm-setup.zip解压安装即可。 安装效验以及镜像配置 在cmd中&#xff0c;输入nvm -v 会反馈相应的安装版本&#xff0c;即表示安装成功。配置镜像源: nvm node_mirror https://npm.taobao.org/mirrors/node/ nvm npm_mir…

GitHub上1.5K标星的QA和软件测试学习路线图

​最近在GitHub上发现一个项目&#xff0c;项目描述了作为QA工程师&#xff0c;进行软件测试技能提升时的&#xff0c;建议的软件测试学习顺序图​。 虽然2021年起就不再更新了&#xff0c;但是居然有1.5K的​星。 整个项目有两个部分​&#xff1a; ​1.QA和软件测试学习顺序…

线程池大小设置多少,比较合适?

设置线程数的核心点 压测&#xff01;压测&#xff01;再压测&#xff01;实际对性能要求比较高的场景&#xff0c;压测是最佳的方式&#xff01; 并发编程适用于什么场景&#xff1f; CPU 密集型 对于 CPU 密集型任务&#xff0c;希望最大限度地提高 CPU 利用率&#xff0c…

使用String.valueOf()的坑

说明&#xff1a;记录一次使用String.valueOf()的坑&#xff0c;以下是一段有问题的代码&#xff1a; String count String.valueOf(listData.get(0).get(0).get("count");if (StringUtils.isBlank(count) || "0".equals(count)) {result.setResult(page)…

LLM推理部署(五):AirLLM使用4G显存即可在70B大模型上进行推理

众所周知&#xff0c;大模型的训练和推理需要大量的GPU资源&#xff0c;70B参数的大模型需要130G的GPU显存来存储&#xff0c;需要两个A100&#xff08;显存为100G&#xff09;。 ​ 在推理过程中&#xff0c;整个输入序列也需要加载到内存中进行复杂的“注意力”计算&am…

【WinForm.NET开发】演示:创建一个图片查看器 Windows 窗体应用

本文演示将创建一个 Windows 窗体应用程序&#xff0c;用于加载和显示图片。 Visual Studio 集成设计环境 (IDE) 提供了创建应用所需的工具。 1、先决条件 若要完成本教程&#xff0c;必须具有 Visual Studio。 请访问Visual Studio 下载页获取免费版本。 2、创建 Windows …

C语言扫雷游戏

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、扫雷游戏的分析和设计1.1扫雷游戏的功能说明1.2数据结构的分析1.3文件结构设计 二、扫雷游戏的代码实现总结 前言 详细介绍扫雷游戏的思路和实现过程。 一…

基于Java SSM框架实现美好生活九宫格日志网站系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现美好生活九宫格日志网站系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人…

风险评估有什么用

风险评估就是量化测评某一事件或事物带来的影响或损失的可能程度。 为什么要做风险评估? 1.更准确地认识风险-系统地评估资产风险事件发生的概率大小和概率分布&#xff0c;及发生后损失的严重程度。帮助区分主要风险和次要风险。 2.保证规划的合理性和可行性-正确反映各风…

【开源】基于Vue.js的智慧社区业务综合平台

文末获取源码&#xff0c;项目编号&#xff1a; S 077 。 \color{red}{文末获取源码&#xff0c;项目编号&#xff1a;S077。} 文末获取源码&#xff0c;项目编号&#xff1a;S077。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 业务类型模块2.2 基础业务模块2.3 预…

网络安全--网络环境构成,系统的安全

2. 网络攻防环境 目标 了解攻防环境构成了解入侵检测系统&#xff08;平台&#xff09;的部署位置 2.1. 环境构成 2.1.1. 环境框图 一个基本的网络攻防实验环境包括&#xff1a;靶机、攻击机、入侵检测分析系统、网络连接四部分组成。 一个基础的网络攻防实验环境需要如下…

Java 学习之多态

多态的概念 多态 晚绑定。 所谓多态&#xff0c;就是父类型的引用可以指向子类型的对象&#xff0c;或者接口类型的引用可以指向实现该接口的类的实例。 不要把函数重载理解为多态。因为多态是一种运行期的行为&#xff0c;不是编译期的行为。 多态&#xff1a;父类型的引用可…