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(COMPONENTS "esptool_py src")
|
||||
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
project(game)
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
idf_component_register(
|
||||
SRCS "main.c"
|
||||
INCLUDE_DIRS ""
|
||||
PRIV_REQUIRES "")
|
||||
SRCS
|
||||
"main.c"
|
||||
"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 "macros.h"
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static const char* LOG_TAG = "Main";
|
||||
|
||||
static uint16_t gFramebuffer[LCD_WIDTH * LCD_HEIGHT];
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
Odroid_InitializeInput();
|
||||
Odroid_InitializeDisplay();
|
||||
|
||||
ESP_LOGI(LOG_TAG, "Odroid initialization complete - entering main loop");
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
uint16_t color = 0xffff;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
memset(gFramebuffer, 0, 320 * 240 * 2);
|
||||
|
||||
Odroid_Input input = Odroid_PollInput();
|
||||
|
||||
printf(
|
||||
"\ra: %d b: %d start: %d select: %d vol: %d menu: %d up: %d down: %d left: %d right: %d ",
|
||||
input.a, input.b, input.start, input.select, input.volume, input.menu,
|
||||
input.up, input.down, input.left, input.right);
|
||||
if (input.left) { x -= 20; }
|
||||
else if (input.right) { x += 20; }
|
||||
|
||||
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
|
||||
|
|
|
@ -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