0

I have a problem with I2C clock in Raspbian Buster (using either SMbus or i2c-dev). It defaults to 50 kHz on RPi 4B and to 62.5 kHz on RPi 3B and Zero W. However, if I set the speed i2c_arm_baudrate=200000 > /boot/config.txt on RPi 4B, then speed is 100 kHz.

But the strange thing that for the first few seconds it is actually 200 kHz, so I2C slave devices that have some kind of initialization only work if I initialize them again after waiting for a couple of seconds in the program. The clock mismatch happens no matter what i2c devices I use, in fact it happens even if there are no devices on the bus (writing 0 to address 9):

Python, SMbus:

import smbus
from time import sleep
bus = smbus.SMBus(1)

while True:
    try:
        bus.write_byte(9, 0)
        sleep(.1)
    except OSError:
        pass

C, i2c-dev:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define delay(A) usleep(A*1000)

char *i2cbus = "/dev/i2c-1";
int addr = 9;
int data[1] = {0};

void loop(void)
{
        int i2cfile = open(i2cbus, O_RDWR);
        ioctl(i2cfile, I2C_SLAVE, addr);
        write(i2cfile, data, 1);
}

int main()
{
        for(;;) {
                loop();
                delay(100);
        }
}

pictures from o-scope: pictures from o-scope

So, my question is: how to access BCM i2c speed registers? Datasheet says that the register is at offset 0x7e804000, but when I try inline assembly like so:

#include <stdio.h>
#include <stdint.h>

int main()
{
    uint32_t reg = 0;
    __asm(
        "push {r0}\n\t"
        "ldr r0, =0x7e804000\n\t"
        "ldr %[result], [r0]\n\t"
        "pop {r0}\n\t"
        : [result] "=r" (reg)
    );
    printf("%x\n", reg);
}

I get segmentation fault. (my knowledge in ARM assembly is lacking)

  • Yes, Rpi3B+ has a design bug, you cannot set the I2C speed, which is a flat 100kHz. Rpi4B is OK. You can set as your wish, down to 10kHz, if you like. – tlfong01 Oct 1 at 5:16
  • And this is the story of the Rpi3+ I2C can-not-change-speed-bug: RASPBERRY PI3 I2C BAUD RATE SETTING Postby samtal » 2018-Aug-04 Sat 1:45 pm raspberrypi.org/forums/… – tlfong01 Oct 1 at 5:23
  • Well, I just changed the I2C baud rate on RPi3B on Buster to 160000 and now it is 100000. So it cannot be changed on Stretch but can on Buster. This suggests the design bug is in software. – Andy Trošin Oct 1 at 8:19
  • And NO, I2C baud rate on RPi3B with Buster isn't 100 kHz, its 62,5. That's why I created that post. – Andy Trošin Oct 1 at 8:20
  • Sorry I am confused. Let us discuss 3 configurations separately, (1) Rpi3B+ stretch, (2) Rpi3B+ buster, (3) Rpi4B buster. Do you agree that for (1) cannot change I2C speed, always 100kHz, for (3) Can change any speed, and for (2) I have NOT tested at all. Do you mean you found it cannot change and always 62.5 kHz? I am sorry that I did not read your questions carefully, overlooking that your main puzzle is Rpi buster always 62.5kHz, which I have NOT tested. My apologies. – tlfong01 Oct 1 at 8:26
0

Memory locations are a protected resource. If they weren't any Linux user could read the data of another Linux user.

To access memory outside your address space you have to use the mmap command (man mmap).

Here is some example code. It is mainly to show how to access the GPIO registers but the I2C offsets are shown and the same methods apply.

The highlights of the example are

static volatile uint32_t piPeriphBase = 0x20000000;

#define SYST_BASE  (piPeriphBase + 0x003000)
#define DMA_BASE   (piPeriphBase + 0x007000)
#define CLK_BASE   (piPeriphBase + 0x101000)
#define GPIO_BASE  (piPeriphBase + 0x200000)
#define UART0_BASE (piPeriphBase + 0x201000)
#define PCM_BASE   (piPeriphBase + 0x203000)
#define SPI0_BASE  (piPeriphBase + 0x204000)
#define I2C0_BASE  (piPeriphBase + 0x205000)
#define PWM_BASE   (piPeriphBase + 0x20C000)
#define BSCS_BASE  (piPeriphBase + 0x214000)
#define UART1_BASE (piPeriphBase + 0x215000)
#define I2C1_BASE  (piPeriphBase + 0x804000)
#define I2C2_BASE  (piPeriphBase + 0x805000)
#define DMA15_BASE (piPeriphBase + 0xE05000)

#define DMA_LEN   0x1000 /* allow access to all channels */
#define CLK_LEN   0xA8
#define GPIO_LEN  0xF4
#define SYST_LEN  0x1C
#define PCM_LEN   0x24
#define PWM_LEN   0x28
#define I2C_LEN   0x1C
#define BSCS_LEN  0x40

static volatile uint32_t  *gpioReg = MAP_FAILED;
static volatile uint32_t  *systReg = MAP_FAILED;
static volatile uint32_t  *bscsReg = MAP_FAILED;

/* Map in registers. */

static uint32_t * initMapMem(int fd, uint32_t addr, uint32_t len)
{
    return (uint32_t *) mmap(0, len,
       PROT_READ|PROT_WRITE|PROT_EXEC,
       MAP_SHARED|MAP_LOCKED,
       fd, addr);
}

gpioReg  = initMapMem(fd, GPIO_BASE,  GPIO_LEN);
systReg  = initMapMem(fd, SYST_BASE,  SYST_LEN);
bscsReg  = initMapMem(fd, BSCS_BASE,  BSCS_LEN);

I'm not sure if this will be enough to solve your problem. You probably also need to set a stable core clock frequency.

  • Thanks. I accessed the registers and here is what i've found (reg=5 is clock divider): Pi3B, Buster /boot/config.txt > i2c_arm_baudrate=100000 (62.5 kHz actual reading): reg=5 val= FA0 = 4000 Pi3B, Buster /boot/config.txt > i2c_arm_baudrate=160000 (100 kHz actual reading): reg=5 val= 9C4 = 2500 Pi3B, Stretch /boot/config.txt > i2c_arm_baudrate=100000: reg=5 val= 9C4 = 2500 – Andy Trošin Oct 1 at 10:51
  • This suggests that buster has incorrectly set dividers. I reported this issue on github. – Andy Trošin Oct 1 at 10:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.