mini2440驱动奇谭——LED驱动与测试(动态加载)

我的博客:http://blog.csdn.net/muyang_ren

实现功能:开发板动态加载led驱动模块并能通过测试程序

系统:Ubuntu 14.04

驱动交叉编译内核:linux-2.6.32.2               //建立交叉编译

开发板:mini2440 (128M nandflash)     //关于怎么烧写linux到开发板请点击,Linux RootFs 选择rootfs_rtm_2440.img
 (光盘目录:image/linux/rtm )

开发所需工具:NFS网络文件  minicom 
vim

linux文件目录:/opt/FriendlyARM/mini2440/linux-2.6.32.2

自己驱动目录: /home/lianghuiyong/my2440drivers

注意:我的开发板内核被我裁剪了led驱动部分,包括之后要写的一些驱动,裁剪内核很简单,make menuconfig菜单后对应的驱动空格为空就行了

给大家推荐一个很好用的vim插件:ma6174,因为我用的是NFS网络挂载,使用了路由器上网,当大家不想用到路由器,用的是无线网的时候还想使用NFS网络挂载文件,其他没变,只是将网线修改下,我们上网的网线一般是按B接线排的,记得老师说过,只接局域网不上网的话只需要网线的一端是A排法,另一端是B排法。直接连开发板和电脑就行了。

(一)丶LED原理分析

盗图可耻啊,我还是盗图了,原谅我吧。。。

从图中可知:led一端是直接接着芯片上的引脚,另一端怎么接了个电阻?没电阻降压的话电流一接通,就led这么点阻值,芯片估计光荣了。要点亮led,电阻那端的电流是没法改变的,而且电阻那端是高电平,led没亮的情况下,是因为芯片那端也是提供高电平,要点亮led只需将芯片对应的芯片控制寄存器设置为输出,并将数据寄存器写入0 , 0为低电平,电流通过,led点亮!

(二)丶代码部分

驱动模块:

/*************************************************************************
	> File Name:LHY_led.c
	> Author:梁惠涌
        > Mail:
	> Created Time: 2014年09月29日 星期一 20时34分50秒
 ************************************************************************/
//在 linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach 目录下
#include<mach/regs-gpio.h>         // 和GPIO相关的宏定义
#include<mach/hardware.h>          //S3C2410_gpio_cfgpin
                                   //S3C2410_gpio_setpin等gpio函数定义

//在 linux-2.6.32.2/include/linux 目录下
#include<linux/miscdevice.h>        //注册 miscdevide 结构体成员变量
#include<linux/delay.h>             //这个应该是时延函数了
#include<linux/kernel.h>            //内核,不用说
#include<linux/module.h>            //驱动加载卸载函数定义
#include<linux/init.h>              //初始化头文件
#include<linux/mm.h>
#include<linux/fs.h>                //注册file_operations结构体变量
#include<linux/types.h>
#include<linux/moduleparam.h>
#include<linux/slab.h>
#include<linux/errno.h>
#include<linux/ioctl.h>
#include<linux/cdev.h>
#include<linux/string.h>
#include<linux/list.h>
#include<linux/pci.h>
#include<linux/gpio.h>              //gpio口定义文件(虚拟地址)

//在 linux-2.6.32.2/arch/arm/include/asm 目录下
#include<asm/irq.h>
#include<asm/uaccess.h>
#include<asm/atomic.h>
#include<asm/unistd.h>

#define DEVICE_NAME "leds"

static unsigned long led_table[]={
    S3C2410_GPB(5),  //在gpio-nrs.h(是gpio.h包含的头文件)中定义了虚拟地址
    S3C2410_GPB(6),
    S3C2410_GPB(7),
    S3C2410_GPB(8),
};

static unsigned int led_cfg_table[]={
    S3C2410_GPIO_OUTPUT,      //定义在regs-gpio.h头文件
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
};

static int sbc2440_leds_ioctl(
    struct inode *inode,
    struct file *file,
    unsigned int cmd,
    unsigned long arg ){
        switch(cmd){
            case 0:
            case 1:
            if(arg>4){
                return -EINVAL;
            }
            s3c2410_gpio_setpin(led_table[arg],!cmd);
            return 0;
         default:
            return -EINVAL;
        }
    }

//文件结构体
static struct file_operations dev_fops={
 	.owner=		THIS_MODULE,        //THIS_MODULE防止使用过程中被卸载
  	.ioctl =	sbc2440_leds_ioctl,  //.ioctl 指向 sbc2440_leds_ioctl 函数
};

//混杂设备结构体
static struct miscdevice misc ={
    .minor = MISC_DYNAMIC_MINOR,     //动态分配次设备号(驱动编号)
    .name  = DEVICE_NAME,            //驱动名为定义的 DEVICE_NAME
    .fops  = &dev_fops,              //文件操作指针指向 dev_fops的地址
};

//模块加载并初始化函数
static int __init dev_init(void){
    int ret;
    int i;

    printk(KERN_ALERT "Hello,mini2440 led is installed!\n");
    for(i=0;i<4;i++){
        //配置led 引脚功能,led_table[i]为上面定义的led虚拟地址;
        //led_cfg_table[i]为上面定义的功能(输出)
        s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);

        //设置led状态,0为低电平,led点亮
        s3c2410_gpio_setpin(led_table[i],0);
    }

    ret =misc_register(&misc);

    printk(DEVICE_NAME"\t Lianghuiyong-leds\n");
    return ret;
}

static void __exit dev_exit(void){
    printk(KERN_ALERT"Good-bye,mini2440 led is removed!\n");
    misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");              //GPL协议
MODULE_AUTHOR("Lianghuiyong Inc.");

驱动makefile文件

PWD = $(shell pwd)
KDIR =/opt/FriendlyARM/mini2440/linux-2.6.32.2/
obj-m:= LHY_led.o
all:
	$(MAKE) -C $(KDIR) M=$(PWD) CONFIG_DEBUG_SECTION_MISMATCH=y
clean:
	rm -rf *.o *~core.depend. *.cmd *.ko *.mod.c .tmp_versions
	rm -rf *.order Module.*
insmod:
	insmod LHY_led.ko
rmmod:
	rmmod LHY_led
active:
	echo -e "$(MAKE) \n"
	$(MAKE) -C $(KDIR) M=$(PWD)

测试模块:

/*************************************************************************
	> File Name: ledceshi.c
	> Author:梁惠涌
	> Mail:
	> Created Time: 2014年10月02日 星期日 01时10分03秒
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ioctl.h>

int main(int argc, char **argv)
{
    int on;
    int led_on;
    int fd;

    /*检查两个参数,如果不符合则退出*/
    if(argc !=3 ||sscanf(argv[1],"%d",&led_on)!=1 || sscanf(argv[2],"%d",&on)!=1 || on <0 || on>1 || led_on<0 ||led_on>3){
       fprintf(stderr,"Usage: leds led_on 0|1 \n");
        exit(1);
    }

    fd = open("/dev/leds0", 0);
    if(fd<0){
        fd = open("/dev/leds",0);
    }
    if(fd<0){
        perror("open device leds");
        exit(1);
    }
    /*通过ioctl调用输入的led_on和on控制led*/
    ioctl(fd,on,led_on);
    close(fd);
    return 0;
}

测试文件曾用过官方的makefile文件来make,但是开发板运行时出现syntax error: word unexpected (expecting ")")错误,直接使用gcc吧

命令:arm-linux-gcc -g ledceshi.c(c文件) -o ledceshi(产生的可执行文件名)

开发板上的操作截图(这个绝不是盗图了。。。。。。)

(三)丶函数调用笔记

(一)S3C2410_GPB(5)

头文件中的宏定义

#define S3C2410_GPB(_nr)	(S3C2410_GPIO_B_START + (_nr))

S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),

#define S3C2410_GPIO_NEXT(__gpio) 	((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)

#define S3C2410_GPIO_B_NR	(32)

S3C2410_GPIO_A_START = 0,

我们只用到 S3C2410_GPB(_nr)这个宏定义,而这个虚拟地址怎么算?宏定义(第一行)和等式(第二行)很容易理解,

S3C2410_GPIO_NEXT(S3C2410_GPIO_A)这个呢?

S3C2410_GPIO_NEXT(S3C2410_GPIO_A)=S3C2410_GPIO_A_START(0)+S3C2410_GPIO_A_NR(32)+CONFIG_S3C_GPIO_SPACE+0,

而CONFIG_S3C_GPIO_SPACE的值?根据

#if CONFIG_S3C_GPIO_SPACE != 0
#error CONFIG_S3C_GPIO_SPACE cannot be zero at the moment
#endif

可知 CONFIG_S3C_GPIO_SPACE 只能为0才会讨论虚拟地址是多少,反推过去就知道 S3C2410_GPB(_nr)=32+ _nr 。

总结:S3C2410_GPn(_nr) ,n为 A、B、C、D等,S3C2410_GPn(_nr)=n×32+_nr  (A时n为0,B时为1,C为2 .......)

(二) s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);  函数以S3C2410_GPB(5),S3C2410_GPIO_OUTPUT 实参分析

该函数的具体定义:

gpio-nrs.h 用到的定义 ---  S3C2410_GPIO_BANKB

regs-gpio.h                    ---  S3C2410_GPIO_OUTPUT ,   S3C2410_GPIO_BANKB,    S3C24XX_GPIO_BASE(x),   S3C2410_GPIO_OFFSET(pin)

---  S3C2410_GPIO_BASE(pin)

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) //根据上面的计算,可知pin=37  ,function 可根据宏定义  #define S3C2410_GPIO_OUTPUT  (0xFFFFFFF1)
{
    void __iomem *base = S3C24XX_GPIO_BASE(pin); //#define S3C24XX_GPIO_BASE(x)  S3C2410_GPIO_BASE(x)

/*S3C24XX_GPIO_BASE(pin)的计算:
(以下定义是在 /linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach/map.h
/linux-2.6.32.2/arch/arm/plat-s3c24xx/include/plat/map.h
/linux-2.6.32.2/arch/arm/plat-s3c/include/plat/Map-base.h下)

#define     S3C2410_GPIO_BASE(pin)       ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define     S3C24XX_VA_GPIO                   ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
#define     S3C24XX_PA_GPIO                   S3C2410_PA_GPIO
#define     S3C24XX_PA_UART                   S3C2410_PA_UART
#define     S3C24XX_VA_UART                   S3C_VA_UART
#define     S3C2410_PA_GPIO                   (0x56000000)
#define     S3C2410_PA_UART                   (0x50000000)
#define     S3C_VA_UART                       S3C_ADDR(0x01000000)
#define     S3C_ADDR(x)                      (S3C_ADDR_BASE + (x))
#define     S3C_ADDR_BASE                     (0xF4000000)

 S3C24XX_GPIO_BASE(pin) = S3C2410_GPIO_BASE(x)
   = ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
   = ((((pin) & ~31) >> 1) + (S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
   = ((((pin) & ~31) >> 1) + (0x56000000- 0x50000000) + S3C_VA_UART)
   = ((((pin) & ~31) >> 1) + (0x56000000- 0x50000000) + (S3C_ADDR_BASE + (0x01000000))
   = ((((pin) & ~31) >> 1) + (0x56000000- 0x50000000) + ((0xF4000000) + (0x01000000))
   = ((((pin) & ~31) >> 1) + (0xFB000000)
   = 0xFB000010
//((((pin) & ~31) >> 1)运算:((pin) & ~31) =100000,右移一位=10000,也就是十六进制0x10。
 //base变量存储的是0xFB000010虚拟地址           */

    unsigned long mask;
    unsigned long con;
    unsigned long flags;
    if (pin < S3C2410_GPIO_BANKB) {    // #define S3C2410_GPIO_BANKB   (32*1) ,GPB虚拟地址的开始,
         // 成立则pin是指向GPA,否则指向GPA之外地址
        mask = 1 << S3C2410_GPIO_OFFSET(pin); // #define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) , ((pin) & 31) 也就是将第六位(包括)以上的数清零
    } else {
        mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;     //S3C2410_GPIO_OFFSET(37)=(( 37 ) & 31 ) *2= 00101*2=10 ,mask=3<<10
}
//根据我的理解上面这个if语句,1 和 3分别代表了寄存器控制位的初始状态,S3C2410_GPIO_OFFSET(pin)*2 和  S3C2410_GPIO_OFFSET(pin)
//只是求出控制寄存器的具体位,那么1 << S3C2410_GPIO_OFFSET(pin) 和3 << S3C2410_GPIO_OFFSET(pin)*2 就是将寄存器的某一控制位赋
//予初始状态,之所以要分1 和 3、 S3C2410_GPIO_OFFSET(pin);和S3C2410_GPIO_OFFSET(pin)*2;    主要是因为只有GBA的控制状态位只要一
//位表示就够了(两个功能),而其他的寄存器需要两位(四个功能)来表示。

    switch (function) {
        case S3C2410_GPIO_LEAVE:  //是否是最后一个基(虚拟)地址
              mask = 0;
              function = 0;
              break;

        case S3C2410_GPIO_INPUT:
        case S3C2410_GPIO_OUTPUT:
        case S3C2410_GPIO_SFN2:
        case S3C2410_GPIO_SFN3:

        if (pin < S3C2410_GPIO_BANKB) {    //这个if和我上面分析的一样
            function - = 1;
            function &= 1;
                function <<= S3C2410_GPIO_OFFSET(pin);
            } else {         //GPB的pin属于下面的情况
                function &= 3;       //取 function 后两位,其余位清零 , function=0xFFFFFFF1 ,相与后=01
                function <<= S3C2410_GPIO_OFFSET(pin)*2;     //通过上面的if分析可以知道function=01<<10
                 }
            }
       /*  modify the specified register wwith IRQs off */
    local_irq_save(flags);    // 关中断,中断标志位存储于flags中,定义在<asm/system.h>文件中 

    con = __raw_readl(base + 0x00);//base是上面的指针变量,存储的是0xFB000010 基地址,没找到函数的定义,con应该是读取控制寄存器的值
    con &= ~mask;         //con对应位[10,11]置零,~mask = ~(3<<10)
    con |= function;      //con对应位设置[10,11]为function后两位 01,function = 01 << 10 

     __raw_writel(con, base + 0x00);    //将改变后的con写入寄存器
    local_irq_restore(flags);         //开寄存器中断
}

s3c2410_gpio_setpin(led_table[i],0); 函数 和 s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]); 大同小异,就不分析了。

盗用下网友的虚拟地址映射图(这样这张图才有意义吧,O(∩_∩)O哈哈~)

错误笔记(2.6.32.2内核):

1、/home/nfsshare/root_qtopia/Drive/LED/mini2440_leds.c:29: error: ‘S3C2410_GPB5‘ undeclared here (not in a function)

解决方法:这是因为内核版本问题,gpio-nrs.h定义和手册的例子上用的有所不同:S3C2410_GPB5改为 S3C2410_GPB(5)

2、/home/lianghuiyong/my2440drivers/LED/LHY_led.c:40: error: ‘S3C2410_GPB5_OUTP‘ undeclared here (not in a function)

解决方法:同样原因,在arch/arm/mach-s3c2410/include/mach/regs-gpio.h文件下 可以看到

47行    #define S3C2410_GPIO_OUTPUT  (0xFFFFFFF1)

在S3C2410_GPB5_OUTP 改为 S3C2410_GPIO_OUTPUT

3、error: ‘misc‘ undeclared (first use in this function)

error: variable ‘dev_fops‘ has initializer but incomplete type

error: unknown field ‘owner‘ specified in initializer

这都是没注意,写错了代码:file_operations写错了,misc也写的上下不一致,MISC_DYNAMIC_MINOR漏写了字母

4、 line 1: syntax error: word unexpected (expecting ")")  错误

直接使用gcc编译

命令:arm-linux-gcc -g ledceshi.c(c文件) -o ledceshi(产生的可执行文件名)

时间: 2025-01-03 21:29:26

mini2440驱动奇谭——LED驱动与测试(动态加载)的相关文章

2440驱动奇谭--helloworld

要不要这样,还没写好过驱动就要我去裁剪内核?  好吧,看到用户手册上" LED 驱动已经被编译到缺省内核中,因此不能再使用 insmod 方式加载. "  我重新把手册上手工定制内核走了一遍,把自己之后要写的一些驱动给否掉,以便可以insmod 去掉的驱动有:lcd驱动(包括其中有个logo的选项),触摸屏驱动,音频驱动,看门狗驱动,I2C驱动,pwm驱动,led驱动,按键驱动,A/D驱动,RTC驱动 由于把lcd驱动否掉了,开发板里也就没烧写qt,之烧写了文件系统rootfs,文件名

《Windows驱动开发技术详解》之编程加载NT式驱动

之前我们加载驱动都是利用INSTDRV这个应用,其原理是在注册表中写入相应的字段,这一节我们手动编写代码去加载驱动,其原理类似: 设备驱动程序的动态加载主要由服务控制管理程序(Service Control Manager,SCM)系统组件完成.加载和卸载NT驱动分为四个步骤: 为NT驱动创建新的服务: 开启此服务: 关闭此服务: 删除NT驱动所创建的服务. LoadNTDriver装载驱动代码主要流程如下: 代码如下: 1 BOOL LoadNTDriver(char*lpszDriverNa

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总线来进

基于platform驱动模型的LED驱动

上一篇博文<platform设备驱动框架搭建分析>主要是根据内核源码来分析platform驱动模型工作的原理,在实际的驱动开发中如何使用Linux的这么一种模型来管理这种类型的设备呢?把tq2440开发板上的LED1当做是平台设备注册到Linux系统中,让系统可以用这种platform驱动来管理他. ①总线层:代码不用我们自己去写,内核已经提供了 ②设备层:向platform总线层注册硬件相关的资源,一般是寄存器地址.内存空间.中断号(序号的一种代表)等等 led_dev.c #include

驱动学习之LED驱动框架

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

第三十三天:Tiny4412驱动开发之LED驱动和按键驱动编写

从今天开始进入驱动开发的课程的学习,共完成四件事情.一:u-boot的简单移植,二:uboot中编写helloword程序 三:开发板中led灯的驱动编写,包括led点亮,闪烁,跑马,流水.四:开发板中按键的驱动编写,按下按键后在屏幕中显示字符. 一:u-boot的简单移植 1.进入开发板提供的源码文件包,解压uboot源码包. cd /home/bunfly/source_code/ tar xf uboot_tiny4412-20130729.tgz 2.进入uboot文件夹,更改uboot

在Win64系统上动态加载无签名驱动:WIN64LUD

1.WIN64LUD的全称是WIN64 Load Unsigned Driver,功能如其名,在WIN64系统上加载无签名的驱动. 2.支持Windows 7/8/8.1/2008R2/2012/2012R2(7600/7601/9200/9600). 3.使用方法很简单,运行『win64lud.exe』,如果提示初始化成功,直接把驱动文件拖到窗口里,再按下回车就能加载了. 4.在『DemoForTest』文件夹里有几个无签名的驱动,可以用WIN64LUD加载,配合DBGVIEW可以看到效果.

测试网页加载时间(转)

网站的加载速度是决定网站等级的重要因素,值得站长特别关注.原因很简单,没有人愿意为了打开一个网页而等老半天,换句话说,如果你的网站打开速度 很慢,将流失大量的访客,甚至出现多米诺效应的不良影响.在埋头深入代码中试图提高网站速度之前,先要确定导致网站访问速度缓慢的真正原因是什么.本文收 集了非常有用的WEB服务和工具,可以帮助你诊断和分析你的网站,让你更加全面的了解自己的网站性能. 在线测试工具 测试网站所有对象的加载时间(HTML,images,JavaScript,CSS,嵌入式框架等).您还

2015年传智播客JavaEE 第168期就业班视频教程16-框架结构测试(加载全spring配置文件)+struts2属性驱动测试

模块的规范化我们已经做完了,下面我们要做我们的功能了. 如果是模型驱动就是name="对应model的name" 如果用属性驱动的话,必须得把表现层(Action类)的用于封装对象的模型属性的变量名给带上 课改在提,以后再讲.