From 1267d1356de31278ec6945aa0c77e73ac363149f Mon Sep 17 00:00:00 2001 From: Mitchell Marino Date: Sat, 12 Jul 2025 16:39:28 -0500 Subject: [PATCH] impl i2c mutex and some work on global star code handler --- .../SparkFunBQ27441/SparkFunBQ27441.cpp | 3 + main/drivers/all.cpp | 33 -------- main/drivers/all.h | 1 + main/drivers/char_lcd.cpp | 77 ++++++++++++------- main/drivers/char_lcd.h | 31 ++++++-- main/drivers/i2c.cpp | 32 ++++++++ main/drivers/i2c.h | 21 +++++ main/drivers/i2c_lcd_pcf8574.c | 11 ++- main/drivers/power.cpp | 4 +- main/drivers/power.h | 4 +- main/drivers/star_code.h | 11 +-- 11 files changed, 152 insertions(+), 76 deletions(-) create mode 100644 main/drivers/i2c.cpp create mode 100644 main/drivers/i2c.h diff --git a/main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp b/main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp index cbb3de1..47915d8 100644 --- a/main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp +++ b/main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp @@ -32,6 +32,7 @@ Arduino Uno (any 'duino should do) ******************************************************************************/ #include "SparkFunBQ27441.h" +#include "../i2c.h" /***************************************************************************** ************************** Initialization Functions ************************* @@ -710,7 +711,9 @@ bool BQ27441::writeExtendedData(uint8_t classID, uint8_t offset, uint8_t * data, int16_t BQ27441::i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count) { int16_t timeout = BQ72441_I2C_TIMEOUT; + xSemaphoreTake(i2c0_mutex, portMAX_DELAY); i2c_master_write_read_device(BQ72441_I2C_NUM, _deviceAddress, &subAddress, 1, dest, count, timeout); + xSemaphoreGive(i2c0_mutex); return timeout; } diff --git a/main/drivers/all.cpp b/main/drivers/all.cpp index 8fcbdb3..a634480 100644 --- a/main/drivers/all.cpp +++ b/main/drivers/all.cpp @@ -1,7 +1,6 @@ #include "all.h" static const char *TAG = "driver_all"; -static void init_i2c(); void init_drivers() { init_i2c(); @@ -18,35 +17,3 @@ void init_drivers() { init_leds(); init_power_board(); } - -/// @brief Initializes `I2C_NUM_0`. -/// -/// This is hooked up the to: -/// - The bottom half -/// - The char lcd -/// - The power board -/// - The MPU6050 -/// - The PERH port -/// - The Capacitive Touch Panel -static void init_i2c() { - ESP_LOGI(TAG, "Initializing i2c..."); - - i2c_config_t conf = { - .mode = I2C_MODE_MASTER, - .sda_io_num = GPIO_NUM_5, - .scl_io_num = GPIO_NUM_6, - .sda_pullup_en = GPIO_PULLUP_DISABLE, - .scl_pullup_en = GPIO_PULLUP_DISABLE, - // .sda_pullup_en = GPIO_PULLUP_ENABLE, - // .scl_pullup_en = GPIO_PULLUP_ENABLE, - .master = { - .clk_speed = 100*1000, - }, - .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL - }; - - ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf)); - ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0)); - - ESP_LOGI(TAG, "i2c initialized!"); -} \ No newline at end of file diff --git a/main/drivers/all.h b/main/drivers/all.h index 5db7c9a..72a02c3 100644 --- a/main/drivers/all.h +++ b/main/drivers/all.h @@ -4,6 +4,7 @@ #include "bottom_half.h" #include "char_lcd.h" #include "game_timer.h" +#include "i2c.h" #include "leds.h" #include "power.h" #include "sd.h" diff --git a/main/drivers/char_lcd.cpp b/main/drivers/char_lcd.cpp index cc4bca6..05b3ade 100644 --- a/main/drivers/char_lcd.cpp +++ b/main/drivers/char_lcd.cpp @@ -7,7 +7,11 @@ i2c_lcd_pcf8574_handle_t lcd; +static volatile bool header_enabled; + static const char *TAG = "char_lcd"; +static const char* EMPTY_ROW = " "; +static const char* EMPTY_ROW3 = " "; static char buf[65]; @@ -57,8 +61,7 @@ static bool replay_handler(const char* event, char* arg) { return true; } if (strcmp(event, "LCD_BACKLIGHT") == 0) { - uint32_t brightness = atoi(arg); - lcd_set_backlight(brightness); + lcd_set_backlight(strcmp(arg, "true") == 0); return true; } if (strcmp(event, "LCD_CREATE_CHAR") == 0) { @@ -102,19 +105,19 @@ void init_lcd() { } void lcd_clear() { - lcd_clear(&lcd); + if (!header_enabled) { + lcd_clear(&lcd); - if (is_state_tracking()) { - event_occured("LCD_CLEAR", NULL); + if (is_state_tracking()) { + event_occured("LCD_CLEAR", NULL); + } + } else { + lcd_print(1, 2, EMPTY_ROW3); } } void lcd_cursor_home() { - lcd_home(&lcd); - - if (is_state_tracking()) { - event_occured("LCD_CURSOR", "0,0"); - } + lcd_set_cursor_pos(0, 0); } void lcd_set_cursor_pos(uint8_t col, uint8_t row) { @@ -204,12 +207,12 @@ void lcd_set_autoscroll(bool autoscroll) { } } -void lcd_set_backlight(uint8_t brightness) { - lcd_set_backlight(&lcd, brightness); +void lcd_set_backlight(bool backlight) { + lcd_set_backlight(&lcd, backlight); if (is_state_tracking()) { - sprintf(buf, "%d", brightness); - event_occured("LCD_BACKLIGHT", buf); + sprintf(buf, "%d", backlight); + event_occured("LCD_BACKLIGHT", backlight ? "true" : "false"); } } @@ -225,16 +228,8 @@ void lcd_create_char(uint8_t location, uint8_t* charmap) { } } -void lcd_write(uint8_t value) { - lcd_write(&lcd, value); - - if (is_state_tracking()) { - sprintf(buf, "%d", value); - event_occured("LCD_WRITE", buf); - } -} - -void lcd_print(const char* str) { +void lcd_print(uint8_t col, uint8_t row, const char* str) { + lcd_set_cursor_pos(col, row); lcd_print(&lcd, str); if (is_state_tracking()) { @@ -243,7 +238,35 @@ void lcd_print(const char* str) { } } -void lcd_print(uint8_t col, uint8_t row, const char* str) { - lcd_set_cursor_pos(col, row); - lcd_print(str); +void set_lcd_header_enabled(bool enable) { + if (!header_enabled && enable) { + // enabling header + // TODO: enable header + } else if (header_enabled && !enable) { + // disabling header + lcd_print(1, 1, EMPTY_ROW); + } + header_enabled = enable; +} + +bool lcd_header_enabled() { + return header_enabled; +} + +void lcd_print_header() { + lcd_print_header_star_code(); + lcd_print_header_step(); + lcd_print_header_bat(); +} + +void lcd_print_header_star_code() { + +} + +void lcd_print_header_step() { + +} + +void lcd_print_header_bat() { + } diff --git a/main/drivers/char_lcd.h b/main/drivers/char_lcd.h index 73d562b..81040d2 100644 --- a/main/drivers/char_lcd.h +++ b/main/drivers/char_lcd.h @@ -49,13 +49,30 @@ void lcd_set_backlight(uint8_t brightness); // Create a custom character void lcd_create_char(uint8_t location, uint8_t charmap[]); -// Write a character to the LCD -void lcd_write(uint8_t value); - -// Print a string to the LCD -void lcd_print(const char* str); - -// Print a string to the LCD at a given pos +/// @brief Print a string to the LCD at a given pos. +/// @param col the column to print the string at. +/// @param row the row the print the string at. +/// @param str the string to print. void lcd_print(uint8_t col, uint8_t row, const char* str); +/// @brief Enables or disables the header on the LCD. +/// @param enable `true` to enable the header, `false` to disable. +void set_lcd_header_enabled(bool enable); + +/// @brief Returns weather or not the lcd_header is enabled. +/// @return `true` if the header is enabled, `false` otherwise. +bool lcd_header_enabled(); + +/// @brief Prints the header in the LCD. +void lcd_print_header(); + +/// @brief Prints the star code section of the LCD header. +void lcd_print_header_star_code(); + +/// @brief Prints the step section of the LCD header. +void lcd_print_header_step(); + +/// @brief Prints the battery section of the LCD header. +void lcd_print_header_bat(); + #endif /* CHAR_LCD_H */ \ No newline at end of file diff --git a/main/drivers/i2c.cpp b/main/drivers/i2c.cpp new file mode 100644 index 0000000..85c2018 --- /dev/null +++ b/main/drivers/i2c.cpp @@ -0,0 +1,32 @@ +#include "i2c.h" +#include "esp_log.h" +#include "esp_err.h" +#include "driver/i2c_master.h" + +static const char *TAG = "i2c"; + +static void init_i2c() { + ESP_LOGI(TAG, "Initializing i2c..."); + + i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = GPIO_NUM_5, + .scl_io_num = GPIO_NUM_6, + .sda_pullup_en = GPIO_PULLUP_DISABLE, + .scl_pullup_en = GPIO_PULLUP_DISABLE, + // .sda_pullup_en = GPIO_PULLUP_ENABLE, + // .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master = { + .clk_speed = 100*1000, + }, + .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL + }; + + ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf)); + ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0)); + + i2c0_mutex = xSemaphoreCreateMutex(); + assert(i2c0_mutex != NULL); + + ESP_LOGI(TAG, "i2c initialized!"); +} diff --git a/main/drivers/i2c.h b/main/drivers/i2c.h new file mode 100644 index 0000000..2b9ee53 --- /dev/null +++ b/main/drivers/i2c.h @@ -0,0 +1,21 @@ +#ifndef I2C_H +#define I2C_H + +#include "FreeRTOS.h" +#include "freertos/semphr.h" + +/// The mutex for accessing `I2C_NUM_0`. +SemaphoreHandle_t i2c0_mutex; + +/// @brief Initializes `I2C_NUM_0`. +/// +/// This is hooked up the to: +/// - The bottom half +/// - The char lcd +/// - The power board +/// - The MPU6050 +/// - The PERH port +/// - The Capacitive Touch Panel +void init_i2c(); + +#endif /* I2C_H */ \ No newline at end of file diff --git a/main/drivers/i2c_lcd_pcf8574.c b/main/drivers/i2c_lcd_pcf8574.c index cfdd941..18312bc 100644 --- a/main/drivers/i2c_lcd_pcf8574.c +++ b/main/drivers/i2c_lcd_pcf8574.c @@ -12,6 +12,7 @@ #include "esp_check.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "i2c.h" #define TAG "I2C_LCD_PCF8574" @@ -58,6 +59,7 @@ void lcd_begin(i2c_lcd_pcf8574_handle_t* lcd, uint8_t cols, uint8_t rows) { lcd->entrymode = 0x02; // The following are the reset sequence: Please see "Initialization instruction in the PCF8574 datasheet." + xSemaphoreTake(i2c0_mutex, portMAX_DELAY); i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); // We left-shift the device addres and add the read/write command @@ -95,6 +97,7 @@ void lcd_begin(i2c_lcd_pcf8574_handle_t* lcd, uint8_t cols, uint8_t rows) { i2c_master_stop(cmd); i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); + xSemaphoreGive(i2c0_mutex); // Instruction: function set = 0x20 lcd_send(lcd, 0x20 | (rows > 1 ? 0x08 : 0x00), false); @@ -110,7 +113,7 @@ void lcd_clear(i2c_lcd_pcf8574_handle_t* lcd) { // Instruction: Clear display = 0x01 lcd_send(lcd, 0x01, false); // Clearing the display takes a while: takes approx. 1.5ms - esp_rom_delay_us(1600); + esp_rom_delay_us(2000); } // lcd_clear() // Set the display to home @@ -118,7 +121,7 @@ void lcd_home(i2c_lcd_pcf8574_handle_t* lcd) { // Instruction: Return home = 0x02 lcd_send(lcd, 0x02, false); // Same as clearing the display: takes approx. 1.5ms - esp_rom_delay_us(1600); + esp_rom_delay_us(2000); } // lcd_home() // Set the cursor to a new position. @@ -295,6 +298,7 @@ void lcd_print_number(i2c_lcd_pcf8574_handle_t* lcd, uint8_t col, uint8_t row, u static void lcd_send(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value, bool is_data) { + xSemaphoreTake(i2c0_mutex, portMAX_DELAY); i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true); @@ -303,6 +307,7 @@ static void lcd_send(i2c_lcd_pcf8574_handle_t* lcd, uint8_t value, bool is_data) i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); + xSemaphoreGive(i2c0_mutex); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to send data to LCD: %s", esp_err_to_name(ret)); @@ -341,6 +346,7 @@ static void lcd_write_i2c(i2c_lcd_pcf8574_handle_t* lcd, uint8_t data, bool is_d data |= lcd->backlight_mask; } + xSemaphoreTake(i2c0_mutex, portMAX_DELAY); i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (lcd->i2c_addr << 1) | I2C_MASTER_WRITE, true); @@ -348,6 +354,7 @@ static void lcd_write_i2c(i2c_lcd_pcf8574_handle_t* lcd, uint8_t data, bool is_d i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(lcd->i2c_port, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); + xSemaphoreGive(i2c0_mutex); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to write to LCD: %s", esp_err_to_name(ret)); diff --git a/main/drivers/power.cpp b/main/drivers/power.cpp index 09574dd..65a52c9 100644 --- a/main/drivers/power.cpp +++ b/main/drivers/power.cpp @@ -50,4 +50,6 @@ void init_power_board() { ESP_LOGI(TAG, "Power board initialized!"); } - +uint16_t get_bat_voltage() { + lipo.voltage(); +} diff --git a/main/drivers/power.h b/main/drivers/power.h index 463b0ec..a74ac21 100644 --- a/main/drivers/power.h +++ b/main/drivers/power.h @@ -3,12 +3,14 @@ #include "SparkFunBQ27441/SparkFunBQ27441.h" +extern volatile uint32_t battery_charge; + void bat_monitor_task(void* arg); /// Initializes the battery gas guage for getting battery stats. void init_power_board(); -/// @brief Gets the battery voltage +/// @brief Gets the battery voltage. /// @return battery voltage in mV. uint16_t get_bat_voltage(); diff --git a/main/drivers/star_code.h b/main/drivers/star_code.h index 31b8b55..09215c0 100644 --- a/main/drivers/star_code.h +++ b/main/drivers/star_code.h @@ -66,16 +66,17 @@ bool rm_star_codes_str(const char** codes, size_t len); /// @brief clears all star codes. void clear_star_codes(); -/// @brief Triggers the given starcode. +/// @brief Triggers the given star code. /// @param code the star code to trigger (without the *) /// @return true iff a star code was triggered bool trigger_star_code(const char* code); -/// @brief Tempararilly stops the starcode system from handling new starcodes. -void pause_star_code_system(); +/// @brief Tempararilly starts/stops the star code system from handling new star codes. +void set_star_code_sys_enabled(bool enable); -/// @brief Resumes the starcode system to start handling new starcodes again. -void resume_star_code_system(); +/// @brief Gets weather or not the star code system is handling new star codes. +/// @return `true` if the star code system is handling star codes. +bool star_code_sys_enabled(); #endif /* STAR_CODE_H */ \ No newline at end of file