1
0
Fork 0

Part 3: Display

This commit is contained in:
Austin Morlan 2020-05-17 20:29:45 -07:00
parent 2156cb1e1a
commit 801a4e790d
Signed by: austin
GPG Key ID: FD6B27654AF5E348
6 changed files with 259 additions and 10 deletions

View File

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

View File

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

21
src/macros.h Normal file
View File

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

View File

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

184
src/odroid/display.c Normal file
View File

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

13
src/odroid/display.h Normal file
View File

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