Compare commits
5 Commits
801a4e790d
...
1096c7eac2
Author | SHA1 | Date |
---|---|---|
Austin Morlan | 1096c7eac2 | |
Austin Morlan | 02a911ef9f | |
Austin Morlan | 8f17172aa3 | |
Austin Morlan | e525a823dc | |
Austin Morlan | d86013c90c |
16
README.md
16
README.md
|
@ -2,8 +2,22 @@
|
|||
|
||||
* Get the [ESP-IDF v4.0](https://github.com/espressif/esp-idf/releases/tag/v4.0)
|
||||
* Follow Steps 1 through 3 of the [Documentation](https://docs.espressif.com/projects/esp-idf/en/v4.0/get-started/index.html#step-1-install-prerequisites)
|
||||
* Modify the `export.sh` script in this directory and fill out `IDF_PATH` and `IDF_TOOLS_PATH`
|
||||
* Comment out line 303 of `esp-idf/components/driver/sdspi_host.c` to enable the shared SPI bus
|
||||
|
||||
```c
|
||||
// Initialize SPI bus
|
||||
esp_err_t ret = spi_bus_initialize((spi_host_device_t)slot, &buscfg,
|
||||
slot_config->dma_channel);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGD(TAG, "spi_bus_initialize failed with rc=0x%x", ret);
|
||||
//return ret;
|
||||
}
|
||||
```
|
||||
|
||||
* Modify the `export.sh` script in the `game` directory and fill out `IDF_PATH` and `IDF_TOOLS_PATH`
|
||||
* `IDF_PATH` is the extracted ESP-IDF
|
||||
* `IDF_TOOLS_PATH` is the path to the toolchain
|
||||
* `source export.sh`
|
||||
|
||||
![Current Status](https://austinmorlan.com/posts/embedded_game_programming_8/media/demo.gif)
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,4 @@
|
|||
ABCDEFGHIJ
|
||||
KLMNOPQRST
|
||||
UVWXYZ1234
|
||||
567890:!?
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
JASC-PAL
|
||||
0100
|
||||
4
|
||||
255 255 255
|
||||
171 171 171
|
||||
84 84 84
|
||||
0 0 0
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -3,7 +3,6 @@ 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,12 +1,17 @@
|
|||
idf_component_register(
|
||||
SRCS
|
||||
"main.c"
|
||||
"odroid/audio.c"
|
||||
"odroid/battery.c"
|
||||
"odroid/display.c"
|
||||
"odroid/input.c"
|
||||
"odroid/sdcard.c"
|
||||
"text.c"
|
||||
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
|
||||
PRIV_REQUIRES
|
||||
"esp_adc_cal")
|
||||
"esp_adc_cal"
|
||||
"fatfs")
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
// AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
static const int GLYPH_WIDTH = 16;
|
||||
static const int GLYPH_HEIGHT = 16;
|
||||
|
||||
|
||||
int GetGlyphIndex(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'a': case 'A': { return 0; break; }
|
||||
case 'b': case 'B': { return 1; break; }
|
||||
case 'c': case 'C': { return 2; break; }
|
||||
case 'd': case 'D': { return 3; break; }
|
||||
case 'e': case 'E': { return 4; break; }
|
||||
case 'f': case 'F': { return 5; break; }
|
||||
case 'g': case 'G': { return 6; break; }
|
||||
case 'h': case 'H': { return 7; break; }
|
||||
case 'i': case 'I': { return 8; break; }
|
||||
case 'j': case 'J': { return 9; break; }
|
||||
case 'k': case 'K': { return 10; break; }
|
||||
case 'l': case 'L': { return 11; break; }
|
||||
case 'm': case 'M': { return 12; break; }
|
||||
case 'n': case 'N': { return 13; break; }
|
||||
case 'o': case 'O': { return 14; break; }
|
||||
case 'p': case 'P': { return 15; break; }
|
||||
case 'q': case 'Q': { return 16; break; }
|
||||
case 'r': case 'R': { return 17; break; }
|
||||
case 's': case 'S': { return 18; break; }
|
||||
case 't': case 'T': { return 19; break; }
|
||||
case 'u': case 'U': { return 20; break; }
|
||||
case 'v': case 'V': { return 21; break; }
|
||||
case 'w': case 'W': { return 22; break; }
|
||||
case 'x': case 'X': { return 23; break; }
|
||||
case 'y': case 'Y': { return 24; break; }
|
||||
case 'z': case 'Z': { return 25; break; }
|
||||
case '1': { return 26; break; }
|
||||
case '2': { return 27; break; }
|
||||
case '3': { return 28; break; }
|
||||
case '4': { return 29; break; }
|
||||
case '5': { return 30; break; }
|
||||
case '6': { return 31; break; }
|
||||
case '7': { return 32; break; }
|
||||
case '8': { return 33; break; }
|
||||
case '9': { return 34; break; }
|
||||
case '0': { return 35; break; }
|
||||
case ':': { return 36; break; }
|
||||
case '!': { return 37; break; }
|
||||
case '?': { return 38; break; }
|
||||
default: { assert(NULL); break; }
|
||||
}
|
||||
}
|
||||
|
||||
static const uint16_t glyphMap[39][16] =
|
||||
{
|
||||
// A
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x781E,0x781E,0x781E,0x7FFE,
|
||||
0x7FFE,0x7FFE,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x0000,
|
||||
},
|
||||
|
||||
// B
|
||||
{
|
||||
0x0000,0x7FFC,0x7FFE,0x7FFE,
|
||||
0x780E,0x780E,0x7FFE,0x7FFE,
|
||||
0x7FFC,0x780C,0x780E,0x780E,
|
||||
0x7FFE,0x7FFE,0x7FFC,0x0000,
|
||||
},
|
||||
|
||||
// C
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x7800,0x7800,0x7800,0x7800,
|
||||
0x7800,0x7800,0x7800,0x7800,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// D
|
||||
{
|
||||
0x0000,0x7FF8,0x7FFE,0x7FFE,
|
||||
0x781E,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x781E,
|
||||
0x7FFE,0x7FFE,0x7FF8,0x0000,
|
||||
},
|
||||
|
||||
// E
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x7800,0x7800,0x7FFC,0x7FFC,
|
||||
0x7FFC,0x7800,0x7800,0x7800,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// F
|
||||
{
|
||||
0x0000,0x7FF8,0x7FF8,0x7FF8,
|
||||
0x7800,0x7800,0x7FF0,0x7FF0,
|
||||
0x7FF0,0x7800,0x7800,0x7800,
|
||||
0x7800,0x7800,0x7800,0x0000,
|
||||
},
|
||||
|
||||
// G
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x7800,0x7800,0x7800,0x7800,
|
||||
0x787E,0x787E,0x781E,0x781E,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// H
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x7FFE,0x7FFE,
|
||||
0x7FFE,0x7FFE,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x0000,
|
||||
},
|
||||
|
||||
// I
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x03C0,0x03C0,0x03C0,0x03C0,
|
||||
0x03C0,0x03C0,0x03C0,0x03C0,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// J
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x00F0,0x00F0,0x00F0,0x00F0,
|
||||
0x00F0,0x00F0,0x70F0,0x70F0,
|
||||
0x7FF0,0x7FF0,0x7FF0,0x0000,
|
||||
},
|
||||
|
||||
// K
|
||||
{
|
||||
0x0000,0x780E,0x780E,0x7838,
|
||||
0x7838,0x79E0,0x79E0,0x7F80,
|
||||
0x7F80,0x79E0,0x79E0,0x7878,
|
||||
0x7878,0x781E,0x781E,0x0000,
|
||||
},
|
||||
|
||||
// L
|
||||
{
|
||||
0x0000,0x7800,0x7800,0x7800,
|
||||
0x7800,0x7800,0x7800,0x7800,
|
||||
0x7800,0x7800,0x7800,0x7800,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// M
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x7E7E,
|
||||
0x7E7E,0x7FFE,0x7FFE,0x799E,
|
||||
0x799E,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x0000,
|
||||
},
|
||||
|
||||
// N
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x7E1E,
|
||||
0x7E1E,0x7F9E,0x7F9E,0x7FFE,
|
||||
0x79FE,0x79FE,0x787E,0x787E,
|
||||
0x781E,0x781E,0x781E,0x0000,
|
||||
},
|
||||
|
||||
// O
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x781E,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x781E,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// P
|
||||
{
|
||||
0x0000,0x7FF8,0x7FFE,0x7FFE,
|
||||
0x781E,0x781E,0x781E,0x7FFE,
|
||||
0x7FF8,0x7FF8,0x7800,0x7800,
|
||||
0x7800,0x7800,0x7800,0x0000,
|
||||
},
|
||||
|
||||
// Q
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x7006,0x7006,0x7006,0x7006,
|
||||
0x7006,0x7006,0x70C6,0x70C6,
|
||||
0x7FF8,0x7FF8,0x001E,0x0000,
|
||||
},
|
||||
|
||||
// R
|
||||
{
|
||||
0x0000,0x7FF8,0x7FFE,0x7FFE,
|
||||
0x781E,0x781E,0x781E,0x7FFE,
|
||||
0x7FF8,0x7FF8,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x0000,
|
||||
},
|
||||
|
||||
// S
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x7800,0x7800,0x7FFE,0x7FFE,
|
||||
0x7FFE,0x001E,0x001E,0x001E,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// T
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x03C0,0x03C0,0x03C0,0x03C0,
|
||||
0x03C0,0x03C0,0x03C0,0x03C0,
|
||||
0x03C0,0x03C0,0x03C0,0x0000,
|
||||
},
|
||||
|
||||
// U
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x781E,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// V
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x781E,
|
||||
0x1E78,0x1E78,0x1E78,0x1E78,
|
||||
0x07E0,0x07E0,0x07E0,0x0000,
|
||||
},
|
||||
|
||||
// W
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x781E,0x799E,
|
||||
0x799E,0x7FFE,0x7FFE,0x7E7E,
|
||||
0x7E7E,0x781E,0x781E,0x0000,
|
||||
},
|
||||
|
||||
// X
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x781E,
|
||||
0x1E78,0x1E78,0x07E0,0x07E0,
|
||||
0x07E0,0x07E0,0x1E78,0x1E78,
|
||||
0x781E,0x781E,0x781E,0x0000,
|
||||
},
|
||||
|
||||
// Y
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x7E7E,0x7E7E,
|
||||
0x1FF8,0x1FF8,0x07E0,0x07E0,
|
||||
0x07E0,0x07E0,0x07E0,0x0000,
|
||||
},
|
||||
|
||||
// Z
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x0078,0x0078,0x01E0,0x01E0,
|
||||
0x0780,0x0780,0x1E00,0x1E00,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// 1
|
||||
{
|
||||
0x0000,0x01E0,0x01E0,0x01E0,
|
||||
0x01E0,0x01E0,0x01E0,0x01E0,
|
||||
0x01E0,0x01E0,0x01E0,0x01E0,
|
||||
0x01E0,0x01E0,0x01E0,0x0000,
|
||||
},
|
||||
|
||||
// 2
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x001E,0x001E,0x7FFE,0x7FFE,
|
||||
0x7FFE,0x7800,0x7800,0x7800,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// 3
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x001E,0x001E,0x3FFE,0x3FFE,
|
||||
0x3FFE,0x001E,0x001E,0x001E,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// 4
|
||||
{
|
||||
0x0000,0x781E,0x781E,0x781E,
|
||||
0x781E,0x781E,0x7FFE,0x7FFE,
|
||||
0x7FFE,0x7FFE,0x001E,0x001E,
|
||||
0x001E,0x001E,0x001E,0x0000,
|
||||
},
|
||||
|
||||
// 5
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x7800,0x7800,0x7FFE,0x7FFE,
|
||||
0x7FFE,0x001E,0x001E,0x001E,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// 6
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x7800,0x7800,0x7FFE,0x7FFE,
|
||||
0x7FFE,0x781E,0x781E,0x781E,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// 7
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x001E,0x001E,0x001E,0x001E,
|
||||
0x001E,0x001E,0x001E,0x001E,
|
||||
0x001E,0x001E,0x001E,0x0000,
|
||||
},
|
||||
|
||||
// 8
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x781E,
|
||||
0x781E,0x781E,0x7FFE,0x7FFE,
|
||||
0x7FFE,0x781E,0x781E,0x781E,
|
||||
0x781E,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// 9
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x781E,0x781E,0x781E,0x7FFE,
|
||||
0x7FFE,0x7FFE,0x001E,0x001E,
|
||||
0x001E,0x001E,0x001E,0x0000,
|
||||
},
|
||||
|
||||
// 0
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x781E,0x781E,0x799E,0x799E,
|
||||
0x799E,0x799E,0x781E,0x781E,
|
||||
0x7FFE,0x7FFE,0x7FFE,0x0000,
|
||||
},
|
||||
|
||||
// :
|
||||
{
|
||||
0x0000,0x0000,0x3C00,0x3C00,
|
||||
0x3C00,0x3C00,0x0000,0x0000,
|
||||
0x0000,0x0000,0x3C00,0x3C00,
|
||||
0x3C00,0x3C00,0x0000,0x0000,
|
||||
},
|
||||
|
||||
// !
|
||||
{
|
||||
0x0000,0x3C00,0x3C00,0x3C00,
|
||||
0x3C00,0x3C00,0x3C00,0x3C00,
|
||||
0x3C00,0x3C00,0x0000,0x0000,
|
||||
0x3C00,0x3C00,0x3C00,0x0000,
|
||||
},
|
||||
|
||||
// ?
|
||||
{
|
||||
0x0000,0x7FFE,0x7FFE,0x7FFE,
|
||||
0x781E,0x781E,0x79FE,0x79FE,
|
||||
0x01E0,0x01E0,0x0000,0x0000,
|
||||
0x01E0,0x01E0,0x01E0,0x0000,
|
||||
},
|
||||
|
||||
};
|
|
@ -0,0 +1,236 @@
|
|||
#include "odroid/audio.h"
|
||||
#include "odroid/battery.h"
|
||||
#include "odroid/display.h"
|
||||
#include "odroid/input.h"
|
||||
#include "odroid/sdcard.h"
|
||||
#include "macros.h"
|
||||
#include "text.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];
|
||||
|
||||
|
||||
static const uint16_t palette[4] =
|
||||
{
|
||||
0xFFFF,
|
||||
0x55AD,
|
||||
0xAA52,
|
||||
0x0000,
|
||||
};
|
||||
|
||||
static const uint8_t tiles[][16*16] =
|
||||
{
|
||||
// White
|
||||
{
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
},
|
||||
|
||||
// Light Grey
|
||||
{
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||
0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
},
|
||||
|
||||
// Dark Grey
|
||||
{
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||
0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
},
|
||||
|
||||
// Black
|
||||
{
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,3,3,3,3,3,3,3,3,3,3,3,3,0,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||
0,0,3,3,3,3,3,3,3,3,3,3,3,3,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
},
|
||||
};
|
||||
|
||||
static int tileBuffer[15][40] =
|
||||
{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0},
|
||||
{0, 0, 3, 0, 0, 0, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
|
||||
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
|
||||
{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},
|
||||
{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},
|
||||
{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},
|
||||
{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;
|
||||
int startY = y * 16;
|
||||
|
||||
for (int row = 0; row < 16; ++row)
|
||||
{
|
||||
for (int col = 0; col < 16; ++col)
|
||||
{
|
||||
uint8_t paletteIndex = tiles[index][row * 16 + col];
|
||||
|
||||
int screenY = startY + row;
|
||||
int screenX = startX + col;
|
||||
|
||||
uint16_t color = palette[paletteIndex];
|
||||
|
||||
gFramebuffer[screenY * LCD_WIDTH + screenX] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
Odroid_InitializeInput();
|
||||
Odroid_InitializeDisplay();
|
||||
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;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
memset(gFramebuffer, 0xff, 320*240*2);
|
||||
|
||||
Odroid_Input input = Odroid_PollInput();
|
||||
|
||||
if (input.left)
|
||||
{
|
||||
xLeft -= 1;
|
||||
|
||||
if (xLeft < 0)
|
||||
{
|
||||
xLeft = 39;
|
||||
}
|
||||
}
|
||||
|
||||
else if (input.right)
|
||||
{
|
||||
xLeft += 1;
|
||||
|
||||
if (xLeft > 39)
|
||||
{
|
||||
xLeft = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int tileY = 0; tileY < 15; ++tileY)
|
||||
{
|
||||
for (int tileX = xLeft; tileX < xLeft + 20; ++tileX)
|
||||
{
|
||||
int tile = tileX % 40;
|
||||
|
||||
int tileIndex = tileBuffer[tileY][tile];
|
||||
|
||||
DrawTile(tileIndex, tileX - xLeft, tileY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char string[5];
|
||||
snprintf(string, 5, "%02d", xLeft);
|
||||
DrawText(gFramebuffer, string, 2, 0, 0, palette[3]);
|
||||
|
||||
int tileRight = (xLeft + 20) % 40;
|
||||
snprintf(string, 5, "%02d", tileRight);
|
||||
DrawText(gFramebuffer, string, 2, 18, 0, palette[3]);
|
||||
|
||||
if (input.menu)
|
||||
{
|
||||
snprintf(snapFilename, 20, "/sdcard/frame%02d", frameIndex);
|
||||
|
||||
ESP_LOGI(LOG_TAG, "Writing snapshot to %s", snapFilename);
|
||||
|
||||
FILE* snapFile = fopen(snapFilename, "wb");
|
||||
assert(snapFile);
|
||||
|
||||
fwrite(gFramebuffer, 1, LCD_WIDTH * LCD_HEIGHT * sizeof(gFramebuffer[0]), snapFile);
|
||||
|
||||
fclose(snapFile);
|
||||
|
||||
++frameIndex;
|
||||
}
|
||||
|
||||
|
||||
Odroid_DrawFrame(gFramebuffer);
|
||||
}
|
||||
|
||||
// Should never get here
|
||||
esp_restart();
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
#include "audio.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <driver/i2s.h>
|
||||
|
||||
|
||||
static const gpio_num_t AUDIO_AMP_SD_PIN = GPIO_NUM_25;
|
||||
|
||||
static QueueHandle_t gQueue;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t* buffer;
|
||||
int length;
|
||||
} QueueData;
|
||||
|
||||
|
||||
static void PlayTask(void *arg)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
QueueData data;
|
||||
|
||||
if (xQueueReceive(gQueue, &data, 10))
|
||||
{
|
||||
size_t bytesWritten;
|
||||
i2s_write(I2S_NUM_0, data.buffer, data.length, &bytesWritten, portMAX_DELAY);
|
||||
i2s_zero_dma_buffer(I2S_NUM_0);
|
||||
}
|
||||
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void Odroid_InitializeAudio(void)
|
||||
{
|
||||
// Configure the amplifier shutdown signal
|
||||
{
|
||||
gpio_config_t gpioConfig = {};
|
||||
|
||||
gpioConfig.mode = GPIO_MODE_OUTPUT;
|
||||
gpioConfig.pin_bit_mask = 1ULL << AUDIO_AMP_SD_PIN;
|
||||
|
||||
ESP_ERROR_CHECK(gpio_config(&gpioConfig));
|
||||
|
||||
gpio_set_level(AUDIO_AMP_SD_PIN, 1);
|
||||
}
|
||||
|
||||
// Configure the I2S driver
|
||||
{
|
||||
i2s_config_t i2sConfig= {};
|
||||
|
||||
i2sConfig.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN;
|
||||
i2sConfig.sample_rate = 5512;
|
||||
i2sConfig.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
|
||||
i2sConfig.communication_format = I2S_COMM_FORMAT_I2S_MSB;
|
||||
i2sConfig.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
|
||||
i2sConfig.dma_buf_count = 8;
|
||||
i2sConfig.dma_buf_len = 64;
|
||||
|
||||
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2sConfig, 0, NULL));
|
||||
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_LEFT_EN));
|
||||
}
|
||||
|
||||
// Create task for playing sounds so that our main task isn't blocked
|
||||
{
|
||||
gQueue = xQueueCreate(1, sizeof(QueueData));
|
||||
assert(gQueue);
|
||||
|
||||
BaseType_t result = xTaskCreatePinnedToCore(&PlayTask, "I2S Task", 1024, NULL, 5, NULL, 1);
|
||||
assert(result == pdPASS);
|
||||
}
|
||||
}
|
||||
|
||||
void Odroid_PlayAudio(uint16_t* buffer, int length)
|
||||
{
|
||||
QueueData data = {};
|
||||
|
||||
data.buffer = buffer;
|
||||
data.length = length;
|
||||
|
||||
xQueueSendToBack(gQueue, &data, portMAX_DELAY);
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
void Odroid_InitializeAudio(void);
|
||||
void Odroid_PlayAudio(uint16_t* buffer, int length);
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#include "battery.h"
|
||||
#include <driver/adc.h>
|
||||
#include <esp_adc_cal.h>
|
||||
#include <esp_log.h>
|
||||
#include <soc/adc_channel.h>
|
||||
|
||||
|
||||
static const char* LOG_TAG = "OdroidBattery";
|
||||
|
||||
static const adc1_channel_t BATTERY_READ_PIN = ADC1_GPIO36_CHANNEL;
|
||||
static const gpio_num_t BATTERY_LED_PIN = GPIO_NUM_2;
|
||||
|
||||
static esp_adc_cal_characteristics_t gCharacteristics;
|
||||
|
||||
void Odroid_InitializeBatteryReader()
|
||||
{
|
||||
// Configure LED
|
||||
{
|
||||
gpio_config_t gpioConfig = {};
|
||||
|
||||
gpioConfig.mode = GPIO_MODE_OUTPUT;
|
||||
gpioConfig.pin_bit_mask = 1ULL << BATTERY_LED_PIN;
|
||||
|
||||
ESP_ERROR_CHECK(gpio_config(&gpioConfig));
|
||||
}
|
||||
|
||||
// Configure ADC
|
||||
{
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
adc1_config_channel_atten(BATTERY_READ_PIN, ADC_ATTEN_DB_11);
|
||||
adc1_config_channel_atten(BATTERY_READ_PIN, ADC_ATTEN_DB_11);
|
||||
|
||||
esp_adc_cal_value_t type = esp_adc_cal_characterize(
|
||||
ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &gCharacteristics);
|
||||
|
||||
// The ESP32 in the Odroid Go should have its fuse set with a pre-calibrated vref
|
||||
assert(type == ESP_ADC_CAL_VAL_EFUSE_VREF);
|
||||
}
|
||||
|
||||
ESP_LOGI(LOG_TAG, "Battery reader initialized");
|
||||
}
|
||||
|
||||
uint32_t Odroid_ReadBatteryLevel(void)
|
||||
{
|
||||
const int SAMPLE_COUNT = 20;
|
||||
|
||||
|
||||
uint32_t raw = 0;
|
||||
|
||||
for (int sampleIndex = 0; sampleIndex < SAMPLE_COUNT; ++sampleIndex)
|
||||
{
|
||||
raw += adc1_get_raw(BATTERY_READ_PIN);
|
||||
}
|
||||
|
||||
raw /= SAMPLE_COUNT;
|
||||
|
||||
|
||||
// Voltage divider reports half actual voltage
|
||||
uint32_t voltage = 2 * esp_adc_cal_raw_to_voltage(raw, &gCharacteristics);
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
||||
void Odroid_EnableBatteryLight(void)
|
||||
{
|
||||
gpio_set_level(BATTERY_LED_PIN, 1);
|
||||
}
|
||||
|
||||
void Odroid_DisableBatteryLight(void)
|
||||
{
|
||||
gpio_set_level(BATTERY_LED_PIN, 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
void Odroid_InitializeBatteryReader(void);
|
||||
uint32_t Odroid_ReadBatteryLevel(void);
|
||||
void Odroid_EnableBatteryLight(void);
|
||||
void Odroid_DisableBatteryLight(void);
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#include "sdcard.h"
|
||||
#include <esp_vfs_fat.h>
|
||||
#include <driver/sdmmc_host.h>
|
||||
#include <driver/sdspi_host.h>
|
||||
#include <sdmmc_cmd.h>
|
||||
|
||||
|
||||
static const gpio_num_t SD_PIN_MISO = GPIO_NUM_19;
|
||||
static const gpio_num_t SD_PIN_MOSI = GPIO_NUM_23;
|
||||
static const gpio_num_t SD_PIN_SCLK = GPIO_NUM_18;
|
||||
static const gpio_num_t SD_PIN_CS = GPIO_NUM_22;
|
||||
|
||||
|
||||
void Odroid_InitializeSdcard()
|
||||
{
|
||||
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
|
||||
host.slot = VSPI_HOST;
|
||||
|
||||
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
|
||||
slot_config.gpio_miso = SD_PIN_MISO;
|
||||
slot_config.gpio_mosi = SD_PIN_MOSI;
|
||||
slot_config.gpio_sck = SD_PIN_SCLK;
|
||||
slot_config.gpio_cs = SD_PIN_CS;
|
||||
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {};
|
||||
mount_config.format_if_mount_failed = false;
|
||||
mount_config.max_files = 5;
|
||||
|
||||
sdmmc_card_t* card;
|
||||
|
||||
ESP_ERROR_CHECK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card));
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
void Odroid_InitializeSdcard(void);
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#include "font.h"
|
||||
#include "odroid/display.h"
|
||||
#include "text.h"
|
||||
|
||||
|
||||
static const int MAX_GLYPHS_PER_ROW = LCD_WIDTH / GLYPH_WIDTH;
|
||||
static const int MAX_GLYPHS_PER_COL = LCD_HEIGHT / GLYPH_HEIGHT;
|
||||
|
||||
void DrawText(uint16_t* framebuffer, char* string, int length, int x, int y, uint16_t color)
|
||||
{
|
||||
assert(x + length <= MAX_GLYPHS_PER_ROW);
|
||||
assert(y <= MAX_GLYPHS_PER_COL);
|
||||
|
||||
for (int charIndex = 0; charIndex < length; ++charIndex)
|
||||
{
|
||||
char c = string[charIndex];
|
||||
|
||||
if (c == ' ')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int xStart = GLYPH_WIDTH * (x + charIndex);
|
||||
int yStart = GLYPH_HEIGHT * y;
|
||||
|
||||
for (int row = 0; row < GLYPH_HEIGHT; ++row)
|
||||
{
|
||||
for (int col = 0; col < GLYPH_WIDTH; ++col)
|
||||
{
|
||||
int bitPosition = 1U << (15U - col);
|
||||
int glyphIndex = GetGlyphIndex(c);
|
||||
|
||||
uint16_t pixel = glyphMap[glyphIndex][row] & bitPosition;
|
||||
|
||||
if (pixel)
|
||||
{
|
||||
int screenX = xStart + col;
|
||||
int screenY = yStart + row;
|
||||
|
||||
framebuffer[screenY * LCD_WIDTH + screenX] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
void DrawText(uint16_t* framebuffer, char* string, int length, int tileX, int tileY, uint16_t color);
|
||||
|
56
src/main.c
56
src/main.c
|
@ -1,56 +0,0 @@
|
|||
#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();
|
||||
|
||||
if (input.left) { x -= 20; }
|
||||
else if (input.right) { x += 20; }
|
||||
|
||||
if (input.up) { y -= 20; }
|
||||
else if (input.down) { y += 20; }
|
||||
|
||||
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
|
||||
esp_restart();
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(tools)
|
||||
|
||||
set(CMAKE_C_FLAGS "-Wall -Werror")
|
||||
|
||||
add_executable(font_processor src/font_processor.c)
|
||||
add_executable(palette_processor src/palette_processor.c)
|
||||
add_executable(tile_processor src/tile_processor.c)
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 6)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <input image> <layout file> <output header> <glyph width> <glyph height>\n", argv[0]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* inFilename = argv[1];
|
||||
const char* layoutFilename = argv[2];
|
||||
const char* outFilename = argv[3];
|
||||
const int glyphWidth = atoi(argv[4]);
|
||||
const int glyphHeight = atoi(argv[5]);
|
||||
|
||||
|
||||
int imageWidth;
|
||||
int imageHeight;
|
||||
uint8_t* imageBuffer;
|
||||
{
|
||||
FILE* inFile = fopen(inFilename, "rb");
|
||||
assert(inFile);
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct BmpHeader
|
||||
{
|
||||
char magic[2];
|
||||
uint32_t totalSize;
|
||||
uint32_t reserved;
|
||||
uint32_t offset;
|
||||
uint32_t headerSize;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint16_t planes;
|
||||
uint16_t depth;
|
||||
uint32_t compression;
|
||||
uint32_t imageSize;
|
||||
int32_t horizontalResolution;
|
||||
int32_t verticalResolution;
|
||||
uint32_t paletteColorCount;
|
||||
uint32_t importantColorcount;
|
||||
} bmpHeader;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Read the BMP header so we know where the image data is located
|
||||
fread(&bmpHeader, 1, sizeof(bmpHeader), inFile);
|
||||
assert(bmpHeader.magic[0] == 'B' && bmpHeader.magic[1] == 'M');
|
||||
assert(bmpHeader.depth == 8);
|
||||
assert(bmpHeader.headerSize == 40);
|
||||
|
||||
// Go to location in file of image data
|
||||
fseek(inFile, bmpHeader.offset, SEEK_SET);
|
||||
|
||||
// Read in the image data
|
||||
imageBuffer = malloc(bmpHeader.imageSize);
|
||||
assert(imageBuffer);
|
||||
fread(imageBuffer, 1, bmpHeader.imageSize, inFile);
|
||||
|
||||
imageWidth = bmpHeader.width;
|
||||
imageHeight = bmpHeader.height;
|
||||
|
||||
fclose(inFile);
|
||||
}
|
||||
|
||||
|
||||
char** glyphLayout = NULL;
|
||||
int layoutRows = 0;
|
||||
{
|
||||
FILE* layoutFile = fopen(layoutFilename, "r");
|
||||
assert(layoutFile);
|
||||
|
||||
|
||||
// Count the number of lines in the file
|
||||
while (!feof(layoutFile))
|
||||
{
|
||||
char c = fgetc(layoutFile);
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
++layoutRows;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return file position indicator to start
|
||||
rewind(layoutFile);
|
||||
|
||||
|
||||
// Allocate enough memory for one string pointer per row
|
||||
glyphLayout = malloc(sizeof(*glyphLayout) * layoutRows);
|
||||
assert(glyphLayout);
|
||||
|
||||
|
||||
// Read the file into memory
|
||||
for (int rowIndex = 0; rowIndex < layoutRows; ++rowIndex)
|
||||
{
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
getline(&line, &len, layoutFile);
|
||||
|
||||
|
||||
int newlinePosition = strlen(line) - 1;
|
||||
|
||||
if (line[newlinePosition] == '\n')
|
||||
{
|
||||
line[newlinePosition] = '\0';
|
||||
}
|
||||
|
||||
|
||||
glyphLayout[rowIndex] = line;
|
||||
}
|
||||
|
||||
fclose(layoutFile);
|
||||
}
|
||||
|
||||
|
||||
printf("Input: %s\n", inFilename);
|
||||
printf("Output: %s\n", outFilename);
|
||||
printf("Width: %d\n", imageWidth);
|
||||
printf("Height: %d\n", imageHeight);
|
||||
printf("Glyph: %dx%d\n\n", glyphWidth, glyphHeight);
|
||||
|
||||
FILE* outFile = fopen(outFilename, "w");
|
||||
assert(outFile);
|
||||
|
||||
|
||||
// Generate the preamble
|
||||
{
|
||||
fprintf(outFile, "// AUTOMATICALLY GENERATED. DO NOT EDIT.\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "#pragma once\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "#include <assert.h>\n");
|
||||
fprintf(outFile, "#include <stdint.h>\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "static const int GLYPH_WIDTH = %d;\n", glyphWidth);
|
||||
fprintf(outFile, "static const int GLYPH_HEIGHT = %d;\n", glyphHeight);
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "\n");
|
||||
}
|
||||
|
||||
|
||||
// Generate the GetGlyphIndex function
|
||||
int glyphCount = 0;
|
||||
{
|
||||
fprintf(outFile, "int GetGlyphIndex(char c)\n");
|
||||
fprintf(outFile, "{\n");
|
||||
fprintf(outFile, " switch (c)\n");
|
||||
fprintf(outFile, " {\n");
|
||||
|
||||
for (int row = 0; row < layoutRows; ++row)
|
||||
{
|
||||
int glyphsInRow = strlen(glyphLayout[row]);
|
||||
|
||||
for (int glyph = 0; glyph < glyphsInRow; ++glyph)
|
||||
{
|
||||
char c = glyphLayout[row][glyph];
|
||||
|
||||
fprintf(outFile, " ");
|
||||
|
||||
if (isalpha(c))
|
||||
{
|
||||
fprintf(outFile, "case '%c': ", tolower(c));
|
||||
}
|
||||
|
||||
fprintf(outFile, "case '%c': { return %d; break; }\n", c, glyphCount);
|
||||
|
||||
++glyphCount;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(outFile, " default: { assert(NULL); break; }\n");
|
||||
fprintf(outFile, " }\n");
|
||||
fprintf(outFile, "}\n\n");
|
||||
}
|
||||
|
||||
|
||||
// Generate the font map with the calculated glyph bytes
|
||||
{
|
||||
fprintf(outFile, "static const uint16_t glyphMap[%d][%d] =\n", glyphCount, glyphHeight);
|
||||
fprintf(outFile, "{\n");
|
||||
|
||||
for (int y = 0; y < layoutRows; ++y)
|
||||
{
|
||||
int glyphsInRow = strlen(glyphLayout[y]);
|
||||
|
||||
for (int x = 0; x < glyphsInRow; ++x)
|
||||
{
|
||||
char c = glyphLayout[y][x];
|
||||
|
||||
fprintf(outFile, " // %c\n", c);
|
||||
fprintf(outFile, " {\n");
|
||||
fprintf(outFile, " ");
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (int row = y * glyphHeight; row < (y + 1) * glyphHeight; ++row)
|
||||
{
|
||||
uint16_t val = 0;
|
||||
|
||||
for (int col = x * glyphWidth; col < (x + 1) * glyphWidth; ++col)
|
||||
{
|
||||
// BMP is laid out bottom-to-top, but we want top-to-bottom (0-indexed)
|
||||
int y = imageHeight - row - 1;
|
||||
|
||||
uint8_t pixel = imageBuffer[y * imageWidth + col];
|
||||
|
||||
int bitPosition = 15 - (col % glyphWidth);
|
||||
val |= (pixel << bitPosition);
|
||||
}
|
||||
|
||||
fprintf(outFile, "0x%04X,", val);
|
||||
++count;
|
||||
|
||||
// Put a newline after four values to keep it orderly
|
||||
if ((count % 4) == 0)
|
||||
{
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, " ");
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(outFile, "},\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(outFile, "};\n");
|
||||
}
|
||||
|
||||
fclose(outFile);
|
||||
|
||||
printf("DONE\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <input palette> <output header>\n", argv[0]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* inFilename = argv[1];
|
||||
const char* outFilename = argv[2];
|
||||
|
||||
// Read the file
|
||||
uint16_t* palette;
|
||||
int paletteSize;
|
||||
{
|
||||
FILE* inFile = fopen(inFilename, "r");
|
||||
assert(inFile);
|
||||
|
||||
char* line;
|
||||
size_t len = 0;
|
||||
|
||||
getline(&line, &len, inFile);
|
||||
assert(strncmp(line, "JASC-PAL", 8) == 0);
|
||||
|
||||
getline(&line, &len, inFile);
|
||||
assert(strncmp(line, "0100", 4) == 0);
|
||||
|
||||
getline(&line, &len, inFile);
|
||||
paletteSize = atoi(line);
|
||||
|
||||
palette = malloc(sizeof(*palette) * paletteSize);
|
||||
assert(palette);
|
||||
|
||||
// Each line is of form R G B
|
||||
for (int i = 0; i < paletteSize; ++i)
|
||||
{
|
||||
getline(&line, &len, inFile);
|
||||
|
||||
char* tok = strtok(line, " ");
|
||||
int red = atoi(tok);
|
||||
|
||||
tok = strtok(NULL, " ");
|
||||
int green = atoi(tok);
|
||||
|
||||
tok = strtok(NULL, " ");
|
||||
int blue = atoi(tok);
|
||||
|
||||
uint16_t rgb565 =
|
||||
((red >> 3u) << 11u)
|
||||
| ((green >> 2u) << 5u)
|
||||
| (blue >> 3u);
|
||||
|
||||
uint16_t endianSwap = ((rgb565 & 0xFFu) << 8u) | (rgb565 >> 8u);
|
||||
|
||||
palette[i] = endianSwap;
|
||||
}
|
||||
|
||||
fclose(inFile);
|
||||
}
|
||||
|
||||
|
||||
printf("Input: %s\n", inFilename);
|
||||
printf("Output: %s\n", outFilename);
|
||||
printf("Palette Size: %d\n", paletteSize);
|
||||
|
||||
|
||||
// Output to the file
|
||||
{
|
||||
FILE* outFile = fopen(outFilename, "w");
|
||||
assert(outFile);
|
||||
|
||||
fprintf(outFile, "// AUTOMATICALLY GENERATED. DO NOT EDIT.\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "#pragma once\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "\n");
|
||||
|
||||
fprintf(outFile, "uint16_t palette[%d] =\n", paletteSize);
|
||||
fprintf(outFile, "{\n");
|
||||
|
||||
for (int i = 0; i < paletteSize; ++i)
|
||||
{
|
||||
fprintf(outFile, " 0x%04X,\n", palette[i]);
|
||||
}
|
||||
|
||||
fprintf(outFile, "};\n");
|
||||
fprintf(outFile, "\n");
|
||||
|
||||
fclose(outFile);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
printf("DONE\n");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <input image> <output header>\n", argv[0]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* inFilename = argv[1];
|
||||
const char* outFilename = argv[2];
|
||||
|
||||
|
||||
int tileWidth;
|
||||
int tileHeight;
|
||||
uint8_t* tileBuffer;
|
||||
{
|
||||
FILE* inFile = fopen(inFilename, "rb");
|
||||
assert(inFile);
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct BmpHeader
|
||||
{
|
||||
char magic[2];
|
||||
uint32_t totalSize;
|
||||
uint32_t reserved;
|
||||
uint32_t offset;
|
||||
uint32_t headerSize;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint16_t planes;
|
||||
uint16_t depth;
|
||||
uint32_t compression;
|
||||
uint32_t imageSize;
|
||||
int32_t horizontalResolution;
|
||||
int32_t verticalResolution;
|
||||
uint32_t paletteColorCount;
|
||||
uint32_t importantColorcount;
|
||||
} bmpHeader;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Read the BMP header so we know where the image data is located
|
||||
fread(&bmpHeader, 1, sizeof(bmpHeader), inFile);
|
||||
assert(bmpHeader.magic[0] == 'B' && bmpHeader.magic[1] == 'M');
|
||||
assert(bmpHeader.depth == 8);
|
||||
assert(bmpHeader.headerSize == 40);
|
||||
|
||||
// Go to location in file of image data
|
||||
fseek(inFile, bmpHeader.offset, SEEK_SET);
|
||||
|
||||
// Read in the image data
|
||||
tileBuffer = malloc(bmpHeader.imageSize);
|
||||
assert(tileBuffer);
|
||||
fread(tileBuffer, 1, bmpHeader.imageSize, inFile);
|
||||
|
||||
tileWidth = bmpHeader.width;
|
||||
tileHeight = bmpHeader.height;
|
||||
|
||||
fclose(inFile);
|
||||
}
|
||||
|
||||
|
||||
printf("Input: %s\n", inFilename);
|
||||
printf("Output: %s\n", outFilename);
|
||||
printf("Width: %d\n", tileWidth);
|
||||
printf("Height: %d\n", tileHeight);
|
||||
|
||||
FILE* outFile = fopen(outFilename, "w");
|
||||
assert(outFile);
|
||||
|
||||
|
||||
// Generate the preamble
|
||||
{
|
||||
fprintf(outFile, "// AUTOMATICALLY GENERATED. DO NOT EDIT.\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "#pragma once\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "#include <stdint.h>\n");
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, "\n");
|
||||
}
|
||||
|
||||
|
||||
// Generate the font map with the calculated glyph bytes
|
||||
{
|
||||
fprintf(outFile, "static const uint8_t tile[%d][%d] =\n", tileHeight, tileWidth);
|
||||
fprintf(outFile, "{\n");
|
||||
fprintf(outFile, " ");
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (int row = 0; row < tileHeight; ++row)
|
||||
{
|
||||
for (int col = 0; col < tileWidth; ++col)
|
||||
{
|
||||
// BMP is laid out bottom-to-top, but we want top-to-bottom (0-indexed)
|
||||
int y = tileHeight - row - 1;
|
||||
|
||||
uint8_t paletteIndex = tileBuffer[y * tileWidth + col];
|
||||
|
||||
fprintf(outFile, "%d,", paletteIndex);
|
||||
++count;
|
||||
|
||||
// Put a newline after sixteen values to keep it orderly
|
||||
if ((count % 16) == 0)
|
||||
{
|
||||
fprintf(outFile, "\n");
|
||||
fprintf(outFile, " ");
|
||||
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(outFile, "};\n");
|
||||
}
|
||||
|
||||
fclose(outFile);
|
||||
|
||||
printf("\n");
|
||||
printf("DONE\n");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue