TQ2440在linux下的LED驱动程序

        学习驱动程序不久,看LDD3大概有4章吧,最开始写了个hello world驱动,后来是个面向内存的字符设备驱动,后者让我清楚了量子和量子集的使用,但是没有个真正的,肉眼看得见的设备真是有点不爽,查了些资料,参考了下其他书,打算自己写个LED的linux驱动,学了字符设备驱动的话写这个其实挺简单的。

        带系统的驱动跟裸机的驱动可不一样啊,裸机的驱动像单片机那种,你只要把端口设0或者设1就可以控制那些设备,多简单,与带操作系统的驱动程序不同。第一,单片机驱动控制的地址是物理地址,而带操作系统的驱动控制的是虚拟地址,这个在linux上可以很方便的用ioremap函数把物理地址映射为虚拟地址,这个函数在<asm/io.h>声明;第二,带操作系统的驱动,是在内核空间工作的,你要提供API给用户,用户用这些接口来写他们的应用程序,他们不会关心底层是怎么运行的,好比餐厅里的,操作系统的驱动是厨师,应用程序是顾客,这样说可能明白很多。

        看下图,是linux的应用程序调用的基本框架,上部黄色的是用户空间,下部蓝色的是内核空间。从上往下,程序员在最上层写程序,操作一些文件,这些文件可以是普通的文件,也可能是设备文件等等,这些操作例如open、read、write、ioctl等,这些操作,到了C库这一层,C库会产生一个swi val中断或者说异常,这个异常就会使应用程序从用户空间进入到内核空间,下面一层叫System all interface,系统调用接口,这一层会调用下面的虚拟文件系统VFS(Virtual File System)的sys_open、sys_write这些函数,这些函数就会根据不同的文件不同的属性,设备文件中还会根据设备号,来寻找特定的驱动程序,这些驱动程序就是我们要写的,里面带led_open、led_write这些函数。

        TQ2440在linux下的LED驱动程序_第1张图片

这些虚拟文件系统函数sys_open等是怎么找到我们的驱动程序里的led_open等的呢?其实在虚拟文件系统中有我们的很多种类的文件,普通文件、目录文件、设备文件等,就设备文件中的字符设备举例,它会建立一个字符设备的数组,这些数组以主设备号为下标,貌似从0到255,这些数组里面装的是一些file_operations结构指针,这些file_operations是在驱动程序里面定义的,而file_operations结构里面就包含了我们的驱动的操作函数led_open、led_write等,数组成员不同,file_operations结构就不同,驱动程序就不同,操作的设备就不同......大概是这样

TQ2440在linux下的LED驱动程序_第2张图片

上面是为了帮助大家理清思路,大牛无视就行了,

下面正题了,简单的在linux下的led驱动程序。

开发平台:

ubuntu10.04

测试平台:

TQ2440,操作系统为linux,内核版本linux-2.6.30.4

//leds.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/kernel.h>
#include <asm/io.h>

#define GPBOUT  (1<<(5*2)) |(1<<(6*2)) | (1<<(7*2)) | (1<<(8*2));               //设置GPB5/6/7/8为输出


int major = 0;
struct cdev* leds_cdev;
volatile unsigned long *GPBCON = NULL;
volatile unsigned long *GPBDAT = NULL;


static int  leds_open(struct inode *inode, struct file *filp)
{
    *GPBCON = 0;                                        //控制端口清零
    *GPBCON = GPBOUT;
    return 0;
}

static int leds_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg)
{
    if (arg > 4)
    {
        printk("deyond the led no\n");
    }
    
    switch (cmd)
    {
        case 1:
            *GPBDAT |= (1<<(arg + 4));                      //灭灯
            break;
        case 0:
            *GPBDAT &= ~(1<<(arg + 4));                 //亮灯    
            break;
        default:
            printk("cmd error\n");
            break;
    }
    return 0;
}


struct file_operations leds_fops = 
{
    .owner = THIS_MODULE,
    .open = leds_open,
    .ioctl = leds_ioctl,
};

static int __init leds_init(void)
{
    dev_t dev;
    leds_cdev = cdev_alloc();
    
    alloc_chrdev_region(&dev, 0, 1, "leds");

    major = MAJOR(dev);

    leds_cdev->ops = &leds_fops;

    cdev_init(&leds_cdev, &leds_fops);

    cdev_add(&leds_cdev, dev, 1);

    GPBCON = (volatile unsigned int *)ioremap(0x56000010, 16);                  //将物理地址映射到虚拟地址,GPBCON物理地址为0x56000010
    GPBDAT = GPBCON + 1;                                                                          //GPBDAT物理地址为0x56000014

    return 0;
}

static void __exit leds_exit(void)
{
    dev_t dev;
    dev = MKDEV(major, 0);
    cdev_del(&leds_cdev);
    unregister_chrdev_region(dev, 1);
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("不做超哥已多年");
MODULE_DESCRIPTION("leds");
测试程序:

//leds_test.c
#include <stdio.h>
#include <linux/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define ON 0
#define OFF 1
int main(int argc, char* argv[])
{
    int led_no;
    if (argc != 3)
    {
        printf("Usage: %s <led_no> <ON/OFF>\n", argv[0]);
        exit(0);
    }
    int fd;
    fd = open("/dev/leds", O_RDWR);
    if (fd < 0)
    {
        printf("open error\n");
        exit(0);
    }

    led_no = strtoul(argv[1], 0, 0);          //将字符串转换成无符号长整型数
    
    if (!strcmp(argv[2], "ON"))
        ioctl(fd, ON, led_no);
    else if (!strcmp(argv[2], "OFF"))
        ioctl(fd, OFF, led_no);
    else 
        exit(0);
        
    return 0;
}
编译驱动模块,在arm板上加载,交叉编译测试程序,在板子上运行成功

命令是./leds_test led_no ON/OFF 如 ./led_test 1 ON就是点亮led1灯

点亮四个灯后效果:

TQ2440在linux下的LED驱动程序_第3张图片

看到可以控制灯亮灭了,你是否想起了当初玩单片机时点led灯时的兴奋呢......哈哈哈哈,好兴奋啊

你可能感兴趣的:(TQ2440在linux下的LED驱动程序)