Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3004120
  • 博文数量: 523
  • 博客积分: 11908
  • 博客等级: 上将
  • 技术积分: 5475
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-03 15:50
文章分类

全部博文(523)

文章存档

2019年(3)

2013年(4)

2012年(71)

2011年(78)

2010年(57)

2009年(310)

分类: LINUX

2009-05-01 23:49:21

以下是扬创开发板给的led例程,将对应用程序和驱动程序进行详细注释和分析,并验证!

/*

 *  LED  interface   driver for utu2440

 * This file is subject to the terms and conditions of the GNU General Public

 * License.  See the file "COPYING" in the main directory of this archive

 * for more details.

 *   bit.lili@gmail.com 2007-6

 */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#define LED_DRIVER       "utu2440 LED Driver v1.00"

 

static unsigned long led_table [] = {

       S3C2410_GPF4,

       S3C2410_GPF5,

       S3C2410_GPF6,

       S3C2410_GPF7,

};

 

static int led_ioctl(struct inode *inode, struct file *file,

                   unsigned int cmd, unsigned long arg);

/*ioctl(fd, on, led_number);

inode filp 指针是对应应用程序传递的文件描述符 fd 的值,

和传递给 open 方法的相同参数*/

static int led_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);

/*给寄存器赋值,使某一位置10led亮,给寄存器置0,因此对cmd取反*/

/*通过s3c2410_gpio_setpin()来做,此函数为驱动函数的核心操作*/

/*case 0case 1操作一样*/

       default:

              return -EINVAL;

       }

       return 0;

}

 

/*设备驱动文件操作结构体*/

static struct file_operations led_fops = {

       .ioctl = led_ioctl,

};

 

static struct miscdevice led_dev = {

       MISC_DYNAMIC_MINOR,

       "led",

       &led_fops

};

/*

struct miscdevice  {

       int minor; //MISC_DYNAMIC_MINOR

       const char *name;//"led"

       const struct file_operations *fops; //&led_fops

       struct list_head list;

       struct device *parent;

       struct device *this_device;

};

此结构体是注册混合设备所需要的参数。主要有:

minor:次设备号,所有的misc设备共用一个主设备号,所以注册misc设备时只要次设备号就可以了。利用次设备号来区分设备的。

name:misc设备名。

*fops:misc设备文件操作结构体。其它三个参数很少用

*/

 

static int led_init(void)

{

       pr_info("%s\n", LED_DRIVER);

       /*printk(KERN_INFO fmt, ##arg)

       #defineKERN_INFO"<6>"   提示信息,如驱动程序启动时,打印硬件信息

       没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL

       (这个默认级别一般为<4>,即与KERN_WARNING在一个级别上),

       其定义在linux26/kernel/printk.c中可以找到。

  下面是一个比较简单的使用

  printk(KERN_INFO "INFO\n");  //这里可以使用数字代替 KERN_INFO

  即可以写成printk(<6> "INFO\n"); 

  在这个格式的定义中,日志级别和信息文本之间不能够使用逗号隔开,

  因为系统在进行编译的时候,将日志级别转换成字符串于后面的文本信息进行连接。*/

       misc_register(&led_dev);// misc设备注册

/*非标准设备使用 misc_register,即一些字符设备不符合预先确定的字符设备范畴

这些设备就用主编号10一起归于"其他类型"misc_register()用主编号10调用 register_chrdev(),设备名称和函数表指针通过miscdevice数据结构获得。同样,miscdevice 数据结构还保存设备驱动程序所使用的次要号码。*/   

       return 0;

}

 

static void __exit led_exit(void)

{

       misc_deregister(&led_dev);

       /*misc(混合,其他类型,不能严格划分的设备类型)类设备的注销函数,成功返回为0,错误返回一个错误代码*/

}

 

module_init(led_init);

module_exit(led_exit);

 

MODULE_AUTHOR("lili         bit.lili@gmail.com");

MODULE_LICENSE("GPL");

 

 

应用程序:

#include /*标准输入输出库,sscanf函数,fprintf函数都是在这个库里*/

#include

#include /*一些宏的定义在这里面,stderr*/

#include /*文件操作控制库,ioctl函数就在这里*/

/*执行:./led 1 1*/

int main(int argc, char **argv)

/*argc表示参数的个数,而参数都存放在argv,它是指针数组*/

{

       int on; /*led的开关状态,从第三个参数中获取*/

       int led_number; /*led的编号,从第二个参数中获取*/

       int fd; /*设备号,将从打开的leds设备获得*/

 

/*获取参数,并作参数的检验*/

    /*scanf/sscanf 函数的返回值反映的是按照指定的格式符正确读入的数据的个数

sscanf(argv[1], "%d", &led_number)返回正确,应等于1)。

如果输入数据与指定格式不符,则会产生输入错误。遇到输入错误,scanf函数会立即终止,返回已经成功读取的数据的个数。所以,通过scanf函数的返回值和指定输入数据的个数(由格式符决定)的比较,可以判断数据输入是否成功。

*/

       if (argc != 3 || sscanf(argv[1], "%d", &led_number) != 1 || sscanf(argv[2],"%d", &on) != 1 ||on < 0 || on > 1 || led_number < 0 || led_number > 3) {

              fprintf(stderr, "Usage:\n");

              fprintf(stderr, "\t led led_number on|off\n");

              fprintf(stderr, "Options:\n");

              fprintf(stderr, "\t led_number from 0 to 3\n");

              fprintf(stderr, "\t on 1   off 0\n");

/*stdout -- 标准输出设备 (printf("..")) stdout
stderr --
标准错误输出设备
两者默认向屏幕输出。
但如果用转向标准输出到磁盘文件,则可看出两者区别。stdout输出到磁盘文件,stderr在屏幕*/

              exit(1);

       }

       fd = open("/dev/led", 0);

       /*驱动程序可以不实现open这个函数,在这种情况下,设备的打开操作永远成功。*/

       if (fd < 0) {

              perror("open device /dev/led");

              exit(1);

       }

       ioctl(fd, on, led_number);

       close(fd);

       return 0;

}

 

总结:

上面是点亮led的应用程序和驱动程序,在应用程序中,可以根据需要进行修改,比如让led闪烁等等,驱动程序和应用程序分别给了详细注释,如有错误欢迎指正!程序已在扬创开发板上验证,但验证中发现第二个led不亮,还不知为什么,望高手指点!

阅读(3503) | 评论(3) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-03-20 11:42:34

/dev/led的设备名是通过mknod生成的,此驱动是静态加载的,如果是按模块加载,方法如下: insmod led mknod /de/led c 243 0 欢迎讨论!

pan_1304212010-03-19 18:10:36

请教一下, /dev/led节点是在 misc_register(&led_dev);调用中生成的吗? 这是不是mknod命令的代码实现? 一般的char设备注册还可以用什么函数呢? 谢谢。

chinaunix网友2009-08-22 10:28:49

IO 没有初始化, 即IO是输出还是输入,或者处于其他状态,是未知也