initialize repository

This commit is contained in:
2026-09-10 14:35:16 +03:00
commit f2007b7e9b
16 changed files with 2144 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

21
CMakeLists.txt Normal file
View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.27)
set(PICO_SDK_PATH /home/sl/pico-sdk)
set(PICO_PLATFORM rp2350)
include(pico_sdk_import.cmake)
project(GPIOTest)
set(PICO_PSRAM_CS_PIN 47 CACHE STRING "GPIO used as Chip Select for PSRAM")
set(PICO_PSRAM_SIZE_BYTES 8388608 CACHE STRING "PSRAM size in bytes")
pico_sdk_init()
add_executable(program main.c st7789.c graphics.c utf8.c ds1302.c beep.c)
target_link_libraries(program pico_stdlib pico_bootrom pico_rand hardware_gpio hardware_pwm hardware_spi hardware_regs hardware_psram pico_stdio_usb pico_time pico_sync)
#target_compile_definitions(program PRIVATE PICO_PSRAM_CS_PIN=${PICO_PSRAM_CS_PIN} PICO_PSRAM_SIZE_BYTES=${PICO_PSRAM_CS_PIN})
target_compile_definitions(program PRIVATE PICO_PSRAM_CS_PIN=0 PICO_PSRAM_SIZE_BYTES=8388608)
pico_enable_stdio_usb(program 1)
pico_embed_pt_in_binary(program "${CMAKE_CURRENT_LIST_DIR}/partition_table.json")
pico_add_extra_outputs(program)

162
ds1302.c Normal file
View File

@@ -0,0 +1,162 @@
#include "ds1302.h"
void _prepare_read(uint8_t address);
void _prepare_write(uint8_t address);
uint8_t _read_byte();
void _write_byte(uint8_t value);
void _make_clock_cycle();
void _end_transfer();
#define REG_SECONDS 0x80 // SEC
#define REG_MINUTES 0x82 // MIN
#define REG_HOUR 0x84 // HR
#define REG_DATE 0x86 // DATE
#define REG_MONTH 0x88 // MONTH
#define REG_DAY 0x8A // DAY
#define REG_YEAR 0x8C // YEAR
#define REG_WP 0x8E // CONTROL
// TRICKLE CHARGER 1001000_ 0x90
#define REG_BURST 0xBE // CLOCK BURST
#define REG_RAM00 0xC0
#define REG_RAM_BURST 0xFE
void ds1302_init() {
gpio_init(PIN_RTC_RESET);
gpio_set_dir(PIN_RTC_RESET, GPIO_OUT);
gpio_put(PIN_RTC_RESET, LOW);
gpio_init(PIN_RTC_CLOCK);
gpio_set_dir(PIN_RTC_CLOCK, GPIO_OUT);
gpio_put(PIN_RTC_CLOCK, LOW);
gpio_init(PIN_RTC_IO);
gpio_set_dir(PIN_RTC_IO, GPIO_IN);
}
int ds1302_is_clock_halted() {
_prepare_read(REG_SECONDS);
uint8_t seconds = _read_byte();
_end_transfer();
return (seconds & 0b10000000);
}
void ds1302_get_datetime(DateTime *dt) {
_prepare_read(REG_BURST);
dt->second = ds1302_decode_bcd(_read_byte() & 0b01111111);
dt->minute = ds1302_decode_bcd(_read_byte() & 0b01111111);
dt->hour = ds1302_decode_bcd(_read_byte() & 0b00111111);
dt->day = ds1302_decode_bcd(_read_byte() & 0b00111111);
dt->month = ds1302_decode_bcd(_read_byte() & 0b00011111);
dt->dow = ds1302_decode_bcd(_read_byte() & 0b00000111);
dt->year = ds1302_decode_bcd(_read_byte() & 0b01111111);
_end_transfer();
}
void ds1302_set_datetime(DateTime *dt) {
// disable write protection
_prepare_write(REG_WP);
_write_byte(0b00000000); // 0, false
_end_transfer();
// OR call writeProtect(0)
_prepare_write(REG_BURST);
_write_byte(ds1302_encode_bcd(dt->second % 60));
_write_byte(ds1302_encode_bcd(dt->minute % 60));
_write_byte(ds1302_encode_bcd(dt->hour % 24));
_write_byte(ds1302_encode_bcd(dt->day % 32));
_write_byte(ds1302_encode_bcd(dt->month % 13));
_write_byte(ds1302_encode_bcd(dt->dow % 8));
_write_byte(ds1302_encode_bcd(dt->year % 100));
_write_byte(0b10000000);
_end_transfer();
}
void ds1302_halt() {
_prepare_write(REG_SECONDS);
_write_byte(0b10000000);
_end_transfer();
}
void _prepare_read(uint8_t address) {
gpio_set_dir(PIN_RTC_IO, GPIO_OUT);
gpio_put(PIN_RTC_RESET, HIGH);
uint8_t command = 0b10000001 | address;
_write_byte(command);
gpio_set_dir(PIN_RTC_IO, GPIO_IN);
}
void _prepare_write(uint8_t address) {
gpio_set_dir(PIN_RTC_IO, GPIO_OUT);
gpio_put(PIN_RTC_RESET, HIGH);
uint8_t command = 0b10000000 | address;
_write_byte(command);
}
void _end_transfer() { gpio_put(PIN_RTC_RESET, LOW); }
uint8_t _read_byte() {
uint8_t byte = 0;
for (uint8_t b = 0; b < 8; b++) {
if (gpio_get(PIN_RTC_IO) == HIGH)
byte |= 0x01 << b;
_make_clock_cycle();
}
return byte;
}
void _write_byte(uint8_t value) {
for (uint8_t b = 0; b < 8; b++) {
gpio_put(PIN_RTC_IO, (value & 0x01) ? HIGH : LOW);
_make_clock_cycle();
value >>= 1;
}
}
void _make_clock_cycle() {
gpio_put(PIN_RTC_CLOCK, HIGH);
sleep_us(1);
gpio_put(PIN_RTC_CLOCK, LOW);
sleep_us(1);
}
void ds1302_get_ram_byte(uint8_t addr, uint8_t *data) {
_prepare_read(addr);
*data = _read_byte();
_end_transfer();
}
void ds1302_set_ram_byte(uint8_t addr, uint8_t *data) {
_prepare_write(addr);
_write_byte(*data);
_end_transfer();
}
void ds1302_set_write_protection(int enable) {
_prepare_write(REG_WP);
_write_byte(enable);
_end_transfer();
}
void ds1302_write_ram_bulk(const uint8_t *data, int len) {
if (len <= 0 || len > 31) {
return;
}
_prepare_write(REG_RAM_BURST);
for (int i = 0; i < len; i++) {
_write_byte(data[i]);
}
_end_transfer();
}
void ds1302_read_ram_bulk(uint8_t *data, int len) {
if (len <= 0 || len > 31) {
return;
}
_prepare_read(REG_RAM_BURST);
for (int i = 0; i < len; i++) {
data[i] = _read_byte();
}
_end_transfer();
}

84
ds1302.h Normal file
View File

@@ -0,0 +1,84 @@
/**
* Ds1302.h
*
* @version 1.0.3
* @author Rafa Couto <caligari@treboada.net>
* @license GNU Affero General Public License v3.0
* @see https://github.com/Treboada/Ds1302
*
* adapted for Pico by Ken Webb, 27 Oct 2022
*
* 31 Oct 2022
* I am using additional ideas and code on ds1302 Ram from :
* https://github.com/msparks/arduino-ds1302 Matt Sparks no license?
* "Arduino library for the DS1302 Real Time Clock chip"
* includes "Setting and accessing the 31 bytes of static RAM. Single-byte and multi-byte (burst) modes are supported."
*
*/
#ifndef _DS_1302_H
#define _DS_1302_H
#include "pico/stdlib.h"
#include "pins.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// the following defines are from Arduino.h
// these are needed in Ds1302.cpp
#define HIGH 0x1
#define LOW 0x0
typedef struct {
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t dow;
} DateTime;
/**
* Months of year
*/
enum MONTH {
MONTH_JAN = 1,
MONTH_FEB = 2,
MONTH_MAR = 3,
MONTH_APR = 4,
MONTH_MAY = 5,
MONTH_JUN = 6,
MONTH_JUL = 7,
MONTH_AUG = 8,
MONTH_SET = 9,
MONTH_OCT = 10,
MONTH_NOV = 11,
MONTH_DEC = 12
};
/**
* Days of week
*/
enum DOW { DOW_MON = 1, DOW_TUE = 2, DOW_WED = 3, DOW_THU = 4, DOW_FRI = 5, DOW_SAT = 6, DOW_SUN = 7 };
void ds1302_init();
int ds1302_is_clock_halted();
void ds1302_halt();
void ds1302_get_datetime(DateTime *dt);
void ds1302_set_datetime(DateTime *dt);
void ds1302_get_ram_byte(uint8_t addr, uint8_t *data);
void ds1302_set_ram_byte(uint8_t addr, uint8_t *data);
void ds1302_set_write_protection(int enable);
void ds1302_write_ram_bulk(const uint8_t *data, int len);
void ds1302_read_ram_bulk(uint8_t *data, int len);
inline uint8_t ds1302_encode_bcd(uint8_t dec) { return ((dec / 10 * 16) + (dec % 10)); }
inline uint8_t ds1302_decode_bcd(uint8_t bcd) { return ((bcd / 16 * 10) + (bcd % 16)); }
#endif // _DS_1302_H

521
font_ubuntu.h Normal file
View File

@@ -0,0 +1,521 @@
// Header File for SSD1306 characters
// Generated with TTF2BMH, edited manually
// Font Ubuntu Regular
// Font Size: 24
const char GLYPH_MISSING[] = {254, 2, 194, 226, 98, 98, 226, 194, 130, 2, 254, 0, 255, 0, 0, 0, 192, 248,
62, 15, 3, 0, 255, 0, 127, 64, 64, 64, 76, 76, 64, 64, 64, 64, 127, 0};
const char GLYPH_EXCLAMATION_MARK[] = {254, 254, 63, 63, 7, 7};
const char GLYPH_QUOTATION_MARK[] = {63, 63, 0, 0, 0, 63, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_NUMBER_SIGN[] = {192, 192, 192, 240, 254, 206, 192, 192, 192, 192, 192, 240, 254, 206, 192,
48, 48, 252, 255, 51, 48, 48, 48, 48, 48, 252, 255, 51, 48, 48,
0, 7, 7, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0};
const char GLYPH_DOLLAR_SIGN[] = {240, 248, 152, 12, 12, 15, 15, 12, 12, 24, 0, 0, 1, 3, 3, 7, 6,
12, 12, 24, 240, 224, 3, 7, 6, 6, 6, 62, 62, 6, 7, 3, 1};
const char GLYPH_PERCENT_SIGN[] = {248, 252, 14, 6, 6, 14, 252, 248, 0, 0, 128, 224, 120, 60, 14, 2, 0,
1, 3, 7, 6, 6, 7, 195, 225, 120, 30, 7, 1, 248, 252, 14, 6, 6,
0, 0, 0, 0, 4, 7, 3, 1, 0, 0, 0, 0, 1, 3, 7, 6, 6};
const char GLYPH_AMPERSAND[] = {0, 0, 120, 252, 142, 6, 6, 134, 206, 252, 120, 0, 0, 0, 0,
240, 248, 12, 7, 7, 7, 15, 29, 57, 112, 192, 192, 112, 60, 12,
0, 3, 3, 7, 6, 6, 6, 6, 6, 3, 3, 1, 3, 6, 4};
const char GLYPH_APOSTROPHE[] = {63, 63, 0, 0, 0, 0};
const char GLYPH_LEFT_PARENTHESIS[] = {128, 224, 120, 14, 3, 1, 255, 255, 0, 0, 0, 0, 0, 3, 15, 56, 96, 192};
const char GLYPH_RIGHT_PARENTHESIS[] = {1, 3, 14, 120, 224, 128, 0, 0, 0, 0, 255, 255, 192, 96, 56, 15, 3, 0};
const char GLYPH_ASTERISK[] = {16, 56, 48, 176, 238, 126, 238, 176, 48, 56, 16, 0, 0, 2, 7, 3, 0,
1, 3, 7, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_PLUS_SIGN[] = {0, 0, 0, 0, 0, 224, 224, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 255,
255, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0};
const char GLYPH_COMMA[] = {0, 0, 0, 0, 0, 0, 96, 127, 15};
const char GLYPH_HYPHEN_MINUS[] = {0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0};
const char GLYPH_FULL_STOP[] = {0, 0, 0, 0, 0, 0, 7, 7, 7};
const char GLYPH_SOLIDUS[] = {0, 0, 0, 0, 0, 0, 224, 248, 31, 7, 0, 0, 128, 224, 124,
31, 3, 0, 0, 0, 240, 124, 15, 3, 0, 0, 0, 0, 0, 0};
const char GLYPH_DIGIT_ZERO[] = {192, 240, 60, 12, 6, 6, 6, 6, 6, 12, 60, 240, 192, 63, 255, 192, 0, 0, 0, 0,
0, 0, 0, 192, 255, 63, 0, 0, 3, 3, 6, 6, 6, 6, 7, 3, 1, 0, 0};
const char GLYPH_DIGIT_ONE[] = {32, 112, 48, 56, 28, 254, 254, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_DIGIT_TWO[] = {8, 12, 14, 6, 6, 6, 6, 142, 252, 248, 0, 0, 192, 224, 112, 56, 12,
6, 3, 1, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6};
const char GLYPH_DIGIT_THREE[] = {4, 12, 6, 6, 6, 6, 6, 134, 140, 252, 120, 0, 0, 0, 0, 3, 3, 3,
3, 7, 7, 140, 252, 240, 3, 7, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_DIGIT_FOUR[] = {0, 0, 0, 128, 192, 224, 56, 60, 30, 254, 254, 0, 0, 56, 60, 55, 51, 49, 48, 48,
48, 48, 255, 255, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0};
const char GLYPH_DIGIT_FIVE[] = {0, 0, 254, 254, 6, 6, 6, 6, 6, 6, 0, 0, 3, 3, 3, 3, 3,
7, 6, 14, 252, 248, 3, 7, 6, 6, 6, 6, 6, 7, 3, 1, 0};
const char GLYPH_DIGIT_SIX[] = {0, 192, 240, 56, 24, 12, 12, 6, 6, 6, 6, 0, 127, 255, 134, 3, 3, 3,
3, 3, 7, 142, 252, 248, 0, 1, 3, 7, 6, 6, 6, 6, 7, 3, 1, 0};
const char GLYPH_DIGIT_SEVEN[] = {6, 6, 6, 6, 6, 6, 134, 230, 118, 30, 14, 0, 0, 0, 0, 240, 126,
15, 1, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0};
const char GLYPH_DIGIT_EIGHT[] = {112, 248, 140, 142, 6, 6, 6, 6, 6, 14, 140, 252, 112, 240, 249, 141, 7, 7, 3, 2,
6, 7, 15, 157, 252, 240, 0, 1, 3, 7, 6, 6, 6, 6, 6, 7, 3, 1, 0};
const char GLYPH_DIGIT_NINE[] = {240, 248, 28, 14, 6, 6, 6, 6, 12, 28, 248, 224, 1, 7, 7, 14, 12, 12,
12, 140, 204, 246, 127, 15, 0, 6, 6, 6, 6, 3, 3, 3, 1, 0, 0, 0};
const char GLYPH_COLON[] = {192, 192, 192, 1, 1, 1, 7, 7, 7};
const char GLYPH_SEMICOLON[] = {192, 192, 192, 1, 1, 1, 96, 127, 15};
const char GLYPH_LESS_THAN_SIGN[] = {0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 192, 128,
28, 28, 28, 54, 54, 119, 99, 99, 193, 193, 193, 128, 128,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0};
const char GLYPH_EQUALS_SIGN[] = {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 49, 49, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_GREATER_THAN_SIGN[] = {128, 192, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0,
128, 128, 193, 193, 193, 99, 99, 119, 54, 54, 28, 28, 28,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_QUESTION_MARK[] = {4, 6, 6, 6, 6, 6, 142, 252, 120, 0, 0, 0, 56, 62,
7, 3, 1, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0};
const char GLYPH_COMMERCIAL_AT[] = {0, 192, 240, 120, 24, 12, 12, 142, 198, 198, 198, 198, 198, 198, 204, 12,
255, 255, 129, 0, 0, 126, 255, 195, 129, 0, 0, 0, 0, 255, 255, 0,
0, 3, 15, 30, 24, 56, 48, 113, 99, 99, 99, 99, 99, 97, 99, 3};
const char GLYPH_LATIN_CAPITAL_LETTER_A[] = {0, 0, 0, 0, 0, 192, 240, 60, 14, 60, 240, 192, 0, 0, 0, 0, 0,
0, 0, 224, 248, 62, 55, 49, 48, 48, 48, 49, 55, 62, 248, 224, 0, 0,
4, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 7, 4};
const char GLYPH_LATIN_CAPITAL_LETTER_B[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 12, 156, 248, 240, 0,
255, 255, 6, 6, 6, 6, 6, 6, 6, 7, 15, 141, 248, 240,
7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_C[] = {128, 240, 120, 28, 12, 14, 6, 6, 6, 6, 6, 12, 12,
63, 255, 224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 3, 3, 6, 6, 6, 6, 6, 6, 7, 3};
const char GLYPH_LATIN_CAPITAL_LETTER_D[] = {254, 254, 6, 6, 6, 6, 6, 6, 14, 12, 12, 24, 120, 224, 128,
255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 224, 127, 31,
7, 7, 6, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_E[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 255, 255, 6, 6, 6, 6,
6, 6, 6, 6, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
const char GLYPH_LATIN_CAPITAL_LETTER_F[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 6, 6, 255, 255, 6, 6, 6, 6,
6, 6, 6, 6, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_G[] = {128, 240, 120, 28, 12, 6, 6, 6, 6, 6, 6, 12, 4,
63, 255, 224, 128, 0, 0, 0, 0, 0, 0, 0, 252, 252,
0, 0, 1, 3, 3, 6, 6, 6, 6, 6, 6, 7, 3};
const char GLYPH_LATIN_CAPITAL_LETTER_H[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 254,
255, 255, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 255, 255,
7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_LATIN_CAPITAL_LETTER_I[] = {254, 254, 255, 255, 7, 7};
const char GLYPH_LATIN_CAPITAL_LETTER_J[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 254, 0, 0, 0, 0, 0, 0,
0, 0, 128, 255, 255, 2, 3, 6, 6, 6, 6, 6, 7, 3, 1, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_K[] = {254, 254, 0, 0, 128, 192, 96, 48, 24, 12, 6, 2, 0, 0,
255, 255, 6, 15, 29, 24, 56, 112, 224, 192, 128, 0, 0, 0,
7, 7, 0, 0, 0, 0, 0, 0, 0, 1, 3, 7, 6, 4};
const char GLYPH_LATIN_CAPITAL_LETTER_L[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
const char GLYPH_LATIN_CAPITAL_LETTER_M[] = {0, 254, 126, 60, 240, 192, 0, 0, 0, 0, 0, 0, 192, 240, 60, 126,
255, 255, 0, 0, 0, 3, 15, 124, 240, 240, 124, 15, 3, 0, 0, 0,
7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_N[] = {254, 252, 28, 56, 112, 192, 128, 0, 0, 0, 0, 0, 0, 254, 254,
255, 255, 0, 0, 0, 0, 1, 3, 14, 28, 56, 112, 192, 255, 255,
7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7};
const char GLYPH_LATIN_CAPITAL_LETTER_O[] = {192, 240, 120, 28, 12, 14, 6, 6, 6, 6, 14, 12, 28, 120, 240, 192,
63, 255, 224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 128, 224, 255, 31,
0, 0, 1, 3, 3, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_P[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 12, 28, 248, 240,
255, 255, 12, 12, 12, 12, 12, 12, 12, 6, 7, 3, 1,
7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_Q[] = {192, 240, 120, 28, 12, 14, 6, 6, 6, 6, 14, 12, 28, 120, 240, 192,
63, 255, 224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 128, 224, 127, 31,
0, 0, 1, 3, 3, 6, 6, 30, 62, 118, 98, 99, 193, 193, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_R[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 12, 28, 248, 240, 0,
255, 255, 12, 12, 12, 12, 12, 12, 60, 118, 231, 195, 1, 0,
7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 7, 6};
const char GLYPH_LATIN_CAPITAL_LETTER_S[] = {248, 252, 140, 6, 6, 6, 6, 6, 6, 14, 4, 0, 0, 1, 1, 3, 3, 6,
6, 12, 12, 24, 248, 224, 3, 3, 6, 6, 6, 6, 6, 6, 6, 3, 3, 1};
const char GLYPH_LATIN_CAPITAL_LETTER_T[] = {6, 6, 6, 6, 6, 6, 254, 254, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_U[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 254,
127, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 127,
0, 1, 3, 3, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_V[] = {6, 30, 252, 224, 0, 0, 0, 0, 0, 0, 0, 0, 224, 252, 30, 2,
0, 0, 0, 7, 31, 252, 224, 128, 128, 224, 252, 31, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 3, 7, 7, 3, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_W[] = {
14, 254, 248, 0, 0, 0, 0, 0, 128, 248, 120, 224, 0, 0, 0, 0, 0, 0, 1, 31, 255, 240, 0, 224, 252, 31,
3, 0, 3, 31, 252, 224, 0, 240, 0, 0, 0, 1, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 7, 7};
const char GLYPH_LATIN_CAPITAL_LETTER_X[] = {2, 6, 28, 56, 224, 192, 0, 0, 192, 224, 56, 28, 6, 2,
0, 0, 128, 224, 112, 57, 15, 15, 57, 112, 224, 128, 0, 0,
4, 7, 3, 1, 0, 0, 0, 0, 0, 0, 1, 3, 7, 4};
const char GLYPH_LATIN_CAPITAL_LETTER_Y[] = {2, 14, 60, 240, 192, 0, 0, 0, 0, 192, 240, 60, 14, 2,
0, 0, 0, 0, 1, 7, 254, 254, 7, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_CAPITAL_LETTER_Z[] = {6, 6, 6, 6, 6, 6, 6, 134, 198, 102, 54, 30, 14,
0, 128, 224, 112, 24, 12, 7, 1, 0, 0, 0, 0, 0,
7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
const char GLYPH_LEFT_SQUARE_BRACKET[] = {255, 255, 1, 1, 1, 1, 255, 255, 0, 0, 0, 0, 255, 255, 192, 192, 192, 192};
const char GLYPH_REVERSE_SOLIDUS[] = {7, 31, 248, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 31, 124,
224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 15, 124, 240, 128};
const char GLYPH_RIGHT_SQUARE_BRACKET[] = {1, 1, 1, 1, 255, 255, 0, 0, 0, 0, 255, 255, 192, 192, 192, 192, 255, 255};
const char GLYPH_CIRCUMFLEX_ACCENT[] = {0, 128, 224, 240, 124, 62, 14, 62, 124, 240, 224, 128, 0, 2, 7, 3, 1, 0, 0, 0,
0, 0, 1, 3, 7, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_LOW_LINE[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192};
const char GLYPH_GRAVE_ACCENT[] = {1, 3, 7, 14, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_SMALL_LETTER_A[] = {0, 96, 96, 96, 96, 96, 96, 96, 192, 192, 128, 224, 240, 48, 24, 24, 24,
24, 24, 24, 255, 255, 1, 3, 7, 6, 6, 6, 6, 6, 6, 7, 7};
const char GLYPH_LATIN_SMALL_LETTER_B[] = {255, 255, 192, 96, 96, 96, 96, 96, 192, 192, 128, 0,
255, 255, 0, 0, 0, 0, 0, 0, 0, 193, 255, 126,
3, 7, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_LATIN_SMALL_LETTER_C[] = {0, 128, 192, 192, 96, 96, 96, 96, 96, 96, 126, 255, 129, 0, 0,
0, 0, 0, 0, 0, 0, 1, 3, 3, 6, 6, 6, 6, 6, 7};
const char GLYPH_LATIN_SMALL_LETTER_D[] = {0, 128, 192, 192, 96, 96, 96, 96, 96, 192, 255, 255,
126, 255, 193, 0, 0, 0, 0, 0, 0, 0, 255, 255,
0, 1, 3, 3, 7, 6, 6, 6, 6, 6, 7, 3};
const char GLYPH_LATIN_SMALL_LETTER_E[] = {0, 128, 192, 192, 96, 96, 96, 96, 96, 224, 192, 128, 0,
126, 255, 153, 24, 24, 24, 24, 24, 24, 24, 25, 31, 30,
0, 1, 3, 3, 6, 6, 6, 6, 6, 6, 6, 3, 0};
const char GLYPH_LATIN_SMALL_LETTER_F[] = {252, 255, 99, 97, 97, 97, 97, 1, 255, 255, 0, 0,
0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_SMALL_LETTER_G[] = {0, 128, 192, 192, 96, 96, 96, 96, 96, 96, 224, 192,
62, 255, 193, 128, 0, 0, 0, 0, 0, 128, 255, 255,
0, 96, 193, 193, 195, 195, 195, 195, 227, 113, 63, 31};
const char GLYPH_LATIN_SMALL_LETTER_H[] = {255, 255, 96, 96, 96, 96, 96, 224, 192, 192, 0, 255, 255, 0, 0, 0, 0,
0, 0, 1, 255, 255, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_LATIN_SMALL_LETTER_I[] = {227, 227, 255, 255, 7, 7};
const char GLYPH_LATIN_SMALL_LETTER_J[] = {0, 0, 227, 227, 0, 0, 255, 255, 192, 224, 127, 63};
const char GLYPH_LATIN_SMALL_LETTER_K[] = {255, 255, 0, 0, 0, 0, 128, 192, 96, 32, 32, 0, 255, 255, 24, 60, 54, 115,
225, 192, 128, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 1, 3, 7, 6, 4};
const char GLYPH_LATIN_SMALL_LETTER_L[] = {255, 255, 0, 0, 255, 255, 0, 0, 3, 7, 6, 6};
const char GLYPH_LATIN_SMALL_LETTER_M[] = {192, 224, 96, 96, 96, 96, 96, 224, 192, 192, 64, 96, 96, 96, 96, 224,
255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0,
7, 7, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_SMALL_LETTER_N[] = {192, 224, 96, 96, 96, 96, 96, 224, 192, 192, 0, 255, 255, 0, 0, 0, 0,
0, 0, 1, 255, 255, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_LATIN_SMALL_LETTER_O[] = {0, 128, 192, 192, 96, 96, 96, 96, 96, 192, 192, 128, 0,
126, 255, 129, 0, 0, 0, 0, 0, 0, 0, 131, 255, 126,
0, 1, 3, 3, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_LATIN_SMALL_LETTER_P[] = {192, 224, 96, 96, 96, 96, 96, 224, 192, 192, 128, 0,
255, 255, 0, 0, 0, 0, 0, 0, 0, 131, 255, 126,
255, 255, 3, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_LATIN_SMALL_LETTER_Q[] = {0, 128, 192, 192, 96, 96, 96, 96, 96, 96, 224, 192,
126, 255, 129, 0, 0, 0, 0, 0, 0, 0, 255, 255,
0, 1, 3, 3, 6, 6, 6, 6, 6, 3, 255, 255};
const char GLYPH_LATIN_SMALL_LETTER_R[] = {192, 224, 96, 96, 96, 96, 96, 96, 255, 255, 0, 0,
0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0};
const char GLYPH_LATIN_SMALL_LETTER_S[] = {128, 192, 224, 96, 96, 96, 96, 96, 224, 0, 7, 15, 12, 28, 24,
24, 56, 48, 240, 224, 3, 6, 6, 6, 6, 6, 6, 7, 3, 1};
const char GLYPH_LATIN_SMALL_LETTER_T[] = {254, 254, 96, 96, 96, 96, 96, 0, 255, 255, 0, 0,
0, 0, 0, 0, 1, 3, 7, 6, 6, 6, 6, 3};
const char GLYPH_LATIN_SMALL_LETTER_U[] = {224, 224, 0, 0, 0, 0, 0, 0, 0, 224, 224, 255, 255, 128, 0, 0, 0,
0, 0, 0, 255, 255, 0, 3, 3, 7, 6, 6, 6, 6, 6, 7, 3};
const char GLYPH_LATIN_SMALL_LETTER_V[] = {96, 224, 192, 0, 0, 0, 0, 0, 0, 192, 224, 96, 0, 3, 31, 252, 240, 128,
128, 240, 252, 31, 3, 0, 0, 0, 0, 0, 3, 7, 7, 3, 0, 0, 0, 0};
const char GLYPH_LATIN_SMALL_LETTER_W[] = {
96, 224, 128, 0, 0, 0, 0, 0, 224, 224, 128, 0, 0, 0, 0, 0, 128, 224, 0, 7, 63, 252, 224, 0, 224, 254, 31,
1, 31, 252, 224, 0, 224, 252, 63, 7, 0, 0, 0, 1, 7, 7, 7, 1, 0, 0, 0, 0, 7, 7, 7, 1, 0, 0};
const char GLYPH_LATIN_SMALL_LETTER_X[] = {32, 96, 224, 128, 0, 0, 0, 0, 0, 128, 224, 96, 32,
0, 0, 128, 195, 231, 126, 24, 126, 231, 195, 128, 0, 0,
4, 6, 7, 1, 0, 0, 0, 0, 0, 1, 7, 6, 4};
const char GLYPH_LATIN_SMALL_LETTER_Y[] = {32, 224, 192, 0, 0, 0, 0, 0, 0, 192, 224, 32,
0, 1, 15, 62, 248, 192, 0, 224, 252, 31, 3, 0,
192, 192, 192, 192, 97, 127, 31, 7, 0, 0, 0, 0};
const char GLYPH_LATIN_SMALL_LETTER_Z[] = {96, 96, 96, 96, 96, 96, 96, 224, 224, 224, 0, 128, 224, 112, 56,
12, 7, 3, 1, 0, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6};
const char GLYPH_LEFT_CURLY_BRACKET[] = {0, 0, 254, 255, 3, 1, 1, 24, 60, 247, 227,
0, 0, 0, 0, 0, 63, 127, 224, 192, 192};
const char GLYPH_VERTICAL_LINE[] = {255, 255, 255, 255, 255, 255};
const char GLYPH_RIGHT_CURLY_BRACKET[] = {1, 1, 3, 255, 254, 0, 0, 0, 0, 0, 227,
247, 60, 24, 192, 192, 224, 127, 63, 0, 0};
const char GLYPH_NUMERO_SIGN[] = {254, 254, 28, 56, 224, 192, 0, 0, 0, 0, 0, 0, 254, 254, 255, 255, 0, 0, 0, 1, 7,
14, 56, 112, 192, 128, 255, 255, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 7, 7};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_BE[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 255, 255, 3, 3, 3, 3,
3, 3, 6, 142, 252, 248, 7, 7, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_GHE[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 6, 6, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_DE[] = {
0, 0, 0, 0, 0, 0, 254, 254, 6, 6, 6, 6, 6, 6, 254, 254, 0, 0, 0, 0, 0, 0, 192, 252, 63, 3, 0,
0, 0, 0, 0, 0, 255, 255, 0, 0, 254, 254, 6, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 254, 254};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_IO[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 255, 255, 6, 6, 6, 6,
6, 6, 6, 6, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_ZHE[] = {2, 6, 12, 24, 48, 96, 192, 128, 0, 0, 254, 254, 0, 0, 128, 192, 96,
0, 0, 128, 192, 96, 56, 28, 7, 3, 2, 255, 255, 2, 3, 7, 28, 56,
4, 7, 3, 1, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_ZE[] = {0, 4, 14, 6, 6, 6, 6, 6, 14, 140, 252, 240, 0,
0, 0, 0, 6, 6, 6, 6, 6, 7, 15, 29, 248, 240,
3, 3, 7, 6, 6, 6, 6, 6, 6, 3, 3, 3, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_I[] = {254, 254, 0, 0, 0, 0, 0, 0, 128, 192, 224, 112, 24, 252, 254,
255, 255, 192, 96, 56, 28, 14, 7, 3, 1, 0, 0, 0, 255, 255,
7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_SHORT_I[] = {
254, 254, 0, 0, 0, 0, 0, 0, 128, 192, 224, 112, 24, 252, 254, 255, 255, 192, 96, 56, 28, 14, 7,
3, 1, 0, 0, 0, 255, 255, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_KA[] = {254, 254, 0, 0, 128, 192, 96, 48, 24, 12, 6, 2, 0, 0,
255, 255, 6, 15, 29, 24, 56, 112, 224, 192, 128, 0, 0, 0,
7, 7, 0, 0, 0, 0, 0, 0, 0, 1, 3, 7, 6, 4};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_EL[] = {0, 0, 0, 0, 0, 0, 254, 254, 6, 6, 6, 6, 6, 6, 254, 254,
0, 0, 0, 128, 192, 252, 63, 3, 0, 0, 0, 0, 0, 0, 255, 255,
6, 6, 7, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_PE[] = {254, 254, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 254, 254,
255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_U[] = {2, 14, 60, 240, 192, 0, 0, 0, 0, 0, 224, 252, 30, 2,
0, 0, 0, 0, 3, 15, 188, 240, 124, 31, 3, 0, 0, 0,
0, 6, 6, 6, 6, 7, 3, 1, 0, 0, 0, 0, 0, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_EF[] = {128, 224, 112, 56, 24, 24, 12, 12, 12, 255, 255, 12, 12,
12, 24, 24, 56, 15, 63, 112, 224, 192, 192, 128, 128, 128,
255, 255, 128, 128, 128, 192, 192, 224, 0, 0, 0, 0, 0,
0, 1, 1, 1, 7, 7, 1, 1, 1, 0, 0, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_TSE[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 254, 0, 0,
255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 254, 254};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_CHE[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 254, 254,
3, 15, 14, 28, 24, 24, 24, 24, 24, 24, 255, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_SHA[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 254, 254, 0, 0, 0, 0, 0,
255, 255, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0,
7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_SHCHA[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 254, 254, 0, 0, 0, 0,
255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0,
7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_HARD_SIGN[] = {6, 6, 6, 6, 6, 254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 255, 255, 3, 3, 3, 3, 3, 3, 6, 142, 252, 248,
0, 0, 0, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_YERU[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254,
255, 255, 3, 3, 3, 3, 3, 3, 6, 142, 252, 248, 0, 0, 0, 255,
7, 7, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0, 0, 0, 0, 7};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_SOFT_SIGN[] = {254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255, 255, 3, 3, 3, 3, 3, 3, 6, 142, 252, 248,
7, 7, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_E[] = {12, 12, 6, 6, 6, 6, 6, 6, 12, 28, 120, 240, 192,
0, 0, 0, 6, 6, 6, 6, 6, 6, 134, 230, 255, 63,
3, 7, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0, 0};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_YU[] = {254, 254, 0, 0, 0, 0, 128, 224, 120, 24, 12, 14, 6, 6, 6, 6,
255, 255, 6, 6, 6, 6, 63, 255, 224, 128, 0, 0, 0, 0, 0, 0,
7, 7, 0, 0, 0, 0, 0, 0, 1, 3, 3, 7, 6, 6, 6, 6};
const char GLYPH_CYRILLIC_CAPITAL_LETTER_YA[] = {0, 240, 248, 28, 12, 6, 6, 6, 6, 6, 6, 6, 254, 254,
0, 1, 131, 231, 126, 28, 12, 12, 12, 12, 12, 12, 255, 255,
4, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_BE[] = {192, 248, 220, 198, 102, 99, 99, 99, 97, 193, 193, 129, 0,
127, 255, 192, 0, 0, 0, 0, 0, 0, 0, 129, 255, 126,
0, 1, 3, 3, 6, 6, 6, 6, 6, 3, 3, 1, 0};
const char GLYPH_CYRILLIC_SMALL_LETTER_VE[] = {224, 224, 96, 96, 96, 96, 224, 192, 128, 0, 255, 255, 24, 24, 24,
24, 28, 63, 247, 224, 7, 7, 6, 6, 6, 6, 6, 7, 3, 1};
const char GLYPH_CYRILLIC_SMALL_LETTER_GHE[] = {224, 224, 96, 96, 96, 96, 96, 96, 96, 255, 255, 0, 0, 0,
0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0};
const char GLYPH_CYRILLIC_SMALL_LETTER_DE[] = {0, 0, 0, 0, 0, 224, 224, 96, 96, 96, 96, 224, 224, 0, 0,
0, 0, 0, 128, 240, 255, 15, 0, 0, 0, 0, 255, 255, 0, 0,
126, 126, 6, 7, 7, 6, 6, 6, 6, 6, 6, 7, 7, 126, 126};
const char GLYPH_CYRILLIC_SMALL_LETTER_IO[] = {0, 128, 192, 199, 103, 103, 96, 96, 103, 231, 199, 128, 0,
126, 255, 153, 24, 24, 24, 24, 24, 24, 24, 25, 31, 30,
0, 1, 3, 3, 6, 6, 6, 6, 6, 6, 6, 3, 0};
const char GLYPH_CYRILLIC_SMALL_LETTER_ZHE[] = {
0, 32, 96, 224, 192, 0, 0, 0, 0, 224, 224, 0, 0, 0, 0, 192, 224, 96, 0, 0, 0, 128, 193, 99, 54, 28, 8,
255, 255, 8, 28, 54, 99, 193, 128, 0, 4, 6, 7, 1, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 1, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_ZE[] = {192, 96, 96, 96, 96, 96, 96, 96, 192, 192, 128, 0, 0, 0, 24, 24, 24,
24, 24, 60, 255, 227, 3, 6, 6, 6, 6, 6, 6, 6, 7, 3, 1};
const char GLYPH_CYRILLIC_SMALL_LETTER_I[] = {224, 224, 0, 0, 0, 0, 0, 0, 128, 192, 224, 224,
255, 255, 192, 224, 112, 60, 30, 7, 3, 1, 255, 255,
7, 7, 3, 1, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_SHORT_I[] = {224, 224, 1, 3, 7, 6, 6, 7, 131, 193, 224, 224,
255, 255, 192, 224, 112, 60, 30, 7, 3, 1, 255, 255,
7, 7, 3, 1, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_KA[] = {224, 224, 0, 0, 0, 0, 128, 192, 96, 32, 0, 0,
255, 255, 24, 60, 54, 115, 225, 192, 128, 0, 0, 0,
7, 7, 0, 0, 0, 0, 0, 1, 3, 7, 6, 4};
const char GLYPH_CYRILLIC_SMALL_LETTER_EL[] = {0, 0, 0, 0, 224, 224, 96, 96, 96, 96, 96, 96, 224, 224,
0, 0, 128, 224, 255, 31, 0, 0, 0, 0, 0, 0, 255, 255,
6, 7, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_EM[] = {192, 224, 96, 192, 0, 0, 0, 0, 0, 0, 0, 192, 96, 224, 224,
255, 255, 0, 1, 15, 56, 224, 128, 224, 56, 15, 1, 0, 255, 255,
7, 7, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_EN[] = {224, 224, 0, 0, 0, 0, 0, 0, 0, 224, 224, 255, 255, 24, 24, 24, 24,
24, 24, 24, 255, 255, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_PE[] = {224, 224, 96, 96, 96, 96, 96, 96, 96, 224, 224, 255, 255, 0, 0, 0, 0,
0, 0, 0, 255, 255, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_TE[] = {96, 96, 96, 96, 224, 224, 96, 96, 96, 96, 0, 0, 0, 0, 255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0};
const char GLYPH_CYRILLIC_SMALL_LETTER_EF[] = {
0, 128, 128, 192, 224, 96, 96, 255, 255, 96, 96, 224, 192, 128, 128, 0, 126, 255, 195, 0, 0, 0, 0, 255,
255, 0, 0, 0, 0, 195, 255, 126, 0, 1, 3, 3, 7, 6, 6, 255, 255, 6, 6, 7, 3, 1, 0, 0};
const char GLYPH_CYRILLIC_SMALL_LETTER_TSE[] = {224, 224, 0, 0, 0, 0, 0, 0, 224, 224, 0, 0,
255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
7, 7, 6, 6, 6, 6, 6, 6, 7, 7, 126, 126};
const char GLYPH_CYRILLIC_SMALL_LETTER_CHE[] = {224, 224, 0, 0, 0, 0, 0, 0, 224, 224, 15, 31, 56, 48, 48,
48, 48, 48, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_SHA[] = {224, 224, 0, 0, 0, 0, 0, 0, 224, 224, 0, 0, 0, 0, 0, 224,
255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255,
7, 7, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_SHCHA[] = {224, 224, 0, 0, 0, 0, 0, 0, 224, 224, 0, 0, 0, 0, 0, 224,
255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255,
7, 7, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_HARD_SIGN[] = {96, 96, 96, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 255, 12, 12, 12, 12, 12, 12, 28, 24, 248, 240,
0, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 6, 3, 3, 1};
const char GLYPH_CYRILLIC_SMALL_LETTER_YERU[] = {224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224,
255, 255, 12, 12, 12, 12, 12, 12, 24, 248, 240, 0, 0, 0, 255, 255,
7, 7, 6, 6, 6, 6, 6, 6, 3, 3, 1, 0, 0, 0, 7, 7};
const char GLYPH_CYRILLIC_SMALL_LETTER_SOFT_SIGN[] = {224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 12, 12, 12,
12, 12, 28, 248, 240, 7, 7, 6, 6, 6, 6, 6, 7, 3, 1};
const char GLYPH_CYRILLIC_SMALL_LETTER_E[] = {64, 96, 96, 96, 96, 96, 192, 192, 128, 0, 0, 0, 0, 24, 24,
24, 24, 153, 255, 126, 7, 6, 6, 6, 6, 6, 7, 3, 1, 0};
const char GLYPH_CYRILLIC_SMALL_LETTER_YU[] = {224, 224, 0, 0, 0, 0, 128, 192, 192, 96, 96, 96, 96, 96, 192, 192,
255, 255, 24, 24, 24, 126, 255, 129, 0, 0, 0, 0, 0, 0, 0, 131,
7, 7, 0, 0, 0, 0, 1, 3, 3, 6, 6, 6, 6, 6, 3, 3};
const char GLYPH_CYRILLIC_SMALL_LETTER_YA[] = {0, 128, 192, 192, 96, 96, 96, 96, 96, 224, 224,
0, 15, 159, 248, 112, 48, 48, 48, 48, 255, 255,
4, 7, 3, 0, 0, 0, 0, 0, 0, 7, 7};
const char GLYPH_WIDTH[143] = {
2, 7, 15, 11, 17, 15, 2, 6, 6, 11, 12, 3, 6, 3, 10, 13, 7, 11, 12, 13, 11, 12, 11, 13, 12, 3, 3, 13, 12,
13, 9, 16, 17, 14, 13, 15, 12, 11, 13, 14, 2, 11, 14, 12, 16, 15, 16, 13, 16, 14, 12, 14, 14, 16, 17, 14, 14, 13,
6, 11, 6, 13, 13, 5, 11, 12, 10, 12, 13, 8, 12, 11, 2, 4, 12, 4, 16, 11, 13, 12, 12, 8, 10, 8, 11, 12, 18,
13, 12, 10, 7, 2, 7, 14, 12, 11, 18, 12, 17, 13, 15, 15, 14, 16, 14, 14, 17, 16, 12, 16, 16, 17, 16, 12, 13, 16,
14, 13, 10, 9, 15, 13, 18, 11, 12, 12, 12, 14, 15, 11, 11, 10, 16, 12, 10, 16, 16, 15, 16, 10, 10, 16, 11};
const char *GLYPH_ADDR[143] = {
GLYPH_EXCLAMATION_MARK,
GLYPH_QUOTATION_MARK,
GLYPH_NUMBER_SIGN,
GLYPH_DOLLAR_SIGN,
GLYPH_PERCENT_SIGN,
GLYPH_AMPERSAND,
GLYPH_APOSTROPHE,
GLYPH_LEFT_PARENTHESIS,
GLYPH_RIGHT_PARENTHESIS,
GLYPH_ASTERISK,
GLYPH_PLUS_SIGN,
GLYPH_COMMA,
GLYPH_HYPHEN_MINUS,
GLYPH_FULL_STOP,
GLYPH_SOLIDUS,
GLYPH_DIGIT_ZERO,
GLYPH_DIGIT_ONE,
GLYPH_DIGIT_TWO,
GLYPH_DIGIT_THREE,
GLYPH_DIGIT_FOUR,
GLYPH_DIGIT_FIVE,
GLYPH_DIGIT_SIX,
GLYPH_DIGIT_SEVEN,
GLYPH_DIGIT_EIGHT,
GLYPH_DIGIT_NINE,
GLYPH_COLON,
GLYPH_SEMICOLON,
GLYPH_LESS_THAN_SIGN,
GLYPH_EQUALS_SIGN,
GLYPH_GREATER_THAN_SIGN,
GLYPH_QUESTION_MARK,
GLYPH_COMMERCIAL_AT,
GLYPH_LATIN_CAPITAL_LETTER_A,
GLYPH_LATIN_CAPITAL_LETTER_B,
GLYPH_LATIN_CAPITAL_LETTER_C,
GLYPH_LATIN_CAPITAL_LETTER_D,
GLYPH_LATIN_CAPITAL_LETTER_E,
GLYPH_LATIN_CAPITAL_LETTER_F,
GLYPH_LATIN_CAPITAL_LETTER_G,
GLYPH_LATIN_CAPITAL_LETTER_H,
GLYPH_LATIN_CAPITAL_LETTER_I,
GLYPH_LATIN_CAPITAL_LETTER_J,
GLYPH_LATIN_CAPITAL_LETTER_K,
GLYPH_LATIN_CAPITAL_LETTER_L,
GLYPH_LATIN_CAPITAL_LETTER_M,
GLYPH_LATIN_CAPITAL_LETTER_N,
GLYPH_LATIN_CAPITAL_LETTER_O,
GLYPH_LATIN_CAPITAL_LETTER_P,
GLYPH_LATIN_CAPITAL_LETTER_Q,
GLYPH_LATIN_CAPITAL_LETTER_R,
GLYPH_LATIN_CAPITAL_LETTER_S,
GLYPH_LATIN_CAPITAL_LETTER_T,
GLYPH_LATIN_CAPITAL_LETTER_U,
GLYPH_LATIN_CAPITAL_LETTER_V,
GLYPH_LATIN_CAPITAL_LETTER_W,
GLYPH_LATIN_CAPITAL_LETTER_X,
GLYPH_LATIN_CAPITAL_LETTER_Y,
GLYPH_LATIN_CAPITAL_LETTER_Z,
GLYPH_LEFT_SQUARE_BRACKET,
GLYPH_REVERSE_SOLIDUS,
GLYPH_RIGHT_SQUARE_BRACKET,
GLYPH_CIRCUMFLEX_ACCENT,
GLYPH_LOW_LINE,
GLYPH_GRAVE_ACCENT,
GLYPH_LATIN_SMALL_LETTER_A,
GLYPH_LATIN_SMALL_LETTER_B,
GLYPH_LATIN_SMALL_LETTER_C,
GLYPH_LATIN_SMALL_LETTER_D,
GLYPH_LATIN_SMALL_LETTER_E,
GLYPH_LATIN_SMALL_LETTER_F,
GLYPH_LATIN_SMALL_LETTER_G,
GLYPH_LATIN_SMALL_LETTER_H,
GLYPH_LATIN_SMALL_LETTER_I,
GLYPH_LATIN_SMALL_LETTER_J,
GLYPH_LATIN_SMALL_LETTER_K,
GLYPH_LATIN_SMALL_LETTER_L,
GLYPH_LATIN_SMALL_LETTER_M,
GLYPH_LATIN_SMALL_LETTER_N,
GLYPH_LATIN_SMALL_LETTER_O,
GLYPH_LATIN_SMALL_LETTER_P,
GLYPH_LATIN_SMALL_LETTER_Q,
GLYPH_LATIN_SMALL_LETTER_R,
GLYPH_LATIN_SMALL_LETTER_S,
GLYPH_LATIN_SMALL_LETTER_T,
GLYPH_LATIN_SMALL_LETTER_U,
GLYPH_LATIN_SMALL_LETTER_V,
GLYPH_LATIN_SMALL_LETTER_W,
GLYPH_LATIN_SMALL_LETTER_X,
GLYPH_LATIN_SMALL_LETTER_Y,
GLYPH_LATIN_SMALL_LETTER_Z,
GLYPH_LEFT_CURLY_BRACKET,
GLYPH_VERTICAL_LINE,
GLYPH_RIGHT_CURLY_BRACKET,
GLYPH_NUMERO_SIGN,
GLYPH_CYRILLIC_CAPITAL_LETTER_BE,
GLYPH_CYRILLIC_CAPITAL_LETTER_GHE,
GLYPH_CYRILLIC_CAPITAL_LETTER_DE,
GLYPH_CYRILLIC_CAPITAL_LETTER_IO,
GLYPH_CYRILLIC_CAPITAL_LETTER_ZHE,
GLYPH_CYRILLIC_CAPITAL_LETTER_ZE,
GLYPH_CYRILLIC_CAPITAL_LETTER_I,
GLYPH_CYRILLIC_CAPITAL_LETTER_SHORT_I,
GLYPH_CYRILLIC_CAPITAL_LETTER_KA,
GLYPH_CYRILLIC_CAPITAL_LETTER_EL,
GLYPH_CYRILLIC_CAPITAL_LETTER_PE,
GLYPH_CYRILLIC_CAPITAL_LETTER_U,
GLYPH_CYRILLIC_CAPITAL_LETTER_EF,
GLYPH_CYRILLIC_CAPITAL_LETTER_TSE,
GLYPH_CYRILLIC_CAPITAL_LETTER_CHE,
GLYPH_CYRILLIC_CAPITAL_LETTER_SHA,
GLYPH_CYRILLIC_CAPITAL_LETTER_SHCHA,
GLYPH_CYRILLIC_CAPITAL_LETTER_HARD_SIGN,
GLYPH_CYRILLIC_CAPITAL_LETTER_YERU,
GLYPH_CYRILLIC_CAPITAL_LETTER_SOFT_SIGN,
GLYPH_CYRILLIC_CAPITAL_LETTER_E,
GLYPH_CYRILLIC_CAPITAL_LETTER_YU,
GLYPH_CYRILLIC_CAPITAL_LETTER_YA,
GLYPH_CYRILLIC_SMALL_LETTER_BE,
GLYPH_CYRILLIC_SMALL_LETTER_VE,
GLYPH_CYRILLIC_SMALL_LETTER_GHE,
GLYPH_CYRILLIC_SMALL_LETTER_DE,
GLYPH_CYRILLIC_SMALL_LETTER_IO,
GLYPH_CYRILLIC_SMALL_LETTER_ZHE,
GLYPH_CYRILLIC_SMALL_LETTER_ZE,
GLYPH_CYRILLIC_SMALL_LETTER_I,
GLYPH_CYRILLIC_SMALL_LETTER_SHORT_I,
GLYPH_CYRILLIC_SMALL_LETTER_KA,
GLYPH_CYRILLIC_SMALL_LETTER_EL,
GLYPH_CYRILLIC_SMALL_LETTER_EM,
GLYPH_CYRILLIC_SMALL_LETTER_EN,
GLYPH_CYRILLIC_SMALL_LETTER_PE,
GLYPH_CYRILLIC_SMALL_LETTER_TE,
GLYPH_CYRILLIC_SMALL_LETTER_EF,
GLYPH_CYRILLIC_SMALL_LETTER_TSE,
GLYPH_CYRILLIC_SMALL_LETTER_CHE,
GLYPH_CYRILLIC_SMALL_LETTER_SHA,
GLYPH_CYRILLIC_SMALL_LETTER_SHCHA,
GLYPH_CYRILLIC_SMALL_LETTER_HARD_SIGN,
GLYPH_CYRILLIC_SMALL_LETTER_YERU,
GLYPH_CYRILLIC_SMALL_LETTER_SOFT_SIGN,
GLYPH_CYRILLIC_SMALL_LETTER_E,
GLYPH_CYRILLIC_SMALL_LETTER_YU,
GLYPH_CYRILLIC_SMALL_LETTER_YA,
};
inline int glyph_index_for_code_point(int codePoint) {
const int cyrillicTable[] = {32, 94, 33, 95, 96, 36, 98, 99, 100, 101, 102, 103, 44, 39, 46, 104,
47, 34, 51, 105, 106, 55, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
64, 117, 118, 119, 120, 68, 122, 123, 124, 125, 126, 127, 128, 129, 78, 130,
79, 66, 131, 88, 132, 87, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142};
if (codePoint < 32)
return -1;
else if (codePoint < 126)
return codePoint - 33;
else if (codePoint < 0x401)
return -1;
else if (codePoint == 0x401)
return 97;
else if (codePoint < 0x410)
return -1;
else if (codePoint < 0x450)
return cyrillicTable[codePoint - 0x410];
else
return -1;
}

225
graphics.c Normal file
View File

@@ -0,0 +1,225 @@
#include "graphics.h"
#include "font_ubuntu.h"
#include "utf8.h"
#include <pico/stdlib.h>
void lcd_draw_horizontal_digit_segment(uint16_t x, uint16_t y, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
uint16_t y1 = y + 8;
uint16_t x0 = x + 7;
uint16_t w = 32;
for (int i = y; i < y1; ++i, w += 2, --x0) {
_draw_h_strip(x0, i, w, color);
}
x0 += 2;
w -= 4;
y = y1;
y1 = y + 7;
for (int i = y; i < y1; ++i, w -= 2, ++x0) {
_draw_h_strip(x0, i, w, color);
}
}
void lcd_draw_vertical_digit_segment(uint16_t x, uint16_t y, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
uint16_t x1 = x + 8;
uint16_t y0 = y + 7;
uint16_t h = 32;
for (int i = x; i < x1; ++i, h += 2, --y0) {
_draw_v_strip(i, y0, h, color);
}
y0 += 2;
h -= 4;
x = x1;
x1 = x + 7;
for (int i = x; i < x1; ++i, h -= 2, ++y0) {
_draw_v_strip(i, y0, h, color);
}
}
void lcd_draw_horizontal_small_digit_segment(uint16_t x, uint16_t y, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
uint16_t y1 = y + 6;
uint16_t x0 = x + 5;
uint16_t w = 24;
for (int i = y; i < y1; ++i, w += 2, --x0) {
_draw_h_strip(x0, i, w, color);
}
x0 += 2;
w -= 4;
y = y1;
y1 = y + 5;
for (int i = y; i < y1; ++i, w -= 2, ++x0) {
_draw_h_strip(x0, i, w, color);
}
}
void lcd_draw_vertical_small_digit_segment(uint16_t x, uint16_t y, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
uint16_t x1 = x + 6;
uint16_t y0 = y + 5;
uint16_t h = 24;
for (int i = x; i < x1; ++i, h += 2, --y0) {
_draw_v_strip(i, y0, h, color);
}
y0 += 2;
h -= 4;
x = x1;
x1 = x + 5;
for (int i = x; i < x1; ++i, h -= 2, ++y0) {
_draw_v_strip(i, y0, h, color);
}
}
void lcd_draw_horizontal_xsmall_digit_segment(uint16_t x, uint16_t y, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
uint16_t y1 = y + 4;
uint16_t x0 = x + 3;
uint16_t w = 16;
for (int i = y; i < y1; ++i, w += 2, --x0) {
_draw_h_strip(x0, i, w, color);
}
x0 += 2;
w -= 4;
y = y1;
y1 = y + 3;
for (int i = y; i < y1; ++i, w -= 2, ++x0) {
_draw_h_strip(x0, i, w, color);
}
}
void lcd_draw_vertical_xsmall_digit_segment(uint16_t x, uint16_t y, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
uint16_t x1 = x + 4;
uint16_t y0 = y + 3;
uint16_t h = 16;
for (int i = x; i < x1; ++i, h += 2, --y0) {
_draw_v_strip(i, y0, h, color);
}
y0 += 2;
h -= 4;
x = x1;
x1 = x + 3;
for (int i = x; i < x1; ++i, h -= 2, ++y0) {
_draw_v_strip(i, y0, h, color);
}
}
const uint8_t DIGIT_MASKS[10] = {0b1110111, 0b0100100, 0b1011101, 0b1101101, 0b0101110,
0b1101011, 0b1111011, 0b0100101, 0b1111111, 0b1101111};
void lcd_draw_digit(uint8_t digit, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor) {
const uint16_t SEGMENT_OFFSETS[7][2] = {{9, 0}, {0, 9}, {49, 9}, {9, 49}, {0, 58}, {49, 58}, {9, 99}};
for (int seg = 0; seg < 7; ++seg) {
uint16_t segColor = ((DIGIT_MASKS[digit] >> seg) & 1) ? color : bgColor;
if (seg == 0 || seg == 3 || seg == 6)
lcd_draw_horizontal_digit_segment(x + SEGMENT_OFFSETS[seg][0], y + SEGMENT_OFFSETS[seg][1], segColor);
else
lcd_draw_vertical_digit_segment(x + SEGMENT_OFFSETS[seg][0], y + SEGMENT_OFFSETS[seg][1], segColor);
}
}
void lcd_draw_small_digit(uint8_t digit, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor) {
const uint16_t SEGMENT_OFFSETS[7][2] = {{7, 0}, {0, 7}, {37, 7}, {7, 37}, {0, 44}, {37, 44}, {7, 74}};
for (int seg = 0; seg < 7; ++seg) {
uint16_t segColor = ((DIGIT_MASKS[digit] >> seg) & 1) ? color : bgColor;
if (seg == 0 || seg == 3 || seg == 6)
lcd_draw_horizontal_small_digit_segment(x + SEGMENT_OFFSETS[seg][0], y + SEGMENT_OFFSETS[seg][1], segColor);
else
lcd_draw_vertical_small_digit_segment(x + SEGMENT_OFFSETS[seg][0], y + SEGMENT_OFFSETS[seg][1], segColor);
}
}
void lcd_draw_xsmall_digit(uint8_t digit, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor) {
const uint16_t SEGMENT_OFFSETS[7][2] = {{4, 0}, {0, 4}, {23, 4}, {4, 23}, {0, 27}, {23, 27}, {4, 46}};
for (int seg = 0; seg < 7; ++seg) {
uint16_t segColor = ((DIGIT_MASKS[digit] >> seg) & 1) ? color : bgColor;
if (seg == 0 || seg == 3 || seg == 6)
lcd_draw_horizontal_xsmall_digit_segment(x + SEGMENT_OFFSETS[seg][0], y + SEGMENT_OFFSETS[seg][1], segColor);
else
lcd_draw_vertical_xsmall_digit_segment(x + SEGMENT_OFFSETS[seg][0], y + SEGMENT_OFFSETS[seg][1], segColor);
}
}
void lcd_draw_decimal_point(uint16_t x, uint16_t y, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
for (int i = 0; i < 5; ++i) {
_draw_h_strip(x + 4 - i, y + i, 8 + i, color);
}
for (int i = 5; i < 9; ++i) {
_draw_h_strip(x, y + i, 12, color);
}
for (int i = 9; i < 13; ++i) {
_draw_h_strip(x, y + i, 20 - i, color);
}
}
void lcd_write_string(const char *string, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
static uint16_t glyphPixels[24 * 24];
while (*string) {
int character = decode_code_point(&string);
if (character != 32) {
int glyphIndex = glyph_index_for_code_point(character);
const uint8_t *glyph;
int8_t glyphWidth;
if (glyphIndex != -1) {
glyph = GLYPH_ADDR[glyphIndex];
glyphWidth = GLYPH_WIDTH[glyphIndex];
} else {
glyph = GLYPH_MISSING;
glyphWidth = 12;
}
int row = 0, col = 0;
for (int byte = 0; byte < glyphWidth * 3; ++byte) {
int rem = glyph[byte];
for (int bit = 0; bit < 8; ++bit) {
glyphPixels[(row * 8 + bit) * (glyphWidth + LETTER_SPACING) + col] = (rem & 1) ? color : bgColor;
rem >>= 1;
}
if (++col == glyphWidth) {
col = 0;
++row;
}
}
for (int j = 0; j < 24; ++j) {
for (int i = 0; i < LETTER_SPACING; ++i)
glyphPixels[j * (glyphWidth + LETTER_SPACING) + glyphWidth + i] = 0;
}
st7789_caset(x, x + glyphWidth + LETTER_SPACING - 1);
st7789_raset(y, y + 24 - 1);
st7789_write(glyphPixels, 24 * (glyphWidth + LETTER_SPACING) * sizeof(uint16_t));
x += glyphWidth + LETTER_SPACING;
} else {
lcd_fill_rectangle(x, y, 12, 24, bgColor);
x += 12;
}
}
}
int lcd_string_width(const char *string) {
int width = 0;
while (*string) {
int character = decode_code_point(&string);
if (character == 32) {
width += 12;
continue;
} else {
int glyphIndex = glyph_index_for_code_point(character);
if (glyphIndex != -1) {
width += GLYPH_WIDTH[glyphIndex] + 1;
} else {
width += 12;
}
}
}
return width;
}

62
graphics.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef VELOMETER_GRAPHICS_H
#define VELOMETER_GRAPHICS_H
#include "st7789.h"
#define LCD_OFFSET_X 2
#define LCD_OFFSET_Y 34
#define LCD_WIDTH 320
#define LCD_HEIGHT 172
#define LETTER_SPACING 2
static inline void _set_pixel(uint16_t x, uint16_t y, uint16_t color) {
st7789_set_cursor(x, y);
st7789_put(color);
}
static inline void _draw_h_strip(uint16_t x, uint16_t y, uint16_t len, uint16_t color) {
st7789_caset(x, x + len - 1);
st7789_raset(y, y);
while (len--) {
st7789_put(color);
}
}
static inline void _draw_v_strip(uint16_t x, uint16_t y, uint16_t len, uint16_t color) {
st7789_caset(x, x);
st7789_raset(y, y + len - 1);
while (len--) {
st7789_put(color);
}
}
inline void lcd_set_pixel(uint16_t x, uint16_t y, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
st7789_set_cursor(x, y);
st7789_put(color);
}
inline void lcd_fill_rectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
x += LCD_OFFSET_X;
y += LCD_OFFSET_Y;
st7789_caset(x, x + w - 1);
st7789_raset(y, y + h - 1);
for (uint16_t i = 0; i < w * h; ++i) {
st7789_put(color);
}
}
void lcd_draw_horizontal_digit_segment(uint16_t x, uint16_t y, uint16_t color);
void lcd_draw_vertical_digit_segment(uint16_t x, uint16_t y, uint16_t color);
void lcd_draw_horizontal_small_digit_segment(uint16_t x, uint16_t y, uint16_t color);
void lcd_draw_vertical_small_digit_segment(uint16_t x, uint16_t y, uint16_t color);
void lcd_draw_horizontal_xsmall_digit_segment(uint16_t x, uint16_t y, uint16_t color);
void lcd_draw_vertical_xsmall_digit_segment(uint16_t x, uint16_t y, uint16_t color);
void lcd_draw_digit(uint8_t digit, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor);
void lcd_draw_small_digit(uint8_t digit, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor);
void lcd_draw_xsmall_digit(uint8_t digit, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor);
void lcd_draw_decimal_point(uint16_t x, uint16_t y, uint16_t color);
void lcd_write_string(const char *string, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor);
int lcd_string_width(const char *string);
#endif

589
main.c Normal file
View File

@@ -0,0 +1,589 @@
#include <boot/picobin.h>
#include <hardware/flash.h>
#include <hardware/gpio.h>
#include <hardware/irq.h>
#include <hardware/psram.h>
#include <hardware/pwm.h>
#include <hardware/regs/intctrl.h>
#include <pico/bootrom.h>
#include <pico/critical_section.h>
#include <pico/stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "beep.h"
#include "ds1302.h"
#include "graphics.h"
#include "pins.h"
#include "st7789.h"
#define LOGGING
#define HALL_SENSOR_DEBOUNCE_DELAY_us 10000
#define BUTTON_DEBOUNCE_DELAY_us 3000
#define BUTTON_HOLD_TIME_ms 1000
#define TICKS_PER_REVOLUTION 6
#define SPEED_MEASUREMENT_INTERVAL_ms 100
#define SPEED_MEASUREMENT_WINDOW_SIZE 16
#define SPEED_MEASUREMENT_WINDOW_INTERVAL (SPEED_MEASUREMENT_INTERVAL_ms * SPEED_MEASUREMENT_WINDOW_SIZE)
enum GUIScreen {
MAIN_SCREEN,
SETTINGS_SCREEN,
};
enum DisplayMode {
DISPLAY_MODE_SPEED,
DISPLAY_MODE_DISTANCE,
};
struct GUIState {
uint screen;
union {
struct {
uint displayMode;
bool displayModeChanged;
} mainScreen;
struct {
//...
} settingsScreen;
} screenState;
};
void on_gpio_interrupt(uint pin, uint32_t eventMask);
int64_t hall_sensor_debounce_callback(alarm_id_t alarmId, void *userData);
int64_t button_debounce_callback(alarm_id_t alarmId, void *userData);
int64_t button_hold_callback(alarm_id_t alarmId, void *userData);
bool speed_measure_timer_callback(repeating_timer_t *rt);
void on_button_short_press(uint button);
void on_button_long_press(uint button);
void update_lcd();
void gui_init(struct GUIState *state);
void init_button_states();
void save_to_rtc();
bool load_from_rtc();
void reset();
const uint8_t FRONT_BUTTON_PINS[4] = {PIN_BUTTON_0, PIN_BUTTON_1, PIN_BUTTON_2, PIN_BUTTON_3};
const uint16_t BRIGHTNESS_CONVERT_TABLE_X[] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
const uint16_t BRIGHTNESS_CONVERT_TABLE_Y[] = {0, 7, 12, 21, 37, 64, 111, 194, 338, 588, 1024};
const size_t BRIGHTNESS_CONVERT_LABLE_LEN = 11;
const uint millimetersPerRevolution = 1540;
const uint cmphPerTpw = 360000 * millimetersPerRevolution / SPEED_MEASUREMENT_WINDOW_INTERVAL /
TICKS_PER_REVOLUTION; // cm/hour per Ticks/window
const uint cmPerTick = millimetersPerRevolution / TICKS_PER_REVOLUTION / 10; // cm per tick
int16_t convert_brightness(int16_t input) {
for (int i = 0; i < BRIGHTNESS_CONVERT_LABLE_LEN - 1; ++i) {
if (input < BRIGHTNESS_CONVERT_TABLE_X[i + 1]) {
int16_t x0 = BRIGHTNESS_CONVERT_TABLE_X[i];
int16_t x1 = BRIGHTNESS_CONVERT_TABLE_X[i + 1];
int16_t y0 = BRIGHTNESS_CONVERT_TABLE_Y[i];
int16_t y1 = BRIGHTNESS_CONVERT_TABLE_Y[i + 1];
return y0 + (y1 - y0) * (input - x0) / (x1 - x0);
}
}
return -1;
}
// for debouncing
alarm_id_t hallSensorDebounceAlarm = 0;
bool hallSensorLastState = 0;
bool hallSensorPrevState = 0;
struct {
alarm_id_t debounceAlarm, holdAlarm;
bool currentState, prevState, isHeld;
} buttonStates[4];
// for debug (and for the future)
absolute_time_t lastWheelTickTime = 0;
// for speed measurement
uint8_t intervalWheelTicks[SPEED_MEASUREMENT_WINDOW_SIZE];
uint8_t currentInterval = 0;
uint16_t ticksPerWindow = 0;
// critical_section_t intervalWheelTicksCritSec;
repeating_timer_t speedMeasureTimer;
uint totalTicks = 0;
DateTime currentDatetime;
// unused currently
// repeating_timer_t audioPlaybackTimer;
struct GUIState guiState;
const uint16_t *find_splash_image_offset(const uint16_t *start) {
const uint16_t marker[3] = {0x6f66, 0x626f, 0x7261};
for (int offs = 0; offs < 2 * 1024 * 1024; ++offs) {
if (start[offs + 0] == marker[0] && start[offs + 1] == marker[1] && start[offs + 2] == marker[2]) {
return start + offs;
}
}
return NULL;
}
int main() {
#ifdef LOGGING
stdio_usb_init();
#endif
// display init
const struct st7789_config displayConfig = {
.spi = PICO_DEFAULT_SPI_INSTANCE(),
.gpio_din = PIN_LCD_SDA,
.gpio_clk = PIN_LCD_SCL,
.gpio_cs = PIN_LCD_CS,
.gpio_dc = PIN_LCD_DATA_COMMAND,
.gpio_rst = PIN_LCD_RESET,
.gpio_bl = PIN_LCD_BACKLIGHT,
};
st7789_init(&displayConfig, LCD_WIDTH + LCD_OFFSET_X, LCD_HEIGHT + LCD_OFFSET_Y);
// splash image (just for fun)
const uint16_t *splashImage = (const uint16_t *)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + 1581056);
if (splashImage) {
st7789_caset(LCD_OFFSET_X, LCD_OFFSET_X + 317);
st7789_raset(LCD_OFFSET_Y, LCD_OFFSET_Y + 171);
st7789_write(splashImage, 318 * 172 * sizeof(uint16_t));
} else {
rom_reset_usb_boot(0, 0);
}
sleep_ms(1000);
#ifdef LOGGING
if (psram_is_available()) {
printf("PSRAM available. detecting size... ");
size_t psramSize = psram_detect_size();
printf("%u\n", psramSize);
}
#endif
// critical_section_init(&intervalWheelTicksCritSec);
add_repeating_timer_ms(SPEED_MEASUREMENT_INTERVAL_ms, speed_measure_timer_callback, NULL, &speedMeasureTimer);
gpio_set_irq_callback(on_gpio_interrupt);
gpio_init(PIN_BEEPER);
gpio_set_dir(PIN_BEEPER, GPIO_OUT);
// hall sensor
gpio_init(PIN_MAIN_HALL_SENSOR);
gpio_pull_up(PIN_MAIN_HALL_SENSOR);
gpio_set_irq_enabled(PIN_MAIN_HALL_SENSOR, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
// on-board LED
gpio_init(25);
gpio_set_dir(25, true);
// front buttons
init_button_states();
gpio_init_mask(0b1111 << 15);
for (int pin = 15; pin <= 18; ++pin) {
gpio_pull_up(pin);
gpio_set_irq_enabled(pin, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
}
irq_set_enabled(IO_IRQ_BANK0, 1);
ds1302_init();
DateTime tmp = {26, 9, 9, 15, 10, 0, DOW_WED};
// ds1302_set_datetime(&tmp);
load_from_rtc();
gpio_set_function(PIN_LCD_BACKLIGHT, GPIO_FUNC_PWM);
pwm_set_wrap(BACKLIGHT_PWM_SLICE, 1024);
pwm_set_chan_level(BACKLIGHT_PWM_SLICE, BACKLIGHT_PWM_CHANNEL, 1024);
pwm_set_enabled(BACKLIGHT_PWM_SLICE, true);
gpio_set_function(7, GPIO_FUNC_PWM);
pwm_set_wrap(3, 256);
pwm_set_chan_level(3, PWM_CHAN_B, 0);
pwm_set_enabled(3, true);
gui_init(&guiState);
absolute_time_t nextLcdUpdateTime = delayed_by_ms(get_absolute_time(), 50);
absolute_time_t nextClockUpdateTime = get_absolute_time();
absolute_time_t nextSaveToRtcTime = delayed_by_ms(get_absolute_time(), 10000);
while (true) {
absolute_time_t t = get_absolute_time();
if (t >= nextClockUpdateTime) {
nextClockUpdateTime = delayed_by_ms(t, 1000);
ds1302_get_datetime(&currentDatetime);
printf("time is %i %i %i %i %i %i %i\n", currentDatetime.year, currentDatetime.month, currentDatetime.day,
currentDatetime.dow, currentDatetime.hour, currentDatetime.minute, currentDatetime.second);
}
if (t >= nextLcdUpdateTime) {
nextLcdUpdateTime = delayed_by_ms(t, 50);
update_lcd();
if (gpio_get(PIN_BUTTON_0) == 0 && gpio_get(PIN_BUTTON_3) == 0) {
rom_reset_usb_boot(0, 0);
}
}
if (t >= nextSaveToRtcTime) {
nextSaveToRtcTime = delayed_by_ms(t, 10000);
save_to_rtc();
}
sleep_until(nextLcdUpdateTime);
}
return 0;
}
void on_gpio_interrupt(uint pin, uint32_t eventMask) {
if (pin == PIN_MAIN_HALL_SENSOR) {
if (eventMask & GPIO_IRQ_EDGE_RISE)
hallSensorLastState = 0;
if (eventMask & GPIO_IRQ_EDGE_FALL)
hallSensorLastState = 1;
if (hallSensorDebounceAlarm)
cancel_alarm(hallSensorDebounceAlarm);
hallSensorDebounceAlarm =
add_alarm_in_us(HALL_SENSOR_DEBOUNCE_DELAY_us, hall_sensor_debounce_callback, NULL, false);
gpio_acknowledge_irq(pin, eventMask);
} else if (pin == PIN_BUTTON_0 || pin == PIN_BUTTON_1 || pin == PIN_BUTTON_2 || pin == PIN_BUTTON_3) {
uint buttonIndex;
switch (pin) {
case PIN_BUTTON_0:
buttonIndex = 0;
break;
case PIN_BUTTON_1:
buttonIndex = 1;
break;
case PIN_BUTTON_2:
buttonIndex = 2;
break;
case PIN_BUTTON_3:
buttonIndex = 3;
}
auto buttonState = &buttonStates[buttonIndex];
// if (eventMask & GPIO_IRQ_EDGE_RISE)
// buttonState->currentState = 0;
// if (eventMask & GPIO_IRQ_EDGE_FALL)
// buttonState->currentState = 1;
// buttonState->currentState = !gpio_get(pin);
if (buttonState->debounceAlarm != -1)
cancel_alarm(buttonState->debounceAlarm);
buttonState->debounceAlarm =
add_alarm_in_us(BUTTON_DEBOUNCE_DELAY_us, button_debounce_callback, (void *)buttonIndex, false);
gpio_acknowledge_irq(pin, eventMask);
}
}
int64_t hall_sensor_debounce_callback(alarm_id_t alarmId, void *userData) {
(void)alarmId;
(void)userData;
if (hallSensorLastState != hallSensorPrevState) {
absolute_time_t now = get_absolute_time();
lastWheelTickTime = now;
++intervalWheelTicks[currentInterval];
++totalTicks;
gpio_put(25, !gpio_get(25));
hallSensorPrevState = hallSensorLastState;
}
return 0;
}
int64_t button_debounce_callback(alarm_id_t alarmId, void *userData) {
(void)alarmId;
uint buttonIndex = (uint)userData;
auto buttonState = &buttonStates[buttonIndex];
bool currentState = !gpio_get(FRONT_BUTTON_PINS[buttonIndex]);
if (currentState != buttonState->prevState) {
buttonState->prevState = currentState;
if (buttonState->holdAlarm != -1) {
cancel_alarm(buttonState->holdAlarm);
}
if (currentState == 1) {
buttonState->holdAlarm = add_alarm_in_ms(BUTTON_HOLD_TIME_ms, button_hold_callback, (void *)buttonIndex, false);
} else {
buttonState->holdAlarm = -1;
if (!buttonState->isHeld)
on_button_short_press(buttonIndex);
else
buttonState->isHeld = false;
}
}
return 0;
}
int64_t button_hold_callback(alarm_id_t alarmId, void *userData) {
(void)alarmId;
uint buttonIndex = (uint)userData;
auto buttonState = &buttonStates[buttonIndex];
buttonState->holdAlarm = -1;
buttonState->isHeld = true;
on_button_long_press(buttonIndex);
return 0;
}
void on_button_short_press(uint button) {
// beep(60, 900);
#ifdef LOGGING
printf("button %i short press\n", button);
#endif
if (button == 3 && guiState.screen == MAIN_SCREEN) {
uint oldDispMode = guiState.screenState.mainScreen.displayMode;
if (oldDispMode == DISPLAY_MODE_SPEED)
guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE;
else
guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_SPEED;
guiState.screenState.mainScreen.displayModeChanged = true;
}
}
void on_button_long_press(uint button) {
// beep(60, 1273);
#ifdef LOGGING
printf("button %i long press\n");
#endif
if (button == 2 && buttonStates[3].isHeld ||
button == 3 && buttonStates[2].isHeld) { // if buttons 2 & 3 pressed and held simultaneously...
reset();
}
}
bool speed_measure_timer_callback(repeating_timer_t *rt) {
(void)rt;
uint16_t sum = 0;
/*
printf("window: ");
for (int i = 0; i < 16; ++i) {
sum += intervalWheelTicks[i];
if (currentInterval == i)
printf("[%i] ", intervalWheelTicks[i]);
else
printf("%i ", intervalWheelTicks[i]);
}
printf("\n");
*/
for (int i = 0; i < 16; ++i) {
sum += intervalWheelTicks[i];
}
currentInterval = (currentInterval + 1) % 16;
intervalWheelTicks[currentInterval] = 0;
ticksPerWindow = sum;
// printf("Total %i \r", sum);
return true;
}
// speed is in "2.1" decimal fixed-point, in km/h
void draw_speed(uint value) {
#define DIGITS_START_X 20
#define DIGITS_START_Y 37
#define DIGIT_WIDTH 64
#define DIGIT_SPACING 8
if (value > 999)
value = 999;
uint16_t color;
if (value >= 300)
color = 0xfd04;
else if (value >= 200)
color = 0xfe84;
else if (value >= 150)
color = 0xdfe4;
else if (value >= 100)
color = 0x6fe4;
else if (value >= 50)
color = 0x27fb;
else
color = 0x04ff;
lcd_draw_digit(value / 100, DIGITS_START_X, DIGITS_START_Y, color, 0x0000);
lcd_draw_digit((value / 10) % 10, DIGITS_START_X + DIGIT_WIDTH + DIGIT_SPACING, DIGITS_START_Y, color, 0x0000);
lcd_draw_decimal_point(DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING, DIGITS_START_Y + 101, color);
lcd_draw_digit(value % 10, DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + 5, DIGITS_START_Y, color, 0x0000);
#undef DIGITS_START_X
#undef DIGITS_START_Y
#undef DIGIT_WIDTH
#undef DIGIT_SPACING
}
// distance is in "2.3" decimal fixed-point, in kilometers
void draw_distance(uint value) {
#define DIGITS_START_X 20
#define DIGITS_START_Y 37
#define SMALL_DIGITS_Y_OFFSET 28
#define XSMALL_DIGITS_Y_OFFSET 28
#define DIGIT_WIDTH 64
#define SMALL_DIGIT_WIDTH 48
#define XSMALL_DIGIT_WIDTH 30
#define DIGIT_SPACING 8
#define SMALL_DIGIT_SPACING 6
#define XSMALL_DIGIT_SPACING 4
if (value > 99999)
value = 99999;
lcd_draw_digit(value / 10000, DIGITS_START_X, DIGITS_START_Y, 0xffff, 0x0000);
lcd_draw_digit((value / 1000) % 10, DIGITS_START_X + DIGIT_WIDTH + DIGIT_SPACING, DIGITS_START_Y, 0xffff, 0x0000);
lcd_draw_decimal_point(DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING, DIGITS_START_Y + 101, 0xffff);
lcd_draw_small_digit((value / 100) % 10, DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + 10,
DIGITS_START_Y + SMALL_DIGITS_Y_OFFSET, 0xffff, 0x0000);
lcd_draw_small_digit((value / 10) % 10,
DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + SMALL_DIGIT_WIDTH + SMALL_DIGIT_SPACING +
10,
DIGITS_START_Y + SMALL_DIGITS_Y_OFFSET, 0xffff, 0x0000);
lcd_draw_xsmall_digit(value % 10,
DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + SMALL_DIGIT_WIDTH * 2 +
SMALL_DIGIT_SPACING + XSMALL_DIGIT_SPACING + 10,
DIGITS_START_Y + XSMALL_DIGITS_Y_OFFSET, 0xffff, 0x0000);
#undef DIGITS_START_X
#undef DIGITS_START_Y
#undef DIGIT_WIDTH
#undef DIGIT_SPACING
}
void update_lcd() {
if (guiState.screen == MAIN_SCREEN) {
if (guiState.screenState.mainScreen.displayModeChanged) {
// lcd_fill_rectangle(49, 27, 213, 113, 0x0000);
st7789_fill(0x0000);
guiState.screenState.mainScreen.displayModeChanged = false;
}
if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_SPEED) {
uint convertedSpeed = ticksPerWindow * cmphPerTpw / 10000;
draw_speed(convertedSpeed);
lcd_write_string("Скорость", 20, 0, 0xffff, 0x0000);
lcd_write_string("км/ч", 241, 126, 0xffff, 0x0000);
} else if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_DISTANCE) {
uint convertedDistance = totalTicks * cmPerTick / 100;
draw_distance(convertedDistance);
lcd_write_string("Расстояние", 20, 0, 0xffff, 0x0000);
lcd_write_string("км", 280, 126, 0xffff, 0x0000);
}
char timeDisplay[6];
memset(timeDisplay, ' ', sizeof(timeDisplay));
timeDisplay[5] = 0;
sprintf(timeDisplay, "%i:%02i", currentDatetime.hour, currentDatetime.minute);
lcd_write_string(timeDisplay, 320 - 20 - lcd_string_width(timeDisplay), 0, 0xffff, 0x0000);
lcd_fill_rectangle(320 - 160, 0, 140 - lcd_string_width(timeDisplay), 24, 0x0000);
} else if (guiState.screen == SETTINGS_SCREEN) {
}
}
void gui_init(struct GUIState *state) {
state->screen = 0;
state->screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE;
state->screenState.mainScreen.displayModeChanged = true;
}
void init_button_states() {
for (int i = 0; i < 4; ++i) {
buttonStates[i].debounceAlarm = -100;
buttonStates[i].holdAlarm = -1;
buttonStates[i].currentState = 0;
buttonStates[i].prevState = 0;
buttonStates[i].isHeld = 0;
}
}
void reset() {
pwm_set_chan_level(BACKLIGHT_PWM_SLICE, BACKLIGHT_PWM_CHANNEL, 0); // turn off the screen
(*((volatile uint32_t *)(PPB_BASE + 0x0ED0C))) = 0x5FA0004; // weird trick to actually reset the controller
}
void save_to_rtc() {
printf("saving distance to RTC... ");
static bool savedBefore = false;
static uint savedDistance;
uint8_t buffer[31];
memset(buffer, 0, 31);
uint newDistance = totalTicks * cmPerTick / 10;
if (savedBefore && newDistance == savedDistance) {
printf("skipped saving\n");
return;
}
*(uint *)(buffer + 0) = newDistance;
uint16_t checksum = 0;
for (int i = 0; i < 29; ++i) {
checksum += buffer[i];
}
*(uint16_t *)(buffer + 29) = checksum;
uint8_t checkBuffer[31];
int limit = 10;
do {
printf("\nattempting to write...\n");
ds1302_set_write_protection(0);
// for (int i = 0; i < 31; ++i) {
// ds1302_set_ram_byte(0xC0 + i * 2, buffer + i);
// sleep_us(2);
// }
ds1302_write_ram_bulk(buffer, 31);
for (int i = 0; i < 30; ++i)
printf("%02x ", buffer[i]);
printf("%02x\n", buffer[30]);
ds1302_read_ram_bulk(checkBuffer, 31);
printf("attempting to read back...\n");
for (int i = 0; i < 30; ++i)
printf("%02x ", checkBuffer[i]);
printf("%02x\n", checkBuffer[30]);
if (limit-- == 0)
break;
} while (memcmp(buffer, checkBuffer, 31) != 0);
#ifdef LOGGING
for (int i = 0; i < 30; ++i)
printf("%02x ", buffer[i]);
printf("%02x\n", buffer[30]);
#endif
savedDistance = newDistance;
savedBefore = true;
#ifdef LOGGING
printf("saved\n");
#endif
}
bool load_from_rtc() {
uint8_t buffer[31];
ds1302_read_ram_bulk(buffer, 31);
#ifdef LOGGING
for (int i = 0; i < 30; ++i)
printf("%02x ", buffer[i]);
printf("%02x\n", buffer[30]);
#endif
uint16_t checksum = *(uint16_t *)(buffer + 29);
uint16_t actualChecksum = 0;
for (int i = 0; i < 29; ++i) {
actualChecksum += buffer[i];
}
if (actualChecksum != checksum) {
#ifdef LOGGING
printf("discarding invalid data in RTC memory\n");
#endif
return false;
}
uint savedDistance = *(uint *)(buffer + 0);
totalTicks = savedDistance * 10 / cmPerTick;
#ifdef LOGGING
printf("successfully loaded data from RTC memory\n");
#endif
return true;
}

15
partition_table.json Normal file
View File

@@ -0,0 +1,15 @@
{
"version": [1, 0],
"unpartitioned": {
"families": ["absolute"],
"permissions": {
"secure": "rw",
"nonsecure": "rw",
"bootloader": "rw"
}
},
"partitions": [
{"name": "app", "id": 0, "size": "1536K", "families": ["rp2350-arm-s"], "permissions": {"secure": "rw", "nonsecure": "rw", "bootloader": "rw"}},
{"name": "splash", "id": 1, "size": "112K", "families": ["data"], "permissions": {"secure": "rw", "nonsecure": "rw", "bootloader": "rw"}}
]
}

121
pico_sdk_import.cmake Normal file
View File

@@ -0,0 +1,121 @@
# 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()
# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
endif ()
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
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")
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for 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 ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
)
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Populate(
pico_sdk
QUIET
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
GIT_SUBMODULES_RECURSE FALSE
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
else ()
FetchContent_Populate(
pico_sdk
QUIET
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
endif ()
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})

28
pins.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef VELOMETER_PINS_H
#define VELOMETER_PINS_H
#define PIN_LCD_CS 1
#define PIN_LCD_SCL 2
#define PIN_LCD_SDA 3
#define PIN_LCD_DATA_COMMAND 4
#define PIN_LCD_RESET 5
#define PIN_LCD_BACKLIGHT 6
#define BACKLIGHT_PWM_SLICE 3
#define BACKLIGHT_PWM_CHANNEL 0
#define PIN_RTC_RESET 9
#define PIN_RTC_CLOCK 10
#define PIN_RTC_IO 11
#define PIN_BEEPER 12
#define PIN_BUTTON_0 15
#define PIN_BUTTON_1 16
#define PIN_BUTTON_2 17
#define PIN_BUTTON_3 18
#define PIN_MAIN_HALL_SENSOR 23
#define PIN_AUX_HALL_SENSOR 24
#endif

BIN
splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

214
st7789.c Normal file
View File

@@ -0,0 +1,214 @@
/*
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#include <string.h>
#include "hardware/gpio.h"
#include "pico/stdlib.h"
#include "st7789.h"
static struct st7789_config st7789_cfg;
static uint16_t st7789_width;
static uint16_t st7789_height;
static bool st7789_data_mode = false;
void st7789_cmd(uint8_t cmd, const uint8_t *data, size_t len) {
if (st7789_cfg.gpio_cs > -1) {
spi_set_format(st7789_cfg.spi, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
} else {
spi_set_format(st7789_cfg.spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
}
st7789_data_mode = false;
sleep_us(1);
if (st7789_cfg.gpio_cs > -1) {
gpio_put(st7789_cfg.gpio_cs, 0);
}
gpio_put(st7789_cfg.gpio_dc, 0);
sleep_us(1);
spi_write_blocking(st7789_cfg.spi, &cmd, sizeof(cmd));
if (len) {
sleep_us(1);
gpio_put(st7789_cfg.gpio_dc, 1);
sleep_us(1);
spi_write_blocking(st7789_cfg.spi, data, len);
}
sleep_us(1);
if (st7789_cfg.gpio_cs > -1) {
gpio_put(st7789_cfg.gpio_cs, 1);
}
gpio_put(st7789_cfg.gpio_dc, 1);
sleep_us(1);
}
void st7789_caset(uint16_t xs, uint16_t xe) {
uint8_t data[] = {
xs >> 8,
xs & 0xff,
xe >> 8,
xe & 0xff,
};
// CASET (2Ah): Column Address Set
st7789_cmd(0x2a, data, sizeof(data));
}
void st7789_raset(uint16_t ys, uint16_t ye) {
uint8_t data[] = {
ys >> 8,
ys & 0xff,
ye >> 8,
ye & 0xff,
};
// RASET (2Bh): Row Address Set
st7789_cmd(0x2b, data, sizeof(data));
}
void st7789_init(const struct st7789_config *config, uint16_t width, uint16_t height) {
memcpy(&st7789_cfg, config, sizeof(st7789_cfg));
st7789_width = width;
st7789_height = height;
spi_init(st7789_cfg.spi, 125 * 1000 * 1000);
if (st7789_cfg.gpio_cs > -1) {
spi_set_format(st7789_cfg.spi, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
} else {
spi_set_format(st7789_cfg.spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
}
gpio_set_function(st7789_cfg.gpio_din, GPIO_FUNC_SPI);
gpio_set_function(st7789_cfg.gpio_clk, GPIO_FUNC_SPI);
if (st7789_cfg.gpio_cs > -1) {
gpio_init(st7789_cfg.gpio_cs);
}
gpio_init(st7789_cfg.gpio_dc);
gpio_init(st7789_cfg.gpio_rst);
gpio_init(st7789_cfg.gpio_bl);
if (st7789_cfg.gpio_cs > -1) {
gpio_set_dir(st7789_cfg.gpio_cs, GPIO_OUT);
}
gpio_set_dir(st7789_cfg.gpio_dc, GPIO_OUT);
gpio_set_dir(st7789_cfg.gpio_rst, GPIO_OUT);
gpio_set_dir(st7789_cfg.gpio_bl, GPIO_OUT);
if (st7789_cfg.gpio_cs > -1) {
gpio_put(st7789_cfg.gpio_cs, 1);
}
gpio_put(st7789_cfg.gpio_dc, 1);
gpio_put(st7789_cfg.gpio_rst, 1);
sleep_ms(100);
// SWRESET (01h): Software Reset
st7789_cmd(0x01, NULL, 0);
sleep_ms(150);
// SLPOUT (11h): Sleep Out
st7789_cmd(0x11, NULL, 0);
sleep_ms(50);
// COLMOD (3Ah): Interface Pixel Format
// - RGB interface color format = 65K of RGB interface
// - Control interface color format = 16bit/pixel
st7789_cmd(0x3a, (uint8_t[]){0x55}, 1);
sleep_ms(10);
// MADCTL (36h): Memory Data Access Control
// - Page Address Order = Top to Bottom
// - Column Address Order = Left to Right
// - Page/Column Order = Normal Mode
// - Line Address Order = LCD Refresh Top to Bottom
// - RGB/BGR Order = RGB
// - Display Data Latch Data Order = LCD Refresh Left to Right
st7789_cmd(0x36, (uint8_t[]){0xA0}, 1);
st7789_caset(0, width);
st7789_raset(0, height);
// INVON (21h): Display Inversion On
st7789_cmd(0x21, NULL, 0);
sleep_ms(10);
// NORON (13h): Normal Display Mode On
st7789_cmd(0x13, NULL, 0);
sleep_ms(10);
// DISPON (29h): Display On
st7789_cmd(0x29, NULL, 0);
sleep_ms(10);
st7789_fill(0x0000);
sleep_ms(50);
gpio_put(st7789_cfg.gpio_bl, 1);
}
void st7789_ramwr() {
sleep_us(1);
if (st7789_cfg.gpio_cs > -1) {
gpio_put(st7789_cfg.gpio_cs, 0);
}
gpio_put(st7789_cfg.gpio_dc, 0);
sleep_us(1);
// RAMWR (2Ch): Memory Write
uint8_t cmd = 0x2c;
spi_write_blocking(st7789_cfg.spi, &cmd, sizeof(cmd));
sleep_us(1);
if (st7789_cfg.gpio_cs > -1) {
gpio_put(st7789_cfg.gpio_cs, 0);
}
gpio_put(st7789_cfg.gpio_dc, 1);
sleep_us(1);
}
void st7789_write(const void *data, size_t len) {
if (!st7789_data_mode) {
st7789_ramwr();
if (st7789_cfg.gpio_cs > -1) {
spi_set_format(st7789_cfg.spi, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
} else {
spi_set_format(st7789_cfg.spi, 16, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
}
st7789_data_mode = true;
}
spi_write16_blocking(st7789_cfg.spi, data, len / 2);
}
void st7789_put(uint16_t pixel) { st7789_write(&pixel, sizeof(pixel)); }
void st7789_fill(uint16_t pixel) {
int num_pixels = st7789_width * st7789_height;
st7789_set_cursor(0, 0);
for (int i = 0; i < num_pixels; i++) {
st7789_put(pixel);
}
}
void st7789_set_cursor(uint16_t x, uint16_t y) {
st7789_caset(x, st7789_width);
st7789_raset(y, st7789_height);
}
void st7789_vertical_scroll(uint16_t row) {
uint8_t data[] = {(row >> 8) & 0xff, row & 0x00ff};
// VSCSAD (37h): Vertical Scroll Start Address of RAM
st7789_cmd(0x37, data, sizeof(data));
}

33
st7789.h Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#ifndef _PICO_ST7789_H_
#define _PICO_ST7789_H_
#include "hardware/spi.h"
struct st7789_config {
spi_inst_t *spi;
uint gpio_din;
uint gpio_clk;
int gpio_cs;
uint gpio_dc;
uint gpio_rst;
uint gpio_bl;
};
void st7789_cmd(uint8_t cmd, const uint8_t *data, size_t len);
void st7789_init(const struct st7789_config *config, uint16_t width, uint16_t height);
void st7789_write(const void *data, size_t len);
void st7789_put(uint16_t pixel);
void st7789_fill(uint16_t pixel);
void st7789_caset(uint16_t xs, uint16_t xe);
void st7789_raset(uint16_t ys, uint16_t ye);
void st7789_set_cursor(uint16_t x, uint16_t y);
void st7789_vertical_scroll(uint16_t row);
#endif

59
utf8.c Normal file
View File

@@ -0,0 +1,59 @@
#include "utf8.h"
// This macro tests if a char is a continuation byte in utf8.
#define IS_CONT(x) (((x) & 0xc0) == 0x80)
// This returns the code point encoded at **s and advances *s to point to the
// next character. Thus it can easily be used in a loop.
int decode_code_point(const char **s) {
int k = **s ? __builtin_clz(~(**s << 24)) : 0; // Count # of leading 1 bits.
int mask = (1 << (8 - k)) - 1; // All 1s with k leading 0s.
int value = **s & mask;
// k = 0 for one-byte code points; otherwise, k = #total bytes.
for (++(*s), --k; k > 0 && IS_CONT(**s); --k, ++(*s)) {
value <<= 6;
value += (**s & 0x3F);
}
return value;
}
// This assumes that `code` is <= 0x10FFFF and ensures that nothing will be
// written at or beyond `end`. It advances *s so it's easy to use in a loop.
void encode_code_point(char **s, char *end, int code) {
char val[4];
int lead_byte_max = 0x7F;
int val_index = 0;
while (code > lead_byte_max) {
val[val_index++] = (code & 0x3F) | 0x80;
code >>= 6;
lead_byte_max >>= (val_index == 1 ? 2 : 1);
}
val[val_index++] = (code & lead_byte_max) | (~lead_byte_max << 1);
while (val_index-- && *s < end) {
**s = val[val_index];
(*s)++;
}
}
// This returns 0 if no split was needed.
int split_into_surrogates(int code, int *surr1, int *surr2) {
if (code <= 0xFFFF)
return 0;
*surr2 = 0xDC00 | (code & 0x3FF); // Save the low 10 bits.
code >>= 10; // Drop the low 10 bits.
// If `code` now has low bits "uuu uuxx xxxx", then the bits of *surr are
// "1101 10ww wwxx xxxx" where wwww = (uuuuu - 1).
*surr1 = 0xD800 | ((code & 0x7FF) - 0x40);
return 1;
}
// This expects to be used in a loop and see all code points in *code. Start
// *old at 0; this function updates *old for you - don't change it after
// initialization. This returns 0 when *code is the 1st of a surrogate pair;
// otherwise use *code as the final code point.
int join_from_surrogates(int *old, int *code) {
if (*old)
*code = (((*old & 0x3FF) + 0x40) << 10) + (*code & 0x3FF);
*old = ((*code & 0xD800) == 0xD800 ? *code : 0);
return !(*old);
}

9
utf8.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef VELOMETER_UTF8_H
#define VELOMETER_UTF8_H
int decode_code_point(const char **s);
void encode_code_point(char **s, char *end, int code);
int split_into_surrogates(int code, int *surr1, int *surr2);
int join_from_surrogates(int *old, int *code);
#endif