From 9600ef330bc6c28da78e4dccc05ad95f22242c5d Mon Sep 17 00:00:00 2001 From: Macoy Madson Date: Tue, 3 Nov 2020 15:02:39 -0800 Subject: [PATCH] Added timer, sleep mode, and input functions --- .clang-format | 22 ++++++ ReadMe.org | 2 + app/src/main.c | 141 ++++++++++++++++++++++++++++++++++----- app/src/odroid/display.c | 132 ++++++++++++++++++++++-------------- app/src/odroid/display.h | 3 + app/src/odroid/input.c | 83 +++++++++++++++++++++++ app/src/odroid/input.h | 3 +- 7 files changed, 321 insertions(+), 65 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..6d3bf6b --- /dev/null +++ b/.clang-format @@ -0,0 +1,22 @@ +# http://releases.llvm.org/6.0.0/tools/clang/docs/ClangFormatStyleOptions.html +BasedOnStyle: Google +AccessModifierOffset: -4 +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BreakBeforeBraces: Allman +BraceWrapping: + AfterNamespace: false +BreakBeforeTernaryOperators: false +ColumnLimit: 100 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +IndentWidth: 4 +Standard: Cpp11 +TabWidth: 4 +UseTab: ForIndentation +DerivePointerAlignment: false +PointerAlignment: Left +NamespaceIndentation: None +IndentCaseLabels: true \ No newline at end of file diff --git a/ReadMe.org b/ReadMe.org index 42f94e1..f82c1c7 100644 --- a/ReadMe.org +++ b/ReadMe.org @@ -66,6 +66,8 @@ cd ../../app idf.py build && idf.py -p /dev/ttyUSB0 flash #+END_SRC +Note that the ~idf.py~ commands will only work in the terminal where you executed ~export.sh~. + Monitor, if it doesn't work: #+BEGIN_SRC sh idf.py -p /dev/ttyUSB0 monitor diff --git a/app/src/main.c b/app/src/main.c index d0e5aa3..1c7320e 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -13,13 +13,20 @@ #define ArraySize(array) sizeof((array)) / sizeof((array)[0]) -static const char* LOG_TAG = "Main"; -static uint16_t gFramebuffer[LCD_WIDTH * LCD_HEIGHT]; - static bool drawBattery = true; -static bool drawTimeSinceBoot = true; +static bool drawTimeSinceBoot = false; static bool drawFrameRate = true; +/* static bool printInput = true; */ +static bool printInput = false; + +static bool enableLowFrameRate = true; + +static int64_t autoSleepModeMicroseconds = 10 * 1000000; + +static const char* LOG_TAG = "Main"; +static uint16_t gFramebuffer[LCD_WIDTH * LCD_HEIGHT]; + /* static const uint16_t lightPalette[4] = */ /* { */ /* 0xFFFF, */ @@ -28,7 +35,7 @@ static bool drawFrameRate = true; /* 0x0000, */ /* }; */ -static const uint16_t palette[4] = +static const uint16_t darkPalette[4] = { 0x0000, 0xAA52, @@ -36,6 +43,8 @@ static const uint16_t palette[4] = 0xFFFF, }; +static const uint16_t* palette = darkPalette; + static const uint8_t tiles[][16*16] = { // White @@ -138,7 +147,6 @@ static int tileBuffer[15][40] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }; - void DrawTile(int index, int x, int y) { int startX = x * 16; @@ -160,29 +168,109 @@ void DrawTile(int index, int x, int y) } } - void app_main(void) { Odroid_InitializeInput(); Odroid_InitializeDisplay(); + // Does not work for me yet /* Odroid_InitializeSdcard(); */ Odroid_InitializeBatteryReader(); /* Odroid_InitializeAudio(); */ ESP_LOGI(LOG_TAG, "Odroid initialization complete - entering main loop"); + /* uint8_t frameIndex = 0; */ + /* char snapFilename[20]; */ + /* int xLeft = 0; */ - uint8_t frameIndex = 0; - char snapFilename[20]; - int xLeft = 0; + bool timerRunning = false; + int64_t timerStartMicroSecs = 0; + // Includes timings before pause, not current timing + int64_t timerTotalMicroSecsPrePause = 0; int64_t lastFrameTimeMicroSecs = esp_timer_get_time(); - for (;;) + int64_t timeSinceLastInputMicroSecs = esp_timer_get_time(); + + Odroid_Input lastFrameInput = {0}; + + bool backlightOn = true; + bool waitForSleepActivateRelease = false; + + while (true) { - memset(gFramebuffer, palette[0], 320*240*2); + memset(gFramebuffer, palette[0], 320 * 240 * 2); + + int64_t currentTimeMicroSecs = esp_timer_get_time(); Odroid_Input input = Odroid_PollInput(); + bool hasInputsThisFrame = Odroid_HasAnyInput(&input); + if (hasInputsThisFrame) + timeSinceLastInputMicroSecs = esp_timer_get_time(); + + if (printInput) + Odroid_PrintInputState(&input); + + // Sleep mode + if (!backlightOn) + { + if (!waitForSleepActivateRelease && hasInputsThisFrame) + { + Odroid_BacklightInit(); + backlightOn = true; + } + else + { + if (!input.b) + waitForSleepActivateRelease = false; + + // Try to save battery while still listening to inputs + // TODO: Use the ESP sleep modes + vTaskDelay(250); + + continue; + } + } + + if (input.a && !lastFrameInput.a) + { + printf("Pause timer\n"); + timerRunning = !timerRunning; + + if (timerRunning) + timerStartMicroSecs = currentTimeMicroSecs; + else + timerTotalMicroSecsPrePause += currentTimeMicroSecs - timerStartMicroSecs; + + /* esp_err_t err = ledc_stop(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0); */ + /* if (err != ESP_OK) */ + /* { */ + /* printf("%s: ledc_stop failed.\n", __func__); */ + /* } */ + } + + if (input.start && !lastFrameInput.start) + { + printf("Reset timer\n"); + + timerRunning = false; + timerTotalMicroSecsPrePause = 0; + } + + bool sleepModeActivate = + currentTimeMicroSecs - timeSinceLastInputMicroSecs > autoSleepModeMicroseconds || + (input.b && !lastFrameInput.b); + if (sleepModeActivate) + { + if (backlightOn) + { + Odroid_BacklightDeinit(); + waitForSleepActivateRelease = true; + } + else + Odroid_BacklightInit(); + backlightOn = !backlightOn; + } // Movement /* if (input.left) */ @@ -222,7 +310,7 @@ void app_main(void) if (drawBattery) { char string[10]; - snprintf(string, ArraySize(string), "B: %02d", Odroid_ReadBatteryLevel()); + snprintf(string, ArraySize(string), "B: %d", Odroid_ReadBatteryLevel()); /* snprintf(string, ArraySize(string), "B: %02d", Odroid_ReadBatteryLevel()); */ DrawText(gFramebuffer, string, ArraySize(string), 0, 0, palette[3]); } @@ -247,6 +335,26 @@ void app_main(void) lastFrameTimeMicroSecs = currentTimeMicroSecs; } + // Timer + { + char string[18]; + int64_t currentTimeMicroSecs = esp_timer_get_time(); + float timerSeconds = + timerRunning ? + ((timerTotalMicroSecsPrePause + (currentTimeMicroSecs - timerStartMicroSecs)) / + 1000000.f) : + timerTotalMicroSecsPrePause / 1000000.f; + snprintf(string, ArraySize(string), "%d:%.2d", (int)timerSeconds / 60, + (int)timerSeconds % 60); + DrawText(gFramebuffer, string, ArraySize(string), 3, 5, palette[3]); + + if (!timerRunning) + { + snprintf(string, ArraySize(string), "Paused"); + DrawText(gFramebuffer, string, ArraySize(string), 3, 6, palette[3]); + } + } + // Save screenshots for making gifs /* if (input.menu) */ /* { */ @@ -264,11 +372,14 @@ void app_main(void) /* ++frameIndex; */ /* } */ - Odroid_DrawFrame(gFramebuffer); + + if (enableLowFrameRate) + vTaskDelay(25); + + lastFrameInput = input; } // Should never get here esp_restart(); } - diff --git a/app/src/odroid/display.c b/app/src/odroid/display.c index 97b4275..819fda7 100644 --- a/app/src/odroid/display.c +++ b/app/src/odroid/display.c @@ -1,9 +1,12 @@ #include "display.h" -#include "macros.h" #include +#include #include #include +#include "macros.h" +// memset +#include static const char* LOG_TAG = "OdroidDisplay"; @@ -14,6 +17,8 @@ static const gpio_num_t LCD_PIN_CS = GPIO_NUM_5; static const gpio_num_t LCD_PIN_DC = GPIO_NUM_21; static const gpio_num_t LCD_PIN_BACKLIGHT = GPIO_NUM_14; +const int LCD_BACKLIGHT_ON_VALUE = 1; +const int DUTY_MAX = 0x1fff; typedef enum { @@ -27,7 +32,6 @@ typedef enum PIXEL_FORMAT_SET = 0x3Au, } CommandCode; - typedef struct { CommandCode code; @@ -35,52 +39,28 @@ typedef struct uint8_t length; } StartupCommand; - static spi_device_handle_t gSpiHandle; -static StartupCommand gStartupCommands[] = -{ - // Reset to defaults - { - SOFTWARE_RESET, - {}, - 0 - }, - - // Landscape Mode - // Top-Left Origin - // BGR Panel - { - MEMORY_ACCESS_CONTROL, - {0x20 | 0xC0 | 0x08}, - 1 - }, +static StartupCommand gStartupCommands[] = { + // Reset to defaults + {SOFTWARE_RESET, {}, 0}, - // 16 bits per pixel - { - PIXEL_FORMAT_SET, - {0x55}, - 1 - }, + // Landscape Mode + // Top-Left Origin + // BGR Panel + {MEMORY_ACCESS_CONTROL, {0x20 | 0xC0 | 0x08}, 1}, - // Exit sleep mode - { - SLEEP_OUT, - {}, - 0 - }, + // 16 bits per pixel + {PIXEL_FORMAT_SET, {0x55}, 1}, - // Turn on the display - { - DISPLAY_ON, - {}, - 0 - }, -}; + // Exit sleep mode + {SLEEP_OUT, {}, 0}, + // Turn on the display + {DISPLAY_ON, {}, 0}, +}; -static -void SendCommandCode(CommandCode code) +static void SendCommandCode(CommandCode code) { spi_transaction_t transaction = {}; @@ -92,8 +72,7 @@ void SendCommandCode(CommandCode code) spi_device_transmit(gSpiHandle, &transaction); } -static -void SendCommandParameters(uint8_t* data, int length) +static void SendCommandParameters(uint8_t* data, int length) { spi_transaction_t transaction = {}; @@ -105,7 +84,6 @@ void SendCommandParameters(uint8_t* data, int length) spi_device_transmit(gSpiHandle, &transaction); } - void Odroid_InitializeDisplay(void) { // Initialize the SPI bus @@ -123,7 +101,6 @@ void Odroid_InitializeDisplay(void) ESP_LOGI(LOG_TAG, "Initialized SPI Bus"); } - // Add the display device to the SPI bus { spi_device_interface_config_t spiDeviceConfig = {}; @@ -137,14 +114,12 @@ void Odroid_InitializeDisplay(void) ESP_LOGI(LOG_TAG, "Added display to SPI bus"); } - // Set the DC and backlight pins as outputs { gpio_set_direction(LCD_PIN_DC, GPIO_MODE_OUTPUT); gpio_set_direction(LCD_PIN_BACKLIGHT, GPIO_MODE_OUTPUT); } - // Send the initialization commands to the display { int commandCount = ARRAY_COUNT(gStartupCommands); @@ -163,17 +138,19 @@ void Odroid_InitializeDisplay(void) ESP_LOGI(LOG_TAG, "Initialized display"); } + + Odroid_BacklightInit(); } void Odroid_DrawFrame(uint16_t* buffer) { // Set drawing window width to (0, LCD_WIDTH) - uint8_t drawWidth[] = { 0, 0, UPPER_BYTE_16(LCD_WIDTH), LOWER_BYTE_16(LCD_WIDTH) }; + uint8_t drawWidth[] = {0, 0, UPPER_BYTE_16(LCD_WIDTH), LOWER_BYTE_16(LCD_WIDTH)}; SendCommandCode(COLUMN_ADDRESS_SET); SendCommandParameters(drawWidth, ARRAY_COUNT(drawWidth)); // Set drawing window height to (0, LCD_HEIGHT) - uint8_t drawHeight[] = { 0, 0, UPPER_BYTE_16(LCD_HEIGHT), LOWER_BYTE_16(LCD_HEIGHT) }; + uint8_t drawHeight[] = {0, 0, UPPER_BYTE_16(LCD_HEIGHT), LOWER_BYTE_16(LCD_HEIGHT)}; SendCommandCode(PAGE_ADDRESS_SET); SendCommandParameters(drawHeight, ARRAY_COUNT(drawHeight)); @@ -182,3 +159,60 @@ void Odroid_DrawFrame(uint16_t* buffer) SendCommandParameters((uint8_t*)buffer, LCD_WIDTH * LCD_HEIGHT * LCD_DEPTH); } +// +// Backlight control +// + +void Odroid_BacklightInit() +{ + // (duty range is 0 ~ ((2**bit_num)-1) + + // configure timer0 + ledc_timer_config_t ledc_timer; + memset(&ledc_timer, 0, sizeof(ledc_timer)); + + ledc_timer.bit_num = LEDC_TIMER_13_BIT; // set timer counter bit number + ledc_timer.freq_hz = 5000; // set frequency of pwm + ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; // timer mode, + ledc_timer.timer_num = LEDC_TIMER_0; // timer index + + ledc_timer_config(&ledc_timer); + + // set the configuration + ledc_channel_config_t ledc_channel; + memset(&ledc_channel, 0, sizeof(ledc_channel)); + + // set LEDC channel 0 + ledc_channel.channel = LEDC_CHANNEL_0; + // set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) + ledc_channel.duty = (LCD_BACKLIGHT_ON_VALUE) ? 0 : DUTY_MAX; + // GPIO number + ledc_channel.gpio_num = LCD_PIN_BACKLIGHT; + // GPIO INTR TYPE, as an example, we enable fade_end interrupt here. + ledc_channel.intr_type = LEDC_INTR_FADE_END; + // set LEDC mode, from ledc_mode_t + ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE; + // set LEDC timer source, if different channel use one timer, + // the frequency and bit_num of these channels should be the same + ledc_channel.timer_sel = LEDC_TIMER_0; + + ledc_channel_config(&ledc_channel); + + // initialize fade service. + ledc_fade_func_install(0); + + // duty range is 0 ~ ((2**bit_num)-1) + ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, + (LCD_BACKLIGHT_ON_VALUE) ? DUTY_MAX : 0, 500); + ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); +} + +void Odroid_BacklightDeinit() +{ + ledc_fade_func_uninstall(); + esp_err_t err = ledc_stop(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0); + if (err != ESP_OK) + { + printf("%s: ledc_stop failed.\n", __func__); + } +} diff --git a/app/src/odroid/display.h b/app/src/odroid/display.h index a727be1..da8a82e 100644 --- a/app/src/odroid/display.h +++ b/app/src/odroid/display.h @@ -11,3 +11,6 @@ void Odroid_InitializeDisplay(void); void Odroid_DrawFrame(uint16_t* buffer); +void Odroid_BacklightInit(); +// Also works to sleep the display +void Odroid_BacklightDeinit(); diff --git a/app/src/odroid/input.c b/app/src/odroid/input.c index a0a6648..ab0d46a 100644 --- a/app/src/odroid/input.c +++ b/app/src/odroid/input.c @@ -97,3 +97,86 @@ Odroid_Input Odroid_PollInput(void) return input; } +void Odroid_PrintInputState(Odroid_Input* input) +{ + bool inputSet = false; + if (input->a) + { + inputSet = true; + printf("a "); + } + if (input->b) + { + inputSet = true; + printf("b "); + } + if (input->volume) + { + inputSet = true; + printf("volume "); + } + if (input->menu) + { + inputSet = true; + printf("menu "); + } + if (input->select) + { + inputSet = true; + printf("select "); + } + if (input->start) + { + inputSet = true; + printf("start "); + } + if (input->left) + { + inputSet = true; + printf("left "); + } + if (input->right) + { + inputSet = true; + printf("right "); + } + if (input->up) + { + inputSet = true; + printf("up "); + } + if (input->down) + { + inputSet = true; + printf("down "); + } + + if (inputSet) + printf("\n"); +} + +int Odroid_HasAnyInput(Odroid_Input* input) +{ + if (input->a) + return true; + if (input->b) + return true; + if (input->volume) + return true; + if (input->menu) + return true; + if (input->select) + return true; + if (input->start) + return true; + if (input->left) + return true; + if (input->right) + return true; + if (input->up) + return true; + if (input->down) + return true; + + return false; +} diff --git a/app/src/odroid/input.h b/app/src/odroid/input.h index 656fd3f..0e477ae 100644 --- a/app/src/odroid/input.h +++ b/app/src/odroid/input.h @@ -19,4 +19,5 @@ typedef struct void Odroid_InitializeInput(void); Odroid_Input Odroid_PollInput(void); - +void Odroid_PrintInputState(Odroid_Input* input); +int Odroid_HasAnyInput(Odroid_Input* input);