led驱动实例

前面分析了fl2440的led驱动代码,现在就基于之前的LED驱动代码完成应用程序(跑马灯)的实现,并且在linux系统下手动创建设备节点,运行跑马灯程序。


1.跑马灯程序

首先来看 用户空间下跑马灯程序的实现

#include <stdio.h>  
#include <sys/ioctl.h>  
#include <sys/stat.h>  
#include <unistd.h>  
#include <stdarg.h>  
#include <errno.h>  
#include <sys/types.h>  
#include <fcntl.h>  
#include <string.h>  
   
#define LED_NUM         4  
#define DEVNAME_LEN     10  
   
   
   
#define PLATDRV_MAGIC             0x60  
#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)  
#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)  
   
int main(int argc, char **argv)  
{  
        int fd[LED_NUM];  
        int     i;  
        int     j;  
        char devname[DEVNAME_LEN] = {0};  
   
        for(i = 0; i < LED_NUM; i++)  
        {  
                snprintf(devname, sizeof(devname), "/dev/led%d",i);  
                fd[i] = open(devname, O_RDWR,755);  
                if(fd[i] < 0)  
                {  
                        printf("Can not open %s: %s", devname, strerror(errno));  
                        for(j = 0; j < i; j++)  //如果某一设备不能打开,则要关掉之前已经打开的设备
                        {  
                                if(fd[j] > 0)  
                                        close(fd[j]);  
                        }  
                        return -1;  
                }  
        }  
   
        while(1)  
        {  
                for(i = 0; i < LED_NUM; i++)  
                {  
                        ioctl(fd[i], LED_ON);  //打开led灯
                        sleep(1);  //延时1s
                        ioctl(fd[i], LED_OFF);//关闭led灯  
                }  
        }  
   
        for(i = 0; i < LED_NUM; i++)  
        {  
                close(fd[i]);  //关掉设备文件
        }  
        return 0;  
}  

在 linux 下编写完跑马灯程序之后,我们必须使用交叉编译器对该程序进行编译。

[lwn@localhost s3c-led]$ /opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-gcc ledtest.c 
/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-gcc是我交叉编译器的目录下的交叉编译工具, 编译之后生成a.out可执行文件


2.led驱动程序:

/*********************************************************************************
 *      Copyright:  (C) 2011 Guo Wenxue<guowenxue@gmail.com>  
 *                  All rights reserved.
 *
 *       Filename:  plat_button.c
 *    Description:  This is the common button driver runs on S3C2440
 *                 
 *        Version:  1.0.0(4/27/2017~)
 *         Author:  Li Wanneng <liwjng@gmail.com>
 *      ChangeLog:  1, Release initial version on "4/27/2017 11:39:10 AM"
 *                 
 ********************************************************************************/

#include <linux/module.h>   /* Every Linux kernel module must include this head */
#include <linux/init.h>     /* Every Linux kernel module must include this head */
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* struct fops */
#include <linux/errno.h>    /* error codes */
#include <linux/cdev.h>     /* cdev_alloc()  */
#include <asm/io.h>         /* ioremap()  */
#include <linux/ioport.h>   /* request_mem_region() */

#include <asm/ioctl.h>      /* Linux kernel space head file for macro _IO() to generate ioctl command  */
#ifndef __KERNEL__
#include <sys/ioctl.h>      /* User space head file for macro _IO() to generate ioctl command */
#endif
//#include <linux/printk.h> /* Define log level KERN_DEBUG, no need include here */


#define DRV_AUTHOR                "Li Wanneng <liwjng@gmail.com>"
#define DRV_DESC                  "S3C24XX LED driver"

#define DEV_NAME                  "led"
#define LED_NUM                   4

/* Set the LED dev major number */
//#define LED_MAJOR                 79
#ifndef LED_MAJOR
#define LED_MAJOR                 0
#endif

#define DRV_MAJOR_VER             1
#define DRV_MINOR_VER             0
#define DRV_REVER_VER             0

#define DISABLE                   0
#define ENABLE                    1

#define GPIO_INPUT                0x00
#define GPIO_OUTPUT               0x01


/*防止ioctl传入的命令与其他参数冲突*/
#define PLATDRV_MAGIC             0x60 //魔术字
#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)

/*GPB有3个寄存器,GPBCON, GPBDATA和GPBUP寄存器,下面是其对应的物理地址*/
#define S3C_GPB_BASE              0x56000010//s3c2440GPB寄存器的基地址
#define GPBCON_OFFSET             0         //s3c2440GPBCON寄存器偏移地址0x56000010
#define GPBDAT_OFFSET             4         //s3c2440GPBDATA寄存器偏移地址 0x56000014
#define GPBUP_OFFSET              8         //s3c2440GPBUP寄存器的偏移地址 0x56000018
#define S3C_GPB_LEN               0x10        /* 0x56000010~0x56000020  */

int led[LED_NUM] = {5,6,8,10};  /* Four LEDs use GPB5,GPB6,GPB8,GPB10 */

static void __iomem *s3c_gpb_membase; //__iomem表示指针s3c_gpb_membase是指向一个I/O的内存空间


// #define _raw_readl(reg) *(volatile unsigned int *)(reg)
#define s3c_gpio_write(val, reg) __raw_writel((val), (reg)+s3c_gpb_membase) //虚拟地址与物理地址的映射(写)
#define s3c_gpio_read(reg)       __raw_readl((reg)+s3c_gpb_membase)    //虚拟地址与物理地址的映射(读)


int dev_count = ARRAY_SIZE(led); //设备个数
int dev_major = LED_MAJOR; //定义主设备号
int dev_minor = 0; //定义次设备号
int debug = DISABLE; //debug = 0,上面宏定义DISABLE为0

/*struct cdev 是表示字符设备的内核内部结构,当innod指向一个字符设备文件时,该字段包含了指向struct cdev 结构的指针*/
static struct cdev      *led_cdev;  //定义cdev结构体指针,led内核结构

static int s3c_hw_init(void)//硬件初始化,设置相应GPIO口为output模式
{
    int          i;
    volatile unsigned long  gpb_con, gpb_dat, gpb_up;
    
	/*为s3c2440_led申请一片物理地址,其起始地址为S3C_GPB_BASE,大小为s3c_GPB_LEN;如果申请失败,返回-EBUSY */
    if(!request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN, "s3c2440 led"))
    {
        return -EBUSY;
    }

	/*如果物理地址申请成功,将其映射到相应的虚拟地址。以后操作寄存器一律操作虚拟地址即s3c_gpb_membase的地址*/
    if( !(s3c_gpb_membase=ioremap(S3C_GPB_BASE, S3C_GPB_LEN)) )//ioremap函数作用为将物理地址映射到虚拟地址
    {
        release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);//如果映射失败,一定要将物理地址释放
        return -ENOMEM;//申请失败返回错误信息-ENOMEM
    }

	/*1,设置相应gpio端口为输出模式
	  2,disable上拉寄存器哦
	  3,设置gpio数据寄存器为1,即使相应GPIO端口输出高电平,默认关闭LED灯
	 */
    for(i=0; i<dev_count; i++)//dev_count为设备数量 
    {
        /* Set GPBCON register, set correspond GPIO port as input or output mode  */
        gpb_con = s3c_gpio_read(GPBCON_OFFSET);//获取GPBCON寄存器的值
        gpb_con &= ~(0x3<<(2*led[i]));   /* Clear the currespond LED GPIO configure register */
        gpb_con |= GPIO_OUTPUT<<(2*led[i]); /* Set the currespond LED GPIO as output mode */
        s3c_gpio_write(gpb_con, GPBCON_OFFSET);//将gpb_con的值写到寄存器GPBCON寄存器

        /* Set GPBUP register, set correspond GPIO port pull up resister as enable or disable  */
        gpb_up = s3c_gpio_read(GPBUP_OFFSET);
       
        gpb_up |= (0x1<<led[i]);  /* Disable pull up resister */
        s3c_gpio_write(gpb_up, GPBUP_OFFSET);

        /* Set GPBDAT register, set correspond GPIO port power level as high level or low level */
        gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
        //gpb_dat &= ~(0x1<<led[i]); /* This port set to low level, then turn LED on */
        gpb_dat |= (0x1<<led[i]);  /* This port set to high level, then turn LED off */
        s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);//1:46
    }

    return 0;
}


static void turn_led(int which, unsigned int cmd)//操作led
{
    volatile unsigned long  gpb_dat;

    gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);

    if(LED_ON == cmd)
    {
        gpb_dat &= ~(0x1<<led[which]); /*  Turn LED On */
    }
    else if(LED_OFF == cmd)
    {
        gpb_dat |= (0x1<<led[which]);  /*  Turn LED off */
    }

    s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
}

static void s3c_hw_term(void)//清除led操作
{
    int                     i;
    volatile unsigned long  gpb_dat;

    for(i=0; i<dev_count; i++)
    {
        gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
        gpb_dat |= (0x1<<led[i]);  /* Turn LED off */
        s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
    }

    release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);//释放已申请的物理内存
    iounmap(s3c_gpb_membase);//取消物理地址到虚拟地址的映射关系
}


static int led_open(struct inode *inode, struct file *file)//"打开led"函数
{
    int minor = iminor(inode);//获取次设备号

    file->private_data = (void *)minor;//在file结构体中,private_data是一个空类型指针

    printk(KERN_DEBUG "/dev/led%d opened.\n", minor);
    return 0;
}

static int led_release(struct inode *inode, struct file *file)//"关闭led"函数
{
    printk(KERN_DEBUG "/dev/led%d closed.\n", iminor(inode));

    return 0;
}

static void print_help(void)//打印帮助信息
{
    printk("Follow is the ioctl() commands for %s driver:\n", DEV_NAME);
    //printk("Enable Driver debug command: %u\n", SET_DRV_DEBUG);
    printk("Turn LED on command  : %u\n", LED_ON);
    printk("Turn LED off command : %u\n", LED_OFF);

    return;
}


//ioctl(fd, LED_ON, 0)
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)//ioctl()函数会用到,其中参数为用户程序空间传过来的参数
{
    int which = (int)file->private_data;//获取次设备号

    switch (cmd)//判断并执行相应的命令
    {
        case LED_ON:

            turn_led(which, LED_ON);
            break;

        case LED_OFF:
            turn_led(which, LED_OFF);
            break;
    
        default:
            printk(KERN_ERR "%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);
            print_help();
            break;
    }

    return 0;
}


/*面向对象的思想*/
static struct file_operations led_fops = //定义fop结构体,针对该驱动提供的系统调用和操作
{
    .owner = THIS_MODULE,//.owener的值一般为THIS_MODULE
    .open = led_open,//open为指向led_open()的函数指针,调用open()函数时会用到
    .release = led_release,
    .unlocked_ioctl = led_ioctl,
};


static int __init s3c_led_init(void)//led初始化函数
{
    int                    result;
    dev_t                  devno;

	/*如果硬件初始化失败,打印如下信息并返回-ENODEV*/
    if( 0 != s3c_hw_init() )
    {
        printk(KERN_ERR "s3c2440 LED hardware initialize failure.\n");
        return -ENODEV;
    }

    /* 如果硬件初始化成功,分配主次设备号 */
    if (0 != dev_major) /* 如果已经有了设备号 静态获取主次设备号 */
    {
        devno = MKDEV(dev_major, 0);
        result = register_chrdev_region (devno, dev_count, DEV_NAME);
    }
    else//动态获取主次设备号
    {
        result = alloc_chrdev_region(&devno, dev_minor, dev_count, DEV_NAME);
        dev_major = MAJOR(devno);
    }

    /*  如果设备号申请失败,打印如下信息并返回-ENODEV */
    if (result < 0)
    {
        printk(KERN_ERR "S3C %s driver can't use major %d\n", DEV_NAME, dev_major);
        return -ENODEV;
    } 
    printk(KERN_DEBUG "S3C %s driver use major %d\n", DEV_NAME, dev_major);

    if(NULL == (led_cdev=cdev_alloc()) )//分配cdev结构体,如果分配失败,打印如下信息	
    {
        printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
        unregister_chrdev_region(devno, dev_count);
        return -ENOMEM;
    }
    
    led_cdev->owner = THIS_MODULE;//如果分配成功,将cdev结构体添加进内核 
    cdev_init(led_cdev, &led_fops);//连接led_cdev和led_fops
 
    /*只有当cdev_add()函数执行之后,才能在 /dev/目录下看到设备节点*/
    result = cdev_add(led_cdev, devno, dev_count);//将其添加进内核,字符设备注册的最后一步
    if (0 != result)
    {   
        printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result); 
        goto ERROR;
    }

            
    printk(KERN_ERR "S3C %s driver[major=%d] version %d.%d.%d installed successfully!\n", 
            DEV_NAME, dev_major, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);
    return 0;


ERROR:
    printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);
    cdev_del(led_cdev);
    unregister_chrdev_region(devno, dev_count);
    return result;
}

static void __exit s3c_led_exit(void)//led卸载函数
{
    dev_t devno = MKDEV(dev_major, dev_minor);

    s3c_hw_term();//释放。。。

    cdev_del(led_cdev);//删掉cdev
    unregister_chrdev_region(devno, dev_count);//释放设备节点号

    printk(KERN_ERR "S3C %s driver version %d.%d.%d removed!\n", 
            DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);

    return ;
}



/* These two functions defined in <linux/init.h> */
module_init(s3c_led_init);//Led驱动首先应该找到module_init函数
module_exit(s3c_led_exit);

module_param(debug, int, S_IRUGO);//使能驱动的debug功能,debug为全局变量
module_param(dev_major, int, S_IRUGO);//函数传参,insmod时通过参数动态修改主设备号

MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");


3.Makefile

驱动程序通过Makefile编译,Makefile主要内容如下

[lwn@localhost s3c-led]vim Makefile
 Makefile                                                                                                                            
  1 LINUX_SRC?=../../kernel/linux-lwn-3.0.1
  2 CROSS_COMPILE=/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-
  3 
  4 obj-m := s3c_led.o
  5 
  6 modules:
  7     @make -C $(LINUX_SRC) M=`pwd` modules
  8     @make clean
  9 
 10 clean:
 11     rm -f  *.ko.* *.o *mod.c *.order *.symvers
 12     
                                                     


编写完Makefile之后使用make命令生成 sec_led.ko二进制文件

[lwn@localhost s3c-led]$ ls
a.out  ledtest.c  Makefile  s3c_led.c  s3c_led.ko

4.开发板上面运行程序

在主机上面生成了a.out和s3c_led.ko之后,使用 tftp 工具将其下载到开发板。


现在开发板上只是有了s3c_led.ko文件,但是如果要使用驱动,还需要安装驱动,创建设备节点,运行应用程序

使用insmod命令安装驱动

下面是在开发板上的操作


执行a.out之后在开发板上可以看到led已经跑起来了。到此,整个LED驱动和用户程序已经成功执行。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HD2010操作说明(HD2010 operating instructions) ⅰ.软件安装(Installed software) 双击光盘HD2010 V2.0目录下的安装文件图标 ,将控制系统安装到个人电脑.如下图2-1 (Open Profile HD2010 V2.0 in the compact disk,Dblclick Installed the HD2010 V2.0 in your personal computer.As shown in Figure 2-1) 图(Figure)2-1 点击确定后进入下一步如图2-2 (Click确定intro- next step.As shown in Figure 2-2 ) 图(Figure)2-2 点击下一步后进入下一步(英文环境)在中文环境直接点击下一步完成安装)如图2-3 2-4 2-5 2-6 2-7 (Click next intro- next step.As shown in Figure 2-3 2-4 2-5 2-6 2-7) 图(Figure)2-3 注意:发送的文件保存在安装目录下的ProjFile文件夹里,点击下一步完成安装 (Announcements:The file what you send saved in the ProjFile in path of Install.And click next to install and finish) 图(Figure)2-4 图(Figure)2-5 图(Figure)2-6 安装最后一步如图2-7 (The final step of install. As shown in Figure 2-7) 图(Figure)2-7 点击完成打开软件主界面如图2-8 (Click finish open main interface of the software. As shown in Figure 2-8) ⅱ.软件设置(Setting software) ⅱ.ⅰ主界面如图2-8(Main interface.As shown in Figure 2-8) 图(Figure)2-8 ⅱ.ⅱ软件属性(Software properties) 1.文件菜单(File menu) a.新建--新建一个新的显示屏(New—Create a new screen) b.打开--打开一个显示屏(Open—Open a exist screen) c.保存--保存建立的文件(Save—Save file) d.另存为--保存副本(Save as—save a copy) e.导出.hds--导出.hds文件,用于u盘读取文件 (Export.hds—Export.hds file,for usb reading) f.退出--退出软件(Exit—Exit the software) 2.设置菜单(Settings menu) a.屏参设置--设置显示屏属性 (Screen settings—Display Screen configuration attributes) b.通信设置--通信端口/方式设置 (Communication settings—Set Communication prot and fashion) c.系统设置--配置系统默认项 (System settings—Set the acquice properties of software) 3.操作菜单(Operate menu) a.发送项目--发送节目(Send project—send the programmes) b.导出到U盘--把节目导入到U盘 (Export to U disk—send the programme to your U disk) c.时间设置--时间校对 (Time setting—get the right time im your computer) d.亮度设置--选择亮度模式 (Luminance setting—Choice the module of Luminance) e.固件更新--固件升级(Update Firmware—Update your controller Firmware) 4.语言菜单(Language menu) a.简体中文(Simplified Chinese) b.繁体中文(Chinese Traditional) c.英语(English) ⅱ.ⅲ节目编辑(Edit

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值