This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

请教AM335X的GPMC_AD8 到GPMC_AD15的GPIO功能

Other Parts Discussed in Thread: AM3359

你好:

    我在项目中使用AM3359,出现如下问题:

NAND设备已经使用了GPMC_AD0 ~GPMC_AD7。LCD的RGB信号为565格式。

BOOT[15:0]配置如下:

 

由于管脚紧张,想使用GPMC_AD8 ~ GPMC_AD15的GPIO功能控制LED灯。目前调试时发现对应管脚上无电平变化。

还有,这样做是否会影响LCD的显示?因为下文中将其屏蔽。

 

           附录:

1. LINUX内核board-am335xevm.c文件中,修改如下:

/* Module pin mux for LCDC */

static struct pinmux_config lcdc_pin_mux[] = {

    {"lcd_data0.lcd_data0",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data1.lcd_data1",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data2.lcd_data2",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data3.lcd_data3",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data4.lcd_data4",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data5.lcd_data5",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data6.lcd_data6",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data7.lcd_data7",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data8.lcd_data8",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data9.lcd_data9",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data10.lcd_data10",   OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data11.lcd_data11",   OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data12.lcd_data12",   OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data13.lcd_data13",   OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data14.lcd_data14",   OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    {"lcd_data15.lcd_data15",   OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT

                               | AM33XX_PULL_DISA},

    // {"gpmc_ad8.lcd_data23",      OMAP_MUX_MODE1 | AM33XX_PIN_OUTPUT},

    // {"gpmc_ad9.lcd_data22",      OMAP_MUX_MODE1 | AM33XX_PIN_OUTPUT},

    // {"gpmc_ad10.lcd_data21", OMAP_MUX_MODE1 | AM33XX_PIN_OUTPUT},

    // {"gpmc_ad11.lcd_data20", OMAP_MUX_MODE1 | AM33XX_PIN_OUTPUT},

    // {"gpmc_ad12.lcd_data19", OMAP_MUX_MODE1 | AM33XX_PIN_OUTPUT},

    // {"gpmc_ad13.lcd_data18", OMAP_MUX_MODE1 | AM33XX_PIN_OUTPUT},

    // {"gpmc_ad14.lcd_data17", OMAP_MUX_MODE1 | AM33XX_PIN_OUTPUT},

    // {"gpmc_ad15.lcd_data16", OMAP_MUX_MODE1 | AM33XX_PIN_OUTPUT},

    {"lcd_vsync.lcd_vsync",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

    {"lcd_hsync.lcd_hsync",     OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

    {"lcd_pclk.lcd_pclk",       OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

    {"lcd_ac_bias_en.lcd_ac_bias_en", OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},

    {NULL, 0},

};

 

/* Pin mux for GPMC bus */

static struct pinmux_config gpmc_pin_mux[] = {

    {"gpmc_ad0.gpmc_ad0",     OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_ad1.gpmc_ad1",     OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_ad2.gpmc_ad2",     OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_ad3.gpmc_ad3",     OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_ad4.gpmc_ad4",     OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_ad5.gpmc_ad5",     OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_ad6.gpmc_ad6",     OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_ad7.gpmc_ad7",     OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

 

    {"gpmc_ad8.gpio0_22",     OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

    {"gpmc_ad9.gpio0_23",     OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

    {"gpmc_ad10.gpio0_26",    OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

    {"gpmc_ad11.gpio0_27",    OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

    {"gpmc_ad12.gpio1_12",    OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

    {"gpmc_ad13.gpio1_13",    OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

    {"gpmc_ad14.gpio1_14",    OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

    {"gpmc_ad15.gpio1_15",    OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},

 

    {"gpmc_wait0.gpmc_wait0", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_wpn.gpmc_wpn",     OMAP_MUX_MODE7 | AM33XX_PIN_INPUT_PULLUP},

    {"gpmc_csn1.gpmc_csn1",   OMAP_MUX_MODE0 | AM33XX_PULL_DISA},

    {"gpmc_advn_ale.gpmc_advn_ale",  OMAP_MUX_MODE0 | AM33XX_PULL_DISA},

    {"gpmc_oen_ren.gpmc_oen_ren",   OMAP_MUX_MODE0 | AM33XX_PULL_DISA},

    {"gpmc_wen.gpmc_wen",     OMAP_MUX_MODE0 | AM33XX_PULL_DISA},

    {"gpmc_ben0_cle.gpmc_ben0_cle", OMAP_MUX_MODE0 | AM33XX_PULL_DISA},

    {"gpmc_clk.gpmc_clk",   OMAP_MUX_MODE0 | AM33XX_PIN_INPUT},

    {"ecap0_in_pwm0_out.xdma_event_intr2", OMAP_MUX_MODE6 | AM33XX_PIN_INPUT}, // DMAREQ

    {NULL, 0},

};

 

2. LINUX内核mux33xx.c文件中,对应如下:

    _AM33XX_MUXENTRY(GPMC_AD8, 0,

        "gpmc_ad8", "lcd_data23", "mmc1_dat0", "mmc2_dat4",

        NULL, NULL, NULL, "gpio0_22"),

    _AM33XX_MUXENTRY(GPMC_AD9, 0,

        "gpmc_ad9", "lcd_data22", "mmc1_dat1", "mmc2_dat5",

        "ehrpwm2B", NULL, NULL, "gpio0_23"),

    _AM33XX_MUXENTRY(GPMC_AD10, 0,

        "gpmc_ad10", "lcd_data21", "mmc1_dat2", "mmc2_dat6",

        NULL, NULL, NULL, "gpio0_26"),

    _AM33XX_MUXENTRY(GPMC_AD11, 0,

        "gpmc_ad11", "lcd_data20", "mmc1_dat3", "mmc2_dat7",

        NULL, NULL, NULL, "gpio0_27"),

    _AM33XX_MUXENTRY(GPMC_AD12, 0,

        "gpmc_ad12", "lcd_data19", "mmc1_dat4", "mmc2_dat0",

        NULL, NULL, NULL, "gpio1_12"),

    _AM33XX_MUXENTRY(GPMC_AD13, 0,

        "gpmc_ad13", "lcd_data18", "mmc1_dat5", "mmc2_dat1",

        NULL, NULL, NULL, "gpio1_13"),

    _AM33XX_MUXENTRY(GPMC_AD14, 0,

        "gpmc_ad14", "lcd_data17", "mmc1_dat6", "mmc2_dat2",

        NULL, NULL, NULL, "gpio1_14"),

    _AM33XX_MUXENTRY(GPMC_AD15, 0,

        "gpmc_ad15", "lcd_data16", "mmc1_dat7", "mmc2_dat3",

        NULL, NULL, NULL, "gpio1_15"),

 

 

 

3. GPIO的驱动程序如下:

    /*-----------------------------------------------------*/

    #define DEVICE_NAME        "gpioCtl"

    #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))

    /*------------------------------------------------------*/

 

    struct gpio_qset;

 

    /*设备编号存储结构体*/

    struct dev_num

    {

            dev_t devNum;

            unsigned int major;

            unsigned int minor;

            unsigned int minor_first;

            unsigned int count;

    };

    struct dev_num gpio_dev_num;

 

    /*设备描述结构体*/

    struct gpio_dev

    {

            struct cdev cdev;

            struct gpio_qset* dptr;        //设备数据存储链表第一项

            unsigned long size;        //链表长度(随着申请的GPIO端口数增长)

    };

    struct gpio_dev *gpio_devp;

 

    /*设备数据存储结构体(采用链式存储,不理解的可以看《数据结构》)*/

    struct gpio_qset

    {

            unsigned long port;        //端口号 #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))

            unsigned int ddr;        //方向(输入(0)或输出(1))

            char value;                //高(1)低(0)电平

            unsigned long num;        //当前编号(按照申请顺序编号)

            char label[10];                //申请的GPIO使用名称

            struct gpio_qset* next;        //指向链表下一项的指针

    };

 

    /**

    * 功能:初始化gpio_dev

    * *inode:

    * *filp:

    * 描述:用户空间调用open时运行

    *         int (*open) (struct inode *, struct file *);

    * 返回值:0

    */

    static int gpio_open(struct inode *inode, struct file *filp)

    {

            struct gpio_dev *dev;

           

            //由container_of获得结构体指针inode中结构体cdev的指针,

            //让结构体指针dev指向上述的指针所指的地址,

            //再让file->private指向这一地址,

            dev = container_of(inode->i_cdev, struct gpio_dev, cdev);

            filp->private_data = dev;

            dev->dptr = NULL;

            dev->size = 0;

            //printk(KERN_ERR "gpio_open success!\n");

            return 0;

    }

 

    /**

    * 功能:释放申请的GPIO端口以及释放链表(gpio_qset)

    * *inode:

    * *filp:

    * 描述:用户空间调用close时运行

    *         int (*release) (struct inode *, struct file *);

    * 返回值:0

    */

    static int gpio_close(struct inode *inode, struct file *filp)

    {

            struct gpio_dev *dev = filp->private_data;

            struct gpio_qset *qset, *qsetTmp;

            qsetTmp = (struct gpio_qset *)kzalloc(sizeof(struct gpio_qset), GFP_KERNEL);

            for(qset = dev->dptr; qset->next !=NULL; qset=qsetTmp)

            {

                    qsetTmp = qset->next;

                    gpio_free(qset->port);        //释放申请的端口

                    kfree(qset);                //释放gpio_qset内存

            }

            gpio_free(qsetTmp->port);

            kfree(qsetTmp);

             //printk(KERN_ERR "gpio release!\n");

            return 0;

    }

 

    /**

    * 功能:申请新的gpio_qset的内存,确定相应的GPIO端口功能

    * *inode:

    * *filp:

    * cmd:实现的功能,

    *     0:读,

    *     1:写,

    *     2:释放GPIO端口

    * arg:执行操作的GPIO端口

    * 描述:用户空间调用ioctl时运行

    *         long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

    * 返回值:成功返回操作的GPIO端口号

    *           错误返回相应的错误码

    */

    static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

    {

            int ret;

            struct gpio_dev *dev = filp->private_data;

            struct gpio_qset *qset;

 

            //cmd == 2 设计成为释放arg端口的功能,之后申请内存的任务便不必执行了,所以直接返回

            if(cmd == 2)

            {

                    gpio_free(arg);

                    return arg;

            }

 

            dev->size++;

 

            if(dev->dptr == NULL)

            {

                    dev->dptr = (struct gpio_qset *)kzalloc(sizeof(struct gpio_qset), GFP_KERNEL);

                    qset = dev->dptr;

            }

            else

            {

                    for(qset = dev->dptr; qset->next != NULL; qset = qset->next);                        //找到链表最后一项

                    qset->next = (struct gpio_qset *)kzalloc(sizeof(struct gpio_qset), GFP_KERNEL);

                    qset = qset->next;

            }

            /*链表数据*/

            qset->num = dev->size;        //确定自己的编号

            qset->ddr = cmd;        //确定方向

            qset->port = arg;        //确定端口号

            qset->next = NULL;        //最后一项地址清空

 

            //printk(KERN_ERR "qset->num=%d,qset->ddr=%d,qset->port=%d\n", qset->num, qset->ddr, qset->port);

            sprintf(qset->label, "gpio%ld", qset->port);                //确定申请的GPIO使用名称(和端口直接相关)

            ret = gpio_request(qset->port, qset->label);                //申请端口

           

            /*由于gpio_requset会自己判断成功与否并且退出函数,故注释掉对ret的判断

            if(ret < 0)

                    printk(KERN_ERR "%s_requset failled!%d \n", qset->label, ret);

            */

 

            /*判断GPIO工作方向(输出或输出)*/       

            switch(qset->ddr)

            {

                    case 0:        ret = gpio_direction_input(qset->port);

                            if(ret < 0)

                                    printk(KERN_ERR "gpio_direction_input failled!\n");

                            break;

                    case 1:        ret = gpio_direction_output(qset->port, 1);

                            if(ret < 0)

                                    printk(KERN_ERR "gpio_direction_output failled!\n");

                            break;

                    default:

                            return -EPERM;        /* Operation not permitted */

            }

            return qset->num;

    }

 

    /**

    * 功能:获取相应端口电平,写入相应的qset_dev->value,写到用户空间的readBuf

    * *inode:

    * *filp:

    * *readBuf:读数据缓存指针,用户空间的指针

    * port:被读取的GPIO端口号

    * *offp:

    * 描述:用户空间调用read时运行

    *         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

    * 返回值:成功返回qset->value

    *           错误返回相应的错误码

    */

    static ssize_t gpio_read (struct file *filp, char __user *readBuf, size_t port, loff_t *offp)

    {

            long ret;

            struct gpio_dev *dev = filp->private_data;

            struct gpio_qset *qset;

           

            if(dev->dptr == NULL)       

                    return -ENODEV;                /* No such device */

                           

            for(qset = dev->dptr; qset != NULL; qset = qset->next)

            {

                    if(qset->port == port)

                            break;

                    if(qset->next == NULL)       

                            return -ENODEV;        /* No such device */

            }

 

            if(qset->ddr != 0)                //判断是否ioctl设置为读操作

                    return -EPERM;                /* Operation not permitted */

            qset->value = gpio_get_value(qset->port);

            //printk(KERN_ERR "qset->port:%d, qset->value:%d\n", qset->port, qset->value);

            switch(qset->value)

            {

                    case 0:        ret = copy_to_user(readBuf, "0", 1);

                            break;

                    case 1:        ret = copy_to_user(readBuf, "1", 1);

                            break;

            }

            return qset->value;

    }

    /**

    * 功能:写入相应端口电平,写入相应的qset_dev->value,数据来自用户空间的writeBuf

    * *inode:

    * *filp:

    * *writeBuf:写数据缓存指针,用户空间的指针

    * port:被写入的GPIO端口号

    * *offp:

    * 描述:用户空间调用write时运行

    *         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

    * 返回值:成功返回qset->value

    *           错误返回相应的错误码

    */

    static ssize_t gpio_write (struct file *filp, const char __user *writeBuf, size_t port, loff_t *offp)

    {

            long ret;

            struct gpio_dev *dev = filp->private_data;

            struct gpio_qset *qset;

 

            if(dev->dptr == NULL)       

                    return -ENODEV;                /* No such device */

                           

            for(qset = dev->dptr; qset != NULL; qset = qset->next)

            {

            //        printk(KERN_ERR "qset->port=%d,port=%d\n", qset->port, port);

                    if(qset->port == port)

                            break;

                    if(qset->next == NULL)       

                            return -ENODEV;        /* No such device */

            }

 

            if(qset->ddr != 1)                //判断是否ioctl设置为写操作

                    return -EPERM;                /* Operation not permitted */

 

            ret = copy_from_user(&qset->value, writeBuf, 1);

            //printk(KERN_ERR "write:%d\n", qset->value);

            switch(qset->value)

            {

                    case '0': qset->value = 0;

                              gpio_set_value(qset->port, 0);

                              break;

                    default : qset->value = 1;

                              gpio_set_value(qset->port, 1);

                              break;

            }

            return qset->value;

    }

 

    /*文件操作结构体*/

    static const struct file_operations gpio_fops = {

            .owner = THIS_MODULE,

            .open = gpio_open,

            .unlocked_ioctl = gpio_ioctl,

            .write = gpio_write,

            .read = gpio_read,

            .release = gpio_close,

    };

 

    /*cdev注册函数*/

    static void gpio_setup_cdev(struct gpio_dev *dev, int index)

    {

            int ret,devno = gpio_dev_num.devNum + index;

            cdev_init(&dev->cdev, &gpio_fops);

            dev->cdev.owner = THIS_MODULE;

            dev->cdev.ops = &gpio_fops;

 

            ret = cdev_add(&dev->cdev, devno, 1);

 

            if(ret)

                    printk(KERN_ERR "error %d : adding gpioCtl%d",ret,index);

    }

 

    /*设备初始化函数*/

    static int __init omap3gpio_init(void)

    {

            int ret;

 

            gpio_dev_num.count = 1;

            gpio_dev_num.minor_first = 0;

            ret = alloc_chrdev_region(&gpio_dev_num.devNum, gpio_dev_num.minor_first, gpio_dev_num.count, DEVICE_NAME);

           

            if(ret < 0)       

                    return ret;

           

            gpio_dev_num.major = MAJOR(gpio_dev_num.devNum);

            gpio_dev_num.minor = MINOR(gpio_dev_num.devNum);

 

            gpio_devp = kzalloc(sizeof(struct gpio_dev),GFP_KERNEL);

 

            gpio_setup_cdev(gpio_devp, 0);

 

            printk(KERN_ERR "gpio alloc_chrdev_region success, major = %d\n", gpio_dev_num.major);

            return 0;

    }

 

    /*设备释放函数*/

    static void __exit omap3gpio_exit(void)

    {

            /*test*/

            //struct file *filp;

            //struct inode *inode;

            //inode = container_of(&gpio_devp->cdev, struct inode, i_cdev);

            //filp = container_of(gpio_devp, struct file, private_data);

            //gpio_close(inode, filp);

            /*test*/

 

            cdev_del(&gpio_devp->cdev);

            kfree(gpio_devp);

            unregister_chrdev_region(gpio_dev_num.devNum, 1);

            printk(KERN_ERR "gpio unregister_chrdev_region success, major = %d\n", gpio_dev_num.major);

    }

 

    MODULE_LICENSE("Dual BSD/GPL");

    MODULE_AUTHOR("Mlo_Lv,Tute-421E-studio");

    MODULE_DESCRIPTION("This module is used to conrtol omap3 gpio");

 

    module_init(omap3gpio_init);

    module_exit(omap3gpio_exit);

 

说明:

该GPIO驱动程序,在应用程序中确定使用的GPIO序号,且同一应用程序中供多个GPIO使用,如下:

  ioctl(fd, 1, GPIO_TO_PIN(0, 22));   //设置为输出, 用于DO2

  write(fd,"0",GPIO_TO_PIN(0, 22));

 

  ioctl(fd, 1, GPIO_TO_PIN(0, 23));   //设置为输出, 用于DO2

  write(fd,"0",GPIO_TO_PIN(0, 23));

 

  ioctl(fd, 1, GPIO_TO_PIN(0, 26));   //设置为输出, 用于DO2

  write(fd,"0",GPIO_TO_PIN(0, 26));

 

该驱动程序已经成功用于本板其他的GPIO。