char lcd
This commit is contained in:
parent
bd5a07924a
commit
b3bcb4108d
@ -1,8 +1,8 @@
|
||||
idf_component_register(
|
||||
SRCS "blk_box.cpp"
|
||||
INCLUDE_DIRS "include" "."
|
||||
REQUIRES led_strip
|
||||
PRIV_REQUIRES
|
||||
led_strip
|
||||
esp_driver_gpio
|
||||
esp_driver_i2c
|
||||
)
|
||||
|
||||
@ -3,9 +3,11 @@
|
||||
#include "blk_box_drivers/i2c.h"
|
||||
#include "blk_box_drivers/inputs.hpp"
|
||||
#include "blk_box_drivers/leds.hpp"
|
||||
#include "blk_box_drivers/char_lcd.hpp"
|
||||
|
||||
void init_blk_box(BlkBoxInitConfig cfg) {
|
||||
init_main_i2c();
|
||||
init_expander();
|
||||
init_leds();
|
||||
init_lcd();
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
set(SOURCES
|
||||
"char_lcd_headers.cpp"
|
||||
"char_lcd.cpp"
|
||||
"helpers.cpp"
|
||||
"inputs.cpp"
|
||||
"i2c.cpp"
|
||||
"lcd2004.cpp"
|
||||
"leds.cpp"
|
||||
)
|
||||
|
||||
|
||||
317
drivers/char_lcd.cpp
Normal file
317
drivers/char_lcd.cpp
Normal file
@ -0,0 +1,317 @@
|
||||
#include "blk_box_drivers/char_lcd.hpp"
|
||||
|
||||
#include "lcd2004.hpp"
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
// mutex is for all these vars
|
||||
SemaphoreHandle_t lcd_mutex;
|
||||
|
||||
LCD2004I2C lcd;
|
||||
|
||||
static CursorMode cursor_resting_mode = CursorMode::Hide;
|
||||
static CursorMode cursor_print_mode = CursorMode::Hide;
|
||||
static uint8_t resting_cursor_row = 0;
|
||||
static uint8_t resting_cursor_col = 0;
|
||||
|
||||
static bool is_header_enabled = false;
|
||||
|
||||
static const char *TAG = "char_lcd";
|
||||
static const char* EMPTY_ROW = " ";
|
||||
|
||||
// TODO: move this to power.cpp
|
||||
// static void monitor_battery_task(void* _arg) {
|
||||
// (void) _arg;
|
||||
|
||||
// while (true) {
|
||||
// vTaskDelay(pdMS_TO_TICKS(1'000));
|
||||
// lcd_print_header_bat();
|
||||
// }
|
||||
// }
|
||||
|
||||
// static bool replay_handler(const char* event, char* arg) {
|
||||
// if (strcmp(event, "LCD_CLEAR") == 0) {
|
||||
// lcd_clear();
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_SET_DISPLAY") == 0) {
|
||||
// lcd_set_display(strcmp(arg, "true") == 0);
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_CURSOR_VIS") == 0) {
|
||||
// lcd_set_cursor_vis(strcmp(arg, "true") == 0);
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_CURSOR_BLINK") == 0) {
|
||||
// lcd_set_cursor_blink(strcmp(arg, "true") == 0);
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_SCROLL_DISPLAY_LEFT") == 0) {
|
||||
// lcd_scroll_display_left();
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_SCROLL_DISPLAY_RIGHT") == 0) {
|
||||
// lcd_scroll_display_right();
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_LEFT_TO_RIGHT") == 0) {
|
||||
// lcd_left_to_right();
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_RIGHT_TO_LEFT") == 0) {
|
||||
// lcd_right_to_left();
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_AUTOSCROLL") == 0) {
|
||||
// lcd_set_autoscroll(strcmp(arg, "true") == 0);
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_BACKLIGHT") == 0) {
|
||||
// lcd_set_backlight(strcmp(arg, "true") == 0);
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_CREATE_CHAR") == 0) {
|
||||
// char* location_str = strtok(arg, ",");
|
||||
// uint8_t location = atoi(location_str);
|
||||
|
||||
// uint8_t charmap[8];
|
||||
// for (int i = 0; i < 8; i++) {
|
||||
// char* str = strtok(NULL, ",");
|
||||
// charmap[i] = atoi(str);
|
||||
// }
|
||||
|
||||
// lcd_create_char(location, charmap);
|
||||
// }
|
||||
// else if (strcmp(event, "LCD_PRINT") == 0) {
|
||||
// char* str = strtok(arg, ",");
|
||||
// uint8_t row = atoi(str);
|
||||
// str = strtok(NULL, ",");
|
||||
// uint8_t col = atoi(str);
|
||||
// // get remaining part of string.
|
||||
// str = strtok(NULL, "");
|
||||
|
||||
// // TODO: handle \r and \n
|
||||
// lcd_print(row, col, str);
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
void init_lcd() {
|
||||
ESP_LOGI(TAG, "Initializing LCD...");
|
||||
|
||||
lcd_mutex = xSemaphoreCreateRecursiveMutex();
|
||||
assert(lcd_mutex != NULL);
|
||||
|
||||
lcd = LCD2004I2C();
|
||||
lcd.init(LCD_ADDR);
|
||||
|
||||
// register_replay_fn(replay_handler);
|
||||
|
||||
// xTaskCreate(monitor_battery_task, "bat_monitor", 1024*2, nullptr, 0, nullptr);
|
||||
|
||||
ESP_LOGI(TAG, "LCD initialized!");
|
||||
}
|
||||
|
||||
void LCDController::clear() {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
|
||||
if (!is_header_enabled) {
|
||||
lcd.clear();
|
||||
|
||||
// if (is_state_tracking()) {
|
||||
// event_occured("LCD_CLEAR", NULL);
|
||||
// }
|
||||
} else {
|
||||
print(1, 0, EMPTY_ROW);
|
||||
print(2, 0, EMPTY_ROW);
|
||||
print(3, 0, EMPTY_ROW);
|
||||
}
|
||||
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
bool LCDController::get_backlight() {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
bool backlight = lcd.get_backlight();
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
return backlight;
|
||||
}
|
||||
|
||||
void LCDController::set_backlight(bool backlight) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
lcd.set_backlight(backlight);
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
|
||||
// if (is_state_tracking()) {
|
||||
// sprintf(buf, "%d", backlight);
|
||||
// event_occured("LCD_BACKLIGHT", backlight ? "true" : "false");
|
||||
// }
|
||||
}
|
||||
|
||||
void LCDController::set_display_show(bool show_display) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
lcd.show_hide(show_display);
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
bool LCDController::get_display_show() {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
bool show_display = lcd.get_show_hide();
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
return show_display;
|
||||
}
|
||||
|
||||
/// Changes the cursor display mode to the given mode.
|
||||
static void change_cursor_display(CursorMode cursor_display_mode) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
lcd.show_blink_cursor(
|
||||
cursor_display_mode != CursorMode::Hide,
|
||||
cursor_display_mode == CursorMode::Blink
|
||||
);
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
/// Moves the position of the resting cursor.
|
||||
///
|
||||
/// If the resting cursor mode is `Hide`, then this position has no effect on the display, but is still stored
|
||||
/// for when it is put into resting cursor mode.
|
||||
void LCDController::set_resting_cursor_pos(uint8_t row, uint8_t col) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
|
||||
// update pos if we're not hiding the resting cursor
|
||||
if (
|
||||
(resting_cursor_row != row || resting_cursor_col != col) &&
|
||||
cursor_resting_mode != CursorMode::Hide
|
||||
) {
|
||||
lcd.move_cursor(row, col);
|
||||
}
|
||||
|
||||
resting_cursor_row = row;
|
||||
resting_cursor_col = col;
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
/// Gets the position of the resting cursor.
|
||||
///
|
||||
/// This will return the value of the resting cursor position even if the resting cursor mode is `Hide`
|
||||
/// even though the values are not meaningful during that time.
|
||||
void LCDController::get_cursor_resting_position(uint8_t* row, uint8_t* col) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
if (row) *row = resting_cursor_row;
|
||||
if (col) *col = resting_cursor_col;
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
/// This puts the display in and out of resting cursor mode.
|
||||
///
|
||||
/// If the resting mode is not `Hide`, then the cursor will be displayed in the resting position when not printing.
|
||||
///
|
||||
/// The cursor mode will change to the "cursor print mode"
|
||||
/// during prints, then return to it's resting location and
|
||||
/// switch back to the "cursor resting mode".
|
||||
void LCDController::set_resting_cursor_mode(CursorMode new_mode) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
if (cursor_resting_mode != new_mode) {
|
||||
cursor_resting_mode = new_mode;
|
||||
change_cursor_display(cursor_resting_mode);
|
||||
if (cursor_resting_mode != CursorMode::Hide) {
|
||||
lcd.move_cursor(resting_cursor_row, resting_cursor_col);
|
||||
}
|
||||
}
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
/// Gets the display mode of the cursor when it is resting.
|
||||
CursorMode LCDController::get_resting_cursor_mode() {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
CursorMode mode = cursor_resting_mode;
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/// Sets the display mode of the cursor during printing.
|
||||
void LCDController::set_cursor_print_mode(CursorMode new_mode) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
cursor_print_mode = new_mode;
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
/// Gets the display mode of the cursor during printing.
|
||||
CursorMode LCDController::get_cursor_print_mode() {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
CursorMode mode = cursor_print_mode;
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
return mode;
|
||||
}
|
||||
|
||||
void LCDController::create_custom_char(uint8_t location, const uint8_t charmap[]) {
|
||||
if (location == 8) location = 0;
|
||||
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
lcd.create_custom_char(location, charmap);
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
|
||||
// if (is_state_tracking()) {
|
||||
// snprintf(buf, sizeof(buf),
|
||||
// "%d,%d,%d,%d,%d,%d,%d,%d,%d", location,
|
||||
// charmap[0], charmap[1], charmap[2], charmap[3], charmap[4], charmap[5], charmap[6], charmap[7]
|
||||
// );
|
||||
// event_occured("LCD_CREATE_CHAR", buf);
|
||||
// }
|
||||
}
|
||||
|
||||
/// Prints a string to the given row and column.
|
||||
///
|
||||
/// Do not print across lines, as that leads to goofy behavior.
|
||||
void LCDController::print(uint8_t row, uint8_t col, const char* str) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
lcd.move_cursor(row, col);
|
||||
change_cursor_display(cursor_print_mode);
|
||||
lcd.write_str(str);
|
||||
|
||||
if (cursor_resting_mode != CursorMode::Hide) {
|
||||
lcd.move_cursor(resting_cursor_row, resting_cursor_col);
|
||||
}
|
||||
if (cursor_resting_mode != cursor_print_mode) {
|
||||
change_cursor_display(cursor_resting_mode);
|
||||
}
|
||||
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
|
||||
// if (is_state_tracking()) {
|
||||
// // TODO: handle \r and \n and others
|
||||
// snprintf(buf, sizeof(buf), "%d,%d,%s", row, col, str);
|
||||
// event_occured("LCD_PRINT", buf);
|
||||
// }
|
||||
}
|
||||
|
||||
void LCDController::set_lcd_header_enabled(bool enable) {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
bool old_header_enabled = is_header_enabled;
|
||||
is_header_enabled = enable;
|
||||
|
||||
// print the header in response to enabling/disabling it
|
||||
if (enable && !old_header_enabled) {
|
||||
print_header();
|
||||
} else if (!enable && old_header_enabled) {
|
||||
print(0, 0, EMPTY_ROW);
|
||||
}
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
bool LCDController::header_enabled() {
|
||||
xSemaphoreTakeRecursive(lcd_mutex, portMAX_DELAY);
|
||||
bool enabled = is_header_enabled;
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
bool LCDController::lock(uint32_t ticks_to_wait) {
|
||||
return xSemaphoreTakeRecursive(lcd_mutex, ticks_to_wait);
|
||||
}
|
||||
|
||||
void LCDController::unlock() {
|
||||
xSemaphoreGiveRecursive(lcd_mutex);
|
||||
}
|
||||
|
||||
void LCDController::print_header() {
|
||||
// TODO:
|
||||
// lcd_print_header_star_code();
|
||||
// lcd_print_header_step();
|
||||
// lcd_print_header_bat();
|
||||
}
|
||||
0
drivers/char_lcd_headers.cpp
Normal file
0
drivers/char_lcd_headers.cpp
Normal file
28
drivers/helpers.cpp
Normal file
28
drivers/helpers.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "blk_box_drivers/helpers.hpp"
|
||||
#include "blk_box_drivers/char_lcd.hpp"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void lcd_do_splash() {
|
||||
const uint8_t custom_char[6][8] = {
|
||||
{ 0x01, 0x01, 0x02, 0x02, 0x07, 0x07, 0x0F, 0x0D },
|
||||
{ 0x10, 0x10, 0x18, 0x18, 0x1C, 0x0C, 0x0E, 0x06 },
|
||||
{ 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x07, 0x07 },
|
||||
{ 0x19, 0x1B, 0x13, 0x17, 0x07, 0x0F, 0x0F, 0x1F },
|
||||
{ 0x13, 0x1B, 0x1F, 0x1F, 0x00, 0x1F, 0x1F, 0x1F },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x00, 0x18, 0x1C, 0x1C },
|
||||
};
|
||||
|
||||
LCDController::lock(portMAX_DELAY);
|
||||
LCDController::create_custom_char(1, custom_char[0]);
|
||||
LCDController::create_custom_char(2, custom_char[1]);
|
||||
LCDController::create_custom_char(3, custom_char[2]);
|
||||
LCDController::create_custom_char(4, custom_char[3]);
|
||||
LCDController::create_custom_char(5, custom_char[4]);
|
||||
LCDController::create_custom_char(6, custom_char[5]);
|
||||
|
||||
LCDController::print(1, 6, "\x01\x02Marino");
|
||||
LCDController::print(2, 5, "\x03\x04\x05\x06""DEV");
|
||||
LCDController::unlock();
|
||||
}
|
||||
328
drivers/lcd2004.cpp
Normal file
328
drivers/lcd2004.cpp
Normal file
@ -0,0 +1,328 @@
|
||||
#include "lcd2004.hpp"
|
||||
|
||||
#include "pins.h"
|
||||
#include "blk_box_drivers/i2c.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
const static char* TAG = "LCD2004";
|
||||
|
||||
// masks
|
||||
const uint8_t ENABLE_MASK = 0x04;
|
||||
const uint8_t BACKLIGHT_MASK = 0x08;
|
||||
const uint8_t DATA_MASK = 0x01;
|
||||
|
||||
const uint8_t CMD_CLEAR_DISPLAY = 0x01;
|
||||
|
||||
const uint8_t CMD_RETURN_HOME = 0x02;
|
||||
|
||||
const uint8_t CMD_ENTRY_MODE_SET = 0x04;
|
||||
const uint8_t ENTRY_MODE_INC_DEC = 0x02;
|
||||
const uint8_t ENTRY_MODE_SHIFT = 0x01;
|
||||
|
||||
const uint8_t CMD_DISPLAY_CONTROL = 0x08;
|
||||
const uint8_t DISPLAY_CONTROL_SHOW = 0x04;
|
||||
const uint8_t DISPLAY_CONTROL_CURSOR = 0x02;
|
||||
const uint8_t DISPLAY_CONTROL_BLINK = 0x01;
|
||||
|
||||
const uint8_t CMD_CURSOR_OR_DISPLAY_SHIFT = 0x10;
|
||||
const uint8_t CURSOR_OR_DISPLAY_SHIFT_SC = 0x08;
|
||||
const uint8_t CURSOR_OR_DISPLAY_SHIFT_RL = 0x04;
|
||||
|
||||
const uint8_t CMD_FUNCTION_SET = 0x20;
|
||||
const uint8_t FUNCTION_SET_DL = 0x10;
|
||||
const uint8_t FUNCTION_SET_N = 0x08;
|
||||
const uint8_t FUNCTION_SET_F = 0x04;
|
||||
|
||||
const uint8_t CMD_SET_CGRAM_ADDRESS = 0x40;
|
||||
const uint8_t CMD_SET_DDRAM_ADDRESS = 0x80;
|
||||
|
||||
LCD2004I2C::LCD2004I2C() {
|
||||
backlight = 0;
|
||||
display_control = 0;
|
||||
entry_mode = 0;
|
||||
}
|
||||
|
||||
void LCD2004I2C::init(uint8_t addr) {
|
||||
i2c_device_config_t dev_config = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = addr,
|
||||
.scl_speed_hz = LCD2004_CLK_SPEED_HZ,
|
||||
.scl_wait_us = 0, // default
|
||||
.flags = {
|
||||
.disable_ack_check = 0,
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: replace all these ESP_ERROR_CHECK with proper error handling that doesn't just crash the program
|
||||
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_main_bus_handle, &dev_config, &lcd_device_handle));
|
||||
ESP_LOGD(TAG, "LCD2004 device added to bus");
|
||||
|
||||
// Initialize the LCD
|
||||
// set to 8-bit mode to bring it to a known state
|
||||
for (int i = 0; i < 3; i++) {
|
||||
send4_cmd(CMD_FUNCTION_SET | FUNCTION_SET_DL);
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
}
|
||||
|
||||
// set to 4-bit mode, while in 8 bit mode
|
||||
send4_cmd(CMD_FUNCTION_SET);
|
||||
|
||||
// use all 4 rows
|
||||
send_cmd(CMD_FUNCTION_SET | FUNCTION_SET_N);
|
||||
|
||||
// display, set cursor visible
|
||||
send_cmd(CMD_DISPLAY_CONTROL | DISPLAY_CONTROL_SHOW);
|
||||
|
||||
// clear display
|
||||
send_cmd(CMD_CLEAR_DISPLAY);
|
||||
|
||||
// set cursor to increment to the right
|
||||
send_cmd(CMD_ENTRY_MODE_SET | ENTRY_MODE_INC_DEC);
|
||||
|
||||
display_control = DISPLAY_CONTROL_SHOW;
|
||||
entry_mode = ENTRY_MODE_INC_DEC;
|
||||
|
||||
ESP_LOGI(TAG, "LCD2004 initialized!");
|
||||
}
|
||||
|
||||
// i2c wrappers
|
||||
|
||||
/// Sends `cmd` to the device as a command.
|
||||
void LCD2004I2C::send_cmd(uint8_t cmd) {
|
||||
uint8_t control_bits = backlight;
|
||||
|
||||
uint8_t high = control_bits | (cmd & 0xF0);
|
||||
uint8_t high_enable = high | ENABLE_MASK;
|
||||
uint8_t low = control_bits | ((cmd << 4) & 0xF0);
|
||||
uint8_t low_enable = low | ENABLE_MASK;
|
||||
|
||||
uint8_t i2c_data[] = {
|
||||
high_enable,
|
||||
high,
|
||||
low_enable,
|
||||
low
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
void LCD2004I2C::send_data(uint8_t data) {
|
||||
uint8_t control_bits = DATA_MASK | backlight;
|
||||
|
||||
uint8_t high = control_bits | (data & 0xF0);
|
||||
uint8_t high_enable = high | ENABLE_MASK;
|
||||
uint8_t low = control_bits | ((data << 4) & 0xF0);
|
||||
uint8_t low_enable = low | ENABLE_MASK;
|
||||
|
||||
// data pins are on pins D7 to D4
|
||||
uint8_t i2c_data[] = {
|
||||
high_enable,
|
||||
high,
|
||||
low_enable,
|
||||
low
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
/// Sends the ***HIGH*** 4 bits of `cmd` as a command.
|
||||
void LCD2004I2C::send4_cmd(uint8_t cmd) {
|
||||
uint8_t control_bits = backlight;
|
||||
|
||||
uint8_t high = control_bits | (cmd & 0xF0);
|
||||
uint8_t high_enable = high | ENABLE_MASK;
|
||||
|
||||
// data pins are on pins D7 to D4
|
||||
uint8_t i2c_data[] = {
|
||||
high_enable,
|
||||
high
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, i2c_data, sizeof(i2c_data), I2C_MASTER_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
// LCD2004 class implementations
|
||||
|
||||
bool LCD2004I2C::get_backlight() {
|
||||
return backlight != 0;
|
||||
}
|
||||
|
||||
void LCD2004I2C::set_backlight(bool backlight) {
|
||||
if (get_backlight() == backlight) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->backlight = backlight ? BACKLIGHT_MASK : 0;
|
||||
uint8_t data = this->backlight;
|
||||
ESP_ERROR_CHECK(i2c_master_transmit(lcd_device_handle, &data, 1, I2C_MASTER_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
void LCD2004I2C::clear() {
|
||||
send_cmd(CMD_CLEAR_DISPLAY);
|
||||
esp_rom_delay_us(1600);
|
||||
entry_mode |= ENTRY_MODE_INC_DEC;
|
||||
}
|
||||
|
||||
void LCD2004I2C::move_cursor(uint8_t row, uint8_t col) {
|
||||
const uint8_t ROW_OFFSETS[4] = {0x00, 0x40, 0x14, 0x54};
|
||||
uint8_t addr = ROW_OFFSETS[row] + col;
|
||||
send_cmd(CMD_SET_DDRAM_ADDRESS | addr);
|
||||
}
|
||||
|
||||
bool LCD2004I2C::get_show_hide() {
|
||||
return (display_control & DISPLAY_CONTROL_SHOW) != 0;
|
||||
}
|
||||
|
||||
void LCD2004I2C::show_hide(bool show) {
|
||||
if (get_show_hide() == show) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (show) {
|
||||
display_control |= DISPLAY_CONTROL_SHOW;
|
||||
} else {
|
||||
display_control &= ~DISPLAY_CONTROL_SHOW;
|
||||
}
|
||||
send_cmd(CMD_DISPLAY_CONTROL | display_control);
|
||||
}
|
||||
|
||||
bool LCD2004I2C::get_show_cursor() {
|
||||
return (display_control & DISPLAY_CONTROL_CURSOR) != 0;
|
||||
}
|
||||
|
||||
void LCD2004I2C::show_cursor(bool show_cursor) {
|
||||
if (get_show_cursor() == show_cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (show_cursor) {
|
||||
display_control |= DISPLAY_CONTROL_CURSOR;
|
||||
} else {
|
||||
display_control &= ~DISPLAY_CONTROL_CURSOR;
|
||||
}
|
||||
send_cmd(CMD_DISPLAY_CONTROL | display_control);
|
||||
}
|
||||
|
||||
bool LCD2004I2C::get_blink_cursor() {
|
||||
return (display_control & DISPLAY_CONTROL_BLINK) != 0;
|
||||
}
|
||||
|
||||
void LCD2004I2C::blink_cursor(bool blink_cursor) {
|
||||
if (get_blink_cursor() == blink_cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blink_cursor) {
|
||||
display_control |= DISPLAY_CONTROL_BLINK;
|
||||
} else {
|
||||
display_control &= ~DISPLAY_CONTROL_BLINK;
|
||||
}
|
||||
send_cmd(CMD_DISPLAY_CONTROL | display_control);
|
||||
}
|
||||
|
||||
void LCD2004I2C::show_blink_cursor(bool show_cursor, bool blink_cursor) {
|
||||
if (get_show_cursor() == show_cursor && get_blink_cursor() == blink_cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blink_cursor) {
|
||||
display_control |= DISPLAY_CONTROL_BLINK;
|
||||
} else {
|
||||
display_control &= ~DISPLAY_CONTROL_BLINK;
|
||||
}
|
||||
if (show_cursor) {
|
||||
display_control |= DISPLAY_CONTROL_CURSOR;
|
||||
} else {
|
||||
display_control &= ~DISPLAY_CONTROL_CURSOR;
|
||||
}
|
||||
send_cmd(CMD_DISPLAY_CONTROL | display_control);
|
||||
}
|
||||
|
||||
bool LCD2004I2C::get_scroll_direction() {
|
||||
return (entry_mode & ENTRY_MODE_INC_DEC) != 0;
|
||||
}
|
||||
|
||||
void LCD2004I2C::scroll_direction(bool left_to_right) {
|
||||
if (get_scroll_direction() == left_to_right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (left_to_right) {
|
||||
entry_mode |= ENTRY_MODE_INC_DEC;
|
||||
} else {
|
||||
entry_mode &= ~ENTRY_MODE_INC_DEC;
|
||||
}
|
||||
send_cmd(CMD_ENTRY_MODE_SET | entry_mode);
|
||||
}
|
||||
|
||||
bool LCD2004I2C::get_display_shift() {
|
||||
return (entry_mode & ENTRY_MODE_SHIFT) != 0;
|
||||
}
|
||||
|
||||
void LCD2004I2C::shift_display(bool shift_display) {
|
||||
if (get_display_shift() == shift_display) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shift_display) {
|
||||
entry_mode |= ENTRY_MODE_SHIFT;
|
||||
} else {
|
||||
entry_mode &= ~ENTRY_MODE_SHIFT;
|
||||
}
|
||||
send_cmd(CMD_ENTRY_MODE_SET | entry_mode);
|
||||
}
|
||||
|
||||
void LCD2004I2C::shift_cursor_left() {
|
||||
send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT);
|
||||
}
|
||||
|
||||
void LCD2004I2C::shift_cursor_right() {
|
||||
send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_RL);
|
||||
}
|
||||
|
||||
void LCD2004I2C::shift_display_left() {
|
||||
send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC);
|
||||
}
|
||||
|
||||
void LCD2004I2C::shift_display_right() {
|
||||
send_cmd(CMD_CURSOR_OR_DISPLAY_SHIFT | CURSOR_OR_DISPLAY_SHIFT_SC | CURSOR_OR_DISPLAY_SHIFT_RL);
|
||||
}
|
||||
|
||||
void LCD2004I2C::create_custom_char(uint8_t location, const uint8_t char_map[8]) {
|
||||
uint8_t loc = location % 8;
|
||||
send_cmd(CMD_SET_CGRAM_ADDRESS | (loc << 3));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
send_data(char_map[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void LCD2004I2C::print_char(char c) {
|
||||
if (c == '\x08') {
|
||||
// custom char 0 is represented by \x08 since \x00 is a null terminator
|
||||
c = 0;
|
||||
}
|
||||
send_data(static_cast<uint8_t>(c));
|
||||
}
|
||||
|
||||
void LCD2004I2C::print_u8(uint8_t b) {
|
||||
if (b == 0x08) {
|
||||
// custom char 0 is represented by \x08 since \x00 is a null terminator
|
||||
b = 0;
|
||||
}
|
||||
send_data(b);
|
||||
}
|
||||
|
||||
void LCD2004I2C::write_str(const char* s) {
|
||||
// TODO: replace this with a single I2C transmission.
|
||||
while (*s) {
|
||||
print_char(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
112
drivers/lcd2004.hpp
Normal file
112
drivers/lcd2004.hpp
Normal file
@ -0,0 +1,112 @@
|
||||
#ifndef LCD2004_HPP
|
||||
#define LCD2004_HPP
|
||||
|
||||
#include "driver/i2c_master.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define LCD2004_CLK_SPEED_HZ 100000
|
||||
|
||||
#define I2C_MASTER_TIMEOUT_MS 1000
|
||||
|
||||
class LCD2004I2C {
|
||||
private:
|
||||
i2c_master_dev_handle_t lcd_device_handle;
|
||||
/// Weather or not the backlight is on.
|
||||
/// `BACKLIGHT_MASK` if backlight is on,
|
||||
/// `0` if backlight is off.
|
||||
uint8_t backlight;
|
||||
/// The mask for the display control command.
|
||||
uint8_t display_control;
|
||||
uint8_t entry_mode;
|
||||
|
||||
void send_cmd(uint8_t cmd);
|
||||
void send_data(uint8_t data);
|
||||
void send4_cmd(uint8_t cmd);
|
||||
|
||||
public:
|
||||
/// Constructs a new LCD2004 device.
|
||||
LCD2004I2C();
|
||||
|
||||
/// Initializes the LCD2004 device. Must be called before any other methods.
|
||||
///
|
||||
/// The address is usually `0x27`.
|
||||
void init(uint8_t addr);
|
||||
|
||||
/// Gets the state of the backlight of the module.
|
||||
bool get_backlight();
|
||||
|
||||
/// Sets the backlight of the module to be on or off.
|
||||
void set_backlight(bool backlight);
|
||||
|
||||
/// Clears the display, and resets the cursor to
|
||||
void clear();
|
||||
|
||||
/// Moves the cursor to `row` and `col`.
|
||||
void move_cursor(uint8_t row, uint8_t col);
|
||||
|
||||
/// Returns `true` iff the display is showing.
|
||||
bool get_show_hide();
|
||||
|
||||
/// Shows or hides the display.
|
||||
void show_hide(bool show);
|
||||
|
||||
/// Returns `true` iff the cursor display is showing.
|
||||
bool get_show_cursor();
|
||||
|
||||
/// Shows or hides the cursor.
|
||||
void show_cursor(bool show_cursor);
|
||||
|
||||
/// Returns `true` iff the cursor is blinking.
|
||||
///
|
||||
/// This value is stored even if the cursor is not visible.
|
||||
bool get_blink_cursor();
|
||||
|
||||
/// Blinks or stops blinking the cursor.
|
||||
void blink_cursor(bool blink_cursor);
|
||||
|
||||
/// Sets the cursor show state and blink state.
|
||||
void show_blink_cursor(bool show_cursor, bool blink_cursor);
|
||||
|
||||
/// Returns `true` iff the display scrolls left to right.
|
||||
bool get_scroll_direction();
|
||||
|
||||
/// Sets the scroll direction for the display.
|
||||
void scroll_direction(bool left_to_right);
|
||||
|
||||
/// Returns `true` iff the display is shifting with the cursor.
|
||||
bool get_display_shift();
|
||||
|
||||
/// Sets the display to shift with the cursor.
|
||||
///
|
||||
/// This is like autoscrolling.
|
||||
void shift_display(bool shift_display);
|
||||
|
||||
/// Shifts the cursor left 1
|
||||
void shift_cursor_left();
|
||||
|
||||
/// Shifts the cursor right 1
|
||||
void shift_cursor_right();
|
||||
|
||||
/// Shifts the display left 1
|
||||
void shift_display_left();
|
||||
|
||||
/// Shifts the display right 1
|
||||
void shift_display_right();
|
||||
|
||||
/// Sets the character map for a custom character.
|
||||
///
|
||||
/// There are 8 locations (0..=7), for 8 characters.
|
||||
/// You can print these characters by sending '\x00' - '\x07'.
|
||||
void create_custom_char(uint8_t location, const uint8_t char_map[8]);
|
||||
|
||||
/// Prints a single character to the display.
|
||||
void print_char(char c);
|
||||
|
||||
/// Prints a single byte to the display.
|
||||
void print_u8(uint8_t b);
|
||||
|
||||
/// Writes a string to the display.
|
||||
void write_str(const char* s);
|
||||
};
|
||||
|
||||
#endif // LCD2004_HPP
|
||||
@ -57,6 +57,8 @@ void init_leds() {
|
||||
|
||||
/// Create the LED strip object
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||
|
||||
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||
}
|
||||
|
||||
void LEDController::set_led(uint32_t led, uint32_t color) {
|
||||
|
||||
104
include/blk_box_drivers/char_lcd.hpp
Normal file
104
include/blk_box_drivers/char_lcd.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
#ifndef CHAR_LCD_HPP
|
||||
#define CHAR_LCD_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define LCD_ADDR 0x27
|
||||
#define LCD_ROWS 4
|
||||
#define LCD_COLS 20
|
||||
|
||||
/// A possible display mode for the cursor.
|
||||
enum class CursorMode: uint8_t {
|
||||
/// The cursor is not displayed.
|
||||
Hide = 0b00,
|
||||
/// The cursor is displayed as an underline.
|
||||
Show = 0b01,
|
||||
/// The cursor is displayed as a blinking block.
|
||||
Blink = 0b11,
|
||||
};
|
||||
|
||||
/// Initializes the 2004 Character LCD
|
||||
void init_lcd();
|
||||
|
||||
class LCDController {
|
||||
public:
|
||||
/// Clears the display and resets the cursor to the home position (0, 0).
|
||||
static void clear();
|
||||
|
||||
/// Gets the backlight state of the LCD.
|
||||
static bool get_backlight();
|
||||
/// Sets the backlight state of the LCD.
|
||||
static void set_backlight(bool backlight);
|
||||
|
||||
/// Shows or hides the display.
|
||||
static void set_display_show(bool show_display);
|
||||
|
||||
/// Gets the display state of the LCD.
|
||||
static bool get_display_show();
|
||||
|
||||
/// Moves the position of the resting cursor.
|
||||
///
|
||||
/// If the resting cursor mode is `Hide`, then this position has no effect on the display, but is still stored
|
||||
/// for when it is put into resting cursor mode.
|
||||
static void set_resting_cursor_pos(uint8_t row, uint8_t col);
|
||||
|
||||
/// Gets the position of the resting cursor.
|
||||
///
|
||||
/// This will return the value of the resting cursor position even if the resting cursor mode is `Hide`
|
||||
/// even though the values are not meaningful during that time.
|
||||
static void get_cursor_resting_position(uint8_t* row, uint8_t* col);
|
||||
|
||||
/// This puts the display in and out of resting cursor mode.
|
||||
///
|
||||
/// If the resting mode is not `Hide`, then the cursor will be displayed in the resting position when not printing.
|
||||
///
|
||||
/// The cursor mode will change to the "cursor print mode"
|
||||
/// during prints, then return to it's resting location and
|
||||
/// switch back to the "cursor resting mode".
|
||||
static void set_resting_cursor_mode(CursorMode new_mode);
|
||||
|
||||
/// Gets the display mode of the cursor when it is resting.
|
||||
static CursorMode get_resting_cursor_mode();
|
||||
|
||||
/// Sets the display mode of the cursor during printing.
|
||||
static void set_cursor_print_mode(CursorMode new_mode);
|
||||
|
||||
/// Gets the display mode of the cursor during printing.
|
||||
static CursorMode get_cursor_print_mode();
|
||||
|
||||
/// Sets the character map for a custom character.
|
||||
///
|
||||
/// There are 8 locations (1..=8), for 8 characters.
|
||||
/// You can print these characters by sending '\x01' - '\x08'.
|
||||
static void create_custom_char(uint8_t location, const uint8_t charmap[]);
|
||||
|
||||
/// Prints a string to the given row and column.
|
||||
///
|
||||
/// Do not print across lines, as that leads to goofy behavior.
|
||||
static void print(uint8_t row, uint8_t col, const char* str);
|
||||
|
||||
/// Enables or disables the header row.
|
||||
///
|
||||
/// Try to keep it enabled, since its used as a status display and shows the starcode the user is typing.
|
||||
///
|
||||
/// But can be disabled if needed.
|
||||
static void set_lcd_header_enabled(bool enable);
|
||||
|
||||
/// Gets whether the header row is enabled.
|
||||
static bool header_enabled();
|
||||
|
||||
/// Prints the LCD header. Usually, you will not need to call this manually.
|
||||
static void print_header();
|
||||
|
||||
/// Locks the LCD to allow you to perform multiple operations uninterrupted.
|
||||
///
|
||||
/// Every lock should have an unlock. Do not hold the lock for too long.
|
||||
///
|
||||
/// This will wait up to `ticks_to_wait` ticks to acquire the lock, and return `false` if it fails to acquire the lock within that time.
|
||||
static bool lock(uint32_t ticks_to_wait);
|
||||
|
||||
/// Unlocks the LCD after a lock.
|
||||
static void unlock();
|
||||
};
|
||||
|
||||
#endif /* CHAR_LCD_HPP */
|
||||
9
include/blk_box_drivers/helpers.hpp
Normal file
9
include/blk_box_drivers/helpers.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef HELPERS_HPP
|
||||
#define HELPERS_HPP
|
||||
|
||||
// Does the MarinoDev splash screen.
|
||||
void lcd_do_splash();
|
||||
|
||||
|
||||
|
||||
#endif // HELPERS_HPP
|
||||
Loading…
Reference in New Issue
Block a user