I'm using the following (relevant) combination:
- STM32F103C8T6 devboard (bluepill)
- 1602 LCD with I2C (HDD44780)
- STM32CubeIDE HAL
Also I have a keypad and some other components attached, but these are irrelevant for the problem.
I use a stripped down library, removing some unnecessary delays (I think, since in debugging circumstances it works).
What I see is when I debug the application, all is ok. Occasionally the LCD will not go through initialization, but when I remove the power to the LCD and reconnect, and restart, debugging is fine.
However, when I do not debug, thus running the application 'normally', the I2C display shows either rubbish or shows the first line lighted and the second line empty. The application itself seems to work, except for the display.
The only way to solve this is to press the reset button on the STM32F103C8T6. Removing the power from the STM32 (and thus LCD) and reconnect power does not help.
How can I fix this? It seems I have to programmatically reset/init the I2C somehow?
Relevant code
MyMain.cpp
Global code, creating the LCD display, MyInit that is called from main.cpp (Init).
…
// Refresh every 99 ms, not 100 ms (than fractions are changing when displayed items have a period of
// % 100 ms == 0
LcdDisplay _lcdDisplay (&hi2c1, 0x27, &UpdateLcd, 99, 1);
...
void MyInit()
{
//_lcdDisplay.I2C_Scan();
_lcdDisplay.Init();
…
LcdDisplay.cpp
The constructor saves some variables for the instance, MyInit initializes the screen. In this function is high likely a problem.
LcdDisplay::LcdDisplay(I2C_HandleTypeDef* hI2c, uint8_t i2cChannel,
UPDATE_LCD_FUNCTION_PTR callbackFunction, uint16_t refreshTime, uint8_t sysTickSubscriberIndex)
: _hI2c(hI2c),
_i2cChannel(i2cChannel << 1),
_callbackFunction(callbackFunction)
{
SysTickSubscribers::SetSubscriber(sysTickSubscriberIndex, this);
SysTickSubscribers::SetInterval(sysTickSubscriberIndex, refreshTime);
}
void LcdDisplay::Init()
{
// 4-bit mode, 2 lines, 5x7 format
SendCommand(0b00110000);
// display & cursor home (keep this!)
SendCommand(0b00000010);
// display on, right shift, underline off, blink off
SendCommand(0b00001100);
// clear display (optional here)
SendCommand(0b00000001);
}
void LcdDisplay::SendCommand(uint8_t cmd)
{
SendInternal(cmd, 0);
}
HAL_StatusTypeDef LcdDisplay::SendInternal(uint8_t data, uint8_t flags)
{
HAL_StatusTypeDef res;
for(;;) {
res = HAL_I2C_IsDeviceReady(_hI2c, _i2cChannel, 1, HAL_MAX_DELAY);
if(res == HAL_OK)
break;
}
uint8_t up = data & 0xF0;
uint8_t lo = (data << 4) & 0xF0;
uint8_t data_arr[4];
data_arr[0] = up|flags|BACKLIGHT|PIN_EN;
data_arr[1] = up|flags|BACKLIGHT;
data_arr[2] = lo|flags|BACKLIGHT|PIN_EN;
data_arr[3] = lo|flags|BACKLIGHT;
//res = HAL_I2C_Master_Transmit_IT(_hI2c, _i2cChannel, data_arr, sizeof(data_arr));
res = HAL_I2C_Master_Transmit(_hI2c, _i2cChannel, data_arr, sizeof(data_arr), HAL_MAX_DELAY);
//HAL_Delay(LCD_DELAY_MS);
return res;
}
As you can see I removed the HAL_Delay (it seemed unnecessary). I tried to use I2C using interrupts, but didn't got it working (so far).