在基于OMAP3530的DevKit8000开发板上实现Linux操作系统下的GPIO读写操作

来源:互联网 发布:海湾消防主机联动编程 编辑:程序博客网 时间:2024/05/22 15:06

毕业设计上,想在Devkit8000上利用无线模块和外部通信,所以需要用到SPI接口。由于Linux和OMAP3530对于阿呆都是崭新的,所以道路走得异常艰辛。

Linux下直接写SPI接口的驱动,阿呆已经不再考虑了,那是等把Linux学习到一定层次之后再尝试的事情,目前的状况使尽快利用OMAP3530把无线模块驱动起来,所以就考虑先实现GPIO的控制,然后模拟SPI时序。

天漠提供的源文件里面有对于LED的控制,本来以为在其控制LED的基础上稍加修改,就可以实现对GPIO的控制。可是在board-omap3devkit8000.c文件下,添加相应代码:

static struct gpio_led gpio_leds[] = {
    {
        .name            = "led1",
        .default_trigger    = "heartbeat",
        .gpio            = 186,
        .active_low        = true,
    },
    {
        .name            = "led2",
        .default_trigger    = "mmc0",
        .gpio            = 163,
        .active_low        = true,
    },
    {
        .name            = "ledB",
        .default_trigger    = "none",
        .gpio            = 153,
        .active_low             = true,
    },
    {
        .name            = "led3",
        .default_trigger    = "none",
        .gpio            = 164,    /* gets replaced */
        .active_low             = true,
    },

    {
        .name            = "gpio",
        .default_trigger    = "none",
        .gpio            = 158,    /* gets replaced */
        .active_low             = true,
    },
};

在相应架构的mux.c文件里面添加:

MUX_CFG_34XX("GPIO158", 0x190,
        OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT_PULLUP)

mux.h的enum omap34xx_index {}

里面添加:GPIO158.

可能的确使对Linux的理解太肤浅了吧,结果没有效果。

然后在网上搜到一个帖子:《基于omap3530/dm3730的开发板,LINUX系统,GPIO能写不能读》(阿呆已经转载过来了)

这篇文章提到了三种控制GPIO的方式(当然是写了,后来也提示了解决方法),阿呆看第一种方法就窘了,由于那里的博主使用的是英码的板子,阿呆使用的是天漠的板子,所以提供的资料略又不同,例如,阿呆ls /sys/class就没有gpio这个文件夹,那对它的写操作也就无从谈起了。

后来终于明白了,原来在编译内核的时候,天漠的默认配置里,没有选择GPIO相关的驱动,这样,对GPIO的读操作就实现了,无论是通过/sys/class/gpio,还是通过写GPIO的驱动。

那么接下来就是解决GPIO的读操作了,从OMAP3530的数据手册((spruf98u)OMAP35x Technical Reference Manual (Rev. U).pdf)上看,对GPIO的配置主要是两部分:

首先:配置CONTROL_PADCONF_x Registers,包括模式和方向及上下拉电阻。具体参见:(spruf98u)7.4.4 Pad Functional Multiplexing and Configuration
其次:配置 GPIOi.GPIO_OE、、GPIOi.GPIO_IRQENABLE1、GPIOi.GPIO_IRQENABLE2、GPIOi.GPIO_WAKEUPENABLE等。

然后就可以进行相应的读写操作了,相关寄存器如GPIO_DATAIN、GPIO_DATAOUT等。

在mux.c文件里已经设置了相应的寄存器值,那么为什么却读不出数据呢,这个问题着实困扰了阿呆很长时间。

又如《能写不能读》所说,他利用内存映射的方法,直接操作相关寄存器。呵呵,这对于玩惯单片机的人来说,真是好消息。

以158引脚为例,阿呆获取的CONTROL_PADCONF_x Registers值为0x0004,果然是没有设置读使能。真是奇怪,按照阿呆的思路,应该是设置好的:

在相应架构的mux.c文件里面添加:

1:

MUX_CFG_34XX("GPIO158", 0x190,
        OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT_PULLUP)

在mux.h中,可以找到:

#define OMAP34XX_PIN_INPUT        OMAP3_INPUT_EN
#define OMAP34XX_PIN_INPUT_PULLUP    (OMAP2_PULL_ENA | OMAP3_INPUT_EN \
                        | OMAP2_PULL_UP)

#define OMAP2_PULL_ENA        (1 << 3)
#define OMAP2_PULL_UP        (1 << 4)

2:在board-omap3devkit8000.c文件的 void __init omap3_devkit8000_init(void)函数中添加:omap_cfg_reg(GPIO158);

结果很让人费解,回头等阿呆对Linux熟悉了在考虑一下这个问题吧。

那么,这样就已经知道问题所在了,只要把CONTROL_PADCONF_x Registers的读控制位使能就可以了,上下拉电阻,根据需要了。

下面代码是利用内存映射的方式,设置好该位的方法

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>


main()
{
    int i;
    int fdm;
    char* mem;
    char *buff = "HELLO";


    if((fdm = open ("/dev/mem", O_RDWR)) < 0)
    {
        perror ("open error");
        return -1;
    }
    //map physical memory 0-10 bytes
//    mem = mmap (0, 0x3c, PROT_READ | PROT_WRITE, MAP_SHARED, fdm, 0x49056000);
    mem = mmap (0,0x200, PROT_READ | PROT_WRITE, MAP_SHARED, fdm, 0x48002000);
    if (mem == MAP_FAILED) {
        perror ("mmap error:");
        return 1;
    }
    //Read old value
    for (i = 0; i <0x10; i++)
    {
        if(i%4==0)printf("\nold mem[%x]:",0x190+i);
        printf(" %x", mem[0x190+i]);
    }
    printf("\n");

    mem[0x191] = 0x01;    //将158引脚输入功能使能
    for (i = 0; i <0x10; i++)
    {
        if(i%4==0)printf("\nnew mem[%x]:",0x190+i);
        printf(" %x", mem[0x190+i]);
    }
    printf("\n");
    munmap (mem, 0x3c);     //destroy map memory
    close (fdm);           //close file
    return 0;
}

驱动程序如下:编译方法见《能读不能写》

#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/mux.h>
#include <media/v4l2-common.h>

//---------------------------------------------------------------------------------------------------------------
static int gpio_open(struct inode *inode,struct file *file)
{
        //printk(KERN_WARNING"gpio_open success!\n");
        return 0;
}
//-----------------------------------------------------------------------------------------------------------------
static int gpio_close(struct inode *inode,struct file *file)
{
        // printk(KERN_WARNING"gpio release \n");
         return 0;
}
//---------------------------------------------------------------------------------------------------------------
static ssize_t  gpio_read(struct file *file,char __user *buf, size_t count,loff_t *offset)
{

                int i;
                i = gpio_get_value(158);
                if(1 == i)
                        copy_to_user(buf,"HIGH",sizeof(buf));
                else
                        copy_to_user(buf,"LOW",sizeof(buf));
                return 0;
}
//---------------------------------------------------------------------------------------------------------------
static ssize_t  gpio_write(struct file *file,char __user *buf, size_t count,loff_t *offset)
{
                int val;

        val = strcmp(buf,"HIGH");
        if(!val)
        {
            switch(count)
            {
                case 1: gpio_set_value(149,1); break;
                case 2: gpio_set_value(156,1); break;
                case 3: gpio_set_value(157,1); break;
                case 4: gpio_set_value(162,1); break;
                default: break;
            }
        }
        else
        {
            switch(count)
            {
                case 1: gpio_set_value(149,0);; break;
                case 2: gpio_set_value(156,0); break;
                case 3: gpio_set_value(157,0); break;
                case 4: gpio_set_value(162,0); break;
                default:break;
            }
        }
                return 0;
}
//-----------------------------------------------------------------------------------------------------------------
static const struct file_operations gpio_ops = {
        .owner = THIS_MODULE,
        .open = gpio_open,
        .read = gpio_read,
    .write=gpio_write,
        .release = gpio_close,
};
//--------------------------------------------------------------------------------------------------------------
static int __init omap3gpio_init(void)
{

        int ret = 0;
        ret = register_chrdev(0,"gpio",&gpio_ops);                
        if(ret < 0)
        {
                printk(KERN_WARNING"gpio register_chrdev faild\n");
                return ret;
        }

        printk(KERN_WARNING"gpio register_chrdev success, major = %d\n", ret);
    omap_cfg_reg(GPIO158);
        gpio_request(149,"gpio1"); //要求打开149号gpio
        gpio_request(156,"gpio2"); //要求打开156号gpio
        gpio_request(157,"gpio3"); //要求打开157号gpio
        gpio_request(158,"gpio4"); //要求打开158号gpio
        gpio_request(162,"gpio5"); //要求打开162号gpio
        gpio_direction_input(158);    //设置其为输入
        gpio_direction_output(149,1); //设置其为输出
        gpio_direction_output(156,1); //设置其为输出
        gpio_direction_output(157,1); //设置其为输出
        gpio_direction_output(162,0); //设置其为输出

        return 0;
}
//---------------------------------------------------------------------------------------------------------------------
static void __exit omap3gpio_exit(void)
{
    gpio_free(149);
    gpio_free(156);
    gpio_free(158);
    gpio_free(162);
}       

module_init(omap3gpio_init);
module_exit(omap3gpio_exit);
MODULE_AUTHOR("ADai");
MODULE_DESCRIPTION("omap3gpio  driver");
MODULE_LICENSE("GPL");

测试程序如下:(改程序在Devkit的板子上,158引脚的状态将通过LED3显示出来)

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>



#define LED3 "/sys/class/leds/led3/brightness"


main()
{
    int fd, num=1;
    char *pResult;
    int f_led3;
    unsigned char dat3;
    unsigned short address;
    unsigned char value;


    int i;
    int fdm;
    char* mem;
    char *buff = "HELLO";


    pResult = (char *)malloc(5*sizeof(char));

    if((f_led3 = open(LED3, O_RDWR)) < 0)
    {
        printf("error in open %s",LED3);
        return -1;
    }

    printf("Program begin!");
    fd=open("/dev/gpio_test",O_RDWR,S_IRWXU|S_IRWXG);// 可 读 写 方 式 打 开 设 备
    if(fd!=-1)
    {
        printf("Open successfully!");
        while(1)
        {
            if(num)
            {
                num = 0;
                write(fd, "HIGH", 3);
                usleep(1000000);
            }
            else
            {
                num = 1;
                write(fd, "LOW", 3);
                usleep(1000000);
            }
            read(fd, pResult,sizeof(pResult));
            printf("Reasult: %s\n",pResult);
            if(*pResult=='H') dat3='1';
            else    dat3 = '0';
            write(f_led3, &dat3, sizeof(dat3));

        }        
        close(fd);
        //关闭设备文件
    }
    else
        printf("Device open failure\n");

}


测试分为三部分:1)insmod gpio_driver.ko,加载gpio驱动
        2)mknod /dev/gpio_test c 250 0,创建节点
        3)./gpio_setinput
        4)./gpio_test

上传一张利用/sys/class/gpio验证读取引脚值成功的图片:


OMAP3530在Linux下实现GPIO的读写操作终于实现了,下一步就是整理一下思路,精简一下代码。

由于这方面基础太差,被这个问题这么了很很很久了,终于把它实现了,心里松了口气,写篇文章小纪念一下。

阿呆 2012年11月26日


这两天又整理了一下改驱动,发现了根源所在,即引脚功能配置寄存器的INPUTENABLE位未被始能:


这里以gpio_158为例,CONTROL_PADCONF_MCBSP1_DX[15:0],中各个位的含义如下:


第八位INPUTENABLE虽然在初始时有相关初始代码,但是不知道为什么,没有产生效果。把该位置一。再进行其他配置就可以了。


欢迎转载,注明处处,谢谢。


原创粉丝点击