你好: 我在项目中使用AM335X,出现如下问题: 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。