Search code examples
cmmapgpio

When I change one GPIO pin output, others also change


I implement GPIO via mmap in C. Now I need to use GPIO to simulate I2C, but when I change the output of one pin, the level of the other pin is pulled low. I think I use AND and OR operations to change the value of a pin will not change the value of other pins, what is wrong with my operation? here is my code and pic:

void i2cSDA(u8* map_base, bool isHigh)
{
    (*(volatile u32*)(map_base + GPIO_BASE_OFFSET + rPE_DAT)) = (isHigh) ?
    ((PE_DAT & 0XFFFFEFFF) | 0X00001000):
    ((PE_DAT & 0XFFFFEFFF));
}

void i2cSCL(u8* map_base, bool isHigh)
{
    (*(volatile u32*)(map_base + GPIO_BASE_OFFSET + rPE_DAT)) = (isHigh) ?
    ((PE_DAT & 0XFFFFF7FF) | 0X00000800):
    ((PE_DAT & 0XFFFFF7FF));
}

and in main.c:

int main(int argc, char** argv)
{
    static int dev_fd;
    unsigned char *map_base;

    dev_fd = open("/dev/mem", O_RDWR);

    map_base = (unsigned char *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, GPIO_PAGE_OFFSET);

    i2cInit(map_base);

    i2cSCL(map_base, 1);
    i2cDely();
    i2cSDA(map_base, 1);

    i2cDely();

    i2cSDA(map_base, 0);

    i2cDely();

    i2cSDA(map_base, 1);

    i2cDely();

    close(dev_fd);

    return 0;
}

and PE_DAT is defined as u32 PE_DAT = (*(volatile u32*)(map_base+GPIO_BASE_OFFSET+rPE_DAT));

I use PE12 and PE11, the data register is 32 bits, 0:12 is data for PE0 to PE12, and 13:32 is reserved.

problem like this

When I try to pull the yellow line to high, the green line get low. Full Code is Here, mainly used I2C.h and GPIO.h.


Solution

  • Your funcktions to set/clear bits are wrong:

    void i2cSDA(u8* map_base, bool isHigh)
    {
        (*(volatile u32*)(map_base + GPIO_BASE_OFFSET + rPE_DAT)) = (isHigh) ?
        ((PE_DAT & 0XFFFFEFFF) | 0X00001000):
        ((PE_DAT & 0XFFFFEFFF));
    }
    

    You never update PE_DAT which makes it contain old invalid values. Also your expressions are too complicated. This function should looke like this:

    void i2cSDA(u8* map_base, bool isHigh)
    {
        if (isHigh != 0)
        {
            (*(volatile u32*)(map_base + GPIO_BASE_OFFSET + rPE_DAT)) |= 0X00001000;
        }
        else
        {
            (*(volatile u32*)(map_base + GPIO_BASE_OFFSET + rPE_DAT)) &= 0XFFFFEFFF;
        }
    }