#include "drivers/display.h" #include #include #include #include #include struct display_t { int handle; bool backlight, display, cursor, blink; }; //Bit mask for display pins #define PIN_E 0b00000100 #define PIN_RW 0b00000010 #define PIN_RS 0b00000001 #define PIN_BL 0b00001000 #define PIN_D4 0b00010000 #define PIN_D5 0b00100000 #define PIN_D6 0b01000000 #define PIN_D7 0b10000000 //Periods for signal hold #define PERIOD_PULSE 500 #define PERIOD_CMD 4100 //Display commands #define CMD_CLEAR 0x01 #define CMD_HOME 0x02 #define CMD_ENTRY 0x04 #define CMD_DISP 0x08 #define CMD_CODS 0x10 #define CMD_FUNC 0x20 #define CMD_CGADR 0x40 #define CMD_DRADR 0x80 //Flags for entry mode #define ENTRY_NORM 0x02 #define ENTRY_SHIFT 0x01 //Flags for display #define DISP_PWR 0x04 #define DISP_OFF 0x00 #define DISP_CURS 0x02 #define DISP_BLNK 0x01 //Flags for CODS #define CODS_CURL 0x00 #define CODS_CURR 0x04 #define CODS_SCRL 0x08 #define CODS_SCRR 0x0C //Flags for function #define FUNC_4BIT 0x00 #define FUNC_8BIT 0x10 #define FUNC_1LIN 0x00 #define FUNC_2LIN 0x08 #define FUNC_5x8D 0x00 #define FUNC_5x11D 0x04 static void write_cmd(int handle, unsigned int cmd, bool backlight) { unsigned int byte; byte = (cmd & 0xF0) | (backlight ? PIN_BL : 0); i2cWriteByte(handle, byte & 0xFF); gpioDelay(PERIOD_PULSE); i2cWriteByte(handle, (byte | PIN_E) & 0xFF); gpioDelay(PERIOD_PULSE); i2cWriteByte(handle, (byte | ~PIN_E) & 0xFF); gpioDelay(PERIOD_CMD); byte = ((cmd << 4) & 0xF0) | (backlight ? PIN_BL : 0); i2cWriteByte(handle, byte & 0xFF); gpioDelay(PERIOD_PULSE); i2cWriteByte(handle, (byte | PIN_E) & 0xFF); gpioDelay(PERIOD_PULSE); i2cWriteByte(handle, (byte | ~PIN_E) & 0xFF); gpioDelay(PERIOD_CMD); } static void write_char(int handle, char ch, bool backlight) { unsigned int byte; byte = (ch & 0xF0) | PIN_RS | (backlight ? PIN_BL : 0); i2cWriteByte(handle, byte & 0xFF); gpioDelay(PERIOD_PULSE); i2cWriteByte(handle, (byte | PIN_E) & 0xFF); gpioDelay(PERIOD_PULSE); i2cWriteByte(handle, (byte | ~PIN_E) & 0xFF); gpioDelay(PERIOD_CMD); byte = ((ch << 4) & 0xF0) | PIN_RS | (backlight ? PIN_BL : 0); i2cWriteByte(handle, byte & 0xFF); gpioDelay(PERIOD_PULSE); i2cWriteByte(handle, (byte | PIN_E) & 0xFF); gpioDelay(PERIOD_PULSE); i2cWriteByte(handle, (byte | ~PIN_E) & 0xFF); gpioDelay(PERIOD_CMD); } display_t *display_init(int bus, uint8_t addr) { if (bus != DISP_I2C0 && bus != DISP_I2C1) { fprintf(stderr, "WARN: display_init received an invalid bus: %d.\n", bus); return NULL; } if (addr >= 0x80) { fprintf(stderr, "WARN: display_init received an invalid address: %d.\n", addr); return NULL; } display_t * ret = malloc(sizeof(display_t)); if (!ret) { fprintf(stderr, "WARN: display_init failed to allocate memory.\n"); return NULL; } ret->backlight = true; ret->handle = i2cOpen(bus, addr, 0); if (ret->handle < 0) { fprintf(stderr, "WARN: display_init could not open the required I2C%d bus for address %d: %d\n", bus, addr, ret->handle); free(ret); return NULL; } //Literal magic, idk why it's needed, idk what the values are //All I know is it no workey without this //Average programming moment write_cmd(ret->handle, 0x03, false); write_cmd(ret->handle, 0x03, false); write_cmd(ret->handle, 0x03, false); write_cmd(ret->handle, 0x02, false); write_cmd(ret->handle, CMD_FUNC | FUNC_4BIT | FUNC_2LIN | FUNC_5x8D, ret->backlight); write_cmd(ret->handle, CMD_DISP | DISP_PWR, ret->backlight); write_cmd(ret->handle, CMD_CLEAR, ret->backlight); write_cmd(ret->handle, CMD_ENTRY | ENTRY_NORM, ret->backlight); gpioDelay(200000); return ret; } void display_destroy(display_t *display) { if (!display) { fprintf(stderr, "WARN: display_destroy received a NULL display.\n"); return; } i2cClose(display->handle); free(display); } void display_clear(display_t *display) { if (!display) { fprintf(stderr, "WARN: display_clear was given a NULL display.\n"); return; } write_cmd(display->handle, CMD_CLEAR, display->backlight); } void display_set_backlight(display_t *display, bool backlight) { if (!display) { fprintf(stderr, "WARN: display_set_backlight was given a NULL display.\n"); return; } display->backlight = backlight; i2cWriteByte(display->handle, backlight ? PIN_BL : 0); } void display_set_cursor(display_t *display, int lin, int col) { if (!display) { fprintf(stderr, "WARN: display_set_cursor was given a NULL display.\n"); return; } int pos = col % 20; switch (lin % 4) { case 1: pos += 0x40; break; case 2: pos += 0x14; break; case 3: pos += 0x54; break; case 0: default: break; } write_cmd(display->handle, CMD_DRADR | (pos & 0x7F), display->backlight); } void display_write_ch(display_t *display, char ch) { if (!display) { fprintf(stderr, "WARN: display_write_ch was given a NULL display.\n"); return; } write_char(display->handle, ch, display->backlight); } void display_write_str(display_t *display, char *str) { if (!display) { fprintf(stderr, "WARN: display_write_str was given a NULL display.\n"); return; } if (!str) { fprintf(stderr, "WARN: display_write_str was given a NULL str.\n"); return; } for (int i = 0; str[i]; i++) write_char(display->handle, str[i], display->backlight); } void display_write_str_at(display_t *display, char *str, int lin, int col) { if (!display) { fprintf(stderr, "WARN: display_write_str_at was given a NULL display.\n"); return; } if (!str) { fprintf(stderr, "WARN: display_write_str_at was given a NULL str.\n"); return; } display_set_cursor(display, lin, col); display_write_str(display, str); } void display_set_display_on(display_t *display, bool on) { if (!display) { fprintf(stderr, "WARN: display_set_display_on was given a NULL display.\n"); return; } display->display = on; char cmd = CMD_DISP | (display->display ? 0x4 : 0x0) | (display->cursor ? 0x2 : 0x0) | (display->blink ? 0x1 : 0x0); write_cmd(display->handle, cmd, display->backlight); } void display_set_cursor_show(display_t *display, bool show) { if (!display) { fprintf(stderr, "WARN: display_set_cursor_show was given a NULL display.\n"); return; } display->cursor = show; char cmd = CMD_DISP | (display->display ? 0x4 : 0x0) | (display->cursor ? 0x2 : 0x0) | (display->blink ? 0x1 : 0x0); write_cmd(display->handle, cmd, display->backlight); } void display_set_cursor_blink(display_t *display, bool blink) { if (!display) { fprintf(stderr, "WARN: display_set_cursor_blink was given a NULL display.\n"); return; } display->blink = blink; char cmd = CMD_DISP | (display->display ? 0x4 : 0x0) | (display->cursor ? 0x2 : 0x0) | (display->blink ? 0x1 : 0x0); write_cmd(display->handle, cmd, display->backlight); } void display_set_custom_char(display_t *display, uint8_t char_idx, uint64_t data) { if (!display) { fprintf(stderr, "WARN: display_set_custom_char was given a NULL display.\n"); return; } if (char_idx >= 8) { fprintf(stderr, "WARN: display_set_custom_char was given an invalid char_idx: %d.\n", char_idx); return; } write_cmd(display->handle, CMD_CGADR | (char_idx << 3), display->backlight); for (int i = 7; i >= 0; i--) write_char(display->handle, (data >> (i * 8)) & 0xFF, display->backlight); }