转自:http://www.xuebuyuan.com/1856562.html

水平有限,描述不当之处请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7711695

本节介绍如何利用板载的Led和Linux的内核定时器实现一个简单的流水灯的驱动,所使用的开发板是TQ2440,内核版本2.6.30.4。

程序比较简单,也没涉及到什么机制,直接上代码了!关于定时器的使用模板可以参考<<Linux设备驱动开发详解>>.

驱动程序:

#include<linux/module.h>
#include<linux/init.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/mm.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/timer.h>
#include<linux/jiffies.h>
#include<asm/io.h>
#include<asm/uaccess.h>
#include<mach/regs-gpio.h> #define LED_MAJOR 244 #define LED_ON 0
#define LED_OFF 1 #define LED1_PIN S3C2410_GPB5
#define LED2_PIN S3C2410_GPB6
#define LED3_PIN S3C2410_GPB7
#define LED4_PIN S3C2410_GPB8 static unsigned long led_major = LED_MAJOR; struct led_dev
{
struct cdev cdev;
struct timer_list s_timer;
atomic_t led_no; //LED编号
atomic_t sec_counter; //秒计时数
}; struct led_dev *led_devp; void led_control(int led_no)
{
switch(led_no)
{
case 1:s3c2410_gpio_setpin(LED1_PIN,LED_ON);
s3c2410_gpio_setpin(LED2_PIN,LED_OFF);
s3c2410_gpio_setpin(LED3_PIN,LED_OFF);
s3c2410_gpio_setpin(LED4_PIN,LED_OFF);
break;
case 2:s3c2410_gpio_setpin(LED1_PIN,LED_OFF);
s3c2410_gpio_setpin(LED2_PIN,LED_ON);
s3c2410_gpio_setpin(LED3_PIN,LED_OFF);
s3c2410_gpio_setpin(LED4_PIN,LED_OFF);
break;
case 3:s3c2410_gpio_setpin(LED1_PIN,LED_OFF);
s3c2410_gpio_setpin(LED2_PIN,LED_OFF);
s3c2410_gpio_setpin(LED3_PIN,LED_ON);
s3c2410_gpio_setpin(LED4_PIN,LED_OFF);
break;
case 4:s3c2410_gpio_setpin(LED1_PIN,LED_OFF);
s3c2410_gpio_setpin(LED2_PIN,LED_OFF);
s3c2410_gpio_setpin(LED3_PIN,LED_OFF);
s3c2410_gpio_setpin(LED4_PIN,LED_ON);
break;
default:break; }
} //定时器处理函数
static void sec_timer_handler(unsigned long arg)
{
int num; mod_timer(&led_devp->s_timer,jiffies+HZ); num = atomic_read(&led_devp->led_no);
if(num == 4)
{
atomic_set(&led_devp->led_no,1);
}
else
{
atomic_inc(&led_devp->led_no);
} num = atomic_read(&led_devp->led_no);
led_control(num); atomic_inc(&led_devp->sec_counter);
num = atomic_read(&led_devp->sec_counter);
printk(KERN_INFO "sec_count:%d\n",num); } static int led_open(struct inode *inode,struct file *filp)
{
struct timer_list *timer; timer = &led_devp->s_timer;
init_timer(timer);
timer->function = sec_timer_handler;
timer->expires = jiffies+HZ; //计时频率为HZ add_timer(timer);
atomic_set(&led_devp->sec_counter,0);
atomic_set(&led_devp->led_no,0); return 0; } static int led_release(struct inode *inode, struct file *filp)
{
del_timer(&led_devp->s_timer);
return 0;
} static ssize_t led_read(struct file *filp, char __user *buf,
size_t size, loff_t *ppos)
{
int count,led_no;
int result; count = atomic_read(&led_devp->sec_counter);
led_no = atomic_read(&led_devp->led_no);
result = (count<<3)+led_no; if(put_user(result,(int*)buf))
{
return -EFAULT;
}
else
{
return sizeof(int);
}
} static const struct file_operations led_fops =
{
.owner = THIS_MODULE,
.read = led_read,
.open = led_open,
.release = led_release,
}; static void led_setup_cdev(struct led_dev *dev, int index)
{
int err,devno = MKDEV(led_major,index);
cdev_init(&dev->cdev,&led_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev,devno,1); if(err)
{
printk(KERN_NOTICE "Error %d adding %d\n",err,index);
}
} static int led_init(void)
{
int result; dev_t devno = MKDEV(led_major,0); //获取设备号 /*注册设备*/
if(led_major)
result = register_chrdev_region(devno,1,"led");
else
{
result = alloc_chrdev_region(&devno,0,1,"led");
led_major = MAJOR(devno);
} if(result<0)
{
printk("register failed!");
return result;
} led_devp =(struct led_dev*)kmalloc(sizeof(struct led_dev),GFP_KERNEL); if(!led_devp)
{
result = -ENOMEM;
unregister_chrdev_region(devno,1);
}
memset(led_devp, 0 ,sizeof(struct led_dev)); led_setup_cdev(led_devp,0); /*配置IO口*/
s3c2410_gpio_cfgpin(LED1_PIN,S3C2410_GPIO_OUTPUT);
s3c2410_gpio_cfgpin(LED2_PIN,S3C2410_GPIO_OUTPUT);
s3c2410_gpio_cfgpin(LED3_PIN,S3C2410_GPIO_OUTPUT);
s3c2410_gpio_cfgpin(LED4_PIN,S3C2410_GPIO_OUTPUT); /*初始化IO电平*/
s3c2410_gpio_setpin(LED1_PIN,LED_OFF);
s3c2410_gpio_setpin(LED2_PIN,LED_OFF);
s3c2410_gpio_setpin(LED3_PIN,LED_OFF);
s3c2410_gpio_setpin(LED4_PIN,LED_OFF); return 0;
} static void led_exit(void)
{
cdev_del(&led_devp->cdev);
kfree(led_devp);
unregister_chrdev_region(MKDEV(led_major,0),1);
} MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vanbreaker"); module_init(led_init);
module_exit(led_exit);

在该例程中,由于控制LED亮灭的部分放在了定时器处理函数中,因此led_read函数没有实际作用,这样的话应用程序就简单一些;另一种选择是将该控制部分放在应用程序中完成,不过还得添加一个iocntl函数,这是我之前的做法。

控制s3c2440的IO口可以使用内核中已经提供的操作函数,在arch\arm\plat-s3c24xx\Gpio.c中,需要包含头文件<asm/io.h>相关的IO口定义在arch\arm\s3c2410\include\mach\Regs-gpio.h中,需要包含头文件<mach/regs-gpio.h>

可以分析下s3c2410_gpio_setpin()这个函数

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C24XX_GPIO_BASE(pin);//获取该pin所在的gpio组的虚拟地址
unsigned long offs = S3C2410_GPIO_OFFSET(pin);//计算该pin在gpio组内的偏移
unsigned long flags;
unsigned long dat; local_irq_save(flags); dat = __raw_readl(base + 0x04);//通过加上0x04定位到数据寄存器,将该组gpio的数据读取出来
dat &= ~(1 << offs); //相应位清零
dat |= to << offs; //相应位置位
__raw_writel(dat, base + 0x04); //写回数据 local_irq_restore(flags);
}
#define S3C24XX_GPIO_BASE(x)  S3C2410_GPIO_BASE(x)
#define S3C2410_GPIO_BASE(pin)   ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)

S3C2410_GPIO_BASE(pin)用来计算pin所在的GPIO组的虚拟地址,S3C24XX_VA_GPIO是IO的虚拟基址,我们分析一下 ((pin)&~31)>>1是什么意思。首先我们得了解传入的参数究竟是什么形式的,以S3C2410_GPA0的定义为例:

#define S3C2410_GPA0         S3C2410_GPIONO(S3C2410_GPIO_BANKA, 0)
#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))

#define S3C2410_GPIO_BANKA   (32*0)
#define S3C2410_GPIO_BANKB (32*1)
#define S3C2410_GPIO_BANKC (32*2)
#define S3C2410_GPIO_BANKD (32*3)
#define S3C2410_GPIO_BANKE (32*4)
#define S3C2410_GPIO_BANKF (32*5)
#define S3C2410_GPIO_BANKG (32*6)
#define S3C2410_GPIO_BANKH (32*7)

可以看到bank都有32位,因此传入的pin参数实际就是离起始bank的位偏移,S3C2410_GPA0为0,S3C2410_GPB0为32.而每个GPIO组都有4个寄存器,一个GPIO组所占的内存大小就有4*32/8=32/2=32>>1=16个字节大小,由此可见 (pin)&~31是先将低位屏蔽,计算出pin所在的gpio组,再将结果右移一位就是计算字节的偏移,如GPA的偏移为0,GPB的偏移为0X10,GPC的偏移为0X20……最后将偏移加上IO的虚拟基址就得到了该组IO口的虚拟地址了。

计算组内偏移很简单:

#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)

应用测试程序:

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h> int main()
{
int fd; fd = open("/dev/led_timer",O_RDWR); if(fd != -1)
{
printf("open /dev/led_timer\n");
while(1);
}
else
{
printf("cannot open /dev/led_timer!");
}
}

将编译好的模块和应用程序移到开发板上进行加载和执行,即可以看到流水灯的效果

第一个Linux驱动-流水灯【转】的更多相关文章

  1. 【从零开始,从内核驱动驱动到用户空间调用】编写第一个linux驱动,通过端口访问I/O寄存器。

    目的: 通过I/O端口方式访问RTC的秒寄存器: 由于本人从来没看过linux方面的书籍,也只是会在终端用些常用的命令而已,这次老大叫我学着通过I/O端口方式直接去读写寄存器.于是我在google中搜 ...

  2. 一个linux 驱动升级的小问题记录

    重复踩了两次坑,所以简单记录下. 内核 3.10. 在修改了驱动的gro实现之后,进行驱动版本的升级,make && make install 之后,发现tg3的驱动,没有生效. 相同 ...

  3. 超简单易用的 “在 pcduino 开发板上写 Linux 驱动控制板载 LED 的闪烁”

    版权声明:本文为博主原创文章,未经博主同意不得转载.转载联系 QQ 30952589,加好友请注明来意. https://blog.csdn.net/sleks/article/details/251 ...

  4. Linux代码的重用与强行卸载Linux驱动

    (一)Linux代码的重用 重用=静态重用(将要重用的代码放到其他的文件的头文件中声明)+动态重用(使用另外一个Linux驱动中的资源,例如函数.变量.宏等) 1.编译是由多个文件组成的Linux驱动 ...

  5. linux驱动面试题2

    1.什么是GPIO? general purpose input/output GPIO是相对于芯片本身而言的,如某个管脚是芯片的GPIO脚,则该脚可作为输入或输出高或低电平使用,当然某个脚具有复用的 ...

  6. Xilinx Vivado的使用详细介绍(4):Zedboard+vivado之流水灯(加SDK)

    Vivado+zedboard之初学流水灯 Author:zhangxianhe 环境:vivado 2016.3(已验证适用于2015.4) 开发板:Zedboard version xc7z020 ...

  7. linux驱动面试题整理

    1.字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件? 答:mknod命令结合设备的主设备号和次设备号,可创建一个设备文件. 评:这只是其中一种方式,也 ...

  8. 如何成为一个Linux内核开发者

    你想知道如何成为一个Linux内核开发者么?或者你的老板告诉你,“去为这个设备写一个Linux驱动.“这篇文档的目的,就是通过描述你需要 经历的过程和提示你如何和社区一起工作,来教给你为达到这些目的所 ...

  9. LINUX驱动、系统底层

    就业模拟测试题-LINUX驱动.系统底层工程师职位 本试卷从考试酷examcoo网站导出,文件格式为mht,请用WORD/WPS打开,并另存为doc/docx格式后再使用 试卷编号:143921试卷录 ...

随机推荐

  1. 使用Ninject+Moq在单元测试中抽象数据访问层

    一.测试方法的业务逻辑时,通常都需要从数据库读取测试数据,但是每次初始化数据库数据都很麻烦,也会影响到其它业务对数据的访问,怎样抽象数据访问层呢?就是用Moq去模拟数据访问的逻辑     二.步骤如下 ...

  2. GameMap(类结构)(不断跟新)

    暂时有个疑问为什么这些需要这么复杂的继承

  3. frequentism-and-bayesianism-chs

    frequentism-and-bayesianism-chs 频率主义和贝叶斯主义——一个实用的介绍 此notebook最初出现在博客Pythonic Perambulations的文章.BSD l ...

  4. 引擎设计跟踪(九.14.2d) [翻译] shader的跨平台方案之2014

    Origin: http://aras-p.info/blog/2014/03/28/cross-platform-shaders-in-2014/ 简译 translation: 作者在2012年写 ...

  5. JavaScript之setcookie()讲解

    function setcookie(name,value){          var Days = 30;          var exp  = new Date();          exp ...

  6. iis7/7.5设置上传文件最大大小

    本编今天接到一个客户的修改,说一个68M的pdf文件上传不上去,但是我本地开启断点调试了好几遍,都没有问题,能正常上传文件,由此确定不是代码问题.然后我试着上传5M左右的pdf却能正常的上传,然后上传 ...

  7. Codeforces Round #263 (Div. 2)

    吐槽:一辈子要在DIV 2混了. A,B,C都是简单题,看AC人数就知道了. A:如果我们定义数组为N*N的话就不用考虑边界了 #include<iostream> #include &l ...

  8. CSS 类名的单词连字符:下划线还是连接符?

    本文的部分内容整理自我对此问题的解答: 命名 CSS 的类或 ID 时单词间如何连接? - 知乎 问题 CSS 类或 ID 命名时单词间连接通常有这几种写法: 驼峰式: solutionTitle.s ...

  9. C:\Windows\system32\config\systemprofile\AppData\Local\Microsoft\Team Foundation\4.0\Cache\VersionControl.config is not valid and cannot be loaded.

    Recently, we experienced a strange problem with TFS 2010. We spent a few days before we figured it o ...

  10. POJ 2480 Longge's problem (积性函数,欧拉函数)

    题意:求∑gcd(i,n),1<=i<=n思路:f(n)=∑gcd(i,n),1<=i<=n可以知道,其实f(n)=sum(p*φ(n/p)),其中p是n的因子.为什么呢?原因 ...