关于ok6410 LED and BEEP驱动程序之我见

来源:互联网 发布:淘宝卖家违规分超过48 编辑:程序博客网 时间:2024/05/18 02:02
 

从去年暑假,也就是20117月份开始接触嵌入式,到现在,也半年多的时间了,从最初如何使用linux系统,配置sambanfs服务器,linux应用编程等到后来的内核学习,了解Uboot的启动流程,以及一些代码的作用分析与修改,到Uboot如何配置编译以及下载到我的ok6410开发板,再然后深入到内核,文件系统,挂载Nfs文件系统等,然后才开始弄驱动。

苦于现在市面上的资料大多是关于ARM9,关于6410的却很少,但是我坚信,6410正以他的优势席卷嵌入式这个行业,所以,在此写下一些关于驱动的开端。

我是这样想的,关于驱动程序的基础知识,随便一个百度谷歌或者买本书都会有提及,所以在此不再详述,推荐《LDD3》,《linux设备驱动开发及应用》,《linux设备驱动详解》,想学好驱动编程,这三本书应该是要满足的,个人之见。

当初自学单片机,我跟大多数一样,从点亮一个LED灯开始,然后流水灯,蜂鸣器,数码管等开始单片机编程,算是入了门,现在玩ARM驱动,我想也通过这几个编程入门,然后我们再慢慢的深入到操控各种硬件,但是,入门并不是那么容易,我见网络上很多人在找关于如何驱动LEDARM11ok6410驱动程序,好,别人给你了一个LED驱动程序,但是没有注释,又或者这一个驱动程序,并不是那么让你很容易的理解到他的操控原理,以及你还是没有学会如何去编写驱动程序,在此,我写了一个驱动程序,包含流水灯和蜂鸣器同时工作,仅供参考,本人也尽量多些注释,以便他人找到学习的捷径。

以下是我写的led-beep-driver.c:

#include <linux/module.h>  /*包含宏MODULE_LICENSE(‘GPL’)*/

 #include <linux/kernel.h>  /*以便使用printk()等函数*/

#include <linux/fs.h>  /*包含常用的数据结构,如struct file*/

#include <asm/uaccess.h> /* copy_to_user,copy_from_user */ 

#include <linux/pci.h>   /*包含readlwrietl*/

#include <mach/map.h>   /*不包含此文件,你试试会出现怎样的错误提示呢?*/

#include <mach/regs-gpio.h>    /*定义了GPIO相关的数据宏*/

#include <mach/gpio-bank-m.h>/*以便于使用s3c64xx_GPM~*/

#include <mach/gpio-bank-f.h>   

#include <plat/gpio-cfg.h>  /*包含了GPIO的各种配置函数,如设置输入输出,设置上拉电阻等*/

 

 

#define BEEP_MAJOR 241    /*定义主设备号*/

int j=0;/*定义变量干嘛用?请往后看程序,请思考为什么要定义成全局变量*/

int beep_open (struct inode *inode,struct file *filp) 

 

     

   Unsigned luo,ming;    

         luo = readl(S3C64XX_GPMCON);     /*读取当前S3C64XX_GPMCON值,以便接下来的操作,接下来的操作是位操作,以便于不至于破坏其他控制位,只操控我们需要控制的LED*/

    luo &= (~0xffff);/*这是位操作,将高4位先变为0,而其他位不改变,保护其他位值*/

    luo |= 0x1111;    /*或操作,将对应为变为1,这样设置对应位为输出模式*/

         writel(luo, S3C64XX_GPMCON);

 

          ming = readl(S3C64XX_GPFCON);    

    ming &= (0x3fffffff);

    ming |= (~0xbfffffff);    

         writel(ming, S3C64XX_GPFCON);

  

    printk("~~~~open~~welcome !~~~~~~~~~~\n"); 

 

    

    return 0; 

}

 

 ssize_t beep_read (struct file *filp, char __user *buf, size_t count,loff_t *f_pos) 

    printk("~~~~~~~~read~~~~~~~~~~~~~~~\n"); 

    return count; 

}  /*此函数为空,我们在此不设计设备(及LED和蜂鸣器)的读操作*/

 

 

ssize_t beep_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos) 

    char wbuf[10]; 

    unsigned luo;

        

    printk("~~~~~write~~~~~~~\n"); 

    copy_from_user(wbuf,buf,count);  /*拷贝用户空间地址到内核空间,用于驱动程序中*/

    switch(wbuf[0])  /*通过switch语句解析用户空间传下来的命令,然后case语句中对应控制设备实现对应的操作*/

    { 

        case 0:  //

                         writel(0xf, S3C64XX_GPMDAT);/*将数据寄存器写上全1,如此实现Led的关闭熄灭*/

              luo = readl(S3C64XX_GPFDAT);/*读出当前的GPF数据寄存器的值,方便接下来的操作控制*/

                     luo &= 0x7fff;/*将最高位清零,如此可以控制蜂鸣器*/

                            writel(luo, S3C64XX_GPFDAT);  /*写值到GPF寄存器(看原理图可知道,蜂鸣器连接到GPF相应的引脚上,在此控制该引脚的数据寄存器,可以实现开启和关闭),如此就实现蜂鸣器的控制*/

            break; 

        case 1:  //开,,,此case语句同上,只是实现与上case 0相反的操作罢了

              j=j+1;

           if(j>4) j=1;

           if(j==1) writel(0xe, S3C64XX_GPMDAT);

                if(j==2) writel(0xd, S3C64XX_GPMDAT);

                if(j==3) writel(0xb, S3C64XX_GPMDAT);

                if(j==4) writel(0x7, S3C64XX_GPMDAT);

              

             luo = readl(S3C64XX_GPFDAT);

                     luo &= 0x7fff;

                       luo |= (~0x7fff);

                            writel(luo, S3C64XX_GPFDAT);

 

 

            break; 

        default : 

            break; 

    } 

    return count; 

 

int beep_release (struct inode *inode, struct file *filp) 

    printk(“~~~~~~~~~release~~~~~~~\n"); 

    return 0; 

 

struct file_operations led_beep_fops ={ 

    .owner = THIS_MODULE, 

    .open = beep_open,

    .read = beep_read,   

    .write = beep_write, 

    .release = beep_release, 

};                                  /*这是所有函数的操作集,及函数指针集合,系统调用时,对应于此集合中的指针找到对应的操作函数,实现对硬件的不同操作*/

 

int __init beep_init (void)  /*模块加载时,加载宏调用此函数,在此函数中实现设备驱动注册*/

{   int rc; 

    printk ("Test led--beep dev\n"); 

    rc = register_chrdev(BEEP_MAJOR,"led_beep",&led_beep_fops);/*加载时最关键语句,此函数就是用于注册驱动到内核*/ 

    if (rc <0)  /*注册失败时函数返回负整数,在此即是判断是否注册失败,然后对失败后进行相应的处理,好的驱动程序应该考虑到若失败,若何妥善处理失败,以免造成内核崩溃等错误后果造成大的隐患*/

    { 

        printk ("register %s char dev error\n","led_beep"); 

        return -1; 

    } 

    printk ("success!\n"); 

    return 0; 

 

void __exit beep_exit (void)  /*模块卸载时调用此函数,在此函数中,我们要实现与加载时相反的操作,及我们注册时,申请了什么,我们就应该在卸载时释放什么,以免造成下次使用时不能再用的后果,产生比如中断,内存,IO等的浪费*/

    unregister_chrdev(BEEP_MAJOR,"led_beep");  /*一定要记得,注销函数中的参数,要与注册函数的参数保持相同,而不是简单的一致,因为只有这样才能注册什么就注销什么,不会造成错误注销以免导致其他设备不能用,尽管这样的机会不大,但总是防备点好,当然,若写入的参数在内核中并不存在,注销是会失败的*/

    printk ("module exit have done!\n"); 

    return ; 

 

module_init(beep_init); /*要清楚这是个宏,并不是函数,宏和函数还是有区别的,尽管这看起来不同于常见的一些宏,此宏即实现了加载*/

module_exit(beep_exit);

MODULE_LICENSE("GPL");   /*我们的驱动程序要符合一些标准,如此才能更加完美的匹配内核,当然没有也是可以的,但有的时候或许会出现些问题,避免麻烦,加上更         省事*/

MODULE_AUTHOR(“liuyue@feng”);/*这是作者署名的地方,不再像简单的C语言那样用注释语句来打广告,写上日期和作者,模块中,有很多宏可以来为我们利用,呵呵,在此打个广告,我是低调的,不显山露水的“六月@风”*/

 

OK,关于ledbeepOK6410驱动程序就到此吧,这算是给自己复习的机会,同时,也给那些初学者一个方向,相信通过此分析,你们应该有所领悟了,我用我个人的理解通俗简单的分析就结束了,至于未来你们还需要什么,我又懂得什么,未来再说吧,关注我的空间,随时不定期更新。

在群中,很多人迷茫于不知道如何学,路线如何,我说不少了,有时间也整理一篇文档出来,认真负责的去说下我如何从完全不了解到一步步坚持走来的过程。

OK,那就下次再期待吧!一起努力,共同进步,每天收获一点。

对了,在此也贴上测试程序吧,如下,至于Makefile,这个还是自己去思考,否则完全看别人的,记忆不深刻,留给自己去琢磨:

#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
int main (void) 

    int fd; 
    char buf[10]={0,1}; 
    fd = open("/dev/my_beep",O_RDWR); 
    if (fd < 0) 
    { 
        printf ("Open /dev/my_beep file error\n"); 
        return -1; 
    }    
    while(1) 
    { 
        write(fd,&buf[0],1); 
        sleep(1); 
        write(fd,&buf[1],1); 
        sleep(1); 
    } 
    close (fd); 
    return 0; 

 

转载请注明出处,作者,六月@风 362543692@.qq.com

原创粉丝点击