关于ok6410 LED and BEEP驱动程序之我见
来源:互联网 发布:淘宝卖家违规分超过48 编辑:程序博客网 时间:2024/05/18 02:02
从去年暑假,也就是2011年7月份开始接触嵌入式,到现在,也半年多的时间了,从最初如何使用linux系统,配置samba,nfs服务器,linux应用编程等到后来的内核学习,了解Uboot的启动流程,以及一些代码的作用分析与修改,到Uboot如何配置编译以及下载到我的ok6410开发板,再然后深入到内核,文件系统,挂载Nfs文件系统等,然后才开始弄驱动。
苦于现在市面上的资料大多是关于ARM9,关于6410的却很少,但是我坚信,6410正以他的优势席卷嵌入式这个行业,所以,在此写下一些关于驱动的开端。
我是这样想的,关于驱动程序的基础知识,随便一个百度谷歌或者买本书都会有提及,所以在此不再详述,推荐《LDD3》,《linux设备驱动开发及应用》,《linux设备驱动详解》,想学好驱动编程,这三本书应该是要满足的,个人之见。
当初自学单片机,我跟大多数一样,从点亮一个LED灯开始,然后流水灯,蜂鸣器,数码管等开始单片机编程,算是入了门,现在玩ARM驱动,我想也通过这几个编程入门,然后我们再慢慢的深入到操控各种硬件,但是,入门并不是那么容易,我见网络上很多人在找关于如何驱动LED的ARM11ok6410驱动程序,好,别人给你了一个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> /*包含readl,wrietl*/
#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,关于led和beep的OK6410驱动程序就到此吧,这算是给自己复习的机会,同时,也给那些初学者一个方向,相信通过此分析,你们应该有所领悟了,我用我个人的理解通俗简单的分析就结束了,至于未来你们还需要什么,我又懂得什么,未来再说吧,关注我的空间,随时不定期更新。
在群中,很多人迷茫于不知道如何学,路线如何,我说不少了,有时间也整理一篇文档出来,认真负责的去说下我如何从完全不了解到一步步坚持走来的过程。
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
- 关于ok6410 LED and BEEP驱动程序之我见
- OK6410裸机开发之Beep
- 《OK6410-LED驱动程序设计》之使用系统定义的宏
- OK6410 Linux下LED驱动程序
- OK6410之裸机led
- Linux下的led驱动程序,ok6410
- OK6410之ADC驱动程序
- OK6410 SD卡一键烧写之我见
- OK6410 SD卡一键烧写之我见
- 《OK6410-LED驱动程序设计》之使用ioremap实现访问CPU寄存器
- Linux3.6.7在OK6410平台的移植(五)字符设备驱动程序之LED
- 飞凌OK6410之点亮LED
- OK6410(s3c6410)之LED点亮
- ok6410之lcd驱动程序设计
- OK6410裸机开发之LED灯
- OK6410裸机开发之LED灯
- 字符设备驱动程序之LED驱动程序
- Beep蜂鸣器驱动程序分析
- 第二周
- LGame使用记
- ubuntu gedit 中文乱码
- 常用JS验证函数大全
- android开发中xml修改后对应R.java中找不到相应资源(id等)解决办法
- 关于ok6410 LED and BEEP驱动程序之我见
- Visual C++ 64 位迁移的常见问题
- Page.ClientScript.RegisterStartupScript 与 RegisterClientScriptBlock 之间的区别
- 暂停JS向下执行
- 给你的C代码加上行号
- const 与 readonly知多少
- android Binder 工作流程
- c isprint和isgraphis的区别
- VC++2010.net连接Access,SQL Server2008的程序例子成功