Part 3: Display
This commit is contained in:
parent
2156cb1e1a
commit
801a4e790d
|
@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.5)
|
||||||
set(EXTRA_COMPONENT_DIRS "src")
|
set(EXTRA_COMPONENT_DIRS "src")
|
||||||
set(COMPONENTS "esptool_py src")
|
set(COMPONENTS "esptool_py src")
|
||||||
|
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
|
||||||
project(game)
|
project(game)
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS "main.c"
|
SRCS
|
||||||
INCLUDE_DIRS ""
|
"main.c"
|
||||||
PRIV_REQUIRES "")
|
"odroid/display.c"
|
||||||
|
"odroid/input.c"
|
||||||
|
|
||||||
|
INCLUDE_DIRS
|
||||||
|
"."
|
||||||
|
|
||||||
|
PRIV_REQUIRES
|
||||||
|
"esp_adc_cal")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
// Counts the number of elements in an array
|
||||||
|
#define ARRAY_COUNT(value) ( sizeof(value) / sizeof(value[0]) )
|
||||||
|
|
||||||
|
// Swaps the endianness of a 16-bit value
|
||||||
|
#define SWAP_ENDIAN_16(value) ( (((value) & 0xFFu) << 8u) | ((value) >> 8u) )
|
||||||
|
|
||||||
|
// Constructs a 16-bit color value of the form RGB565 with proper ESP32 endianness
|
||||||
|
#define RGB565(red, green, blue) ( (((red) >> 3u) << 11u) | (((green) >> 2u) << 5u) | ((blue) >> 3u) )
|
||||||
|
|
||||||
|
// Converts bytes to bits
|
||||||
|
#define BYTES_TO_BITS(value) ( (value) * 8 )
|
||||||
|
|
||||||
|
// Extracts the upper byte of a 16-bit value
|
||||||
|
#define UPPER_BYTE_16(value) ( (value) >> 8u )
|
||||||
|
|
||||||
|
// Extracts the lower byte of a 16-bit value
|
||||||
|
#define LOWER_BYTE_16(value) ( (value) & 0xFFu )
|
||||||
|
|
37
src/main.c
37
src/main.c
|
@ -1,30 +1,53 @@
|
||||||
|
#include "odroid/display.h"
|
||||||
#include "odroid/input.h"
|
#include "odroid/input.h"
|
||||||
|
#include "macros.h"
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
static const char* LOG_TAG = "Main";
|
static const char* LOG_TAG = "Main";
|
||||||
|
static uint16_t gFramebuffer[LCD_WIDTH * LCD_HEIGHT];
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
Odroid_InitializeInput();
|
Odroid_InitializeInput();
|
||||||
|
Odroid_InitializeDisplay();
|
||||||
|
|
||||||
ESP_LOGI(LOG_TAG, "Odroid initialization complete - entering main loop");
|
ESP_LOGI(LOG_TAG, "Odroid initialization complete - entering main loop");
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
|
||||||
|
uint16_t color = 0xffff;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
memset(gFramebuffer, 0, 320 * 240 * 2);
|
||||||
|
|
||||||
Odroid_Input input = Odroid_PollInput();
|
Odroid_Input input = Odroid_PollInput();
|
||||||
|
|
||||||
printf(
|
if (input.left) { x -= 20; }
|
||||||
"\ra: %d b: %d start: %d select: %d vol: %d menu: %d up: %d down: %d left: %d right: %d ",
|
else if (input.right) { x += 20; }
|
||||||
input.a, input.b, input.start, input.select, input.volume, input.menu,
|
|
||||||
input.up, input.down, input.left, input.right);
|
|
||||||
|
|
||||||
fflush(stdout);
|
if (input.up) { y -= 20; }
|
||||||
|
else if (input.down) { y += 20; }
|
||||||
|
|
||||||
vTaskDelay(250 / portTICK_PERIOD_MS);
|
if (input.a) { color = SWAP_ENDIAN_16(RGB565(0xff, 0, 0)); }
|
||||||
|
else if (input.b) { color = SWAP_ENDIAN_16(RGB565(0, 0xff, 0)); }
|
||||||
|
else if (input.start) { color = SWAP_ENDIAN_16(RGB565(0, 0, 0xff)); }
|
||||||
|
else if (input.select) { color = SWAP_ENDIAN_16(RGB565(0xff, 0xff, 0xff)); }
|
||||||
|
|
||||||
|
for (int row = y; row < y + 50; ++row)
|
||||||
|
{
|
||||||
|
for (int col = x; col < x + 50; ++col)
|
||||||
|
{
|
||||||
|
gFramebuffer[LCD_WIDTH * row + col] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Odroid_DrawFrame(gFramebuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should never get here
|
// Should never get here
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
#include "display.h"
|
||||||
|
#include "macros.h"
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
|
||||||
|
static const char* LOG_TAG = "OdroidDisplay";
|
||||||
|
|
||||||
|
static const gpio_num_t LCD_PIN_MISO = GPIO_NUM_19;
|
||||||
|
static const gpio_num_t LCD_PIN_MOSI = GPIO_NUM_23;
|
||||||
|
static const gpio_num_t LCD_PIN_SCLK = GPIO_NUM_18;
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SOFTWARE_RESET = 0x01u,
|
||||||
|
SLEEP_OUT = 0x11u,
|
||||||
|
DISPLAY_ON = 0x29u,
|
||||||
|
COLUMN_ADDRESS_SET = 0x2Au,
|
||||||
|
PAGE_ADDRESS_SET = 0x2Bu,
|
||||||
|
MEMORY_WRITE = 0x2Cu,
|
||||||
|
MEMORY_ACCESS_CONTROL = 0x36u,
|
||||||
|
PIXEL_FORMAT_SET = 0x3Au,
|
||||||
|
} CommandCode;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
CommandCode code;
|
||||||
|
uint8_t parameters[15];
|
||||||
|
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
|
||||||
|
},
|
||||||
|
|
||||||
|
// 16 bits per pixel
|
||||||
|
{
|
||||||
|
PIXEL_FORMAT_SET,
|
||||||
|
{0x55},
|
||||||
|
1
|
||||||
|
},
|
||||||
|
|
||||||
|
// Exit sleep mode
|
||||||
|
{
|
||||||
|
SLEEP_OUT,
|
||||||
|
{},
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
// Turn on the display
|
||||||
|
{
|
||||||
|
DISPLAY_ON,
|
||||||
|
{},
|
||||||
|
0
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static
|
||||||
|
void SendCommandCode(CommandCode code)
|
||||||
|
{
|
||||||
|
spi_transaction_t transaction = {};
|
||||||
|
|
||||||
|
transaction.length = BYTES_TO_BITS(1);
|
||||||
|
transaction.tx_data[0] = (uint8_t)code;
|
||||||
|
transaction.flags = SPI_TRANS_USE_TXDATA;
|
||||||
|
|
||||||
|
gpio_set_level(LCD_PIN_DC, 0);
|
||||||
|
spi_device_transmit(gSpiHandle, &transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void SendCommandParameters(uint8_t* data, int length)
|
||||||
|
{
|
||||||
|
spi_transaction_t transaction = {};
|
||||||
|
|
||||||
|
transaction.length = BYTES_TO_BITS(length);
|
||||||
|
transaction.tx_buffer = data;
|
||||||
|
transaction.flags = 0;
|
||||||
|
|
||||||
|
gpio_set_level(LCD_PIN_DC, 1);
|
||||||
|
spi_device_transmit(gSpiHandle, &transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Odroid_InitializeDisplay(void)
|
||||||
|
{
|
||||||
|
// Initialize the SPI bus
|
||||||
|
{
|
||||||
|
spi_bus_config_t spiBusConfig = {};
|
||||||
|
spiBusConfig.miso_io_num = LCD_PIN_MISO;
|
||||||
|
spiBusConfig.mosi_io_num = LCD_PIN_MOSI;
|
||||||
|
spiBusConfig.sclk_io_num = LCD_PIN_SCLK;
|
||||||
|
spiBusConfig.quadwp_io_num = -1;
|
||||||
|
spiBusConfig.quadhd_io_num = -1;
|
||||||
|
spiBusConfig.max_transfer_sz = LCD_WIDTH * LCD_HEIGHT * LCD_DEPTH;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(spi_bus_initialize(VSPI_HOST, &spiBusConfig, 1));
|
||||||
|
|
||||||
|
ESP_LOGI(LOG_TAG, "Initialized SPI Bus");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add the display device to the SPI bus
|
||||||
|
{
|
||||||
|
spi_device_interface_config_t spiDeviceConfig = {};
|
||||||
|
spiDeviceConfig.clock_speed_hz = SPI_MASTER_FREQ_40M;
|
||||||
|
spiDeviceConfig.spics_io_num = LCD_PIN_CS;
|
||||||
|
spiDeviceConfig.queue_size = 1;
|
||||||
|
spiDeviceConfig.flags = SPI_DEVICE_NO_DUMMY;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(spi_bus_add_device(VSPI_HOST, &spiDeviceConfig, &gSpiHandle));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
for (int commandIndex = 0; commandIndex < commandCount; ++commandIndex)
|
||||||
|
{
|
||||||
|
StartupCommand* command = &gStartupCommands[commandIndex];
|
||||||
|
|
||||||
|
SendCommandCode(command->code);
|
||||||
|
|
||||||
|
if (command->length > 0)
|
||||||
|
{
|
||||||
|
SendCommandParameters(command->parameters, command->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(LOG_TAG, "Initialized display");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) };
|
||||||
|
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) };
|
||||||
|
SendCommandCode(PAGE_ADDRESS_SET);
|
||||||
|
SendCommandParameters(drawHeight, ARRAY_COUNT(drawHeight));
|
||||||
|
|
||||||
|
// Send the buffer to the display
|
||||||
|
SendCommandCode(MEMORY_WRITE);
|
||||||
|
SendCommandParameters((uint8_t*)buffer, LCD_WIDTH * LCD_HEIGHT * LCD_DEPTH);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define LCD_WIDTH (320)
|
||||||
|
#define LCD_HEIGHT (240)
|
||||||
|
#define LCD_DEPTH (2)
|
||||||
|
|
||||||
|
|
||||||
|
void Odroid_InitializeDisplay(void);
|
||||||
|
void Odroid_DrawFrame(uint16_t* buffer);
|
||||||
|
|
Loading…
Reference in New Issue