Skip to content

Instantly share code, notes, and snippets.

@pgaskin
Last active March 14, 2024 01:29
Show Gist options
  • Save pgaskin/0ca4e5ef1baa00485cfaf06b69e56b2d to your computer and use it in GitHub Desktop.
Save pgaskin/0ca4e5ef1baa00485cfaf06b69e56b2d to your computer and use it in GitHub Desktop.
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "fnk0079.h"
int main() {
i2c_init(i2c0, 100 * 1000);
gpio_set_function(4, GPIO_FUNC_I2C);
gpio_set_function(5, GPIO_FUNC_I2C);
gpio_pull_up(4);
gpio_pull_up(5);
fnk0079 lcd;
if (fnk0079_probe(i2c0, 0x27)) {
// display not connected or error
}
fnk0079_init(&lcd, i2c0, 0x27, 20, 4);
fnk0079_backlight(&lcd, true);
fnk0079_display_control(&lcd, true, false, false);
fnk0079_writes(&lcd, "test");
fnk0079_write(&lcd, '1');
fnk0079_write(&lcd, '2');
fnk0079_write(&lcd, '3');
busy_wait_ms(2000);
fnk0079_backlight(&lcd, false);
busy_wait_ms(2000);
fnk0079_backlight(&lcd, true);
return 0;
}
/**
* Minimal RPi Pico driver for the FNK0079 I2C LCD display board. Might also
* work with other HD44780 boards.
*
* Loosely based on https://github.com/Freenove/Freenove_LCD_Module.
*/
#include <stdbool.h>
#include <stdint.h>
#include "hardware/i2c.h"
#include "fnk0079.h"
int fnk0079_probe(i2c_inst_t *i2c, uint8_t addr) {
uint8_t buf;
int tries = 8;
while (i2c_read_blocking(i2c, addr, &buf, 1, false) < 1) {
if (!--tries) {
return -1; // not connected or error
}
busy_wait_ms(250);
}
return 0;
}
int fnk0079_init(fnk0079 *lcd, i2c_inst_t *i2c, uint8_t addr, uint8_t cols, uint8_t rows) {
// addr 0x27 is 20x4 black board, blank chip
// similar to the HD44780
int rc;
// clamp dimensions
if (cols > 20) cols = 20;
if (rows > 4) rows = 4;
// init the struct
*lcd = (struct fnk0079) {
.i2c = i2c,
.addr = addr,
.cols = cols,
.rows = rows,
.backlight = false,
};
// hardware requires at least 40ms before accepting commands
busy_wait_ms(40);
// reset expander
fnk0079_cmd(lcd, 0);
busy_wait_ms(1000);
// ensure it's in 4-bit mode
if ((rc = fnk0079_clear(lcd))) goto err;
if ((rc = fnk0079_clear(lcd))) goto err;
if ((rc = fnk0079_clear(lcd))) goto err;
if ((rc = fnk0079_function(lcd, false, false, false))) goto err;
// explicitly reset to defaults
if ((rc = fnk0079_function(lcd, false, false, false))) goto err;
if ((rc = fnk0079_display_control(lcd, false, false, false))) goto err;
if ((rc = fnk0079_clear(lcd))) goto err;
if ((rc = fnk0079_entry_mode(lcd, true, false))) goto err;
if ((rc = fnk0079_home(lcd))) goto err;
// done
return 0;
err:
lcd->i2c = NULL;
return rc;
}
int fnk0079_writes(fnk0079 *lcd, const char *s) {
int rc;
if (s) while (*s) if ((rc = fnk0079_write(lcd, *s++))) return rc;
return 0;
}
int fnk0079_clear(fnk0079 *lcd) {
int rc = fnk0079_cmd(lcd, 0x01); // CLEARDISPLAY
if (!rc) busy_wait_ms(2);
return rc;
}
int fnk0079_home(fnk0079 *lcd) {
int rc = fnk0079_cmd(lcd, 0x02); // RETURNHOME
if (!rc) busy_wait_ms(2);
return rc;
}
int fnk0079_entry_mode(fnk0079 *lcd, bool leftToRight, bool shiftIncrement) {
return fnk0079_cmd(lcd, 0x04 // ENTRYMODESET
| (shiftIncrement ? 0x01 : 0x00) // inc : dec
| (leftToRight ? 0x02 : 0x00) // ltr : rtl
);
}
int fnk0079_display_control(fnk0079 *lcd, bool on, bool cursor, bool blink) {
return fnk0079_cmd(lcd, 0x08 // DISPLAYCONTROL
| (blink ? 0x01 : 0x00) // on : off
| (cursor ? 0x02 : 0x00) // on : off
| (on ? 0x04 : 0x00) // on : off
);
}
int fnk0079_move(fnk0079 *lcd, bool scroll, bool left, bool allLines) {
return fnk0079_cmd(lcd, 0x10 // CURSORSHIFT
| (left ? 0x02 : 0x00) // left : right
| (allLines ? 0x04 : 0x00) // all : current
| (scroll ? 0x08 : 0x00) // scroll : move
);
}
int fnk0079_function(fnk0079 *lcd, bool eightBit, bool twoLine, bool fiveTenDots) {
return fnk0079_cmd(lcd, 0x20 // FUNCTIONSET
| (fiveTenDots ? 0x04 : 0x00) // 5x10dots : 5x8dots
| (twoLine ? 0x08 : 0x00) // 2line : 1line
| (eightBit ? 0x10 : 0x00) // 8bit : 4bit
);
}
int fnk0079_character(fnk0079 *lcd, uint8_t idx, const uint8_t *ch) {
int rc;
if ((rc =
fnk0079_cmd(lcd, 0x40 // SETCGRAMADDR
| ((idx&0x7) << 3) // location 0-7
)
)) {
return rc;
}
for (int i = 0; i < 8; i++) {
if ((rc = fnk0079_write(lcd, ch[i]))) {
return rc;
}
}
return 0;
}
int fnk0079_position(fnk0079 *lcd, uint8_t col, uint8_t row) {
if (!lcd->i2c) return -1;
if (row >= lcd->rows)
row = lcd->rows;
return fnk0079_cmd(lcd, 0x80 // SETDDRAMADDR
| (col + (uint8_t[]){0, 64, 20, 84}[row-1])
);
}
int fnk0079_backlight(fnk0079 *lcd, bool on) {
lcd->backlight = on;
return fnk0079_cmd(lcd, 0);
}
int fnk0079_cmd(fnk0079 *lcd, uint8_t cmd) {
return fnk0079_send(lcd, cmd, false);
}
int fnk0079_write(fnk0079 *lcd, uint8_t ch) {
return fnk0079_send(lcd, ch, true);
}
int fnk0079_send(fnk0079 *lcd, uint8_t value, bool data) {
if (!lcd->i2c) return -1;
int rc;
uint8_t buf[4] = {
(uint8_t)((0xF0 & (value<<0)) | (lcd->backlight ? 0x08 : 0x00) | 0x04 | (data ? 0x01 : 0x00)),
(uint8_t)((0xF0 & (value<<0)) | (lcd->backlight ? 0x08 : 0x00) | 0x00 | (data ? 0x01 : 0x00)),
(uint8_t)((0xF0 & (value<<4)) | (lcd->backlight ? 0x08 : 0x00) | 0x04 | (data ? 0x01 : 0x00)),
(uint8_t)((0xF0 & (value<<4)) | (lcd->backlight ? 0x08 : 0x00) | 0x00 | (data ? 0x01 : 0x00)),
};
if ((rc = i2c_write_timeout_us(lcd->i2c, lcd->addr, buf, sizeof(buf), false, 5000)) != sizeof(buf)) return rc;
return 0;
}
#pragma once
#ifdef __cplusplus
extern "C" {
#include <stdbool.h>
#include <stdint.h>
#endif
typedef struct i2c_inst i2c_inst_t;
typedef struct fnk0079 {
i2c_inst_t *i2c;
uint8_t addr;
uint8_t cols;
uint8_t rows;
bool backlight;
} fnk0079;
int fnk0079_probe(i2c_inst_t *i2c, uint8_t addr);
int fnk0079_init(fnk0079 *lcd, i2c_inst_t *i2c, uint8_t addr, uint8_t cols, uint8_t rows);
int fnk0079_writes(fnk0079 *lcd, const char *s);
int fnk0079_clear(fnk0079 *lcd);
int fnk0079_home(fnk0079 *lcd);
int fnk0079_entry_mode(fnk0079 *lcd, bool leftToRight, bool shiftIncrement);
int fnk0079_display_control(fnk0079 *lcd, bool on, bool cursor, bool blink);
int fnk0079_move(fnk0079 *lcd, bool scroll, bool left, bool allLines);
int fnk0079_function(fnk0079 *lcd, bool eightBit, bool twoLine, bool fiveTenDots);
int fnk0079_character(fnk0079 *lcd, uint8_t idx, const uint8_t *ch);
int fnk0079_position(fnk0079 *lcd, uint8_t col, uint8_t row);
int fnk0079_backlight(fnk0079 *lcd, bool on);
int fnk0079_cmd(fnk0079 *lcd, uint8_t cmd);
int fnk0079_write(fnk0079 *lcd, uint8_t ch);
int fnk0079_send(fnk0079 *lcd, uint8_t value, bool data);
#ifdef __cplusplus
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment