菜单支持,所有功能都测试成功

This commit is contained in:
2026-07-15 15:39:49 +08:00
parent d4112399c2
commit 92facadeb0
16 changed files with 908 additions and 447 deletions

View File

@@ -15,6 +15,8 @@ STM32CubeMX 生成的项目,使用 **Keil MDK-ARM V5.32** 工具链。
- `hz16_data.c/h` — CJK 16×16 字库 - `hz16_data.c/h` — CJK 16×16 字库
- `STM32F103C8T6/Drivers/BSP/AT24C02/` — AT24C02 EEPROM 驱动 - `STM32F103C8T6/Drivers/BSP/AT24C02/` — AT24C02 EEPROM 驱动
- `at24c02.c/h` — I2C 读写(字节/页/连续,自动跨页) - `at24c02.c/h` — I2C 读写(字节/页/连续,自动跨页)
- `STM32F103C8T6/User/` — 用户应用代码
- `menu.c/h` — 菜单系统LCD参数显示、按键调节、EEPROM存储
## 硬件配置 ## 硬件配置
- **MCU**: STM32F103C8T6 (LQFP48, 72MHz, 64KB Flash, 20KB RAM) - **MCU**: STM32F103C8T6 (LQFP48, 72MHz, 64KB Flash, 20KB RAM)
@@ -33,39 +35,30 @@ STM32CubeMX 生成的项目,使用 **Keil MDK-ARM V5.32** 工具链。
## 初始化顺序main.c ## 初始化顺序main.c
``` ```
HAL_Init → SystemClock_Config → MX_GPIO → MX_DMA → MX_USART1 → MX_SPI2 HAL_Init → SystemClock_Config → MX_GPIO → MX_DMA → MX_USART1 → MX_SPI2
→ MX_TIM4 → MX_ADC1 → MX_TIM3 → MX_TIM4 → MX_ADC1 → MX_TIM3 → MX_I2C1
→ button_driver_init() → st7735s_init() → lcd_* draw → button_driver_init() → st7735s_init() → lcd_* draw
→ TIM3 PWM start (PA6~PB1) → TIM4 Base start (TRGO→ADC) → ADC injected start → TIM3 PWM start (PA6~PB1) → TIM4 Base start (TRGO→ADC) → ADC injected start
→ AT24C02 scan → menu_init()
``` ```
## ADC 采坑记录(不可从 CubeMX 重新生成时光靠设置) ## CubeMX 生成代码注意事项
- **ADC 时钟**: SystemClock_Config 初始为 PCLK2/2=36MHz超 14MHz 规格),必须在 `USER CODE SysInit` 中手动改为 `/6` - **TIM4_TRGO**: CubeMX 已正确生成 `TIM_TRGO_UPDATE``tim.c:125`),无需手动写寄存器。若从 `.ioc` 重新生成,检查 `sMasterConfig.MasterOutputTrigger` 是否仍为 `TIM_TRGO_UPDATE`
```c - **ADC 时钟**: CubeMX 已通过 HAL 配置 `RCC_ADCPCLK2_DIV6``adc.c:221`),无需手动改 `RCC->CFGR`。若重新生成,验证 `PeriphClkInit.AdcClockSelection` 是否为 `RCC_ADCPCLK2_DIV6`(必须 ≤14MHz
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_ADCPRE) | RCC_CFGR_ADCPRE_DIV6; - **SPI2 模式**: CubeMX 已配置 Mode 3CPOL=HIGH, CPHA=2EDGE`spi.c:45-46`。重新生成时确认 `CLKPolarity=HIGH, CLKPhase=2EDGE`
```
- **TIM4_TRGO 触发 ADC当前方案**: CubeMX 中 TIM4 无任何 PWM 通道,生成的 `MasterOutputTrigger` 为 `TIM_TRGO_RESET` → TRGO 无脉冲。必须在 `USER CODE TIM4_Init 2` 中写寄存器:
```c
TIM4->CR2 = (TIM4->CR2 & ~TIM_CR2_MMS) | TIM_CR2_MMS_1; /* TRGO = Update */
```
启动时用 `HAL_TIM_Base_Start(&htim4)` 即可。
- **旧方案(已废弃)**: TIM3_CH4 触发 ADC 已废弃PB8/PB9 改为 I2C
## printf 无 MicroLIB ## printf 无 MicroLIB
`usart.c` 的 USER CODE 区通过 `#pragma import(__use_no_semihosting)` + `_sys_exit()` + `_ttywrch()` + `struct __FILE` + `FILE __stdout` 实现。Keil 取消勾选 Use MicroLIB 即可。 `usart.c` 的 USER CODE 区通过 `#pragma import(__use_no_semihosting)` + `_sys_exit()` + `_ttywrch()` + `struct __FILE` + `FILE __stdout` 实现。Keil 取消勾选 Use MicroLIB 即可。
## SPI 注意事项
- LCD (ST7735S) 使用**硬件SPI2**PB13 SCK, PB15 MOSI18MHz**Mode 3**CPOL=HIGH, CPHA=2EDGE
- 若通过 CubeMX 重新生成 `spi.c`,务必在 SPI2 配置中设置 Clock Polarity=High, Clock Phase=2 Edge
- CS(PB11)、DC(PB10)、RST(PB12) 为 GPIO 控制
## LCD 字库生成tools/font_gen.py ## LCD 字库生成tools/font_gen.py
- Python + Pillow 渲染,使用 `C:\ProgramData\anaconda3\python.exe` - Python + Pillow使用 `python font_gen.py`Windows 下默认系统 Python
- ASCII 8×16默认`python font_gen.py` - **仅生成 CJK 字库**ASCII 字库 `lcd_data.c` 已手动维护
- CJK 16×16按需`python font_gen.py --cjk --string "你的汉字" --output STM32F103C8T6/Drivers/BSP/LCD/hz16_data` - CJK 16×16按需
- ASCII 默认参数:`--font simsun --size 56 --threshold 64 --scale 4` ```
- CJK 默认参数:`--font simsun --size 48 --threshold 50 --scale 3` python tools/font_gen.py --cjk --string "你的汉字" --output STM32F103C8T6/Drivers/BSP/LCD/hz16_data
```
- 默认参数:`--font simsun --size 48 --scale 3`Otsu 自适应阈值
- CJK 无等宽替代字体,必须用 simsun
- ASCII `_` 下划线手动设为 `0x7F`GDI 无法可靠提取) - ASCII `_` 下划线手动设为 `0x7F`GDI 无法可靠提取)
- 输出目录 `MDK-ARM\STM32F103C8T6\` 已 gitignore
## 代码规范 ## 代码规范
- **遵循**: `嵌入式C语言代码规范V1.0.md` - **遵循**: `嵌入式C语言代码规范V1.0.md`

View File

@@ -33,6 +33,7 @@
#include "st7735s.h" #include "st7735s.h"
#include "lcd.h" #include "lcd.h"
#include "at24c02.h" #include "at24c02.h"
#include "menu.h"
/* USER CODE END Includes */ /* USER CODE END Includes */
@@ -91,9 +92,6 @@ int main(void)
SystemClock_Config(); SystemClock_Config();
/* USER CODE BEGIN SysInit */ /* USER CODE BEGIN SysInit */
/* ADC 时钟预分频PCLK2 / 6 = 72/6 = 12MHz */
/* 默认 /2=36MHz 超过 14MHz 规格ADC 无法正常工作 */
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_ADCPRE) | RCC_CFGR_ADCPRE_DIV6;
/* USER CODE END SysInit */ /* USER CODE END SysInit */
/* Initialize all configured peripherals */ /* Initialize all configured peripherals */
@@ -143,64 +141,41 @@ int main(void)
/* 启动 ADC 注入组TIM4 每触发一次,自动采一轮 PA0~PA3 */ /* 启动 ADC 注入组TIM4 每触发一次,自动采一轮 PA0~PA3 */
HAL_ADCEx_InjectedStart(&hadc1); HAL_ADCEx_InjectedStart(&hadc1);
/* === AT24C02 EEPROM 测试 === */ /* AT24C02 检测 */
{ if (at24c02_scan() == AT24C02_OK) {
uint8_t wbuf[] = {0x11, 0x22, 0x33, 0x44, 0x55}; lcd_draw_string(0, 80, "AT24C02 OK", COLOR_GREEN, COLOR_BLACK);
uint8_t rbuf[8];
int i, ret;
ret = at24c02_scan();
if (ret == AT24C02_OK) {
lcd_draw_string(0, 64, "AT24C02 OK", COLOR_GREEN, COLOR_BLACK);
} else { } else {
lcd_draw_string(0, 64, "AT24C02 FAIL", COLOR_RED, COLOR_BLACK); lcd_draw_string(0, 80, "AT24C02 FAIL", COLOR_RED, COLOR_BLACK);
} }
/* 在地址 0 写入 5 个字节 */ /* 进入菜单界面 */
ret = at24c02_write_buf(0, wbuf, 5); menu_init();
printf("AT24C02 write: %d\r\n", ret);
/* 从地址 0 读回 8 个字节 */
ret = at24c02_read_buf(0, rbuf, 8);
printf("AT24C02 read[%d]:", ret);
for (i = 0; i < 8; i++) {
printf(" %02X", rbuf[i]);
}
printf("\r\n");
/* 验证 */
lcd_draw_string(0, 80, "EEPROM:", COLOR_WHITE, COLOR_BLACK);
for (i = 0; i < 5; i++) {
if (rbuf[i] != wbuf[i]) {
printf("AT24C02 ERR: addr %d expect %02X got %02X\r\n",
i, wbuf[i], rbuf[i]);
break;
}
}
if (i == 5) {
printf("AT24C02 verify OK\r\n");
}
}
/* USER CODE END 2 */ /* USER CODE END 2 */
/* Infinite loop */ /* Infinite loop */
/* USER CODE BEGIN WHILE */ /* USER CODE BEGIN WHILE */
uint32_t tick_last = 0;
while (1) while (1)
{ {
/* USER CODE END WHILE */ /* USER CODE END WHILE */
/* USER CODE BEGIN 3 */ /* USER CODE BEGIN 3 */
HAL_Delay(1000); /* 菜单按键处理(每帧必须频繁调用) */
menu_process();
/* 每秒执行一次LED翻转 + ADC打印 */
if (HAL_GetTick() - tick_last >= 1000) {
tick_last = HAL_GetTick();
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
/* 读取 ADC 注入组结果TIM4 以 1kHz 触发,每秒打印一次最新值) */
uint16_t adc0 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_1); uint16_t adc0 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_1);
uint16_t adc1 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_2); uint16_t adc1 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_2);
uint16_t adc2 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_3); uint16_t adc2 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_3);
uint16_t adc3 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_4); uint16_t adc3 = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_4);
printf("ADC: %4d %4d %4d %4d\r\n", adc0, adc1, adc2, adc3); printf("ADC: %4d %4d %4d %4d\r\n", adc0, adc1, adc2, adc3);
} }
}
/* USER CODE END 3 */ /* USER CODE END 3 */
} }

View File

@@ -125,6 +125,81 @@ void lcd_draw_string(uint16_t x, uint16_t y, const char *str, uint16_t color, ui
} }
} }
/*
* 函数功能显示一个6x8 ASCII字符
* 入口参数x - 列坐标
* y - 行坐标
* ch - 字符 (ASCII 32-126)
* color - 字符颜色 (RGB565)
* bg_color - 背景颜色 (RGB565)
* 返回值:无
* 函数说明LCD_F6x8为列式存储每字节一列bit0=顶行bit7=底行)
* 框选6x8区域→逐行逐列取bit→发送color或bg_color
*/
void lcd_draw_char_6x8(uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bg_color)
{
uint8_t row;
uint8_t col;
uint8_t col_data;
if (ch < 32 || ch > 126) {
ch = ' ';
}
st7735s_set_window(x, y, x + 5, y + 7);
HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET);
for (row = 0; row < 8; row++) {
for (col = 0; col < 6; col++) {
col_data = LCD_F6x8[ch - 32][col];
if (col_data & (1 << row)) {
st7735s_spi_write_byte((uint8_t)(color >> 8));
st7735s_spi_write_byte((uint8_t)(color & 0xFF));
} else {
st7735s_spi_write_byte((uint8_t)(bg_color >> 8));
st7735s_spi_write_byte((uint8_t)(bg_color & 0xFF));
}
}
}
HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET);
}
/*
* 函数功能显示字符串6x8字体
* 入口参数x - 列坐标
* y - 行坐标
* str - 字符串指针
* color - 字符颜色 (RGB565)
* bg_color - 背景颜色 (RGB565)
* 返回值:无
* 函数说明连续显示6x8字符自动换行
*/
void lcd_draw_string_6x8(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color)
{
uint16_t start_x = x;
while (*str != '\0') {
if (*str == '\n') {
y += FONT_HEIGHT_8;
x = start_x;
str++;
continue;
}
if (x + FONT_WIDTH_6 > s_lcd_width) {
y += FONT_HEIGHT_8;
x = start_x;
}
if (y + FONT_HEIGHT_8 > s_lcd_height) {
break;
}
lcd_draw_char_6x8(x, y, *str, color, bg_color);
x += FONT_WIDTH_6;
str++;
}
}
/* /*
* 函数功能显示一个16x16 CJK汉字 * 函数功能显示一个16x16 CJK汉字
* 入口参数x - 列坐标 * 入口参数x - 列坐标

View File

@@ -21,6 +21,8 @@ extern "C" {
/* 字体定义 */ /* 字体定义 */
#define FONT_WIDTH_8 8 /* 8x16字体宽度含间距 */ #define FONT_WIDTH_8 8 /* 8x16字体宽度含间距 */
#define FONT_HEIGHT_16 16 /* 8x16字体高度 */ #define FONT_HEIGHT_16 16 /* 8x16字体高度 */
#define FONT_WIDTH_6 6 /* 6x8字体宽度 */
#define FONT_HEIGHT_8 8 /* 6x8字体高度 */
/* === 字符/字符串绘制 === */ /* === 字符/字符串绘制 === */
@@ -46,6 +48,28 @@ void lcd_draw_char(uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bg_
*/ */
void lcd_draw_string(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color); void lcd_draw_string(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color);
/*
* 函数功能显示一个6x8 ASCII字符
* 入口参数x - 列坐标
* y - 行坐标
* ch - 字符 (ASCII 32-126)
* color - 字符颜色 (RGB565)
* bg_color - 背景颜色 (RGB565)
* 返回值:无
*/
void lcd_draw_char_6x8(uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bg_color);
/*
* 函数功能显示字符串6x8字体
* 入口参数x - 列坐标
* y - 行坐标
* str - 字符串指针
* color - 字符颜色 (RGB565)
* bg_color - 背景颜色 (RGB565)
* 返回值:无
*/
void lcd_draw_string_6x8(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg_color);
/* /*
* 函数功能显示一个16x16 CJK汉字 * 函数功能显示一个16x16 CJK汉字
* 入口参数x - 列坐标 * 入口参数x - 列坐标

View File

@@ -1,298 +1,300 @@
#include "lcd_data.h" #include "lcd_data.h"
/*ASCII字模数据*********************/ /* ASCII 字模数据 8x16 */
/* 来源: font8x16 (hubenchang0515) — MIT 许可 */
/* 人工逐像素设计的专业位图字体,非算法缩放生成 */
/*宽8像素高16像素*/
/* 字体: simsun.ttc, 字号: 64, 阈值: 50 */
const uint8_t LCD_F8x16[][16] = const uint8_t LCD_F8x16[][16] =
{ {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0
0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18,
0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, // ! 1 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, // ! 1
0x00, 0x16, 0x36, 0x3C, 0x68, 0x40, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " 2 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " 2
0x00, 0x00, 0x20, 0x26, 0x26, 0xFF, 0x7E, 0x24, 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C,
0x64, 0xFF, 0xFE, 0x64, 0x64, 0x00, 0x00, 0x00, // # 3 0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, // # 3
0x00, 0x18, 0x3C, 0x7E, 0x7E, 0x78, 0x38, 0x18, 0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06,
0x1C, 0x1E, 0x7E, 0x56, 0x7C, 0x18, 0x18, 0x00, // $ 4 0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00, // $ 4
0x00, 0x00, 0x60, 0xF4, 0x94, 0x9C, 0x98, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xC6, 0x0C, 0x18,
0x1E, 0x1B, 0x3B, 0x2B, 0x6E, 0x04, 0x00, 0x00, // % 5 0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00, // % 5
0x00, 0x00, 0x30, 0x78, 0x68, 0x78, 0x78, 0x76, 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC,
0xF4, 0xD4, 0xDC, 0xCC, 0x7F, 0x22, 0x00, 0x00, // & 6 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, // & 6
0x00, 0x60, 0x70, 0x30, 0x60, 0x40, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ' 7 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ' 7
0x00, 0x02, 0x06, 0x0C, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30,
0x18, 0x18, 0x18, 0x0C, 0x0C, 0x06, 0x02, 0x00, // ( 8 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, // ( 8
0x00, 0x40, 0x60, 0x30, 0x30, 0x10, 0x18, 0x18, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C,
0x18, 0x18, 0x18, 0x30, 0x30, 0x60, 0x40, 0x00, // ) 9 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // ) 9
0x00, 0x00, 0x00, 0x18, 0x18, 0xFF, 0x7E, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3C, 0xFF,
0xFF, 0xDB, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // * 10 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // * 10
0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E,
0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 11 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 11
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // , 12 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, // , 12
0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // - 13 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // - 13
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // . 14 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, // . 14
0x00, 0x02, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18,
0x10, 0x30, 0x20, 0x60, 0x40, 0xC0, 0x80, 0x00, // / 15 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, // / 15
0x00, 0x00, 0x3C, 0x66, 0x66, 0x42, 0xC3, 0xC3, 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xD6, 0xD6,
0xC3, 0xC3, 0x42, 0x66, 0x3C, 0x18, 0x00, 0x00, // 0 16 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, // 0 16
0x00, 0x00, 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x3C, 0x00, 0x00, // 1 17 0x18, 0x18, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, // 1 17
0x00, 0x00, 0x3C, 0x66, 0x62, 0x62, 0x06, 0x04, 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30,
0x08, 0x10, 0x20, 0x42, 0xFE, 0x7C, 0x00, 0x00, // 2 18 0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, // 2 18
0x00, 0x00, 0x3C, 0x64, 0x66, 0x06, 0x0C, 0x1C, 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06,
0x06, 0x02, 0x62, 0x66, 0x7C, 0x18, 0x00, 0x00, // 3 19 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // 3 19
0x00, 0x00, 0x04, 0x0C, 0x1C, 0x1C, 0x2C, 0x6C, 0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE,
0x4C, 0xFE, 0x0C, 0x0C, 0x0E, 0x1E, 0x00, 0x00, // 4 20 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, // 4 20
0x00, 0x00, 0x3E, 0x7E, 0x60, 0x40, 0x7C, 0x66, 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x06,
0x06, 0x02, 0x62, 0x66, 0x7C, 0x18, 0x00, 0x00, // 5 21 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // 5 21
0x00, 0x00, 0x1C, 0x26, 0x62, 0x40, 0xDC, 0xF6, 0x00, 0x00, 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6,
0xC2, 0xC3, 0x43, 0x62, 0x3E, 0x18, 0x00, 0x00, // 6 22 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // 6 22
0x00, 0x00, 0x7E, 0x7E, 0x44, 0x04, 0x0C, 0x08, 0x00, 0x00, 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18,
0x18, 0x18, 0x18, 0x38, 0x38, 0x10, 0x00, 0x00, // 7 23 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, // 7 23
0x00, 0x00, 0x3C, 0x66, 0x42, 0x62, 0x76, 0x3C, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6,
0x6E, 0x46, 0xC2, 0x46, 0x7C, 0x18, 0x00, 0x00, // 8 24 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // 8 24
0x00, 0x00, 0x3C, 0x66, 0x46, 0xC2, 0xC2, 0x66, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06,
0x7E, 0x16, 0x06, 0x64, 0x7C, 0x10, 0x00, 0x00, // 9 25 0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00, // 9 25
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, // : 26 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // : 26
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
0x00, 0x18, 0x18, 0x18, 0x10, 0x00, 0x00, 0x00, // ; 27 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // ; 27
0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60,
0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, // < 28 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, // < 28
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // = 29 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // = 29
0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06,
0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, // > 30 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, // > 30
0x00, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0x46, 0x0C, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18,
0x18, 0x10, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, // ? 31 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, // ? 31
0x00, 0x00, 0x1C, 0x36, 0x6F, 0x5F, 0xD7, 0xF5, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xDE, 0xDE,
0xF5, 0xFE, 0x7E, 0x63, 0x3E, 0x18, 0x00, 0x00, // @ 32 0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00, // @ 32
0x00, 0x00, 0x18, 0x18, 0x18, 0x3C, 0x2C, 0x2C, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE,
0x3C, 0x7E, 0x46, 0x46, 0xC7, 0xC7, 0x00, 0x00, // A 33 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, // A 33
0x00, 0x00, 0xFC, 0x66, 0x62, 0x62, 0x66, 0x7C, 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66,
0x66, 0x63, 0x63, 0x63, 0x7E, 0xF8, 0x00, 0x00, // B 34 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, // B 34
0x00, 0x00, 0x1E, 0x63, 0x41, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0xC1, 0x62, 0x3E, 0x18, 0x00, 0x00, // C 35 0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, // C 35
0x00, 0x00, 0xF8, 0x6C, 0x66, 0x63, 0x63, 0x63, 0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66,
0x63, 0x63, 0x62, 0x66, 0x7C, 0xF0, 0x00, 0x00, // D 36 0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00, // D 36
0x00, 0x00, 0x7E, 0x66, 0x62, 0x60, 0x64, 0x7C, 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68,
0x64, 0x60, 0x60, 0x63, 0x7E, 0x7E, 0x00, 0x00, // E 37 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, // E 37
0x00, 0x00, 0x7E, 0x63, 0x61, 0x60, 0x64, 0x7C, 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68,
0x64, 0x64, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, // F 38 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, // F 38
0x00, 0x00, 0x3C, 0x66, 0x42, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE,
0xC7, 0xC6, 0x46, 0x66, 0x3E, 0x18, 0x00, 0x00, // G 39 0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00, // G 39
0x00, 0x00, 0xE7, 0x66, 0x66, 0x66, 0x66, 0x7E, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6,
0x66, 0x66, 0x66, 0x66, 0x66, 0xE7, 0x00, 0x00, // H 40 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, // H 40
0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x3C, 0x00, 0x00, // I 41 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, // I 41
0x00, 0x3F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xF8, 0x20, 0x00, // J 42 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, // J 42
0x00, 0x00, 0xE6, 0x66, 0x6C, 0x68, 0x70, 0x78, 0x00, 0x00, 0xE6, 0x66, 0x66, 0x6C, 0x78, 0x78,
0x68, 0x6C, 0x6C, 0x66, 0x67, 0x67, 0x00, 0x00, // K 43 0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, // K 43
0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60,
0x60, 0x60, 0x60, 0x63, 0x7E, 0x7E, 0x00, 0x00, // L 44 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, // L 44
0x00, 0x00, 0xC3, 0x66, 0x66, 0x66, 0x66, 0x6E, 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6,
0x7E, 0x7A, 0x5A, 0x5A, 0xD7, 0xC7, 0x00, 0x00, // M 45 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, // M 45
0x00, 0x00, 0xC7, 0x62, 0x72, 0x72, 0x52, 0x5A, 0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE,
0x4A, 0x4E, 0x46, 0x46, 0xE2, 0xE0, 0x00, 0x00, // N 46 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, // N 46
0x00, 0x00, 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
0xC3, 0xC3, 0x43, 0x66, 0x3C, 0x18, 0x00, 0x00, // O 47 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // O 47
0x00, 0x00, 0x7C, 0x66, 0x63, 0x63, 0x62, 0x7E, 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60,
0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, // P 48 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, // P 48
0x00, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
0xC3, 0xFB, 0x7A, 0x6E, 0x3C, 0x06, 0x00, 0x00, // Q 49 0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00, // Q 49
0x00, 0x00, 0x7C, 0x66, 0x62, 0x62, 0x66, 0x7C, 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C,
0x68, 0x6C, 0x64, 0x66, 0x67, 0x63, 0x00, 0x00, // R 50 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, // R 50
0x00, 0x00, 0x3C, 0x66, 0x42, 0x60, 0x70, 0x3C, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C,
0x0E, 0x06, 0xC2, 0x46, 0x7E, 0x18, 0x00, 0x00, // S 51 0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // S 51
0x00, 0x00, 0x7E, 0xDB, 0x99, 0x18, 0x18, 0x18, 0x00, 0x00, 0x7E, 0x7E, 0x5A, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, // T 52 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, // T 52
0x00, 0x00, 0xE7, 0x62, 0x62, 0x62, 0x62, 0x62, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
0x62, 0x62, 0x62, 0x62, 0x3C, 0x18, 0x00, 0x00, // U 53 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // U 53
0x00, 0x00, 0xE7, 0x66, 0x66, 0x64, 0x24, 0x24, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
0x3C, 0x38, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, // V 54 0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, // V 54
0x00, 0x00, 0xDB, 0xDB, 0x5A, 0x5A, 0x5A, 0x7E, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6,
0x7C, 0x7C, 0x2C, 0x2C, 0x24, 0x00, 0x00, 0x00, // W 55 0xD6, 0xFE, 0xEE, 0x6C, 0x00, 0x00, 0x00, 0x00, // W 55
0x00, 0x00, 0x66, 0x66, 0x24, 0x3C, 0x18, 0x18, 0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x7C, 0x38, 0x38,
0x18, 0x3C, 0x2C, 0x24, 0x66, 0x66, 0x00, 0x00, // X 56 0x7C, 0x6C, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, // X 56
0x00, 0x00, 0xE7, 0x62, 0x66, 0x34, 0x34, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, // Y 57 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, // Y 57
0x00, 0x00, 0x7E, 0x66, 0x46, 0x0C, 0x08, 0x18, 0x00, 0x00, 0xFE, 0xC6, 0x86, 0x0C, 0x18, 0x30,
0x10, 0x30, 0x20, 0x62, 0xFE, 0x7C, 0x00, 0x00, // Z 58 0x60, 0xC2, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, // Z 58
0x00, 0x1E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x00, // [ 59 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, 0x00, // [ 59
0x00, 0x40, 0x60, 0x20, 0x30, 0x30, 0x10, 0x18, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38,
0x08, 0x0C, 0x0C, 0x04, 0x06, 0x02, 0x02, 0x00, // \ 60 0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, // \ 60
0x00, 0x78, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x78, 0x00, // ] 61 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00, 0x00, // ] 61
0x00, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ^ 62 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ^ 62
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, // _ 63 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, // _ 63
0x00, 0x70, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ` 64 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ` 64
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x46, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C,
0x66, 0xC6, 0x6F, 0x36, 0x00, 0x00, 0x00, 0x00, // a 65 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, // a 65
0x00, 0x00, 0xC0, 0x60, 0x60, 0x60, 0x7C, 0x76, 0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66,
0x66, 0x62, 0x62, 0x66, 0x66, 0x5C, 0x00, 0x00, // b 66 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00, // b 66
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0,
0x40, 0x62, 0x76, 0x1C, 0x00, 0x00, 0x00, 0x00, // c 67 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // c 67
0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x76, 0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC,
0x66, 0x46, 0x46, 0x66, 0x6E, 0x3E, 0x00, 0x00, // d 68 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, // d 68
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x42, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xFE,
0x40, 0x62, 0x76, 0x1C, 0x00, 0x00, 0x00, 0x00, // e 69 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // e 69
0x00, 0x00, 0x0E, 0x1B, 0x31, 0x30, 0x7C, 0x30, 0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60,
0x30, 0x30, 0x30, 0x30, 0x38, 0x7C, 0x00, 0x00, // f 70 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, // f 70
0x00, 0x00, 0x00, 0x1B, 0x2F, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC,
0x78, 0x7E, 0x46, 0x66, 0x3C, 0x00, 0x00, 0x00, // g 71 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, // g 71
0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x7C, 0x66, 0x00, 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, // h 72 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, // h 72
0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x38, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x3C, 0x00, 0x00, // i 73 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, // i 73
0x00, 0x04, 0x06, 0x00, 0x00, 0x1E, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0E, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x04, 0x6C, 0x38, 0x00, // j 74 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, // j 74
0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x66, 0x6C, 0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78,
0x68, 0x78, 0x6C, 0x6C, 0x66, 0x67, 0x00, 0x00, // k 75 0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, // k 75
0x00, 0x00, 0x38, 0x38, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, // l 76 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, // l 76
0x00, 0x00, 0x00, 0x00, 0xFE, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0xD6,
0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00, // m 77 0xD6, 0xD6, 0xD6, 0xC6, 0x00, 0x00, 0x00, 0x00, // m 77
0x00, 0x00, 0x00, 0x00, 0xFC, 0x76, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, // n 78 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, // n 78
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x42, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6,
0xC3, 0x42, 0x66, 0x38, 0x00, 0x00, 0x00, 0x00, // o 79 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // o 79
0x00, 0x00, 0x00, 0x7C, 0x76, 0x62, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66,
0x62, 0x66, 0x7C, 0x60, 0x70, 0x00, 0x00, 0x00, // p 80 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, // p 80
0x00, 0x00, 0x00, 0x3E, 0x66, 0x46, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC,
0x46, 0x7E, 0x16, 0x06, 0x0E, 0x00, 0x00, 0x00, // q 81 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00, // q 81
0x00, 0x00, 0x00, 0x00, 0xFE, 0x3B, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x76, 0x66,
0x30, 0x30, 0x30, 0xF8, 0x00, 0x00, 0x00, 0x00, // r 82 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, // r 82
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x60,
0x0E, 0x46, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00, // s 83 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, // s 83
0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00, 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30,
0x10, 0x10, 0x10, 0x1E, 0x0C, 0x00, 0x00, 0x00, // t 84 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, // t 84
0x00, 0x00, 0x00, 0x00, 0xE6, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC,
0x66, 0x66, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, // u 85 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, // u 85
0x00, 0x00, 0x00, 0x00, 0xE6, 0x66, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66,
0x38, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // v 86 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, // v 86
0x00, 0x00, 0x00, 0x00, 0xDB, 0xDA, 0x5A, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xD6,
0x7C, 0x2C, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, // w 87 0xD6, 0xD6, 0xFE, 0x6C, 0x00, 0x00, 0x00, 0x00, // w 87
0x00, 0x00, 0x00, 0x00, 0x76, 0x34, 0x38, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38,
0x18, 0x2C, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, // x 88 0x38, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, // x 88
0x00, 0x00, 0x00, 0x67, 0x66, 0x34, 0x34, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6,
0x18, 0x18, 0x18, 0x70, 0x60, 0x00, 0x00, 0x00, // y 89 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00, // y 89
0x00, 0x00, 0x00, 0x00, 0x7E, 0x4C, 0x08, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x18,
0x30, 0x22, 0x7E, 0x7C, 0x00, 0x00, 0x00, 0x00, // z 90 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, // z 90
0x00, 0x06, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18,
0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x06, 0x00, // { 91 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x00, // { 91
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // | 92 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, // | 92
0x00, 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18,
0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x00, // } 93 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, // } 93
0x00, 0x70, 0x59, 0x0E, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ~ 94 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ~ 94
}; };
/*宽6像素高8像素(未使用)*/ /* 宽6像素高8像素 */
/* 来源: font6x8 (fantasticmao/font6x8), GPL-3.0 */
/* 列式存储每字节一列bit0=顶行bit7=底行 */
const uint8_t LCD_F6x8[][6] = const uint8_t LCD_F6x8[][6] =
{ {
0x00,0x00,0x00,0x00,0x00,0x00, // 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0
0x00,0x00,0x00,0x00,0x00,0x00, // ! 1 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, // ! 1
0x00,0x00,0x00,0x00,0x00,0x00, // " 2 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, // " 2
0x00,0x00,0x00,0x00,0x00,0x00, // # 3 0x00, 0x14, 0x3E, 0x14, 0x3E, 0x14, // # 3
0x00,0x00,0x00,0x00,0x00,0x00, // $ 4 0x00, 0x04, 0x2A, 0x7F, 0x2A, 0x10, // $ 4
0x00,0x00,0x00,0x00,0x00,0x00, // % 5 0x00, 0x26, 0x10, 0x08, 0x04, 0x32, // % 5
0x00,0x00,0x00,0x00,0x00,0x00, // & 6 0x00, 0x1A, 0x25, 0x29, 0x10, 0x28, // & 6
0x00,0x00,0x00,0x00,0x00,0x00, // ' 7 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ' 7
0x00,0x00,0x00,0x00,0x00,0x00, // ( 8 0x00, 0x00, 0x3E, 0x41, 0x41, 0x00, // ( 8
0x00,0x00,0x00,0x00,0x00,0x00, // ) 9 0x00, 0x00, 0x41, 0x41, 0x3E, 0x00, // ) 9
0x00,0x00,0x00,0x00,0x00,0x00, // * 10 0x00, 0x28, 0x18, 0x0E, 0x18, 0x28, // * 10
0x00,0x00,0x00,0x00,0x00,0x00, // + 11 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // + 11
0x00,0x00,0x00,0x00,0x00,0x00, // , 12 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, // , 12
0x00,0x00,0x00,0x00,0x00,0x00, // - 13 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // - 13
0x00,0x00,0x00,0x00,0x00,0x00, // . 14 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // . 14
0x00,0x00,0x00,0x00,0x00,0x00, // / 15 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // / 15
0x00,0x00,0x00,0x00,0x00,0x00, // 0 16 0x00, 0x00, 0x1E, 0x21, 0x21, 0x1E, // 0 16
0x00,0x00,0x00,0x00,0x00,0x00, // 1 17 0x00, 0x00, 0x00, 0x22, 0x3F, 0x20, // 1 17
0x00,0x00,0x00,0x00,0x00,0x00, // 2 18 0x00, 0x00, 0x22, 0x31, 0x29, 0x26, // 2 18
0x00,0x00,0x00,0x00,0x00,0x00, // 3 19 0x00, 0x00, 0x12, 0x21, 0x25, 0x1A, // 3 19
0x00,0x00,0x00,0x00,0x00,0x00, // 4 20 0x00, 0x00, 0x0C, 0x0A, 0x3F, 0x08, // 4 20
0x00,0x00,0x00,0x00,0x00,0x00, // 5 21 0x00, 0x00, 0x17, 0x25, 0x25, 0x19, // 5 21
0x00,0x00,0x00,0x00,0x00,0x00, // 6 22 0x00, 0x00, 0x1C, 0x26, 0x25, 0x18, // 6 22
0x00,0x00,0x00,0x00,0x00,0x00, // 7 23 0x00, 0x00, 0x01, 0x39, 0x05, 0x03, // 7 23
0x00,0x00,0x00,0x00,0x00,0x00, // 8 24 0x00, 0x00, 0x1A, 0x25, 0x25, 0x1A, // 8 24
0x00,0x00,0x00,0x00,0x00,0x00, // 9 25 0x00, 0x00, 0x06, 0x09, 0x29, 0x1E, // 9 25
0x00,0x00,0x00,0x00,0x00,0x00, // : 26 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, // : 26
0x00,0x00,0x00,0x00,0x00,0x00, // ; 27 0x00, 0x00, 0x20, 0x14, 0x00, 0x00, // ; 27
0x00,0x00,0x00,0x00,0x00,0x00, // < 28 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, // < 28
0x00,0x00,0x00,0x00,0x00,0x00, // = 29 0x00, 0x00, 0x14, 0x14, 0x14, 0x00, // = 29
0x00,0x00,0x00,0x00,0x00,0x00, // > 30 0x00, 0x00, 0x22, 0x14, 0x08, 0x00, // > 30
0x00,0x00,0x00,0x00,0x00,0x00, // ? 31 0x00, 0x02, 0x01, 0x29, 0x06, 0x00, // ? 31
0x00,0x00,0x00,0x00,0x00,0x00, // @ 32 0x00, 0x1E, 0x21, 0x2D, 0x2E, 0x00, // @ 32
0x00,0x00,0x00,0x00,0x00,0x00, // A 33 0x00, 0x30, 0x0C, 0x0B, 0x0C, 0x30, // A 33
0x00,0x00,0x00,0x00,0x00,0x00, // B 34 0x00, 0x00, 0x3F, 0x25, 0x25, 0x1A, // B 34
0x00,0x00,0x00,0x00,0x00,0x00, // C 35 0x00, 0x00, 0x1E, 0x21, 0x21, 0x12, // C 35
0x00,0x00,0x00,0x00,0x00,0x00, // D 36 0x00, 0x00, 0x3F, 0x21, 0x21, 0x1E, // D 36
0x00,0x00,0x00,0x00,0x00,0x00, // E 37 0x00, 0x00, 0x3F, 0x25, 0x25, 0x25, // E 37
0x00,0x00,0x00,0x00,0x00,0x00, // F 38 0x00, 0x00, 0x3F, 0x05, 0x05, 0x05, // F 38
0x00,0x00,0x00,0x00,0x00,0x00, // G 39 0x00, 0x00, 0x1E, 0x21, 0x29, 0x1A, // G 39
0x00,0x00,0x00,0x00,0x00,0x00, // H 40 0x00, 0x00, 0x3F, 0x04, 0x04, 0x3F, // H 40
0x00,0x00,0x00,0x00,0x00,0x00, // I 41 0x00, 0x00, 0x21, 0x3F, 0x21, 0x00, // I 41
0x00,0x00,0x00,0x00,0x00,0x00, // J 42 0x00, 0x18, 0x20, 0x21, 0x1F, 0x00, // J 42
0x00,0x00,0x00,0x00,0x00,0x00, // K 43 0x00, 0x00, 0x3F, 0x0C, 0x12, 0x21, // K 43
0x00,0x00,0x00,0x00,0x00,0x00, // L 44 0x00, 0x00, 0x3F, 0x20, 0x20, 0x20, // L 44
0x00,0x00,0x00,0x00,0x00,0x00, // M 45 0x00, 0x3F, 0x06, 0x08, 0x06, 0x3F, // M 45
0x00,0x00,0x00,0x00,0x00,0x00, // N 46 0x00, 0x00, 0x3F, 0x06, 0x18, 0x3F, // N 46
0x00,0x00,0x00,0x00,0x00,0x00, // O 47 0x00, 0x00, 0x1E, 0x21, 0x21, 0x1E, // O 47
0x00,0x00,0x00,0x00,0x00,0x00, // P 48 0x00, 0x00, 0x3F, 0x09, 0x09, 0x06, // P 48
0x00,0x00,0x00,0x00,0x00,0x00, // Q 49 0x00, 0x1E, 0x21, 0x21, 0x5E, 0x00, // Q 49
0x00,0x00,0x00,0x00,0x00,0x00, // R 50 0x00, 0x00, 0x3F, 0x09, 0x19, 0x26, // R 50
0x00,0x00,0x00,0x00,0x00,0x00, // S 51 0x00, 0x00, 0x12, 0x25, 0x29, 0x12, // S 51
0x00,0x00,0x00,0x00,0x00,0x00, // T 52 0x00, 0x01, 0x01, 0x3F, 0x01, 0x01, // T 52
0x00,0x00,0x00,0x00,0x00,0x00, // U 53 0x00, 0x1F, 0x20, 0x20, 0x20, 0x1F, // U 53
0x00,0x00,0x00,0x00,0x00,0x00, // V 54 0x00, 0x0F, 0x10, 0x20, 0x10, 0x0F, // V 54
0x00,0x00,0x00,0x00,0x00,0x00, // W 55 0x00, 0x1F, 0x20, 0x1C, 0x20, 0x1F, // W 55
0x00,0x00,0x00,0x00,0x00,0x00, // X 56 0x00, 0x21, 0x12, 0x0C, 0x12, 0x21, // X 56
0x00,0x00,0x00,0x00,0x00,0x00, // Y 57 0x00, 0x03, 0x04, 0x38, 0x04, 0x03, // Y 57
0x00,0x00,0x00,0x00,0x00,0x00, // Z 58 0x00, 0x00, 0x31, 0x29, 0x25, 0x23, // Z 58
0x00,0x00,0x00,0x00,0x00,0x00, // [ 59 0x00, 0x00, 0x3F, 0x21, 0x21, 0x00, // [ 59
0x00,0x00,0x00,0x00,0x00,0x00, // \ 60 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, // \ 60
0x00,0x00,0x00,0x00,0x00,0x00, // ] 61 0x00, 0x00, 0x21, 0x21, 0x3F, 0x00, // ] 61
0x00,0x00,0x00,0x00,0x00,0x00, // ^ 62 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^ 62
0x00,0x00,0x00,0x00,0x00,0x00, // _ 63 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _ 63
0x00,0x00,0x00,0x00,0x00,0x00, // ` 64 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, // ` 64
0x00,0x00,0x00,0x00,0x00,0x00, // a 65 0x00, 0x18, 0x24, 0x24, 0x18, 0x20, // a 65
0x00,0x00,0x00,0x00,0x00,0x00, // b 66 0x00, 0x00, 0x3F, 0x24, 0x24, 0x18, // b 66
0x00,0x00,0x00,0x00,0x00,0x00, // c 67 0x00, 0x00, 0x18, 0x24, 0x24, 0x00, // c 67
0x00,0x00,0x00,0x00,0x00,0x00, // d 68 0x00, 0x00, 0x18, 0x24, 0x24, 0x3F, // d 68
0x00,0x00,0x00,0x00,0x00,0x00, // e 69 0x00, 0x00, 0x1C, 0x2A, 0x2A, 0x0C, // e 69
0x00,0x00,0x00,0x00,0x00,0x00, // f 70 0x00, 0x04, 0x04, 0x3E, 0x05, 0x05, // f 70
0x00,0x00,0x00,0x00,0x00,0x00, // g 71 0x00, 0x00, 0x0C, 0x52, 0x52, 0x3E, // g 71
0x00,0x00,0x00,0x00,0x00,0x00, // h 72 0x00, 0x00, 0x3F, 0x04, 0x04, 0x38, // h 72
0x00,0x00,0x00,0x00,0x00,0x00, // i 73 0x00, 0x00, 0x24, 0x3D, 0x20, 0x00, // i 73
0x00,0x00,0x00,0x00,0x00,0x00, // j 74 0x00, 0x20, 0x40, 0x44, 0x3D, 0x00, // j 74
0x00,0x00,0x00,0x00,0x00,0x00, // k 75 0x00, 0x00, 0x3F, 0x08, 0x14, 0x22, // k 75
0x00,0x00,0x00,0x00,0x00,0x00, // l 76 0x00, 0x00, 0x1F, 0x20, 0x20, 0x10, // l 76
0x00,0x00,0x00,0x00,0x00,0x00, // m 77 0x00, 0x3E, 0x04, 0x18, 0x04, 0x3E, // m 77
0x00,0x00,0x00,0x00,0x00,0x00, // n 78 0x00, 0x00, 0x3C, 0x02, 0x02, 0x3C, // n 78
0x00,0x00,0x00,0x00,0x00,0x00, // o 79 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1C, // o 79
0x00,0x00,0x00,0x00,0x00,0x00, // p 80 0x00, 0x00, 0x7E, 0x12, 0x12, 0x0C, // p 80
0x00,0x00,0x00,0x00,0x00,0x00, // q 81 0x00, 0x00, 0x0C, 0x12, 0x12, 0x7E, // q 81
0x00,0x00,0x00,0x00,0x00,0x00, // r 82 0x00, 0x00, 0x04, 0x38, 0x04, 0x04, // r 82
0x00,0x00,0x00,0x00,0x00,0x00, // s 83 0x00, 0x00, 0x04, 0x2A, 0x2A, 0x10, // s 83
0x00,0x00,0x00,0x00,0x00,0x00, // t 84 0x00, 0x00, 0x04, 0x1E, 0x24, 0x10, // t 84
0x00,0x00,0x00,0x00,0x00,0x00, // u 85 0x00, 0x1C, 0x20, 0x20, 0x20, 0x1C, // u 85
0x00,0x00,0x00,0x00,0x00,0x00, // v 86 0x00, 0x0C, 0x10, 0x20, 0x10, 0x0C, // v 86
0x00,0x00,0x00,0x00,0x00,0x00, // w 87 0x00, 0x1C, 0x20, 0x18, 0x20, 0x1C, // w 87
0x00,0x00,0x00,0x00,0x00,0x00, // x 88 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, // x 88
0x00,0x00,0x00,0x00,0x00,0x00, // y 89 0x00, 0x04, 0x48, 0x30, 0x08, 0x04, // y 89
0x00,0x00,0x00,0x00,0x00,0x00, // z 90 0x00, 0x00, 0x24, 0x34, 0x2C, 0x24, // z 90
0x00,0x00,0x00,0x00,0x00,0x00, // { 91 0x00, 0x08, 0x08, 0x36, 0x41, 0x41, // { 91
0x00,0x00,0x00,0x00,0x00,0x00, // | 92 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, // | 92
0x00,0x00,0x00,0x00,0x00,0x00, // } 93 0x00, 0x41, 0x41, 0x36, 0x08, 0x08, // } 93
0x00,0x00,0x00,0x00,0x00,0x00 // ~ 94 0x00, 0x08, 0x04, 0x08, 0x10, 0x08 // ~ 94
}; };

View File

@@ -15,13 +15,8 @@
#include "spi.h" #include "spi.h"
#include <stdio.h> #include <stdio.h>
/* #define X_OFFSET 0
* 部分屏幕厂家虽然标称128x160但实际使用132x162驱动晶片 #define Y_OFFSET 0
* 左右各偏移2列上下偏移1行才能正确显示。
* 根据自己的屏幕实测调整这两个值。
*/
#define X_OFFSET 2
#define Y_OFFSET 1
uint16_t s_lcd_width = LCD_WIDTH; uint16_t s_lcd_width = LCD_WIDTH;
uint16_t s_lcd_height = LCD_HEIGHT; uint16_t s_lcd_height = LCD_HEIGHT;

View File

@@ -340,7 +340,7 @@
<MiscControls></MiscControls> <MiscControls></MiscControls>
<Define>USE_HAL_DRIVER,STM32F103xB</Define> <Define>USE_HAL_DRIVER,STM32F103xB</Define>
<Undefine></Undefine> <Undefine></Undefine>
<IncludePath>../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;../Drivers/BSP/MultiButton;../Drivers/BSP/LCD;..\Drivers\BSP\AT24C02</IncludePath> <IncludePath>../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;../Drivers/BSP/MultiButton;../Drivers/BSP/LCD;..\Drivers\BSP\AT24C02;..\User</IncludePath>
</VariousControls> </VariousControls>
</Cads> </Cads>
<Aads> <Aads>
@@ -1152,6 +1152,16 @@
</File> </File>
</Files> </Files>
</Group> </Group>
<Group>
<GroupName>Application/User</GroupName>
<Files>
<File>
<FileName>menu.c</FileName>
<FileType>1</FileType>
<FilePath>..\User\menu.c</FilePath>
</File>
</Files>
</Group>
<Group> <Group>
<GroupName>::CMSIS</GroupName> <GroupName>::CMSIS</GroupName>
</Group> </Group>

View File

@@ -36,6 +36,7 @@
"stm32f103c8t6\button_driver_1.o" "stm32f103c8t6\button_driver_1.o"
"stm32f103c8t6\multi_button_1.o" "stm32f103c8t6\multi_button_1.o"
"stm32f103c8t6\at24c02.o" "stm32f103c8t6\at24c02.o"
"stm32f103c8t6\menu.o"
--strict --scatter "STM32F103C8T6\STM32F103C8T6.sct" --strict --scatter "STM32F103C8T6\STM32F103C8T6.sct"
--summary_stderr --info summarysizes --map --load_addr_map_info --xref --callgraph --symbols --summary_stderr --info summarysizes --map --load_addr_map_info --xref --callgraph --symbols
--info sizes --info totals --info unused --info veneers --info sizes --info totals --info unused --info veneers

Binary file not shown.

356
STM32F103C8T6/User/menu.c Normal file
View File

@@ -0,0 +1,356 @@
/*
* 模块名称:菜单系统
* 模块功能通过LCD显示参数按键导航/调整AT24C02存储
* 适用平台STM32F103C8T6
* 作者:王建锋
* 创建日期2026-07-14
* 修改记录:
* 2026-07-14 王建锋 创建初始版本
*/
#include "menu.h"
#include "button_driver.h"
#include "at24c02.h"
#include "lcd.h"
#include "tim.h"
#include <stdio.h>
#define COLOR_GRAY 0x8410 /* RGB565 灰 */
/* ======================== EEPROM 存储布局 ======================== */
#define EEPROM_DATA_ADDR 0x10 /* 参数存储起始地址 */
#define EEPROM_MAGIC 0xA55A /* 有效数据标识 */
#define EEPROM_MAGIC_ADDR 0x0E /* Magic 存储地址2字节 */
/* ======================== 参数定义 ======================== */
typedef struct {
const char *name; /* 显示名称 */
const char *unit; /* 单位 */
int32_t default_val; /* 默认值 */
int32_t min; /* 最小值 */
int32_t max; /* 最大值 */
int32_t step; /* 步进 */
} ParamDef_t;
static const ParamDef_t s_param_def[PARAM_COUNT] = {
{"PWM1", "", 500, 0, 999, 50 },
{"PWM2", "", 500, 0, 999, 50 },
{"PWM3", "", 500, 0, 999, 50 },
{"PWM4", "", 500, 0, 999, 50 },
};
/* ======================== 运行时状态 ======================== */
static int32_t s_param_val[PARAM_COUNT]; /* 当前参数值 */
static uint8_t s_sel = 0; /* 选中的参数索引 */
static uint8_t s_editing = 0; /* 0=导航, 1=编辑 */
static uint8_t s_redraw = 1; /* 需要重绘标志 */
/* ======================== 静态函数声明 ======================== */
static void draw_menu(void);
static void load_params(void);
static int save_params(void);
static void apply_param(uint8_t idx);
/* ======================== 公开函数 ======================== */
/*
* 函数功能:菜单系统初始化
* 入口参数:无
* 返回值:无
* 函数说明从EEPROM加载参数清屏绘制初始界面
*/
void menu_init(void)
{
lcd_draw_string(0, 0, "Menu Loading...", COLOR_WHITE, COLOR_BLACK);
load_params();
s_sel = 0;
s_editing = 0;
s_redraw = 1;
apply_param(PARAM_PWM1);
apply_param(PARAM_PWM2);
apply_param(PARAM_PWM3);
apply_param(PARAM_PWM4);
st7735s_fill_screen(COLOR_BLACK);
draw_menu();
}
/*
* 函数功能:菜单系统循环处理
* 入口参数:无
* 返回值:无
* 函数说明:轮询按键事件,更新参数/界面。需在主循环中频繁调用
*/
void menu_process(void)
{
ButtonEvent evt;
uint8_t i;
int32_t *p_val;
/* 依次检查5个按键的事件 */
for (i = 0; i < 5; i++) {
evt = button_driver_get_event(i);
if (evt == BTN_NONE_PRESS) {
continue;
}
if (evt == BTN_SINGLE_CLICK) {
switch (i) {
case 0: /* KEY1: 上移 */
if (!s_editing) {
if (s_sel > 0) {
s_sel--;
} else {
s_sel = PARAM_COUNT - 1;
}
s_redraw = 1;
}
break;
case 1: /* KEY2: 减小 */
p_val = &s_param_val[s_sel];
*p_val -= s_param_def[s_sel].step;
if (*p_val < s_param_def[s_sel].min) {
*p_val = s_param_def[s_sel].min;
}
s_editing = 1;
s_redraw = 1;
apply_param(s_sel);
break;
case 2: /* KEY3: 下移 */
if (!s_editing) {
s_sel++;
if (s_sel >= PARAM_COUNT) {
s_sel = 0;
}
s_redraw = 1;
}
break;
case 3: /* KEY4: 增大 */
p_val = &s_param_val[s_sel];
*p_val += s_param_def[s_sel].step;
if (*p_val > s_param_def[s_sel].max) {
*p_val = s_param_def[s_sel].max;
}
s_editing = 1;
s_redraw = 1;
apply_param(s_sel);
break;
case 4: /* KEY5: 确认保存 */
if (save_params() == AT24C02_OK) {
lcd_draw_string(0, 80, "SAVED! ", COLOR_GREEN, COLOR_BLACK);
printf("Menu: params saved\r\n");
} else {
lcd_draw_string(0, 80, "SAVE FAIL! ", COLOR_RED, COLOR_BLACK);
printf("Menu: params save FAIL\r\n");
}
s_editing = 0;
s_redraw = 1;
break;
default:
break;
}
}
/* 只处理一个按键事件每帧 */
break;
}
if (s_redraw) {
draw_menu();
s_redraw = 0;
}
}
/*
* 函数功能:获取指定参数的当前值
* 入口参数id - 参数ID
* 返回值:参数值
*/
int32_t menu_get_param(ParamId_t id)
{
if (id >= PARAM_COUNT) {
return 0;
}
return s_param_val[id];
}
/*
* 函数功能强制保存参数到EEPROM
* 入口参数:无
* 返回值0=成功,负值=失败
*/
int menu_save_params(void)
{
return save_params();
}
/* ======================== 内部函数 ======================== */
/*
* 函数功能从EEPROM加载参数
* 入口参数:无
* 返回值:无
* 函数说明读取EEPROM中存储的参数值若Magic不对则使用默认值
*/
static void load_params(void)
{
uint8_t magic_buf[2];
uint8_t data_buf[PARAM_COUNT * 4];
int ret;
uint8_t i;
/* 先读取Magic */
ret = at24c02_read_buf(EEPROM_MAGIC_ADDR, magic_buf, 2);
if ((ret != AT24C02_OK) ||
(magic_buf[0] != ((EEPROM_MAGIC >> 8) & 0xFF)) ||
(magic_buf[1] != (EEPROM_MAGIC & 0xFF))) {
/* Magic不匹配使用默认值 */
for (i = 0; i < PARAM_COUNT; i++) {
s_param_val[i] = s_param_def[i].default_val;
}
printf("Menu: using defaults (no saved data)\r\n");
return;
}
/* 读取参数数据 */
ret = at24c02_read_buf(EEPROM_DATA_ADDR, data_buf, PARAM_COUNT * 4);
if (ret != AT24C02_OK) {
for (i = 0; i < PARAM_COUNT; i++) {
s_param_val[i] = s_param_def[i].default_val;
}
printf("Menu: EEPROM read fail, using defaults\r\n");
return;
}
/* 解析数据(小端:低字节在前) */
for (i = 0; i < PARAM_COUNT; i++) {
s_param_val[i] = (int32_t)(
(uint32_t)data_buf[i * 4 + 0] |
((uint32_t)data_buf[i * 4 + 1] << 8) |
((uint32_t)data_buf[i * 4 + 2] << 16) |
((uint32_t)data_buf[i * 4 + 3] << 24));
/* 钳位到有效范围 */
if (s_param_val[i] < s_param_def[i].min) {
s_param_val[i] = s_param_def[i].min;
}
if (s_param_val[i] > s_param_def[i].max) {
s_param_val[i] = s_param_def[i].max;
}
}
printf("Menu: params loaded from EEPROM\r\n");
}
/*
* 函数功能保存参数到EEPROM
* 入口参数:无
* 返回值0=成功,负值=失败
* 函数说明先写Magic标识再写参数数据
*/
static int save_params(void)
{
uint8_t magic_buf[2];
uint8_t data_buf[PARAM_COUNT * 4];
uint8_t i;
int ret;
/* 打包Magic */
magic_buf[0] = (EEPROM_MAGIC >> 8) & 0xFF;
magic_buf[1] = EEPROM_MAGIC & 0xFF;
/* 打包参数(小端) */
for (i = 0; i < PARAM_COUNT; i++) {
data_buf[i * 4 + 0] = (uint8_t)(s_param_val[i] & 0xFF);
data_buf[i * 4 + 1] = (uint8_t)((s_param_val[i] >> 8) & 0xFF);
data_buf[i * 4 + 2] = (uint8_t)((s_param_val[i] >> 16) & 0xFF);
data_buf[i * 4 + 3] = (uint8_t)((s_param_val[i] >> 24) & 0xFF);
}
/* 写入EEPROM */
ret = at24c02_write_buf(EEPROM_MAGIC_ADDR, magic_buf, 2);
if (ret != AT24C02_OK) {
return ret;
}
ret = at24c02_write_buf(EEPROM_DATA_ADDR, data_buf, PARAM_COUNT * 4);
if (ret != AT24C02_OK) {
return ret;
}
return AT24C02_OK;
}
/*
* 函数功能将指定参数立即应用到对应PWM通道
* 入口参数idx - 参数索引 (0=PWM1, 1=PWM2, 2=PWM3, 3=PWM4)
* 返回值:无
*/
static void apply_param(uint8_t idx)
{
uint32_t duty = (uint32_t)s_param_val[idx];
static const uint16_t ch_map[4] = {
TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4
};
if (idx < PARAM_COUNT) {
__HAL_TIM_SET_COMPARE(&htim3, ch_map[idx], duty);
printf("PWM%d duty=%lu\r\n", idx + 1, (unsigned long)duty);
}
}
/*
* 函数功能:绘制菜单界面
* 入口参数:无
* 返回值:无
* 函数说明:清空参数区域,重新绘制所有参数行
*/
static void draw_menu(void)
{
char buf[24];
uint8_t i;
uint16_t color;
/* 绘制标题行 */
lcd_draw_string(0, 0, "-- PARAM MENU --", COLOR_CYAN, COLOR_BLACK);
/* 绘制各参数每行16像素从y=16开始 */
for (i = 0; i < PARAM_COUNT; i++) {
if (i == s_sel) {
if (s_editing) {
/* 编辑态:高亮 + 左右尖括号 */
snprintf(buf, sizeof(buf), ">%s:%ld %s< ",
s_param_def[i].name,
(long)s_param_val[i],
s_param_def[i].unit);
color = COLOR_YELLOW;
} else {
/* 选中态:箭头 + 高亮 */
snprintf(buf, sizeof(buf), ">%s:%ld %s ",
s_param_def[i].name,
(long)s_param_val[i],
s_param_def[i].unit);
color = COLOR_WHITE;
}
} else {
/* 未选中:普通显示 */
snprintf(buf, sizeof(buf), " %s:%ld %s ",
s_param_def[i].name,
(long)s_param_val[i],
s_param_def[i].unit);
color = COLOR_GRAY;
}
/* 填充整行背景以覆盖之前内容 */
lcd_draw_string(0, 16 + i * 16, buf, color, COLOR_BLACK);
}
/* 底部提示 */
if (s_editing) {
lcd_draw_string_6x8(0, 140, "KEY5:SAVE ", COLOR_GREEN, COLOR_BLACK);
} else {
lcd_draw_string_6x8(0, 140, "K1/K3:Sel K2/K4:Adj", COLOR_GRAY, COLOR_BLACK);
}
}

55
STM32F103C8T6/User/menu.h Normal file
View File

@@ -0,0 +1,55 @@
#ifndef __MENU_H
#define __MENU_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/* 参数数量 */
#define PARAM_COUNT 4
/* 参数ID枚举 */
typedef enum {
PARAM_PWM1 = 0, /* PA6 TIM3_CH1 */
PARAM_PWM2, /* PA7 TIM3_CH2 */
PARAM_PWM3, /* PB0 TIM3_CH3 */
PARAM_PWM4, /* PB1 TIM3_CH4 */
} ParamId_t;
/*
* 函数功能:菜单系统初始化
* 入口参数:无
* 返回值:无
* 函数说明从EEPROM加载参数清屏绘制初始界面
*/
void menu_init(void);
/*
* 函数功能:菜单系统循环处理
* 入口参数:无
* 返回值:无
* 函数说明:轮询按键事件,更新参数/界面。需在主循环中频繁调用
*/
void menu_process(void);
/*
* 函数功能:获取指定参数的当前值
* 入口参数id - 参数ID
* 返回值:参数值
*/
int32_t menu_get_param(ParamId_t id);
/*
* 函数功能强制保存参数到EEPROM
* 入口参数:无
* 返回值0=成功,负值=失败
*/
int menu_save_params(void);
#ifdef __cplusplus
}
#endif
#endif /* __MENU_H */

View File

@@ -1,14 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
STM32 ST7735S LCD 字模生成工具Pillow 版) STM32 ST7735S LCD CJK 字模生成工具Pillow 版)
- ASCII 8×16生成 lcd_data.c 生成 hz16_data.c + hz16_data.h含二分查找函数
- CJK 16×16生成 hz16_data.c + hz16_data.h含二分查找函数
用法: 用法:
# ASCII
python font_gen.py --font simsun --size 56 --threshold 64
# CJK 常用汉字GB2312 一级,约 3755 字) # CJK 常用汉字GB2312 一级,约 3755 字)
python font_gen.py --cjk --font simsun --size 32 --threshold 64 python font_gen.py --cjk --font simsun --size 32 --threshold 64
@@ -19,7 +15,6 @@ STM32 ST7735S LCD 字模生成工具Pillow 版)
import os import os
import sys import sys
import argparse import argparse
import struct
try: try:
from PIL import Image, ImageFont, ImageDraw from PIL import Image, ImageFont, ImageDraw
@@ -27,12 +22,44 @@ except ImportError:
print("请安装 Pillow: pip install Pillow", file=sys.stderr) print("请安装 Pillow: pip install Pillow", file=sys.stderr)
sys.exit(1) sys.exit(1)
# ── Otsu 自适应阈值 ──
def otsu_threshold(pixels: list) -> int:
"""Otsu 算法自动计算最佳二值化阈值"""
hist = [0] * 256
for v in pixels:
hist[v] += 1
total = len(pixels)
sum_all = sum(i * hist[i] for i in range(256))
sum_bg = 0
w_bg = 0
w_fg = 0
max_var = 0
best = 128
for t in range(256):
w_bg += hist[t]
if w_bg == 0:
continue
w_fg = total - w_bg
if w_fg == 0:
break
sum_bg += t * hist[t]
mean_bg = sum_bg / w_bg
mean_fg = (sum_all - sum_bg) / w_fg
between_var = w_bg * w_fg * (mean_bg - mean_fg) * (mean_bg - mean_fg)
if between_var > max_var:
max_var = between_var
best = t
return best
# ── 通用渲染 ── # ── 通用渲染 ──
def render_char(font, char: str, w: int, h: int, scale: int = 1, def render_char(font, char: str, w: int, h: int, scale: int = 1,
threshold: int = 64) -> list: threshold: int = None) -> list:
""" """
在 w*scale × h*scale 画布上渲染字符,缩放到 w×h 在 w*scale × h*scale 画布上渲染字符,缩放到 w×h
threshold=None 时自动使用 Otsu 算法
返回逐行字节列表MSB 优先) 返回逐行字节列表MSB 优先)
""" """
bw, bh = w * scale, h * scale bw, bh = w * scale, h * scale
@@ -42,13 +69,20 @@ def render_char(font, char: str, w: int, h: int, scale: int = 1,
bbox = draw.textbbox((0, 0), char, font=font) bbox = draw.textbbox((0, 0), char, font=font)
tw = bbox[2] - bbox[0] tw = bbox[2] - bbox[0]
th = bbox[3] - bbox[1] th = bbox[3] - bbox[1]
cx = (bw - tw) // 2 - bbox[0]
cy = (bh - th) // 2 - bbox[1] # 防止 bbox[0]/bbox[1] 为负导致偏移错误
left = bbox[0] if bbox[0] > 0 else 0
top = bbox[1] if bbox[1] > 0 else 0
cx = (bw - tw) // 2 - left
cy = (bh - th) // 2 - top
draw.text((cx, cy), char, font=font, fill=255) draw.text((cx, cy), char, font=font, fill=255)
small = img.resize((w, h), Image.LANCZOS) small = img.resize((w, h), Image.LANCZOS)
pixels = list(small.getdata()) pixels = list(small.getdata())
if threshold is None:
threshold = otsu_threshold(pixels)
bytes_per_row = (w + 7) // 8 bytes_per_row = (w + 7) // 8
result = [] result = []
for r in range(h): for r in range(h):
@@ -62,57 +96,6 @@ def render_char(font, char: str, w: int, h: int, scale: int = 1,
return result return result
# ── ASCII 8×16 ──
def generate_ascii(output_path: str, font_path: str, size: int,
threshold: int, scale: int = 4):
font = ImageFont.truetype(font_path, size)
chars = list(range(32, 127))
N = len(chars)
lines = [
'#include "lcd_data.h"',
'/*ASCII字模数据*********************/',
'',
'/*宽8像素高16像素*/',
f'/* 字体: {os.path.basename(font_path)}, 字号: {size}, 阈值: {threshold} */',
'const uint8_t LCD_F8x16[][16] =',
'{',
]
for idx, ch in enumerate(chars):
if ch == ord('_'):
bitmap = [0x00] * 15 + [0x7F]
else:
bitmap = render_char(font, chr(ch), 8, 16, scale, threshold)
show = chr(ch) if 32 <= ch <= 126 else '?'
comma = ',' if idx < N - 1 else ''
line1 = ', '.join(f'0x{b:02X}' for b in bitmap[:8])
line2 = ', '.join(f'0x{b:02X}' for b in bitmap[8:])
lines.append(f' {line1},')
lines.append(f' {line2}{comma} // {show} {idx}')
if (idx + 1) % 10 == 0:
print(f" ASCII 进度: {idx+1}/{N}")
lines.append('};')
lines.append('')
lines.append('/*宽6像素高8像素未使用*/')
lines.append('const uint8_t LCD_F6x8[][6] =')
lines.append('{')
for idx, ch in enumerate(chars):
show = chr(ch) if 32 <= ch <= 126 else '?'
comma = ',' if idx < N - 1 else ''
lines.append(f' 0x00,0x00,0x00,0x00,0x00,0x00{comma} // {show} {idx}')
lines.append('};')
lines.append('')
content = '\n'.join(lines)
with open(output_path, 'w', encoding='utf-8', newline='\n') as f:
f.write(content)
print(f"已生成 ASCII 字模: {output_path}")
# ── CJK 16×16 ── # ── CJK 16×16 ──
CJK_W, CJK_H = 16, 16 CJK_W, CJK_H = 16, 16
@@ -344,35 +327,33 @@ def _resolve_font(name_or_path: str) -> str:
def main(): def main():
p = argparse.ArgumentParser(description='字模生成工具') p = argparse.ArgumentParser(description='CJK 字模生成工具')
p.add_argument('--font', default='simsun', help='字体名称或路径') p.add_argument('--font', default='simsun', help='字体名称或路径')
p.add_argument('--size', type=int, default=64, help='渲染字号') p.add_argument('--size', type=int, default=None, help='渲染字号')
p.add_argument('--threshold', type=int, default=50, help='二值化阈值(默认50') p.add_argument('--threshold', type=int, default=None, help='二值化阈值(默认 Otsu 自适应')
p.add_argument('--scale', type=int, default=4, help='缩放倍率') p.add_argument('--scale', type=int, default=None, help='缩放倍率(越大边缘越平滑)')
p.add_argument('--cjk', action='store_true', help='生成 CJK 汉字字模(必须指定)')
g = p.add_argument_group('ASCII 8×16') p.add_argument('--common', action='store_true', help='CJK仅常用汉字GB2312 一级)')
g.add_argument('--ascii', action='store_true', help='生成 ASCII 字模(默认)') p.add_argument('--gb2312', action='store_true', help='CJKGB2312 全部汉字')
p.add_argument('--punct', action='store_true', help='CJK加入中文标点/全角字符')
g = p.add_argument_group('CJK 16×16') p.add_argument('--string', type=str, default=None, help='CJK仅生成指定字符串中的字符节省空间')
g.add_argument('--cjk', action='store_true', help='生成 CJK 汉字字模') p.add_argument('--output', '-o', default=None, help='CJK 输出基名(不含扩展名)')
g.add_argument('--common', action='store_true', help='CJK仅常用汉字GB2312 一级)')
g.add_argument('--gb2312', action='store_true', help='CJKGB2312 全部汉字')
g.add_argument('--punct', action='store_true', help='CJK加入中文标点/全角字符')
g.add_argument('--string', type=str, default=None, help='CJK仅生成指定字符串中的字符节省空间')
g.add_argument('--output', '-o', default=None, help='CJK 输出基名(不含扩展名)')
args = p.parse_args() args = p.parse_args()
font_path = _resolve_font(args.font) font_path = _resolve_font(args.font)
print(f"字体: {font_path}") print(f"字体: {font_path}")
if not args.cjk:
print("请指定 --cjk 来生成 CJK 字库ASCII 字库已不再由此工具生成")
return
proj_dir = os.path.abspath(os.path.join( proj_dir = os.path.abspath(os.path.join(
os.path.dirname(__file__), os.path.dirname(__file__),
'..', 'STM32F103C8T6', 'Drivers', 'BSP', 'LCD')) '..', 'STM32F103C8T6', 'Drivers', 'BSP', 'LCD'))
if args.cjk: size = args.size if args.size is not None else 48
size = args.size or 48 scale = args.scale if args.scale is not None else 3
scale = args.scale or 3
out_base = args.output or os.path.join(proj_dir, 'hz16_data') out_base = args.output or os.path.join(proj_dir, 'hz16_data')
if args.string is not None: if args.string is not None:
@@ -382,12 +363,6 @@ def main():
generate_cjk(out_base, font_path, size, args.threshold, scale, generate_cjk(out_base, font_path, size, args.threshold, scale,
mode='common' if args.common else ('gb2312' if args.gb2312 else 'common'), mode='common' if args.common else ('gb2312' if args.gb2312 else 'common'),
punct=args.punct) punct=args.punct)
else:
# 默认 ASCII
size = args.size or 56
scale = args.scale or 4
out = os.path.join(proj_dir, 'lcd_data.c')
generate_ascii(out, font_path, size, args.threshold, scale)
if __name__ == '__main__': if __name__ == '__main__':