You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IMX6平台下基于mmap的GPIO置位/清零及LCD显示问题咨询

i.MX6平台GPIO mmap映射与LCD显示伪影问题解决

问题背景

因sysfs操作GPIO速度过慢,在i.MX6平台尝试通过mmap直接内存访问实现GPIO置位/清零,当前使用的GPIO信息:

cat /sys/kernel/debug/gpio

gpiochip4: GPIOs 128-159, parent: platform/20ac000.gpio, 20ac000.gpio:
 gpio-136 (                    |sysfs               ) out lo    
 gpio-137 (                    |sysfs               ) out lo    
 gpio-138 (                    |sysfs               ) out lo    
 gpio-139 (                    |sysfs               ) out lo    
 gpio-140 (                    |sysfs               ) out lo    
 gpio-141 (                    |sysfs               ) out lo    
 gpio-142 (                    |sysfs               ) out lo    
 gpio-143 (                    |sysfs               ) out lo    
 gpio-144 (                    |sysfs               ) out lo    
 gpio-145 (                    |sysfs               ) out lo  

已实现物理地址0x020ac000到虚拟地址的映射,代码如下:

#define GPIO_BASE_ADDR_4    (0x020ac000)
volatile unsigned *gpio_reg; 
void* gpio_map;
int mem_fd;

mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
if(mem_fd < 0) {
    printf("ERROR could not open gpio mapping");
    exit(1);
}

gpio_map = mmap(NULL, 
                getpagesize(), 
                (PROT_READ | PROT_WRITE), 
                MAP_SHARED, 
                mem_fd, 
                GPIO_BASE_ADDR_4);

close(mem_fd);

if(gpio_map == MAP_FAILED) {
    printf("ERROR could not open gpio mapping");
    exit(1);
}

gpio_reg = (volatile unsigned *)gpio_map;

最初的置位/清零函数:

void clear(int gpioNr) {
    *(gpio_reg+0x190) = 1<<(gpioNr-128);
}

void set(int gpioNr) {
    *(gpio_reg+0x194) = 1<<(gpioNr-128);
}

程序以root权限执行,初始通过sysfs导出GPIO并设为输出模式。后续修改了置位/清零函数:

void clear(int gpioNr) {
    int reg_index = (gpioNr - 128) / 32;
    int bit_pos = (gpioNr - 128) % 32;
    gpio_reg[reg_index] &= ~(1 << bit_pos);

} 
void set(int gpioNr) {
    int reg_index = (gpioNr - 128) / 32;
    int bit_pos = (gpioNr - 128) % 32;
    gpio_reg[reg_index] |= (1 << bit_pos);
}

修改后GPIO输出有所改善,但驱动NHD-14432WG LCD时约50-70%区域显示正常,其余区域有伪影;此前用sysfs控制显示正常但刷新极慢。

问题分析与解决方案

1. 地址映射正确性验证

  • 物理地址对齐:i.MX6的GPIO基地址0x020ac000是4KB对齐的(末尾三位为0),mmap使用getpagesize()(通常4KB)映射没问题,不存在对齐问题。
  • 映射范围:i.MX6单个GPIO控制器的寄存器空间为0x400字节(从BASE到BASE+0x3FF),getpagesize()(4KB=0x1000)完全覆盖所需寄存器区域,范围足够。
  • 映射参数:MAP_SHARED和PROT_READ|PROT_WRITE参数正确,保证内核与用户空间共享映射,且具备读写权限。

2. GPIO寄存器使用正确性

i.MX6的GPIO寄存器设计中:

  • 初始函数错误点:*(gpio_reg+0x190)是按4字节偏移计算的,0x190字节偏移对应寄存器索引为0x190/4=0xA4,并非正确的置位/清零寄存器。i.MX6的GPIO置位寄存器是GPIOx_DR_SET(偏移0x14),清零寄存器是GPIOx_DR_CLEAR(偏移0x18),而非直接操作数据寄存器GPIOx_DR(偏移0x00)。
  • 修改后函数的问题:直接对GPIOx_DR寄存器做读改写操作存在竞争风险——若内核或其他程序同时操作该GPIO组,读和写之间的间隙会导致数据丢失。此外,i.MX6的GPIO数据寄存器在推挽输出模式下直接操作有效,但正确的置位/清零应使用专门的SET/CLEAR寄存器,避免读改写操作。

正确的置位/清零函数:

// i.MX6 GPIO SET寄存器偏移0x14,CLEAR寄存器偏移0x18
// 按unsigned int索引,每个寄存器占4字节
#define GPIO_DR_SET     0x14/4
#define GPIO_DR_CLEAR   0x18/4

void clear(int gpioNr) {
    int bit_pos = gpioNr - 128;
    gpio_reg[GPIO_DR_CLEAR] = 1 << bit_pos;
}

void set(int gpioNr) {
    int bit_pos = gpioNr - 128;
    gpio_reg[GPIO_DR_SET] = 1 << bit_pos;
}

3. LCD显示伪影解决方案

显示伪影的核心原因是GPIO操作时序不达标或竞争条件导致的电平异常:

  • 时序问题:sysfs操作慢但时序稳定,mmap操作快但可能未满足LCD的建立/保持时间要求。需在GPIO电平切换后添加短延时(使用usleep(),根据LCD datasheet调整,比如1-10us)。
  • 竞争风险:直接操作GPIOx_DR的读改写会引发不可预测的电平跳变,换成SET/CLEAR寄存器可避免该问题。
  • 信号完整性:检查GPIO引脚的硬件连接,确保走线无过长、干扰情况,必要时添加上拉/下拉电阻。
  • 内存屏障:在GPIO操作前后添加内存屏障,确保硬件及时响应寄存器写入:
    #include <stdlib.h>
    #include <stdint.h>
    
    #define dmb() __asm__ __volatile__ ("dmb" : : : "memory")
    
    void set(int gpioNr) {
        int bit_pos = gpioNr - 128;
        dmb();
        gpio_reg[GPIO_DR_SET] = 1 << bit_pos;
        dmb();
    }
    

总结

  1. 地址映射本身无问题,范围和对齐均符合要求;
  2. 需使用i.MX6专用的GPIO SET/CLEAR寄存器,避免直接操作数据寄存器的读改写;
  3. 调整GPIO操作时序、添加内存屏障、排查硬件连接可解决LCD伪影问题。

内容的提问来源于stack exchange,提问作者codeshredder726b

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 06:20:37