mirror of
https://github.com/utkumaden/esp-idf-lcd.git
synced 2026-04-11 17:39:05 +02:00
266 lines
9.1 KiB
C
266 lines
9.1 KiB
C
#include "lcd.h"
|
|
|
|
/**
|
|
* Control the LCD bus, write value and read.
|
|
* @param driver The driver controlling the bus.
|
|
* @param rw Read/Write pin.
|
|
* @param rs Register Select pin.
|
|
* @param en Enable pin.
|
|
* @param data Data bus.
|
|
* @return Non-negative on success, bus value if reading, negative on error.
|
|
*/
|
|
int WEAK lcdBusIO(lcdDriver_t *driver, bool rw, bool rs, bool en, uint8_t data)
|
|
{
|
|
assert(driver->busIO);
|
|
return driver->busIO(driver, rw, rs, en, data);
|
|
}
|
|
|
|
/**
|
|
* Delay with microsecond precision.
|
|
* @param driver The driver delaying.
|
|
* @param delay Delay in microseconds.
|
|
* @return Zero for success, non-zero on failure.
|
|
* @remarks
|
|
* Although the implementation should have microsecond precision, over delay
|
|
* is not critical as long as the condition that the driver suspended for
|
|
* delay microseconds is satisfied.
|
|
*/
|
|
int WEAK lcdDelay(lcdDriver_t *driver, uint32_t delay)
|
|
{
|
|
assert(driver->delay);
|
|
return driver->delay(driver, delay);
|
|
}
|
|
|
|
int lcdCommand(lcdDriver_t *driver, uint8_t command)
|
|
{
|
|
// Write command into bus.
|
|
if (
|
|
lcdBusIO(driver, 0, 0, 0, command) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 1, command) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 0, command) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.dataHold)
|
|
)
|
|
{
|
|
// IO failed.
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
|
|
if (driver->fourBits)
|
|
{
|
|
// Write bottom nibble of command into bus if in 4bit mode.
|
|
command <<= 4;
|
|
if (
|
|
lcdBusIO(driver, 0, 0, 0, command) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 1, command) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 0, command) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.dataHold)
|
|
)
|
|
{
|
|
// IO failed.
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (driver->writeOnly)
|
|
{
|
|
// Just do a dumb delay if in write only mode.
|
|
return lcdDelay(driver, driver->busTiming.busyHoldShort);
|
|
}
|
|
else
|
|
{
|
|
// Setup read from busy flag.
|
|
if (
|
|
lcdBusIO(driver, 1, 0, 0, 0) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0
|
|
)
|
|
{
|
|
// IO failed.
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
|
|
int value = 0;
|
|
do {
|
|
if (
|
|
lcdDelay(driver, driver->busTiming.busyInterval) != 0 || // Delay before reading busy pin.
|
|
lcdBusIO(driver, 1, 0, 1, 0) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
(value = lcdBusIO(driver, 1, 0, 1, 0)) < 0 || // Read busy value.
|
|
lcdBusIO(driver, 1, 0, 0, 0) < 0 ||
|
|
((driver->fourBits) && ( // 4-bit mode extra ticks.
|
|
lcdDelay(driver, driver->busTiming.dataHold) != 0 ||
|
|
lcdBusIO(driver, 1, 0, 1, 0) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) ||
|
|
lcdBusIO(driver, 1, 0, 0, 0) < 0
|
|
))
|
|
)
|
|
{
|
|
// IO failed.
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
} while (value & (1 << 7)); // Busy flag is the 7th bit.
|
|
|
|
return lcdBusIO(driver, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
int lcdWrite(lcdDriver_t *driver, uint8_t data)
|
|
{
|
|
// Write data into bus.
|
|
if (
|
|
lcdBusIO(driver, 0, 1, 0, data) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0 ||
|
|
lcdBusIO(driver, 0, 1, 1, data) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
lcdBusIO(driver, 0, 1, 0, data) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.dataHold)
|
|
)
|
|
{
|
|
// IO failed.
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
|
|
if (driver->fourBits)
|
|
{
|
|
// Write bottom nibble of data into bus if in 4bit mode.
|
|
data <<= 4;
|
|
if (
|
|
lcdBusIO(driver, 0, 1, 0, data) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0 ||
|
|
lcdBusIO(driver, 0, 1, 1, data) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
lcdBusIO(driver, 0, 1, 0, data) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.dataHold)
|
|
)
|
|
{
|
|
// IO failed.
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (driver->writeOnly)
|
|
{
|
|
// Just do a dumb delay if in write only mode.
|
|
return lcdDelay(driver, driver->busTiming.busyHoldShort);
|
|
}
|
|
else
|
|
{
|
|
// Setup read from busy flag.
|
|
if (
|
|
lcdBusIO(driver, 1, 0, 0, 0) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0
|
|
)
|
|
{
|
|
// IO failed.
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
|
|
int value = 0;
|
|
do {
|
|
if (
|
|
lcdDelay(driver, driver->busTiming.busyInterval) != 0 || // Delay before reading busy pin.
|
|
lcdBusIO(driver, 1, 0, 1, 0) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
(value = lcdBusIO(driver, 1, 0, 1, 0)) < 0 || // Read busy value.
|
|
lcdBusIO(driver, 1, 0, 0, 0) < 0 ||
|
|
((driver->fourBits) && ( // 4-bit mode extra ticks.
|
|
lcdDelay(driver, driver->busTiming.dataHold) != 0 ||
|
|
lcdBusIO(driver, 1, 0, 1, 0) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) ||
|
|
lcdBusIO(driver, 1, 0, 0, 0) < 0
|
|
))
|
|
)
|
|
{
|
|
// IO failed.
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
} while (value & (1 << 7)); // Busy flag is the 7th bit.
|
|
|
|
return lcdBusIO(driver, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
int lcdInit4Bit(lcdDriver_t *driver)
|
|
{
|
|
uint8_t cmd = LCD_CMD_FUNCTION(1, 0, 0);
|
|
|
|
// Do operations on the bus as if the display is in 8 bit mode.
|
|
if (
|
|
lcdBusIO(driver, 0, 0, 0, cmd) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 1, cmd) < 0 || // Set 8 bit mode.
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 0, cmd) < 0 ||
|
|
lcdDelay(driver, 5000) != 0 || // Sleep for 5ms. (from hitachi manual)
|
|
|
|
lcdBusIO(driver, 0, 0, 0, cmd) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 1, cmd) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 0, cmd) < 0 || // Set 8 bit mode again.
|
|
lcdDelay(driver, 100) != 0 || // Sleep for 100 uS (from hitachi manual)
|
|
|
|
lcdBusIO(driver, 0, 0, 1, cmd) < 0 || // Set 8 bit mode once again.
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 0, cmd) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.dataHold) != 0
|
|
)
|
|
{
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
|
|
cmd = LCD_CMD_FUNCTION(0, 0, 0);
|
|
|
|
if (
|
|
lcdBusIO(driver, 0, 0, 0, cmd) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.addressSetup) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 1, cmd) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.enableHold) != 0 ||
|
|
lcdBusIO(driver, 0, 0, 0, cmd) < 0 ||
|
|
lcdDelay(driver, driver->busTiming.dataHold + 100) != 0 // Request four bit mode, still in 8 bit mode.
|
|
)
|
|
{
|
|
driver->error = EIO;
|
|
return -1;
|
|
}
|
|
|
|
return lcdCommand(driver, cmd); // Final request for four bit mode, LCD is initialized.
|
|
}
|
|
|
|
int lcdInit(lcdDriver_t *driver)
|
|
{
|
|
assert(driver);
|
|
driver->cursor.x = 0;
|
|
driver->cursor.y = 0;
|
|
|
|
if (
|
|
driver->fourBits &&
|
|
(
|
|
lcdInit4Bit(driver) ||
|
|
lcdCommand(driver, LCD_CMD_FUNCTION(0, driver->dimensions.height > 1, driver->largeFont)) ||
|
|
lcdDelay(driver, driver->busTiming.busyHoldShort)
|
|
)
|
|
)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (!driver->fourBits)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
return lcdClear(driver);
|
|
}
|