[经验] Linux下AM335X的GPIO控制

chenzhufly   2012-5-3 14:06 楼主

作者:chenzhufly QQ36886052 ( 转载请注明出处)
一路走来,熟悉硬件系统,搭建软件开发环境,编译Linux系统等等,现在也该到对硬件做一些事情了,这是我这几天的研究心得,与君共享。

1. GPIOchar型驱动,这里主要就是点个灯,感受一下驱动的设计和硬件的控制驱动程序:

  1. #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/leds.h>
    #include <linux/io.h>

    #include <linux/semaphore.h>
    #include <linux/kernel.h>
    #include <linux/cdev.h>

    #include <linux/types.h>
    #include <linux/fs.h>
    #include <mach/gpio.h>
    #include <plat/mux.h>

    #include <linux/gpio.h>

    /*******************************************/
    #define NAME "leds"
    #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))

    static int major =251;//定义主设备号
    /*******************************************/
    void led_on(void)
    {
    gpio_set_value(GPIO_TO_PIN(1,22), 1);
    }

    void led_off(void)
    {
    gpio_set_value(GPIO_TO_PIN(1,22), 0);
    }

    void led_init(void)
    {
    int result;
    /* Allocating GPIOs and setting direction */
    result = gpio_request(GPIO_TO_PIN(1,22), "Leds");//usr1
    if (result != 0)
    printk("gpio_request(1_22) failed!\n");
    result = gpio_direction_output(GPIO_TO_PIN(1,22), 1);
    if (result != 0)
    printk("gpio_direction(1_22) failed!\n");

    }

    struct light_dev
    {
    struct cdev cdev;
    unsigned char value;
    };

    struct light_dev *light_devp;

    MODULE_AUTHOR("chenzhufly");
    MODULE_LICENSE("Dual BSD/GPL");

    // 打开和关闭函数
    int light_open(struct inode *inode,struct file *filp)
    {
    struct light_dev *dev;

    // 获得设备结构体指针
    dev = container_of(inode->i_cdev,struct light_dev,cdev);
    // 让设备结构体作为设备的私有信息
    filp->private_data = dev;

    return 0;
    }

    int light_release(struct inode *inode,struct file *filp)
    {
    return 0;
    }


    // ioctl
    int light_ioctl(struct file *filp,unsigned int cmd,
    unsigned long arg)
    {
    struct light_dev *dev = filp->private_data;

    switch(cmd)
    {
    case 0:
    dev->value = 0;
    led_off();
    break;

    case 1:
    dev->value = 1;
    led_on();
    break;

    default:

    return -ENOTTY;
    // break;
    }

    return 0;
    }

    struct file_operations light_fops =
    {
    .owner = THIS_MODULE,
    .unlocked_ioctl = light_ioctl,
    .open = light_open,
    .release = light_release,
    };
    // 模块加载函数
    int light_init(void)
    {
    int ret;
    led_init();
    printk(KERN_ALERT "led modules is install\n");
    ret=register_chrdev(major,NAME,&light_fops);
    if(ret<0)
    {
    printk("unable to register myled driver!\n");
    return ret;
    }
    return 0;

    }

    // 模块卸载函数
    void light_cleanup(void)
    {
    unregister_chrdev(major,NAME);
    printk("Goodbye,cruel world!\n");
    }

    module_init(light_init);
    module_exit(light_cleanup);


应用程序:

  1. #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <linux/input.h>
    #include <unistd.h>
    #include <sys/ioctl.h>

    int main(int argc, char * argv)
    {
    int i, n, fd;

    fd = open("/dev/leds", O_RDWR);
    if (fd < 0)
    {
    printf("can't open /dev/leds!\n");
    exit(1);
    }

    while (1) {
    ioctl(fd, 1, 1);
    sleep(1);

    ioctl(fd, 0, 1);
    sleep(1);
    }

    close(fd);

    return 0;
    }


Makefile文件:

  1. ARCH=arm
    CROSS_COMPILE=/home/chenzhufly/beaglebone/linux-devkit/bin/arm-arago-linux-gnueabi-
    obj-m := leds.o
    KDIR := /home/chenzhufly/beaglebone/board-support/linux-3.1.0-psp04.06.00.03.sdk
    PWD := $(shell pwd)
    default:
    make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
    app: leds_test.c
    $(CROSS_COMPILE)gcc -o leds_test leds_test.c
    clean:
    $(MAKE) -C $(KDIR) M=$(PWD) clean


leds.sh脚本

  1. insmod leds.ko
    mknod /dev/leds c 251 0
    ./leds_test


2. 使用echo命令,这个我在前面也说过

  1. 点亮usr1
    root@beaglebone:~# echo 1 >
    /sys/class/leds/beaglebone::usr1/brightness

    关闭usr1
    root@beaglebone:~# echo 0 > /sys/class/leds/beaglebone::usr1/brightness


3. 做个应用程序实现流水灯功能吧

  1. #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/ioctl.h>
    #include <fcntl.h>
    #define LED1 "/sys/class/leds/beaglebone::usr1/brightness" // usr1 led
    #define LED2 "/sys/class/leds/beaglebone::usr2/brightness" // usr2 led
    #define LED3 "/sys/class/leds/beaglebone::usr3/brightness" // usr3 led

    int main(void)
    {
    int f_led1 = open(LED1, O_RDWR);
    int f_led2 = open(LED2, O_RDWR);
    int f_led3 = open(LED3, O_RDWR);

    unsigned char dat1, dat2, dat3;
    unsigned char i = 0;

    if (f_led1 < 0)
    {
    printf("error in open %s",LED1);
    return -1;
    }
    if (f_led2 < 0)
    {
    printf("error in open %s",LED2);
    return -1;
    }
    if (f_led3 < 0)
    {
    printf("error in open %s",LED3);
    return -1;
    }
    //add 10 times
    for(i=1; i<30; i++)
    {
    dat1 = ((i%3) == 1) ? '1' : '0';
    dat2 = ((i%3) == 2) ? '1' : '0';
    dat3 = ((i%3) == 0) ? '1' : '0';
    write(f_led1, &dat1, sizeof(dat1));
    write(f_led2, &dat2, sizeof(dat2));
    write(f_led3, &dat3, sizeof(dat3));
    usleep(300000);
    }
    // all the bright
    {
    dat1 = '1';
    dat2 = '1';
    dat3 = '1';
    write(f_led1, &dat1, sizeof(dat1));
    write(f_led2, &dat2, sizeof(dat2));
    write(f_led3, &dat3, sizeof(dat3));
    }
    }


有兴趣的可以试试,等我找个示波器测试一下IO的速率,再贴个图上来。

生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙 =================================== 做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰

回复评论 (22)

:carnation:
呵呵!感谢共享,先学习了。
点赞  2012-5-4 13:37

chenzhufly,你好。

如何编译获得驱动程序文件leds.o? 是用命令行还是Makefile文件。

 

能不能把驱动程序leds.c 和Makefile共享一下。

我复制粘贴得到的文件编译无法成功。

 

出现以下信息: make: Entering directory `/home/zdp/ti-sdk-am335x-evm-05.03.02.00/board-support/linux-3.1.0-psp04.06.00.03.sdk' /home/zdp/Makefile:9: *** missing separator. Stop. make: *** [_module_/home/zdp] Error 2 make: Leaving directory `/home/zdp/ti-sdk-am335x-evm-05.03.02.00/board-support/linux-3.1.0-psp04.06.00.03.sdk'

[ 本帖最后由 zhdphao 于 2012-6-2 14:44 编辑 ]
点赞  2012-6-2 14:41
呵呵,我还以为你早就尝试过了呢,加油加油!

文件见附件:

[ 本帖最后由 chenzhufly 于 2012-6-3 20:33 编辑 ]
生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙 =================================== 做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
点赞  2012-6-3 01:05
不好意思,前段时间都在看硬件,这周才开始驱动程序。
点赞  2012-6-3 14:33

    还是没有编译成功,我已经改动了makefile文件,如下:

 

ARCH=arm
CROSS_COMPILE= arm-arago-linux-gnueabi-
obj-m := leds.o
KDIR := /home/zdp/AM335X-LINUX-PSP-04.06.00.03/src/kernel/linux-04.06.00.03/include/
PWD := $(shell pwd)
default:
 make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
app: leds_test.c
 $(CROSS_COMPILE)gcc -o leds_test leds_test.c
clean:
 $(MAKE) -C $(KDIR) M=$(PWD) clean

 

 

[ 本帖最后由 zhdphao 于 2012-6-3 16:13 编辑 ]
点赞  2012-6-3 16:09
我把:KDIR := /home/zdp/AM335X-LINUX-PSP-04.06.00.03/src/kernel/linux-04.06.00.03/include/
改成:KDIR := /home/zdp/AM335X-LINUX-PSP-04.06.00.03/src/kernel/linux-04.06.00.03/
结果也一样,报错误:
leds.c:1:24: error: linux/init.h: No such file or directory
leds.c:3:24: error: linux/leds.h: No such file or directory
leds.c:4:22: error: linux/io.h: No such file or directory
......
点赞  2012-6-3 16:15
不好意思,上传错了耶
led_test.zip (21.84 KB)
(下载次数: 269, 2012-6-3 20:33 上传)

生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙 =================================== 做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
点赞  2012-6-3 20:33
非常感谢,现在可以编译驱动程序了。Makefile文件要求很严格, 稍微哪里不对就不干活了。以后就用你的例子改了。
点赞  2012-6-5 06:56
呵呵  你的任务还是很艰巨的 加油加油!
生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙 =================================== 做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
点赞  2012-6-5 10:29
chenzhufly,您好,谢谢您共享的驱动代码,学习了,您的驱动代码里包含了palt/mux.h头文件,但驱动里好像您没有对多路复用的端口进行配置,虽然启动好后默认选择MODE7,GPIO。plat/mux.h(包括mach-omap2/mux.h)里提供的函数实现代码里都是__init修饰的,应该不能在内核模块里调用。不知道有没有什么好的方法对复用的端口进行配置,目前,我使用的是原始的方法,I/O内存访问:
        if (request_mem_region(CONF_LED0, 16, "led0-3") == NULL)
        {
                PDEBUG("request_mem_region error\n");
                return -EBUSY;
        }
        PDEBUG("request_mem_region success\n");

        g_p_ctrl_led0 = ioremap(CONF_LED0, 4);
        if (NULL == g_p_ctrl_led0)
        {
                PDEBUG("ioremap led0 error\n");
                return -EIO;
        }
        PDEBUG("ioremap led0 success, p = %p\n", g_p_ctrl_led0);
......
最原始的

不知道内核里有没有相关的函数直接配置?
点赞  2012-6-30 18:03
希望大家有时间能互相交流,本人QQ:527973684,我最近建了一个QQ群,
2884819993
有兴趣的朋友可以加一下,大家互相讨论讨论,应该有好处。
点赞  2012-6-30 18:17
不是用了这个函数吗?gpio_direction_output()

你的方式也是可以的啊,把IO的控制寄存器映射到用户空间来配置,完全可行的。
生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙 =================================== 做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
点赞  2012-6-30 21:06

回复 13楼 chenzhufly 的帖子

这个是选GPIO是输入口还是输出口,在这之前应该还要选择这个引脚是作GPIO用还是作其他功能,这个好像只能用寄存器映射的方式了,要不就要修改内核里的mux.c文件。
点赞  2012-6-30 22:16
默认情况下是gpio

这个东西实在board_xxx.c里面设置的
生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙 =================================== 做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
点赞  2012-7-1 00:40

回复 12楼 gaofeisz_sibet 的帖子

你的qq加不上,qq群查不到。
点赞  2012-7-6 20:19
chenzhufly,您好,学习了你的代码。但是有个地方不懂,我在beaglebone,自己编译的内核,加载ldes驱动后,
打开fd = open(\"/dev/leds\", O_RDWR);成功
#define LED1 "/sys/class/leds/beaglebone::usr1/brightness"  //sys led
打开f_led1 = open(LED1, O_RDWR);失败
并且我查看了/sys/class/下并没有leds设备文件。
希望您有空能解答一下,谢谢
本人刚接触linux,菜鸟问题,莫怪啊,呵呵
点赞  2012-7-19 15:27

回复 16楼 agoodog 的帖子

不好意思,之前加了安全问题,现在取消了,另外,QQ群号发错了,是:247421046
点赞  2012-7-22 19:58

更正下以前的回复

希望大家有时间能互相交流,本人QQ:527973684,我最近建了一个QQ群,247421046
有兴趣的朋友可以加一下,大家互相讨论讨论,应该有好处。
点赞  2012-7-22 20:00

回复 17楼 fabregass 的帖子

你自己编译的内核里是没有这个文件的,这个是出厂系统里带的驱动
点赞  2012-7-22 20:02
12下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复