I2C Enabled LCD Unreliability

i2clcd

I'm trying to control an I2C Enabled LCD Display using the STM32F0. I have a bunch boards built with the LCDs and a couple of them work all the time, a few work intermittently and a few rarely work at all and I'm not sure where the issue is coming from.

I've tried analyzing the I2C lines and sometimes I see the display NACKing while othertimes I see it ACK but the I2C just stops. Sometimes I even see a bunch of unexpected data being sent.

I'm trying to figure out if this is a hardware issue, LCD issue, or FW issue to start with. From a hardware perspective I've followed the recommended circuit on the datasheet and used ~6k pull-ups. I can only assume the LCD itself is working since sometimes I can get it to display properly. This leads me to believe it's a FW issue. I've noticed that if I play around with the delays between messages I can get it to work sometimes and I can certainly break it. Is there any more error checking or similar that I should be doing do on the uC to improve reliability?

enter image description here

void I2C_LowLevel_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;

  /* Configure the I2C clock source. The clock is derived from the HSI (8MHz default)*/
  RCC_I2CCLKConfig(RCC_I2C1CLK_HSI);

  /* LCD_I2C_SCL_GPIO_CLK and LCD_I2C_SDA_GPIO_CLK Periph clock enable */
  RCC_AHBPeriphClockCmd(LCD_I2C_SCL_GPIO_CLK | LCD_I2C_SDA_GPIO_CLK, ENABLE);

  /* LCD_I2C Periph clock enable */
  RCC_APB1PeriphClockCmd(LCD_I2C_CLK, ENABLE);

  /* Connect PXx to I2C_SCL*/
  GPIO_PinAFConfig(LCD_I2C_SCL_GPIO_PORT, LCD_I2C_SCL_SOURCE, LCD_I2C_SCL_AF);

  /* Connect PXx to I2C_SDA*/
  GPIO_PinAFConfig(LCD_I2C_SDA_GPIO_PORT, LCD_I2C_SDA_SOURCE, LCD_I2C_SDA_AF);

  /* GPIO configuration */  
  /* Configure LCD_I2C pins: SCL */
  GPIO_InitStructure.GPIO_Pin = LCD_I2C_SCL_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(LCD_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);

  /* Configure LCD_I2C pins: SDA */
  GPIO_InitStructure.GPIO_Pin = LCD_I2C_SDA_PIN;
  GPIO_Init(LCD_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
}


void I2C_Initialize(void)
{ 
  I2C_InitTypeDef  I2C_InitStructure;

  I2C_LowLevel_Init();

  /* I2C configuration */
  /* LCD_I2C configuration */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
  I2C_InitStructure.I2C_DigitalFilter = 0x00;
  I2C_InitStructure.I2C_OwnAddress1 = 0x00;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_Timing = I2C_TIMING;

  /* Apply LCD_I2C configuration after enabling it */
  I2C_Init(LCD_I2C, &I2C_InitStructure);

  /* LCD_I2C Peripheral Enable */
  I2C_Cmd(LCD_I2C, ENABLE);

  /* Select the LCD device address */
  I2C_Address = I2C_HW_ADDRESS;
  Delay(10);
}

uint32_t LCD_Initialize(void)
{
  static int initBuffer[8] = {0x14, LCD_CONTRAST_RESET, 0x5E, 0x6D, LCD_DISPLAY_ON, LCD_CLEAR, 0x06};
  static char welcomeMessage1[LCD_MAX_LINE_SIZE + 1] = "HELLO WORLD";
  int n = 0;

  /* Send Start Bit and I2C Device Address */
  I2C_TransferHandling(LCD_I2C, (LCD_DEVICE_ADDRESS << 1), 10, I2C_AutoEnd_Mode, I2C_Generate_Start_Write);

  /* Wait for TXIS to be set */
  //lcdTimeout = LCD_LONG_TIMEOUT;
  while(I2C_GetFlagStatus(LCD_I2C, I2C_ISR_TXIS) == RESET)
  {
    //if((lcdTimeout--) == 0) return LCD_INITIALIZE_FAIL;
  }

  I2C_SendData(LCD_I2C, 0x00);          /* CMD Byte- Only Data to Follow */
  Delay(1);
  I2C_SendData(LCD_I2C, LCD_BASIC_CONFIG);
  Delay(10);
  I2C_SendData(LCD_I2C, LCD_EXTENDED_CONFIG);
  Delay(10);

  /* Send Remaining Initialization Buffer Data */
  for (n=0; n<7; n++)
  {
    I2C_SendData(LCD_I2C, initBuffer[n]);
    Delay(1);
  }

  Delay(10);


  /* Display welcome message to confirm LCD initialization */
  LCD_Display(LCD_LINE1_START, welcomeMessage1);
  Delay(1000);
  LCD_Clear();
  return LCD_INITIALIZE_PASS;
}

Best Answer

6K seems a bit wimpy for the pullup resistors; normally, you want them to be passing 2-3 mA when the line is low. 2700 Ω is a commonly-used value in 5V systems.