PINE64

Full Version: Clusterboard i2c and Linux
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello all,

I'm trying to connect an MCP23017 to the i2c bus on one of the Clusterboard's headers (J4) and I'm unable to see it from Linux using i2cdetect:

Code:
[email protected]:~$ sudo i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --                         
[email protected]:~$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --                    
    

I had the same problem interfacing this same chip to a full-size A64 board and resolved it by adding some pull-up resistors to the i2c pins (connecting them to the 3.3v supply as suggested in this thread: https://forum.pine64.org/showthread.php?tid=2079&page=2).  However the Clusterboard doesn't supply 3.3v at this header.

I'm not sure how best to proceed.  Assuming the pull-ups are required on the Clusterboard as they are on the A64, do I need to provide a 3.3v supply (perhaps stepping the 5v supply down to 3.3v) or is there a better way to achieve the same thing?

It seems weird to me that the header would supply the i2c pins if they couldn't work without external regulation but then again I'm pretty new to working with i2c and maybe this is normal?

Any pointers are appreciated, thanks!
Update:

Since I ran out of ideas, I added a 3.3v LDO (AMS1117) to the circuit and used this to pull the SDA & SCL pins up to 3.3v. This works, but it seems kind of overkill to me. Maybe this is the best way to go but if there's a simpler, more elegant or lower-power alternative let me know, thanks!


jjg
I've been looking at the sopine compute module and clusterboard schematics, and it looks like there are no pullup resistors on the I2C bus at all (but I see mention that there is on the sopine baseboard, but a problem with VCC_CTP not being turned on??)

Since you have a working setup, it might be worth trying out the script mentioned here, to see if you can enable the internal pullups on the I2C pins... that might just do the trick, and remove the need for the 3v3 regulator just for pullups (as I2C does need the bus pulled up). I'm sure I've seen some mention of a way to enable pullups through sysfs, which would be easier, but I could be wrong there...

It is a bit annoying looking at the 20 pin header at last that pin17 wasn't brought out as a 3v3 pin... 5v is there on pin 1, so 17 would have been the perfect place to bring it out... as it may be needed to make proper use of the GPIO pins... unless you want to start using buffers or external voltage regulation. Edit: Just had an evil thought... if the GPIOs are 3v3 and you're not using the SPI bus... just use one of the GPIOs pulled high as the 3v3 supply... presuming there's enough drive current to do the necessary pullup... (which I can't see being problematic... but it would need verifying...)... :-D

Credit goes to khogh for putting this together for the pine64...
Code:
/*
* To compile the code
* gcc EnableI2cPullup.c -o EnableI2cPullup
*
* Make sure the program is run under root privilage
* sudo ./EnableI2cPullup
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>


/*
* GPIO Base Address for Allwinner CPU
*/
#define SUNXI_GPIO_BASE0 (0x01C20800)
#define SUNXI_GPIO_BASE1 (0x01F02C00)
/*
* Offset address for PH wher I2c1 is located
* pull up/down value
* 0 = off
* 1 = Pull up
* 2 = Pull down
*/
#define PHPULL0 ((7*0x24)+0x1C)
#define PCPULL0 ((2*0x24)+0x1C)
#define PLPULL0 ((0*0x24)+0x1C)

#define PAGE_SIZE (4*1024)

volatile unsigned *gpio0;
volatile unsigned *gpio1;

// Setup the I/O memory map
void setup_io(int gpiono)
{
    int  mem_fd;
    void *gpio_map;
    unsigned int  addr_start, addr_offset, PageSize, PageMask;
    PageSize = PAGE_SIZE;
    PageMask = (~(PageSize-1));
    if (gpiono==0) {
        addr_start = SUNXI_GPIO_BASE0 & PageMask;
        addr_offset = SUNXI_GPIO_BASE0 & ~PageMask;
    }
    else {
        addr_start = SUNXI_GPIO_BASE1 & PageMask;
        addr_offset = SUNXI_GPIO_BASE1 & ~PageMask;
    }
    
  /* open /dev/mem */
  if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
     printf("can't open /dev/mem \n");
     exit(-1);
  }

  /* mmap GPIO */
  gpio_map = mmap(
     NULL,             //Any adddress in our space will do
     PageSize,       //Map length
     PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
     MAP_SHARED,       //Shared with other processes
     mem_fd,           //File to map
     addr_start         //Offset to GPIO peripheral
  );

  close(mem_fd); //No need to keep mem_fd open after mmap

  if (gpio_map == MAP_FAILED) {
     printf("mmap error %d\n", errno);//Print errno
     exit(-1);
  }

  // Always use volatile pointer!
  if (gpiono==0) {
       gpio0 = (volatile unsigned *)(gpio_map+addr_offset);
     }
     else {
          gpio1 = (volatile unsigned *)(gpio_map+addr_offset);
     }
}

// Read from the GPIO
uint32_t readgpio(int gpiono, uint32_t offset)
{
    volatile uint32_t *addr;
    if (gpiono==0) {
        addr=(volatile uint32_t *)((uint64_t)gpio0+offset);
    }
    else {
        addr=(volatile uint32_t *)((uint64_t)gpio1+offset);
    }
    //printf("Reading: gpiobase %08lx, offset %04x, final addr %08lx\r\n",(uint64_t)(gpio),offset,(uint64_t)(addr));
    return(*(addr));
}

// Write into GPIO
void writegpio(int gpiono, uint32_t offset,uint32_t data)
{
    volatile uint32_t *addr;
    if (gpiono==0) {
        addr=(volatile uint32_t *)((uint64_t)gpio0+offset);
    }
    else {
        addr=(volatile uint32_t *)((uint64_t)gpio1+offset);
    }
    *addr=data;
    //printf("Writing: gpiobase %08lx, offset %04x, final addr %08lx writedata %04x, rereaddata %04x\r\n",(uint64_t)(gpio),offset,(uint64_t)(addr),data,(*addr));
}

#define bitno(pin) ((pin)*2)

int main (int argc, char *argv []){
    uint32_t tmp;
    setup_io(0);    //setup the io
    setup_io(1);    //setup the io
    
    tmp=readgpio(0,PHPULL0);                                    //read from the gpio register
    tmp &= ~((3<<bitno(2))|(3<<bitno(3))|(3<<bitno(5))|(3<<bitno(6)));    //mask off the bit 2 and 3
    tmp |=((1<<bitno(2))|(1<<bitno(3))|(1<<bitno(5))|(1<<bitno(6)));          //set bit 2 and 3 to enable pull up.
    writegpio(0,PHPULL0,tmp);                                 //write the setting into the gpio register
    
    tmp=readgpio(0,PCPULL0);    
    tmp &= ~((3<<bitno(5))|(3<<bitno(9)));
    tmp |=((1<<bitno(5))|(1<<bitno(9)));
    writegpio(0,PCPULL0,tmp);
    
    tmp=readgpio(1,PLPULL0);    
    tmp &= ~((3<<bitno(8))|(3<<bitno(9)));
    tmp |=((1<<bitno(8))|(1<<bitno(9)));
    writegpio(1,PLPULL0,tmp);
    printf("I/O Port for Pine64 I2c POT pull up enabled.\n\r");
    return(0);
}
This is very useful info, thanks !

Using a GPIO as a 3.3v supply is a clever hack, I might have to try that Smile

I'll also give the script a run next time I have things apart.  I've designed a PCB around my current configuration, but it would be awesome if I could eliminate the LDO and replace the MPC23017 with the MCP23008 like you suggested before I order a batch of boards to cover all cluster nodes.