rel="File-List" href="file:///C:%5CDOCUME%7E1%5C%E6%88%91%E7%9A%84%E5%A4%A9%E7%A9%BA%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

                    我的嵌入式学习之路(一)

linux 跑的第一个程序 LED

 

学习嵌入式系统,可能第一个要做的事情就是点亮LED灯,就像写程序的Hello World一样,虽然是非常简单的一个问题,可是让我鼓捣了两三天,才把等灯点亮。所以写下来,能给初学者一些借鉴的地方。

我的开发板用的是S 3C 2440,基本上所有的2410/2440开发板都一样的,大部分都是抄的三星公司那个开发板,好了废话不说了,看程序。

led驱动代码如下:

 

/****************************************Copyright(c)********************************************

**                               南京汉德森科技股份有限公司

**                                     显示研发部

**                                        

**                                                            wangdongxu@handson.cc

**                                 http://www.handson.cc

**

**--------------文件信息--------------------------------------------------------------------------------

**      : LED驱动

**      : 王东旭

**最后修改日期: 2008 8 27 21:10:50

**        : 2440LED驱动程序,这是我能正确运行的第一个嵌入式程序

**              原作者是《嵌入式Linux应用开发完全手册》的作者韦东山先生

**                         我只是在上面又修改了一些内容。

**--------------历史版本信息----------------------------------------------------------------------------

** 创建人: 王东旭

**   : v1.0

** 日 期: 2008 8 27 21:11:37

** 描 述: 原始版本

**

**------------------------------------------------------------------------------------------------------

** 修改人:

**   :

** 日 期:

** 描 述:

**

**--------------当前版本修订------------------------------------------------------------------------------

** 修改人:

** 日 期:

** 描 述:

**

**------------------------------------------------------------------------------------------------------

********************************************************************************************************/

 

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <asm/arch/regs-gpio.h>

#include <asm/hardware.h>

 

#define DEVICE_NAME     "leds"  /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */

#define LED_MAJOR       231     /* 主设备号 */

 

/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */

#define IOCTL_LED_ON    0

#define IOCTL_LED_OFF   1

 

/* 用来指定LED所用的GPIO引脚 */

static unsigned long led_table [] = {          

      

       /* ★★★★这里一定要注意,你用的是那个管脚,我就因为弄错了,一个上午就没有了。

              下面这些定义可以在内核根目录 /include/asm/arch/regs-gpio.h中找到

       */

   

    S 3C 2410_GPF4,

    S 3C 2410_GPF5,

    S 3C 2410_GPF6,

    S 3C 2410_GPF7,

   

};

 

/* 用来指定GPIO引脚的功能:输出 */

static unsigned int led_cfg_table [] = {

    S 3C 2410_GPF4_OUTP,

    S 3C 2410_GPF5_OUTP,

    S 3C 2410_GPF6_OUTP,

    S 3C 2410_GPF7_OUTP,

  

};

 

/* 应用程序对设备文件/dev/leds执行open(...)时,

 * 就会调用s 3c 24xx_leds_open函数

 */

static int s 3c 24xx_leds_open(struct inode *inode, struct file *file)

{

    int i;

   

    for (i = 0; i < 4; i++) {

        // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能

        /*

               ★★★★s 3c 2410_gpio_cfgpin()s 3c 2410_gpio_setpin可以在

               内核根目录 /include/asm/arch/hardware.h中找到

        */

        s 3c 2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);

    }

    return 0;

}

 

/* 应用程序对设备文件/dev/leds执行ioclt(...)时,

 * 就会调用s 3c 24xx_leds_ioctl函数

 */

static int s 3c 24xx_leds_ioctl(

    struct inode *inode,

    struct file *file,

    unsigned int cmd,

    unsigned long arg)

{

    if (arg > 4) {

        return -EINVAL;

    }

   

    switch(cmd) {

    case IOCTL_LED_ON:

        // 设置指定引脚的输出电平为0

        s 3c 2410_gpio_setpin(led_table[arg], 0);

        return 0;

 

    case IOCTL_LED_OFF:

        // 设置指定引脚的输出电平为1

        s 3c 2410_gpio_setpin(led_table[arg], 1);

        return 0;

 

    default:

        return -EINVAL;

    }

}

 

/* 这个结构是字符设备驱动程序的核心

 * 当应用程序操作设备文件时所调用的openreadwrite等函数,

 * 最终会调用这个结构中指定的对应函数

 */

static struct file_operations s 3c 24xx_leds_fops = {

    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

    .open   =   s 3c 24xx_leds_open,    

    .ioctl  =   s 3c 24xx_leds_ioctl,

};

 

/*

 * 执行“insmod s 3c 24xx_leds.ko”命令时就会调用这个函数

 */

static int __init s 3c 24xx_leds_init(void)

{

    int ret;

 

    /* 注册字符设备驱动程序

     * 参数为主设备号、设备名字、file_operations结构;

     * 这样,主设备号就和具体的file_operations结构联系起来了,

     * 操作主设备为LED_MAJOR的设备文件时,就会调用s 3c 24xx_leds_fops中的相关成员函数

     * LED_MAJOR可以设为0,表示由内核自动分配主设备号

     */

    ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s 3c 24xx_leds_fops);

    if (ret < 0) {

      printk(DEVICE_NAME " can't register major number/n");

      return ret;

    }

   

    printk(DEVICE_NAME " initialized/n");

    return 0;

}

 

/*

 * 执行”rmmod s 3c 24xx_leds.ko”命令时就会调用这个函数

 */

static void __exit s 3c 24xx_leds_exit(void)

{

    /* 卸载驱动程序 */

    unregister_chrdev(LED_MAJOR, DEVICE_NAME);

}

 

/* 这两行指定驱动程序的初始化函数和卸载函数 */

module_init(s 3c 24xx_leds_init);

module_exit(s 3c 24xx_leds_exit);

 

/* 描述驱动程序的一些信息,不是必须的 */

MODULE_AUTHOR("http://www.100ask.net");             // 驱动程序的原作者

MODULE_DESCRIPTION("S 3C 2410/S 3C 2440 LED Driver");   // 一些描述信息

MODULE_LICENSE("GPL");                              // 遵循的协议

 

 

 

驱动写好以后,文件名为s 3c 24xx_leds.c把这个文件放到 /drivers/char子目录下,修改这个目录的Makefile,其实只需要增加一行,obj +=  s 3c 24xx_leds.o,然后,退回到这个内核的根目录下(比如内核源码文件夹叫linux-2.6,tmp目录下,则你的内核根目录就是/tmp/linux-2.6,以下内核根目录,我简称为根目录),执行make modules,执行完你可以在/drivers/char目录下找到s 3c 24xx_leds.ko这个文件,把这个文件发送到开发板上去,可以在串口运行r z命令,接收文件,把文件拷贝到开发板上的/lib/module目录下,然后在开发板执行insmod s 3c 24xx_leds.ko,这个时候如果出现leds initialized,说明加载成功 ,然后,建立设备文件,在开发板上执行mknod /dev/leds  c  231  0

好了我们开始写测试程序了,这个是在PC上写的,代码如下:

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

 

#define IOCTL_LED_ON    0

#define IOCTL_LED_OFF   1

 

void usage(char *exename)

{

    printf("Usage:/n");

    printf("    %s <led_no> <on/off>/n", exename);

    printf("    led_no = 1, 2, 3 or 4/n");

}

 

int main(int argc, char **argv)

{

    unsigned int led_no;

    int fd = -1;

   

    if (argc != 3)

        goto err;

       

    fd = open("/dev/leds", 0);  // 打开设备

    if (fd < 0) {

        printf("Can't open /dev/leds/n");

        return -1;

    }

   

    led_no = strtoul(argv[1], 0, 0) - 1;    // 操作哪个LED

    if (led_no > 3)

        goto err;

   

    if (!strcmp(argv[2], "on")) {

        ioctl(fd, IOCTL_LED_ON, led_no);    // 点亮它

    } else if (!strcmp(argv[2], "off")) {

        ioctl(fd, IOCTL_LED_OFF, led_no);   // 熄灭它

    } else {

        goto err;

    }

   

    close(fd);

    return 0;

   

err:

    if (fd > 0)

        close(fd);

    usage(argv[0]);

    return -1;

}

保存文件名为led_test.c ,保存到任意一个单独的文件夹下,比如/tmp/led目录,然后写makefile,如下

CROSS=arm-linux-

 

all: led_test

 

led_test: led_test.c

      $(CROSS)gcc -o $@ led_test.c -static

 

clean:

      @rm -rf led_test *.o

 

保存名Makefile ,保存到/tmp/led,然后在这个目录下执行make命令。会生成一个led_test文件,同样传输到开发板上,保存在开发板的/usr/bin目录下

这个时候在超级终端中输入 led_test  1  on 这个时候led应该会亮的,输入led_test  1  off 这个时候会灭的。


如果你在使用 led_test这条命令的时候,出现了这样的错误:Permission denied,这是权限问题,用 chmod 777 led_test 就可以解决了。

实验到此结束。希望能给你们帮助。

我的邮箱 wangdongxu@handson.cc

Logo

更多推荐