Linux驱动开发(驱动中如何操控硬件LED)

本文介绍了Linux内核驱动中LED驱动的原理和实现,包括硬件不变性、地址映射、内核驱动框架及三星平台的具体应用。在32位处理器上,虚拟地址和物理地址通过MMU映射,驱动开发中需使用内核提供的接口如`led_classdev_register`注册LED设备,并实现硬件操作。以S5PV210为例,展示了如何编写和注册LED驱动函数。
摘要由CSDN通过智能技术生成

1、还是那个硬件
(1)硬件物理原理不变
(2)硬件操作接口(寄存器)不变
(3)硬件操作代码不变

2、哪里不同了?
(1)寄存器地址不同。原来是直接用物理地址,现在需要用该物理地址在内核虚拟地址空间相对应的虚拟地址。寄存器的物理地址是CPU设计时决定的,从datasheet中查找到的。
(2)编程方法不同。裸机中习惯直接用指针操作寄存器地址,而kernel中习惯用封装好的io读写函数来操作寄存器,以实现最大程度可移植性

3.地址映射

在编写驱动之前,我们需要先简单了解一下 MMU 这个神器, MMU 全称叫做 Memory
Manage Unit,也就是内存管理单元。
MMU 主要完成的功能如下:
①、完成虚拟空间到物理空间的映射。
②、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。
首先了解两个地址概念:虚拟地址(VA,Virtual Address)、物理地址(PA, Physcical Address)。对于 32 位的处理器来说,虚拟地址范围是 2^32=4GB,我们的开发板上有 512MB 的 DDR3,这 512MB 的内存就是物理内存,经过 MMU 可以将其映射到整个 4GB 的虚拟空间。


4.内核驱动框架中LED的基本

(1) 相关文件:

drivers/leds目录:LED驱动的总文件。

led-class.c和led-core.c:LED驱动框架的第一部分,描述的是内核中所有厂家的不同LED硬件的相同部分的逻辑。

leds-xxxx.c:是LED驱动框架的第2部分,由不同厂商的驱动工程师编写添加的厂商驱动工程师结合自己公司的硬件的不同情况来对LED进行操作。

(2) 九鼎移植的内核中led驱动(厂家):

九鼎实际未使用内核推荐的led驱动框架,路径:drivers/char/led/x210-led.c。

(3) 三星驱动框架的使用(厂商):
以leds-s3c24xx.c为例,leds-s3c24xx.c中通过调用led_classdev_register来完成我们的LED驱动的注册,而led_classdev_register是在drivers/leds/led-class.c中定义的。所以其实SoC厂商的驱动工程师是调用内核开发者在驱动框架中提供的接口来实现自己的驱动的。
驱动框架的关键点就是:分清楚内核开发者提供了什么,驱动开发者自己要提供什么。


5.基于三星驱动框架写led驱动

(1)参考 drivers/leds/leds-s3c24xx.c(三星的驱动LED文件)
(2)关键点:led_classdev_register(内核开发者提供的LED驱动接口)

驱动源码:qewruiopqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqwiouqewri

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>



#define GPJ0CON		S5PV210_GPJ0CON
#define GPJ0DAT		S5PV210_GPJ0DAT

static struct led_classdev mydev1;			// 定义结构体变量
static struct led_classdev mydev2;			// 定义结构体变量
static struct led_classdev mydev3;			// 定义结构体变量

// 这个函数就是要去完成具体的硬件读写任务的
static void s5pv210_led1_set(struct led_classdev *led_cdev,
			    enum led_brightness value)
{
	printk(KERN_INFO "s5pv210_led1_set\n");
	
	writel(0x11111111, GPJ0CON);
	
	// 在这里根据用户设置的值来操作硬件
	// 用户设置的值就是value
	if (value == LED_OFF)
	{
		// 用户给了个0,希望LED灭
		//writel(0x11111111, GPJ0CON);
		// 读改写三部曲
		writel((readl(GPJ0DAT) | (1<<3)), GPJ0DAT);
	}
	else
	{
		// 用户给的是非0,希望LED亮
		//writel(0x11111111, GPJ0CON);
		writel((readl(GPJ0DAT) & ~(1<<3)), GPJ0DAT);
	}
}

static void s5pv210_led2_set(struct led_classdev *led_cdev,
			    enum led_brightness value)
{
	printk(KERN_INFO "s5pv2102_led_set\n");
	
	writel(0x11111111, GPJ0CON);
	
	// 在这里根据用户设置的值来操作硬件
	// 用户设置的值就是value
	if (value == LED_OFF)
	{
		// 用户给了个0,希望LED灭
		//writel(0x11111111, GPJ0CON);
		// 读改写三部曲
		writel((readl(GPJ0DAT) | (1<<4)), GPJ0DAT);
	}
	else
	{
		// 用户给的是非0,希望LED亮
		//writel(0x11111111, GPJ0CON);
		writel((readl(GPJ0DAT) & ~(1<<4)), GPJ0DAT);
	}
}

static void s5pv210_led3_set(struct led_classdev *led_cdev,
			    enum led_brightness value)
{
	printk(KERN_INFO "s5pv210_led3_set\n");
	
	writel(0x11111111, GPJ0CON);
	
	// 在这里根据用户设置的值来操作硬件
	// 用户设置的值就是value
	if (value == LED_OFF)
	{
		// 用户给了个0,希望LED灭
		//writel(0x11111111, GPJ0CON);
		// 读改写三部曲
		writel((readl(GPJ0DAT) | (1<<5)), GPJ0DAT);
	}
	else
	{
		// 用户给的是非0,希望LED亮
		//writel(0x11111111, GPJ0CON);
		writel((readl(GPJ0DAT) & ~(1<<5)), GPJ0DAT);
	}
}


static int __init s5pv210_led_init(void)
{
	// 用户insmod安装驱动模块时会调用该函数
	// 该函数的主要任务就是去使用led驱动框架提供的设备注册函数来注册一个设备
	int ret = -1;
	
	// led1
	mydev1.name = "led1";
	mydev1.brightness = 255;	
	mydev1.brightness_set = s5pv210_led1_set;
	
	ret = led_classdev_register(NULL, &mydev1);
	if (ret < 0) {
		printk(KERN_ERR "led_classdev_register failed\n");
		return ret;
	}
	
	// led2
	mydev2.name = "led2";
	mydev2.brightness = 255;	
	mydev2.brightness_set = s5pv210_led2_set;
	
	ret = led_classdev_register(NULL, &mydev2);
	if (ret < 0) {
		printk(KERN_ERR "led_classdev_register failed\n");
		return ret;
	}
	
	// led3
	mydev3.name = "led3";
	mydev3.brightness = 255;	
	mydev3.brightness_set = s5pv210_led3_set;
	
	ret = led_classdev_register(NULL, &mydev3);
	if (ret < 0) {
		printk(KERN_ERR "led_classdev_register failed\n");
		return ret;
	}
	
	return 0;
}

static void __exit s5pv210_led_exit(void)
{
	led_classdev_unregister(&mydev1);
	led_classdev_unregister(&mydev2);
	led_classdev_unregister(&mydev3);
}

module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");							// 描述模块的许可证
MODULE_AUTHOR("aston <1264671872@qq.com>");		// 描述模块的作者
MODULE_DESCRIPTION("s5pv210 led driver");		// 描述模块的介绍信息
MODULE_ALIAS("s5pv210_led");					// 描述模块的别名信息

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式_笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值