linux led 驱动

news2024/11/23 21:18:10

前言

今天是儿童节,挣个奖牌给小孩玩玩。
在这里插入图片描述
在 linux 驱动大家庭中,LED 驱动算是个儿童,今天就写写他吧。正好之前写过他的婴儿时期《i.MX6ULL 裸机点亮 LED》,记得那时候他还穿着开裆裤呢,裸鸡嘛在这里插入图片描述在这里插入图片描述在这里插入图片描述

ioremap()

裸机程序也好、linux 驱动程序也好,最终都是要操作真实设备的,那就要操作物理地址(设备寄存器)。之前写裸机程序,由于没有开启 MMU,CPU 操作的地址就是物理地址。现在写 linux 驱动程序不一样了,内核(包括驱动)操作的都是虚拟地址而无法直接操作物理地址。要想操作物理地址,就要上一个大杀器:ioremap(),可以参考我之前的一篇文章《ioremap()》,这里就不多介绍了。

代码

led.c

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

#define LED_MAJOR 200  /* 主设备号 */
#define LED_NAME "led" /* 字符设备名称: cat /proc/devices 显示的字符设备名称 */

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

/* 寄存器物理地址 */
#define CCM_CCR_BASE 0x020C4000	 // Clock Controller Module(CCM)
#define CCM_CCGR1 (CCM_CCR_BASE + 0x6C)
#define GPIO1_BASE 0x0209C000
#define GPIO1_DR (GPIO1_BASE + 0x0)
#define GPIO1_GDIR (GPIO1_BASE + 0x4)
#define SW_MUX_CTL_BASE 0x020E0000	// software mux control registers
#define SW_MUX_CTL_PAD_GPIO1_IO03 (SW_MUX_CTL_BASE + 0x68)
#define SW_PAD_CTL_PAD_GPIO1_IO03 (SW_MUX_CTL_BASE + 0x2F4)

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *V_CCM_CCGR1;
static void __iomem *V_SW_MUX_GPIO1_IO03;
static void __iomem *V_SW_PAD_GPIO1_IO03;
static void __iomem *V_GPIO1_DR;
static void __iomem *V_GPIO1_GDIR;

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;
}

void led_switch(u8 sta)
{
	u32 val;

	if (sta == LEDON) {
		val = readl(V_GPIO1_DR);
		val &= ~(1 << 3);
		writel(val, V_GPIO1_DR);
	} else if (sta == LEDOFF) {
		val = readl(V_GPIO1_DR);
		val |= (1 << 3);
		writel(val, V_GPIO1_DR);
	}
}

/*
 * @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;
	unsigned char databuf[1];
	unsigned char ledstat;

	retvalue = copy_from_user(databuf, buf, cnt);
	if (retvalue < 0) {
		printk("kernel write failed!\n");
		return -EFAULT;
	}

	ledstat = databuf[0]; /* 获取状态值 */

	if (ledstat == LEDON) {
		led_switch(LEDON);	/* 打开LED灯 */
	} else if (ledstat == LEDOFF) {
		led_switch(LEDOFF); /* 关闭LED灯 */
	}

	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;

	/* 1. 寄存器地址映射 */
	V_CCM_CCGR1 = ioremap(CCM_CCGR1, 4);
	V_SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_CTL_PAD_GPIO1_IO03, 4);
	V_SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_CTL_PAD_GPIO1_IO03, 4);
	V_GPIO1_DR = ioremap(GPIO1_BASE, 4);
	V_GPIO1_GDIR = ioremap(GPIO1_GDIR, 4);

	/* 2. 使能GPIO1时钟 */
	val = readl(V_CCM_CCGR1);
	val |= (3 << 26);
	writel(val, V_CCM_CCGR1);

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

	/* 4. 设置GPIO1_IO03为输出功能 */
	val = readl(V_GPIO1_GDIR);
	val |= (1 << 3);
	writel(val, V_GPIO1_GDIR);

	/* 5. 配置引脚属性,驱动能力、速度、上下拉 */
	writel(0x10B0, V_SW_PAD_GPIO1_IO03);

	/* 6. 默认关闭 LED */
	val = readl(V_GPIO1_DR);
	val |= (1 << 3);
	writel(val, V_GPIO1_DR);

	/* 7. 注册字符设备驱动 */
	retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
	if (retvalue < 0) {
		printk("register chrdev failed!\n");
		return -EIO;
	}

	return 0;
}

static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(V_CCM_CCGR1);
	iounmap(V_SW_MUX_GPIO1_IO03);
	iounmap(V_SW_PAD_GPIO1_IO03);
	iounmap(V_GPIO1_DR);
	iounmap(V_GPIO1_GDIR);

	/* 注销字符设备驱动 */
	unregister_chrdev(LED_MAJOR, LED_NAME);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liyongjun");

led_app.c

#include "fcntl.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/stat.h"
#include "sys/types.h"
#include "unistd.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("Usage: %s devfile 0/1\n", argv[0]);
		return -1;
	}

	filename = argv[1];

	/* 打开设备文件驱动 */
	fd = open(filename, O_RDWR);
	if (fd < 0) {
		printf("file %s open failed!\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!\n");
		close(fd);
		return -1;
	}

	/* 关闭文件 */
	retvalue = close(fd);
	if (retvalue < 0) {
		printf("file %s close failed!\n", argv[1]);
		return -1;
	}

	return 0;
}

Makefile

KERNELDIR := ../../../linux/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
	rm led_app

CROSS_COMPILE = ../../../tool/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc

app:
	$(CROSS_COMPILE) led_app.c -o led_app

install:
	cp led.ko led_app ../../../rootfs/home/root/

验证

step1:安装驱动程序

insmod led.ko

# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 led
207 ttymxc

step2:创建字符设备文件

# mknod /dev/led c 200 0
#
# ls -lh /dev/led
crw-r--r-- 1 root root 200, 0 May 31 14:06 /dev/led

step3:执行测试程序

# ./led_app /dev/led 1
# ./led_app /dev/led 0

看到 led 亮、灭

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

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

相关文章

某点资讯Signature纯算逆向

本篇主要是介绍一些工作的运用熟练性&#xff0c;以及跟踪堆栈去看是否做一些其他操作等&#xff1a; 抓包: signature 为加密值&#xff1b; 先上trace下堆栈及加密 我们把结果base64下&#xff0c;看结果是否一致&#xff0c;来判断base64是否魔改 验证base64为标准&…

新规之下产业园区如何合理收费水电费用

一、政策背景 2018年3月30日&#xff0c;国家发改委发布《国家发展改革委关于降低一般工商业电价有关事项的通知》。明确提出进一步规范和降低电网环节收费&#xff0c;一是提高两部制电价的灵活性&#xff1b;二是全面清理规范电网企业在输配电价之外的收费项目&#xff0c;重…

三极管 场效应管

NPN 高电平导通 PNP 低电平导通 N-MOS 高电平导通 P-MOS 低电平导通 1. NPN 三极管&#xff0c;对于软件工程师来说&#xff0c;只需要关注数字电路&#xff0c;即: 导通还是截止&#xff0c;高电平还是低电平。至于三级管内部如何构成的&#xff0c;以及串了多少个电阻&am…

智能安全配电装置在老旧建筑防火中的应用

【摘要】现代社会的发展离不开电能&#xff0c;随着电能应用的广泛性&#xff0c;对用电安全有了更高的要求。近些年来&#xff0c;用电安全形式严峻&#xff0c;尤其是一些老旧建筑中因用电而引起的火灾事故频发&#xff0c;造成一系列严重的损失&#xff0c;严重影响着民众的…

PCout(n) -- STM32F103RCT6 位带操作

1. 使用位带操作控制GPIO口的输入、输出模式&#xff0c;以及输出的电平高、低 注&#xff1a;位带操作一般是操作单独的一个bit 位&#xff0c;而&&#xff0c;| 则可操作多个bit位&#xff0c;看自己的需求吧。&#xff08;不懂&&#xff0c;| 是什么意思的自行问度…

MySQL-6-多表操作

一、复制表 格式 create table 表名 select查询语句注意&#xff1a;复制成新表时&#xff0c;键值&#xff08;pri,index等等&#xff09;索引不会同步复制案例 mysql> create table t2 select name,sex,age from user;二、多表查询 2.1、 多表查询–>连接查询 将2个…

Vue.js 比较重要知识点总结一

概述 谈一谈你对 Vue.js 的响应式数据的理解Vue3 出现解决了什么问题&#xff1f;它有哪些优势&#xff1f;Vue3 新特性有哪些vue2 和 vue3 的响应式有什么区别&#xff1f; 谈一谈你对 Vue.js 的响应式数据的理解 Vue 2.x 对象类型&#xff1a;通过 object.defineProperty(…

MySQL——初窥门径

前言 六一&#xff1f;作为一个大小孩当然是快快乐乐搞技术啦~在这篇文章中&#xff0c;荔枝会梳理SQL语句的基本语法以及MySQL中的函数、约束。多表关系以及查询、事务和事务隔离级别等内容&#xff0c;大致内容归属于MySQL基础知识&#xff0c;荔枝又弄了一篇万字长文哈哈哈哈…

R:GAM非线性回归曲线拟合与散点密度图绘制

作者:CSDN @ _养乐多_ 本文将介绍使用R语言以及GAM模型,绘制回归曲线和散点密度图。 文章目录 一、R语言脚本二、色带一、R语言脚本 install.packages("ggpointdensity") install.packages("ggplot2") insta

IPD发展史

随着IPD&#xff08;集成产品开发&#xff09;在IBM、华为等企业取得了巨大的成功&#xff0c;其他行业也开始在相关新产品研发中初步引入IPD的研发管理理念及模式&#xff0c;对IPD在行业的应用进行初步的探索和研究。 为了更好地应用IPD &#xff0c;不仅要对它的理念和思想理…

浅谈高等学校能源监控管理体系建设

摘要&#xff1a;现代高校担当着人才培养&#xff0c;社会服务和文化传承与创新的光荣使命。高校低碳节能工作是加快建设“和谐社会”、“绿色校园”的重要举措 。当前高校以“数字化能源监测平台”为重心 &#xff0c;积极推动能源管理的转型 。该文总结高校能源监管平台建设的…

达梦数据库作业调度及警报配置

目录 作业... 4 创建代理环境... 4 1、命令行创建及删除... 4 2、客户端创建及删除... 4 操作员... 5 1、命令行创建及删除... 5 2、客户端创建及删除... 5 作业... 6 一、命令行... 6 1、命令行创建作业... 6 2、命令行修改作业... 7 3、启动或暂停作业... 7 4、…

MATLAB与深度学习:Neural Network Toolbox和Deep Learning Toolbox的使用和模型设计

章节一&#xff1a;引言 在当今人工智能和深度学习的时代&#xff0c;MATLAB作为一种功能强大的科学计算和数据分析工具&#xff0c;在深度学习领域也发挥着重要作用。本文将重点介绍MATLAB中的两个关键工具&#xff1a;Neural Network Toolbox和Deep Learning Toolbox的使用和…

chatgpt赋能python:Python主页的SEO优化

Python主页的SEO优化 Python是一种简单易学、高效灵活的编程语言。其主页Python.org是全球最受欢迎的编程语言之一的官方网站。但是&#xff0c;即使是最著名的网站也需要进行优化&#xff0c;以便在搜索引擎中排名更高。在本文中&#xff0c;我们将探讨如何通过SEO来改进Pyth…

关系型数据库一些概念性的知识点总结

在当今数据驱动的世界中&#xff0c;信息为王。从客户资料到金融交易&#xff0c;每个组织都依赖数据来做出明智的决策并在竞争中保持领先地位。但随着数据量以前所未有的速度增长&#xff0c;管理和分析所有这些信息很快就会变得不堪重负。这就是关系数据库的用武之地。 关系数…

kafka的基础知识及概念

介绍 kafka在过去几年获得了巨大的普及。在微服务架构中&#xff0c;它起着举足轻重的作用。它使数据能够从一项服务转移到另一项服务。我开始这个系列是为了帮助初学者深入了解 Kafka。但是&#xff0c;在我们深入之前&#xff0c;了解一些基础知识很重要。因此&#xff0c;在…

《智能新工厂规划白皮书》:新工厂发展趋势一览

在经济下行压力、人口红利消失、消费结构升级、疫情冲击等多种因素推动下&#xff0c;传统工厂的寒冬已至&#xff0c;必须要变革才能顺应未来的发展趋势。伴随着5G、工业互联网、AI、工业大数据、工业软件等技术或产品的发展融合&#xff0c;许多企业纷纷规划建设智能新工厂&a…

【Web服务器集群】Nginx网站服务

文章目录 一、Nginx 概述1.什么是 Nginx2.Nginx 的特点3.Nginx 应用场景 二、Nginx 服务基础1.编译安装 Nginx 服务1.1 布置环境1.2 安装依赖包1.3 创建运行用户、组1.4 编译安装 2.Nginx 的运行控制2.1 检查配置文件2.2 启动、停止 Nginx2.3 日志分割以及升级 Nginx 服务2.4 添…

Linux - 第18节 - 网络基础(传输层一)

目录 1.传输层 1.1.再谈端口号 1.2.端口号范围划分 1.3.认识知名端口号 1.4.两个问题 1.5.netstat命令 1.6.pidof命令 2.UDP协议 2.1.UDP协议格式 2.2.UDP协议的特点 2.3.面向数据报 2.4.UDP的缓冲区 2.5.UDP使用注意事项 2.6.基于UDP的应用层协议 3.TCP协议 3…

抖音seo源码开发代码分享

抖音seo优化&#xff0c;抖音seo源码开发&#xff0c;抖音排名系统源码开发展示实例&#xff1a; 思路&#xff1a;抖音seo源码&#xff0c;抖音seo矩阵系统底层框架上支持了从ai视频混剪&#xff0c;视频批量原创产出&#xff0c;云存储批量视频制作&#xff0c;账号矩阵&…