有注释的LED驱动

裸机下控制LED灯非常方便,只需要配置好GPIO引脚功能,然后向GPIO引脚映射的内存地址处写入数据即可,但linux下驱动就不那么简单了,需要结合字符设备驱动的架构,然后将功能实现添加进去,笔者参考linux设备驱动程序(第三版)中介绍的新的接口来实现驱动。友善之臂官网提供的源码是基于miscdevice的驱动,而且接口似乎有点老,比如在linux设备驱动程序(第三版)中强调需要使用新的内存I/O接口来访问映射内存,建议使用ioread32,iowrite32等,但是它依然使用writel,readl等函数。另外笔者使用了两种方法:一种是修改友善之臂的驱动;另一种是使用标准的字符设备驱动

第一种方法实现:

混杂设备驱动中需要实现的是填充file_operations结构,然后设置miscdevices的各个字段值,然后注册到内核即可。详细流程如下:

1.编写ioctl接口函数

2.填充file_operations结构,主要是将ioctl函数注册进去

3.填充miscdevice结构,主要是次设备号、fops和设备名称。

4.编写要提供给module_init宏使用的初始化代码,设置LED端口为输出,注册到内核

5.编写要提供给module_exit宏使用的注销代码,主要是注销掉初始化中注册到内核的miscdevice设备

6.本源码修改了arg参数的值,0表示LED1~LED4全选中,1~4分别控制对应的LED灯

详细代码如下:

/*
 * Tiny6410开发板-led驱动(linux)使用miscdevice实现
 * 使用方法:
 * 1.编译内核2.6.38
 * 2.编写Makefile文件
 * 3.编译运行
 * 4.运行app/led测试(有关具体的测试细节清参考led_app.c)
 * 本程序中读写GPIO使用了新的访问I/O内存的函数ioread32,iowrite32
 * Author:jefby
 * Emai:[email protected]
 */ 
#include
<linux/module.h>//MODULE_AUTHOR,MODULE_LICENSE
#include
<linux/init.h>//module_init,module_exit
#include
<linux/fs.h>//file_operations
#include
<linux/miscdevice.h>//misdevice 
#include
<asm/io.h>//ioread32,iowrite32
#include
<mach/gpio-bank-k.h>//定义了GPKCON
#include
<mach/regs-gpio.h>//定义了gpio-bank-k中使用的S3C64XX_GPK_BASE
#include
<mach/map.h>//定义了S3C64XX_VA_GPIO

/*设备名称*/
#define DEVICE_NAME "leds"

/* ioctl接口函数,cmd=0,表示关闭参数arg指定的LED灯;arg的值不能大于4;
 * 其中0表示所有的LED,LED1~LED4
 * 1~4分别表示LED1~LED4
 * 返回0或者错误-EINVAL
 *
*/
static int s3c6410_leds_ioctl(
    struct
file*filp,
    unsigned int cmd,
    unsigned long
arg
        )
{
 switch(cmd){
 
unsigned tmp;
 case 0://close
 case 1://open
  if(arg
> 4){//参数错误
   return -EINVAL;  
  }
 
else if(arg == 0){//全亮或者全灭
   tmp =
ioread32(S3C64XX_GPKDAT);//读出LED1~LED4所在寄存器的值
   tmp &=
~(0xF<<4);//打开LED1~LED4
   if(cmd ==
0)//若是关闭,则关闭掉LED1~LED4
    tmp |= (0xF<<4);//if close,then
write 0xF to GPK4~GPK7
   iowrite32(tmp,S3C64XX_GPKDAT);
 
}else{ //参数为1~4范围内
   tmp =
ioread32(S3C64XX_GPKDAT);
   tmp &=
~(1<<(4+arg-1));//清除arg所指示的那一位值
   tmp |=
((!cmd)<<(4+arg-1));//写入新值
 
 iowrite32(tmp,S3C64XX_GPKDAT);
  }
  return
0;
 default:
  return
-EINVAL;
 }//switch(cmd)
 return 0;//应该不会执行
}

static struct file_operations dev_fops = {
 .owner =
THIS_MODULE,
 .unlocked_ioctl =
s3c6410_leds_ioctl,//定义的ioctl函数
};
static struct miscdevice misc  =
{
 .minor = MISC_DYNAMIC_MINOR,//动态分配次设备号
 .name =
DEVICE_NAME,//设备名称
 .fops = &dev_fops,
};

/*模块初始化*/
static int __init dev_init(void)
{
 int
ret;
/*
 * 在设备驱动程序注册的时候初始化LED1~LED4所对应的GPIO管脚为输出
 * 并关闭LED1~LED4
 */ 
 unsigned
tmp;
 tmp = ioread32(S3C64XX_GPKCON);
 tmp = (tmp &
~(0xFFFFU<<16)) |
(0x1111U<<16);//先清除然后再设置其为输出
 iowrite32(tmp,S3C64XX_GPKCON);//写入GPKCON
 
 tmp
= readl(S3C64XX_GPKDAT);
 tmp |=
(0xF<<4);//关闭LED灯
 iowrite32(tmp,S3C64XX_GPKDAT);
//注册misc
 ret
= misc_register(&misc);
 printk(DEVICE_NAME"\tinitialized.\n");

return ret;
}

static void __exit
dev_exit(void)
{
//卸载
 misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("jefby");

第2种方法使用了标准的字符设备驱动的编写方法,具体流程如下:

1.编写在file_operations中使用的ioctl和open函数,其中open函数初始化GPIO接口,ioctl函数实现LED灯的亮灭

2.填充file_operations结构

3.编写module_init宏使用的初始化代码;在这段代码中主要完成

a.申请主设备号,默认使用动态分配的方法

b.动态分配cdev结构,cdev表示一个字符设备

c.初始化cdev结构

d.注册到内核

4.编写module_exit宏使用的注销代码,主要是注销cdev结构和动态申请的主设备号

部分代码如下:

static int __init leds_init(void)
{
 /*
 
*使用linux设备驱动中介绍的新方法来写,而不是用老的接口
  *申请主设备号
 
*register_chrdev
  *新方法:
 
* 0.获得一个或者多个设备编号(register_chrdev_region,或者alloc_chrdev_region)
 
* 1.分配cdev结构
  * 2.初始化该cdev结构
 
* 3.注册到内核
  *
  */
 int result;

printk("Tiny 6410 leds module
init.\n");
 if(leds_major){
  dev =
MKDEV(leds_major,0);
  result =
register_chrdev_region(dev,1,"leds");
 }else{
  result =
alloc_chrdev_region(&dev,0,1,"leds");
  leds_major =
MAJOR(dev);//获得主设备号
  printk(KERN_ALERT "leds major =
%d.\n",leds_major);
 }
 if(result < 0){
 
printk(KERN_WARNING "leds:can‘t get major %d\n",leds_major);
  return
result;
 }
 leds_cdev  =
cdev_alloc();
 leds_cdev->ops = &leds_fops;
 //void
cdev_init(struct cdev*cdev,struct file_operations
*fops)
 cdev_init(leds_cdev,&leds_fops);
 //int
cdev_add(struct cdev *dev,dev_t num,unsigned int
count)
 cdev_add(leds_cdev,dev,1);
 printk("cdev add
ok.\n");

return 0;
}

int leds_ioctl(
  struct file * filp,
  unsigned int
cmd,
  unsigned long arg
     
 )
{
 if(arg > 4)//确定参数必须为0,此时ON和OFF都对应的是LED1~LED4
 
return -EINVAL;
 switch(cmd){
  unsigned long tmp;
 
case 0:
  case 1:
   if(arg == 0){
    tmp =
ioread32(S3C64XX_GPKDAT);
    tmp &= ~(0xF<<4);
 
  if(cmd == 0)
     tmp |= 0xF<<4;
   
iowrite32(tmp,S3C64XX_GPKDAT);
     
 
 }else{
    tmp = ioread32(S3C64XX_GPKDAT);
   
tmp &= ~(0x1<<(4+arg-1));//打开arg对应的LED灯
    if(cmd ==
0)
     tmp |= 0x1<<(4+arg-1);
   
iowrite32(tmp,S3C64XX_GPKDAT);
   }
   return
0;
  default:
   return -EINVAL;
 }
 return
0;
}

编写Makefile如下:

ifneq ($(KERNELRELEASE),)
 obj-m := leds.o
else
 KERNELDIR
?= /opt/FriendlyARM/mini6410/linux/linux-2.6.38
 PWD := $(shell
pwd)
default:
 $(MAKE) -C $(KERNELDIR) M=$(PWD)
modules
clean:
 rm -rf *.ko *.o *.order *~ *symvers
*.mod.c
endif

完成后,接着编写应用程序测试它:

/*
 *
 * 控制LED的应用程序,格式 ./led arg
cmd
 * arg=[0~4],cmd=[0.1]
 * cmd=0表示关闭LED,cmd=1表示打开LED
 * arg=0,全关或者全开LED灯
 * arg=1~4表示打开或关闭指定的LED灯 
 * Author:jefby
 * Email:[email protected]
 *
 **/
#include
<stdio.h>
#include <stdlib.h>
#include
<unistd.h>
#include <sys/ioctl.h>
int main(int argc, char
**argv)
{
 int on;
 int led_no;
 int fd;
 /*
检查led 控制的两个参数,如果没有参数输入则退出。*/
 if (argc != 3 || sscanf(argv[1], "%d",
&led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 ||\
 on < 0
|| on > 1 || led_no < 0 || led_no > 4) {
  fprintf(stderr,
"Usage: leds led_no 0|1\n");
 
exit(1);
 }
 /*打开/dev/leds 设备文件*/
 fd =
open("/dev/leds0", 0);
 if (fd < 0) {
 fd = open("/dev/leds",
0);
 }
 if (fd < 0) {
 perror("open device
leds");
 exit(1);
 }
 /*通过系统调用ioctl
和输入的参数控制led*/
 ioctl(fd, on,
led_no);
 /*关闭设备句柄*/
 close(fd);
 return 0;
}

编译,然后使用NFS挂载到开发板上,加载模块然后运行应用程序即可,注意标准字符设备驱动程序还需要创建设备,如下图所示:

运行led程序可以看到LED灯已经可以成功驱动了!

有注释的LED驱动,布布扣,bubuko.com

时间: 2024-11-05 11:34:18

有注释的LED驱动的相关文章

第7章:LED驱动的实现原理

本章完成了一个真正意义上的 Linux 驱动.该 Linux 驱动用来控 制开发版上的 4个 LED 小灯.也就是说通过向 Linux 驱动发送数据可以控制 LED 小灯的开关.为 了方便称呼这个驱动,本书及后面的章节都将其称为 LED 驱动. 虽然 LED 驱动并不复杂,只是控制 了 4个 LED,"但 LED 驱动已经包括了 Linux 驱动所有必要的部分 一个完整的 Linux 驱动主要由 内部处理和与硬件交互降部分组成.其中内部处理主要是指 Linux 驱动的装载.卸载.与设备文件 相关

基于S3C2440的linux-3.6.6移植——LED驱动【转】

本文转载自:http://www.voidcn.com/blog/lqxandroid2012/article/p-625005.html 目前的linux版本的许多驱动都是基于设备模型,LED也不例外. 简单地说,设备模型就是系统认为所有的设备都是挂接在总线上的,而要使设备工作,就需要相应的驱动.设备模型会产生一个虚拟的文件系统——sysfs,它给用户提供了一个从用户空间去访问内核设备的方法,它在linux里的路径是/sys.如果要写程序访问sysfs,可以像读写普通文件一样来操作/sys目录

编写LED驱动

1.创建LED驱动的设备文件: (1)使用cdev-init函数初始化cdev (2)指定设备号 设备号的分配有两种指定方法:直接在代码中指定(硬编码) 动态分配 (3)使用cdev-add函数将字符设备添加到内核中的字符设备数组中 (4)使用class-creat宏创建struct class (5)使用device-create函数创建设备文件 2.LED驱动通过两种方式控制LED 通过字符串控制LED 通过I/O命令LED 3.一个完整的linux驱动主要由内部处理和与硬件交互两部分组成.

linux驱动(九)platform驱动模型详解,以及基于platform驱动模型的led驱动

参考: http://blog.csdn.net/qq_28992301/article/details/52385518 http://blog.csdn.net/zoe6553/article/details/6372445 http://blog.chinaunix.net/uid-25014876-id-111745.html 1:什么是platform总线?platform总线是区别于实体总线USB. I2C.SPI .PIC总线的虚拟总线,一些usb设备选址的话需要通过USB总线来进

兼容可控硅调光的一款LED驱动电路记录

1.该款电路为兼容可控硅调光的LED驱动电路,采用OB3332为开关控制IC,拓扑方案为Buck: 2.FB1:磁珠的单位是欧姆,而不是亨利,这一点要特别注意.因为磁珠的单位是按照它在某一频率 产生的阻抗来标称的,阻抗的单位也是欧姆.磁珠的 DATASHEET上一般会提供频率和阻抗的特性曲线图,一般以100MHz为标准,比如[email protected],意思就是在100MHz频率的时候磁珠的阻抗相当于600欧姆: 3.UFM14PL-TP普通二极管: Maximum Recurrent P

(笔记)linux设备驱动--LED驱动

linux设备驱动--LED驱动 最近正在学习设备驱动开发,因此打算写一个系列博客,即是对自己学习的一个总结,也是对自己的一个督促,有不对,不足,需要改正的地方还望大家指出,而且希望结识志同道合的朋友一起学习技术,共同进步. 作者:liufei_learning(转载请注明出处) email:[email protected] IT学习交流群:160855096 转至:http://blog.csdn.net/liufei_learning/article/details/7025246 开发环

FL2440驱动添加(4)LED 驱动添加

硬件信息:FL2440板子,s3c2440CPU带四个LED,分别在链接GPB5,GPB6,GPB8,GPB10 内核版本:linux-3.8.0 led驱动代码如下: 值得注意地方地方: 1,定时器的使用:在include/linux/timer.h下定义struct timer_list struct timer_list { /* * All fields that change during normal runtime grouped to the * same cacheline *

驱动学习之LED驱动框架

一:什么是驱动框架  (1)内核中驱动部分维护者针对每个种类的驱动设计一套成熟的.标准的.典型的驱动实现,然后把不同厂家的同类硬件驱动中相同的部分抽出来自己实现好,再把不同部分留出接口给具体的驱动开发工程师来实现,这就叫驱动框架.  (2)内核维护者在内核中设计了一些统一管控系统资源的体系,这些体系让内核能够对资源在各个驱动之间的使用统一协调和分配,保证整个内核的稳定健康运行.譬如系统中所有的GPIO就属于系统资源,每个驱动模块如果要使用某个GPIO就要先调用特殊的接口先申请,申请到后使用,使用

嵌入式Linux学习笔记之LED驱动

最近在学习嵌入式Linux驱动开发,大致了解了驱动的基本开发流程,本文主要针对字符设备驱动开发做一个简要介绍,也当作是对这几天工作的一个小小总结. 计算机系统是由软硬件相互协调共同完成工作的,作为专用计算机系统的嵌入式系统也不例外,既要有CPU.SDRAM.FLASH.IO等硬件,同时也少不了操作系统和应用软件等软件的支持,而作为应用程序与硬件的桥梁--驱动程序,是整个嵌入式系统开发过程中的关键环节.驱动开发涉及底层,而了解底层作用机制对于整个系统的开发意义重大. Linux内核中有60%以上是