//
// Grove-LCD to wokwi-lcd1602 converter.
//
#include <stdio.h>
#include <stdlib.h>
#include "wokwi-api.h"
#define I2C_DATA_LEN 32
#define LCD_I2C_ADDR 0x3E
#define LCD_MODE_COMMAND 0x00
#define LCD_MODE_DATA 0x40
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINC 0x01
#define LCD_ENTRYSHIFTDEC 0x00
typedef struct {
int idx;
uint8_t data[I2C_DATA_LEN];
} i2c_t;
typedef struct {
pin_t pin_rs;
pin_t pin_rw;
pin_t pin_e;
pin_t pin_d0;
pin_t pin_d1;
pin_t pin_d2;
pin_t pin_d3;
pin_t pin_d4;
pin_t pin_d5;
pin_t pin_d6;
pin_t pin_d7;
pin_t pin_a;
pin_t pin_k;
i2c_t i2c;
} chip_state_t;
void lcd_write(chip_state_t *chip)
{
if ((chip->i2c.data[0] & LCD_MODE_DATA) == 0) {
if ((chip->i2c.data[1] & 0x70) == LCD_FUNCTIONSET) {
chip->i2c.data[1] |= LCD_8BITMODE;
}
pin_write(chip->pin_rs, LOW);
} else {
pin_write(chip->pin_rs, HIGH);
}
for (int i = 1; i < chip->i2c.idx; i++) {
pin_write(chip->pin_d0, (chip->i2c.data[i] & (1 << 0)) ? HIGH : LOW);
pin_write(chip->pin_d1, (chip->i2c.data[i] & (1 << 1)) ? HIGH : LOW);
pin_write(chip->pin_d2, (chip->i2c.data[i] & (1 << 2)) ? HIGH : LOW);
pin_write(chip->pin_d3, (chip->i2c.data[i] & (1 << 3)) ? HIGH : LOW);
pin_write(chip->pin_d4, (chip->i2c.data[i] & (1 << 4)) ? HIGH : LOW);
pin_write(chip->pin_d5, (chip->i2c.data[i] & (1 << 5)) ? HIGH : LOW);
pin_write(chip->pin_d6, (chip->i2c.data[i] & (1 << 6)) ? HIGH : LOW);
pin_write(chip->pin_d7, (chip->i2c.data[i] & (1 << 7)) ? HIGH : LOW);
pin_write(chip->pin_e, HIGH);
pin_write(chip->pin_e, LOW);
pin_write(chip->pin_rs, HIGH);
}
}
void lcd_init(chip_state_t *chip)
{
chip->i2c.idx = 2;
chip->i2c.data[0] = LCD_MODE_COMMAND;
chip->i2c.data[1] = (LCD_FUNCTIONSET | LCD_8BITMODE) >> 4;
for (int i = 0; i < 3; i++) {
lcd_write(chip);
}
}
bool on_i2c_connect(void *user_data, uint32_t address, bool read)
{
chip_state_t *chip = user_data;
chip->i2c.idx = 0;
return true;
}
uint8_t on_i2c_read(void *user_data)
{
return 0;
}
bool on_i2c_write(void *user_data, uint8_t data)
{
chip_state_t *chip = user_data;
chip->i2c.data[chip->i2c.idx] = data;
if (chip->i2c.idx < (I2C_DATA_LEN - 1)) {
chip->i2c.idx++;
}
return true;
}
void on_i2c_disconnect(void *user_data)
{
chip_state_t *chip = user_data;
if (chip->i2c.idx > 1) {
lcd_write(chip);
}
}
void chip_init()
{
chip_state_t *chip = calloc(1, sizeof(chip_state_t));
chip->pin_rs = pin_init("RS", OUTPUT_LOW);
chip->pin_rw = pin_init("RW", OUTPUT_LOW);
chip->pin_e = pin_init("E", OUTPUT_LOW);
chip->pin_d0 = pin_init("D0", OUTPUT_LOW);
chip->pin_d1 = pin_init("D1", OUTPUT_LOW);
chip->pin_d2 = pin_init("D2", OUTPUT_LOW);
chip->pin_d3 = pin_init("D3", OUTPUT_LOW);
chip->pin_d4 = pin_init("D4", OUTPUT_LOW);
chip->pin_d5 = pin_init("D5", OUTPUT_LOW);
chip->pin_d6 = pin_init("D6", OUTPUT_LOW);
chip->pin_d7 = pin_init("D7", OUTPUT_LOW);
chip->pin_a = pin_init("A", OUTPUT_HIGH);
chip->pin_k = pin_init("K", OUTPUT_LOW);
const i2c_config_t i2c_config = {
.address = LCD_I2C_ADDR,
.scl = pin_init("SCL", INPUT_PULLUP),
.sda = pin_init("SDA", INPUT_PULLUP),
.connect = on_i2c_connect,
.read = on_i2c_read,
.write = on_i2c_write,
.disconnect = on_i2c_disconnect,
.user_data = chip
};
i2c_init(&i2c_config);
lcd_init(chip);
}