Reading data from a sensor over I2C interupts my display output

Hi All,

I am using the following equipment:
Arduino Uno R3
240x320 TFT LCD Shield (XC4630 from Jaycar) Using the Adafruit TFTLCD library it works as 0x7575 which is a HX8347G driver.
MPU6050 IMU sensor

This is for an assignment where I am to use arduino hardware and create an artificial horizon. I have it working however the system is running at about 2 fps. Feedback from the instructors is that it needs to be a smooth motion on the screen.

The issue I am finding is that every time I execute the following line
mpu.getEvent(&a, &g, &temp);
The screen goes white. The only way to get the screen on again is running this
tft.begin(0x7575);

tft.begin() takes around 250ms to execute and as a result my loop takes nearly 500ms.

From digging into the libraries it apprears that every time the following function is called the screen goes white.
read(read_buffer, read_len) (from Adafruit_I2CDevice.cpp
Part of the Adafruit_BusIO library). (This function is called in the mpu.getEvent(&a, &g, &temp); line)

Code below (I apologise in advance. It isn't the most refined):

#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;

#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, A4);

// Assign human-readable names to some common 16-bit color values:
#define  BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
#define BROWN   0xFBE0

float rollold;
float pitchlineold;

void setup(void) {
  Serial.begin(9600);
    
  Serial.println("Serial Connected");
  Serial.println("Begin IMU setup");
  if (!mpu.begin()) {
  Serial.println("Failed to find MPU6050 chip");
  while (1) {
    delay(10);
    }
  }

  Serial.println("MPU6050 Found!");

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  tft.reset();
  tft.begin(0x7575);
  tft.fillScreen(WHITE);

  Serial.println("Screen Initialized");

  
}


void loop() {
  //Declare Variables
  int beginloop;
  int datafromsensor;
  int screenreset;
  int calculatedata;
  int cleardisplay;
  int displaydata;
  int sendserialdata;
  int centreline;
  int lower;
  int rolldeg;
  int pitchdeg;
  int pitchline;
   
  float accelx;
  float accely;
  float accelz;
  float gravity;
  float pitch;
  float roll;  

  //Pull Data from sensor
  beginloop = millis();
  
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  datafromsensor = millis();

  //Re-initialise display
  tft.begin(0x7575);

  screenreset = millis();
  

  //Process Sensor Data
  accelx = a.acceleration.x;
  accely = a.acceleration.y;
  accelz = a.acceleration.z;

  gravity = sqrt(accelx*accelx + accely*accely + accelz*accelz);
  pitch = asin(accelx/gravity);
  roll = atan(accely/accelz);

  rolldeg = (180/3.14)* roll;
  pitchdeg = (180/3.14)* pitch;

  pitchline = 120 + 60 * tan(pitch);

  calculatedata = millis();

  //Clear previous line
  for(int i = 0; i < 320 ; i++)
  {
    centreline = i - 160;
    lower = centreline * -tan(rollold);

    tft.drawPixel(pitchlineold-1-lower,i,WHITE);
    tft.drawPixel(pitchlineold-lower,i,WHITE);
    tft.drawPixel(pitchlineold+1-lower,i,WHITE);
} 
  
  cleardisplay = millis();

  //Display new line
  for(int i = 0; i < 320 ; i++)
  {
    centreline = i - 160;
    lower = centreline * -tan(roll);

    tft.drawPixel(pitchline-1-lower,i,BLACK);
    tft.drawPixel(pitchline-lower,i,BLACK);
    tft.drawPixel(pitchline+1-lower,i,BLACK);
  }

  displaydata = millis();

  //Store roll and pitch data for clearing next loop
  rollold = roll;
  pitchlineold = pitchline;

  //Send data over Serial
  Serial.print("Pitch: ");
  Serial.print(pitchdeg);
  Serial.print(" Roll: ");
  Serial.println(rolldeg);

  Serial.print("Data recieved from sensor: ");
  Serial.print(datafromsensor-beginloop);

  Serial.print("ms  Screen Reset: ");
  Serial.print(screenreset-datafromsensor);
    
  Serial.print("ms  Calculate Data: ");
  Serial.print(calculatedata-screenreset);
    
  Serial.print("ms  Clear Display: ");
  Serial.print(cleardisplay-calculatedata);

  Serial.print("ms  Display Data: ");
  Serial.print(displaydata-cleardisplay);

  sendserialdata = millis();

  Serial.print("ms  Send Serial Data: ");
  Serial.print(sendserialdata-displaydata);

  Serial.print("ms Screen Time: ");
  Serial.print((screenreset-datafromsensor) + (cleardisplay-calculatedata) +( displaydata-cleardisplay));
  
  Serial.print("ms Total Time: ");
  Serial.print(sendserialdata-beginloop);  
  Serial.println("ms");
    
} 

After further digging every time the following line is executed in twi.cpp the screen stops.

TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA);	// enable INTs

within the following function (approximately line 250)

uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)

That could be a hardware problem. Please post an annotated schematic as you have wired it showing power connections and grounds. A frizzy picture does not have enough information. Also post links to technical information on the hardware links. There are several hardware items that have the same but are different.

That wont help.

You may find some ideas here

Have you tried a simpler sketch just to write data to the display? TFT displays arent very fast so maybe the library is slowing things down.

As suggested, increase this to maybe 115200 or higher. This is 2023, not 1983!

Also increase the i²c speed with

Wire.setClock(400000);

Attached is a schematic showing how things are connected.
The Display is a shield and just clips on top. The sensor only uses the SCL and SDA pins plus power and ground.

This is the sensor purhcased:MPU6050

This is the display: Jaycar - XC4630

On Uno, A4 & A5 are same pins as SCK & SDA.

One of them is marked NC on your shield, but the other is connected to LCD_RST. This could explain why your TFT keeps resetting.

Looks like this shield is not compatible with i²c devices on Uno.

I tried a simpler sketch today for that.
All I had it do was rotate a line on the screen from 30 degrees left to 30 degrees right and I could get about 6 fps.

I am continuing to look into how I can clear and draw the line on the screen faster.

At the moment my main issue is the need to run the tft.begin() function in each loop (as pulling sensor data makes the screen go white. It's default condition when powered on).

I'm not overally familiar with Arduino so forgive me if this is a dumb question but why would the serial baud rate have anything to do with the screen stopping each time data is written via I2C?
Isn't the serial baud rate only impacting USB comunications? (I do understand that it will reduce the time to send data via USB and I will update the baud rate accordingly. I just don't understand the line to the screen turning off).

I initially tried with the MCUFRIEND library and followed the steps in the instructions provided by jaycar and the forum post you linked but was unable to get the display to work. It has only been the Adafruit TFTLCD library that I had any success with.

It does not have anything to do with that. But using slow baud rates can slow down your sketch unnecessarily.

Ah. I didn't see that. Thank you.
That would make sense.

I'll see if I can change LCD_RST to use D0/D1 instead (or just disconnect it if works without it)

I won't have access to the Arduino until next monday (it has to be kept at Uni) so I will update this post then with the results

Ok. Makes sense.

Thank you for the advice (currently the Serial.print section at the bottom of the code takes about 70ms so it should at least help there).

On Uno, D0 & D1 are Serial Tx & Rx, so you could have similar problems, plus this might prevent uploading your sketch and using serial monitor.

I'm not using the SD card function so I should have D10-D13 free

Perhaps that could work.

I guess you would have to remove some pins from the shield so that they can be wired elsewhere. This can probably be done by heating the pin's solder pad with soldering iron while gently pulling the pin with small pliers, from the other side. You might have to clamp the shield in a vice to do this, so take great care not to damage the LCD! You can then solder wires from those solder pads to other Uno pins.

Or maybe you could connect the Arduino RESET pin to the LCD_RST. I wonder why the shield's designer did not do this. Perhaps they knew it would not work.

I am using another shield between the Arduino and the display shield (for the purpose of lower damage risk primarily) so I'll adjust the generic shield. Not the LCD shield itself.

Or I'll use some wires in between (which I was planning on doing anyway to assist with the mounting of the screen inside the case)

My guess is that, since the LCD shield has an sd card read as well they didn't have enough pins to not use i2c ones (although it still leaves the question as to why they didn't use the Arduino RESET pin or at least provide a warning in the documentation...)