diff --git a/main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp b/main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp index 06437ff..0f7cd8d 100644 --- a/main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp +++ b/main/drivers/SparkFunBQ27441/SparkFunBQ27441.cpp @@ -729,7 +729,9 @@ uint16_t BQ27441::i2cWriteBytes(uint8_t subAddress, uint8_t * src, uint8_t count w_buff[i+1] = src[i]; } + xSemaphoreTake(main_i2c_mutex, portMAX_DELAY); esp_err_t ret = i2c_master_write_to_device(BQ72441_I2C_NUM, _deviceAddress, src, count+1, timeout); + xSemaphoreGive(main_i2c_mutex); return ret == ESP_OK; } diff --git a/main/drivers/bottom_half.cpp b/main/drivers/bottom_half.cpp index fdd401a..723d3df 100644 --- a/main/drivers/bottom_half.cpp +++ b/main/drivers/bottom_half.cpp @@ -76,6 +76,7 @@ void init_bottom_half() { static uint8_t receive_delta(void) { uint8_t reg = 1; + xSemaphoreTake(main_i2c_mutex, portMAX_DELAY); esp_err_t result = i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS)); ESP_ERROR_CHECK_WITHOUT_ABORT(result); if (result != ESP_OK) { @@ -85,10 +86,11 @@ static uint8_t receive_delta(void) { } static void receive_keypad(void) { - // TODO: use mutex // TODO: change the bottom half polling scheme from a state-based protocol to an event based protocol uint8_t reg = 2; + xSemaphoreTake(main_i2c_mutex, portMAX_DELAY); esp_err_t result = i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 2, (100 / portTICK_PERIOD_MS)); + xSemaphoreGive(main_i2c_mutex); ESP_ERROR_CHECK_WITHOUT_ABORT(result); if (result != ESP_OK) { return; @@ -117,7 +119,9 @@ static void receive_keypad(void) { static void receive_button_switch(void) { uint8_t reg = 3; + xSemaphoreTake(main_i2c_mutex, portMAX_DELAY); esp_err_t result = i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 2, (100 / portTICK_PERIOD_MS)); + xSemaphoreGive(main_i2c_mutex); ESP_ERROR_CHECK_WITHOUT_ABORT(result); if (result != ESP_OK) { return; @@ -188,7 +192,9 @@ static void receive_button_switch(void) { static void receive_touch(void) { uint8_t reg = 4; buf[0] = 0; + xSemaphoreTake(main_i2c_mutex, portMAX_DELAY); ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(BOTTOM_I2C_NUM, BOTTOM_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS))); + xSemaphoreGive(main_i2c_mutex); bool new_touch_state = buf[0] != 0; bool just_pressed = new_touch_state & !touch_state; diff --git a/main/drivers/char_lcd.cpp b/main/drivers/char_lcd.cpp index 393cdd9..3d69b2b 100644 --- a/main/drivers/char_lcd.cpp +++ b/main/drivers/char_lcd.cpp @@ -11,6 +11,10 @@ i2c_lcd_pcf8574_handle_t lcd; SemaphoreHandle_t lcd_mutex; +static volatile bool cursor_visible = false; +static volatile uint8_t cursor_resting_row = 0; +static volatile uint8_t cursor_resting_col = 0; + static volatile bool header_enabled = false; static const char *TAG = "char_lcd"; @@ -147,6 +151,8 @@ void lcd_set_cursor_vis(bool cursor, bool no_lock) { } if (!no_lock) xSemaphoreGive(lcd_mutex); + cursor_visible = cursor; + if (is_state_tracking()) { event_occured("LCD_CURSOR_VIS", cursor ? "true" : "false"); } @@ -249,6 +255,11 @@ void lcd_print(uint8_t row, uint8_t col, const char* str, bool no_lock) { if (!no_lock) xSemaphoreTake(lcd_mutex, portMAX_DELAY); lcd_set_cursor(&lcd, col, row); lcd_print(&lcd, str); + + if (cursor_visible) { + lcd_set_cursor(&lcd, cursor_resting_col, cursor_resting_row); + } + if (!no_lock) xSemaphoreGive(lcd_mutex); if (is_state_tracking()) { @@ -310,4 +321,18 @@ bool lcd_lock(uint32_t ticks_to_wait) { void lcd_unlock() { xSemaphoreGive(lcd_mutex); +} + +void lcd_set_cursor_resting_position(uint8_t row, uint8_t col) { + cursor_resting_row = row; + cursor_resting_col = col; +} + +void lcd_get_cursor_resting_position(uint8_t* row, uint8_t* col) { + if (row) *row = cursor_resting_row; + if (col) *col = cursor_resting_col; +} + +bool lcd_is_cursor_visible() { + return cursor_visible; } \ No newline at end of file diff --git a/main/drivers/char_lcd.h b/main/drivers/char_lcd.h index bfc5347..7cc7e1f 100644 --- a/main/drivers/char_lcd.h +++ b/main/drivers/char_lcd.h @@ -79,4 +79,18 @@ bool lcd_lock(uint32_t ticks_to_wait); /// @brief Unlocks the LCD to give away the mutex access to it. void lcd_unlock(); +/// @brief Set the resting position for the cursor +/// @param row the row where the cursor should rest +/// @param col the column where the cursor should rest +void lcd_set_cursor_resting_position(uint8_t row, uint8_t col); + +/// @brief Get the current resting position of the cursor +/// @param row pointer to store the resting row +/// @param col pointer to store the resting column +void lcd_get_cursor_resting_position(uint8_t* row, uint8_t* col); + +/// @brief Check if the cursor is currently visible +/// @return true if cursor is visible, false otherwise +bool lcd_is_cursor_visible(); + #endif /* CHAR_LCD_H */ diff --git a/main/drivers/hwdata.cpp b/main/drivers/hwdata.cpp index 673f3a1..68d56e9 100644 --- a/main/drivers/hwdata.cpp +++ b/main/drivers/hwdata.cpp @@ -1,6 +1,10 @@ #include "hwdata.h" #include "esp_err.h" #include "esp_log.h" +#include "bottom_half.h" +#include "char_lcd.h" +#include "../helper.h" +#include "nvs.h" static const char* TAG = "hwdata"; @@ -120,10 +124,10 @@ void HWData1::load(nvs_handle_t handle) { if (nvs_get_u8(handle, "bat_type", &tmp) == ESP_OK) bat_type = static_cast(tmp); nvs_get_u16(handle, "bat_cap", &bat_cap); - if (nvs_get_u8(handle, "shape1", &tmp) == ESP_OK) shape1 = static_cast(tmp); - if (nvs_get_u8(handle, "shape2", &tmp) == ESP_OK) shape2 = static_cast(tmp); - if (nvs_get_u8(handle, "shape3", &tmp) == ESP_OK) shape3 = static_cast(tmp); - if (nvs_get_u8(handle, "shape4", &tmp) == ESP_OK) shape4 = static_cast(tmp); + if (nvs_get_u8(handle, "shape1", &tmp) == ESP_OK) shape1 = static_cast(tmp); + if (nvs_get_u8(handle, "shape2", &tmp) == ESP_OK) shape2 = static_cast(tmp); + if (nvs_get_u8(handle, "shape3", &tmp) == ESP_OK) shape3 = static_cast(tmp); + if (nvs_get_u8(handle, "shape4", &tmp) == ESP_OK) shape4 = static_cast(tmp); nvs_get_u8(handle, "switch_pos", &switch_pos); nvs_get_u8(handle, "has_speaker", &tmp); has_speaker = tmp; @@ -135,4 +139,356 @@ void HWData1::load(nvs_handle_t handle) { nvs_get_u8(handle, "has_close_hall", &tmp); has_close_hall = tmp; } +static void handle_uint8(KeypadKey key, uint8_t& val) { + char key_c = char_of_keypad_key(key); + bool is_digit = std::isdigit(static_cast(key_c)); + uint8_t digit = is_digit ? static_cast(key_c - '0') : 0; + if (key == KeypadKey::star) { + val = 0; + } else if (is_digit) { + uint16_t new_digit = ((uint16_t) val) * 10 + (uint16_t) digit; + if (new_digit < 255) val = new_digit; + } +} +static void handle_uint16(KeypadKey key, uint16_t& val) { + char key_c = char_of_keypad_key(key); + bool is_digit = std::isdigit(static_cast(key_c)); + uint8_t digit = is_digit ? static_cast(key_c - '0') : 0; + + if (key == KeypadKey::star) { + val = 0; + } else if (is_digit) { + uint32_t new_digit = ((uint32_t) val) * 10 + (uint32_t) digit; + if (new_digit < 65535) val = new_digit; + } +} + +static void handle_enum(ButtonKey key, uint8_t& val, uint8_t n_items) { + if (key == ButtonKey::b1) { + // dec + val = (val + n_items - 1) % n_items; + } else if (key == ButtonKey::b2) { + // inc + val = (val + 1) % n_items; + } +} + +void hardware_config() { + clean_bomb(); + uint8_t current_item = 0; + const uint8_t n_items = 28; + + HWData1& hwdata = get_hw_data().inner; + + ButtonKey btn; + KeypadKey key; + bool dirty = true; + while (true) { + if (dirty) { + // display + char name[21]; + char value[21]; + + switch (current_item) { + case 0: // serial_num + snprintf(name, sizeof(name), "%-20s", "serial_num"); + snprintf(value, sizeof(value), "%s", hwdata.serial_num.c_str()); + break; + + case 1: // rev_ctrl_maj + snprintf(name, sizeof(name), "%-20s", "rev_ctrl_maj"); + snprintf(value, sizeof(value), "%d", hwdata.rev_ctrl_maj); + break; + + case 2: // rev_ctrl_min + snprintf(name, sizeof(name), "%-20s", "rev_ctrl_min"); + snprintf(value, sizeof(value), "%d", hwdata.rev_ctrl_min); + break; + + case 3: // rev_exp_maj + snprintf(name, sizeof(name), "%-20s", "rev_exp_maj"); + snprintf(value, sizeof(value), "%d", hwdata.rev_exp_maj); + break; + + case 4: // rev_exp_min + snprintf(name, sizeof(name), "%-20s", "rev_exp_min"); + snprintf(value, sizeof(value), "%d", hwdata.rev_exp_min); + break; + + case 5: // rev_ft_maj + snprintf(name, sizeof(name), "%-20s", "rev_ft_maj"); + snprintf(value, sizeof(value), "%d", hwdata.rev_ft_maj); + break; + + case 6: // rev_ft_min + snprintf(name, sizeof(name), "%-20s", "rev_ft_min"); + snprintf(value, sizeof(value), "%d", hwdata.rev_ft_min); + break; + + case 7: // rev_fb_maj + snprintf(name, sizeof(name), "%-20s", "rev_fb_maj"); + snprintf(value, sizeof(value), "%d", hwdata.rev_fb_maj); + break; + + case 8: // rev_fb_min + snprintf(name, sizeof(name), "%-20s", "rev_fb_min"); + snprintf(value, sizeof(value), "%d", hwdata.rev_fb_min); + break; + + case 9: // sseg_color_t + snprintf(name, sizeof(name), "%-20s", "sseg_color_t"); + snprintf(value, sizeof(value), "%s", SSEG_COLOR_NAMES[static_cast(hwdata.sseg_color_t)]); + break; + + case 10: // sseg_color_b + snprintf(name, sizeof(name), "%-20s", "sseg_color_b"); + snprintf(value, sizeof(value), "%s", SSEG_COLOR_NAMES[static_cast(hwdata.sseg_color_b)]); + break; + + case 11: // lcd_color + snprintf(name, sizeof(name), "%-20s", "lcd_color"); + snprintf(value, sizeof(value), "%s", LCD_COLOR_NAMES[static_cast(hwdata.lcd_color)]); + break; + + case 12: // switch_pos + snprintf(name, sizeof(name), "%-20s", "switch_pos"); + snprintf(value, sizeof(value), "%d", hwdata.switch_pos); + break; + + case 13: // button_type + snprintf(name, sizeof(name), "%-20s", "button_type"); + snprintf(value, sizeof(value), "%s", BUTTON_TYPE_NAMES[static_cast(hwdata.button_type)]); + break; + + case 14: // tft_type + snprintf(name, sizeof(name), "%-20s", "tft_type"); + snprintf(value, sizeof(value), "%s", TFT_TYPE_NAMES[static_cast(hwdata.tft_type)]); + break; + + case 15: // bat_type + snprintf(name, sizeof(name), "%-20s", "bat_type"); + snprintf(value, sizeof(value), "%s", BAT_TYPE_NAMES[static_cast(hwdata.bat_type)]); + break; + + case 16: // bat_cap + snprintf(name, sizeof(name), "%-20s", "bat_cap"); + snprintf(value, sizeof(value), "%d", hwdata.bat_cap); + break; + + case 17: // shape1 + snprintf(name, sizeof(name), "%-20s", "shape1"); + snprintf(value, sizeof(value), "%s", SHAPE_TYPE_NAMES[static_cast(hwdata.shape1)]); + break; + + case 18: // shape2 + snprintf(name, sizeof(name), "%-20s", "shape2"); + snprintf(value, sizeof(value), "%s", SHAPE_TYPE_NAMES[static_cast(hwdata.shape2)]); + break; + + case 19: // shape3 + snprintf(name, sizeof(name), "%-20s", "shape3"); + snprintf(value, sizeof(value), "%s", SHAPE_TYPE_NAMES[static_cast(hwdata.shape3)]); + break; + + case 20: // shape4 + snprintf(name, sizeof(name), "%-20s", "shape4"); + snprintf(value, sizeof(value), "%s", SHAPE_TYPE_NAMES[static_cast(hwdata.shape4)]); + break; + + case 21: // has_speaker + snprintf(name, sizeof(name), "%-20s", "has_speaker"); + snprintf(value, sizeof(value), "%s", hwdata.has_speaker ? "true" : "false"); + break; + + case 22: // has_mic + snprintf(name, sizeof(name), "%-20s", "has_mic"); + snprintf(value, sizeof(value), "%s", hwdata.has_mic ? "true" : "false"); + break; + + case 23: // has_ir + snprintf(name, sizeof(name), "%-20s", "has_ir"); + snprintf(value, sizeof(value), "%s", hwdata.has_ir ? "true" : "false"); + break; + + case 24: // has_rfid + snprintf(name, sizeof(name), "%-20s", "has_rfid"); + snprintf(value, sizeof(value), "%s", hwdata.has_rfid ? "true" : "false"); + break; + + case 25: // has_fp + snprintf(name, sizeof(name), "%-20s", "has_fp"); + snprintf(value, sizeof(value), "%s", hwdata.has_fp ? "true" : "false"); + break; + + case 26: // has_fp_hall + snprintf(name, sizeof(name), "%-20s", "has_fp_hall"); + snprintf(value, sizeof(value), "%s", hwdata.has_fp_hall ? "true" : "false"); + break; + + case 27: // has_close_hall + snprintf(name, sizeof(name), "%-20s", "has_close_hall"); + snprintf(value, sizeof(value), "%s", hwdata.has_close_hall ? "true" : "false"); + break; + + default: + break; + } + + lcd_print(1, 0, name); + lcd_print(2, 0, value); + + dirty = false; + } + + if (get_button_pressed(&btn)) { + dirty = true; + switch (btn) { + case ButtonKey::b3: // dec + current_item = (current_item + n_items - 1) % n_items; + break; + case ButtonKey::b4: // inc + current_item = (current_item + 1) % n_items; + break; + default: + switch (current_item) { + case 9: // sseg_color_t + handle_enum(btn, reinterpret_cast(hwdata.sseg_color_t), SSEG_COLOR_COUNT); + break; + + case 10: // sseg_color_b + handle_enum(btn, reinterpret_cast(hwdata.sseg_color_b), SSEG_COLOR_COUNT); + break; + + case 11: // lcd_color + handle_enum(btn, reinterpret_cast(hwdata.lcd_color), LCD_COLOR_COUNT); + break; + + case 13: // button_type + handle_enum(btn, reinterpret_cast(hwdata.button_type), BUTTON_TYPE_COUNT); + break; + + case 14: // tft_type + handle_enum(btn, reinterpret_cast(hwdata.tft_type), TFT_TYPE_COUNT); + break; + + case 15: // bat_type + handle_enum(btn, reinterpret_cast(hwdata.bat_type), BAT_TYPE_COUNT); + break; + + case 17: // shape1 + handle_enum(btn, reinterpret_cast(hwdata.shape1), SHAPE_TYPE_COUNT); + break; + case 18: // shape2 + handle_enum(btn, reinterpret_cast(hwdata.shape2), SHAPE_TYPE_COUNT); + break; + case 19: // shape3 + handle_enum(btn, reinterpret_cast(hwdata.shape3), SHAPE_TYPE_COUNT); + break; + case 20: // shape4 + handle_enum(btn, reinterpret_cast(hwdata.shape4), SHAPE_TYPE_COUNT); + break; + + case 21: // has_speaker + hwdata.has_speaker = btn == ButtonKey::button_green; + break; + + case 22: // has_mic + hwdata.has_mic = btn == ButtonKey::button_green; + break; + + case 23: // has_ir + hwdata.has_ir = btn == ButtonKey::button_green; + break; + + case 24: // has_rfid + hwdata.has_rfid = btn == ButtonKey::button_green; + break; + + case 25: // has_fp + hwdata.has_fp = btn == ButtonKey::button_green; + break; + + case 26: // has_fp_hall + hwdata.has_fp_hall = btn == ButtonKey::button_green; + break; + + case 27: // has_close_hall + hwdata.has_close_hall = btn == ButtonKey::button_green; + break; + + default: + break; + } + break; + } + } + if (get_keypad_pressed(&key)) { + dirty = true; + if (key == KeypadKey::pound) { + // TODO: ask the user to save + return; // done + } + + char key_c = char_of_keypad_key(key); + bool is_digit = std::isdigit(static_cast(key_c)); + uint8_t digit = is_digit ? static_cast(key_c - '0') : 0; + + // update the current value + switch (current_item) { + case 0: // serial_num + if (key == KeypadKey::star) { + hwdata.serial_num.clear(); + } else { + hwdata.serial_num.push_back(char_of_keypad_key(key)); + } + break; + + case 1: // rev_ctrl_maj + handle_uint8(key, hwdata.rev_ctrl_maj); + break; + + case 2: // rev_ctrl_min + handle_uint8(key, hwdata.rev_ctrl_min); + break; + + case 3: // rev_exp_maj + handle_uint8(key, hwdata.rev_exp_maj); + break; + + case 4: // rev_exp_min + handle_uint8(key, hwdata.rev_exp_min); + break; + + case 5: // rev_ft_maj + handle_uint8(key, hwdata.rev_ft_maj); + break; + + case 6: // rev_ft_min + handle_uint8(key, hwdata.rev_ft_min); + break; + + case 7: // rev_fb_maj + handle_uint8(key, hwdata.rev_fb_maj); + break; + + case 8: // rev_fb_min + handle_uint8(key, hwdata.rev_fb_min); + break; + + case 12: // switch_pos + if (digit == 2 || digit == 3) hwdata.switch_pos = digit; + break; + + case 16: // bat_cap + handle_uint16(key, hwdata.bat_cap); + break; + + default: + break; + } + } + + vTaskDelay(pdMS_TO_TICKS(10)); + } +} diff --git a/main/drivers/hwdata.h b/main/drivers/hwdata.h index 569ac5d..9a5c07f 100644 --- a/main/drivers/hwdata.h +++ b/main/drivers/hwdata.h @@ -10,6 +10,19 @@ #define CURRENT_HWDATA_VERSION 1 #define CURRENT_HWDATA_STRUCT HWData1 +#define SSEG_COLOR_COUNT 10 +static constexpr const char* SSEG_COLOR_NAMES[SSEG_COLOR_COUNT] = { + "UNKNOWN", + "NONE", + "OTHER", + "RED", + "ORANGE", + "YELLOW", + "GREEN", + "BLUE", + "PURPLE", + "WHITE" +}; enum class SSegColor : uint8_t { UNKNOWN = 0, NONE = 1, @@ -23,6 +36,17 @@ enum class SSegColor : uint8_t { WHITE = 9, }; +#define LCD_COLOR_COUNT 8 +static constexpr const char* LCD_COLOR_NAMES[LCD_COLOR_COUNT] = { + "UNKNOWN", + "NONE", + "OTHER", + "BLACK_GREEN", + "WHITE_BLUE", + "BLACK_SKY", + "BLACK_WHITE", + "WHITE_BLACK" +}; enum class LCDColor : uint8_t { UNKNOWN = 0, NONE = 1, @@ -34,6 +58,15 @@ enum class LCDColor : uint8_t { WHITE_BLACK = 7, }; +#define BUTTON_TYPE_COUNT 6 +static constexpr const char* BUTTON_TYPE_NAMES[BUTTON_TYPE_COUNT] = { + "UNKNOWN", + "NONE", + "OTHER", + "WHITE", + "BROWN", + "RED" +}; enum class ButtonType : uint8_t { UNKNOWN = 0, NONE = 1, @@ -43,6 +76,14 @@ enum class ButtonType : uint8_t { RED = 5, }; +#define TFT_TYPE_COUNT 5 +static constexpr const char* TFT_TYPE_NAMES[TFT_TYPE_COUNT] = { + "UNKNOWN", + "NONE", + "OTHER", + "EAST_RISING", + "SHENZHEN" +}; enum class TFTType : uint8_t { UNKNOWN = 0, NONE = 1, @@ -51,6 +92,14 @@ enum class TFTType : uint8_t { SHENZHEN = 4, }; +#define BAT_TYPE_COUNT 5 +static constexpr const char* BAT_TYPE_NAMES[BAT_TYPE_COUNT] = { + "UNKNOWN", + "NONE", + "OTHER", + "BAT_18650", + "LIPO" +}; enum class BatType : uint8_t { UNKNOWN = 0, NONE = 1, @@ -59,7 +108,21 @@ enum class BatType : uint8_t { LIPO = 4, }; -enum class Shape : uint8_t { +#define SHAPE_TYPE_COUNT 11 +static constexpr const char* SHAPE_TYPE_NAMES[SHAPE_TYPE_COUNT] = { + "UNKNOWN", + "OTHER", + "CIRCLE", + "SQUARE", + "TRIANGLE", + "X", + "STAR", + "SPADE", + "DIAMOND", + "CLUB", + "HEART" +}; +enum class ShapeType : uint8_t { UNKNOWN = 0, OTHER = 1, CIRCLE = 2, @@ -74,6 +137,7 @@ enum class Shape : uint8_t { }; + /// @brief Version 1 of HWData, kept constant for migrations struct HWData1 { std::string serial_num; @@ -95,10 +159,10 @@ struct HWData1 { BatType bat_type; uint16_t bat_cap; - Shape shape1; - Shape shape2; - Shape shape3; - Shape shape4; + ShapeType shape1; + ShapeType shape2; + ShapeType shape3; + ShapeType shape4; bool has_speaker; bool has_mic; @@ -130,4 +194,6 @@ struct HWData { static HWData load(nvs_handle_t handle); }; +void hardware_config(); + #endif /* HWDATA_H */ diff --git a/main/drivers/power.cpp b/main/drivers/power.cpp index 72a5bd3..8e840b6 100644 --- a/main/drivers/power.cpp +++ b/main/drivers/power.cpp @@ -76,3 +76,25 @@ void lcd_print_header_bat() { lcd_print(0, 16, buf); } + +// memory version +// void lcd_print_header_bat() { +// if (!lcd_header_enabled()) return; +// if (lcd_starcode_displaying_result()) return; + +// // Show memory usage percentage instead of battery percentage +// char buf[6]; +// size_t free_heap = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); +// size_t total_heap = heap_caps_get_total_size(MALLOC_CAP_DEFAULT); +// uint8_t mem_percent = 0; +// if (total_heap > 0) { +// mem_percent = (uint8_t)(100 - ((free_heap * 100) / total_heap)); +// } +// if (mem_percent >= 100) { +// snprintf(buf, sizeof(buf), " 100"); +// } else { +// snprintf(buf, sizeof(buf), " %2d%%", mem_percent); +// } + +// lcd_print(0, 16, buf); +// } diff --git a/main/drivers/starcode.cpp b/main/drivers/starcode.cpp index 4aa9d80..a1ec091 100644 --- a/main/drivers/starcode.cpp +++ b/main/drivers/starcode.cpp @@ -7,6 +7,7 @@ #include "drivers/bottom_half.h" #include "char_lcd.h" #include "esp_timer.h" +#include "freertos/task.h" static const char* TAG = "star_code"; @@ -14,7 +15,7 @@ volatile bool handling_new_starcodes = false; static volatile bool system_initialized = false; // TODO: use the semaphore, convert to RWLock? -static volatile SemaphoreHandle_t star_codes_sem; +static volatile SemaphoreHandle_t star_codes_mutex; static std::vector star_codes; static const char EMPTY_STAR_CODE_HEADER[] = " "; @@ -29,23 +30,40 @@ static uint16_t starcode_waiting_on_release; static char current[STARCODE_MAX_LEN + 1]; static size_t current_idx; +// Task handle for the starcode callback task +static TaskHandle_t starcode_callback_task_handle = nullptr; + +static void starcode_callback_task(void* arg) { + (void) arg; + + while (true) { + // Wait for notification from starcode_trigger_cb + uint32_t notification_value; + if (xTaskNotifyWait(0, ULONG_MAX, ¬ification_value, portMAX_DELAY) == pdTRUE) { + // Process the starcode callback + delaying_for_starcode = false; + lcd_print_header(); + + if (current_starcode != nullptr) { + if (current_starcode->triggered_sem != nullptr) + xSemaphoreGive(current_starcode->triggered_sem); + if (current_starcode->callback != nullptr) + (current_starcode->callback)(); + + current_starcode = nullptr; + } + } + } +} + static void starcode_trigger_cb(void* arg) { (void) arg; - delaying_for_starcode = false; - - if (current_starcode != nullptr) { - if (current_starcode->triggered_sem != nullptr) - xSemaphoreGive(current_starcode->triggered_sem); - if (current_starcode->callback != nullptr) - (current_starcode->callback)(); - - current_starcode = nullptr; + if (starcode_callback_task_handle != nullptr) { + xTaskNotify(starcode_callback_task_handle, 1, eSetBits); } - - // TODO: rename star code everywhere to starcode - lcd_print_header(); } +// TODO: rename star code everywhere to starcode void star_code_handle_keypad(uint16_t* just_pressed, uint16_t* just_released) { @@ -91,8 +109,9 @@ void star_code_handle_keypad(uint16_t* just_pressed, uint16_t* just_released) { } void init_star_code_system() { - star_codes_sem = xSemaphoreCreateBinary(); - xSemaphoreGive(star_codes_sem); + star_codes_mutex = xSemaphoreCreateMutex(); + + xTaskCreate(starcode_callback_task, "starcode_cb", 4096, NULL, 3, &starcode_callback_task_handle); const esp_timer_create_args_t timer_args = { .callback = &starcode_trigger_cb, diff --git a/main/drivers/wires.cpp b/main/drivers/wires.cpp index bc257d3..ef46c65 100644 --- a/main/drivers/wires.cpp +++ b/main/drivers/wires.cpp @@ -2,6 +2,9 @@ extern uint32_t current_step; +/// The mutex for accessing `I2C_NUM_1` (wires I2C bus). +SemaphoreHandle_t wires_i2c_mutex; + uint32_t total_strikes; uint32_t step_strikes[N_STEPS] = {0}; uint32_t step_finish_times[N_STEPS] = {0}; @@ -41,6 +44,10 @@ void init_wires(void) { ESP_ERROR_CHECK(i2c_param_config(WIRES_I2C_NUM, &wires_conf)); ESP_ERROR_CHECK(i2c_driver_install(WIRES_I2C_NUM, wires_conf.mode, 0, 0, 0)); + // Create mutex for wires I2C bus + wires_i2c_mutex = xSemaphoreCreateMutex(); + assert(wires_i2c_mutex != NULL); + gpio_config_t int_pin_conf = {}; // delta_pin_conf.intr_type = GPIO_INTR_LOW_LEVEL; int_pin_conf.mode = GPIO_MODE_INPUT; @@ -92,38 +99,48 @@ void strike(const char* reason) { step_strikes[current_step - 1] += 1; } uint8_t reg = 6; + xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY); ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_to_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, ®, 1, (100 / portTICK_PERIOD_MS))); + xSemaphoreGive(wires_i2c_mutex); } void set_leds(uint8_t led_states) { buf[0] = 5; // register 5 buf[1] = led_states; + xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY); ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_to_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, buf, 2, (100 / portTICK_PERIOD_MS))); + xSemaphoreGive(wires_i2c_mutex); } static uint8_t receive_delta(void) { uint8_t reg = 1; buf[0] = 0; + xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY); ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS))); + xSemaphoreGive(wires_i2c_mutex); return buf[0]; } static void receive_wires(void) { uint8_t reg = 2; buf[0] = 0; + xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY); ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS))); + xSemaphoreGive(wires_i2c_mutex); uint8_t new_wires = buf[0]; - + uint8_t just_cut = ~new_wires & wires_state; wires_cut |= just_cut; - + wires_state = new_wires; } static void receive_button(void) { uint8_t reg = 3; buf[0] = 0; + xSemaphoreTake(wires_i2c_mutex, portMAX_DELAY); ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_write_read_device(WIRES_I2C_NUM, WIRES_I2C_ADDR, ®, 1, buf, 1, (100 / portTICK_PERIOD_MS))); + xSemaphoreGive(wires_i2c_mutex); bool new_button = buf[0] != 0; bool just_pressed = new_button & !button_state; diff --git a/main/drivers/wires.h b/main/drivers/wires.h index e87499b..9244f60 100644 --- a/main/drivers/wires.h +++ b/main/drivers/wires.h @@ -5,6 +5,8 @@ #include #include #include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" #include "drivers/char_lcd.h" #include "drivers/game_timer.h" #include "main.h" @@ -16,6 +18,9 @@ #define WIRES_I2C_NUM I2C_NUM_1 #define WIRES_I2C_ADDR 125 +/// The mutex for accessing `I2C_NUM_1` (wires I2C bus). +extern SemaphoreHandle_t wires_i2c_mutex; + #define DELTA_BIT_WIRES 0 #define DELTA_BIT_BUTTON 1 diff --git a/main/steps/setup_wires.cpp b/main/steps/setup_wires.cpp index e1c3eb4..9a687bb 100644 --- a/main/steps/setup_wires.cpp +++ b/main/steps/setup_wires.cpp @@ -23,8 +23,8 @@ void print_wires(WireColor* wires, int editing_idx) { } lcd_print(3, 1, string_buf); - // TODO: find a way to indicate without a cursor. - // lcd_set_cursor_pos(editing_idx+1, 1); + lcd_set_cursor_vis(true); + lcd_set_cursor_resting_position(1, editing_idx+1); } void setup_wires(void) { diff --git a/main/steps/step0.cpp b/main/steps/step0.cpp index 182d40f..838a740 100644 --- a/main/steps/step0.cpp +++ b/main/steps/step0.cpp @@ -34,9 +34,6 @@ static void battery_stats() { vTaskDelete(xHandle); } } -static void hardware_config() { - -} // TODO: remove. This is temperary static void replay_last() { @@ -224,8 +221,8 @@ static void _update_display(uint8_t* digits, uint8_t cursor_pos) { lcd_print(1, 1, str_buf); cursor_pos = MAX(0, MIN(4, cursor_pos)); int mapped_cursor_pos = CURSOR_POS_MAP[cursor_pos]; - // TODO: find some way to indicate without a cursor. - // lcd_set_cursor_pos(mapped_cursor_pos, 1); + + lcd_set_cursor_resting_position(1, mapped_cursor_pos); } static void set_game_time() {