Random
This commit is contained in:
6
a
Normal file
6
a
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pactl set-card-profile bluez_card.7C_89_56_4C_BD_1B a2dp_source
|
||||||
|
pactl list short sinks
|
||||||
|
pactl list short sources
|
||||||
|
pactl load-module module-loopback source=bluez_source.7C_89_56_4C_BD_1B.a2dp_source sink=alsa_output.platform-bcm2835_audio.analog-stereo
|
||||||
|
pactl list short sinks
|
||||||
|
pactl set-sink-volume 0 50%
|
||||||
25
doc/features.md
Normal file
25
doc/features.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Planned features
|
||||||
|
|
||||||
|
## Pages
|
||||||
|
|
||||||
|
- [ ] Page description format
|
||||||
|
- [ ] Pages auto generated for LCD GLCD
|
||||||
|
- [ ] Self organizing page list
|
||||||
|
|
||||||
|
## Music
|
||||||
|
|
||||||
|
- [ ] Bluetooth audio
|
||||||
|
|
||||||
|
## Utility
|
||||||
|
|
||||||
|
- [ ] Shutdown/restart program ability
|
||||||
|
- [ ] Shutdown/reboot system ability
|
||||||
|
- [ ] View local IP
|
||||||
|
- [ ] Manage Wi-Fi
|
||||||
|
- [ ] Manage Bluetooth
|
||||||
|
|
||||||
|
## OBD-II
|
||||||
|
|
||||||
|
- [ ] Connect to OBD-II via bluetooth
|
||||||
|
- [ ] Display measured values
|
||||||
|
- [ ] Display DTCs
|
||||||
56
include/drivers/display.h
Normal file
56
include/drivers/display.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//display.h - Raw control over I2C display hardware
|
||||||
|
|
||||||
|
#ifndef DISPLAY_H_
|
||||||
|
#define DISPLAY_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define DISP_LINES 4
|
||||||
|
#define DISP_COLS 20
|
||||||
|
#define DISP_SIZE (DISP_COLS * DISP_LINES)
|
||||||
|
|
||||||
|
#define DISP_I2C0 0
|
||||||
|
#define DISP_I2C1 1
|
||||||
|
|
||||||
|
#define DISP_CHAR_ARROWR '\x7e'
|
||||||
|
#define DISP_CHAR_ARROWL '\x7f'
|
||||||
|
#define DISP_CHAR_DOT_HIGH '\xa5'
|
||||||
|
#define DISP_CHAR_BOX '\xdb'
|
||||||
|
#define DISP_CHAR_DEGREE '\xdf'
|
||||||
|
#define DISP_CHAR_ALPHA '\xe0'
|
||||||
|
#define DISP_CHAR_BETA '\xe2'
|
||||||
|
#define DISP_CHAR_EPSILON '\xe3'
|
||||||
|
#define DISP_CHAR_MU '\xe4'
|
||||||
|
#define DISP_CHAR_SQRT '\xe9'
|
||||||
|
#define DISP_CHAR_OMEGA '\xf4'
|
||||||
|
#define DISP_CHAR_SUM '\xf7'
|
||||||
|
#define DISP_CHAR_PI '\xf8'
|
||||||
|
#define DISP_CHAR_DOT_DIV '\xfd'
|
||||||
|
#define DISP_CHAR_BLACK '\xff'
|
||||||
|
|
||||||
|
//TODO: Set custom chars
|
||||||
|
#define DISP_CHAR_NOTE '\x01'
|
||||||
|
#define DISP_CHAR_LIST '\x02'
|
||||||
|
#define DISP_CHAR_SEARCH '\x03'
|
||||||
|
|
||||||
|
typedef struct display_t display_t;
|
||||||
|
|
||||||
|
display_t *display_init(int bus, uint8_t addr);
|
||||||
|
void display_destroy(display_t *display);
|
||||||
|
|
||||||
|
void display_set_backlight(display_t *display, bool backlight);
|
||||||
|
void display_set_cursor(display_t *display, int lin, int col);
|
||||||
|
void display_clear(display_t *display);
|
||||||
|
void display_write_ch(display_t *display, char ch);
|
||||||
|
void display_write_str(display_t *display, char *str);
|
||||||
|
void display_write_str_at(display_t *display, char *str, int lin, int col);
|
||||||
|
|
||||||
|
void display_set_display_on(display_t *display, bool on);
|
||||||
|
void display_set_cursor_show(display_t *display, bool show);
|
||||||
|
void display_set_cursor_blink(display_t *display, bool blink);
|
||||||
|
void display_set_custom_char(display_t *display, uint8_t char_idx, uint64_t data);
|
||||||
|
|
||||||
|
#endif
|
||||||
21
include/systems/lcd_writer.h
Normal file
21
include/systems/lcd_writer.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//lcd_writer.h - Defines interface for buffered, async LCD display writting
|
||||||
|
|
||||||
|
#ifndef DISPLAY_WRITER_H_
|
||||||
|
#define DISPLAY_WRITER_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <drivers/display.h>
|
||||||
|
|
||||||
|
typedef struct lcd_writer_t lcd_writer_t;
|
||||||
|
|
||||||
|
lcd_writer_t *lcd_writer_init(display_t *display, bool bypass);
|
||||||
|
void lcd_writer_destroy(lcd_writer_t *lcdw);
|
||||||
|
|
||||||
|
void lcd_writer_clr(lcd_writer_t *lcdw);
|
||||||
|
void lcd_writer_clr_line(lcd_writer_t *lcdw, int lin);
|
||||||
|
void lcd_writer_chr(lcd_writer_t *lcdw, char ch, int lin, int col);
|
||||||
|
void lcd_writer_str(lcd_writer_t *lcdw, char *str, int lin, int col);
|
||||||
|
void lcd_writer_flush(lcd_writer_t *lcdw);
|
||||||
|
|
||||||
|
#endif
|
||||||
7
include/systems/pager.h
Normal file
7
include/systems/pager.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void pager_init();
|
||||||
|
void pager_destroy();
|
||||||
|
|
||||||
|
//void pager_add_page(page_t *page);
|
||||||
|
//void pager_draw(display_t *disp);
|
||||||
329
src/pages/display.c
Normal file
329
src/pages/display.c
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
#include "drivers/display.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <pigpio.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
271
src/pages/lcd_writer.c
Normal file
271
src/pages/lcd_writer.c
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
#include "systems/lcd_writer.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "drivers/display.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void *writer_thread_main(void *arg);
|
||||||
|
static void write_disp_diff(char *buff, char *cur, display_t *disp);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct lcd_writer_t
|
||||||
|
{
|
||||||
|
bool bypass;
|
||||||
|
display_t *display;
|
||||||
|
char buffer[DISP_SIZE];
|
||||||
|
|
||||||
|
pthread_t write_thread;
|
||||||
|
pthread_mutex_t write_mutex;
|
||||||
|
pthread_cond_t write_condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lcd_writer_t *lcd_writer_init(display_t *display, bool bypass)
|
||||||
|
{
|
||||||
|
lcd_writer_t *ret = malloc(sizeof(lcd_writer_t));
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_init failed to allocate memory.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bypass)
|
||||||
|
{
|
||||||
|
if (pthread_mutex_init(&ret->write_mutex, NULL))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_init failed to init required mutex.\n");
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_cond_init(&ret->write_condition, NULL))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_init failed to init required condition.\n");
|
||||||
|
pthread_mutex_destroy(&ret->write_mutex);
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_create(&ret->write_thread, NULL, writer_thread_main, ret))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_init failed to start separate thread.\n");
|
||||||
|
pthread_cond_destroy(&ret->write_condition);
|
||||||
|
pthread_mutex_destroy(&ret->write_mutex);
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->bypass = bypass;
|
||||||
|
ret->display = display;
|
||||||
|
memset(ret->buffer, ' ', DISP_SIZE);
|
||||||
|
display_clear(display);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_writer_destroy(lcd_writer_t *lcdw)
|
||||||
|
{
|
||||||
|
if (!lcdw)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_destroy received a NULL lcdw.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
{
|
||||||
|
pthread_cancel(lcdw->write_thread);
|
||||||
|
pthread_join(lcdw->write_thread, NULL);
|
||||||
|
pthread_cond_destroy(&lcdw->write_condition);
|
||||||
|
pthread_mutex_destroy(&lcdw->write_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(lcdw);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void lcd_writer_clr(lcd_writer_t *lcdw)
|
||||||
|
{
|
||||||
|
if (!lcdw)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_clr received a NULL lcdw.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
pthread_mutex_lock(&lcdw->write_mutex);
|
||||||
|
memset(lcdw->buffer, ' ', DISP_SIZE);
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
pthread_mutex_unlock(&lcdw->write_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_writer_clr_line(lcd_writer_t *lcdw, int lin)
|
||||||
|
{
|
||||||
|
if (!lcdw)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_clr_line received a NULL lcdw.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lin >= DISP_LINES)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_clr_line received too high a line number: %d.\n", lin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
pthread_mutex_lock(&lcdw->write_mutex);
|
||||||
|
memset(&lcdw->buffer[lin * DISP_COLS], ' ', DISP_COLS);
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
pthread_mutex_unlock(&lcdw->write_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_writer_chr(lcd_writer_t *lcdw, char ch, int lin, int col)
|
||||||
|
{
|
||||||
|
if (!lcdw)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_chr received a NULL lcdw.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lin >= DISP_LINES)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_chr received too high a line number: %d.\n", lin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (col >= DISP_COLS)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_chr received too high a column number: %d.\n", col);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
pthread_mutex_lock(&lcdw->write_mutex);
|
||||||
|
lcdw->buffer[lin * DISP_COLS + col] = ch;
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
pthread_mutex_unlock(&lcdw->write_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_writer_str(lcd_writer_t *lcdw, char *str, int lin, int col)
|
||||||
|
{
|
||||||
|
if (!lcdw)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_str received a NULL lcdw.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!str)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_str received a NULL string.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lin >= DISP_LINES)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_str received too high a line number: %d.\n", lin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (col >= DISP_COLS)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_str received too high a column number: %d.\n", col);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(str);
|
||||||
|
if ((int)len > DISP_COLS - col)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_str received too long a string. Truncating.\n");
|
||||||
|
len = DISP_COLS - col;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
pthread_mutex_lock(&lcdw->write_mutex);
|
||||||
|
memcpy(&lcdw->buffer[lin * DISP_COLS + col], str, len);
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
pthread_mutex_unlock(&lcdw->write_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lcd_writer_flush(lcd_writer_t *lcdw)
|
||||||
|
{
|
||||||
|
if (!lcdw)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARN: lcd_writer_flush received a NULL lcdw.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lcdw->bypass)
|
||||||
|
{
|
||||||
|
pthread_cond_signal(&lcdw->write_condition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise draw entire display syncronously
|
||||||
|
for (int l = 0; l < DISP_LINES; l++)
|
||||||
|
{
|
||||||
|
display_set_cursor(lcdw->display, l, 0);
|
||||||
|
for (int c = 0; c < DISP_COLS; c++)
|
||||||
|
display_write_ch(lcdw->display, lcdw->buffer[l * DISP_COLS + c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void *writer_thread_main(void *arg)
|
||||||
|
{
|
||||||
|
lcd_writer_t *lcdw = arg;
|
||||||
|
char buff[DISP_SIZE];
|
||||||
|
char cur[DISP_SIZE];
|
||||||
|
|
||||||
|
memset(buff, ' ', DISP_SIZE);
|
||||||
|
memset(cur, ' ', DISP_SIZE);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&lcdw->write_mutex);
|
||||||
|
//FIXME: Will miss a cond_signal if not blocked here
|
||||||
|
pthread_cond_wait(&lcdw->write_condition, &lcdw->write_mutex);
|
||||||
|
|
||||||
|
memcpy(buff, lcdw->buffer, DISP_SIZE);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&lcdw->write_mutex);
|
||||||
|
|
||||||
|
write_disp_diff(buff, cur, lcdw->display);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_disp_diff(char *buff, char *cur, display_t *disp)
|
||||||
|
{
|
||||||
|
//NOTE: set_cursor = 1 write => always worth when skipping characters
|
||||||
|
//NOTE: line changes always use set_cursor (because of unusual buffer address mapping)
|
||||||
|
int last_c;
|
||||||
|
|
||||||
|
for (int l = 0; l < DISP_LINES; l++)
|
||||||
|
{
|
||||||
|
last_c = -1;
|
||||||
|
for (int c = 0; c < DISP_COLS; c++)
|
||||||
|
{
|
||||||
|
int addr = l * DISP_COLS + c;
|
||||||
|
|
||||||
|
if (buff[addr] != cur[addr])
|
||||||
|
{
|
||||||
|
//Use set_cursor if first of line non-consecutive write
|
||||||
|
if (last_c == -1 || c - last_c > 1)
|
||||||
|
display_set_cursor(disp, l, c);
|
||||||
|
|
||||||
|
display_write_ch(disp, buff[addr]);
|
||||||
|
last_c = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update cur buffer
|
||||||
|
memcpy(cur, buff, DISP_SIZE);
|
||||||
|
}
|
||||||
1
src/pages/pager.c
Normal file
1
src/pages/pager.c
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "systems/pager.h"
|
||||||
Reference in New Issue
Block a user