Browse Source

Added timer, sleep mode, and input functions

master
Macoy Madson 11 months ago
parent
commit
9600ef330b
7 changed files with 321 additions and 65 deletions
  1. +22
    -0
      .clang-format
  2. +2
    -0
      ReadMe.org
  3. +126
    -15
      app/src/main.c
  4. +83
    -49
      app/src/odroid/display.c
  5. +3
    -0
      app/src/odroid/display.h
  6. +83
    -0
      app/src/odroid/input.c
  7. +2
    -1
      app/src/odroid/input.h

+ 22
- 0
.clang-format View File

@ -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

+ 2
- 0
ReadMe.org View File

@ -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


+ 126
- 15
app/src/main.c View File

@ -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();
}

+ 83
- 49
app/src/odroid/display.c View File

@ -1,9 +1,12 @@
#include "display.h"
#include "macros.h"
#include <driver/gpio.h>
#include <driver/ledc.h>
#include <driver/spi_master.h>
#include <esp_log.h>
#include "macros.h"
// memset
#include <string.h>
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__);
}
}

+ 3
- 0
app/src/odroid/display.h View File

@ -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();

+ 83
- 0
app/src/odroid/input.c View File

@ -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;
}

+ 2
- 1
app/src/odroid/input.h View File

@ -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);

Loading…
Cancel
Save