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/