一、总体架构

  

二、硬件电路

  1.硬件原理图

  

  

  2.寄存器配置

  

三、代码编写

  写linux的设备驱动操作的是系统的虚拟地址,并不是像裸机程序一样操作的是物理地址。

  物理地址要映射成虚拟地址,就要用到ioremap函数,用来把物理地址映射成虚拟地址。

  3.1 确定主设备号

    执行命令 cat proc/devices 可以查看到已经用了的主设备号,给自己的设备定义设备号的时候,可以选用没有被设备使用的主设备号。或者可以写0,让系统自动给我们的设备分配主设备号。

  3.2 设备节点的创建方法

    a. 手动创建

      

    b. 自动创建

      应用程序里面应用udev机制

      mdev机制:mdev会根据系统的信息,sys目录下有很多系统的信息。注册一个驱动程序的时候,会在此目录下生成设备的信息,mdev可以根据设备的信息自动创建设备节点。

      

      

  3.3 源码

 /*
* =====================================================================================
* Filename: led.c
* Description:
* Version: 1.0
* Created: 2017年05月22日 11时40分23秒
* Author: YOUR NAME (),
* Organization:
* =====================================================================================
*/ #include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/class.h>
#include <linux/io.h> /* 1.建立模块
* 1.1 模块初始化模板
* 1.1.1 映射寄存器
* 1.1.2 注册设备驱动,告诉内核
* 1.1.1.1 主设备号
* 1.1.3 设备类的建立
* 1.1.1.2.1 创建变量:设备类和设备
* 1.1.1.2.2 创建设备类
* 1.1.1.2.3 在设备类下创建设备
* 1.2 模块退出模板
*
* 2.建立文件操作
* 2.1 文件打开和关闭函数
* 2.1.1 文件打开函数
* 2.2 文件读写函数
* 2.2.1 文件读函数
* 2.2.2 文件写函数
* 2.2.2.1 设置GPIO引脚输出的数据
* 2.2.2.1.1 从用户空间读取数据
* 2.3 创建文件操作结构体
*/ #define DEVICE_NAME "leds" /* 加载模式后,执行"cat /proc/devices"命令看到设备的名称 */
#define LED_MAJOR 231 /* 主设备号 */
static unsigned long gpio_va; //次设备号 /* 1.1.1.2.1 创建变量:设备类和设备 */
static struct class *leds_class; //创建led类
static struct class_device *leds_class_devs[];//在led类下面创建设备 static char leds_status = 0x0;
static DECLARE_MUTEX(leds_lock); //定义赋值 //volatile unsigned long *gpfcon = NULL;
//volatile unsigned long *gpfdat = NULL;
#define GPIO_OFT(x) ((x) - 0x56000000)
#define GPFCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000050)))
#define GPFDAT (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000054))) /* 2.1.1 文件打开函数 */
static int led_open(struct inode *inode, struct file *filp)
{
int minor = MINOR(inode->irdev);//获取次设备号 switch(minor)
{
case :
GPFCON &= ~((0x3 << ( * )) | (0x3 << ( * )) | (0x3 << ( * )));
GPFCON |= ((0x1 << ( * )) | (0x1 << ( * )) | (0x1 << ( * ))); //初始化状态:LED灯全亮,gpf引脚输出0
GPFDAT &= ~((<<) | (<<) | (<<)); down(&leds_lock);
leds_status = 0x0;
up(&leds_lock);
break;
case :
GPFCON &= ~(0x3 <<( * ));
GPFCON |= ( << ( * )); //初始化状态:LED4亮,gpf4引脚输出0
GPFDAT &= ~( << ); down(&leds_lock);
leds_status &= ~( << );
up(&leds_lock);
break;
case :
GPFCON &= ~(0x3 << ( * ));
GPFCON |= ( << ( * )); GPFDAT &= ~( << ); down(&leds_lock);
leds_status &= ~( << );
up(&leds_lock);
break;
case :
GPFCON &= ~(0x3 << ( * ));
GPFCON |= ( << ( * )); GPFDAT &= ~( << ); down(&leds_lock);
leds_status &= ~( << );
up(&leds_lock);
break;
} return ;
} /* 2.2.1 文件读函数 */
static ssize_t led_read(struct file *filp, char __user *buff, ssize_t count, loff_t *oops)
{
int minor = MINOR(filp->f_dentry->d_inode->i_rdev);//获取次设备号
char val; switch(minor)
{
case :
copy_to_user(buff, (const void *)&leds_status, );
break;
case :
down(&leds_lock);
val = leds_status & 0x1;
up(&leds_lock);
copy_to_user(buff, (const void *)&val, );
break;
case :
down(&leds_lock);
val = (leds_status>>) & 0x1;
up(&leds_lock);
copy_to_user(buff, (const void *)&val, );
break;
case :
down(&leds_lock);
val = (leds_status>>) & 0x1;
up(&leds_lock);
copy_to_user(buff, (const void *)&val, );
break;
}
return ;
} /* 2.2.2 文件写函数 */
static ssize_t led_wirte(struct file *filp, const char __user *buff, ssize_t count, loff_t *oops)
{
int minor = MINOR(file->fdentry->d_inode->i_rdev);
char val; /* 2.2.2.1 设置GPIO引脚输出 */
/* 2.2.2.1.1 从用户空间读取设置的数据 */
copy_from_user(&val, buf, conut); switch(minor)
{
case :
if ( == val) //亮灯
GPFDAT &= ~(( << ) | ( << ) | ( << ));
else//灭灯
GPFDAT |= ( << ) | ( << ) | ( << );
down(&leds_lock);
leds_status = val;
up(&leds_lock);
break;
case :
if (val == )
{
GPFDAT &= ~(<<);
down(&leds_lock);
leds_status &= ~(<<);
up(&leds_lock);
}
else
{
GPFDAT |= ( << );
down(&leds_lock);
leds_status |= (<<);
up(&leds_lock);
}
break;
case :
if (val == )
{
GPFDAT &= ~( << );
down(&leds_lock);
leds_status &= ~( << );
up(&leds_lock);
}
else
{
GPFDAT |= ( << );
down(&leds_lock);
leds_status |= ( << );
up(&leds_lock);
}
break;
case :
if(val == )
{
GPFDAT &= ~( << );
down(&leds_lock);
leds_status &= ~( << );
up(&leds_lock);
}
else
{
GPFDAT |= ( << );
down(&leds_lock);
leds_status |= ( << );
up(&leds_lock);
}
break;
} return ;
} struct file_operations led_drv_fops = {
.owner = THIS_MODULE;
.open = led_open;
.release = led_release;
.read = led_read;
.write = led_wirte;
} /* 1.1 模块初始化函数 */
static int __init led_init(void)
{
int ret;
int minor = ; /* 1.1.1 映射寄存器 */
gpio_va = ioremap(0x56000000, 0x100000);
if (!gpio_va) {
return -EIO;
} /* 1.1.2 注册字符设备,将主设备号与file_operations结构联系起来 */
/* LED_MAJOR可以设为0,表示由内核自动分配主设备号 */
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &led_drv_fops);
if (ret < ) {
printk(DEVICE_NAME " can't register major number!!!\n");
return ret;
} /* 1.1.3 设备类的创建 */
leds_class = class_create(THIS_MODULE, "leds");
if (IS_ERR(leds_class))
return PTR_ERR(leds_class); /* 1.1.4 在leds类下面创建一个设备 */
/* mdev 会自动创建一个/dev/leds 设备节点 */
leds_class_devs[] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, ), NULL, "leds");
if (unlikely(IS_ERR(leds_class_devs[])))
return PTR_ERR(leds_class_devs[]);
for(minor = ; minor < ; minor++)
{
leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, minor), NULL, "led%d", minor);
if (unlikely(IS_ERR(leds_class_devs[minor])))
return PTR_ERR(leds_class_devs[minor]);
} printk(DEVICE_NAME " initialized\n");
return ;
} /* 1.2 模块退出函数 */
static void __exit led_exit(void)
{
int minor;
for(minor = ; minor < ; minor++)
{
class_device_unregister(leds_class_devs[minor]);
}
class_destroy(leds_class);
unregister_chrdev(LED_MAJOR, DEVICE_NAME);//卸载
iounmap(gpfcon); return ;
} module_init(led_init);//定义一个结构体,结构体内有一个函数指针,指向led_init这个入口函数
module_exit(led_exit); MODULE_LICENSE("Dual BSD/GPL");

  

字符设备驱动(一)---led的更多相关文章

  1. 字符设备驱动之Led驱动学习记录

    一.概述 Linux内核就是由各种驱动组成的,内核源码中大约有85%的各种渠道程序的代码.一般来说,编写Linux设备驱动大致流程如下: 1.查看原理图,数据手册,了解设备的操作方法. 2.在内核中找 ...

  2. 【Linux 驱动】简单字符设备驱动架构(LED驱动)

    本文基于icool210开发板,内核版本:linux2.6.35: 驱动代码: (1)头文件:led.h #ifndef __LED_H__ #define __LED_H__ #define LED ...

  3. 字符设备驱动之LED驱动

    实现 ①编写驱动框架 ②编写硬件实现代码 (在Linux系统下操作硬件,需要操作虚拟地址,因此需要先把物理地址转换为虚拟地址 ioremap()) 如何实现单个灯的操作: 实现方法之一--操作次设备号 ...

  4. fl2440 platform总线led字符设备驱动

    首先需要知道的是,设备跟驱动是分开的.设备通过struct device来定义,也可以自己将结构体封装到自己定义的device结构体中: 例如:struct platform_device: 在inc ...

  5. 嵌入式Linux驱动学习之路(十)字符设备驱动-my_led

    首先贴上代码: 字符设备驱动代码: /** *file name: led.c */#include <linux/sched.h> #include <linux/signal.h ...

  6. 深入理解Linux字符设备驱动

    文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...

  7. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  8. 【转】linux设备驱动程序之简单字符设备驱动

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/03/2272869.html 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用 ...

  9. Linux字符设备驱动

    一.字符设备基础 字符设备 二.字符设备驱动与用户空间访问该设备的程序三者之间的关系 三.字符设备模型 1.Linux内核中,使用 struct cdev 来描述一个字符设备 动态申请(构造)cdev ...

  10. 字符设备驱动1:新的方式添加cdev + 在open函数中将文件私有数据指向设备结构体

    本例中,驱动入口处,使用cdev_add添加驱动,这点也可与字符设备驱动0:一个简单但完整的字符设备驱动程序对比一下. 另外主要讲xx_open实现文件私有数据指向设备结构体. 引子: 偶然看到,在j ...

随机推荐

  1. [bzoj1717][Milk Patterns 产奶的模式]

    题目链接 思路 先求出后缀数组,并且求出LCP.二分一下长度len.check的时候就是看有没有连续的k个后缀的LCP大于len.也就是判断是不是有连续的k-1个height大于len. 代码 #in ...

  2. IE jQuery ajax 请求缓存问题

    我最近在IE下测试开发我们的系统,经常出现改过的jsp页面,刷新IE后也不能显示,这就是IE的缓存问题,查了一下百度,说是IE9在ajax进行请求时,如果两次请求url相同,则不会请求服务器,而是从缓 ...

  3. python在数据处理中常用的模块之numpy

    一 numpy模块 NumPy系统是Python的一种开源的数值计算扩展.这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多(该 ...

  4. JVM调优工具

    JMap 首先要知道Java进程的pid. Windows: .. .. .. Linux: ps -ef | grep java 查看堆栈信息(jmap -heap pid) jmap -heap ...

  5. phpstudy vhosts.conf 文件配置 记录下!

    <VirtualHost _default_:80>DocumentRoot "D:\phpStudy\WWW" <Directory "D:\phpS ...

  6. CSS——nth-child()

    nth-child()选择器:CSS3新属性 用法:p:nth-child(2) 选择p标签的父元素 的第二个子元素,并且这个子元素必须是p才起作用 有点绕,有点无厘头,举个栗子: <!DOCT ...

  7. 多态(instanceof)

    多态调用的三种格式 * A:多态的定义格式: * 就是父类的引用变量指向子类对象 父类类型 变量名 = new 子类类型(); 变量名.方法名(); * B: 普通类多态定义的格式 父类 变量名 = ...

  8. 流明(lux)和坎德拉;

    流明是光照度:  坎德拉是光强: 流明是光通量的单位, cd是光强单位 光强是单位立体角的光通量: 照度是单位面积的光通量: 尼特是亮度单位   1尼特 = 1CD/m^2: 1 lx = 1 流明每 ...

  9. python OrderDict

    # encoding: utf-8 import csv import collections d = {'banana':3,'apple':4,'pear':1,'orange':2} print ...

  10. nginx中间件

    Nginx简介 Nginx是一个开源且高性能.可靠的HTTP中间件.代理服务.其特点是占有内存少,并发能力强. Nginx优势:IO多路复用epoll 1.什么是IO复用 它是内核提供的一种同时监控多 ...