0

I have an I2C device connected to my Raspberry Pi 3. The I2C device is storing 9 samples in its FIFO buffer at 104Hz. I poll the device for the amount of samples stored. Each sample is 2 bytes. When the FIFO buffer reaches 2016 samples I read all 4032 bytes in one go, by issuing 126 32byte i2c block data reads.

During the reading the device is still filling up the FIFO. So that when I am done reading all 2016 samples I can immediately poll and see that there are 630 new samples.

If I print the number of samples in the FIFO, then the running time of the block read is faster than if I don't print. In this case I get only 387 new samples.

I don't understand how a print statement makes my IO read faster.

I have tried changing the threshold value. Also, moving the print inside the if statement below made things slow again. Printing with flush=True does nothing, neither before or after the threshold is reached.
I tried to print some dummy strings just before the data reading (above tic()). This causes the first few buffer reads to be fast (0.410s) then slow (0.661s) for all after. The amount of fast reads seems to increase with number of print statements.

import numpy as np
from time import sleep
from time import time
from smbus2 import SMBus


# Helper timing functions
_tstart_stack = []

def tic():
    _tstart_stack.append(time())

def toc(fmt="Elapsed: %s s"):
    print(fmt % (time() - _tstart_stack.pop()))


# Datasheet: https://www.pololu.com/file/0J1087/LSM6DS33.pdf
# REGISTER DEFINITIONS:
WHO_AM_I = 0x0F  # page 46
WHO_AM_I_VAL = 0b01101001
# FIFO registers
FIFO_STATUS1 = 0x3A  # page 59
FIFO_STATUS2 = 0x3B  # page 60
FIFO_STATUS3 = 0x3C  # page 60
FIFO_STATUS4 = 0x3D  # page 61
FIFO_CTRL1 = 0x06  # page 40
FIFO_CTRL2 = 0x07  # page 40
FIFO_CTRL3 = 0x08  # page 41
FIFO_CTRL4 = 0x09  # page 42
FIFO_CTRL5 = 0x0A  # page 43
FIFO_DATA_OUT_L = 0x3E  # page 61
FIFO_DATA_OUT_H = 0x3F  # page 61
# Control registers
CTRL1_XL = 0x10  # page 46
CTRL2_G = 0x11  # page 48
CTRL3_C = 0x12  # page 49
CTRL4_C = 0x13  # page 50
CTRL5_C = 0x14  # page 50
CTRL6_C = 0x15  # page 51
CTRL7_G = 0x16  # page 52
CTRL8_XL = 0x17  # page 52
CTRL9_XL = 0x18  # page 53
CTRL10_C = 0x19  # page 54
TAP_CFG = 0x58  # page 64
WAKE_UP_DUR = 0x5C  # page 66
# Accelerometer read
OUTX_L_XL = 0x28  # page 58
OUTX_H_XL = 0x29  # page 58
OUTY_L_XL = 0x2A  # page 58
OUTY_H_XL = 0x2B  # page 59
OUTY_L_XL = 0x2C  # page 59
OUTY_H_XL = 0x2D  # page 59
# Gyro read
OUTX_L_G = 0x22  # page 56
OUTX_H_G = 0x23  # page 57
OUTY_L_G = 0x24  # page 57
OUTY_H_G = 0x25  # page 57
OUTY_L_G = 0x26  # page 58
OUTY_H_G = 0x27  # page 58

# BIT DEFINITION MASKS
FIFO_THRESHOLD  = 0b10000000  # FTH in FIFO_STATUS2, 1 when threshold is crossed.
FIFO_OVER_RUN   = 0b01000000  # FIFO FULL + 1 sample
FIFO_FULL       = 0b00100000  # in FIFO_STATUS2, FIFO full on next sample
FIFO_EMPTY      = 0b00010000  # in FIFO_STATUS2


def init(BUS, LSM):
    # LSM6DS33 datasheet, page 46-54
    # 104 Hz (high performance), 2g accelerometer full-scale selection, 50Hz anti-aliasing filter bandwidth selection
    BUS.write_byte_data(LSM, CTRL1_XL,  0b01000011)
    # 104 Hz (high performance), 245 dps (degrees per second), full scale disabled
    BUS.write_byte_data(LSM, CTRL2_G,   0b01000000)
    # BDU enabled, auto increment address on multiple byte access.
    BUS.write_byte_data(LSM, CTRL3_C,   0b01000100)
    # high pass filter gyroscope enabled
    BUS.write_byte_data(LSM, CTRL7_G,   0b01000000)
    # low pass filter accelerometer enabled and xyz gyro enable
    BUS.write_byte_data(LSM, CTRL10_C,  0b00111100)

    # threshold on 2016 samples
    BUS.write_byte_data(LSM, FIFO_CTRL1, 0b11100000)
    # Time stamp enable, FIFO on data ready, threshold on 2016 samples
    BUS.write_byte_data(LSM, FIFO_CTRL2, 0b10000111)
    # No decimation (downsampling) for gyro and acc
    BUS.write_byte_data(LSM, FIFO_CTRL3, 0b00001001)
    # No decimation (downsampling) for timer
    BUS.write_byte_data(LSM, FIFO_CTRL4, 0b00001000)
    # FIFO ODR 104Hz, FIFO continous mode
    BUS.write_byte_data(LSM, FIFO_CTRL5, 0b00100110)

    BUS.write_byte_data(LSM, TAP_CFG,     0b10000000)  # Timer enable
    BUS.write_byte_data(LSM, WAKE_UP_DUR, 0b00000000)  # Timer resolution 6.4ms


def who_am_i(BUS, LSM):
    # whoami identification
    return BUS.read_byte_data(LSM, WHO_AM_I) == WHO_AM_I_VAL


def wait_data_ready(BUS, LSM):
    # Check Accelerometer Data Ready
    while True:  # while data not ready
        status = BUS.read_byte_data(LSM, 0x1E)
        if status & 0x01 == 1:
            return 1


def reset_timer(BUS, LSM):
    BUS.write_byte_data(LSM, 0x42, 0xAA)  # TIMESTAMP2_REG Write 0xAA to reset.


def reset_fifo(BUS, LSM):
    current_settings = BUS.read_byte_data(LSM, FIFO_CTRL5)
    bypassmode = 0b01111000 & current_settings
    BUS.write_byte_data(LSM, FIFO_CTRL5, bypassmode)  # FIFO bypass mode
    sleep(0.1)
    BUS.write_byte_data(LSM, FIFO_CTRL5, current_settings)  # Back to normal


def read_fifo(BUS, LSM):
    while 1:
        data = []
        # Read the two FIFO STATUS registers, addr is device I2C address
        status_regs = BUS.read_i2c_block_data(LSM, FIFO_STATUS1, 2)

        # Get the current number of samples in FIFO queue
        if FIFO_FULL & status_regs[1]:
            n_samp = 4095
        else:
            n_samp = (status_regs[1] & 0b00001111)*256 + status_regs[0]

        if FIFO_EMPTY & status_regs[1]:
            print("FIFO EMPTY!")

        # THIS PRINT STATEMENT SPEEDS UP THE FOR LOOP BELOW
        # print("number of samples ", n_samp)

        if FIFO_THRESHOLD & status_regs[1]:
            tic()
            # Threshold: 2016 samples=4032bytes=126 reads of 32 bytes
            for _ in range(126):
                data.extend(BUS.read_i2c_block_data(LSM, FIFO_DATA_OUT_L, 32))
            toc()

            status_regs = BUS.read_i2c_block_data(LSM, FIFO_STATUS1, 2)
            n_samp = (status_regs[1] & 0b00001111)*256 + status_regs[0]
            print("number of samples after read", n_samp)


if __name__ == "__main__":
    # I2C bus and register
    BUS = SMBus(1)
    LSM = 0x6b  # 3-axis gyroscope and 3-axis accelerometer

    print("Whoami: ", who_am_i(BUS, LSM))
    init(BUS, LSM)
    reset_fifo(BUS, LSM)
    reset_timer(BUS, LSM)
    read_fifo(BUS, LSM)

With the print statement I get:
elapsed time: 0.410 s
number of samples after read: 387

Without the print statement I get:
elapsed time: 0.661 s
number of samples after read: 621

I expected that removing the print would speed things up, not slow them down. I reset the FIFO on first run so the program always prints "FIFO EMPTY" first.

Edit: Included enough code to run standalone. The problem is in the read_fifo(BUS, LSM) function. I am running on Python 3.4.2 and using the smbus2 module: https://pypi.org/project/smbus2/

Ketil
  • 21
  • 3
  • Looking at the register names - is it an LSM6DS3? I happen to have one of those lying around (along with an RPi3), so if you include an [MCVE](https://stackoverflow.com/help/mcve) (ie. enough code that I can run it and reproduce the behaviour you're seeing without having to guess how you're initializing the bus/device and/or modify the code), I might take a look at with a logic analyzer. – Aleksi Torhamo Mar 27 '19 at 16:30
  • Correctly guessed! I edited to include MCVE. – Ketil Mar 28 '19 at 08:58

0 Answers0