在上一节中已经将驱动程序框架搭建好了

接下来开始写硬件的操作(控制LED):

(1)看原理图,确定引脚

(2)看2440手册

(3)写代码(需要使用ioremap()函数映射虚拟地址,在linux中只能使用虚拟地址)

(4)修改上一节的测试程序

(5)使用次设备号来控制设备下不同的灯

1.看led引脚

最终确定: LED1 ->GPF4  LED2 ->GPF5   LED3 ->GPF6

2.看2440手册

配置GPFCON[15:0](0x56000050)的位[8:9]、位[10:11]、位[12:13] 都等于0x01(输出模式)

控制GPFDAT[7:0](0x56000054)中的位4~6来使灯亮灭(低电平亮)

3.写代码

3.1添加全局变量:

volatile unsigned long *GPFcon=NULL;       

volatile unsigned long *GPFdat=NULL;

3.2 first_drv_init入口函数中使用ioremap()映射虚拟地址:

GPFcon = ioremap(0x56000050, );   //ioremap:物理地址映射,返回虚拟地址

GPFdat=GPFcon+;             //long:32位,所以GPFdat=0x56000050+(32/8)

3.3 first_drv_exit出口函数中注销虚拟地址:

iounmap(GPFcon);          //注销虚拟地址

3.4 first_drv_open函数中添加配置GPFCON:

*GPFcon&=~ ((0X11<<)| (0X11<<)| (0X11<<)); 

*GPFcon|=    ((0X01<<)| (0X01<<)| (0X01<<)); 

3.5 first_drv_write函数中添加拷贝应用层数据,然后来控制GPFDAT:

/*copy_to_user():将数据上给用户*/
copy_from_user(&val,buf,count); //从用户(应用层)拷贝数据
if(val==) //点灯(低电平亮)
{
*GPFdat&=~((0X1<<)| (0X1<<)| (0X1<<));
}
else //灭灯
{
*GPFdat|=((0X1<<)| (0X1<<)| (0X1<<));
}

4.修改测试程序main()

代码如下:

int main(int argc,char **argv) //argc:参数个数,argv数组
{
int fd1, fd2;
int val=;
fd1 = open("/dev/xyz",O_RDWR); //打开/dev/xxx设备节点
if(fd1<) //无法打开,返回-1
printf("can't open%d!\n", fd1);
if(argc!=)
{
printf("Usage:\n");
printf("%s <on|off>",argv[]);
return ;
} if(strcmp(argv[],"on")==) //开灯
{
printf("led on...\n");
val=;
}
else //关灯
{
printf("led off...\n");
val=;
} write(fd1, &val, );
return ;
}

当输入first_driver_text on点3个灯, 否则关3个灯

若参数不等于2时,不能控制点灯

如果我们想分别控制不同的灯,该怎么做?

可以使用此设备号,此设备号就是用来区分同一设备下不同子设备

5使用次设备号来控制设备下不同的灯

我们先来看下面两个函数MAJOR和MINOR,分别是提取主次设备号

minor=MINOR(inode->i_rdev);    //open函数中提取次设备号
major=MAJOR(inode->i_rdev); //open函数中提取主设备号 minor=MINOR (file->f_dentry->d_inode->i_rdev); //write/read函数中提取次设备号
major= MAJOR (file->f_dentry->d_inode->i_rdev); //write/read函数中提取主设备号

思路如下:

在测试程序中:

通过dev[1]来open打开不同的子设备节点,然后通过dev[2]来write写入数据

实例: first_driver_text led1 on        //点亮led1

在first_dev.c驱动文件中:

first_drv_init函数中创建不同的子设备节点

first_drv_exti函数中注销不同的子设备节点

first_drv_open函数中通过MINOR(inode->i_rdev)来初始化不同的灯

first_drv_write函数中通过MINOR(file->f_dentry->d_inode->i_rdev)来控制不同的灯

如下图,insmod后自动注册3个设备节点

测试程序如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h> /*
* ledtest <dev> <on|off>
*/ void print_usage(char *file) //报错打印帮助
{
printf("Usage:\n");
printf("%s <dev> <on|off>\n",file);
printf("eg. \n");
printf("%s /dev/leds on\n", file);
printf("%s /dev/leds off\n", file);
printf("%s /dev/led1 on\n", file);
printf("%s /dev/led1 off\n", file);
} int main(int argc, char **argv)
{
int fd;
char* filename;
char val;

if (argc != )
{
print_usage(argv[]);
return ;
} filename = argv[];
fd = open(filename, O_RDWR);
if (fd < )
{
printf("error, can't open %s\n", filename);
return ;
} if (!strcmp("on", argv[]))
{
// 亮灯
val = ;
write(fd, &val, );
} else if (!strcmp("off", argv[]))
{
// 灭灯
val = ;
write(fd, &val, );
}
else //数据输入错误,打印帮助提示
{
print_usage(argv[]);
return ;
}
return ;
}

驱动程序如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
#include <asm/io.h> static struct class *firstdrv_class; //创建一个class类
static struct class_device *firstdrv_class_devs[]; //创建类的设备,led,led1,led2,led3 volatile unsigned long *GPFcon=NULL;
volatile unsigned long *GPFdat=NULL; /*1写出驱动程序first_drv_open first_drv_write */
static int first_drv_open(struct inode *inode, struct file *file)
{
int minor=MINOR(inode->i_rdev);
printk("first_drv_open\n"); //打印,在内核中打印只能用printk()
GPFcon = ioremap(0x56000050, ); //ioremap:物理地址映射,返回虚拟地址
GPFdat=GPFcon+; //long:32位,所以GPFdat=0x56000050+(32/8)

switch(minor)
{
case : //进入led设备,控制所有led
*GPFcon&=~ ((0X3<<)| (0X3<<)| (0X3<<));
*GPFcon|= ((0X01<<)| (0X01<<)| (0X01<<));
break; case : //进入led1设备,控制 led1
*GPFcon&=~ ((0X3<<) );
*GPFcon|= (0X1<<) ;
break; case : //进入led2设备,控制 led2
*GPFcon&=~ ((0X3<<) );
*GPFcon|= (0X1<<) ;
break; case : //进入led3设备,控制 led3
*GPFcon&=~ ((0X3<<) );
*GPFcon|= ((0X1<<) );
break;
}
return ; } /*参数filp为目标文件结构体指针,buffer为要写入文件的信息缓冲区,count为要写入信息的长度,ppos为当前的偏移位置,这个值通常是用来判断写文件是否越界*/
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
int minor=MINOR(file->f_dentry->d_inode->i_rdev);
copy_from_user(&val,buf,count); //通过用户(应用层)拷贝数据
switch(minor)
{
case : //进入led设备,控制所有led
printk("led0,%d\n",val); if(val) //开灯
{*GPFdat&=~ ((0X1<<)| (0X1<<)| (0X1<<));
*GPFdat|= ((0X0<<)| (0X0<<)| (0X0<<)); }
else //关灯
{*GPFdat&=~ ((0X1<<)| (0X1<<)| (0X1<<));
*GPFdat|= ((0X1<<)| (0X1<<)| (0X1<<)); }
break; case : //进入led1设备,控制 led1
printk("led1,%d\n",val);
if(val) //开灯
{*GPFdat&=~ (0X1<<);
*GPFdat|= (0X0<<); }
else //关灯
{ *GPFdat&=~ (0X1<<);
*GPFdat|= (0X1<<); }
break; case : //进入led2设备,控制 led2
printk("led2,%d\n",val);
if(val) //开灯
{*GPFdat&=~ (0X1<<);
*GPFdat|= (0X0<<); }
else //关灯
{*GPFdat&=~ (0X1<<);
*GPFdat|= (0X1<<); }
break; case : //进入led3设备,控制 led3
printk("led3,%d\n",val);
if(val) //开灯
{*GPFdat&=~ (0X1<<);
*GPFdat|= ( 0X0<<); }
else //关灯
{*GPFdat&=~ (0X1<<);
*GPFdat|= (0X1<<); }
break;
}
return ;
} /*2定义file_operations结构体来封装驱动函数first_drv_open first_drv_write */
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, //被使用时阻止模块被卸载
.open = first_drv_open,
.write = first_drv_write,
}; int major; //定义一个全局变量,用来保存主设备号
int first_drv_init(void)
{
int i;
/*3 register_chrdev注册字符设备*/
/*如果设置major为0,表示由内核动态分配主设备号,函数的返回值是主设备号*/
major=register_chrdev (, "first_drv", &first_drv_fops);

firstdrv_class= class_create(THIS_MODULE,"firstdrv");
//创建类,它会在sys目录下创建firstdrv这个类 firstdrv_class_devs[]=class_device_create(firstdrv_class,NULL,MKDEV(major,),NULL,"led");
//创建类设备,它会在firstdrv_class类下创建led设备,然后mdev通过这个自动创建/dev/xyz这个设备节点 for(i=;i<;i++) //创建led1 led2 led3 设备节点,控制led1 led2 led3
{
firstdrv_class_devs[i]=class_device_create(firstdrv_class,NULL,MKDEV(major,i),NULL,"led%d",i);
}
return ;
} /*6 写first_drv_exit出口函数*/
void first_drv_exit(void)
{
int i;
unregister_chrdev (major, "first_drv"); //卸载驱动,只需要主设备号和设备名就行
class_destroy(firstdrv_class); //注销类,与class_create对应 for(i=;i<;i++) //注销类设备led,led1,led2,led3
class_device_unregister(firstdrv_class_devs[i]);

iounmap(GPFcon); //注销虚拟地址
} /*5 module_init修饰入口函数*/
module_init(first_drv_init); /*7 module_exit修饰出口函数*/
module_exit(first_drv_exit); MODULE_LICENSE("GPL v2"); //声明许可证

下章学习:

4.查询方式来写按键驱动程序(详解)

3.修改第一个程序来点亮LED的更多相关文章

  1. 第8课.第一个ARM裸板程序(点亮led)及申引

    1.原理图 2.芯片手册 3.几条汇编代码 1.ldr:读内存 ldr R0, [R1] 假设R1的值是x,读取地址x上的数据(4字节),保存到R0中 ldr R0, =0x12345678 (4字节 ...

  2. Mini2440上的第一个程序——点亮Led

    手头的Mini2440搁置了两年半之后,我再次决定拿出它,重新尝试嵌入式Linux的学习. 我使用的是友善之臂的Mini2440开发板.韦东山的<嵌入式Linux应用开发完成手册>及其视频 ...

  3. [TPYBoard-Micropython教程之1] 运行第一个脚本——点亮LED

    转载请注明:@小五义http://www.cnblogs.com/xiaowuyiQQ群:64770604 会python就能做硬件! 一.TPYBoard V102开发板 TPYBoard V102 ...

  4. java学习 之 第一个程序及认识

    以前也看过一系列的java方面的程序,但是还没有正式敲过,今天正式学习并且正式敲出代码.在这里记录下来今日所得 写作工具:Notepad++ 在写作工具方面好多人建议用 记事本,但是我还是认为用 No ...

  5. [TPYBoard - Micropython之会python就能做硬件 1] 运行第一个脚本——点亮LED

    转载请注明:@小五义http://www.cnblogs.com/xiaowuyiQQ群:64770604 会python就能做硬件! 一.TPYBoard V102开发板 TPYBoard V102 ...

  6. 嵌入式学习笔记(综合提高篇 第一章) -- 利用串口点亮/关闭LED灯

    1      前言 从踏入嵌入式行业到现在已经过去了4年多,参与开发过的产品不少,有交换机.光端机以及光纤收发器,停车场出入缴费系统,二维码扫码枪,智能指纹锁以及数字IC芯片开发等; 涉及产品中中既有 ...

  7. rails再体验(第一个程序)

    掌握redmine plugin开发的目标在2016年未实现,2017年继续. 选择<Ruby on Rails Tutorial>教程,windows安装railsinstaller,该 ...

  8. STM32F407第一步之点亮LED

    STM32F407第一步之点亮LED. 要点亮LED,首先了解一下F4的GPIO模块.首先看一下STM32F4数据手册,GPIO模块的内部结构图 看上去有点复杂,不要怕,慢慢理解就可以了.对外引脚那里 ...

  9. OpenGL学习笔记1——第一个程序

    学习的参考书基本是按照GL编程指南,如果有消息机制概念,对于GLUT的理解是很自然的.下面就按照自己写的第一个程序详细解释一下GL,还是比较容易上手的. 程序实现的功能是,根据当前随即种子摇出来的结果 ...

随机推荐

  1. [补档][HNOI 2008]GT考试

    [HNOI 2008]GT考试 题目 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字. 他的不吉利数学A1A2... ...

  2. jquery.cityselect.js基于jQuery+JSON的省市或自定义联动效果

    一.插件介绍 最早做省市联动的时候都特别麻烦,后来在helloweba的一篇文章中看到这个插件,很不错的,后来就一直用了. 省市区联动下拉效果在WEB中应用非常广泛,尤其在一些会员信息系统.电商网站最 ...

  3. 神经网络中的BP神经网络和贝叶斯

    1 贝叶斯网络在地学中的应用 1 1.1基本原理及发展过程 1 1.2 具体的研究与应用 4 2 BP神经网络在地学中的应用 6 2.1BP神经网络简介 6 2.2基本原理 7 2.3 在地学中的具体 ...

  4. java中链接数据库的具体操作以及pstmt.setObject(i+1, objects[i])这行代码的意思

    package dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStat ...

  5. Luogu P2807 三角形计数

    题目背景 三角形计数(triangle) 递推 题目描述 把大三角形的每条边n等分,将对应的等分点连接起来(连接线分别平行于三条边),这样一共会有多少三角形呢?编程来解决这个问题. 输入输出格式 输入 ...

  6. 资金平台交易明细扩展开发-DEP

    前言 支持农垦项目中,需要增加抓取银行接口传输数据,需求有两点:一是银企平台前置机需要增加扩展中行.农行数据字段.(代理账号.代理账户名称.代理银行) 二是EAS资金模块中的交易明细查询列表界面能够显 ...

  7. JavaScript闭包,只学这篇就够了

    # 闭包不是魔法 这篇文章使用一些简单的代码例子来解释JavaScript闭包的概念,即使新手也可以轻松参透闭包的含义. 其实只要理解了核心概念,闭包并不是那么的难于理解.但是,网上充斥了太多学术性的 ...

  8. 创建 macvlan 网络 - 每天5分钟玩转 Docker 容器技术(55)

    上一节我们准备好了 macvlan 的实验环境,今天在 host1 和 host2 中创建 macvlan 网络 mac_net1: 注意:在 host2 中也要执行相同的命令. ① -d macvl ...

  9. DIV+CSS架构网站的7种版面布局形式

    "T"结构布局形式.所谓"T"结构,就是指页面顶部为横条网站标志+广告条,下方左面为主菜单,右面显示内容的布局,整体效果类似英文字母"T", ...

  10. Do not request Window.FEATURE_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead

    在某个activity中使用toolbar的使用,必须是在相应的application主题中也设置 <item name="windowNoTitle">true< ...