commit
b4f9b87ce0
@ -0,0 +1,3 @@ |
||||
build |
||||
cmake-build-* |
||||
.idea/ |
@ -0,0 +1,20 @@ |
||||
cmake_minimum_required(VERSION 3.13) |
||||
include(pico_sdk_import.cmake) |
||||
project(test_project C CXX ASM) |
||||
set(CMAKE_C_STANDARD 11) |
||||
set(CMAKE_CXX_STANDARD 17) |
||||
pico_sdk_init() |
||||
|
||||
add_executable(test |
||||
main.c |
||||
ufb/framebuffer.c ufb/utf8.c ufb/font.c ufb/fb_7seg.c ufb/fb_text.c |
||||
) |
||||
|
||||
|
||||
target_include_directories(test PRIVATE ufb) |
||||
|
||||
pico_enable_stdio_usb(test 1) |
||||
#pico_enable_stdio_uart(test 1) |
||||
|
||||
pico_add_extra_outputs(test) |
||||
target_link_libraries(test pico_stdlib hardware_spi) |
@ -0,0 +1,276 @@ |
||||
#include <stdio.h> |
||||
#include "pico/stdlib.h" |
||||
#include "hardware/gpio.h" |
||||
#include "pico/binary_info.h" |
||||
|
||||
#include "hardware/spi.h" |
||||
|
||||
#include "framebuffer.h" |
||||
#include "fb_text.h" |
||||
|
||||
const uint LED_PIN = 25; |
||||
const uint DC_PIN = 20; |
||||
const uint RST_PIN = 21; |
||||
|
||||
#define SSD1309_HEIGHT 64 |
||||
|
||||
|
||||
|
||||
/* Picotool info */ |
||||
// Make the SPI pins available to picotool
|
||||
bi_decl(bi_2pins_with_func(PICO_DEFAULT_SPI_TX_PIN, PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI)) |
||||
bi_decl(bi_1pin_with_name(PICO_DEFAULT_SPI_CSN_PIN, "SPI CS")) |
||||
bi_decl(bi_1pin_with_name(DC_PIN, "OLED DC")) |
||||
bi_decl(bi_1pin_with_name(RST_PIN, "OLED RST")) |
||||
bi_decl(bi_1pin_with_name(LED_PIN, "Blink LED")) |
||||
|
||||
static inline void cs_select() { |
||||
asm volatile("nop \n nop \n nop"); |
||||
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 0); // Active low
|
||||
asm volatile("nop \n nop \n nop"); |
||||
} |
||||
|
||||
static inline void cs_deselect() { |
||||
asm volatile("nop \n nop \n nop"); |
||||
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1); |
||||
asm volatile("nop \n nop \n nop"); |
||||
} |
||||
|
||||
static inline void dc_command() { |
||||
gpio_put(DC_PIN, 0); |
||||
} |
||||
|
||||
static inline void dc_data() { |
||||
gpio_put(DC_PIN, 1); |
||||
} |
||||
|
||||
static void oled_command16(uint8_t cmd, uint8_t arg) { |
||||
uint8_t buf[2]; |
||||
buf[0] = cmd; |
||||
buf[1] = arg; |
||||
dc_command(); |
||||
cs_select(); |
||||
spi_write_blocking(spi_default, buf, 2); |
||||
cs_deselect(); |
||||
} |
||||
|
||||
static void oled_command(uint8_t cmd) { |
||||
dc_command(); |
||||
cs_select(); |
||||
spi_write_blocking(spi_default, &cmd, 1); |
||||
cs_deselect(); |
||||
} |
||||
|
||||
static void oled_reset() { |
||||
// Issue a display reset
|
||||
gpio_put(RST_PIN, 0); |
||||
sleep_ms(1); |
||||
gpio_put(RST_PIN, 1); |
||||
sleep_ms(1); |
||||
} |
||||
|
||||
int main() { |
||||
bi_decl(bi_program_description("This is a test binary.")); |
||||
bi_decl(bi_1pin_with_name(LED_PIN, "On-board LED")); |
||||
|
||||
|
||||
|
||||
stdio_init_all(); |
||||
|
||||
gpio_init(LED_PIN); |
||||
gpio_set_dir(LED_PIN, GPIO_OUT); |
||||
|
||||
|
||||
// This example will use SPI0 at 10MHz.
|
||||
spi_init(spi_default, 10 * 1000 * 1000); |
||||
gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI); |
||||
gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI); |
||||
|
||||
// Chip select is active-low, so we'll initialise it to a driven-high state
|
||||
gpio_init(PICO_DEFAULT_SPI_CSN_PIN); |
||||
gpio_set_dir(PICO_DEFAULT_SPI_CSN_PIN, GPIO_OUT); |
||||
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1); |
||||
|
||||
gpio_init(DC_PIN); |
||||
gpio_set_dir(DC_PIN, GPIO_OUT); |
||||
|
||||
gpio_init(RST_PIN); |
||||
gpio_set_dir(RST_PIN, GPIO_OUT); |
||||
|
||||
oled_reset(); |
||||
|
||||
// simplified display init
|
||||
|
||||
oled_command(0xAE); /* Display off */ |
||||
//oled_command16(0xD5, 0xF0); // set clock speed
|
||||
|
||||
oled_command16(0x20, 0b00); // page addressing mode: 00 - horizontal, 01 vertical, 10 page
|
||||
|
||||
oled_command16(0x81, 0xFF); |
||||
|
||||
//oled_command16(0xD9, 0xff);
|
||||
|
||||
oled_command(0xAF); /*--turn on SSD1309 panel */ |
||||
|
||||
#if 0 |
||||
|
||||
/* Init OLED */ |
||||
oled_command(0xAE); /* Display off */ |
||||
|
||||
// Configure the display - only setting things that differ from the default:
|
||||
|
||||
|
||||
oled_command16(0x20, 0b10); /* Set Memory Addressing Mode */ |
||||
/* 00,Horizontal Addressing Mode; 01,Vertical Addressing Mode; */ |
||||
/* 10,Page Addressing Mode (RESET); 11,Invalid */ |
||||
|
||||
oled_command(0xB0); /*Set Page Start Address for Page Addressing Mode, B0-B7 */ |
||||
|
||||
#ifdef SSD1309_MIRROR_VERT |
||||
oled_command(0xC1); |
||||
#else |
||||
oled_command(0xC0); |
||||
#endif |
||||
|
||||
// lower nibble is value
|
||||
oled_command(0x00); /*---set low column address 00~0F */ |
||||
|
||||
oled_command(0x10); /*---set high column address 10~1F */ |
||||
|
||||
oled_command(0x40); /*--set start line address - 40~7F */ |
||||
|
||||
oled_command16(0x81, 0x7F); /*--set contrast control register - ? 0xFF */ |
||||
|
||||
#ifdef SSD1309_MIRROR_HORIZ |
||||
oled_command(0xA1); |
||||
#else |
||||
oled_command(0xA0); |
||||
#endif |
||||
|
||||
#ifdef SSD1309_INVERSE_COLOR |
||||
oled_command(0xA7); /*--set inverse color */ |
||||
#else |
||||
oled_command(0xA6); /*--set normal color */ |
||||
#endif |
||||
|
||||
/*--set multiplex ratio(1 to 64) */ |
||||
#if (SSD1309_HEIGHT == 32) |
||||
oled_command16(0xA8, 0x1F); |
||||
#elif (SSD1309_HEIGHT == 64) |
||||
oled_command16(0xA8, 0x3F); |
||||
#else |
||||
#error "bad height" |
||||
#endif |
||||
|
||||
oled_command(0xA4); /* 0xA4, Output follows RAM content;0xa5,Output ignores RAM content */ |
||||
|
||||
oled_command16(0xD3, 0x00); /*-set display offset */ |
||||
|
||||
oled_command16(0xD5, 0xF0); /*--set display clock divide ratio/oscillator frequency */ |
||||
|
||||
oled_command16(0xD9, 0x22); /*--set pre-charge period */ |
||||
|
||||
/*--set com pins hardware configuration */ |
||||
#if (SSD1309_HEIGHT == 32) |
||||
oled_command16(0xDA, 0x02); |
||||
#elif (SSD1309_HEIGHT == 64) |
||||
oled_command16(0xDA, 0x12); |
||||
#else |
||||
#error "bad height" |
||||
#endif |
||||
|
||||
oled_command16(0xDB, 0x34); /*--set vcomh */ |
||||
|
||||
// ??? not listed in the datasheet
|
||||
// oled_command(0x8D); /*--set DC-DC enable */
|
||||
// oled_command(0x14); /* */
|
||||
|
||||
oled_command(0xAF); /*--turn on SSD1309 panel */ |
||||
|
||||
|
||||
oled_command(0xA5); // entire display on - for test
|
||||
|
||||
#endif |
||||
|
||||
|
||||
#define ZIR_W 12 |
||||
#define ZIR_H 24 |
||||
const uint8_t zirafa[(ZIR_H/8) * ZIR_W] = { |
||||
// levo
|
||||
0b10000000, |
||||
0b01000000, |
||||
0b00100000, |
||||
0b00010000, |
||||
0b00001000, |
||||
0b00000100, |
||||
0b00000010, |
||||
0b00011001, |
||||
0b00011111, |
||||
0b11111000, |
||||
0b00011111, |
||||
0b00001000, |
||||
//
|
||||
0b10000000, |
||||
0b01000000, |
||||
0b00100000, |
||||
0b00010000, |
||||
0b00001000, |
||||
0b00000100, |
||||
0b00000010, |
||||
0b00000001, |
||||
0b10000000, |
||||
0b11111111, |
||||
0b00100000, |
||||
0b00010000, |
||||
//
|
||||
0b00110000, |
||||
0b10001000, |
||||
0b11111000, |
||||
0b00011000, |
||||
0b11111000, |
||||
0b10011000, |
||||
0b00011000, |
||||
0b10011000, |
||||
0b11111000, |
||||
0b00011111, |
||||
0b11111000, |
||||
0b10000000, |
||||
}; |
||||
|
||||
fb_clear(); |
||||
fb_bitmap(5, 5, ZIR_W, ZIR_H, zirafa, 1); |
||||
fb_text(40, 23, "MEOW", FONT_DOUBLE, 1); |
||||
fb_text(50, 35, "meow", FONT_DOUBLE, 1); |
||||
fb_frame(0, 0, 128, 64, 2, 1); |
||||
fb_blit(); |
||||
|
||||
// dc_data();
|
||||
// cs_select();
|
||||
// uint8_t pattern = 0;
|
||||
// for (uint i = 0; i < 128 * 8; i++) {
|
||||
// pattern = i & 0xFF;
|
||||
// spi_write_blocking(spi_default, &pattern, 1);
|
||||
// }
|
||||
// cs_deselect();
|
||||
|
||||
while (1) { |
||||
oled_command(0xA6); // invert
|
||||
|
||||
gpio_put(LED_PIN, 0); |
||||
sleep_ms(1000); |
||||
|
||||
oled_command(0xA7); // uninvert
|
||||
|
||||
gpio_put(LED_PIN, 1); |
||||
|
||||
//puts("Hello World\n");
|
||||
sleep_ms(1000); |
||||
} |
||||
} |
||||
|
||||
void fb_blit() { |
||||
dc_data(); |
||||
cs_select(); |
||||
spi_write_blocking(spi_default, fb, FB_LEN); |
||||
cs_deselect(); |
||||
} |
@ -0,0 +1,73 @@ |
||||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake |
||||
|
||||
# This can be dropped into an external project to help locate this SDK |
||||
# It should be include()ed prior to project() |
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) |
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) |
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") |
||||
endif () |
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) |
||||
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) |
||||
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") |
||||
endif () |
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) |
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) |
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") |
||||
endif () |
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") |
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") |
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") |
||||
|
||||
if (NOT PICO_SDK_PATH) |
||||
if (PICO_SDK_FETCH_FROM_GIT) |
||||
include(FetchContent) |
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) |
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH) |
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") |
||||
endif () |
||||
# GIT_SUBMODULES_RECURSE was added in 3.17 |
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") |
||||
FetchContent_Declare( |
||||
pico_sdk |
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk |
||||
GIT_TAG master |
||||
GIT_SUBMODULES_RECURSE FALSE |
||||
) |
||||
else () |
||||
FetchContent_Declare( |
||||
pico_sdk |
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk |
||||
GIT_TAG master |
||||
) |
||||
endif () |
||||
|
||||
if (NOT pico_sdk) |
||||
message("Downloading Raspberry Pi Pico SDK") |
||||
FetchContent_Populate(pico_sdk) |
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) |
||||
endif () |
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) |
||||
else () |
||||
message(FATAL_ERROR |
||||
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." |
||||
) |
||||
endif () |
||||
endif () |
||||
|
||||
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") |
||||
if (NOT EXISTS ${PICO_SDK_PATH}) |
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") |
||||
endif () |
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) |
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) |
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") |
||||
endif () |
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) |
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE}) |
@ -0,0 +1,4 @@ |
||||
*.o |
||||
ufb-test |
||||
.idea/ |
||||
|
@ -0,0 +1,17 @@ |
||||
.PHONY: build run fonts |
||||
|
||||
all: fonts ufb-test |
||||
|
||||
fonts: fontedit_35.c fontedit_45.c fontedit_57.c |
||||
tcc -run fontedit_35.c > font_35.inc.c || true
|
||||
tcc -run fontedit_45.c > font_45.inc.c || true
|
||||
tcc -run fontedit_57.c > font_57.inc.c || true
|
||||
|
||||
run: ufb-test |
||||
./ufb-test
|
||||
|
||||
clean: |
||||
rm -f ufb-test
|
||||
|
||||
ufb-test: main.c framebuffer.c framebuffer.h framebuffer_config.h utf8.c utf8.h progmem.h font.c font.h fb_7seg.c fb_7seg.h fb_text.c fb_text.h |
||||
$(CC) -o $@ $^ -I.
|
@ -0,0 +1,72 @@ |
||||
#include "fb_7seg.h" |
||||
#include "progmem.h" |
||||
|
||||
enum SevenSegBars { |
||||
T = 1, RT = 2, RB = 4, B = 8, LB = 16, LT = 32, M = 64 |
||||
}; |
||||
|
||||
static const uint8_t PROGMEM seven[] = { |
||||
[0] = T | RT | RB | B | LB | LT, |
||||
[1] = RT | RB, |
||||
[2] = T | RT | M | LB | B, |
||||
[3] = T | RT | M | RB | B, |
||||
[4] = RT | RB | M | LT, |
||||
[5] = T | LT | M | RB | B, |
||||
[6] = T | LT | LB | B | RB | M, |
||||
[7] = T | RT | RB, |
||||
[8] = T | LT | RT | LB | RB | B | M, |
||||
[9] = T | LT | RT | RB | B | M, |
||||
}; |
||||
|
||||
fbpos_t fb_7seg_dig(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, uint8_t digit, fbcolor_t color) |
||||
{ |
||||
const uint8_t mask = digit > 9 ? 0 : pgm_read_byte(&seven[digit]); |
||||
const fbpos_t wi = w - th * 2; |
||||
const fbpos_t hi = (h - th * 3) / 2; |
||||
|
||||
bool bcolor = !color; // changed for XOR
|
||||
|
||||
fb_rect(x + th, |
||||
y, |
||||
wi, |
||||
th, bcolor ^ (bool) (mask & T)); |
||||
|
||||
fb_rect(x + th, |
||||
y + th + hi, |
||||
wi, |
||||
th, bcolor ^ (bool) (mask & M)); |
||||
|
||||
fb_rect(x + th, |
||||
y + th * 2 + hi * 2, |
||||
wi, |
||||
th, bcolor ^ (bool) (mask & B)); |
||||
|
||||
fb_rect(x, |
||||
y + th, |
||||
th, |
||||
hi, bcolor ^ (bool) (mask & LT)); |
||||
|
||||
fb_rect(x + th + wi, |
||||
y + hi + th, |
||||
th, |
||||
hi, bcolor ^ (bool) (mask & LB)); |
||||
|
||||
fb_rect(x + th + wi, |
||||
y + th, |
||||
th, |
||||
hi, bcolor ^ (bool) (mask & RT)); |
||||
|
||||
fb_rect(x + th + wi, |
||||
y + th * 2 + hi, |
||||
th, |
||||
hi, bcolor ^ (bool) (mask & RB)); |
||||
|
||||
return w; |
||||
} |
||||
|
||||
fbpos_t fb_7seg_period(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, fbcolor_t color) |
||||
{ |
||||
const fbpos_t hi = (h - th * 3) / 2; |
||||
fb_rect(x, y + hi * 2 + th * 2, th, th, color); |
||||
return th; |
||||
} |
@ -0,0 +1,32 @@ |
||||
/**
|
||||
* Draw 7-seg digits to the framebuffer |
||||
*/ |
||||
|
||||
#ifndef FB_7SEG_H |
||||
#define FB_7SEG_H |
||||
|
||||
#include "framebuffer.h" |
||||
|
||||
/// Draw a 7-segment digit. Returns its width (without spacing)
|
||||
///
|
||||
/// \param x - pos X (left top)
|
||||
/// \param y - pos Y (left top)
|
||||
/// \param w - full digit width
|
||||
/// \param h - full digit height; will be adjusted down if needed
|
||||
/// \param th - thickness
|
||||
/// \param digit - digit 0-9
|
||||
/// \return width taken
|
||||
fbpos_t fb_7seg_dig(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, uint8_t digit, fbcolor_t color); |
||||
|
||||
/// Draw a 7-segment period. Returns its width (without spacing).
|
||||
/// Digit height is (w * 2 - th)
|
||||
///
|
||||
/// \param x - pos X (digit left top)
|
||||
/// \param y - pos Y (digit left top)
|
||||
/// \param w - full digit width
|
||||
/// \param h - full digit height; will be adjusted down if needed
|
||||
/// \param th - thickness
|
||||
/// \return width taken
|
||||
fbpos_t fb_7seg_period(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, fbcolor_t color); |
||||
|
||||
#endif //FB_7SEG_H
|
@ -0,0 +1,96 @@ |
||||
/**
|
||||
* TODO file description |
||||
*/ |
||||
|
||||
#include "fb_text.h" |
||||
#include "framebuffer.h" |
||||
#include "utf8.h" |
||||
#include "font.h" |
||||
|
||||
void fb_text_P_or_RAM(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color, bool is_pgmem) { |
||||
struct Utf8Iterator iter; |
||||
Utf8Iterator_Init(&iter, text); |
||||
iter.is_progmem = is_pgmem; |
||||
|
||||
struct Utf8Char uchar; |
||||
|
||||
uint8_t symw; |
||||
uint8_t symh; |
||||
if (flags & FONT_4X5) { |
||||
symw = 4; |
||||
symh = 5; |
||||
} else if (flags & FONT_3X5) { |
||||
symw = 3; |
||||
symh = 5; |
||||
} else { |
||||
symw = 5; |
||||
symh = 7; |
||||
} |
||||
|
||||
while ((uchar = Utf8Iterator_Next(&iter)).uint) { |
||||
const uint8_t *data; |
||||
if (flags & FONT_4X5) { |
||||
const font4x_bitmap_t *sym = font45_getsym(&uchar); |
||||
data = sym->data; |
||||
} else if (flags & FONT_3X5) { |
||||
const font3x_bitmap_t *sym = font35_getsym(&uchar); |
||||
data = sym->data; |
||||
} else{ |
||||
const font5x_bitmap_t *sym = font57_getsym(&uchar); |
||||
data = sym->data; |
||||
} |
||||
|
||||
if (0 == (flags & FONT_DOUBLE)) { |
||||
// no double, using normal format
|
||||
|
||||
#if IS_AVR |
||||
fb_bitmap_P(x, y, symw, symh, data, color); |
||||
#else |
||||
fb_bitmap(x, y, symw, symh, data, color); |
||||
#endif |
||||
|
||||
if (flags & FONT_BOLD) { |
||||
x++; |
||||
|
||||
#if IS_AVR |
||||
fb_bitmap_P(x, y, symw, symh, data, color); |
||||
#else |
||||
fb_bitmap(x, y, symw, symh, data, color); |
||||
#endif |
||||
|
||||
x += symw+2; |
||||
} else { |
||||
x += symw+1; |
||||
} |
||||
} else { |
||||
// slow, pixel-by-pixel drawing
|
||||
const uint8_t pxw = (flags & FONT_DOUBLEW) ? 2 : 1; |
||||
const uint8_t pxh = (flags & FONT_DOUBLEH) ? 2 : 1; |
||||
for(fbpos_t xx = 0; xx < symw; xx++) { |
||||
uint8_t column = pgm_read_byte(&data[xx]); |
||||
for(fbpos_t yy = 0; yy < symh; yy++) { |
||||
if (column & (1 << yy)) { |
||||
fb_rect(x + xx * pxw, y + yy * pxh, pxw, pxh, color); |
||||
if (flags & FONT_BOLD) { |
||||
fb_rect(x + (xx + 1) * pxw, y + yy * pxh, pxw, pxh, color); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
if (flags & FONT_BOLD) { |
||||
x += (symw+2) * pxw; |
||||
} else { |
||||
x += (symw+1) * pxw; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void fb_text_P(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color) |
||||
{ |
||||
fb_text_P_or_RAM(x, y, text, flags, color, true); |
||||
} |
||||
|
||||
void fb_text(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color) { |
||||
fb_text_P_or_RAM(x, y, text, flags, color, false); |
||||
} |
@ -0,0 +1,29 @@ |
||||
/**
|
||||
* Draw text |
||||
*/ |
||||
|
||||
#ifndef UFB_FB_TEXT_H |
||||
#define UFB_FB_TEXT_H |
||||
|
||||
#include "framebuffer.h" |
||||
|
||||
/// Use tiny font 5x4, supports only digits and select symbols
|
||||
#define FONT_4X5 8 |
||||
#define FONT_3X5 16 |
||||
#define FONT_5X7 0 // default font
|
||||
/// Use bold variant (each character is printed twice, 1px offset)
|
||||
#define FONT_BOLD 1 |
||||
/// Print characters 2x wider
|
||||
#define FONT_DOUBLEW 2 |
||||
/// Print characters 2x taller
|
||||
#define FONT_DOUBLEH 4 |
||||
/// Print characters 2x wider and taller
|
||||
#define FONT_DOUBLE (FONT_DOUBLEW | FONT_DOUBLEH) |
||||
|
||||
/// Print text stored in flash
|
||||
void fb_text_P(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color); |
||||
|
||||
/// Print text stored in RAM
|
||||
void fb_text(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color); |
||||
|
||||
#endif //UFB_FB_TEXT_H
|
@ -0,0 +1,81 @@ |
||||
#include "font.h" |
||||
#include "utf8.h" |
||||
#include "progmem.h" |
||||
#include <stdint.h> |
||||
|
||||
union utf_lookup { |
||||
const char symbol[4]; |
||||
/// symbol as uint, but not decoded - just for matching!
|
||||
const uint32_t uint; |
||||
}; |
||||
|
||||
struct utf_glyph5x { |
||||
union utf_lookup utf; |
||||
font5x_bitmap_t graphic; |
||||
}; |
||||
|
||||
struct utf_glyph4x { |
||||
union utf_lookup utf; |
||||
font4x_bitmap_t graphic; |
||||
}; |
||||
|
||||
struct utf_glyph3x { |
||||
union utf_lookup utf; |
||||
font3x_bitmap_t graphic; |
||||
}; |
||||
|
||||
|
||||
#include "font_35.inc.c" |
||||
#include "font_45.inc.c" |
||||
#include "font_57.inc.c" |
||||
|
||||
|
||||
#define ASCII_START 0x20 |
||||
#define ASCII_END 0x7e |
||||
|
||||
#define FONT_GETSYM_IMPL(rettype, symtype, fontname) \ |
||||
const rettype *fontname##_getsym(const struct Utf8Char *ch) { \
|
||||
const uint8_t byte0 = ch->bytes[0]; \
|
||||
if (byte0 < ASCII_START) { \
|
||||
goto fail; \
|
||||
} \
|
||||
if (byte0 <= ASCII_END) { \
|
||||
return &fontname##_ascii[byte0 - ASCII_START]; \
|
||||
} \
|
||||
for (uint8_t i = 0; i < sizeof(fontname##_extra) / sizeof(fontname##_extra[0]); i++) { \
|
||||
const struct symtype *sym = &fontname##_extra[i]; \
|
||||
const uint32_t table_ch = pgm_read_dword(&sym->utf.uint); \
|
||||
if (table_ch == ch->uint) { \
|
||||
return &sym->graphic; \
|
||||
} \
|
||||
} \
|
||||
fail: return &fontname ## _extra[0].graphic; \
|
||||
} |
||||
|
||||
|
||||
FONT_GETSYM_IMPL(font5x_bitmap_t, utf_glyph5x, font57) |
||||
FONT_GETSYM_IMPL(font4x_bitmap_t, utf_glyph4x, font45) |
||||
FONT_GETSYM_IMPL(font3x_bitmap_t, utf_glyph3x, font35) |
||||
|
||||
|
||||
#if 0 |
||||
const font5x_bitmap_t *font57_getsym(const struct Utf8Char *ch) |
||||
{ |
||||
const uint8_t byte0 = ch->bytes[0]; |
||||
if (byte0 < ASCII_START) { |
||||
goto fail; |
||||
} |
||||
if (byte0 <= ASCII_END) { |
||||
return &font57_ascii[byte0 - ASCII_START]; |
||||
} |
||||
for (uint8_t i = 0; i < sizeof(font57_extra) / sizeof(font57_extra[0]); i++) { |
||||
const struct utf_glyph5x *sym = &font57_extra[i]; |
||||
const uint32_t table_ch = pgm_read_dword(&sym->uint); |
||||
if (table_ch == ch->uint) { |
||||
return &sym->graphic; |
||||
} |
||||
} |
||||
fail: |
||||
return &font57_extra[0].graphic; // replacement character
|
||||
} |
||||
#endif |
@ -0,0 +1,35 @@ |
||||
/**
|
||||
* UTF-8 capable bitmap font |
||||
* |
||||
* Created on 2020/01/04. |
||||
*/ |
||||
|
||||
#ifndef GFX_FONT_H |
||||
#define GFX_FONT_H |
||||
|
||||
#include "font.h" |
||||
#include "utf8.h" |
||||
#include <stdint.h> |
||||
|
||||
typedef struct { |
||||
uint8_t data[5]; |
||||
} font5x_bitmap_t; |
||||
|
||||
typedef struct { |
||||
uint8_t data[4]; |
||||
} font4x_bitmap_t; |
||||
|
||||
typedef struct { |
||||
uint8_t data[3]; |
||||
} font3x_bitmap_t; |
||||
|
||||
/// Get font graphic for a character.
|
||||
///
|
||||
/// The returned pointer is PROGMEM!
|
||||
const font5x_bitmap_t *font57_getsym(const struct Utf8Char *ch); |
||||
|
||||
const font4x_bitmap_t *font45_getsym(const struct Utf8Char *ch); |
||||
|
||||
const font3x_bitmap_t *font35_getsym(const struct Utf8Char *ch); |
||||
|
||||
#endif //GFX_FONT_H
|
@ -0,0 +1,14 @@ |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
|
||||
void main() { |
||||
printf("const char* symbols[] = {\n"); |
||||
for(int c = 32; c < 127; c++) { |
||||
printf(" // %d \"%c\"\n", c, c); |
||||
for(int j=0;j<5;j++){ |
||||
printf(" \" \",\n"); |
||||
} |
||||
} |
||||
printf("};\n"); |
||||
} |
@ -0,0 +1,48 @@ |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
|
||||
void generate_glyph_tables() { |
||||
printf("static const font%dx_bitmap_t PROGMEM font%d%d_ascii[] = {\n", FONT_W, FONT_W, FONT_H); |
||||
for (int i = 32; i < 127; i++) { |
||||
uint8_t line[8] = {}; |
||||
for(int j = 0; j < FONT_H; j++) { |
||||
const char *row = font_ascii[(i - 32)*FONT_H + j]; |
||||
for(int k = 0; k < FONT_W; k++) { |
||||
line[k] |= (row[k] != 32) << j; |
||||
} |
||||
} |
||||
|
||||
printf(" {{"); |
||||
for(int k = 0; k < FONT_W; k++) { |
||||
if (k > 0) printf(", "); |
||||
printf("0x%02x", line[k]); |
||||
} |
||||
printf("}},\n"); |
||||
} |
||||
printf("};\n\n"); |
||||
|
||||
printf("static const struct utf_glyph%dx PROGMEM font%d%d_extra[] = {\n", FONT_W, FONT_W, FONT_H); |
||||
for (int i = 0; i < sizeof(font_extras) / (sizeof(font_extras[0]) * FONT_H); i++) { |
||||
uint8_t line[8] = {}; |
||||
for(int j = 0; j < FONT_H; j++) { |
||||
const char *row = font_extras[i*FONT_H + j]; |
||||
for(int k = 0; k < FONT_W; k++) { |
||||
line[k] |= (row[k] != 32) << j; |
||||
} |
||||
} |
||||
|
||||
printf(" {.utf={.symbol=\"%s\"}, {{", font_extras_utf[i]); |
||||
for(int k = 0; k < FONT_W; k++) { |
||||
if (k > 0) printf(", "); |
||||
printf("0x%02x", line[k]); |
||||
} |
||||
printf("}}},\n"); |
||||
} |
||||
printf("};\n"); |
||||
} |
||||
|
||||
|
||||
void main() { |
||||
generate_glyph_tables(); |
||||
} |
@ -0,0 +1,282 @@ |
||||
//
|
||||
// Created by MightyPork on 2022/11/12.
|
||||
//
|
||||
|
||||
#include "framebuffer.h" |
||||
#include "progmem.h" |
||||
|
||||
#define MIN(a, b) ((a)>(b)?(b):(a)) |
||||
#define MAX(a, b) ((a)>(b)?(a):(b)) |
||||
|
||||
#include <string.h> |
||||
|
||||
uint8_t fb[FB_LEN]; |
||||
|
||||
/** Fill with a vertical pattern, 1 byte */ |
||||
void fb_fill(uint8_t pattern) |
||||
{ |
||||
memset(fb, pattern, sizeof(fb)); |
||||
} |
||||
|
||||
static void draw_mask(fbsize_t idx, uint8_t mask, fbcolor_t color) |
||||
{ |
||||
if (color != 0) { |
||||
fb[idx] |= mask; |
||||
} else { |
||||
fb[idx] &= ~mask; |
||||
} |
||||
} |
||||
|
||||
void fb_fill_pattern(const uint8_t *pattern, uint8_t pattern_len, fbcolor_t color) |
||||
{ |
||||
uint8_t p = 0; |
||||
for (fbsize_t i = 0; i < FB_LEN; i++) { |
||||
draw_mask(i, pattern[p], color); |
||||
p++; |
||||
if (p >= pattern_len) { |
||||
p = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void fb_invert() |
||||
{ |
||||
for (fbsize_t i = 0; i < FB_LEN; i++) { |
||||
fb[i] ^= 0xFF; |
||||
} |
||||
} |
||||
|
||||
void fb_px(fbpos_t x, fbpos_t y, fbcolor_t color) |
||||
{ |
||||
if (x >= FBW || y >= FBH) { return; } |
||||
const fbpos_t row = y / 8; |
||||
const fbpos_t rowrem = y % 8; |
||||
const fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW; |
||||
draw_mask(cell, 1 << rowrem, color); |
||||
} |
||||
|
||||
uint8_t fb_getpx(fbpos_t x, fbpos_t y) |
||||
{ |
||||
if (x >= FBW || y >= FBH) { return 0; } |
||||
const fbpos_t row = y / 8; |
||||
const fbpos_t rowrem = y % 8; |
||||
const fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW; |
||||
if (fb[cell] & (1 << rowrem)) { |
||||
return 0xFF; |
||||
} else { |
||||
return 0x00; |
||||
} |
||||
} |
||||
|
||||
void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, fbcolor_t color) |
||||
{ |
||||
if (x >= FBW || y >= FBH) { return; } |
||||
w = MIN(FBW - x, w); |
||||
const fbpos_t row = y / 8; |
||||
const fbpos_t rowrem = y % 8; |
||||
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW; |
||||
for (uint8_t i = 0; i < w; i++) { |
||||
draw_mask(cell, 1 << rowrem, color); |
||||
cell++; |
||||
} |
||||
} |
||||
|
||||
void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, fbcolor_t color) |
||||
{ |
||||
if (x >= FBW || y >= FBH) { return; } |
||||
h = MIN(FBH - y - 1, h); |
||||
const fbpos_t row = y / 8; |
||||
const fbpos_t rowrem = y % 8; |
||||
uint16_t cell = (uint16_t) x + (uint16_t) row * FBW; |
||||
|
||||
if (rowrem + h < 8) { |
||||
// all within one cell
|
||||
const uint8_t mask = (0xFF << rowrem) & (0xFF >> (8 - h - rowrem)); |
||||
draw_mask(cell, mask, color); |
||||
return; |
||||
} else { |
||||
// First
|
||||
draw_mask(cell, 0xFF << rowrem, color); |
||||
h -= (rowrem == 0 ? 8 : (8 - rowrem)); |
||||
|
||||
const fbpos_t whole_cells = h / 8; |
||||
if (whole_cells > 0) { |
||||
h -= whole_cells * 8; |
||||
for (fbpos_t i = 0; i < whole_cells; i++) { |
||||
cell += FBW; |
||||
draw_mask(cell, 0xFF, color); |
||||
} |
||||
} |
||||
|
||||
// last
|
||||
cell += FBW; |
||||
draw_mask(cell, 0xFF >> (8 - h), color); |
||||
} |
||||
} |
||||
|
||||
void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbcolor_t color) |
||||
{ |
||||
if (x >= FBW || y >= FBH) { return; } |
||||
w = MIN(FBW - x, w); |
||||
h = MIN(FBH - y, h); |
||||
const fbpos_t row = y / 8; |
||||
const fbpos_t rowrem = y % 8; |
||||
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW; |
||||
|
||||
if (w == 1 && h == 1) { |
||||
fb_px(x, y, color); |
||||
} else if (w == 1) { |
||||
fb_vline(x, y, h, color); |
||||
} else if (h == 1) { |
||||
fb_hline(x, y, w, color); |
||||
} else if (rowrem + h <= 8) { |
||||
// all within one cell
|
||||
uint8_t mask = (0xFF << rowrem) & (0xFF >> (8 - h - rowrem)); |
||||
|
||||
for (fbpos_t i = 0; i < w; i++) { |
||||
draw_mask(cell + i, mask, color); |
||||
} |
||||
return; |
||||
} else { |
||||
// First
|
||||
uint8_t mask = (0xFF << rowrem); |
||||
for (fbpos_t i = 0; i < w; i++) { |
||||
draw_mask(cell + i, mask, color); |
||||
} |
||||
h -= 8 - rowrem; |
||||
|
||||
const fbpos_t whole_cells = h / 8; |
||||
if (whole_cells > 0) { |
||||
h -= whole_cells * 8; |
||||
for (fbpos_t j = 0; j < whole_cells; j++) { |
||||
cell += FBW; |
||||
for (fbpos_t i = 0; i < w; i++) { |
||||
draw_mask(cell + i, 0xFF, color); |
||||
} |
||||
} |
||||
} |
||||
|
||||
cell += FBW; |
||||
|
||||
// last
|
||||
mask = (0xFF >> (8 - h)); |
||||
for (fbpos_t i = 0; i < w; i++) { |
||||
draw_mask(cell + i, mask, color); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, fbcolor_t color) |
||||
{ |
||||
if (thickness == 0) { |
||||
return; |
||||
} else if (thickness == 1) { |
||||
fb_hline(x, y, w, color); |
||||
fb_vline(x, y, h, color); |
||||
fb_hline(x, y + h - 1, w, color); |
||||
fb_vline(x + w - 1, y, h, color); |
||||
} else { |
||||
fb_rect(x, y, w, thickness, color); |
||||
fb_rect(x, y + h - thickness, w, thickness, color); |
||||
fb_rect(x, y + thickness, thickness, h - 2 * thickness, color); |
||||
fb_rect(x + w - thickness, y + thickness, thickness, h - 2 * thickness, color); |
||||
} |
||||
} |
||||
|
||||
// setCircle draws a circle centered around x0,y0 with a defined
|
||||
// radius. The circle can be black or white. And have a line
|
||||
// thickness ranging from 1 to the radius of the circle.
|
||||
// This function was grabbed from the SparkFun ColorLCDShield
|
||||
// library.
|
||||
void fb_circle(fbpos_t x0, fbpos_t y0, fbpos_t radius, uint8_t thickness, fbcolor_t color) |
||||
{ |
||||
for (uint8_t r = 0; r < thickness; r++) { |
||||
int8_t f = 1 - radius; |
||||
fbpos_t ddF_x = 0; |
||||
int8_t ddF_y = -2 * radius; |
||||
fbpos_t x = 0; |
||||
fbpos_t y = radius; |
||||
|
||||
fb_px(x0, y0 + radius, color); |
||||
fb_px(x0 + radius, y0, color); |
||||
if (y0 >= radius) { |
||||
fb_px(x0, y0 - radius, color); |
||||
} |
||||
if (x0 >= radius) { |
||||
fb_px(x0 - radius, y0, color); |
||||
} |
||||
|
||||
while (x < y) { |
||||
if (f >= 0) { |
||||
y--; |
||||
ddF_y += 2; |
||||
f += ddF_y; |
||||
} |
||||
x++; |
||||
ddF_x += 2; |
||||
f += ddF_x + 1; |
||||
|
||||
fb_px(x0 + x, y0 + y, color); |
||||
fb_px(x0 - x, y0 + y, color); |
||||
fb_px(x0 + x, y0 - y, color); |
||||
fb_px(x0 - x, y0 - y, color); |
||||
fb_px(x0 + y, y0 + x, color); |
||||
fb_px(x0 - y, y0 + x, color); |
||||
fb_px(x0 + y, y0 - x, color); |
||||
fb_px(x0 - y, y0 - x, color); |
||||
} |
||||
radius--; |
||||
} |
||||
} |
||||
|
||||
#if IS_AVR |
||||
void fb_bitmap_P(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color) |
||||
#else |
||||
void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color) |
||||
#endif |
||||
{ |
||||
if (x >= FBW || y >= FBH) { return; } |
||||
const fbpos_t w0 = w; |
||||
w = MIN(FBW - x - 1, w); |
||||
h = MIN(FBH - y - 1, h); |
||||
const fbpos_t row = y / 8; |
||||
const fbpos_t rowrem = y % 8; |
||||
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW; |
||||
|
||||
if (rowrem + h <= 8) { |
||||
for (fbpos_t i = 0; i < w; i++) { |
||||
// all within one cell
|
||||
const uint8_t mask = (pgm_read_byte(&map[i]) & (0xFF >> (8 - h))) << rowrem; |
||||
draw_mask(cell + i, mask, color); |
||||
} |
||||
return; |
||||
} else { |
||||
fbsize_t mapc0 = 0; |
||||
|
||||
// Draw the bitmap slice-by-slice based on how rows of the bitmap intersect with rows of the canvas.
|
||||
// This could be optimized to walk each row of the canvas only once, but the code would get bigger.
|
||||
while (h > 0) { |
||||
for (fbpos_t i = 0; i < w; i++) { |
||||
const uint8_t mask = (pgm_read_byte(&map[i + mapc0]) & (0xFF >> rowrem)) << rowrem; |
||||
draw_mask(cell + i, mask, color); |
||||
} |
||||
|
||||
cell += FBW; |
||||
|
||||
if (rowrem != 0) { |
||||
for (fbpos_t i = 0; i < w; i++) { |
||||
const uint8_t mask = (pgm_read_byte(&map[i + mapc0]) & (0xFF << (8 - rowrem))) >> (8 - rowrem); |
||||
draw_mask(cell + i, mask, color); |
||||
} |
||||
} |
||||
|
||||
if (h > 8) { |
||||
h -= 8; |
||||
} else { |
||||
break; |
||||
} |
||||
mapc0 += w0; |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,87 @@ |
||||
//
|
||||
// Created by MightyPork on 2022/11/12.
|
||||
//
|
||||
|
||||
#ifndef FRAMEBUFFER_H |
||||
#define FRAMEBUFFER_H |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "framebuffer_config.h" |
||||
|
||||
#define FBSET 0xFF |
||||
#define FBCLEAR 0x00 |
||||
|
||||
typedef uint16_t fbsize_t; |
||||
typedef uint8_t fbpos_t; |
||||
typedef uint8_t fbcolor_t; |
||||
|
||||
#define FB_LEN ((FBH / 8) * FBW) |
||||
|
||||
/// Framebuffer backing array.
|
||||
///
|
||||
/// The format is the native format for SSD1306: array of bytes representing pixels in 8 rows at once, LSB is the topmost row.
|
||||
///
|
||||
/// a0 b0 ... til the display width
|
||||
/// a1 b1
|
||||
/// a2 b2
|
||||
/// ... ...
|
||||
/// a7 b7
|
||||
///
|
||||
/// and more bytes continue rows 8-15 and so on
|
||||
extern uint8_t fb[ FB_LEN ]; |
||||
|
||||
/// Fill the entire screen with a byte pattern.
|
||||
/// Use 0xFF or 0x00 for a solid fill. Other patterns may be used to create horizontal stripes.
|
||||
void fb_fill(uint8_t pattern); |
||||
|
||||
/// Invert the entire screen
|
||||
void fb_invert(); |
||||
|
||||
/// Clear the display (fill with 0x00)
|
||||
static inline void fb_clear(void) |
||||
{ |
||||
fb_fill(0); |
||||
} |
||||
|
||||
/// Fill screen with a repeating pattern
|
||||
///
|
||||
/// \param pattern - bytes to repeat, PROGMEM
|
||||
/// \param pattern_len - len of the pattern
|
||||
void fb_fill_pattern(const uint8_t* pattern, uint8_t pattern_len, fbcolor_t color); |
||||
|
||||
/// Set a single pixel
|
||||
void fb_px(fbpos_t x, fbpos_t y, fbcolor_t color); |
||||
|
||||
/// Get pixel color
|
||||
uint8_t fb_getpx(fbpos_t x, fbpos_t y); |
||||
|
||||
/// Draw a horizontal line
|
||||
void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, fbcolor_t color); |
||||
|
||||
/// Draw a vertical line
|
||||
void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, fbcolor_t color); |
||||
|
||||
/// Draw a filled rect
|
||||
void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbcolor_t color); |
||||
|
||||
/// Draw a frame (unfilled rect)
|
||||
void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, fbcolor_t color); |
||||
|
||||
#if IS_AVR |
||||
/// Draw a bitmap from progmem
|
||||
void fb_bitmap_P(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color); |
||||
#else |
||||
void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color); |
||||
#endif |
||||
|
||||
/// Draw a circle
|
||||
void fb_circle(fbpos_t x, fbpos_t y, fbpos_t r, uint8_t thickness, fbcolor_t color); |
||||
|
||||
/// Output the framebuffer array `fb` to the display device.
|
||||
///
|
||||
/// The user must implement this
|
||||
extern void fb_blit(void); |
||||
|
||||
#endif //FRAMEBUFFER_H
|
@ -0,0 +1,8 @@ |
||||
#ifndef FRAMEBUFFER_CONFIG_H |
||||
#define FRAMEBUFFER_CONFIG_H |
||||
|
||||
/* Tiny framebuffer */ |
||||
#define FBW 128 |
||||
#define FBH 64 |
||||
|
||||
#endif /* FRAMEBUFFER_CONFIG_H */ |
@ -0,0 +1,164 @@ |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include "framebuffer.h" |
||||
#include "font.h" |
||||
#include "fb_7seg.h" |
||||
#include "fb_text.h" |
||||
|
||||
void main() { |
||||
fb_clear(); |
||||
|
||||
|
||||
const uint8_t ryba[14] = { |
||||
0b11000011, |
||||
0b01100110, |
||||
0b01111110, |
||||
0b00111100, |
||||
0b00011000, |
||||
0b00111100, |
||||
0b01111110, |
||||
0b11111111, |
||||
0b11111111, |
||||
0b11111011, |
||||
0b11111111, |
||||
0b01111110, |
||||
0b00111100, |
||||
0b00011000, |
||||
}; |
||||
#define ZIR_W 12 |
||||
#define ZIR_H 24 |
||||
const uint8_t zirafa[(ZIR_H/8) * ZIR_W] = { |
||||
// levo
|
||||
0b10000000, |
||||
0b01000000, |
||||
0b00100000, |
||||
0b00010000, |
||||
0b00001000, |
||||
0b00000100, |
||||
0b00000010, |
||||
0b00011001, |
||||
0b00011111, |
||||
0b11111000, |
||||
0b00011111, |
||||
0b00001000, |
||||
//
|
||||
0b10000000, |
||||
0b01000000, |
||||
0b00100000, |
||||
0b00010000, |
||||
0b00001000, |
||||
0b00000100, |
||||
0b00000010, |
||||
0b00000001, |
||||
0b10000000, |
||||
0b11111111, |
||||
0b00100000, |
||||
0b00010000, |
||||
//
|
||||
0b00110000, |
||||
0b10001000, |
||||
0b11111000, |
||||
0b00011000, |
||||
0b11111000, |
||||
0b10011000, |
||||
0b00011000, |
||||
0b10011000, |
||||
0b11111000, |
||||
0b00011111, |
||||
0b11111000, |
||||
0b10000000, |
||||
}; |
||||
|
||||
//fb_bitmap(0, pos, 14, 8, ryba, 1);
|
||||
|
||||
// fb_bitmap(0, 0, ZIR_W, ZIR_H, zirafa, 1);
|
||||
// fb_bitmap(ZIR_W+2, 12, ZIR_W, ZIR_H, zirafa, 1);
|
||||
// fb_bitmap((ZIR_W + 2) * 2, 2, ZIR_W, ZIR_H, zirafa, 1);
|
||||
|
||||
fb_frame(0, 0, 140, 40, 2, 1); |
||||
|
||||
// uint8_t x = 3;
|
||||
// uint8_t w = 7;
|
||||
// uint8_t h = 10;
|
||||
// uint8_t th = 1;
|
||||
// uint8_t sp = 3;
|
||||
// x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 7, /*color*/ 1) + sp;
|
||||
// x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 9, /*color*/ 1) + sp;
|
||||
// x += fb_7seg_period(x, 3, w, h, th, /*color*/ 1) + sp;
|
||||
// x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 5, /*color*/ 1) + sp;
|
||||
|
||||
//fb_circle(60, 15, 10, 1, 1);
|
||||
// fb_circle(60, 15, 5, 1, 1);
|
||||
|
||||
//fb_fill_pattern("\xAA\x55", 2, 0);
|
||||
|
||||
//fb_invert();
|
||||
|
||||
// fb_text(40, 12, "▶Ahoj→!", FONT_BOLD, 1);
|
||||
|
||||
fb_text(15, 15, "MEOW", FONT_DOUBLEH, 1); |
||||
fb_text(40, 15, "MEOW", FONT_DOUBLEW, 1); |
||||
fb_text(40, 23, "MEOW", FONT_BOLD, 1); |
||||
|
||||
fb_text(5, 5, "Tiny 3x5", FONT_3X5, 1); |
||||
fb_text(40, 5, "Tiny 4x5 MEOW", FONT_4X5, 1); |
||||
|
||||
fb_frame(12,12,80,20,1,1); |
||||
|
||||
// fb_text(36, 5, "-123456789.0 CPM°", FONT_TINY, 1);
|
||||
|
||||
// struct Utf8Iterator iter;
|
||||
//// Utf8Iterator_Init_P(&iter, str);
|
||||
// Utf8Iterator_Init(&iter, str);
|
||||
//
|
||||
// struct Utf8Char uchar;
|
||||
// x = 3;
|
||||
// while ((uchar = Utf8Iterator_Next(&iter)).uint) {
|
||||
// const font_bitmap_t *sym = font_getsym(&uchar);
|
||||
// fb_bitmap_P(x, 15, 5, 8, sym->data, 1);
|
||||
// x += 7;
|
||||
// }
|
||||
//
|
||||
//uint8_t xx[] = {
|
||||
// 0x1f, 0x11, 0x11, 0x1f,
|
||||
// 0x02, 0x05, 0x02, 0x00,
|
||||
// 0x00, 0x04, 0x04, 0x04,
|
||||
// 0x0e, 0x11, 0x11, 0x0a,
|
||||
// 0x1f, 0x05, 0x05, 0x02,
|
||||
// 0x1f, 0x01, 0x02, 0x1f,
|
||||
// 0x00, 0x18, 0x18, 0x00,
|
||||
//};
|
||||
//
|
||||
//for(int i=0;i<sizeof(xx);i++) {
|
||||
// uint8_t v = xx[i];
|
||||
// uint8_t out = 0;
|
||||
// for(int i=0;i<8;i++) {
|
||||
// out |= ((v>>i) & 1) << (7-i);
|
||||
// }
|
||||
//
|
||||
// if(i%4==0) printf("\n");
|
||||
// printf("0x%02x, ", out);
|
||||
//}
|
||||
|
||||
fb_blit(); |
||||
} |
||||
|
||||
|
||||
void fb_blit() { |
||||
for(int y = 0; y < FBH; y++) { |
||||
for(int x = 0; x < FBW; x++) { |
||||
bool b = fb[(y/8)*FBW + x] & (1 << (y%8)); |
||||
if (b) { |
||||
printf("\x1b[1;32m█\x1b[m"); |
||||
} else { |
||||
if (y%8 == 0) { |
||||
printf("\x1b[34m.\x1b[m"); |
||||
} else { |
||||
printf("."); |
||||
} |
||||
} |
||||
} |
||||
printf("\r\n"); |
||||
} |
||||
printf("\r\n"); |
||||
} |
@ -0,0 +1,18 @@ |
||||
/**
|
||||
* Progmem support |
||||
*/ |
||||
|
||||
#ifndef UFB_PROGMEM_H |
||||
#define UFB_PROGMEM_H |
||||
|
||||
|
||||
// if built for AVR, uncommend the include and comment the fake defines:
|
||||
// #define IS_AVR 1
|
||||
// #include <avr/pgmspace.h>
|
||||
|
||||
#define IS_AVR 0 |
||||
#define PROGMEM |
||||
#define pgm_read_byte(adr) (*adr) |
||||
#define pgm_read_dword(adr) (*adr) |
||||
|
||||
#endif //UFB_PROGMEM_H
|
@ -0,0 +1,129 @@ |
||||
#include <stdint.h> |
||||
#include "utf8.h" |
||||
|
||||
//
|
||||
// Created by MightyPork on 2017/08/20.
|
||||
//
|
||||
// UTF-8 parser - collects bytes of a code point before writing them
|
||||
// into a screen cell.
|
||||
//
|
||||
|
||||
const struct Utf8Char EMPTY_CHAR = (struct Utf8Char) {.uint = 0}; |
||||
|
||||
// Code Points First Byte Second Byte Third Byte Fourth Byte
|
||||
// U+0000 - U+007F 00 - 7F
|
||||
// U+0080 - U+07FF C2 - DF 80 - BF
|
||||
// U+0800 - U+0FFF E0 *A0 - BF 80 - BF
|
||||
// U+1000 - U+CFFF E1 - EC 80 - BF 80 - BF
|
||||
// U+D000 - U+D7FF ED 80 - *9F 80 - BF
|
||||
// U+E000 - U+FFFF EE - EF 80 - BF 80 - BF
|
||||
// U+10000 - U+3FFFF F0 *90 - BF 80 - BF 80 - BF
|
||||
// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF
|
||||
// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF
|
||||
|
||||
size_t utf8_strlen(const char *text) |
||||
{ |
||||
// TODO optimize
|
||||
struct Utf8Iterator iter; |
||||
Utf8Iterator_Init(&iter, text); |
||||
size_t num = 0; |
||||
while ((Utf8Iterator_Next(&iter)).uint) { |
||||
num++; |
||||
} |
||||
return num; |
||||
} |
||||
|
||||
/**
|
||||
* Handle a received character |
||||
*/ |
||||
struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c) |
||||
{ |
||||
uint8_t *bytes = self->buffer.bytes; |
||||
|
||||
uint8_t uc = (uint8_t) c; |
||||
// collecting unicode glyphs...
|
||||
if (uc & 0x80) { |
||||
if (self->utf_len == 0) { |
||||
bytes[0] = uc; |
||||
self->utf_j = 1; |
||||
|
||||
// start
|
||||
if (uc == 0xC0 || uc == 0xC1 || uc > 0xF4) { |
||||
// forbidden start codes
|
||||
goto fail; |
||||
} |
||||
|
||||
if ((uc & 0xE0) == 0xC0) { |
||||
self->utf_len = 2; |
||||
} |
||||
else if ((uc & 0xF0) == 0xE0) { |
||||
self->utf_len = 3; |
||||
} |
||||
else if ((uc & 0xF8) == 0xF0) { |
||||
self->utf_len = 4; |
||||
} |
||||
else { |
||||
// chars over 127 that don't start unicode sequences
|
||||
goto fail; |
||||
} |
||||
} |
||||
else { |
||||
if ((uc & 0xC0) != 0x80) { |
||||
bytes[self->utf_j++] = uc; |
||||
goto fail; |
||||
} |
||||
else { |
||||
bytes[self->utf_j++] = uc; |
||||
if (self->utf_j >= self->utf_len) { |
||||
// check for bad sequences - overlong or some other problem
|
||||
if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail; |
||||
if (bytes[0] == 0xF0 && bytes[1] < 0x90) goto fail; |
||||
if (bytes[0] == 0xED && bytes[1] > 0x9F) goto fail; |
||||
if (bytes[0] == 0xE0 && bytes[1] < 0xA0) goto fail; |
||||
|
||||
// trap for surrogates - those break javascript
|
||||
if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail; |
||||
|
||||
goto success; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
else { |
||||
bytes[0] = uc; |
||||
goto success; |
||||
} |
||||
|
||||
return EMPTY_CHAR; |
||||
|
||||
success:; |
||||
struct Utf8Char result = self->buffer; |
||||
self->buffer.uint = 0; // erase the buffer
|
||||
self->utf_len = 0; |
||||
return result; |
||||
|
||||
fail: |
||||
self->buffer.uint = 0; // erase the buffer
|
||||
self->utf_len = 0; |
||||
return EMPTY_CHAR; |
||||
} |
||||
|
||||
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self) |
||||
{ |
||||
char c; |
||||
struct Utf8Char uchar; |
||||
while (1) { |
||||
if (self->is_progmem) { |
||||
c = pgm_read_byte(self->source++); |
||||
} else { |
||||
c = *self->source++; |
||||
} |
||||
if (!c) break; |
||||
|
||||
uchar = Utf8Parser_Handle(&self->parser, c); |
||||
if (uchar.uint) { |
||||
return uchar; |
||||
} |
||||
} |
||||
return EMPTY_CHAR; |
||||
} |
@ -0,0 +1,93 @@ |
||||
/**
|
||||
* UTF-8 string parsing and character iteration |
||||
* |
||||
* Created on 2020/01/04. |
||||
*/ |
||||
|
||||
#ifndef LIQUIDTYPE_UTF8_H |
||||
#define LIQUIDTYPE_UTF8_H |
||||
|
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
#include "progmem.h" |
||||
|
||||
/**
|
||||
* UTF-8 encoded character. |
||||
*/ |
||||
struct Utf8Char { |
||||
union { |
||||
/** character bytes; padded by zero bytes if shorter than 4 */ |
||||
uint8_t bytes[4]; |
||||
/** u32 view of the bytes */ |
||||
uint32_t uint; |
||||
}; |
||||
}; |
||||
|
||||
/** UTF8 string parser internal state */ |
||||
struct Utf8Parser { |
||||
/** UTF-8 bytes buffer */ |
||||
struct Utf8Char buffer; |
||||
/** Currently collected UTF-8 character length */ |
||||
uint8_t utf_len; |
||||
/** Position in the current character */ |
||||
uint8_t utf_j; |
||||
}; |
||||
|
||||
static inline void Utf8Parser_Clear(struct Utf8Parser *self) { |
||||
self->buffer.uint = 0; |
||||
self->utf_j = 0; |
||||
self->utf_len = 0; |
||||
} |
||||
|
||||
/**
|
||||
* Utf8 character iterator. |
||||
* |
||||
* Usage: |
||||
* struct Utf8Iterator iter; |
||||
* Utf8Iterator_Init(&iter, myString); |
||||
* |
||||
* union Utf8Char uchar; |
||||
* while ((uchar = Utf8Iterator_Next(&iter)).uint) { |
||||
* // do something with the char
|
||||
* } |
||||
* |
||||
* // Free myString if needed, it is not mutated.
|
||||
*/ |
||||
struct Utf8Iterator { |
||||
/* Characters to parse. The pointer is advanced as the iterator progresses. */ |
||||
const char *source; |
||||
struct Utf8Parser parser; |
||||
bool is_progmem; |
||||
}; |
||||
|
||||
static inline void Utf8Iterator_Init(struct Utf8Iterator *self, const char *source) { |
||||
Utf8Parser_Clear(&self->parser); |
||||
self->source = source; |
||||
self->is_progmem = false; |
||||
} |
||||
|
||||
static inline void Utf8Iterator_Init_P(struct Utf8Iterator *self, const char *source) { |
||||
Utf8Iterator_Init(self, source); |
||||
self->is_progmem = true; |
||||
} |
||||
|
||||
size_t utf8_strlen(const char *text); |
||||
|
||||
/**
|
||||
* Get the next character from the iterator; Returns empty character if there are no more characters to parse. |
||||
* |
||||
* Invalid characters are skipped. |
||||
*/ |
||||
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self); |
||||
|
||||
/**
|
||||
* Parse a character. |
||||
* |
||||
* The returned struct contains NIL (uint == 0) if no character is yet available. |
||||
* |
||||
* ASCII is passed through, utf-8 is collected and returned in one piece. |
||||
*/ |
||||
struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c); |
||||
|
||||
#endif //LIQUIDTYPE_UTF8_H
|
Loading…
Reference in new issue