s3c2440-led驱动分析注释及一次跑马灯测试程序

嵌入式linux驱动与软件详解之二 button驱动与程序 前天 昨天详细介绍了LED驱动程序已经调用驱动的方法,希望对大家有所帮助,可是在实际的开发中,需要我们去做的一般是对于驱动的调用,更深层次的驱动设计工程师也仅仅是根据内核及硬件内容编写驱动,还到不了修改内核的程度。所以昨天之所以深入的探讨一下,其主要目的是为了让大家可以了解这种逐次深入的学习方法,这种学习方法对于一般的语言都是适用的。这几天一直在配置QT4的嵌入式开发环境,现在还在编译当中呢,QT 阅读详情
首先要明白字符设备驱动注册的基本流程:
当我们调用insomd命令加载驱动后,驱动程序从module_init函数开始执行:硬件初始化 -> 申请主次设备号 -> 定义fops(file_operations)结构体 -> 申请cdev结构体并把fops结构体嵌入cdev结构体中与之绑定 -> cdev字符设备的注册。



有一点需要明确的是,在Linux内核中,所有的设备都是以文件。我们对设备的操作即是对Linux内核中文件的操作。设备都在/dev目录下。而inode则是设备索引节点,每个文件产生后都会有相应的inode来标示。
驱动加载完成后,file_operations结构体中的成员可以为应用程序提供对设备进行各种操作的函数指针:open,read,write等。
cdev结构体是用来描述字符设备的,内核中每个字符设备都对应一个 cdev 结构的变量,下面是它的定义:
1 linux-2.6.22/include/linux/cdev.h
2 struct cdev {
3    struct kobject kobj;          // 每个 cdev 都是一个 kobject
4    struct module *owner;       // 指向实现驱动的模块
5    const struct file_operations *ops;   // 操纵这个字符设备文件的方法
6    struct list_head list;       // 与 cdev 对应的字符设备文件的 inode->i_devices 的链表头
7    dev_t dev;                   // 起始设备编号
8    unsigned int count;       // 设备范围号大小
9 };


一个 cdev 一般它有两种定义初始化方式:静态的和动态的。
静态内存定义初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
动态内存定义初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;
两种使用方式的功能是一样的,只是使用的内存区不一样,一般视实际的数据结构需求而定。

s3c_led驱动代码:



//http://blog.youkuaiyun.com/dreaming_my_dreams/article/details/7745148,部分头文件简介及路径分析
 
#include <linux/module.h>   /* Every Linux kernel module must include this head 写内核驱动的时候 必须加载这个头文件,作用是动态的将模块加载到内核中去
*常用的宏定义如 MODULE_LICESENCE(),MODULE_AUTHOR(),等在此文件中,而且 kobject,kset结构体题及其操作函数也在这个结构体中
*http://blog.youkuaiyun.com/kokodudu/article/details/17358001/ 源代码 */
#include <linux/init.h>     /* Every Linux kernel module must include this head  ,在init.h头文件中包含了模块的初始化的宏定义 以及一些其他函数的初始化函数 
http://blog.youkuaiyun.com/kokodudu/article/details/17361161源代码 */
#include <linux/kernel.h>   /* printk()  包含了内核打印函数 printk函数 等,驱动要写入内核,与内核相关的头文件 */
#include <linux/fs.h>       /* struct fops  file_operations 包含了文件操作相关struct的定义 */
#include <linux/errno.h>    /* error codes 包含了对返回值的宏定义,这样用户程序可以用perror输出错误信息。*/
#include <linux/cdev.h>     /* cdev_alloc() 对字符设备结构cdev以及一系列的操作函数的定义,包含了cdev 结构及相关函数的定义。
详细了解cdev,主次设备号。http://blog.youkuaiyun.com/zqixiao_09/article/details/50839042 */
#include <asm/io.h>         /*【 ioremap()将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问 I/O头文件】,该头文件以宏的嵌入汇编程序形式定义对I/O端口操作的函数 */
#include <linux/ioport.h>   /* request_mem_region() Linux设计了一个通用的数据结构resource来描述各种I/O资源(如:I/O端口、外设内存、DMA和IRQ等)。该结构定义在include/linux/ioport.h头文件中。
http://blog.youkuaiyun.com/skyflying2012/article/details/8672011
http://blog.youkuaiyun.com/os_boy/article/details/12164009*/


#include <asm/ioctl.h>      /* Linux kernel space head file for macro _IO() to generate ioctl command  不是特别懂,只是觉得和io端口有关系
ioctl方法详解 http://blog.youkuaiyun.com/huiguixian/article/details/24410885 */
#ifndef __KERNEL__
#include <sys/ioctl.h>      /* User space head file for macro _IO() to generate ioctl command 
 ioctl()是I/O操作的杂货箱,很多事情都要依靠它来完成。除了摄像头,视频采集卡之类的与video相关的操作其实也都与此类似*/
#endif
//#include <linux/printk.h> /* Define log level KERN_DEBUG, no need include here  可以用于内核调试http://blog.youkuaiyun.com/itsenlin/article/details/43205983*/




#define DRV_AUTHOR                "fanmaolin <fanmaolinn@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   程序中出现 LED_MAJOR 代表79这个数值  下面类似
#ifndef LED_MAJOR
#define LED_MAJOR                 0 // 0 定义默认的主设备号为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 //宏定义 GPIO输入模式用00代替
#define GPIO_OUTPUT               0x01 //宏定义 GPIO输出模式用01代替




#define PLATDRV_MAGIC             0x60 //定义了一个魔数,关于魔数的分析http://blog.youkuaiyun.com/leechar_linux/article/details/39545235
#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18) 
#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)    /* 魔术字有着特殊的功能,定义一个系统未用的魔术字,然后让魔术字生成我们定义的LED_OFF与LED_ON,
这样我们的定义就不会和系统中别的宏定义相同了 */
#define S3C_GPB_BASE              0x56000010 //定义GPB管脚控制寄存器的基址地址
#define GPBCON_OFFSET             0 //定义GPBCON的偏移地址,用来选定引脚并设置输入输出模式   这里应该是输入
#define GPBDAT_OFFSET             4 //定义GPBDAT偏移地址(GPBDAT用于读写引脚数据。输入模式时,读此寄存器可知相应引脚的电平是低还是高;输出模式写此引脚可设置输出高低电平)
#define GPBUP_OFFSET              8 // 定义GPBUP偏移地址  


/*GPBUP的某位为1时,相应引脚无内部上拉电阻;为0时相应引脚使用内部上拉电阻。
 上拉电阻的作用是当GPIO引脚出去第三态时,即既不是输出高电平也不是输出低电平,
 而是呈现高阻态,相当于没有接芯片。它的电平状态由上拉电阻下拉电阻决定。*/


#define S3C_GPB_LEN               0x10        /* 0x56000010~0x56000020  GPB寄存器的内存地址总长度*/


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


static void __iomem *s3c_gpb_membase;
//定义s3c_gpb_membase为void __iomem*类型指针 ,在后面用来保存映射后的虚拟空间基地址。Iomem在linux下的作用 http://blog.youkuaiyun.com/gongmin856/article/details/8470540


#define s3c_gpio_write(val, reg) __raw_writel((val), (reg)+s3c_gpb_membase)   //将val的值写入reg地址中
#define s3c_gpio_read(reg)       __raw_readl((reg)+s3c_gpb_membase)     //读取当前虚拟地址寄存器的值




int dev_count = ARRAY_SIZE(led);  //将设备体中设备的个数赋值给dev_count  43、44行
int dev_major = LED_MAJOR;  //主设备号赋值给dev_major
int dev_minor = 0; //次设备号赋值为0
int debug = DISABLE; //出错定义赋值


static struct cdev      *led_cdev; //定义一个cdev类型的结构体指针,cdev是内核中表示字符设备的一个结构体,见23行  


static int s3c_hw_init(void) //硬件初始化函数
{
    int          i;
    volatile unsigned long  gpb_con, gpb_dat, gpb_up;;//分别定义gob_con ;gpb_dat ;gpb_up变量,因为是保存寄存器地址的所以要用volatile类型,防止被优化。  
/*#define     __IO    volatile 的作用就是指示编译器不要因优化而省略此指令,必须每次都直接读写其值。
volatile 详细作用 http://blog.youkuaiyun.com/daa20/article/details/42339695*/
    if(!request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN, "s3c2440 led"))//申请一段IO内存空间  见26行

/*#define request_mem_region(start,n,name)   __request_region(&iomem_resource, (start), (n), (name))  
其中,参数start是I/O内存资源的起始物理地址(是CPU在RAM物理地址空间中的物理地址),  
 参数n指定I/O内存资源的大小。在请求IO内存资源成功后,开始用ioremap进行映射操作.*/

    {
        return -EBUSY;
    }
//判断是否申请失败,如果硬件资源被占据着则返回-EBUSY.成功则往下执行
    if( !(s3c_gpb_membase=ioremap(S3C_GPB_BASE, S3C_GPB_LEN)) )   //ioremap(参考25行)用来将IO资源的物理地址映射到内核虚拟地址空间保存到s3c_gpb_membase                                    
    {
        release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);//映射失败则释放申请的IO内存空间
        return -ENOMEM; //并返回-ENOMEM
    }
//内存申请成功且地址映射成功则进行对各个寄存器的操作 
    for(i=0; i<dev_count; i++)
    {
        /* Set GPBCON register, set correspond GPIO port as input or output mode  */
        gpb_con = s3c_gpio_read(GPBCON_OFFSET);   //读取GPBCON的虚拟地址(在63行)并保存在gpb_con中
        gpb_con &= ~(0x3<<(2*led[i]));   /* Clear the currespond LED GPIO configure register  先将相应的led的gpio引脚复位清零*/
        gpb_con |= GPIO_OUTPUT<<(2*led[i]); /* Set the currespond LED GPIO as output mode 然后将相应的led的gpio引脚设置为输出模式*/
        s3c_gpio_write(gpb_con, GPBCON_OFFSET); //再将设置好的GPBCON引脚信息写入寄存器中  


        /* Set GPBUP register, set correspond GPIO port pull up resister as enable or disable  */
        gpb_up = s3c_gpio_read(GPBUP_OFFSET); //读取GPBUP的虚拟地址并保存在gpb_up中 
        //gpb_up &= ~(0x1<<led[i]); /* Enable pull up resister  使能相应led的gpbio引脚的上拉电位 */
        gpb_up |= (0x1<<led[i]);  /* Disable pull up resister 禁用相应led的gpbio引脚的上拉电位*/
        s3c_gpio_write(gpb_up, GPBUP_OFFSET);   //将设置好的GPBUP引脚信息写入寄存器中


        /* Set GPBDAT register, set correspond GPIO port power level as high level or low level */
        gpb_dat = s3c_gpio_read(GPBDAT_OFFSET); //读取GPBDAT的虚拟地址并保存在gpb_dat中
        //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); //将设置好的GPBDAT引脚信息写入寄存器中
    }


    return 0;
}




static void turn_led(int which, unsigned int cmd) //由ioctl调用,控制单个LED的亮灭
{
    volatile unsigned long  gpb_dat; //参考97行


    gpb_dat = s3c_gpio_read(GPBDAT_OFFSET); //读取GPBDAT的虚拟地址并保存在gpb_dat中 


    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); //将设置好的GPBDAT引脚信息写入寄存器中
}


static void s3c_hw_term(void) //调用LED结束后释放内存所占用的资源及设置相应的GPIO引脚关闭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);
    }
//设置相应的GPBDAT引脚信息,使LED不通
    release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN); //释放指定的IO内存资源
    iounmap(s3c_gpb_membase); //iounmap函数用于取消ioremap()所做的映射
}




static int led_open(struct inode *inode, struct file *file)//驱动功能函数open
{
    int minor = iminor(inode); //通过索引节点获取次设备号


    file->private_data = (void *)minor; //将次设备号保存到private_data中,方便在函数中调用,private_data在系统调用期间保存各种状态信息


    printk(KERN_DEBUG "/dev/led%d opened.\n", minor);//打印成功信息
    return 0;
}


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


    return 0;
}
/*内核中用inode结构表示具体的文件,而用file结构表示打开的文件描述符。  
struct file代表一个打开的文件,在执行file_operation中的open操作时被创建,  
这里需要注意的是与用户空间file指针的区别,一个在内核,而file指针在用户空间,由c库来定义。  
struct inode被内核用来代表一个文件,注意和struct file的区别,struct inode一个是代表文件,struct file一个是代表打开的文件*/
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;
}

static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) //led 中断控制函数  
{
    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;
//如果传进来的既不是ON也不是OFF则打印出错并打印HELP结构体
        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 = //结构体file_operations在头文件 linux/fs.h中定义,用来存储驱动内核模块提供的对设备进行各种操作的函数的指针
{
    .owner = THIS_MODULE, // 指向拥有该结构体模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES  
    .open = led_open, //传递led_open函数打开设备
    .release = led_release, //传递led_release函数关闭设备
    .unlocked_ioctl = led_ioctl, //传递中断函数,不使用BLK的文件系统,将使用此种函数的指针代替ioctl
};


static int __init s3c_led_init(void) //传递中断函数,不使用BLK的文件系统,将使用此种函数的指针代替ioctl
{
    int                    result;
    dev_t                  devno;


    if( 0 != s3c_hw_init() ) //初始化led硬件并判断是否初始化成功
    {
        printk(KERN_ERR "s3c2440 LED hardware initialize failure.\n");
        return -ENODEV;
    }


    /*  Alloc the device for driver */
    if (0 != dev_major) /*  Static 已知主设备号,静态获得设备编号并注册 */
    {
        devno = MKDEV(dev_major, 0); //通过主设备号与次设备号构建32位设备号
        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); //根据设备号devno获得主设备号
    }


    /*  Alloc for device major failure */
    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结构体,一个字符设备用一个cdev结构体来描述
    {
        printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
        unregister_chrdev_region(devno, dev_count);
        return -ENOMEM;
    }
    //cdev_alloc它主要完成了空间的申请和简单的初始化操作,分配一个cdev(在内核中代表字符设备),如果失败了则打印出错并释放分配的设备号 
    led_cdev->owner = THIS_MODULE; //指明设备所属模块,这里永远指向THIS_MODULE
    cdev_init(led_cdev, &led_fops); //初始化cdev的file_operations,使字符设备与操作的函数绑定,led_fops结构体中的成员指向驱动提供的功能函数   


    result = cdev_add(led_cdev, devno, dev_count); //cdev_add为注册设备函数,通常发生在驱动模块的加载函数中并将返回值传递给result,为0则表示成功。
    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) //卸载驱动模块函数
{
    dev_t devno = MKDEV(dev_major, dev_minor);


    s3c_hw_term();


    cdev_del(led_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); //insmod加载内核模块定义的宏
module_exit(s3c_led_exit); //rmmod卸载模块定义的宏,退出内核模块


module_param(debug, int, S_IRUGO); /*传递命令行参数module_param使用了3个参数:变量名,它的类型,以及一个权限掩码用来做一个辅助的sysfs入口。 
这个宏定义应当放在任何函数之外,典型地是出现在源文件的前面*/
module_param(dev_major, int, S_IRUGO);


MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC); // 对这个模块作一个简单的描述,这个描述是"human-readable"的
MODULE_LICENSE("GPL"); // "GPL" 是指明了 这是GNU General Public License的任意版本    http://blog.youkuaiyun.com/lihaoweiv/article/details/6602261


vim Makefile 

 Makefile                                                                                                                           
  1 C=/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc
  2 KDIR?=/home/fanmaolin/fl2440/kernel/linux-3.0
  3 obj-m:=s3c_led.o
  4 
  5 default:  
  6     $(MAKE) -C $(KDIR) M=`pwd` modules  
  7     make clean  
  8 
  9 clean:  
 10     rm -f *.o *mod.c *.order *.symvers 
~                                                                                                                                   
~                                                                                                                                   
~                                                                                                                                   
~                          

  

make

生成.ko文件


编写测试程序(一次跑马灯):


vi testled.c

1 /*********************************************************************************
  2  *      Copyright:  (C) 2017 fanmaolin<fanmaolinn@gmail.com>
  3  *                  All rights reserved.
  4  *
  5  *       Filename:  testled.c
  6  *    Description:  This file  test led driver
  7  *                 
  8  *        Version:  1.0.0(03/25/2017)
  9  *         Author:  fanmaolin <fanmaolinn@gmail.com>
 10  *      ChangeLog:  1, Release initial version on "03/25/2017 09:36:47 AM"
 11  *                 
 12  ********************************************************************************/
 13 #include <sys/ioctl.h>
 14 #include <sys/stat.h>
 15 #include <unistd.h>
 16 #include <stdarg.h>
 17 #include <errno.h> //自己这些头文件要看自己用哪些函数,用man命令查看
 18 #include <sys/types.h>
 19 #include <fcntl.h>
 20 #include <string.h>
 21 #include <stdio.h>
 22
 23 #define LED_NUM         4 //
1 /*********************************************************************************
  2  *      Copyright:  (C) 2017 fanmaolin<fanmaolinn@gmail.com>
  3  *                  All rights reserved.
  4  *
  5  *       Filename:  testled.c
  6  *    Description:  This file  test led driver
  7  *                 
  8  *        Version:  1.0.0(03/25/2017)
  9  *         Author:  fanmaolin <fanmaolinn@gmail.com>
 10  *      ChangeLog:  1, Release initial version on "03/25/2017 09:36:47 AM"
 11  *                 
 12  ********************************************************************************/
 13 #include <sys/ioctl.h>
 14 #include <sys/stat.h>
 15 #include <unistd.h>
 16 #include <stdarg.h>
 17 #include <errno.h>
 18 #include <sys/types.h>
 19 #include <fcntl.h>
 20 #include <string.h>
 21 #include <stdio.h>
 22
 23 #define LED_NUM         4 //定义LED_NUM为4 
 24 #define DEVNAME_LEN     10  //定义DEVNAME_LEN  为10   这样做是方便以后修改这些参数时不用在代码里一个个修改,可以直接在定义这里修改
 25
 26 #define PLATDRV_MAGIC             0x60  //魔数,避免冲突
 27 #define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18) 
 28 #define LED_ON                    _IO (PLATDRV_MAGIC, 0x19) 
 29
 30 int main(int argc , char **argv)
 31 {
 32     int fd[LED_NUM];
 33     int i;
 34     int j;
 35
 36     char devname[DEVNAME_LEN]={0}; //字符串数组为0
 37
 38     for(i=0;i<LED_NUM;i++)
 39     {
 40            snprintf(devname, sizeof(devname), "/dev/led%i",i);//snprint函数,打印设备名
 41            fd[i]= open(devname, O_RDWR,755);//依次打开设备,并设置为读写模式,给权限755,r为4,w为2,x为1
 42            if(fd< 0)                                                                                             //如果出错,打印出错信息,并释放占用的空间,返回-1,                         
 43             {
 44                  printf("Can not open %s: %s", devname, strerror(errno));
 45                 for(j=0;j<LED_NUM;j++)
 46                 {
 47                      close(fd[j]);
 48
 49                 }
 50                 return -1;
 51             }
 52     }
 53
 54     for(i=0;i<LED_NUM;i++) //循环一次,一次灯亮,如果想一直循环下去加一个while语句就可以,条件设置为1
 55     {
 56              ioctl(fd[i], LED_ON);
 57              sleep(1); //延时1s
 58              ioctl(fd[i], LED_OFF);
 59
 60     }
 61
 62     for(i=0;i<LED_NUM;i++) //释放空间
 63     {
 64             close(fd[i]);
 65
 66     }
 67
 68         return 0;
 69
 70
 71 }



[fanmaolin@Centeros led]$ /opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-g
arm-linux-g++        arm-linux-gcc        arm-linux-gcc-4.5.4  arm-linux-gccbug     arm-linux-gcov       arm-linux-gprof
[fanmaolin@Centeros led]$ /opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc testled.c 
[fanmaolin@Centeros led]$ ls
1  a.out  led.c  Makefile  s3c_led.c  s3c_led.ko  testled.c
[fanmaolin@Centeros led]$ sz a.out 
rz
Starting zmodem transfer.  Press Ctrl+C to cancel.
Transferring a.out...
  100%       5 KB    5 KB/s 00:00:01       0 Errors

在开发板上执行:

: tftp -gr a.out 192.168.1.2
a.out                100% |*******************************|  5613   0:00:00 ETA
>: ls
a.out       data        info        linuxrc     root        sys         var
apps        dev         init        mnt         s3c_led.ko  tmp
bin         etc         lib         proc        sbin        usr
>: insmod s3c_led.ko 
S3C led driver[major=250] version 1.0.0 installed successfully!

>: mknod /dev/led0 c 250 0
>: mknod /dev/led1 c 250 1
>: mknod /dev/led2 c 250 2
>: mknod /dev/led3 c 250 3
>:  ls -l dev/led* 
crw-r--r--    1 root     root      250,   0 Dec 31 17:34 dev/led0
crw-r--r--    1 root     root      250,   1 Dec 31 17:34 dev/led1
crw-r--r--    1 root     root      250,   2 Dec 31 17:34 dev/led2
crw-r--r--    1 root     root      250,   3 Dec 31 17:35 dev/led3

>: ll a.out 
-rw-r--r--    1 root     root          5613 Dec 31 17:02 a.out
>: chmod 777 a.out 
>: ll a.out 
-rwxrwxrwx    1 root     root          5613 Dec 31 17:02 a.out
>: ./a.out 
>: ./a.out 
>: lsmod
s3c_led 2103 0 - Live 0xbf000000

就可以看到灯依次亮一次了。。。



问题总结分析:

1、

>: ./a.out
Can not open /dev/led0: No such device>: ./a.out


>: insmod s3c_led.ko 
S3C led driver[major=250] version 1.0.0 installed successfully!

注意这里的设备号,mknod后面的设备号修改后正常

要先用rm -rf filename 删除在/dev下之前创建的错误的节点


2、

在编程的时候要注意编程规范,还有关于snprintf()

int snprintf(char *restrict buf, size_t n, const char * restrict  format, ...);

函数说明:最多从源串中拷贝n-1个字符到目标串中,然后再在后面加一个0。所以如果目标串的大小为n 的话,将不会溢出。

函数返回值:若成功则返回欲写入的字符串长度,若出错则返回负值。



嵌入式Web开发:C语言CGI环境搭建与硬件控制实践 通用网关接口(CGI)作为一种标准协议,定义了Web服务器与外部程序间的通信机制,是实现动态Web内容的基础技术之一。其工作原理是Web服务器通过创建新进程并传递环境变量与标准输入数据,由CGI程序处理后返回HTML输出,完成一次完整的HTTP请求-响应循环。在嵌入式系统开发领域,C语言编写的CGI程序因其极致的轻量化、高效的执行性能以及直接操作硬件的能力而具有独特价值,尤其适合资源受限的MCU或ARM开发板等场景。通过直接读写内存映射寄存器或调用底层驱动,C语言CGI能够实现Web请求到硬件控制的最短路径 阅读详情

相关推荐

S3c2440裸机程序【1】跑马灯

由JZ2440v2原理图可知: nLED1--->GPF4,nLED2--->GPF5,nLED3--->GPF6 S3C2440芯片手册关于IO口配置: 程序代码: #define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned long *)0x56

tiandawangliang的专栏 2717

s3c2440- led驱动分析及其测试程序

*********************************************************************************************************************************** #操作系统:CentOS6.7 #开发板  :fl2440 #cpu        :s3c2440(arm920t) #编译器

crazy boy 1426

s3c2440 LED 跑马灯程序

基于三星s3c2440 开发板写的LED的跑马灯编程

S3C2440 LED驱动实验-基于FES2440

S3C2440 LED驱动实验-基于FES2440

今晚打老虎吖的博客 855

跑马灯程序

led流水灯程序代码 汇编语言的版本 适用于大学单片机开发汇编语言的学习等课程

s3c2440跑马灯的实现

s3c2440跑马灯的实现 led seg

S3C2440 字符设备驱动程序之LED驱动程序_测试改进(三)

字符设备驱动程序之LED驱动程序_测试改进在加载驱动之前,先来看一下/proc/devices,里面是现在内核所支持的设备。第一列是主设备号,对应的是内核数组chrdevs的下标,第二列是它的名字。加载驱动的命令:insmod first_drv.ko                          cat /proc/devices写个测试驱动程序测试它。(见代码二)mknod /dev/xx...

Tim的嵌入式之旅 522

基于proteus的跑马灯仿真分析

本设计主要是在proteus环境下进行跑马灯的仿真分析,代码部分在keil软件环境下编写,含有hex文件,百分之百可以直接进行仿真。

arm9+linux s3c2440 led 驱动s3c_led.c 解析及运行过程

#include   /* Every Linux kernel module must include this head */ #include     /* Every Linux kernel module must include this head */ #include   /* printk() */ #include       /* struct fops */

柯利南的专栏 1001

51单片机数码管显示跑马灯程序源代码讲解

本文主要对51单片机数码管显示跑马灯程序源代码进行了讲解,希望对您的学习有所帮助。

有史以来最小的GIF/png/jpg/jpeg

昨天我对图像进行了 base64 编码,这样我就可以将它发送到 CouchDB 以测试我正在为客户编写的一些代码。它让我想起了我之前在远程服务器上设置 cookie 时所做的事情。基本上,一个小的 PHP 脚本被放在远程服务器上,它接受几个 GET 参数并根据它们的值设置一些 cookie。然后脚本输出一个 1x1 的透明 GIF。本地服务器上的 PHP 脚本生成了一个 IMG 标签,该标签链接到该图像并根据本地服务器上的 COOKIES 设置参数。

凌凌1301的博客 1417

模块学习之————LED驱动

fl2440字符设备led驱动 首先要明白字符设备驱动注册的基本流程 当我们调用insomd命令加载驱动后,驱动程序从module_init函数开始执行:硬件初始化 -> 申请主次设备号 -> 定义fops(file_operations)结构体 -> 申请cdev结构体并把fops结构体嵌入cdev结构体中与之绑定 -> cdev字符设备的注册。 有一点需要明确的是,在Linux内

huangan_xixi的博客 988

详细指南:如何在TI C2000基于C28x的微控制器上部署FreeRTOS_C语言实现

FreeRTOS是一个小型、开源、实时的嵌入式操作系统内核。它为多种微控制器和处理器提供了支持,包括ARM、AVR、MSP430和PIC等。小型和轻量级:适合资源受限的嵌入式系统。实时性:可以满足严格的实时要求。多任务支持:支持多任务并发执行。内存管理:提供了多种内存管理策略。模块化设计:用户可以根据需要选择所需的功能模块。TI C2000系列微控制器是专为实时控制应用设计的。高性能C28x核:提供了高速的浮点运算能力。丰富的外设:包括PWM、ADC、DAC、通信接口等。低功耗。

qq_38334677的博客 1606

嵌入式Linux字符设备入门之--LED驱动详解

<br />一、实验环境<br />PC机:Redhat9.0  内核版本:Linux2.4<br />开发板:FL2440     内核版本:Linux2.6      CPU:s3c2440<br />二、实验步骤<br />1、硬件连接。<br />       FL2440有四个可编程控制的共阳极LED,分别与CPU的GPB5,GPB6,GPB8,GPB10相连。<br />2、驱动程序<br />       由于是刚刚接触驱动,实在是不知道怎么写,只好用开发板自带的led驱动了(手动分配设备号)

azeale321的专栏 804

Tessent ATPG Flow 流程 (代码和注释)

**摘要:Tessent ATPG流程分为五个阶段:1) 环境初始化(加载设计文件、库和扫描链配置);2) DRC规则检查(验证设计可测试性并修复违例);3) ATPG向量生成(设置故障模型并运行引擎优化覆盖率);4) 向量验证与后处理(仿真验证并生成ATE格式文件);5) 进阶操作(多电压域和存储器测试)。流程输出包括测试向量、覆盖率报告和验证日志,支持stuck-at和transition故障模型,使用Tcl脚本控制。关键步骤包含扫描链配置、EDT压缩和覆盖率优化。

QQ_778132974的博客 932

基于 ESP32+IMU+BLE HID 的低延迟无线体感游戏控制器设计与实现

本文设计的基于 ESP32 的低延迟无线体感游戏控制器,通过合理的硬件选型与软件优化,实现了体感姿态感知与无线低延迟传输的核心功能。硬件上采用 ESP32+MPU6050+TP4056 的低成本方案,接线简单、便携性强;软件上通过轻量化库、互补滤波算法与非阻塞 BLE 传输,有效降低了系统延迟,满足体感游戏的使用需求。

weixin_42300449的博客 401

ADS1.2下使用J-LinkV8调试友善之臂mini2440的记录

硬件配置:mini2440开发板,J-Link V8,USB转串口线 软件环境:Windows 7 32bit,ADS1.2,PuTTY(代替超级终端),J-Link ARM V4.34驱动 1.准备步骤 A:装好J-Link V8的驱动,用20pin转10pin的Jtag转接板把J-Link和mini2440的板子连好,板子上开关S2拨在NOR一端,此时打开 SEGGER J-Link G...

weixin_30687587的博客 332

手把手教你学simulink(11.6)--Simulink 航空航天场景:基于Simulink的飞机俯仰控制系统的数学模型

自动驾驶系统在航空航天领域具有重要应用,特别是在商业航空、军用航空和无人机等领域。PID控制器因其简单且有效的特性,被广泛应用于飞行控制。Simulink提供了一个强大的平台,用于建模、仿真和分析动态系统,非常适合设计和测试自动驾驶系统的控制器。通过本项目的实施,我们成功设计和仿真了一个用于控制飞机俯仰角的PID控制器。该项目展示了从系统建模、控制器设计、仿真验证到结果分析的完整流程,为PID控制器的设计提供了有价值的参考。希望这个实例能够为你的航空航天项目提供有用的指导。

MHD0815的博客 565

裸机开发学习心得

首先说明,我没啥经验。只是说一下我的体会。也没有朝谁开火的意思。如果无意伤了谁,还望海涵;如果所言有误,还请高手多多指教。 好了,开始忽悠~~~~~~~~~~~~~~~~ 在论坛里见到好多朋友学习裸机的时候都有一个习惯——做裸机开发时,过度依赖天嵌的裸机测试程序。怎么说呢,我觉得这样不是很好。尤其是对于一个新手。 也许是学习单片机出身的原因,我有一个毛病,就是对别人的代码不放心、凡事非得扣到

yapingmcu的专栏 2572

【反封禁策略】集成 Undetected-Chromedriver:让 Browser-Use 绕过 Cloudflare 五秒盾

摘要:AI Agent如何突破Cloudflare检测的实战指南 本文针对AI Agent在Cloudflare检测下频繁受阻的问题,深入分析了undetected-chromedriver的工作原理与局限性。文章指出,Cloudflare 2026年的检测体系已升级为"浏览器指纹+IP信誉+协议指纹+行为模式"的多维交叉验证,单一伪装方案难以奏效。 核心内容包括: undetected-chromedriver的三层补丁体系解析 v2与v3版本的适用场景对比 两种Browser-Use集成方案:Selen

ZDQ58818的博客 284

微信群“智”变:扣子机器人无缝接入实战

本文介绍了如何将扣子(Coze)机器人接入微信群实现智能化管理。文章首先阐述了扣子机器人的自然语言处理、自动分类和多模态交互等功能特点,以及其深度学习模型和持续学习能力的技术优势。随后详细讲解了接入步骤:从微信公众号配置、扣子平台机器人创建,到使用Flask框架实现消息转发功能的关键代码。通过电商客服和技术社群两个实际案例,展示了扣子机器人在提升响应效率、优化社群运营方面的显著效果。最后展望了未来在智能推荐、多模态交互等领域的发展潜力,为微信群智能化管理提供了实用解决方案。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 832

一个学长的嵌入式裸机的学习经历

首先说明,我没啥经验。只是说一下我的体会。也没有朝谁开火的意思。如果无意伤了谁,还望海涵;如果所言有误,还请高手多多指教。 好了,开始忽悠~~~~~~~~~~~~~~~~ 在论坛里见到好多朋友学习裸机的时候都有一个习惯——做裸机开发时,过度依赖天嵌的裸机测试程序。怎么说呢,我觉得这样不是很好。尤其是对于一个新手。 也许是学习单片机出身的原因,我有一个毛病,就是对别人的代码不放心、凡事非得扣到

东爷的专栏 816

黑猫带你学eMMC协议第17篇:超详细的eMMC HS200 & HS400讲解

1 前言 1.1 声明 本文依据eMMC JEDEC5.1及个人工作经验整理而成,如有错误请留言。 本文结合eMMC JEDEC5.1协议手册查看效果更佳。 1.2 内容提要 本文大约6000字,主要内容有: 1.什么是emmc hs200/hs400? 2.什么是tuning?tuning的过程又是什么? 3.如何设置才能让emmc进入hs200 mode? 4.如何设置才能让emmc进入hs400 mode? 5.emmc关于HS200/HS400内部结构框图对比。 6.部分个人相关经验总结。

黑猫奥利奥的博客 1万+

一个学长的嵌入式裸机的学习经历------

首先说明,我没啥经验。只是说一下我的体会。也没有朝谁开火的意思。如果无意伤了谁,还望海涵;如果所言有误,还请高手多多指教。 好了,开始忽悠~~~~~~~~~~~~~~~~ 在论坛里见到好多朋友学习裸机的时候都有一个习惯——做裸机开发时,过度依赖天嵌的裸机测试程序。怎么说呢,我觉得这样不是很好。尤其是对于一个新手。 也许是学习单片机出身的原因,我有一个毛病,就是对别人的代码不放心、凡事非得扣到

aiwode 的专栏 5790

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

裸机开发经验

首先说明,我没啥经验。只是说一下我的体会。也没有朝谁开火的意思。如果无意伤了谁,还望海涵;如果所言有误,还请高手多多指教。 好了,开始忽悠~~~~~~~~~~~~~~~~ 在论坛里见到好多朋友学习裸机的时候都有一个习惯——做裸机开发时,过度依赖天嵌的裸机测试程序。怎么说呢,我觉得这样不是很好。尤其是对于一个新手。        也许是学习单片机出身的原因,我有一个毛病,就是对别人的代码不放心

ruglcc's blog 1445

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

上一篇: 反斜杠、用法总结
下一篇: platform之led驱动分析及测试程序
fml1997
博客等级 码龄10年 30粉丝 56原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值