├── Example └── main.c ├── README.md ├── User ├── Inc │ ├── font.h │ ├── oled_draw.h │ ├── oled_menu.h │ └── oled_menu_types.h └── Src │ ├── font.c │ ├── oled_draw.c │ └── oled_menu.c └── images ├── 2025-02-28012933.png ├── 2025-02-28013000.png ├── 2025-02-28013026.png ├── 2025-02-28013033.png ├── 2025-02-28013039.png └── 2025-02-28013059.png /Example/main.c: -------------------------------------------------------------------------------- 1 | /* USER CODE BEGIN Header */ 2 | /** 3 | ****************************************************************************** 4 | * @file : main.c 5 | * @brief : Main program body 6 | ****************************************************************************** 7 | * @attention 8 | * 9 | * Copyright (c) 2025 STMicroelectronics. 10 | * All rights reserved. 11 | * 12 | * This software is licensed under terms that can be found in the LICENSE file 13 | * in the root directory of this software component. 14 | * If no LICENSE file comes with this software, it is provided AS-IS. 15 | * 16 | ****************************************************************************** 17 | */ 18 | /* USER CODE END Header */ 19 | /* Includes ------------------------------------------------------------------*/ 20 | #include "main.h" 21 | #include "dma.h" 22 | #include "i2c.h" 23 | #include "usart.h" 24 | #include "gpio.h" 25 | 26 | /* Private includes ----------------------------------------------------------*/ 27 | /* USER CODE BEGIN Includes */ 28 | #include "oled_menu.h" 29 | #include "oled_draw.h" 30 | #include "font.h" 31 | 32 | /* USER CODE END Includes */ 33 | 34 | /* Private typedef -----------------------------------------------------------*/ 35 | /* USER CODE BEGIN PTD */ 36 | 37 | /* USER CODE END PTD */ 38 | 39 | /* Private define ------------------------------------------------------------*/ 40 | /* USER CODE BEGIN PD */ 41 | 42 | /* USER CODE END PD */ 43 | 44 | /* Private macro -------------------------------------------------------------*/ 45 | /* USER CODE BEGIN PM */ 46 | 47 | /* USER CODE END PM */ 48 | 49 | /* Private variables ---------------------------------------------------------*/ 50 | 51 | /* USER CODE BEGIN PV */ 52 | 53 | /* USER CODE END PV */ 54 | 55 | /* Private function prototypes -----------------------------------------------*/ 56 | void SystemClock_Config(void); 57 | /* USER CODE BEGIN PFP */ 58 | int fputc(int ch, FILE *f) 59 | { 60 | HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff); 61 | return ch; 62 | } 63 | int fgetc(FILE *f) 64 | { 65 | uint8_t ch = 0; 66 | HAL_UART_Receive(&huart1, &ch, 1, 0xffff); 67 | return ch; 68 | } 69 | /* USER CODE END PFP */ 70 | 71 | /* Private user code ---------------------------------------------------------*/ 72 | /* USER CODE BEGIN 0 */ 73 | 74 | /* USER CODE END 0 */ 75 | 76 | /** 77 | * @brief The application entry point. 78 | * @retval int 79 | */ 80 | int main(void) 81 | { 82 | 83 | /* USER CODE BEGIN 1 */ 84 | 85 | /* USER CODE END 1 */ 86 | 87 | /* MCU Configuration--------------------------------------------------------*/ 88 | 89 | /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ 90 | HAL_Init(); 91 | 92 | /* USER CODE BEGIN Init */ 93 | 94 | /* USER CODE END Init */ 95 | 96 | /* Configure the system clock */ 97 | SystemClock_Config(); 98 | 99 | /* USER CODE BEGIN SysInit */ 100 | 101 | /* USER CODE END SysInit */ 102 | 103 | /* Initialize all configured peripherals */ 104 | MX_GPIO_Init(); 105 | MX_DMA_Init(); 106 | MX_I2C1_Init(); 107 | MX_USART1_UART_Init(); 108 | /* USER CODE BEGIN 2 */ 109 | 110 | /* USER CODE END 2 */ 111 | 112 | /* Infinite loop */ 113 | /* USER CODE BEGIN WHILE */ 114 | printf("ready:)\n"); 115 | 116 | int switchData = 0; 117 | int switchData2 = 0; 118 | 119 | int data = 10; 120 | 121 | AddMenuItem(&mainMenu, "example2", NULL, NULL, NONE_CTRL, NULL); 122 | AddMenuItem(&mainMenu, "switch", FunctionForCtrl, NULL, SWITCH_CTRL, &switchData); 123 | AddMenuItem(&mainMenu, "switch", FunctionForCtrl, NULL, SWITCH_CTRL, &switchData2); 124 | 125 | 126 | 127 | 128 | 129 | Menutypedef *subMenu = AddMenu("subMenu", NULL, 0, &mainMenu); 130 | AddMenuItem(subMenu, "exampleForSub", NULL, NULL, NONE_CTRL, NULL); 131 | 132 | AddMenuItem(&mainMenu, "nextMenu", FunctionForNextMenu, subMenu, NONE_CTRL, NULL); 133 | 134 | UI_Init(); 135 | while (1) 136 | { 137 | 138 | UI_UpDate(); 139 | UI_Move(); 140 | UI_Show(); 141 | 142 | /* USER CODE END WHILE */ 143 | 144 | /* USER CODE BEGIN 3 */ 145 | } 146 | /* USER CODE END 3 */ 147 | } 148 | 149 | /** 150 | * @brief System Clock Configuration 151 | * @retval None 152 | */ 153 | void SystemClock_Config(void) 154 | { 155 | RCC_OscInitTypeDef RCC_OscInitStruct = {0}; 156 | RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; 157 | 158 | /** Configure the main internal regulator output voltage 159 | */ 160 | __HAL_RCC_PWR_CLK_ENABLE(); 161 | __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); 162 | 163 | /** Initializes the RCC Oscillators according to the specified parameters 164 | * in the RCC_OscInitTypeDef structure. 165 | */ 166 | RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; 167 | RCC_OscInitStruct.HSIState = RCC_HSI_ON; 168 | RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; 169 | RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; 170 | RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; 171 | RCC_OscInitStruct.PLL.PLLM = 8; 172 | RCC_OscInitStruct.PLL.PLLN = 168; 173 | RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; 174 | RCC_OscInitStruct.PLL.PLLQ = 4; 175 | if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) 176 | { 177 | Error_Handler(); 178 | } 179 | 180 | /** Initializes the CPU, AHB and APB buses clocks 181 | */ 182 | RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK 183 | |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; 184 | RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; 185 | RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; 186 | RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; 187 | RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; 188 | 189 | if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) 190 | { 191 | Error_Handler(); 192 | } 193 | } 194 | 195 | /* USER CODE BEGIN 4 */ 196 | 197 | /* USER CODE END 4 */ 198 | 199 | /** 200 | * @brief This function is executed in case of error occurrence. 201 | * @retval None 202 | */ 203 | void Error_Handler(void) 204 | { 205 | /* USER CODE BEGIN Error_Handler_Debug */ 206 | /* User can add his own implementation to report the HAL error return state */ 207 | __disable_irq(); 208 | while (1) 209 | { 210 | } 211 | /* USER CODE END Error_Handler_Debug */ 212 | } 213 | 214 | #ifdef USE_FULL_ASSERT 215 | /** 216 | * @brief Reports the name of the source file and the source line number 217 | * where the assert_param error has occurred. 218 | * @param file: pointer to the source file name 219 | * @param line: assert_param error line source number 220 | * @retval None 221 | */ 222 | void assert_failed(uint8_t *file, uint32_t line) 223 | { 224 | /* USER CODE BEGIN 6 */ 225 | /* User can add his own implementation to report the file name and line number, 226 | ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ 227 | /* USER CODE END 6 */ 228 | } 229 | #endif /* USE_FULL_ASSERT */ 230 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OLED Menu System for STM32 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 4 | 5 | OLED多级菜单系统,支持动态动画效果和多种交互控件,适用于嵌入式设备的用户界面开发 6 | 7 | (暂时只支持英文,后续有时间会增加中文支持) 8 | 9 | 视频介绍: 10 | 11 | https://www.bilibili.com/video/BV1y7NYeMELy/?spm_id_from=333.1387.homepage.video_card.click&vd_source=db23359a52cb1ccca808fd2611658f09 12 | 13 | 问题整理: 14 | - 两个按键gpio的中断需要上升沿和下降沿都触发中断! 15 | - 如果创建的内容较多需要再cubemx的Project Manager中的Linker Settings将堆栈内存设置大一些 16 | - 如果在freertos中使用直接将堆栈分配大一些 17 | - 在freertos中使用时需要在`oled_draw.c`中`InterfaceSwitch()`将延时函数改为非阻塞的 18 | - 如果需要翻转屏幕,在`OLED_Init()`中有详细配置,可以根据注释结合自己的需求更改 19 | 20 | --- 21 | 22 | ## 功能特性 23 | 24 | - **多级菜单支持**:灵活嵌套的树形菜单结构 25 | - **平滑动画过渡**:菜单切换和控件操作均支持缓动动画 26 | - **丰富控件类型**: 27 | - 开关控件(ON/OFF) 28 | - 数值显示控件 29 | - 滑动条控件 30 | - **智能滚动条**:根据菜单项数量自适应调整 31 | - **按键响应优化**:支持长短按区分处理 32 | - **低内存占用**:显存动态管理,适配资源受限设备 33 | 34 | --- 35 | 36 | ## 硬件依赖 37 | 38 | - MCU:STM32(需支持HAL库) 39 | - 显示屏:128x64 OLED(SSD1306驱动) 40 | - 输入:两个物理按键(左/右方向) 41 | 42 | --- 43 | 44 | ## 快速开始 45 | 46 | ### 1. 移植驱动 47 | 将以下文件添加到工程: 48 | 49 | `oled_menu.h` `oled_deaw.h` `oled_menu_types.h` `font.h`移植到Inc文件夹 50 | 51 | `oled_menu.c` `oled_deaw.c` `font.c`移植到Src文件夹 52 | 53 | ### 2. CubeMX配置 54 | 55 | GPIO配置 56 | ![GPIO配置](images/2025-02-28012933.png) 57 | 58 | 中断配置 59 | ![GPIO配置](images/2025-02-28013059.png) 60 | 61 | i2c配置 62 | ![GPIO配置](images/2025-02-28013000.png) 63 | ![GPIO配置](images/2025-02-28013026.png) 64 | ![GPIO配置](images/2025-02-28013039.png) 65 | ![GPIO配置](images/2025-02-28012933.png) 66 | 67 | 68 | ### 3. 按键配置 69 | 在`oled_menu.h`中修改GPIO定义: 70 | ```c 71 | #define RIGHT_KEY_GPIOX GPIOC 72 | #define RIGHT_KEY_GPIO_PIN GPIO_PIN_8 73 | #define LEFT_KEY_GPIOX GPIOC 74 | #define LEFT_KEY_GPIO_PIN GPIO_PIN_9 75 | ``` 76 | 并且需要开启两个按键gpio的中断 77 | 且需要上升沿和下降沿都触发中断! 78 | 79 | ### 4. OLED_Send()函数配置 80 | 在`oled_draw.c`中的OLED_Send是移植本项目时的重要函数 81 | 82 | ```c 83 | void OLED_Send(uint8_t *data, uint8_t len) 84 | { 85 | while(HAL_I2C_Master_Transmit_DMA(&hi2c1, OLED_ADDRESS, data, len) != HAL_OK); 86 | } 87 | ``` 88 | 89 | 默认使用stm32 i2c+DMA(建议在cubemx中配置i2c为高速模式), 如有其他i2c或spi需要请自行更改 90 | 91 | 92 | ### 5. api调用 93 | 94 | `AddMenu()`添加新菜单 `AddMenuItem()`添加菜单项 95 | 96 | 在初始化中调用 `UI_Init()` 97 | 98 | 在主循环中调用 `UI_UpDate()` `UI_Move()` `UI_Show()` 99 | 100 | 具体使用见例程和视频 101 | -------------------------------------------------------------------------------- /User/Inc/font.h: -------------------------------------------------------------------------------- 1 | #ifndef __FONT_H 2 | #define __FONT_H 3 | #include "stdint.h" 4 | #include "string.h" 5 | 6 | typedef struct ASCIIFont { 7 | uint8_t h; 8 | uint8_t w; 9 | uint8_t *chars; 10 | } ASCIIFont; 11 | 12 | extern const ASCIIFont afont8x6; 13 | extern const ASCIIFont afont12x6; 14 | extern const ASCIIFont afont16x8; 15 | extern const ASCIIFont afont24x12; 16 | 17 | /** 18 | * @brief 字体结构体 19 | * @note 字库前4字节存储utf8编码 剩余字节存储字模数据 20 | * @note 字库数据可以使用波特律动LED取模助手生成(https://led.baud-dance.com) 21 | */ 22 | typedef struct Font { 23 | uint8_t h; // 字高度 24 | uint8_t w; // 字宽度 25 | const uint8_t *chars; // 字库 字库前4字节存储utf8编码 剩余字节存储字模数据 26 | uint8_t len; // 字库长度 超过256则请改为uint16_t 27 | const ASCIIFont *ascii; // 缺省ASCII字体 当字库中没有对应字符且需要显示ASCII字符时使用 28 | } Font; 29 | 30 | extern const Font font16x16; 31 | 32 | extern const ASCIIFont afont8x6; 33 | 34 | /** 35 | * @brief 图片结构体 36 | * @note 图片数据可以使用波特律动LED取模助手生成(https://led.baud-dance.com) 37 | */ 38 | typedef struct Image { 39 | uint8_t w; // 图片宽度 40 | uint8_t h; // 图片高度 41 | const uint8_t *data; // 图片数据 42 | } Image; 43 | 44 | extern const Image bilibiliImg; 45 | 46 | #endif // __FONT_H 47 | -------------------------------------------------------------------------------- /User/Inc/oled_draw.h: -------------------------------------------------------------------------------- 1 | #ifndef __OLED_DRAW_H__ 2 | #define __OLED_DRAW_H__ 3 | 4 | #include "font.h" 5 | #include "main.h" 6 | #include "string.h" 7 | 8 | typedef enum { 9 | OLED_COLOR_NORMAL = 0, // 正常模式 黑底白字 10 | OLED_COLOR_REVERSED // 反色模式 白底黑字 11 | } OLED_ColorMode; 12 | 13 | void OLED_Init(); 14 | void OLED_DisPlay_On(); 15 | void OLED_DisPlay_Off(); 16 | 17 | void OLED_NewFrame(); 18 | void OLED_ShowFrame(); 19 | 20 | void OLED_Disappear(void); 21 | void OLED_SetPixel(int16_t x, int16_t y, OLED_ColorMode color); 22 | 23 | void OLED_DrawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, OLED_ColorMode color); 24 | void OLED_DrawRectangle(int16_t x, int16_t y, uint8_t w, uint8_t h, OLED_ColorMode color); 25 | void OLED_DrawEmptyRectangle(int16_t x, int16_t y, uint8_t w, uint8_t h); 26 | void OLED_DrawFilledRectangle(int16_t x, int16_t y, uint8_t w, uint8_t h, OLED_ColorMode color); 27 | void OLED_DrawRectangleWithCorners(int16_t x, int16_t y, uint8_t w, uint8_t h, OLED_ColorMode color); 28 | void OLED_DrawFilledRectangleWithCorners(int16_t x, int16_t y, uint8_t w, uint8_t h, OLED_ColorMode color); 29 | void OLED_DrawTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color); 30 | void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color); 31 | void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color); 32 | void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color); 33 | void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b, OLED_ColorMode color); 34 | void OLED_DrawImage(uint8_t x, uint8_t y, const Image *img, OLED_ColorMode color); 35 | 36 | void OLED_PrintASCIIChar(int16_t x, int16_t y, char ch, const ASCIIFont *font, OLED_ColorMode color); 37 | void OLED_PrintASCIIString(int16_t x, int16_t y, char *str, const ASCIIFont *font, OLED_ColorMode color); 38 | void OLED_PrintString(uint8_t x, uint8_t y, char *str, const Font *font, OLED_ColorMode color); 39 | 40 | 41 | #endif -------------------------------------------------------------------------------- /User/Inc/oled_menu.h: -------------------------------------------------------------------------------- 1 | #ifndef __OLED_MENU_H__ 2 | #define __OLED_MENU_H__ 3 | 4 | #include "main.h" 5 | #include "dma.h" 6 | #include "i2c.h" 7 | #include "usart.h" 8 | #include "gpio.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "oled_menu_types.h" 15 | #include "oled_draw.h" 16 | 17 | #define LIMIT_MAGNITUDE(value, low, high) \ 18 | ((value) < (low) ? (low) : ((value) > (high) ? (high) : (value))) // 限幅函数 19 | 20 | #define OLED_SCREEN_WIDTH 128 // 屏幕宽度 21 | #define OLED_SCREEN_HEIGHT 64 // 屏幕高度 22 | #define MENU_ITEM_HEIGHT 16 // 每一个菜单项的高度 23 | 24 | #define SCROLLBAR_WIDTH 2 25 | #define SCROLLBAR_MARGIN 3 26 | 27 | #define LONG_PRESS_THRESHOLD 600 // 长按判定时间(ms) 28 | 29 | // 设置左右键对应的GPIO,默认高电平为按下 30 | #define RIGHT_KEY_GPIOX GPIOC 31 | #define RIGHT_KEY_GPIO_PIN GPIO_PIN_8 32 | #define LEFT_KEY_GPIOX GPIOC 33 | #define LEFT_KEY_GPIO_PIN GPIO_PIN_9 34 | 35 | extern Menutypedef mainMenu; 36 | extern Menutypedef *currentMenu; 37 | extern KeyTypeDef KeyList[2]; 38 | 39 | extern UIElemTypedef frameY; 40 | extern UIElemTypedef frameWidth; 41 | extern UIElemTypedef screenTop; 42 | extern UIElemTypedef scrollBarY; 43 | extern UIElemTypedef switchCtrlBar; 44 | extern UIElemTypedef displayCtrlBar; 45 | 46 | extern ScreenIndexTypedef screenIndex; 47 | extern uint8_t keyID; 48 | extern uint8_t menuSwitchFlag; 49 | extern uint8_t controlSelectionFlag; 50 | 51 | extern float moveProcess_FrameY; 52 | extern float moveProcess_FrameWidth; 53 | extern float moveProcess_Screen; 54 | extern float moveProcess_ScrollBar; 55 | extern float moveProcess_SwitchCtrlBar; 56 | 57 | Menutypedef *AddMenu(const char *name, ItemTypedef *items, uint16_t itemCount, Menutypedef *parentMenu); 58 | 59 | ItemTypedef *AddMenuItem(Menutypedef *menu, 60 | const char *name, 61 | void (*funtion)(void), 62 | Menutypedef *subMenu, 63 | ControlModeTypedef ctrlMode, 64 | int *ctrlData); 65 | 66 | void FunctionForCtrl(void); 67 | void FunctionForNextMenu(void); 68 | 69 | void UI_Init(void); 70 | void UI_UpDate(void); 71 | void UI_Move(void); 72 | void UI_Show(void); 73 | 74 | 75 | void KeyShortPress(void); 76 | void KeyLongPress(void); 77 | void Frame_Update(void); 78 | void Screen_Update(void); 79 | void ScrollBar_Update(void); 80 | void switchCtrlBar_Update(void); 81 | uint8_t UI_SmoothTransition(UIElemTypedef *elem, float *moveProcess, float moveSpeed); 82 | void InterfaceSwitch(void); 83 | void DrawMenuItems(void); 84 | void DrawControlSelection(void); 85 | void DrawItemName(char *str, int xPos, int yPos); 86 | void DrawControlInformation(ControlTypedef *control, int yPos); 87 | void DrawSelectionFrame(void); 88 | void DrawScrollBar(void); 89 | void DrawSwitchControl(ControlTypedef *control); 90 | void DrawDisplayControl(ControlTypedef *control); 91 | void DrawSliderControl(ControlTypedef *control); 92 | float easeInOut(float t); 93 | 94 | 95 | 96 | #endif 97 | 98 | -------------------------------------------------------------------------------- /User/Inc/oled_menu_types.h: -------------------------------------------------------------------------------- 1 | #ifndef __OLED_MENU_TYPES_H__ 2 | #define __OLED_MENU_TYPES_H__ 3 | 4 | #include 5 | 6 | struct Menutypedef ; 7 | struct ItemTypedef ; 8 | struct ControlTypedef; 9 | 10 | 11 | /** 12 | * @brief 控件模式类型枚举。 13 | * 14 | * 此枚举定义了菜单项中控件的类型,例如开关控件、显示控件和滑动条控件。 15 | * NONE_CTRL表示无控件。 16 | */ 17 | typedef enum 18 | { 19 | NONE_CTRL = 0, /**< 无控件 */ 20 | SWITCH_CTRL = 1, /**< 开关控件 */ 21 | DISPLAY_CTRL = 2, /**< 显示控件 */ 22 | SLIDER_CTRL = 3 /**< 滑动条控件 */ 23 | } ControlModeTypedef; 24 | 25 | /** 26 | * @brief 按键模式类型枚举。 27 | * 28 | * 此枚举定义了按键的操作类型,例如长按和短按。 29 | */ 30 | typedef enum 31 | { 32 | LONG_PRESS = 1, /**< 长按 */ 33 | SHORT_PRESS = 2 /**< 短按 */ 34 | } PressModeTypedef; 35 | 36 | /** 37 | * @brief 菜单结构体。 38 | * 39 | * 定义了一个菜单的属性,包括名称、菜单项、父菜单、子菜单等。 40 | */ 41 | typedef struct Menutypedef 42 | { 43 | char *menuName; /**< 菜单名称 */ 44 | struct ItemTypedef *items; /**< 菜单项指针 */ 45 | struct Menutypedef *parentMenu; /**< 父级菜单指针 */ 46 | uint16_t itemCount; /**< 菜单项数量 */ 47 | int currentItemIndex; /**< 当前菜单项索引 */ 48 | 49 | } Menutypedef; 50 | 51 | /** 52 | * @brief 菜单项结构体。 53 | * 54 | * 定义了菜单项的属性,包括名称、子菜单、控件信息等。 55 | */ 56 | typedef struct ItemTypedef 57 | { 58 | char *str; /**< 菜单项名称 */ 59 | uint8_t len; /**< 菜单项名称长度 */ 60 | void (*Function)(void); /**< 菜单项执行的函数指针 */ 61 | struct Menutypedef *subMenu; /**< 子级菜单指针 */ 62 | struct ControlTypedef *control; /**< 控件信息指针 */ 63 | 64 | } ItemTypedef; 65 | 66 | /** 67 | * @brief 控件结构体。 68 | * 69 | * 定义了控件的属性,包括绑定的数据指针和控件模式。 70 | */ 71 | typedef struct ControlTypedef 72 | { 73 | int *data; /**< 控件绑定的数据指针 */ 74 | ControlModeTypedef mode; /**< 控件模式 */ 75 | 76 | } ControlTypedef; 77 | 78 | /** 79 | * @brief UI 元素结构体。 80 | * 81 | * 定义了 UI 元素的值、目标值和上一次的值。 82 | */ 83 | typedef struct 84 | { 85 | int16_t val; /**< 当前值 */ 86 | int16_t targetVal; /**< 目标值 */ 87 | int16_t lastVal; /**< 上一次值 */ 88 | 89 | } UIElemTypedef; 90 | 91 | /** 92 | * @brief 按键结构体。 93 | * 94 | * 定义了按键的当前值、上一次值、长按和短按标志以及时间信息。 95 | */ 96 | typedef struct 97 | { 98 | uint8_t val; /**< 当前值 */ 99 | uint8_t lastVal; /**< 上一次值 */ 100 | uint8_t updateFlag; /**< 更新标志 */ 101 | uint8_t longPress; /**< 长按标志 */ 102 | uint32_t pressTime; /**< 按下时间 */ 103 | uint32_t releaseTime; /**< 抬起时间 */ 104 | 105 | } KeyTypeDef; 106 | 107 | /** 108 | * @brief 屏幕索引结构体。 109 | * 110 | * 定义了屏幕的顶部和底部索引,用于分页显示菜单。 111 | */ 112 | typedef struct 113 | { 114 | int16_t topIndex; /**< 屏幕顶部索引 */ 115 | int16_t bottomIndex; /**< 屏幕底部索引 */ 116 | 117 | } ScreenIndexTypedef; 118 | 119 | 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /User/Src/font.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file font.c 3 | * @brief 字体库 4 | * 5 | * @attention 6 | * 本字体库与波特律动OLED驱动配套使用 7 | * 英文字库已包含 8 | * 中文字库请使用波特律动LED取模工具生成(https://led.baud-dance.com) 9 | * 图模也使用波特律动LED取模工具生成 10 | */ 11 | // clang-format off 12 | #include "font.h" 13 | 14 | // 8*6 ASCII 15 | const unsigned char ascii_8x6[][6] = { 16 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // space 空格 17 | {0x00, 0x00, 0x00, 0x2f, 0x00, 0x00}, // ! 18 | {0x00, 0x00, 0x07, 0x00, 0x07, 0x00}, // " 19 | {0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14}, // # 20 | {0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12}, // $ 21 | {0x00, 0x62, 0x64, 0x08, 0x13, 0x23}, // % 22 | {0x00, 0x36, 0x49, 0x55, 0x22, 0x50}, // & 23 | {0x00, 0x00, 0x05, 0x03, 0x00, 0x00}, // ' 24 | {0x00, 0x00, 0x1c, 0x22, 0x41, 0x00}, // ( 25 | {0x00, 0x00, 0x41, 0x22, 0x1c, 0x00}, // ) 26 | {0x00, 0x14, 0x08, 0x3E, 0x08, 0x14}, // * 27 | {0x00, 0x08, 0x08, 0x3E, 0x08, 0x08}, // + 28 | {0x00, 0x00, 0x00, 0xA0, 0x60, 0x00}, // , 29 | {0x00, 0x08, 0x08, 0x08, 0x08, 0x08}, // - 30 | {0x00, 0x00, 0x60, 0x60, 0x00, 0x00}, // . 31 | {0x00, 0x20, 0x10, 0x08, 0x04, 0x02}, // / 32 | {0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0 33 | {0x00, 0x00, 0x42, 0x7F, 0x40, 0x00}, // 1 34 | {0x00, 0x42, 0x61, 0x51, 0x49, 0x46}, // 2 35 | {0x00, 0x21, 0x41, 0x45, 0x4B, 0x31}, // 3 36 | {0x00, 0x18, 0x14, 0x12, 0x7F, 0x10}, // 4 37 | {0x00, 0x27, 0x45, 0x45, 0x45, 0x39}, // 5 38 | {0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6 39 | {0x00, 0x01, 0x71, 0x09, 0x05, 0x03}, // 7 40 | {0x00, 0x36, 0x49, 0x49, 0x49, 0x36}, // 8 41 | {0x00, 0x06, 0x49, 0x49, 0x29, 0x1E}, // 9 42 | {0x00, 0x00, 0x36, 0x36, 0x00, 0x00}, // : 43 | {0x00, 0x00, 0x56, 0x36, 0x00, 0x00}, // ; 44 | {0x00, 0x08, 0x14, 0x22, 0x41, 0x00}, // < 45 | {0x00, 0x14, 0x14, 0x14, 0x14, 0x14}, // = 46 | {0x00, 0x00, 0x41, 0x22, 0x14, 0x08}, // > 47 | {0x00, 0x02, 0x01, 0x51, 0x09, 0x06}, // ? 48 | {0x00, 0x32, 0x49, 0x59, 0x51, 0x3E}, // @ 49 | {0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C}, // A 50 | {0x00, 0x7F, 0x49, 0x49, 0x49, 0x36}, // B 51 | {0x00, 0x3E, 0x41, 0x41, 0x41, 0x22}, // C 52 | {0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C}, // D 53 | {0x00, 0x7F, 0x49, 0x49, 0x49, 0x41}, // E 54 | {0x00, 0x7F, 0x09, 0x09, 0x09, 0x01}, // F 55 | {0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A}, // G 56 | {0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F}, // H 57 | {0x00, 0x00, 0x41, 0x7F, 0x41, 0x00}, // I 58 | {0x00, 0x20, 0x40, 0x41, 0x3F, 0x01}, // J 59 | {0x00, 0x7F, 0x08, 0x14, 0x22, 0x41}, // K 60 | {0x00, 0x7F, 0x40, 0x40, 0x40, 0x40}, // L 61 | {0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F}, // M 62 | {0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F}, // N 63 | {0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E}, // O 64 | {0x00, 0x7F, 0x09, 0x09, 0x09, 0x06}, // P 65 | {0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q 66 | {0x00, 0x7F, 0x09, 0x19, 0x29, 0x46}, // R 67 | {0x00, 0x46, 0x49, 0x49, 0x49, 0x31}, // S 68 | {0x00, 0x01, 0x01, 0x7F, 0x01, 0x01}, // T 69 | {0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F}, // U 70 | {0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F}, // V 71 | {0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F}, // W 72 | {0x00, 0x63, 0x14, 0x08, 0x14, 0x63}, // X 73 | {0x00, 0x07, 0x08, 0x70, 0x08, 0x07}, // Y 74 | {0x00, 0x61, 0x51, 0x49, 0x45, 0x43}, // Z 75 | {0x00, 0x00, 0x7F, 0x41, 0x41, 0x00}, // [ 76 | {0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55}, // 55 77 | {0x00, 0x00, 0x41, 0x41, 0x7F, 0x00}, // ] 78 | {0x00, 0x04, 0x02, 0x01, 0x02, 0x04}, // ^ 79 | {0x00, 0x40, 0x40, 0x40, 0x40, 0x40}, // _ 80 | {0x00, 0x00, 0x01, 0x02, 0x04, 0x00}, // ' 81 | {0x00, 0x20, 0x54, 0x54, 0x54, 0x78}, // a 82 | {0x00, 0x7F, 0x48, 0x44, 0x44, 0x38}, // b 83 | {0x00, 0x38, 0x44, 0x44, 0x44, 0x20}, // c 84 | {0x00, 0x38, 0x44, 0x44, 0x48, 0x7F}, // d 85 | {0x00, 0x38, 0x54, 0x54, 0x54, 0x18}, // e 86 | {0x00, 0x08, 0x7E, 0x09, 0x01, 0x02}, // f 87 | {0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C}, // g 88 | {0x00, 0x7F, 0x08, 0x04, 0x04, 0x78}, // h 89 | {0x00, 0x00, 0x44, 0x7D, 0x40, 0x00}, // i 90 | {0x00, 0x40, 0x80, 0x84, 0x7D, 0x00}, // j 91 | {0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // k 92 | {0x00, 0x00, 0x41, 0x7F, 0x40, 0x00}, // l 93 | {0x00, 0x7C, 0x04, 0x18, 0x04, 0x78}, // m 94 | {0x00, 0x7C, 0x08, 0x04, 0x04, 0x78}, // n 95 | {0x00, 0x38, 0x44, 0x44, 0x44, 0x38}, // o 96 | {0x00, 0xFC, 0x24, 0x24, 0x24, 0x18}, // p 97 | {0x00, 0x18, 0x24, 0x24, 0x18, 0xFC}, // q 98 | {0x00, 0x7C, 0x08, 0x04, 0x04, 0x08}, // r 99 | {0x00, 0x48, 0x54, 0x54, 0x54, 0x20}, // s 100 | {0x00, 0x04, 0x3F, 0x44, 0x40, 0x20}, // t 101 | {0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C}, // u 102 | {0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C}, // v 103 | {0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C}, // w 104 | {0x00, 0x44, 0x28, 0x10, 0x28, 0x44}, // x 105 | {0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C}, // y 106 | {0x00, 0x44, 0x64, 0x54, 0x4C, 0x44}, // z 107 | {0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // horiz lines 108 | }; 109 | 110 | const ASCIIFont afont8x6 = {8, 6, (unsigned char *)ascii_8x6}; 111 | 112 | const unsigned char ascii_12x6[][12] = { 113 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*" ",0*/ 114 | {0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}, /*"!",1*/ 115 | {0x00, 0x0C, 0x02, 0x0C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*""",2*/ 116 | {0x90, 0xD0, 0xBC, 0xD0, 0xBC, 0x90, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00}, /*"#",3*/ 117 | {0x18, 0x24, 0xFE, 0x44, 0x8C, 0x00, 0x03, 0x02, 0x07, 0x02, 0x01, 0x00}, /*"$",4*/ 118 | {0x18, 0x24, 0xD8, 0xB0, 0x4C, 0x80, 0x00, 0x03, 0x00, 0x01, 0x02, 0x01}, /*"%",5*/ 119 | {0xC0, 0x38, 0xE4, 0x38, 0xE0, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02}, /*"&",6*/ 120 | {0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"'",7*/ 121 | {0x00, 0x00, 0x00, 0xF8, 0x04, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04}, /*"(",8*/ 122 | {0x00, 0x02, 0x04, 0xF8, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00}, /*")",9*/ 123 | {0x90, 0x60, 0xF8, 0x60, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, /*"*",10*/ 124 | {0x20, 0x20, 0xFC, 0x20, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, /*"+",11*/ 125 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00}, /*",",12*/ 126 | {0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"-",13*/ 127 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00}, /*".",14*/ 128 | {0x00, 0x80, 0x60, 0x1C, 0x02, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00}, /*"/",15*/ 129 | {0xF8, 0x04, 0x04, 0x04, 0xF8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"0",16*/ 130 | {0x00, 0x08, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00}, /*"1",17*/ 131 | {0x18, 0x84, 0x44, 0x24, 0x18, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x00}, /*"2",18*/ 132 | {0x08, 0x04, 0x24, 0x24, 0xD8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"3",19*/ 133 | {0x40, 0xB0, 0x88, 0xFC, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00}, /*"4",20*/ 134 | {0x3C, 0x24, 0x24, 0x24, 0xC4, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"5",21*/ 135 | {0xF8, 0x24, 0x24, 0x2C, 0xC0, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"6",22*/ 136 | {0x0C, 0x04, 0xE4, 0x1C, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00}, /*"7",23*/ 137 | {0xD8, 0x24, 0x24, 0x24, 0xD8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"8",24*/ 138 | {0x38, 0x44, 0x44, 0x44, 0xF8, 0x00, 0x00, 0x03, 0x02, 0x02, 0x01, 0x00}, /*"9",25*/ 139 | {0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}, /*":",26*/ 140 | {0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00}, /*";",27*/ 141 | {0x00, 0x20, 0x50, 0x88, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}, /*"<",28*/ 142 | {0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"=",29*/ 143 | {0x00, 0x02, 0x04, 0x88, 0x50, 0x20, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}, /*">",30*/ 144 | {0x18, 0x04, 0xC4, 0x24, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}, /*"?",31*/ 145 | {0xF8, 0x04, 0xE4, 0x94, 0xF8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00}, /*"@",32*/ 146 | {0x00, 0xE0, 0x9C, 0xF0, 0x80, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02}, /*"A",33*/ 147 | {0x04, 0xFC, 0x24, 0x24, 0xD8, 0x00, 0x02, 0x03, 0x02, 0x02, 0x01, 0x00}, /*"B",34*/ 148 | {0xF8, 0x04, 0x04, 0x04, 0x0C, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"C",35*/ 149 | {0x04, 0xFC, 0x04, 0x04, 0xF8, 0x00, 0x02, 0x03, 0x02, 0x02, 0x01, 0x00}, /*"D",36*/ 150 | {0x04, 0xFC, 0x24, 0x74, 0x0C, 0x00, 0x02, 0x03, 0x02, 0x02, 0x03, 0x00}, /*"E",37*/ 151 | {0x04, 0xFC, 0x24, 0x74, 0x0C, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00}, /*"F",38*/ 152 | {0xF0, 0x08, 0x04, 0x44, 0xCC, 0x40, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00}, /*"G",39*/ 153 | {0x04, 0xFC, 0x20, 0x20, 0xFC, 0x04, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02}, /*"H",40*/ 154 | {0x04, 0x04, 0xFC, 0x04, 0x04, 0x00, 0x02, 0x02, 0x03, 0x02, 0x02, 0x00}, /*"I",41*/ 155 | {0x00, 0x04, 0x04, 0xFC, 0x04, 0x04, 0x06, 0x04, 0x04, 0x03, 0x00, 0x00}, /*"J",42*/ 156 | {0x04, 0xFC, 0x24, 0xD0, 0x0C, 0x04, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"K",43*/ 157 | {0x04, 0xFC, 0x04, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x02, 0x02, 0x03}, /*"L",44*/ 158 | {0xFC, 0x3C, 0xC0, 0x3C, 0xFC, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00}, /*"M",45*/ 159 | {0x04, 0xFC, 0x30, 0xC4, 0xFC, 0x04, 0x02, 0x03, 0x02, 0x00, 0x03, 0x00}, /*"N",46*/ 160 | {0xF8, 0x04, 0x04, 0x04, 0xF8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"O",47*/ 161 | {0x04, 0xFC, 0x24, 0x24, 0x18, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00}, /*"P",48*/ 162 | {0xF8, 0x84, 0x84, 0x04, 0xF8, 0x00, 0x01, 0x02, 0x02, 0x07, 0x05, 0x00}, /*"Q",49*/ 163 | {0x04, 0xFC, 0x24, 0x64, 0x98, 0x00, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"R",50*/ 164 | {0x18, 0x24, 0x24, 0x44, 0x8C, 0x00, 0x03, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"S",51*/ 165 | {0x0C, 0x04, 0xFC, 0x04, 0x0C, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00}, /*"T",52*/ 166 | {0x04, 0xFC, 0x00, 0x00, 0xFC, 0x04, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00}, /*"U",53*/ 167 | {0x04, 0x7C, 0x80, 0xE0, 0x1C, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00}, /*"V",54*/ 168 | {0x1C, 0xE0, 0x3C, 0xE0, 0x1C, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00}, /*"W",55*/ 169 | {0x04, 0x9C, 0x60, 0x9C, 0x04, 0x00, 0x02, 0x03, 0x00, 0x03, 0x02, 0x00}, /*"X",56*/ 170 | {0x04, 0x1C, 0xE0, 0x1C, 0x04, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00}, /*"Y",57*/ 171 | {0x0C, 0x84, 0x64, 0x1C, 0x04, 0x00, 0x02, 0x03, 0x02, 0x02, 0x03, 0x00}, /*"Z",58*/ 172 | {0x00, 0x00, 0xFE, 0x02, 0x02, 0x00, 0x00, 0x00, 0x07, 0x04, 0x04, 0x00}, /*"[",59*/ 173 | {0x00, 0x0E, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00}, /*"\",60*/ 174 | {0x00, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x04, 0x04, 0x07, 0x00, 0x00}, /*"]",61*/ 175 | {0x00, 0x04, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"^",62*/ 176 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, /*"_",63*/ 177 | {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"`",64*/ 178 | {0x00, 0x40, 0xA0, 0xA0, 0xC0, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x02}, /*"a",65*/ 179 | {0x04, 0xFC, 0x20, 0x20, 0xC0, 0x00, 0x00, 0x03, 0x02, 0x02, 0x01, 0x00}, /*"b",66*/ 180 | {0x00, 0xC0, 0x20, 0x20, 0x60, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00}, /*"c",67*/ 181 | {0x00, 0xC0, 0x20, 0x24, 0xFC, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x02}, /*"d",68*/ 182 | {0x00, 0xC0, 0xA0, 0xA0, 0xC0, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00}, /*"e",69*/ 183 | {0x00, 0x20, 0xF8, 0x24, 0x24, 0x04, 0x00, 0x02, 0x03, 0x02, 0x02, 0x00}, /*"f",70*/ 184 | {0x00, 0x40, 0xA0, 0xA0, 0x60, 0x20, 0x00, 0x07, 0x0A, 0x0A, 0x0A, 0x04}, /*"g",71*/ 185 | {0x04, 0xFC, 0x20, 0x20, 0xC0, 0x00, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"h",72*/ 186 | {0x00, 0x20, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00}, /*"i",73*/ 187 | {0x00, 0x00, 0x20, 0xE4, 0x00, 0x00, 0x08, 0x08, 0x08, 0x07, 0x00, 0x00}, /*"j",74*/ 188 | {0x04, 0xFC, 0x80, 0xE0, 0x20, 0x20, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"k",75*/ 189 | {0x04, 0x04, 0xFC, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x02, 0x02, 0x00}, /*"l",76*/ 190 | {0xE0, 0x20, 0xE0, 0x20, 0xC0, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00}, /*"m",77*/ 191 | {0x20, 0xE0, 0x20, 0x20, 0xC0, 0x00, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"n",78*/ 192 | {0x00, 0xC0, 0x20, 0x20, 0xC0, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00}, /*"o",79*/ 193 | {0x20, 0xE0, 0x20, 0x20, 0xC0, 0x00, 0x08, 0x0F, 0x0A, 0x02, 0x01, 0x00}, /*"p",80*/ 194 | {0x00, 0xC0, 0x20, 0x20, 0xE0, 0x00, 0x00, 0x01, 0x02, 0x0A, 0x0F, 0x08}, /*"q",81*/ 195 | {0x20, 0xE0, 0x40, 0x20, 0x20, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00}, /*"r",82*/ 196 | {0x00, 0x60, 0xA0, 0xA0, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x03, 0x00}, /*"s",83*/ 197 | {0x00, 0x20, 0xF8, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00}, /*"t",84*/ 198 | {0x20, 0xE0, 0x00, 0x20, 0xE0, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x02}, /*"u",85*/ 199 | {0x20, 0xE0, 0x20, 0x80, 0x60, 0x20, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00}, /*"v",86*/ 200 | {0x60, 0x80, 0xE0, 0x80, 0x60, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00}, /*"w",87*/ 201 | {0x20, 0x60, 0x80, 0x60, 0x20, 0x00, 0x02, 0x03, 0x00, 0x03, 0x02, 0x00}, /*"x",88*/ 202 | {0x20, 0xE0, 0x20, 0x80, 0x60, 0x20, 0x08, 0x08, 0x07, 0x01, 0x00, 0x00}, /*"y",89*/ 203 | {0x00, 0x20, 0xA0, 0x60, 0x20, 0x00, 0x00, 0x02, 0x03, 0x02, 0x02, 0x00}, /*"z",90*/ 204 | {0x00, 0x00, 0x20, 0xDE, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x00}, /*"{",91*/ 205 | {0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00}, /*"|",92*/ 206 | {0x00, 0x02, 0xDE, 0x20, 0x00, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00, 0x00}, /*"}",93*/ 207 | {0x02, 0x01, 0x02, 0x04, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"~",94*/ 208 | }; 209 | 210 | const ASCIIFont afont12x6 = {12, 6, (unsigned char *)ascii_12x6}; 211 | 212 | const unsigned char ascii_16x8[][16] = { 213 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*" ",0*/ 214 | {0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00}, /*"!",1*/ 215 | {0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*""",2*/ 216 | {0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00, 0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00}, /*"#",3*/ 217 | {0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00, 0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00}, /*"$",4*/ 218 | {0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00}, /*"%",5*/ 219 | {0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10}, /*"&",6*/ 220 | {0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"'",7*/ 221 | {0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00}, /*"(",8*/ 222 | {0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00}, /*")",9*/ 223 | {0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00, 0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00}, /*"*",10*/ 224 | {0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00}, /*"+",11*/ 225 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00}, /*",",12*/ 226 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, /*"-",13*/ 227 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00}, /*".",14*/ 228 | {0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04, 0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00}, /*"/",15*/ 229 | {0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00}, /*"0",16*/ 230 | {0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"1",17*/ 231 | {0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00}, /*"2",18*/ 232 | {0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00, 0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"3",19*/ 233 | {0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00}, /*"4",20*/ 234 | {0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00, 0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"5",21*/ 235 | {0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"6",22*/ 236 | {0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, /*"7",23*/ 237 | {0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00}, /*"8",24*/ 238 | {0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00}, /*"9",25*/ 239 | {0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00}, /*":",26*/ 240 | {0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00}, /*";",27*/ 241 | {0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00}, /*"<",28*/ 242 | {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00}, /*"=",29*/ 243 | {0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00}, /*">",30*/ 244 | {0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00}, /*"?",31*/ 245 | {0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00, 0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00}, /*"@",32*/ 246 | {0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20}, /*"A",33*/ 247 | {0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"B",34*/ 248 | {0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00}, /*"C",35*/ 249 | {0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00}, /*"D",36*/ 250 | {0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00}, /*"E",37*/ 251 | {0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00}, /*"F",38*/ 252 | {0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00}, /*"G",39*/ 253 | {0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20}, /*"H",40*/ 254 | {0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"I",41*/ 255 | {0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00}, /*"J",42*/ 256 | {0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00, 0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00}, /*"K",43*/ 257 | {0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00}, /*"L",44*/ 258 | {0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00, 0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00}, /*"M",45*/ 259 | {0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00}, /*"N",46*/ 260 | {0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00}, /*"O",47*/ 261 | {0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00}, /*"P",48*/ 262 | {0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00}, /*"Q",49*/ 263 | {0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20}, /*"R",50*/ 264 | {0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00}, /*"S",51*/ 265 | {0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00}, /*"T",52*/ 266 | {0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00}, /*"U",53*/ 267 | {0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00}, /*"V",54*/ 268 | {0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00, 0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00}, /*"W",55*/ 269 | {0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08, 0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20}, /*"X",56*/ 270 | {0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00}, /*"Y",57*/ 271 | {0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00}, /*"Z",58*/ 272 | {0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00}, /*"[",59*/ 273 | {0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00}, /*"\",60*/ 274 | {0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00}, /*"]",61*/ 275 | {0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"^",62*/ 276 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, /*"_",63*/ 277 | {0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"`",64*/ 278 | {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20}, /*"a",65*/ 279 | {0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"b",66*/ 280 | {0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00}, /*"c",67*/ 281 | {0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20}, /*"d",68*/ 282 | {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00}, /*"e",69*/ 283 | {0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"f",70*/ 284 | {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00}, /*"g",71*/ 285 | {0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20}, /*"h",72*/ 286 | {0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"i",73*/ 287 | {0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00}, /*"j",74*/ 288 | {0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00}, /*"k",75*/ 289 | {0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"l",76*/ 290 | {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F}, /*"m",77*/ 291 | {0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20}, /*"n",78*/ 292 | {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00}, /*"o",79*/ 293 | {0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"p",80*/ 294 | {0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80}, /*"q",81*/ 295 | {0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00}, /*"r",82*/ 296 | {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00}, /*"s",83*/ 297 | {0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00}, /*"t",84*/ 298 | {0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20}, /*"u",85*/ 299 | {0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00}, /*"v",86*/ 300 | {0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, 0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00}, /*"w",87*/ 301 | {0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00}, /*"x",88*/ 302 | {0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00}, /*"y",89*/ 303 | {0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00}, /*"z",90*/ 304 | {0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40}, /*"{",91*/ 305 | {0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00}, /*"|",92*/ 306 | {0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00}, /*"}",93*/ 307 | {0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"~",94*/ 308 | }; 309 | 310 | const ASCIIFont afont16x8 = {16, 8, (unsigned char *)ascii_16x8}; 311 | 312 | const unsigned char ascii_24x12[][36] = { 313 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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*/ 314 | {0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x1C, 0x00, 0x00, 0x00, 0x00}, /*"!",1*/ 315 | {0x00, 0x00, 0x80, 0x60, 0x30, 0x1C, 0x8C, 0x60, 0x30, 0x1C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*""",2*/ 316 | {0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x86, 0xE6, 0x9F, 0x86, 0x86, 0x86, 0x86, 0xE6, 0x9F, 0x86, 0x00, 0x00, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x00}, /*"#",3*/ 317 | {0x00, 0x00, 0x80, 0xC0, 0x60, 0x20, 0xF8, 0x20, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x0C, 0x18, 0xFF, 0x70, 0xE1, 0x81, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x10, 0x10, 0x7F, 0x10, 0x0F, 0x07, 0x00, 0x00}, /*"$",4*/ 318 | {0x80, 0x60, 0x20, 0x60, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x20, 0x00, 0x00, 0x0F, 0x30, 0x20, 0x30, 0x9F, 0x70, 0xDC, 0x37, 0x10, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x03, 0x00, 0x07, 0x18, 0x10, 0x18, 0x07, 0x00}, /*"%",5*/ 319 | {0x00, 0x00, 0xC0, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0x1F, 0x38, 0xE8, 0x87, 0x03, 0xC4, 0x3C, 0x04, 0x00, 0x00, 0x07, 0x0F, 0x18, 0x10, 0x10, 0x0B, 0x07, 0x0D, 0x10, 0x10, 0x08, 0x00}, /*"&",6*/ 320 | {0x00, 0x80, 0x8C, 0x4C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"'",7*/ 321 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0x30, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x18, 0x20, 0x40, 0x00}, /*"(",8*/ 322 | {0x00, 0x04, 0x08, 0x30, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*")",9*/ 323 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x66, 0x66, 0x3C, 0x18, 0xFF, 0x18, 0x3C, 0x66, 0x66, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"*",10*/ 324 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"+",11*/ 325 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8C, 0x4C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*",",12*/ 326 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"-",13*/ 327 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*".",14*/ 328 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x38, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x70, 0x1C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"/",15*/ 329 | {0x00, 0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x01, 0x07, 0x0E, 0x18, 0x10, 0x10, 0x18, 0x0E, 0x07, 0x01, 0x00}, /*"0",16*/ 330 | {0x00, 0x00, 0x80, 0x80, 0x80, 0xC0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x00, 0x00}, /*"1",17*/ 331 | {0x00, 0x80, 0x40, 0x20, 0x20, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x80, 0x40, 0x20, 0x38, 0x1F, 0x07, 0x00, 0x00, 0x00, 0x1C, 0x1A, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00}, /*"2",18*/ 332 | {0x00, 0x80, 0xC0, 0x20, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x10, 0x10, 0x18, 0x2F, 0xE7, 0x80, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x10, 0x10, 0x10, 0x10, 0x18, 0x0F, 0x07, 0x00, 0x00}, /*"3",19*/ 333 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xB0, 0x88, 0x86, 0x81, 0x80, 0xFF, 0xFF, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x00}, /*"4",20*/ 334 | {0x00, 0x00, 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x10, 0x08, 0x08, 0x08, 0x18, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x07, 0x0B, 0x10, 0x10, 0x10, 0x10, 0x1C, 0x0F, 0x03, 0x00, 0x00}, /*"5",21*/ 335 | {0x00, 0x00, 0x80, 0xC0, 0x40, 0x20, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x21, 0x10, 0x08, 0x08, 0x08, 0x18, 0xF0, 0xE0, 0x00, 0x00, 0x01, 0x07, 0x0C, 0x18, 0x10, 0x10, 0x10, 0x08, 0x0F, 0x03, 0x00}, /*"6",22*/ 336 | {0x00, 0x00, 0xC0, 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xE0, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"7",23*/ 337 | {0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x87, 0xEF, 0x2C, 0x18, 0x18, 0x30, 0x30, 0x68, 0xCF, 0x83, 0x00, 0x00, 0x07, 0x0F, 0x08, 0x10, 0x10, 0x10, 0x10, 0x18, 0x0F, 0x07, 0x00}, /*"8",24*/ 338 | {0x00, 0x00, 0xC0, 0xC0, 0x20, 0x20, 0x20, 0x20, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x60, 0x40, 0x40, 0x40, 0x20, 0x10, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x0C, 0x1C, 0x10, 0x10, 0x10, 0x08, 0x0F, 0x03, 0x00, 0x00}, /*"9",25*/ 339 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0E, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x1C, 0x00, 0x00, 0x00, 0x00}, /*":",26*/ 340 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00}, /*";",27*/ 341 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x00, 0x00, 0x00, 0x10, 0x28, 0x44, 0x82, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00}, /*"<",28*/ 342 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"=",29*/ 343 | {0x00, 0x00, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x82, 0x44, 0x28, 0x10, 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}, /*">",30*/ 344 | {0x00, 0xC0, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10, 0x30, 0xE0, 0xC0, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0xF0, 0x10, 0x08, 0x0C, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"?",31*/ 345 | {0x00, 0x00, 0x00, 0xC0, 0x40, 0x60, 0x20, 0x20, 0x20, 0x40, 0xC0, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0xF0, 0x0E, 0x03, 0xC1, 0xFE, 0x03, 0x80, 0x7F, 0x00, 0x01, 0x07, 0x0E, 0x08, 0x11, 0x11, 0x10, 0x11, 0x09, 0x04, 0x02}, /*"@",32*/ 346 | {0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x43, 0x40, 0x47, 0x7F, 0xF8, 0x80, 0x00, 0x00, 0x10, 0x18, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x1F, 0x1C, 0x10}, /*"A",33*/ 347 | {0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x18, 0x2F, 0xE7, 0x80, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x10, 0x18, 0x0F, 0x07, 0x00}, /*"B",34*/ 348 | {0x00, 0x00, 0x80, 0xC0, 0x40, 0x20, 0x20, 0x20, 0x20, 0x60, 0xE0, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x07, 0x0E, 0x18, 0x10, 0x10, 0x10, 0x08, 0x04, 0x03, 0x00}, /*"C",35*/ 349 | {0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x40, 0xC0, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x18, 0x08, 0x0E, 0x07, 0x01, 0x00}, /*"D",36*/ 350 | {0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x18, 0x06, 0x00}, /*"E",37*/ 351 | {0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x60, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x01, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"F",38*/ 352 | {0x00, 0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0x20, 0x40, 0xE0, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x01, 0x00, 0x00, 0x40, 0x40, 0xC0, 0xC1, 0x40, 0x40, 0x00, 0x01, 0x07, 0x0E, 0x18, 0x10, 0x10, 0x10, 0x0F, 0x0F, 0x00, 0x00}, /*"G",39*/ 353 | {0x20, 0xE0, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0xE0, 0xE0, 0x20, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0xFF, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10}, /*"H",40*/ 354 | {0x00, 0x00, 0x20, 0x20, 0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x00, 0x00}, /*"I",41*/ 355 | {0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x60, 0xE0, 0x80, 0x80, 0x80, 0xC0, 0x7F, 0x3F, 0x00, 0x00, 0x00}, /*"J",42*/ 356 | {0x20, 0xE0, 0xE0, 0x20, 0x00, 0x00, 0x20, 0xA0, 0x60, 0x20, 0x20, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x18, 0x7C, 0xE3, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x00, 0x01, 0x13, 0x1F, 0x1C, 0x18, 0x10}, /*"K",43*/ 357 | {0x20, 0xE0, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x18, 0x06, 0x00}, /*"L",44*/ 358 | {0x20, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0x20, 0x00, 0xFF, 0x01, 0x3F, 0xFE, 0xC0, 0xE0, 0x1E, 0x01, 0xFF, 0xFF, 0x00, 0x10, 0x1F, 0x10, 0x00, 0x03, 0x1F, 0x03, 0x00, 0x10, 0x1F, 0x1F, 0x10}, /*"M",45*/ 359 | {0x20, 0xE0, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xE0, 0x20, 0x00, 0xFF, 0x00, 0x03, 0x07, 0x1C, 0x78, 0xE0, 0x80, 0x00, 0xFF, 0x00, 0x10, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x1F, 0x00}, /*"N",46*/ 360 | {0x00, 0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x01, 0x07, 0x0E, 0x18, 0x10, 0x10, 0x18, 0x0C, 0x07, 0x01, 0x00}, /*"O",47*/ 361 | {0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x1F, 0x0F, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"P",48*/ 362 | {0x00, 0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x01, 0x07, 0x0E, 0x11, 0x11, 0x13, 0x3C, 0x7C, 0x67, 0x21, 0x00}, /*"Q",49*/ 363 | {0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x30, 0xF0, 0xD0, 0x08, 0x0F, 0x07, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x1C, 0x10, 0x10}, /*"R",50*/ 364 | {0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0xE0, 0x00, 0x00, 0x07, 0x0F, 0x0C, 0x18, 0x18, 0x30, 0x30, 0x60, 0xE0, 0x81, 0x00, 0x00, 0x1F, 0x0C, 0x08, 0x10, 0x10, 0x10, 0x10, 0x18, 0x0F, 0x07, 0x00}, /*"S",51*/ 365 | {0x80, 0x60, 0x20, 0x20, 0x20, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x60, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00}, /*"T",52*/ 366 | {0x20, 0xE0, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xE0, 0x20, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x07, 0x0F, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x07, 0x00}, /*"U",53*/ 367 | {0x20, 0x60, 0xE0, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x20, 0xE0, 0x60, 0x20, 0x00, 0x00, 0x07, 0x7F, 0xF8, 0x80, 0x00, 0x80, 0x7C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1F, 0x1C, 0x07, 0x00, 0x00, 0x00, 0x00}, /*"V",54*/ 368 | {0x20, 0xE0, 0xE0, 0x20, 0x00, 0xE0, 0xE0, 0x20, 0x00, 0x20, 0xE0, 0x20, 0x00, 0x07, 0xFF, 0xF8, 0xE0, 0x1F, 0xFF, 0xFC, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1F, 0x03, 0x00, 0x01, 0x1F, 0x03, 0x00, 0x00, 0x00}, /*"W",55*/ 369 | {0x00, 0x20, 0x60, 0xE0, 0xA0, 0x00, 0x00, 0x20, 0xE0, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8F, 0x7C, 0xF8, 0xC6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x1E, 0x13, 0x00, 0x01, 0x17, 0x1F, 0x18, 0x10, 0x00}, /*"X",56*/ 370 | {0x20, 0x60, 0xE0, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x20, 0xE0, 0x60, 0x20, 0x00, 0x00, 0x01, 0x07, 0x3E, 0xF8, 0xE0, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x00, 0x00, 0x00}, /*"Y",57*/ 371 | {0x00, 0x80, 0x60, 0x20, 0x20, 0x20, 0x20, 0xA0, 0xE0, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF0, 0x3E, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1C, 0x1F, 0x17, 0x10, 0x10, 0x10, 0x10, 0x18, 0x06, 0x00}, /*"Z",58*/ 372 | {0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}, /*"[",59*/ 373 | {0x00, 0x00, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1C, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x70, 0x80, 0x00}, /*"\",60*/ 374 | {0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, 0x00}, /*"]",61*/ 375 | {0x00, 0x00, 0x00, 0x10, 0x08, 0x0C, 0x04, 0x0C, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"^",62*/ 376 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, /*"_",63*/ 377 | {0x00, 0x00, 0x00, 0x04, 0x04, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"`",64*/ 378 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xD8, 0x44, 0x64, 0x24, 0x24, 0xFC, 0xF8, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x18, 0x10, 0x10, 0x10, 0x08, 0x1F, 0x1F, 0x10, 0x18}, /*"a",65*/ 379 | {0x00, 0x20, 0xE0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x08, 0x04, 0x04, 0x0C, 0xF8, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x18, 0x10, 0x10, 0x10, 0x18, 0x0F, 0x03, 0x00}, /*"b",66*/ 380 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0x18, 0x04, 0x04, 0x04, 0x3C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x0C, 0x10, 0x10, 0x10, 0x10, 0x08, 0x06, 0x00, 0x00}, /*"c",67*/ 381 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xE0, 0xF0, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0x1C, 0x04, 0x04, 0x04, 0x08, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x18, 0x10, 0x10, 0x10, 0x08, 0x1F, 0x0F, 0x08, 0x00}, /*"d",68*/ 382 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0x48, 0x44, 0x44, 0x44, 0x4C, 0x78, 0x70, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x0C, 0x18, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00}, /*"e",69*/ 383 | {0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x00, 0x04, 0x04, 0x04, 0xFF, 0xFF, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00}, /*"f",70*/ 384 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xF8, 0x8C, 0x04, 0x04, 0x8C, 0xF8, 0x74, 0x04, 0x0C, 0x00, 0x70, 0x76, 0xCF, 0x8D, 0x8D, 0x8D, 0x89, 0xC8, 0x78, 0x70, 0x00}, /*"g",71*/ 385 | {0x00, 0x20, 0xE0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x08, 0x04, 0x04, 0x04, 0xFC, 0xF8, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00}, /*"h",72*/ 386 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x00, 0x00}, /*"i",73*/ 387 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x80, 0x80, 0xC0, 0x7F, 0x3F, 0x00, 0x00, 0x00}, /*"j",74*/ 388 | {0x00, 0x20, 0xE0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0xC0, 0xF4, 0x1C, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x11, 0x00, 0x03, 0x1F, 0x1C, 0x10, 0x10, 0x00}, /*"k",75*/ 389 | {0x00, 0x00, 0x20, 0x20, 0x20, 0xE0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x00, 0x00}, /*"l",76*/ 390 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFC, 0xFC, 0x08, 0x04, 0xFC, 0xFC, 0x08, 0x04, 0xFC, 0xFC, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x1F, 0x1F, 0x10, 0x00, 0x1F, 0x1F, 0x10}, /*"m",77*/ 391 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFC, 0xFC, 0x08, 0x08, 0x04, 0x04, 0xFC, 0xF8, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00, 0x00, 0x10, 0x1F, 0x1F, 0x10, 0x00}, /*"n",78*/ 392 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0x18, 0x0C, 0x04, 0x04, 0x0C, 0x18, 0xF0, 0xE0, 0x00, 0x00, 0x03, 0x0F, 0x0C, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x0F, 0x03, 0x00}, /*"o",79*/ 393 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFC, 0xFC, 0x08, 0x04, 0x04, 0x04, 0x0C, 0xF8, 0xF0, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x88, 0x90, 0x10, 0x10, 0x1C, 0x0F, 0x03, 0x00}, /*"p",80*/ 394 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0x1C, 0x04, 0x04, 0x04, 0x08, 0xF8, 0xFC, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x18, 0x10, 0x10, 0x90, 0x88, 0xFF, 0xFF, 0x80, 0x00}, /*"q",81*/ 395 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0xFC, 0xFC, 0x10, 0x08, 0x04, 0x04, 0x0C, 0x0C, 0x00, 0x10, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00}, /*"r",82*/ 396 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0xCC, 0xC4, 0x84, 0x84, 0x84, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x1E, 0x18, 0x10, 0x10, 0x10, 0x11, 0x19, 0x0F, 0x06, 0x00}, /*"s",83*/ 397 | {0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0xFF, 0xFF, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x10, 0x10, 0x10, 0x0C, 0x00, 0x00}, /*"t",84*/ 398 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFC, 0xFE, 0x00, 0x00, 0x00, 0x04, 0xFC, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x18, 0x10, 0x10, 0x08, 0x1F, 0x0F, 0x08, 0x00}, /*"u",85*/ 399 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0C, 0x3C, 0xFC, 0xC4, 0x00, 0x00, 0xC4, 0x3C, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0F, 0x1E, 0x0E, 0x01, 0x00, 0x00, 0x00}, /*"v",86*/ 400 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3C, 0xFC, 0xC4, 0x00, 0xE4, 0x7C, 0xFC, 0x84, 0x80, 0x7C, 0x04, 0x00, 0x00, 0x07, 0x1F, 0x07, 0x00, 0x00, 0x07, 0x1F, 0x07, 0x00, 0x00}, /*"w",87*/ 401 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x1C, 0x7C, 0xE4, 0xC0, 0x34, 0x1C, 0x04, 0x04, 0x00, 0x00, 0x10, 0x10, 0x1C, 0x16, 0x01, 0x13, 0x1F, 0x1C, 0x18, 0x10, 0x00}, /*"x",88*/ 402 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0C, 0x3C, 0xFC, 0xC4, 0x00, 0xC4, 0x3C, 0x04, 0x04, 0x00, 0x00, 0x00, 0xC0, 0x80, 0xC1, 0x37, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00}, /*"y",89*/ 403 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x04, 0x04, 0xC4, 0xF4, 0x7C, 0x1C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1C, 0x1F, 0x17, 0x11, 0x10, 0x10, 0x18, 0x0E, 0x00}, /*"z",90*/ 404 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x28, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x60, 0x40, 0x00, 0x00}, /*"{",91*/ 405 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"|",92*/ 406 | {0x00, 0x00, 0x04, 0x0C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"}",93*/ 407 | {0x00, 0x18, 0x06, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x30, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 408 | /*"~",94*/ /*"~",94*/ 409 | }; 410 | 411 | const ASCIIFont afont24x12 = {24, 12, (unsigned char *)ascii_24x12}; 412 | 413 | const uint8_t zh16x16[][36] = { 414 | /* 0 波 */ {0xe6,0xb3,0xa2,0x00,0x10,0x60,0x02,0x0c,0xc0,0x00,0xf8,0x88,0x88,0x88,0xff,0x88,0x88,0xa8,0x18,0x00,0x04,0x04,0x7c,0x03,0x80,0x60,0x1f,0x80,0x43,0x2c,0x10,0x28,0x46,0x81,0x80,0x00,}, 415 | /* 1 特 */ {0xe7,0x89,0xb9,0x00,0x40,0x3c,0x10,0xff,0x10,0x10,0x40,0x48,0x48,0x48,0x7f,0x48,0xc8,0x48,0x40,0x00,0x02,0x06,0x02,0xff,0x01,0x01,0x00,0x02,0x0a,0x12,0x42,0x82,0x7f,0x02,0x02,0x00,}, 416 | /* 2 律 */ {0xe5,0xbe,0x8b,0x00,0x00,0x10,0x88,0xc4,0x33,0x10,0x54,0x54,0x54,0xff,0x54,0x54,0x7c,0x10,0x10,0x00,0x02,0x01,0x00,0xff,0x00,0x10,0x12,0x12,0x12,0xff,0x12,0x12,0x12,0x10,0x00,0x00,}, 417 | /* 3 动 */ {0xe5,0x8a,0xa8,0x00,0x40,0x44,0xc4,0x44,0x44,0x44,0x40,0x10,0x10,0xff,0x10,0x10,0x10,0xf0,0x00,0x00,0x10,0x3c,0x13,0x10,0x14,0xb8,0x40,0x30,0x0e,0x01,0x40,0x80,0x40,0x3f,0x00,0x00,} 418 | }; 419 | const Font font16x16 = {16, 16, (const uint8_t *)zh16x16, 4, &afont8x6}; 420 | 421 | const uint8_t bilibiliData[] = { 422 | 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x86, 0x8f, 0x9f, 0xbf, 0xff, 0xfc, 0xf8, 0xf8, 0xe0, 0xe0, 0xc0, 0x80, 423 | 0x80, 0x80, 0x80, 0x80, 0xc0, 0xe0, 0xe0, 0xf8, 0xf8, 0xfc, 0xfe, 0xbf, 0x9f, 0x8f, 0x86, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 424 | 0x00, 0x00, 0x00, 0xf8, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 425 | 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 426 | 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf8, 0xf8, 0xf8, 427 | 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 428 | 0x20, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 429 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xc0, 0xc0, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 430 | 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 431 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 432 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x07, 0x07, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 433 | 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 434 | 0x1f, 0x1f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x07, 0x07, 0x03, 435 | }; 436 | const Image bilibiliImg = {51, 48, bilibiliData}; 437 | -------------------------------------------------------------------------------- /User/Src/oled_draw.c: -------------------------------------------------------------------------------- 1 | #include "oled_draw.h" 2 | #include "i2c.h" 3 | #include 4 | #include 5 | #include 6 | 7 | // OLED器件地址 8 | #define OLED_ADDRESS 0x78 9 | 10 | // OLED参数 11 | #define OLED_PAGE 8 // OLED页数 12 | #define OLED_ROW 8 * OLED_PAGE // OLED行数 13 | #define OLED_COLUMN 128 // OLED列数 14 | 15 | // 显存 16 | uint8_t OLED_GRAM[OLED_PAGE][OLED_COLUMN]; 17 | 18 | // ========================== 底层通信函数 ========================== 19 | 20 | /** 21 | * @brief 向OLED发送数据的函数 22 | * @param data 要发送的数据 23 | * @param len 要发送的数据长度 24 | * @return None 25 | * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他平台时应根据实际情况修改此函数 26 | */ 27 | void OLED_Send(uint8_t *data, uint8_t len) 28 | { 29 | while(HAL_I2C_Master_Transmit_DMA(&hi2c1, OLED_ADDRESS, data, len) != HAL_OK); 30 | } 31 | 32 | /** 33 | * @brief 向OLED发送指令 34 | */ 35 | void OLED_SendCmd(uint8_t cmd) 36 | { 37 | static uint8_t sendBuffer[2] = {0}; 38 | sendBuffer[1] = cmd; 39 | OLED_Send(sendBuffer, 2); 40 | } 41 | 42 | // ========================== OLED驱动函数 ========================== 43 | 44 | /** 45 | * @brief 初始化OLED (SSD1306) 46 | * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他驱动芯片时应根据实际情况修改此函数 47 | */ 48 | void OLED_Init() 49 | { 50 | OLED_SendCmd(0xAE); /*关闭显示 display off*/ 51 | 52 | OLED_SendCmd(0x20); 53 | OLED_SendCmd(0x10); 54 | 55 | OLED_SendCmd(0xB0); 56 | 57 | OLED_SendCmd(0xC0); // 0xC8 & 0xC0 控制上下反转 58 | 59 | OLED_SendCmd(0x00); 60 | OLED_SendCmd(0x10); 61 | 62 | OLED_SendCmd(0x40); 63 | 64 | OLED_SendCmd(0x81); 65 | 66 | OLED_SendCmd(0xDF); 67 | OLED_SendCmd(0xA0); // 0xA0 & 0xA1 控制左右反转 68 | 69 | OLED_SendCmd(0xA6); 70 | OLED_SendCmd(0xA8); 71 | 72 | OLED_SendCmd(0x3F); 73 | 74 | OLED_SendCmd(0xA4); 75 | 76 | OLED_SendCmd(0xD3); 77 | OLED_SendCmd(0x00); 78 | 79 | OLED_SendCmd(0xD5); 80 | OLED_SendCmd(0xF0); 81 | 82 | OLED_SendCmd(0xD9); 83 | OLED_SendCmd(0x22); 84 | 85 | OLED_SendCmd(0xDA); 86 | OLED_SendCmd(0x12); 87 | 88 | OLED_SendCmd(0xDB); 89 | OLED_SendCmd(0x20); 90 | 91 | OLED_SendCmd(0x8D); 92 | OLED_SendCmd(0x14); 93 | 94 | OLED_NewFrame(); 95 | OLED_ShowFrame(); 96 | 97 | OLED_SendCmd(0xAF); /*开启显示 display ON*/ 98 | } 99 | 100 | /** 101 | * @brief 开启OLED显示 102 | */ 103 | void OLED_DisPlay_On() 104 | { 105 | OLED_SendCmd(0x8D); // 电荷泵使能 106 | OLED_SendCmd(0x14); // 开启电荷泵 107 | OLED_SendCmd(0xAF); // 点亮屏幕 108 | } 109 | 110 | /** 111 | * @brief 关闭OLED显示 112 | */ 113 | void OLED_DisPlay_Off() 114 | { 115 | OLED_SendCmd(0x8D); // 电荷泵使能 116 | OLED_SendCmd(0x10); // 关闭电荷泵 117 | OLED_SendCmd(0xAE); // 关闭屏幕 118 | } 119 | 120 | /** 121 | * @brief 设置颜色模式 黑底白字或白底黑字 122 | * @param ColorMode 颜色模式COLOR_NORMAL/COLOR_REVERSED 123 | * @note 此函数直接设置屏幕的颜色模式 124 | */ 125 | void OLED_SetColorMode(OLED_ColorMode mode) 126 | { 127 | if (mode == OLED_COLOR_NORMAL) 128 | { 129 | OLED_SendCmd(0xA6); // 正常显示 130 | } 131 | if (mode == OLED_COLOR_REVERSED) 132 | { 133 | OLED_SendCmd(0xA7); // 反色显示 134 | } 135 | } 136 | 137 | // ========================== 显存操作函数 ========================== 138 | 139 | /** 140 | * @brief 清空显存 绘制新的一帧 141 | */ 142 | void OLED_NewFrame() 143 | { 144 | memset(OLED_GRAM, 0, sizeof(OLED_GRAM)); 145 | } 146 | 147 | /** 148 | * @brief 将当前显存显示到屏幕上 149 | * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他驱动芯片时应根据实际情况修改此函数 150 | */ 151 | void OLED_ShowFrame() 152 | { 153 | static uint8_t sendBuffer[OLED_COLUMN + 1]; 154 | sendBuffer[0] = 0x40; 155 | for (uint8_t i = 0; i < OLED_PAGE; i++) 156 | { 157 | OLED_SendCmd(0xB0 + i); // 设置页地址 158 | OLED_SendCmd(0x00); // 设置列地址低4位 159 | OLED_SendCmd(0x10); // 设置列地址高4位 160 | memcpy(sendBuffer + 1, OLED_GRAM[i], OLED_COLUMN); 161 | OLED_Send(sendBuffer, OLED_COLUMN + 1); 162 | } 163 | } 164 | 165 | /** 166 | * @brief 控制界面渐变消失 167 | * @param moveProcess 移动进度 0-1 168 | */ 169 | void OLED_Disappear(void) 170 | { 171 | srand(HAL_GetTick()); 172 | 173 | for (uint8_t i = 0; i < OLED_PAGE; i++) 174 | { 175 | for (uint8_t j = 0; j < OLED_COLUMN; j++) 176 | { 177 | OLED_GRAM[i][j] = OLED_GRAM[i][j] & (uint8_t)(rand() % 256); 178 | } 179 | } 180 | } 181 | 182 | // void OLED_Appear(float moveProcess) 183 | // { 184 | // srand(HAL_GetTick()); 185 | // int teemo = 0; 186 | // uint8_t Target_GRAM[OLED_PAGE][OLED_COLUMN]; 187 | // memcpy(Target_GRAM, OLED_GRAM, sizeof(OLED_GRAM)); 188 | // for (uint8_t i = 0; i < OLED_PAGE; i++) 189 | // { 190 | // for (uint8_t j = 0; j < OLED_COLUMN; j++) 191 | // { 192 | // teemo = rand() % (int)(moveProcess * 100); 193 | // // if ((teemo) < (moveProcess * 100)) 194 | // // { 195 | // // // 逐渐恢复目标像素 196 | // // OLED_GRAM[i][j] = Target_GRAM[i][j] & (uint8_t)(teemo * 255 / (moveProcess * 100)); 197 | // // } 198 | // // else 199 | // // { 200 | // // // 暂时保持黑色(不显示) 201 | // // OLED_GRAM[i][j] = 0x00; 202 | // // } 203 | // OLED_GRAM[i][j] = Target_GRAM[i][j] & (uint8_t)(teemo * 255 / (moveProcess * 100)); 204 | // } 205 | // } 206 | // } 207 | 208 | /** 209 | * @brief 设置一个像素点 210 | * @param x 横坐标 211 | * @param y 纵坐标 212 | * @param color 颜色 213 | */ 214 | void OLED_SetPixel(int16_t x, int16_t y, OLED_ColorMode color) 215 | { 216 | if (x >= OLED_COLUMN || y >= OLED_ROW || x < 0 || y < 0) 217 | return; 218 | if (!color) 219 | { 220 | OLED_GRAM[y / 8][x] |= 1 << (y % 8); 221 | } 222 | else 223 | { 224 | OLED_GRAM[y / 8][x] ^= (1 << (y % 8)); 225 | } 226 | } 227 | 228 | /** 229 | * @brief 设置显存中一字节数据的某几位 230 | * @param page 页地址 231 | * @param column 列地址 232 | * @param data 数据 233 | * @param start 起始位 234 | * @param end 结束位 235 | * @param color 颜色 236 | * @note 此函数将显存中的某一字节的第start位到第end位设置为与data相同 237 | * @note start和end的范围为0-7, start必须小于等于end 238 | * @note 此函数与OLED_SetByte_Fine的区别在于此函数只能设置显存中的某一真实字节 239 | */ 240 | void OLED_SetByte_Fine(uint8_t page, uint8_t column, uint8_t data, uint8_t start, uint8_t end, OLED_ColorMode color) 241 | { 242 | static uint8_t temp; 243 | if (page >= OLED_PAGE || column >= OLED_COLUMN) 244 | return; 245 | if (color) 246 | data = ~data; 247 | 248 | temp = data | (0xff << (end + 1)) | (0xff >> (8 - start)); 249 | OLED_GRAM[page][column] &= temp; 250 | temp = data & ~(0xff << (end + 1)) & ~(0xff >> (8 - start)); 251 | OLED_GRAM[page][column] |= temp; 252 | // 使用OLED_SetPixel实现 253 | // for (uint8_t i = start; i <= end; i++) { 254 | // OLED_SetPixel(column, page * 8 + i, !((data >> i) & 0x01)); 255 | // } 256 | } 257 | 258 | /** 259 | * @brief 设置显存中的一字节数据 260 | * @param page 页地址 261 | * @param column 列地址 262 | * @param data 数据 263 | * @param color 颜色 264 | * @note 此函数将显存中的某一字节设置为data的值 265 | */ 266 | void OLED_SetByte(uint8_t page, uint8_t column, uint8_t data, OLED_ColorMode color) 267 | { 268 | if (page >= OLED_PAGE || column >= OLED_COLUMN) 269 | return; 270 | if (color) 271 | data = ~data; 272 | OLED_GRAM[page][column] = data; 273 | } 274 | 275 | /** 276 | * @brief 设置显存中的一字节数据的某几位 277 | * @param x 横坐标 278 | * @param y 纵坐标 279 | * @param data 数据 280 | * @param len 位数 281 | * @param color 颜色 282 | * @note 此函数将显存中从(x,y)开始向下数len位设置为与data相同 283 | * @note len的范围为1-8 284 | * @note 此函数与OLED_SetByte_Fine的区别在于此函数的横坐标和纵坐标是以像素为单位的, 可能出现跨两个真实字节的情况(跨页) 285 | */ 286 | void OLED_SetBits_Fine(int8_t x, int8_t y, uint8_t data, uint8_t len, OLED_ColorMode color) 287 | { 288 | uint8_t page = 0; 289 | uint8_t bit = 0; 290 | if (y < 0 && y > -8) 291 | { 292 | page = 0; 293 | bit = y + 8; 294 | } 295 | else 296 | { 297 | page = y / 8; 298 | bit = y % 8; 299 | } 300 | 301 | if (bit + len > 8) 302 | { 303 | OLED_SetByte_Fine(page, x, data << bit, bit, 7, color); 304 | OLED_SetByte_Fine(page + 1, x, data >> (8 - bit), 0, len + bit - 1 - 8, color); 305 | } 306 | else 307 | { 308 | OLED_SetByte_Fine(page, x, data << bit, bit, bit + len - 1, color); 309 | } 310 | // 使用OLED_SetPixel实现 311 | // for (uint8_t i = 0; i < len; i++) { 312 | // OLED_SetPixel(x, y + i, !((data >> i) & 0x01)); 313 | // } 314 | } 315 | 316 | /** 317 | * @brief 设置显存中一字节长度的数据 318 | * @param x 横坐标 319 | * @param y 纵坐标 320 | * @param data 数据 321 | * @param color 颜色 322 | * @note 此函数将显存中从(x,y)开始向下数8位设置为与data相同 323 | * @note 此函数与OLED_SetByte的区别在于此函数的横坐标和纵坐标是以像素为单位的, 可能出现跨两个真实字节的情况(跨页) 324 | */ 325 | void OLED_SetBits(int8_t x, int8_t y, uint8_t data, OLED_ColorMode color) 326 | { 327 | uint8_t page; 328 | uint8_t bit; 329 | // printf("oled: %d, %d\n", x, y); 330 | if (y < 0 && y > -8) 331 | { 332 | page = 0; 333 | bit = y + 8; 334 | OLED_SetByte_Fine(page, x, data >> (8 - bit), 0, bit - 1, color); 335 | return; 336 | } 337 | else 338 | { 339 | page = y / 8; 340 | bit = y % 8; 341 | } 342 | 343 | OLED_SetByte_Fine(page, x, data << bit, bit, 7, color); 344 | if (bit) 345 | { 346 | OLED_SetByte_Fine(page + 1, x, data >> (8 - bit), 0, bit - 1, color); 347 | } 348 | } 349 | 350 | /** 351 | * @brief 设置一块显存区域 352 | * @param x 起始横坐标 353 | * @param y 起始纵坐标 354 | * @param data 数据的起始地址 355 | * @param w 宽度 356 | * @param h 高度 357 | * @param color 颜色 358 | * @note 此函数将显存中从(x,y)开始的w*h个像素设置为data中的数据 359 | * @note data的数据应该采用列行式排列 360 | */ 361 | void OLED_SetBlock(int8_t x, int8_t y, const uint8_t *data, uint8_t w, uint8_t h, OLED_ColorMode color) 362 | { 363 | uint8_t fullRow = h / 8; // 完整的行数 364 | uint8_t partBit = h % 8; // 不完整的字节中的有效位数 365 | for (uint8_t i = 0; i < w; i++) 366 | { 367 | for (uint8_t j = 0; j < fullRow; j++) 368 | { 369 | OLED_SetBits(x + i, y + j * 8, data[i + j * w], color); 370 | } 371 | } 372 | if (partBit) 373 | { 374 | uint16_t fullNum = w * fullRow; // 完整的字节数 375 | for (uint8_t i = 0; i < w; i++) 376 | { 377 | OLED_SetBits_Fine(x + i, y + (fullRow * 8), data[fullNum + i], partBit, color); 378 | } 379 | } 380 | // 使用OLED_SetPixel实现 381 | // for (uint8_t i = 0; i < w; i++) { 382 | // for (uint8_t j = 0; j < h; j++) { 383 | // for (uint8_t k = 0; k < 8; k++) { 384 | // if (j * 8 + k >= h) break; // 防止越界(不完整的字节 385 | // OLED_SetPixel(x + i, y + j * 8 + k, !((data[i + j * w] >> k) & 0x01)); 386 | // } 387 | // } 388 | // } 389 | } 390 | 391 | // ========================== 图形绘制函数 ========================== 392 | /** 393 | * @brief 绘制一条线段 394 | * @param x1 起始点横坐标 395 | * @param y1 起始点纵坐标 396 | * @param x2 终止点横坐标 397 | * @param y2 终止点纵坐标 398 | * @param color 颜色 399 | * @note 此函数使用Bresenham算法绘制线段 400 | */ 401 | void OLED_DrawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, OLED_ColorMode color) 402 | { 403 | 404 | static int16_t temp = 0; 405 | if (x1 == x2) 406 | { 407 | if (y1 > y2) 408 | { 409 | temp = y1; 410 | y1 = y2; 411 | y2 = temp; 412 | } 413 | for (int16_t y = y1; y <= y2; y++) 414 | { 415 | OLED_SetPixel(x1, y, color); 416 | } 417 | } 418 | else if (y1 == y2) 419 | { 420 | if (x1 > x2) 421 | { 422 | temp = x1; 423 | x1 = x2; 424 | x2 = temp; 425 | } 426 | for (int16_t x = x1; x <= x2; x++) 427 | { 428 | OLED_SetPixel(x, y1, color); 429 | } 430 | } 431 | else 432 | { 433 | // Bresenham直线算法 434 | int16_t dx = x2 - x1; 435 | int16_t dy = y2 - y1; 436 | int16_t ux = ((dx > 0) << 1) - 1; 437 | int16_t uy = ((dy > 0) << 1) - 1; 438 | int16_t x = x1, y = y1, eps = 0; 439 | dx = abs(dx); 440 | dy = abs(dy); 441 | if (dx > dy) 442 | { 443 | for (x = x1; x != x2; x += ux) 444 | { 445 | OLED_SetPixel(x, y, color); 446 | eps += dy; 447 | if ((eps << 1) >= dx) 448 | { 449 | y += uy; 450 | eps -= dx; 451 | } 452 | } 453 | } 454 | else 455 | { 456 | for (y = y1; y != y2; y += uy) 457 | { 458 | OLED_SetPixel(x, y, color); 459 | eps += dx; 460 | if ((eps << 1) >= dy) 461 | { 462 | x += ux; 463 | eps -= dy; 464 | } 465 | } 466 | } 467 | } 468 | } 469 | 470 | /** 471 | * @brief 绘制一个矩形 472 | * @param x 起始点横坐标 473 | * @param y 起始点纵坐标 474 | * @param w 矩形宽度 475 | * @param h 矩形高度 476 | * @param color 颜色 477 | */ 478 | void OLED_DrawRectangle(int16_t x, int16_t y, uint8_t w, uint8_t h, OLED_ColorMode color) 479 | { 480 | OLED_DrawLine(x, y, x + w, y, color); 481 | OLED_DrawLine(x, y + h, x + w, y + h, color); 482 | OLED_DrawLine(x, y, x, y + h, color); 483 | OLED_DrawLine(x + w, y, x + w, y + h, color); 484 | } 485 | 486 | /** 487 | * @brief 绘制一个填充矩形 488 | * @param x 起始点横坐标 489 | * @param y 起始点纵坐标 490 | * @param w 矩形宽度 491 | * @param h 矩形高度 492 | * @param color 颜色 493 | */ 494 | void OLED_DrawFilledRectangle(int16_t x, int16_t y, uint8_t w, uint8_t h, OLED_ColorMode color) 495 | { 496 | for (uint8_t i = 0; i < h; i++) 497 | { 498 | OLED_DrawLine(x, y + i, x + w, y + i, color); 499 | } 500 | } 501 | 502 | /** 503 | * @brief 绘制一个空白矩形 504 | * @param x 起始点横坐标 505 | * @param y 起始点纵坐标 506 | * @param w 矩形宽度 507 | * @param h 矩形高度 508 | */ 509 | void OLED_DrawEmptyRectangle(int16_t x, int16_t y, uint8_t w, uint8_t h) 510 | { 511 | 512 | 513 | for (int16_t i = x; i <= x + w; i++) 514 | { 515 | for (int16_t j = y; j <= y + h; j++) 516 | { 517 | OLED_GRAM[j / 8][i] &= ~(1 << (j % 8)); 518 | } 519 | } 520 | 521 | OLED_DrawLine(x, y, x + w, y, OLED_COLOR_NORMAL); 522 | OLED_DrawLine(x, y + h, x + w, y + h, OLED_COLOR_NORMAL); 523 | OLED_DrawLine(x, y, x, y + h, OLED_COLOR_NORMAL); 524 | OLED_DrawLine(x + w, y, x + w, y + h, OLED_COLOR_NORMAL); 525 | 526 | } 527 | 528 | 529 | /** 530 | * @brief 绘制一个圆角矩形 531 | * @param x 起始点横坐标 532 | * @param y 起始点纵坐标 533 | * @param w 矩形宽度 534 | * @param h 矩形高度 535 | * @param color 颜色 536 | */ 537 | void OLED_DrawRectangleWithCorners(int16_t x, int16_t y, uint8_t w, uint8_t h, OLED_ColorMode color) 538 | { 539 | OLED_DrawLine(x + 2, y, x + w - 2, y, color); 540 | OLED_DrawLine(x + 2, y + h, x + w - 2, y + h, color); 541 | OLED_DrawLine(x, y + 2, x, y + h - 2, color); 542 | OLED_DrawLine(x + w, y + 2, x + w, y + h - 2, color); 543 | 544 | OLED_DrawLine(x, y + 2, x + 2, y + 2, color); 545 | OLED_DrawLine(x, y + h - 2, x + 2, y + h - 2, color); 546 | OLED_DrawLine(x + w - 2, y + 2, x + w, y + 2, color); 547 | OLED_DrawLine(x + w - 2, y + h - 2, x + w, y + h - 2, color); 548 | 549 | OLED_DrawLine(x + 2, y, x + 2, y + 2, color); 550 | OLED_DrawLine(x + w - 2, y, x + w - 2, y + 2, color); 551 | OLED_DrawLine(x + 2, y + h, x + 2, y + h - 2, color); 552 | OLED_DrawLine(x + w - 2, y + h, x + w - 2, y + h - 2, color); 553 | } 554 | 555 | /** 556 | * @brief 绘制一个填充圆角矩形 557 | * @param x 起始点横坐标 558 | * @param y 起始点纵坐标 559 | * @param w 矩形宽度 560 | * @param h 矩形高度 561 | * @param color 颜色 562 | */ 563 | void OLED_DrawFilledRectangleWithCorners(int16_t x, int16_t y, uint8_t w, uint8_t h, OLED_ColorMode color) 564 | { 565 | for (uint8_t i = 0; i < 2; i++) 566 | { 567 | OLED_DrawLine(x + 2, y + i, x + w - 2, y + i, color); 568 | } 569 | for (uint8_t i = 2; i < h - 2; i++) 570 | { 571 | OLED_DrawLine(x, y + i, x + w, y + i, color); 572 | } 573 | for (uint8_t i = h - 2; i < h; i++) 574 | { 575 | OLED_DrawLine(x + 2, y + i, x + w - 2, y + i, color); 576 | } 577 | } 578 | 579 | /** 580 | * @brief 绘制一个三角形 581 | * @param x1 第一个点横坐标 582 | * @param y1 第一个点纵坐标 583 | * @param x2 第二个点横坐标 584 | * @param y2 第二个点纵坐标 585 | * @param x3 第三个点横坐标 586 | * @param y3 第三个点纵坐标 587 | * @param color 颜色 588 | */ 589 | void OLED_DrawTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color) 590 | { 591 | OLED_DrawLine(x1, y1, x2, y2, color); 592 | OLED_DrawLine(x2, y2, x3, y3, color); 593 | OLED_DrawLine(x3, y3, x1, y1, color); 594 | } 595 | 596 | /** 597 | * @brief 绘制一个填充三角形 598 | * @param x1 第一个点横坐标 599 | * @param y1 第一个点纵坐标 600 | * @param x2 第二个点横坐标 601 | * @param y2 第二个点纵坐标 602 | * @param x3 第三个点横坐标 603 | * @param y3 第三个点纵坐标 604 | * @param color 颜色 605 | */ 606 | void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color) 607 | { 608 | uint8_t a = 0, b = 0, y = 0, last = 0; 609 | if (y1 > y2) 610 | { 611 | a = y2; 612 | b = y1; 613 | } 614 | else 615 | { 616 | a = y1; 617 | b = y2; 618 | } 619 | y = a; 620 | for (; y <= b; y++) 621 | { 622 | if (y <= y3) 623 | { 624 | OLED_DrawLine(x1 + (y - y1) * (x2 - x1) / (y2 - y1), y, x1 + (y - y1) * (x3 - x1) / (y3 - y1), y, color); 625 | } 626 | else 627 | { 628 | last = y - 1; 629 | break; 630 | } 631 | } 632 | for (; y <= b; y++) 633 | { 634 | OLED_DrawLine(x2 + (y - y2) * (x3 - x2) / (y3 - y2), y, x1 + (y - last) * (x3 - x1) / (y3 - last), y, color); 635 | } 636 | } 637 | 638 | /** 639 | * @brief 绘制一个圆 640 | * @param x 圆心横坐标 641 | * @param y 圆心纵坐标 642 | * @param r 圆半径 643 | * @param color 颜色 644 | * @note 此函数使用Bresenham算法绘制圆 645 | */ 646 | void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color) 647 | { 648 | int16_t a = 0, b = r, di = 3 - (r << 1); 649 | while (a <= b) 650 | { 651 | OLED_SetPixel(x - b, y - a, color); 652 | OLED_SetPixel(x + b, y - a, color); 653 | OLED_SetPixel(x - a, y + b, color); 654 | OLED_SetPixel(x - b, y - a, color); 655 | OLED_SetPixel(x - a, y - b, color); 656 | OLED_SetPixel(x + b, y + a, color); 657 | OLED_SetPixel(x + a, y - b, color); 658 | OLED_SetPixel(x + a, y + b, color); 659 | OLED_SetPixel(x - b, y + a, color); 660 | a++; 661 | if (di < 0) 662 | { 663 | di += 4 * a + 6; 664 | } 665 | else 666 | { 667 | di += 10 + 4 * (a - b); 668 | b--; 669 | } 670 | OLED_SetPixel(x + a, y + b, color); 671 | } 672 | } 673 | 674 | /** 675 | * @brief 绘制一个填充圆 676 | * @param x 圆心横坐标 677 | * @param y 圆心纵坐标 678 | * @param r 圆半径 679 | * @param color 颜色 680 | * @note 此函数使用Bresenham算法绘制圆 681 | */ 682 | void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color) 683 | { 684 | int16_t a = 0, b = r, di = 3 - (r << 1); 685 | while (a <= b) 686 | { 687 | for (int16_t i = x - b; i <= x + b; i++) 688 | { 689 | OLED_SetPixel(i, y + a, color); 690 | OLED_SetPixel(i, y - a, color); 691 | } 692 | for (int16_t i = x - a; i <= x + a; i++) 693 | { 694 | OLED_SetPixel(i, y + b, color); 695 | OLED_SetPixel(i, y - b, color); 696 | } 697 | a++; 698 | if (di < 0) 699 | { 700 | di += 4 * a + 6; 701 | } 702 | else 703 | { 704 | di += 10 + 4 * (a - b); 705 | b--; 706 | } 707 | } 708 | } 709 | 710 | /** 711 | * @brief 绘制一个椭圆 712 | * @param x 椭圆中心横坐标 713 | * @param y 椭圆中心纵坐标 714 | * @param a 椭圆长轴 715 | * @param b 椭圆短轴 716 | */ 717 | void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b, OLED_ColorMode color) 718 | { 719 | int xpos = 0, ypos = b; 720 | int a2 = a * a, b2 = b * b; 721 | int d = b2 + a2 * (0.25 - b); 722 | while (a2 * ypos > b2 * xpos) 723 | { 724 | OLED_SetPixel(x + xpos, y + ypos, color); 725 | OLED_SetPixel(x - xpos, y + ypos, color); 726 | OLED_SetPixel(x + xpos, y - ypos, color); 727 | OLED_SetPixel(x - xpos, y - ypos, color); 728 | if (d < 0) 729 | { 730 | d = d + b2 * ((xpos << 1) + 3); 731 | xpos += 1; 732 | } 733 | else 734 | { 735 | d = d + b2 * ((xpos << 1) + 3) + a2 * (-(ypos << 1) + 2); 736 | xpos += 1, ypos -= 1; 737 | } 738 | } 739 | d = b2 * (xpos + 0.5) * (xpos + 0.5) + a2 * (ypos - 1) * (ypos - 1) - a2 * b2; 740 | while (ypos > 0) 741 | { 742 | OLED_SetPixel(x + xpos, y + ypos, color); 743 | OLED_SetPixel(x - xpos, y + ypos, color); 744 | OLED_SetPixel(x + xpos, y - ypos, color); 745 | OLED_SetPixel(x - xpos, y - ypos, color); 746 | if (d < 0) 747 | { 748 | d = d + b2 * ((xpos << 1) + 2) + a2 * (-(ypos << 1) + 3); 749 | xpos += 1, ypos -= 1; 750 | } 751 | else 752 | { 753 | d = d + a2 * (-(ypos << 1) + 3); 754 | ypos -= 1; 755 | } 756 | } 757 | } 758 | 759 | /** 760 | * @brief 绘制一张图片 761 | * @param x 起始点横坐标 762 | * @param y 起始点纵坐标 763 | * @param img 图片 764 | * @param color 颜色 765 | */ 766 | void OLED_DrawImage(uint8_t x, uint8_t y, const Image *img, OLED_ColorMode color) 767 | { 768 | OLED_SetBlock(x, y, img->data, img->w, img->h, color); 769 | } 770 | 771 | // ================================ 文字绘制 ================================ 772 | 773 | /** 774 | * @brief 绘制一个ASCII字符 775 | * @param x 起始点横坐标 776 | * @param y 起始点纵坐标 777 | * @param ch 字符 778 | * @param font 字体 779 | * @param color 颜色 780 | */ 781 | void OLED_PrintASCIIChar(int16_t x, int16_t y, char ch, const ASCIIFont *font, OLED_ColorMode color) 782 | { 783 | OLED_SetBlock(x, y, font->chars + (ch - ' ') * (((font->h + 7) / 8) * font->w), font->w, font->h, color); 784 | } 785 | 786 | /** 787 | * @brief 绘制一个ASCII字符串 788 | * @param x 起始点横坐标 789 | * @param y 起始点纵坐标 790 | * @param str 字符串 791 | * @param font 字体 792 | * @param color 颜色 793 | */ 794 | void OLED_PrintASCIIString(int16_t x, int16_t y, char *str, const ASCIIFont *font, OLED_ColorMode color) 795 | { 796 | int16_t x0 = x; 797 | while (*str) 798 | { 799 | OLED_PrintASCIIChar(x0, y, *str, font, color); 800 | x0 += font->w; 801 | str++; 802 | } 803 | } 804 | 805 | /** 806 | * @brief 获取UTF-8编码的字符长度 807 | */ 808 | uint8_t _OLED_GetUTF8Len(char *string) 809 | { 810 | if ((string[0] & 0x80) == 0x00) 811 | { 812 | return 1; 813 | } 814 | else if ((string[0] & 0xE0) == 0xC0) 815 | { 816 | return 2; 817 | } 818 | else if ((string[0] & 0xF0) == 0xE0) 819 | { 820 | return 3; 821 | } 822 | else if ((string[0] & 0xF8) == 0xF0) 823 | { 824 | return 4; 825 | } 826 | return 0; 827 | } 828 | 829 | /** 830 | * @brief 绘制字符串 831 | * @param x 起始点横坐标 832 | * @param y 起始点纵坐标 833 | * @param str 字符串 834 | * @param font 字体 835 | * @param color 颜色 836 | * 837 | * @note 为保证字符串中的中文会被自动识别并绘制, 需: 838 | * 1. 编译器字符集设置为UTF-8 839 | * 2. 使用波特律动LED取模工具生成字模(https://led.baud-dance.com) 840 | */ 841 | 842 | void OLED_PrintString(uint8_t x, uint8_t y, char *str, const Font *font, OLED_ColorMode color) 843 | { 844 | uint16_t i = 0; // 字符串索引 845 | uint8_t oneLen = (((font->h + 7) / 8) * font->w) + 4; // 一个字模占多少字节 846 | uint8_t found; // 是否找到字模 847 | uint8_t utf8Len; // UTF-8编码长度 848 | uint8_t *head; // 字模头指针 849 | while (str[i]) 850 | { 851 | found = 0; 852 | utf8Len = _OLED_GetUTF8Len(str + i); 853 | if (utf8Len == 0) 854 | break; // 有问题的UTF-8编码 855 | 856 | // 寻找字符 TODO 优化查找算法, 二分查找或者hash 857 | for (uint8_t j = 0; j < font->len; j++) 858 | { 859 | head = (uint8_t *)(font->chars) + (j * oneLen); 860 | if (memcmp(str + i, head, utf8Len) == 0) 861 | { 862 | OLED_SetBlock(x, y, head + 4, font->w, font->h, color); 863 | // 移动光标 864 | x += font->w; 865 | i += utf8Len; 866 | found = 1; 867 | break; 868 | } 869 | } 870 | 871 | // 若未找到字模,且为ASCII字符, 则缺省显示ASCII字符 872 | if (found == 0) 873 | { 874 | if (utf8Len == 1) 875 | { 876 | OLED_PrintASCIIChar(x, y, str[i], font->ascii, color); 877 | // 移动光标 878 | x += font->ascii->w; 879 | i += utf8Len; 880 | } 881 | else 882 | { 883 | OLED_PrintASCIIChar(x, y, ' ', font->ascii, color); 884 | x += font->ascii->w; 885 | i += utf8Len; 886 | } 887 | } 888 | } 889 | } 890 | -------------------------------------------------------------------------------- /User/Src/oled_menu.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file oled_menu.c 3 | * @brief OLED菜单系统实现,包含菜单项管理、按键响应及UI动态更新 4 | * @author [Ykl] 5 | * @version 1.0 6 | * @date 2025-2-8 7 | * @license MIT 8 | * 9 | * ____ ____ __ ___ __ 10 | * \ \ / / | |/ / | | 11 | * \ \/ / | ' / | | 12 | * \_ _/ | < | | 13 | * | | | . \ | `----. 14 | * |__| |__|\__\ |_______| 15 | * 16 | * @note 功能特性: 17 | * - 多级菜单系统支持 18 | * - 平滑动画过渡效果 19 | * - 支持三种控件类型(开关/数值显示/滑动条) 20 | * - 长短按按键区分处理 21 | * - 自适应滚动条显示 22 | * 23 | * @warning 硬件依赖: 24 | * - STM32 HAL库 25 | * - 128x64 OLED显示屏(SSD1306) 26 | * - 两个物理按键(左/右) 27 | * 28 | * @acknowledgment oled硬件驱动借鉴参考波特律动(keysking)波特律动OLED驱动(SSD1306) 29 | */ 30 | 31 | #include "oled_menu.h" 32 | 33 | /** 34 | * @brief 主菜单项。 35 | * 36 | * 定义了主菜单的静态项。 37 | */ 38 | ItemTypedef mainMenuItems[] = 39 | { 40 | {"example", 7, NULL, NULL, NULL}, /**< 示例菜单项 */ 41 | 42 | }; 43 | 44 | /** 45 | * @brief 主菜单。 46 | * 47 | * 包含主菜单的名称、菜单项、父菜单以及菜单项数量。 48 | */ 49 | Menutypedef mainMenu = {"mainMenu", mainMenuItems, NULL, sizeof(mainMenuItems) / sizeof(ItemTypedef), 0}; 50 | 51 | Menutypedef *currentMenu = &mainMenu; 52 | 53 | /** 54 | * @brief 按键列表。 55 | * 56 | * 存储所有按键的状态信息。 57 | */ 58 | KeyTypeDef KeyList[2] = {0}; 59 | 60 | /** 61 | * @brief 菜单框的 Y 坐标。 62 | */ 63 | UIElemTypedef frameY = 64 | { 65 | .val = 0, /**< 当前值 */ 66 | .targetVal = 0, /**< 目标值 */ 67 | .lastVal = 0 /**< 上一次值 */ 68 | }; 69 | 70 | /** 71 | * @brief 菜单框的宽度。 72 | */ 73 | UIElemTypedef frameWidth = 74 | { 75 | .val = 0, /**< 当前值 */ 76 | .targetVal = 0, /**< 目标值 */ 77 | .lastVal = 0 /**< 上一次值 */ 78 | }; 79 | 80 | /** 81 | * @brief 屏幕顶部的 Y 坐标。 82 | */ 83 | UIElemTypedef screenTop = 84 | { 85 | .val = 0, /**< 当前值 */ 86 | .targetVal = 0, /**< 目标值 */ 87 | .lastVal = 0 /**< 上一次值 */ 88 | }; 89 | 90 | /** 91 | * @brief 滚动条的 Y 坐标。 92 | */ 93 | UIElemTypedef scrollBarY = 94 | { 95 | .val = 2, /**< 当前值 */ 96 | .targetVal = 2, /**< 目标值 */ 97 | .lastVal = 2 /**< 上一次值 */ 98 | }; 99 | 100 | /** 101 | * @brief 开关控件条的 Y 坐标。 102 | */ 103 | UIElemTypedef switchCtrlBar = 104 | { 105 | .val = 64 + 2, /**< 当前值 */ 106 | .targetVal = 64 + 2, /**< 目标值 */ 107 | .lastVal = 64 + 2 /**< 上一次值 */ 108 | }; 109 | 110 | /** 111 | * @brief 显示控件条的 Y 坐标。 112 | */ 113 | UIElemTypedef displayCtrlBar = 114 | { 115 | .val = 0, /**< 当前值 */ 116 | .targetVal = 0, /**< 目标值 */ 117 | .lastVal = 0 /**< 上一次值 */ 118 | }; 119 | 120 | /** 121 | * @brief 屏幕索引。 122 | * 123 | * 包含屏幕顶部和底部菜单项的索引。 124 | */ 125 | ScreenIndexTypedef screenIndex = 126 | { 127 | .topIndex = 0, /**< 顶部索引 */ 128 | .bottomIndex = 3 /**< 底部索引 */ 129 | }; 130 | 131 | /** 132 | * @brief 当前按键 ID。 133 | * 134 | * 0 表示没有按键响应,1 表示 KEY1,2 表示 KEY2。 135 | */ 136 | uint8_t keyID = 0; 137 | 138 | /** 139 | * @brief 菜单切换标志。 140 | * 141 | * 0 表示没有切换菜单,1 表示切换菜单。 142 | */ 143 | uint8_t menuSwitchFlag = 0; 144 | 145 | /** 146 | * @brief 控件选择标志。 147 | * 148 | * 0 表示没有选择控件,1 表示选择控件。 149 | */ 150 | uint8_t controlSelectionFlag = 0; 151 | 152 | /** 153 | * @brief 动画进度变量,用于平滑过渡 UI 组件的移动效果。 154 | * 155 | * 这些变量的值范围通常为 0.0 到 1.0,表示当前动画的进度。 156 | */ 157 | 158 | /** @brief 框架 Y 方向的移动进度 */ 159 | float moveProcess_FrameY = 0.0; 160 | 161 | /** @brief 框架宽度变化的移动进度 */ 162 | float moveProcess_FrameWidth = 0.0; 163 | 164 | /** @brief 屏幕滚动的移动进度 */ 165 | float moveProcess_Screen = 0.0; 166 | 167 | /** @brief 滚动条的移动进度 */ 168 | float moveProcess_ScrollBar = 0.0; 169 | 170 | /** @brief 开关控件滑块的移动进度 */ 171 | float moveProcess_SwitchCtrlBar = 0.0; 172 | 173 | 174 | // ------------函数定义------------// 175 | /** 176 | * @brief 按键中断回调函数。 177 | * 178 | * 根据按键引脚处理按下、释放和长按短按事件。 179 | * 180 | * @param GPIO_Pin 按键引脚号。 181 | */ 182 | void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) 183 | { 184 | uint32_t currentTime = HAL_GetTick(); 185 | 186 | if (GPIO_Pin == RIGHT_KEY_GPIO_PIN) 187 | { 188 | if (HAL_GPIO_ReadPin(RIGHT_KEY_GPIOX, RIGHT_KEY_GPIO_PIN) == GPIO_PIN_SET) // 默认高电平按下,可以更改 189 | { 190 | KeyList[0].val = 1; 191 | KeyList[0].pressTime = currentTime; 192 | keyID = 1; 193 | } 194 | else 195 | { 196 | KeyList[0].val = 0; 197 | KeyList[0].releaseTime = currentTime; 198 | 199 | uint32_t pressDuration = KeyList[0].releaseTime - KeyList[0].pressTime; 200 | if (pressDuration >= LONG_PRESS_THRESHOLD) 201 | { 202 | KeyList[0].longPress = LONG_PRESS; /**< 长按标志 */ 203 | } 204 | else 205 | { 206 | KeyList[0].longPress = SHORT_PRESS; /**< 短按标志 */ 207 | } 208 | } 209 | 210 | if (!KeyList[0].val && KeyList[0].longPress) 211 | { 212 | KeyList[0].updateFlag = 1; 213 | } 214 | 215 | } 216 | else if (GPIO_Pin == LEFT_KEY_GPIO_PIN) 217 | { 218 | if (HAL_GPIO_ReadPin(LEFT_KEY_GPIOX, LEFT_KEY_GPIO_PIN) == GPIO_PIN_SET) 219 | { 220 | KeyList[1].val = 1; 221 | KeyList[1].pressTime = currentTime; 222 | keyID = 2; 223 | } 224 | else 225 | { 226 | KeyList[1].val = 0; 227 | KeyList[1].releaseTime = currentTime; 228 | 229 | uint32_t pressDuration = KeyList[1].releaseTime - KeyList[1].pressTime; 230 | if (pressDuration >= LONG_PRESS_THRESHOLD) 231 | { 232 | KeyList[1].longPress = LONG_PRESS; /**< 长按标志 */ 233 | } 234 | else 235 | { 236 | KeyList[1].longPress = SHORT_PRESS; /**< 短按标志 */ 237 | } 238 | } 239 | 240 | if (!KeyList[1].val && KeyList[1].longPress) 241 | { 242 | KeyList[1].updateFlag = 1; 243 | } 244 | } 245 | } 246 | 247 | 248 | /** 249 | * @brief 创建并添加一个菜单。 250 | * 251 | * @param name 菜单名称。 252 | * @param items 菜单项数组(可为 NULL)。 253 | * @param itemCount 菜单项数量(如果 items 为 NULL,传 0)。 254 | * @param parentMenu 父级菜单(可为 NULL)。 255 | * @return Menutypedef* 返回新创建的菜单指针,失败时返回 NULL。 256 | */ 257 | Menutypedef *AddMenu(const char *name, ItemTypedef *items, uint16_t itemCount, Menutypedef *parentMenu) 258 | { 259 | if (!name) 260 | return NULL; 261 | 262 | // 分配菜单结构体 263 | Menutypedef *newMenu = (Menutypedef *)malloc(sizeof(Menutypedef)); 264 | if (!newMenu) 265 | return NULL; 266 | 267 | // 分配菜单名称 268 | newMenu->menuName = (char *)malloc(strlen(name) + 1); 269 | if (!newMenu->menuName) 270 | { 271 | free(newMenu); 272 | return NULL; 273 | } 274 | strcpy(newMenu->menuName, name); 275 | 276 | // 初始化菜单项 277 | if (items && itemCount > 0) 278 | { 279 | newMenu->items = items; // 指向现有菜单项数组 280 | newMenu->itemCount = itemCount; // 设置菜单项数量 281 | } 282 | else 283 | { 284 | newMenu->items = NULL; // 无菜单项时,初始化为空 285 | newMenu->itemCount = 0; 286 | } 287 | 288 | // 初始化其他属性 289 | newMenu->currentItemIndex = 0; 290 | newMenu->parentMenu = parentMenu; 291 | 292 | return newMenu; 293 | } 294 | 295 | 296 | /** 297 | * @brief 向指定菜单添加一个新的菜单项。 298 | * 299 | * 该函数动态创建并初始化菜单项,并将其添加到 `menu->items` 数组中。 300 | * 如果 `menu->items` 为空,则先分配内存。如果已有菜单项,则使用 `realloc` 进行扩展。 301 | * 302 | * @param menu 指向目标菜单的指针。 303 | * @param name 菜单项的名称(字符串)。 304 | * @param funtion 菜单项对应的回调函数(可为 NULL)。 305 | * @param subMenu 该菜单项的子菜单(可为 NULL)。 306 | * @param ctrlMode 控件模式(如 SWITCH_CTRL、DISPLAY_CTRL 等,无控件时为NONE_CTRL)。 307 | * @param ctrlData 控件数据指针(用于存储控件的值,可为 NULL)。 308 | * @return ItemTypedef* 返回新创建的菜单项指针,若失败则返回 NULL。 309 | * 310 | * @note 311 | * - 该函数会为 `name` 和 `control` 结构体分配内存,确保数据的独立性。 312 | * - 如果 `menu->items` 为空,会先分配空间,否则使用 `realloc` 扩展。 313 | * - 确保 `menu->itemCount` 及时更新,以便正确管理菜单项。 314 | * 315 | * @warning 316 | * - `name` 需要是有效的字符串指针,否则会导致崩溃。 317 | * - 调用该函数后,需确保 `menu` 在程序运行期间不会被提前释放,否则会导致内存访问错误。 318 | */ 319 | ItemTypedef *AddMenuItem(Menutypedef *menu, 320 | const char *name, 321 | void (*funtion)(void), 322 | Menutypedef *subMenu, 323 | ControlModeTypedef ctrlMode, 324 | int *ctrlData) 325 | { 326 | if (!menu || !name) 327 | return NULL; 328 | 329 | ItemTypedef *newItem = (ItemTypedef *)malloc(sizeof(ItemTypedef)); 330 | if (!newItem) 331 | return NULL; 332 | 333 | newItem->str = (char *)malloc(strlen(name) + 1); 334 | if (!newItem->str) 335 | { 336 | free(newItem); 337 | return NULL; 338 | } 339 | strcpy(newItem->str, name); 340 | newItem->len = strlen(newItem->str); 341 | newItem->subMenu = subMenu; 342 | newItem->Function = funtion; 343 | 344 | if (ctrlMode != 0) 345 | { 346 | newItem->control = (ControlTypedef *)malloc(sizeof(ControlTypedef)); 347 | if (!newItem->control) 348 | { 349 | free(newItem->str); 350 | free(newItem); 351 | return NULL; 352 | } 353 | 354 | newItem->control->mode = ctrlMode; 355 | newItem->control->data = ctrlData; 356 | } 357 | else 358 | newItem->control = NONE_CTRL; 359 | 360 | if (!menu->items) 361 | { 362 | 363 | menu->items = (ItemTypedef *)malloc(sizeof(ItemTypedef)); 364 | if (!menu->items) 365 | { 366 | if (newItem->control) 367 | free(newItem->control); 368 | free(newItem->str); 369 | free(newItem); 370 | return NULL; 371 | } 372 | menu->itemCount = 0; 373 | } 374 | else 375 | { 376 | ItemTypedef *temp = (ItemTypedef *)realloc(menu->items, (menu->itemCount + 1) * sizeof(ItemTypedef)); 377 | if (!temp) 378 | { 379 | if (newItem->control) 380 | free(newItem->control); 381 | free(newItem->str); 382 | free(newItem); 383 | return NULL; 384 | } 385 | menu->items = temp; 386 | } 387 | 388 | menu->items[menu->itemCount] = *newItem; 389 | menu->itemCount++; 390 | 391 | free(newItem); 392 | return &menu->items[menu->itemCount - 1]; 393 | } 394 | 395 | 396 | /** 397 | * @brief 初始化 UI 相关参数。 398 | * 399 | * 该函数用于初始化按键状态和 UI 元素的初始值,确保界面在启动时处于正确状态。 400 | * 401 | * @note 402 | * - 使用 `memset` 将 `KeyList` 清零,防止按键状态异常。 403 | * - 计算并设置 `frameWidth.targetVal`,确保选中菜单项的框架宽度正确。 404 | */ 405 | void UI_Init() 406 | { 407 | OLED_Init(); 408 | memset(KeyList, 0, sizeof(KeyList)); 409 | frameWidth.targetVal = currentMenu->items[currentMenu->currentItemIndex].len * 6 + 4; 410 | HAL_Delay(20); 411 | } 412 | 413 | 414 | /** 415 | * @brief 更新 UI 状态。 416 | * 417 | * 根据按键状态标志更新 UI,并触发对应的短按或长按逻辑。 418 | * 419 | * - 如果按键为短按,调用 `KeyShortPress`。 420 | * - 如果按键为长按,调用 `KeyLongPress`。 421 | * - 更新框架、屏幕、滚动条和控件条的显示。 422 | */ 423 | void UI_UpDate() 424 | { 425 | if (KeyList[keyID - 1].updateFlag) 426 | { 427 | KeyList[keyID - 1].updateFlag = 0; 428 | 429 | if (KeyList[keyID - 1].longPress == 2) 430 | { 431 | KeyShortPress(); /**< 处理短按逻辑 */ 432 | } 433 | else if (KeyList[keyID - 1].longPress == 1) 434 | { 435 | KeyLongPress(); /**< 处理长按逻辑 */ 436 | } 437 | 438 | Frame_Update(); /**< 更新框架显示 */ 439 | Screen_Update(); /**< 更新屏幕显示 */ 440 | ScrollBar_Update(); /**< 更新滚动条显示 */ 441 | switchCtrlBar_Update();/**< 更新开关控件条显示 */ 442 | } 443 | } 444 | 445 | /** 446 | * @brief 处理按键长按逻辑。 447 | * 448 | * 根据当前的控件选择状态和按键 ID,执行不同的操作: 449 | */ 450 | void KeyLongPress() 451 | { 452 | if (controlSelectionFlag == 0) 453 | { 454 | if (keyID == 1) 455 | { 456 | // 执行当前菜单项的功能函数 457 | if (currentMenu->items[currentMenu->currentItemIndex].Function) 458 | { 459 | currentMenu->items[currentMenu->currentItemIndex].Function(); 460 | } 461 | } 462 | else if (keyID == 2) 463 | { 464 | // 返回父菜单 465 | if (currentMenu->parentMenu) 466 | { 467 | currentMenu = currentMenu->parentMenu; 468 | menuSwitchFlag = 1; // 设置菜单切换标志 469 | } 470 | } 471 | } 472 | else if (controlSelectionFlag == 1) 473 | { 474 | if (keyID == 1) 475 | { 476 | // 按键 1 暂未实现功能 477 | } 478 | else if (keyID == 2) 479 | { 480 | // 退出控件选择状态 481 | controlSelectionFlag = 0; 482 | menuSwitchFlag = 1; // 设置菜单切换标志 483 | } 484 | } 485 | } 486 | 487 | /** 488 | * @brief 处理按键短按逻辑。 489 | * 490 | * 根据控件选择状态和按键 ID,执行不同的操作 491 | */ 492 | void KeyShortPress() 493 | { 494 | if (controlSelectionFlag == 0) 495 | { 496 | if (keyID == 1) 497 | { 498 | currentMenu->currentItemIndex++; /**< 移动到下一个菜单项 */ 499 | } 500 | else if (keyID == 2) 501 | { 502 | currentMenu->currentItemIndex--; /**< 移动到上一个菜单项 */ 503 | } 504 | 505 | // 循环索引 506 | if (currentMenu->currentItemIndex == currentMenu->itemCount) 507 | { 508 | currentMenu->currentItemIndex = 0; /**< 回到菜单开头 */ 509 | } 510 | else if (currentMenu->currentItemIndex == -1) 511 | { 512 | currentMenu->currentItemIndex = currentMenu->itemCount - 1; /**< 回到菜单结尾 */ 513 | } 514 | } 515 | else if (controlSelectionFlag == 1) 516 | { 517 | // 控件操作逻辑 518 | switch (currentMenu->items[currentMenu->currentItemIndex].control->mode) 519 | { 520 | case SWITCH_CTRL: 521 | // 切换开关状态 522 | if (keyID == 1 || keyID == 2) 523 | { 524 | if (*(currentMenu->items[currentMenu->currentItemIndex].control->data) == 0) 525 | { 526 | *(currentMenu->items[currentMenu->currentItemIndex].control->data) = 1; /**< 设置为开启 */ 527 | } 528 | else 529 | { 530 | *(currentMenu->items[currentMenu->currentItemIndex].control->data) = 0; /**< 设置为关闭 */ 531 | } 532 | } 533 | break; 534 | 535 | case DISPLAY_CTRL: 536 | // 显示控件,暂不处理 537 | break; 538 | 539 | case SLIDER_CTRL: 540 | // 调整滑动条值 541 | if (keyID == 1) 542 | { 543 | *(currentMenu->items[currentMenu->currentItemIndex].control->data) += 1; /**< 增加值 */ 544 | } 545 | else if (keyID == 2) 546 | { 547 | *(currentMenu->items[currentMenu->currentItemIndex].control->data) -= 1; /**< 减小值 */ 548 | } 549 | break; 550 | 551 | default: 552 | break; 553 | } 554 | } 555 | } 556 | 557 | /** 558 | * @brief 更新框架显示目标值。 559 | * 560 | * 根据当前菜单项索引,更新框架的 Y 坐标和宽度的目标值。 561 | */ 562 | void Frame_Update() 563 | { 564 | moveProcess_FrameY = 0.0; 565 | moveProcess_FrameWidth = 0.0; 566 | 567 | frameY.lastVal = frameY.targetVal; 568 | frameWidth.lastVal = frameWidth.targetVal; 569 | 570 | frameY.targetVal = currentMenu->currentItemIndex * MENU_ITEM_HEIGHT; /**< 更新 Y 目标值 */ 571 | frameWidth.targetVal = currentMenu->items[currentMenu->currentItemIndex].len * 6 + 4; /**< 更新宽度目标值 */ 572 | } 573 | 574 | /** 575 | * @brief 更新屏幕显示范围。 576 | * 577 | * 根据当前菜单项索引调整屏幕的顶部和底部索引,并更新屏幕顶部的目标值。 578 | */ 579 | void Screen_Update() 580 | { 581 | if (currentMenu->currentItemIndex > screenIndex.bottomIndex) 582 | { 583 | moveProcess_Screen = 0.0; 584 | screenIndex.bottomIndex = currentMenu->currentItemIndex; 585 | screenIndex.topIndex = screenIndex.bottomIndex - 3; /**< 调整顶部索引 */ 586 | } 587 | else if (currentMenu->currentItemIndex < screenIndex.topIndex) 588 | { 589 | moveProcess_Screen = 0.0; 590 | screenIndex.topIndex = currentMenu->currentItemIndex; 591 | screenIndex.bottomIndex = screenIndex.topIndex + 3; /**< 调整底部索引 */ 592 | } 593 | 594 | screenTop.lastVal = screenTop.targetVal; 595 | screenTop.targetVal = screenIndex.topIndex * MENU_ITEM_HEIGHT; /**< 更新屏幕顶部目标值 */ 596 | } 597 | 598 | /** 599 | * @brief 更新滚动条位置。 600 | * 601 | * 根据当前菜单项索引,计算滚动条的目标位置,支持不同菜单项数量的情况。 602 | */ 603 | void ScrollBar_Update(void) 604 | { 605 | moveProcess_ScrollBar = 0.0; 606 | scrollBarY.lastVal = scrollBarY.val; 607 | 608 | float moveRangef = (float)((OLED_SCREEN_HEIGHT - 4) - (OLED_SCREEN_HEIGHT * 2) / currentMenu->itemCount) / (currentMenu->itemCount - 1); 609 | int moveRange = (int)(moveRangef + 0.5f); 610 | if (currentMenu->itemCount == 2 || currentMenu->itemCount == 1) 611 | { 612 | scrollBarY.targetVal = (currentMenu->currentItemIndex * 10) + 2; 613 | } 614 | else 615 | { 616 | if (currentMenu->currentItemIndex == currentMenu->itemCount - 1) 617 | { 618 | scrollBarY.targetVal = OLED_SCREEN_HEIGHT - 2 - (OLED_SCREEN_HEIGHT * 2) / currentMenu->itemCount; 619 | } 620 | else 621 | { 622 | scrollBarY.targetVal = (currentMenu->currentItemIndex * moveRange) + 2; 623 | } 624 | } 625 | } 626 | 627 | /** 628 | * @brief 更新开关控件条位置。 629 | * 630 | * 根据控件数据状态(0 或 1)调整开关控件条的目标位置。 631 | */ 632 | void switchCtrlBar_Update(void) 633 | { 634 | moveProcess_SwitchCtrlBar = 0.0; 635 | switchCtrlBar.lastVal = switchCtrlBar.val; 636 | 637 | if (*(currentMenu->items[currentMenu->currentItemIndex].control->data) == 0) 638 | { 639 | switchCtrlBar.targetVal = 64 + 2; /**< 开关关闭位置 */ 640 | } 641 | else 642 | { 643 | switchCtrlBar.targetVal = 64 - 30 + 2; /**< 开关开启位置 */ 644 | } 645 | } 646 | 647 | // /** 648 | // * @brief 更新显示控件条位置。 649 | // * 650 | // * 将控件数据值映射到显示控件条的位置,并限制值的范围为 0 到 100。 651 | // */ 652 | // void DisplayCtrlBar_Update(void) 653 | // { 654 | // displayCtrlBar.lastVal = displayCtrlBar.val; 655 | 656 | // float val = LIMIT_MAGNITUDE(*(currentMenu->items[currentMenu->currentItemIndex].control->data), 0, 100); 657 | // val = (val * 76) / 100; 658 | // int displayVal = (int)(val + 0.5f); 659 | 660 | // displayCtrlBar.targetVal = displayVal; /**< 显示控件条目标值 */ 661 | // } 662 | 663 | 664 | /** 665 | * @brief 更新 UI 元素的动画过渡效果。 666 | * 667 | * 该函数通过 `UI_SmoothTransition` 让多个 UI 组件(如框架、滚动条、开关控件等)平滑移动到目标位置,增强界面动画效果。 668 | * 669 | * @note 670 | * - 每个 UI 元素都有一个对应的移动进度变量 (`moveProcess_*`),用于控制过渡进度。 671 | * - `UI_SmoothTransition` 采用缓动算法,使动画更加自然。 672 | * - 该函数应在主循环中定期调用,以维持 UI 的平滑动画。 673 | */ 674 | void UI_Move(void) 675 | { 676 | UI_SmoothTransition(&frameY, &moveProcess_FrameY, 0.1); 677 | UI_SmoothTransition(&frameWidth, &moveProcess_FrameWidth, 0.1); 678 | UI_SmoothTransition(&screenTop, &moveProcess_Screen, 0.1); 679 | UI_SmoothTransition(&scrollBarY, &moveProcess_ScrollBar, 0.1); 680 | UI_SmoothTransition(&switchCtrlBar, &moveProcess_SwitchCtrlBar, 0.1); 681 | } 682 | 683 | 684 | /** 685 | * @brief 更新 UI 元素的移动状态。 686 | * 687 | * 根据当前进度和移动速度,逐步将 UI 元素的值从 `lastVal` 平滑过渡到 `targetVal`。 688 | * 689 | * @param elem 指向需要更新的 UI 元素。 690 | * @param moveProcess 移动进度指针(0.0 到 1.0)。 691 | * @param moveSpeed 移动速度,每次调用增加的进度量。 692 | * @return uint8_t 返回 1 表示移动完成,0 表示未完成。 693 | */ 694 | uint8_t UI_SmoothTransition(UIElemTypedef *elem, float *moveProcess, float moveSpeed) 695 | { 696 | if (elem->val == elem->targetVal) 697 | { 698 | return 1; 699 | } 700 | if (*moveProcess < 1.0) 701 | { 702 | *moveProcess += moveSpeed; 703 | } 704 | if (*moveProcess > 1.0) 705 | { 706 | *moveProcess = 1.0; 707 | } 708 | 709 | float easedProcess = easeInOut(*moveProcess); 710 | 711 | elem->val = (int16_t)((1.0 - easedProcess) * elem->lastVal + easedProcess * elem->targetVal); 712 | 713 | if (*moveProcess == 1.0) 714 | { 715 | elem->val = elem->targetVal; 716 | return 1; 717 | } 718 | else 719 | { 720 | return 0; 721 | } 722 | } 723 | 724 | /** 725 | * @brief 显示当前 UI。 726 | * 727 | * 根据菜单切换标志和控件选择标志,调用对应的绘制函数: 728 | * - `InterfaceSwitch`:处理菜单切换时的过渡效果。 729 | * - `DrawMenuItems`:绘制菜单项。 730 | * - `DrawControlSelection`:绘制控件选择界面。 731 | */ 732 | void UI_Show() 733 | { 734 | if (menuSwitchFlag == 1) 735 | { 736 | InterfaceSwitch(); 737 | } 738 | 739 | if (controlSelectionFlag == 0) 740 | { 741 | DrawMenuItems(); 742 | } 743 | else if (controlSelectionFlag == 1) 744 | { 745 | DrawControlSelection(); 746 | } 747 | } 748 | 749 | /** 750 | * @brief 菜单切换过渡效果。 751 | * 752 | * 显示菜单切换动画,通过三次快速消失与重绘实现。 753 | */ 754 | void InterfaceSwitch() 755 | { 756 | menuSwitchFlag = 0; 757 | for (uint8_t i = 0; i < 3; i++) 758 | { 759 | OLED_Disappear(); 760 | OLED_ShowFrame(); 761 | HAL_Delay(50); /**< 在 FreeRTOS 中使用非阻塞式延时 */ 762 | } 763 | } 764 | 765 | /** 766 | * @brief 绘制当前菜单项。 767 | * 768 | * 根据当前菜单项索引和屏幕显示范围,绘制菜单项名称和控件信息,防止数组越界。 769 | * - 调用 `DrawItemName` 绘制菜单项名称。 770 | * - 调用 `DrawControlInformation` 绘制控件状态。 771 | * - 调用 `DrawSelectionFrame` 和 `DrawScrollBar` 完成其他显示元素。 772 | */ 773 | void DrawMenuItems() 774 | { 775 | int16_t showItemEndNum = 0; 776 | int16_t showItemStartNum = 0; 777 | if (currentMenu->itemCount > 0) 778 | { 779 | showItemEndNum = (currentMenu->itemCount > 4) ? ((currentMenu->currentItemIndex) < currentMenu->itemCount - 4 ? 5 : 4) : currentMenu->itemCount; 780 | showItemStartNum = (currentMenu->currentItemIndex > 3) ? -1 : 0; 781 | } 782 | else 783 | { 784 | showItemEndNum = 0; 785 | } 786 | 787 | OLED_NewFrame(); 788 | 789 | for (int i = showItemStartNum; i < showItemEndNum; i++) 790 | { 791 | int itemIndex = screenIndex.topIndex + i; 792 | if (itemIndex < 0 || itemIndex >= currentMenu->itemCount) 793 | { 794 | continue; /**< 防止越界 */ 795 | } 796 | int yPos = screenTop.targetVal - screenTop.val + (i * MENU_ITEM_HEIGHT) + 4; 797 | 798 | DrawItemName(currentMenu->items[itemIndex].str, 2, yPos); 799 | DrawControlInformation(currentMenu->items[itemIndex].control, yPos); 800 | } 801 | 802 | DrawSelectionFrame(); 803 | DrawScrollBar(); 804 | 805 | OLED_ShowFrame(); 806 | } 807 | 808 | /** 809 | * @brief 绘制控件选择界面。 810 | * 811 | * 显示当前选中的控件及其状态: 812 | * - 根据控件模式调用对应的绘制函数,如 `DrawSwitchControl`、`DrawDisplayControl` 和 `DrawSliderControl`。 813 | * - 绘制控件的外框和名称。 814 | */ 815 | void DrawControlSelection() 816 | { 817 | OLED_DrawEmptyRectangle(16, 8, 96, 48); 818 | 819 | DrawItemName(currentMenu->items[currentMenu->currentItemIndex].str, 16 + 2, 8 + 2); 820 | 821 | switch (currentMenu->items[currentMenu->currentItemIndex].control->mode) 822 | { 823 | case SWITCH_CTRL: 824 | { 825 | DrawSwitchControl(currentMenu->items[currentMenu->currentItemIndex].control); 826 | break; 827 | } 828 | case DISPLAY_CTRL: 829 | { 830 | DrawDisplayControl(currentMenu->items[currentMenu->currentItemIndex].control); 831 | break; 832 | } 833 | case SLIDER_CTRL: 834 | { 835 | DrawSliderControl(currentMenu->items[currentMenu->currentItemIndex].control); 836 | break; 837 | } 838 | default: 839 | break; 840 | } 841 | 842 | OLED_ShowFrame(); 843 | } 844 | 845 | /** 846 | * @brief 绘制菜单项名称。 847 | * 848 | * 在指定位置显示菜单项的文本。 849 | * 850 | * @param str 指向菜单项名称的字符串。 851 | * @param xPos 绘制文本的 X 坐标。 852 | * @param yPos 绘制文本的 Y 坐标。 853 | */ 854 | void DrawItemName(char *str, int xPos, int yPos) 855 | { 856 | OLED_PrintASCIIString(xPos, yPos, str, &afont8x6, OLED_COLOR_NORMAL); 857 | } 858 | 859 | /** 860 | * @brief 绘制控件信息。 861 | * 862 | * 根据控件模式(开关、显示、滑条)显示控件的当前状态或值。 863 | * 864 | * @param control 指向控件信息的指针。 865 | * @param yPos 绘制控件信息的 Y 坐标。 866 | */ 867 | void DrawControlInformation(ControlTypedef *control, int yPos) 868 | { 869 | if (control) 870 | { 871 | switch (control->mode) 872 | { 873 | case SWITCH_CTRL: 874 | { 875 | // 绘制开关控件状态(ON 或 OFF) 876 | if (*(control->data) == 0) 877 | { 878 | OLED_PrintASCIIString(100, yPos, "OFF", &afont8x6, OLED_COLOR_NORMAL); 879 | } 880 | else 881 | { 882 | OLED_PrintASCIIString(100, yPos, "ON", &afont8x6, OLED_COLOR_NORMAL); 883 | } 884 | break; 885 | } 886 | 887 | case DISPLAY_CTRL: 888 | case SLIDER_CTRL: 889 | { 890 | // 绘制显示控件或滑条控件的数值 891 | char str[10]; 892 | snprintf(str, sizeof(str), "%d", *(control->data)); 893 | OLED_PrintASCIIString(100, yPos, str, &afont8x6, OLED_COLOR_NORMAL); 894 | break; 895 | } 896 | 897 | default: 898 | break; 899 | } 900 | } 901 | } 902 | 903 | /** 904 | * @brief 绘制当前选中项的高亮框。 905 | * 906 | * 根据当前选中项的位置绘制反色矩形,突出显示当前项。 907 | */ 908 | void DrawSelectionFrame() 909 | { 910 | OLED_DrawFilledRectangleWithCorners(0, frameY.val + 1 - (screenIndex.topIndex * MENU_ITEM_HEIGHT), frameWidth.val, MENU_ITEM_HEIGHT - 2, OLED_COLOR_REVERSED); 911 | } 912 | 913 | /** 914 | * @brief 绘制滚动条。 915 | * 916 | * 根据当前菜单项数量和选中项位置绘制滚动条,并调整滚动条高度以适配菜单。 917 | */ 918 | void DrawScrollBar() 919 | { 920 | OLED_DrawRectangle(OLED_SCREEN_WIDTH - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN - 2, 0, 6, OLED_SCREEN_HEIGHT - 1, OLED_COLOR_NORMAL); 921 | if (currentMenu->itemCount == 2 || currentMenu->itemCount == 1) 922 | { 923 | OLED_DrawFilledRectangle(OLED_SCREEN_WIDTH - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN, scrollBarY.val, SCROLLBAR_WIDTH, currentMenu->itemCount == 1 ? 60 : 50, OLED_COLOR_NORMAL); 924 | } 925 | else 926 | { 927 | OLED_DrawFilledRectangle(OLED_SCREEN_WIDTH - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN, scrollBarY.val, SCROLLBAR_WIDTH, (OLED_SCREEN_HEIGHT * 2) / currentMenu->itemCount, OLED_COLOR_NORMAL); 928 | } 929 | } 930 | 931 | /** 932 | * @brief 绘制开关控件的状态。 933 | * 934 | * 在控件区域显示 ON/OFF 的文本及其状态指示框。 935 | * 936 | * @param control 指向开关控件的结构体指针。 937 | */ 938 | void DrawSwitchControl(ControlTypedef *control) 939 | { 940 | OLED_DrawRectangleWithCorners(64 - 30, 28, 60, 20, OLED_COLOR_NORMAL); 941 | OLED_PrintASCIIString(64 - 20, 28 + 5, "ON", &afont12x6, OLED_COLOR_NORMAL); 942 | OLED_PrintASCIIString(64 + 7, 28 + 5, "OFF", &afont12x6, OLED_COLOR_NORMAL); 943 | OLED_DrawFilledRectangleWithCorners(switchCtrlBar.val, 28 + 2, 26, 17, OLED_COLOR_REVERSED); 944 | } 945 | 946 | /** 947 | * @brief 绘制显示控件的状态。 948 | * 949 | * 根据控件的值绘制进度条,并显示当前值的文本。 950 | * 951 | * @param control 指向显示控件的结构体指针。 952 | */ 953 | void DrawDisplayControl(ControlTypedef *control) 954 | { 955 | OLED_DrawRectangle(64 - 40, 40, 80, 10, OLED_COLOR_NORMAL); 956 | char str[10]; 957 | sprintf(str, "%d", *(control->data)); 958 | OLED_PrintASCIIString(64 - 6, 25, str, &afont12x6, OLED_COLOR_NORMAL); 959 | 960 | float val = LIMIT_MAGNITUDE(*(control->data), 0, 100); 961 | val = (val * 76) / 100; 962 | int displayVal = (int)(val + 0.5f); 963 | OLED_DrawFilledRectangle(64 - 40 + 2, 40 + 2, displayVal, 7, OLED_COLOR_NORMAL); 964 | } 965 | 966 | /** 967 | * @brief 绘制滑动条控件的状态。 968 | * 969 | * 根据控件的值绘制滑动条的进度,并显示当前值的文本。 970 | * 971 | * @param control 指向滑动条控件的结构体指针。 972 | */ 973 | void DrawSliderControl(ControlTypedef *control) 974 | { 975 | OLED_DrawRectangle(64 - 40, 40, 80, 10, OLED_COLOR_NORMAL); 976 | char str[10]; 977 | sprintf(str, "%d", *(control->data)); 978 | OLED_PrintASCIIString(64 - 6, 25, str, &afont12x6, OLED_COLOR_NORMAL); 979 | 980 | float val = LIMIT_MAGNITUDE(*(control->data), 0, 100); 981 | val = (val * 76) / 100; 982 | int displayVal = (int)(val + 0.5f); 983 | OLED_DrawFilledRectangle(64 - 40 + 2, 40 + 2, displayVal, 7, OLED_COLOR_NORMAL); 984 | } 985 | 986 | /** 987 | * @brief 缓动函数(Ease-In-Out)。 988 | * 989 | * 用于计算缓动效果的进度值,使移动过程更加平滑。 990 | * 991 | * @param t 输入的进度值,范围为 0.0 到 1.0。 992 | * @return float 返回缓动后的进度值。 993 | */ 994 | float easeInOut(float t) 995 | { 996 | if (t < 0.5) 997 | return 2 * t * t; 998 | else 999 | return -1 + (4 - 2 * t) * t; 1000 | } 1001 | 1002 | /** 1003 | * @brief 进入控件选择模式。 1004 | * 1005 | * 该函数用于切换到控件选择界面,允许用户对菜单项的控件进行操作。 1006 | * 1007 | * @note 1008 | * - `controlSelectionFlag = 1`:表示进入控件选择模式。 1009 | * - `menuSwitchFlag = 1`:触发菜单界面刷新,使 UI 适应新的模式。 1010 | */ 1011 | void FunctionForCtrl(void) 1012 | { 1013 | controlSelectionFlag = 1; 1014 | menuSwitchFlag = 1; 1015 | } 1016 | 1017 | /** 1018 | * @brief 进入当前选中菜单项的子菜单。 1019 | * 1020 | * 如果当前选中的菜单项包含子菜单,则切换 `currentMenu` 到对应的子菜单,并触发菜单切换标志。 1021 | * 1022 | * @note 1023 | * - `currentMenu` 会被更新为选中菜单项的 `subMenu`。 1024 | * - `menuSwitchFlag = 1` 触发 UI 重新绘制,以显示新的子菜单。 1025 | * - 如果当前菜单项没有子菜单,则不执行任何操作。 1026 | */ 1027 | void FunctionForNextMenu(void) 1028 | { 1029 | if (currentMenu->items[currentMenu->currentItemIndex].subMenu) 1030 | { 1031 | currentMenu = currentMenu->items[currentMenu->currentItemIndex].subMenu; 1032 | menuSwitchFlag = 1; 1033 | } 1034 | } 1035 | -------------------------------------------------------------------------------- /images/2025-02-28012933.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoraiYanKele/oled-multi-level-menu/f2b9fa6f95ee4f047a255be0d8711f14b0d953a9/images/2025-02-28012933.png -------------------------------------------------------------------------------- /images/2025-02-28013000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoraiYanKele/oled-multi-level-menu/f2b9fa6f95ee4f047a255be0d8711f14b0d953a9/images/2025-02-28013000.png -------------------------------------------------------------------------------- /images/2025-02-28013026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoraiYanKele/oled-multi-level-menu/f2b9fa6f95ee4f047a255be0d8711f14b0d953a9/images/2025-02-28013026.png -------------------------------------------------------------------------------- /images/2025-02-28013033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoraiYanKele/oled-multi-level-menu/f2b9fa6f95ee4f047a255be0d8711f14b0d953a9/images/2025-02-28013033.png -------------------------------------------------------------------------------- /images/2025-02-28013039.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoraiYanKele/oled-multi-level-menu/f2b9fa6f95ee4f047a255be0d8711f14b0d953a9/images/2025-02-28013039.png -------------------------------------------------------------------------------- /images/2025-02-28013059.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoraiYanKele/oled-multi-level-menu/f2b9fa6f95ee4f047a255be0d8711f14b0d953a9/images/2025-02-28013059.png --------------------------------------------------------------------------------