Files
linuxSmartie/src/lcd.c
2026-05-11 10:26:30 +00:00

148 lines
5.0 KiB
C

#include "lcd.h"
#include "log.h"
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int lcd_open(struct lcd_t *lcd, const bool reopen) {
applog(LOG_INF, "Opening serial port at %s", lcd->port);
if (lcd->fd <= 0 || reopen)
lcd->fd = open(lcd->port, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (lcd->fd <= 0) {
applog(LOG_ERR, "Error opening serial port %s", lcd->port); // err
return -1;
}
return 0;
}
void lcd_close(struct lcd_t *lcd) {
if (lcd->fd >= 0) {
close(lcd->fd);
applog(LOG_INF, "LCD port closed");
}
lcd->fd = -1;
}
void lcd_write(struct lcd_t *lcd, const uint8_t *cmdbuf, const uint8_t len) {
if (!write(lcd->fd, cmdbuf, len)) {
applog(LOG_ERR, "failed to execute write(), lost access to port?");
lcd_open(lcd, true);
}
}
void lcd_clear(struct lcd_t *lcd) {
const uint8_t cmdbuf[] = {254, 88};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_set_contrast(struct lcd_t *lcd, const uint8_t contrast) {
applog(LOG_INF, "Set contrast to %d", contrast);
const uint8_t cmdbuf[] = {254, 80, contrast};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_set_backlight(struct lcd_t *lcd, const bool enabled) {
applog(LOG_INF, "Set backlight to %s", enabled ? "true" : "false");
const uint8_t cmdbuf[] = {254, (enabled ? 66 : 70), 255};
lcd_write(lcd, cmdbuf, enabled ? 3 : 2);
}
void lcd_set_cursor(struct lcd_t *lcd, const uint8_t row, const uint8_t col) {
const uint8_t cmdbuf[] = {254, 71, (col + 1), (row + 1)};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
/* reconsider so im not writing every time. prepend custom chars to the write buffer? */
void lcd_set_custom_char(struct lcd_t *lcd, const uint8_t index, const lcd_glyph_t *glyph) {
uint8_t cmdbuf[] = {254, 78, index, 0, 0, 0, 0, 0, 0, 0, 0};
memcpy(&cmdbuf[3], glyph, sizeof(lcd_glyph_t));
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
/* immediately writes string to the lcd at current cursor pos */
void lcd_write_str(struct lcd_t *lcd, const char *str, const uint8_t len) {
lcd_write(lcd, (const uint8_t *)str, len);
}
void lcd_write_str_pos(struct lcd_t *lcd, const char *str, const uint8_t len, const uint8_t posX, const uint8_t posY) {
lcd_set_cursor(lcd, posX, posY);
lcd_write(lcd, (const uint8_t *)str, len);
}
/* writes string to write buffer at calculated pos, we trust it to be null terminated */
void lcd_write_buf(struct lcd_t *lcd, const uint8_t row, const uint8_t col, const char *str) {
size_t len = 0;
while (str[len] != '\0')
len++;
len = MIN(len, sizeof(lcd->writebuf) - col);
uint8_t pos = 4 + col + (LCD_COLS * (row % 2)); // skip 4 bytes to leave room for set cursor command
if (row >= 2)
pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
memcpy(lcd->writebuf + pos, str, len);
}
/* hyperoptimized function to write a formatted string to lcd buffer. */
/* we cant just snprintf to the buffer directly bc it null terminates */
inline void lcd_writef(struct lcd_t *lcd, const uint8_t x, const uint8_t y, const char *fmt, ...) {
char tmp[LCD_COLS + 1];
va_list ap;
va_start(ap, fmt);
uint8_t len = vsnprintf(tmp, sizeof(tmp), fmt, ap); // print fmt and ap to a temporary buffer
va_end(ap);
len = MIN(len, LCD_COLS - x);
uint8_t pos = 4 + x + (LCD_COLS * (y % 2)); // skip 4 bytes to leave room for set cursor command
if (y >= 2)
pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
memcpy(lcd->writebuf + pos, tmp, len); // copy to lcd writebuffer address + whatever pos is
}
inline void lcd_clear_buf(struct lcd_t *lcd) {
memset(lcd->writebuf, ' ', sizeof(lcd->writebuf));
}
/* flushes writebuffer to lcd, hyperoptmized, flushes buffer and commands in a single write operation */
inline void lcd_flush_buf(struct lcd_t *lcd) {
const uint8_t set_disp[] = {254, 71, 1, 1}; // set cursor to char 1, line 1
memcpy(&lcd->writebuf[0], set_disp, 4);
memcpy(&lcd->writebuf[2 * LCD_COLS + 4], set_disp, 4);
lcd->writebuf[2 * LCD_COLS + 7] = 3; // overwrite set cursor c1 l1 to c1 l3
lcd_write(lcd, (const uint8_t *)lcd->writebuf, sizeof(lcd->writebuf));
}
// i have a whole flushbuf func to avoid too many writes, but im writing on every bar, need to rethink this.
// we could have a separate pre-char buffer and put all the setCustomChar commands to that?
inline char lcd_bar(struct lcd_t *lcd, const uint8_t char_id, const double value) {
uint8_t b[8];
memset(b, 0b01110, sizeof(b));
for (uint8_t i = 0; i < (1.0 - value) * 8; i++)
b[i] = 0;
lcd_set_custom_char(lcd, char_id, &b);
return (char)char_id;
}
int lcd_init(struct lcd_t *lcd) {
const int ret = lcd_open(lcd, false);
if (ret < 0) {
lcd_close(lcd);
return -1;
}
struct termios term = {};
term.c_cflag = lcd->baud | CS8 | CLOCAL | CREAD;
tcflush(lcd->fd, TCIFLUSH);
tcsetattr(lcd->fd, TCSANOW, &term);
lcd_clear_buf(lcd);
const uint8_t cmdbuf[] = {254, 75, 254, 84};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
lcd_clear(lcd);
return 0;
}