一、总体架构

  

二、硬件电路

  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. 解决使用jedis连接是报DENIED Redis is running in protected mode错误

    DENIED Redis is running in protected mode because protected mode is enabled, no bind address was spe ...

  2. 使用 MongoDB 存储日志数据

    使用 MongoDB 存储日志数据     线上运行的服务会产生大量的运行及访问日志,日志里会包含一些错误.警告.及用户行为等信息.通常服务会以文本的形式记录日志信息,这样可读性强,方便于日常定位问题 ...

  3. LOJ#2541 猎人杀

    解:step1:猎人死了之后不下台,而是继续开枪,这样分母不变...... 然后容斥,枚举猎人集合s,钦定他们在1之后死.定义打到1的时候结束,枚举游戏在i轮时结束. 发现式子是一个1 + x + x ...

  4. Django 路由

    创建好项目后在项目文件下的urls.py为设置路由 Django 有两种路由方式一种的精确路由 另一个为模糊路由 """mysite URL Configuration ...

  5. 【洛谷P1164 小A点菜】

    题目背景 uim神犇拿到了uoi的ra(镭牌)后,立刻拉着基友小A到了一家……餐馆,很低端的那种. uim指着墙上的价目表(太低级了没有菜单),说:“随便点”. 题目描述 不过uim由于买了一些辅(e ...

  6. c++ sort

    老是搞混 return bool eg. bool cmp(node a,node b) { if (a.score==b.score) ; else return a.score>b.scor ...

  7. SpringBoot文件上传

    先建工程 只勾选web和freemarker模板 最后 先看一下最终目录结构 先修改pom文件,加入common-io依赖 然后修改Application.yml文件 spring: freemark ...

  8. PostCSS理解与运用

    1.PostCSS是什么 它可以被理解为一个平台,可以让一些插件在上面跑 它提供了一个解析器,可以将CSS解析成抽象语法树 通过PostCSS这个平台,我们能够开发一些插件,来处理CSS.热门插件如a ...

  9. python测试断言

    这几天看了一下python的测试,基于函数方法和基于类的测试,主要使用的模块是unittest模块,为特定的方法和类,建立测试模块,测试函数功能是否满足预期.下面是模仿书里面的一个例子. City.p ...

  10. (字符串 枚举)The Hardest Problem Ever hdu1048

    The Hardest Problem Ever 链接:http://acm.hdu.edu.cn/showproblem.php?pid=1048 Time Limit: 2000/1000 MS ...