s3c2440 LED驱动分析

这个开发板已经很久没有动了,这一次辞职后想来想去还是选择去做驱动吧。以前写的那些驱动代码早就不知道哪里去了,当然更不记得了。所以现在从头开始学习,也顺便记录下笔记;

原理

首先看看LED的电路图:

不难看出,LED1==GPB5   LED2==GPB6   LED3==GPB7    LED4==GPB8

然后就去看看IO端口图:

要设置的非常简单,就是把GPBCON设置为输出,GPBDAT设置为0时,则灯亮;设置为1时,则灯灭;

相关知识点

其实上一篇中s3c2440系统自带的管脚宏和函数已经分析过那些引脚宏;

S3C2410_GPB(5):表示GPB5的虚拟地址;

s3c2410_gpio_setpin(S3C2410_GPB(5), 0):表示给GPB5数据位的虚拟地址上设置为0;其实就是GPBDAT中对应位设置为0;

s3c2410_gpio_cfgpin(S3C2410_GPB(5), S3C2410_GPIO_OUTPUT):表示给GPB5控制位的虚拟地址上设置为输出;其实就是GPBCON中对应位设置为输出状态;

下面涉及的结构体和函数都在  include/cdev.h中可以找到

字符设备结构体cdev:

struct cdev {
	struct kobject kobj;
	struct module *owner;// 模块属于谁
	const struct file_operations *ops;// 对应的操作函数
	struct list_head list;
	dev_t dev;// 设备号
	unsigned int count;
};

struct cdev *cdev_alloc(void);为字符设备结构体申请内存;

void cdev_init(struct cdev *, const struct file_operations *);把字符设备结构体和实现的操作函数绑定起来;

int cdev_add(struct cdev *, dev_t, unsigned);把设备号和字符设备结构体绑定起来,其实这里就相当于把我们自己实现的操作函数和设备号对接起来。上层应用打开的文件是根据设备号,也就是说上层操作的仅仅是和设备号有关系,而要上层应用去使用我们自己设计的函数,就必须把实现的函数和设备号绑定起来。这样他们之间就有关系了。(语言表达有限,自己可以体会下)

void cdev_del(struct cdev *);把字符设备卸载下来;

void cdev_put(struct cdev *p);对字符设备结构体内存的释放;(我程序中使用到这个却是说未定义,但在源代码cdev.h中却能找到这个函数,不知道为什么会这样)

还有些函数等使用到的时候再去分析;

实现代码

驱动实现代码

#include<linux/init.h>
 #include<linux/module.h>
 #include<linux/fs.h>
 #include<linux/cdev.h>
 #include<linux/errno.h>
 #include<mach/regs-gpio.h>
 #include<linux/gpio.h>

 #define MAJOR_NUM 250 // 主设备号
 #define MINOR_NUM 0   // 次设备号
 #define DEV_NAME "yzh_led_test" //驱动名称,加载完后用命令 cat /proc/devices可以查看到这个

 #define led_on      1
 #define led_off     2
 #define all_led_on  3
 #define all_led_off 4

 static unsigned long led_IO_port[]=
 {
     S3C2410_GPB(5),
     S3C2410_GPB(6),
     S3C2410_GPB(7),
     S3C2410_GPB(8),
 };

 static struct cdev *cdevp = NULL; // 字符设备结构体cdev
 static int devNum;

 int myOpen(struct inode *inode, struct file *file)
 {
     printk(KERN_INFO"in myOpen() will open led!\n");

     if (file->f_mode & FMODE_READ)
         printk(KERN_INFO"file is readonly!\n");
     else if (file->f_mode & FMODE_WRITE)
         printk(KERN_WARNING"file is writeonly!\n");
     else
         printk(KERN_DEBUG"file is read and write!\n");

     return 0;
 }

 static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
     int i;

     if (0 > arg || 4 < arg)
         return -EINVAL;

    printk(KERN_INFO"cmd:%u, ledNum:%ld\n", cmd, arg);
     switch(cmd){
         case led_on:
             s3c2410_gpio_setpin(led_IO_port[arg], 0);
             break;

         //case led_off:
         case 5:
             s3c2410_gpio_setpin(led_IO_port[arg], 1);
             break;

         case all_led_on:
             for (i = 0; i < 4; i++)
                 s3c2410_gpio_setpin(led_IO_port[i], 0);
             break;

         case all_led_off:
             for (i = 0; i < 4; i++)
                 s3c2410_gpio_setpin(led_IO_port[i], 1);
             break;

         default:
             return -EINVAL;
     }
     return 0;
 }

struct file_operations fops =
 {
     .owner = THIS_MODULE,
     .open  = myOpen,
     .ioctl = led_ioctl,
 };

 static int __init myDrive_init(void)
 {
     int ret, i;
     dev_t allocDevNum;

     printk(KERN_INFO"led init!\n");

     devNum = MKDEV(MAJOR_NUM, MINOR_NUM);//根据主设备号和次设备号得到设备号
     ret = register_chrdev_region(devNum, 1, DEV_NAME);// 静态申请设备号,设备号不变
     if (ret < 0){
         printk(KERN_INFO"register_chrdev_region error!\n");
         ret = alloc_chrdev_region(&allocDevNum, 0, 1, DEV_NAME);// 动态申请设备号,设备号是系统分配的
         if (ret < 0){
             printk(KERN_INFO"alloc_chrdev_region error!\n");
             return -EINVAL;
         }
         devNum = allocDevNum;
     }

     printk(KERN_INFO"devNum:%d, major:%d, minor:%d\n", devNum, MAJOR(devNum), MINOR(devNum));

     cdevp = cdev_alloc();
     cdev_init(cdevp, &fops);
     cdevp->owner = THIS_MODULE;

     ret = cdev_add(cdevp, devNum, 1);
     if (ret){
         printk(KERN_NOTICE"Error %d adding cdev", ret);
         return -EINVAL;
     }

     for (i = 0; i < 4; i++){// 初始化LED  GPBCON和熄灭所有灯
         s3c2410_gpio_cfgpin(led_IO_port[i], S3C2410_GPIO_OUTPUT);
         s3c2410_gpio_setpin(led_IO_port[i], 1);
     }
     return 0;
 }

 static void __exit myDrive_exit(void)
 {
     printk(KERN_INFO"myDrive exit!\n");
     cdev_del(cdevp);
     unregister_chrdev_region(devNum, 1);
 }
 module_init(myDrive_init);
 module_exit(myDrive_exit);
 MODULE_LICENSE("Dual BSD/GPL");

上面的代码都通过测试的,我用ioctl(fd, 2, n)总是报错误地址,所以在代码中我用 5 来替换 led_off(其实这是2)。其他的就没问题,当然这是这基本的一个代码,至于流水灯,跑马灯啊,有兴趣就自己去实现,这里就给个点灯的代码思路;

编译和测试

编译驱动有两种方法,可以参考上一篇blog:驱动的两种编译方法;我两种方法都实验过,我们可以先用动态加载,然后测试,最后确定好了,再把该驱动放到内核中;

首先把 .ko文件用nfs共享给开发板,开发板中insmod xx.ko,把驱动加载进内核,用命令 cat /proc/devices可以查看到加载的驱动,用lsmod也可以看到。当然我们要的是主设备号。

接下来就在 /dev/下创建一个设备文件,用来对应你的驱动。 mknod /dev/myLed  c  250  0;根据主设备号在/dev下创建个驱动对应的设备文件。我们测试的时候就是操作这个文件的;

下面看看测试代码:

#include<stdio.h>
 #include<stdlib.h>
 #include<fcntl.h>
 #include<errno.h>

 int main(int argc, char* argv[])
 {
     int flag, ledNum, ret;
     int fd = open("/dev/charDevice", O_RDWR);
     if (fd < 0){
         printf("open fild!\n");
         printf("errno:%d\n", errno);
         return -1;
     }   

     sscanf(argv[1], "%d", &flag);
     sscanf(argv[2], "%d", &ledNum);

     printf("fd:%d, argv[1]--flag:%d, argv[2]--ledNum:%d\n\n", fd, flag, ledNum);
     ret = ioctl(fd, flag, ledNum);
     if (ret < 0){
         printf("error in ioctl, errno:%d\n", errno);
     }
     return 0;
 }

可能有些多余的语句,那是我开始用来调试驱动的。这里有个errno比较好用,当出错时,可以查看下errno的错误码,方便调试;

当然也可以用write()函数来实现点灯操作,或者其他方法,总之都是大同小异。

转载请注明作者和原文出处,原文地址:http://blog.csdn.net/yuzhihui_no1/article/details/45116185

若有不正确之处,望大家指正,共同学习!谢谢!!!

时间: 2025-01-15 23:23:53

s3c2440 LED驱动分析的相关文章

基于S3C2440的linux-3.6.6移植——LED驱动【转】

本文转载自:http://www.voidcn.com/blog/lqxandroid2012/article/p-625005.html 目前的linux版本的许多驱动都是基于设备模型,LED也不例外. 简单地说,设备模型就是系统认为所有的设备都是挂接在总线上的,而要使设备工作,就需要相应的驱动.设备模型会产生一个虚拟的文件系统——sysfs,它给用户提供了一个从用户空间去访问内核设备的方法,它在linux里的路径是/sys.如果要写程序访问sysfs,可以像读写普通文件一样来操作/sys目录

linux2.6.30.4 s3c2440 platform总线 led驱动

1  basic 在设备驱动程序中经常会见到和platform相关的字段,分布在驱动程序的多个角落,这也是2.6内核中比较重要的一种机制,把它的原理弄懂了,对以后分析驱动程序很有帮助,下面简单介绍一下:    在linux2.6设备模型中,关心总线,设备,驱动这三个实体,总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动.相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的. 一个现实的Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PC

9x25 LED 驱动框架分析 2016.07.13

进入内核 make menuconfig 输入 /led 回车搜索到 │ Location: │ │ -> Device Drivers │ │ -> LED Support (NEW_LEDS [=y]) 进入LED Support发现有这一项 []LED Support for GPIO connected LEDs 在内核搜索该字符串 grep "LED Support for GPIO connected LEDs" * -nR 搜索到 drivers/leds/K

从Linux内核LED驱动来理解字符设备驱动开发流程

目录 博客说明 开发环境 1. Linux字符设备驱动的组成 1.1 字符设备驱动模块加载与卸载函数 1.2 字符设备驱动的file_operations 结构体中的成员函数 2. 字符设备驱动--设备号注册卸载 2.1 设备号注册 2.2 设备号注销 3. 字符设备驱动--文件操作 参考资料 示例代码 @(从Linux内核LED驱动来理解字符设备驱动开发流程) 博客说明 撰写日期 2018.12.08 完稿日期 2019.10.06 最近维护 暂无 本文作者 multimicro 联系方式 [

led驱动

驱动步骤: 1.驱动框架:一般读驱动代码需要module_init一层层找代码 2.硬件配置 代码中led_ioctl函数设置引脚的电平高低,该函数是驱动程序对设备的通道进行统一设置/控制的函数 一.  在用户空间,使用ioctl系统调用来控制设备,原型如下: int ioctl(int fd,unsigned long cmd,...); fd:文件描述符 cmd:控制命令 ...:可选参数:插入*argp,具体内容依赖于cmd用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这

linux串口驱动分析

linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作.UART 使用系统时钟能够支持最高 115.2Kbps 的波特率.每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO. 寄存器 名称 地址 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) UART 线性控制寄存器(ULCONn) ULC

LED驱动程序分析

混杂设备 LED驱动程序分析 /******************************* * *杂项设备驱动:miscdevice *majior=10; * * *****************************/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/types.h> #include <lin

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

S3C2440 LCD驱动(FrameBuffer)实例开发&lt;二&gt;(转)

开发板自带的LCD驱动是基于platform总线写的,所以如果要使其它的LCD能够在自己的开发板上跑起来,那么就先了解platform驱动的架构,下面简单记录下自己看platform驱动时体会,简单的说platform是一种虚拟总线,那么它也是一条总线,所以它分为3个部分,platform_bus,platform_device,platform_driver.在platform_device向platform_bus注册设备,platform_driver向platform_bus注册驱动,注