长安

长安

STM32F103C8T6+MPU6050制作微型无人机实践

118
2025-07-30
  • 项目背景:STM32F103C8T6+MPU6050制作微型无人机

  • 开发工具:STM32CubeIDE

  • 所需材料:

    • STM32F103C8T6最小系统板*1

    • MPU6050陀螺仪*1

    • TB6612电机驱动模块*2

    • 8520空心杯电机*4

    • SSD1306 0.96寸OLED*1

    • JDY-23蓝牙模块*1

  • 接线方式:

    • MPU6050

      • MPU6050.VCC -> STM32.3.3V

      • MPU6050.GND -> STM32.GND

      • MPU6050.SCL -> STM32.PB6(I2C1_SCL)

      • MPU6050.SDA -> STM32.PB7(I2C1_SDA)

    • SSD1306

      • SSD1306.GND -> STM32.GND

      • SSD1306.VCC -> STM32.3.3V

      • SSD1306.SCL-> STM32.PB10(I2C2_SCL)

      • SSD1306.SDA-> STM32.PB11(I2C2_SDA)

  • STM32CubeIDE配置:

    • TIM3

    • I2C1

    • I2C2

  • 项目代码:

    • Inc

      • font.h

        #ifndef __FONT_H
        #define __FONT_H
        #include "stdint.h"
        #include "string.h"
        typedef struct ASCIIFont {
          uint8_t h;
          uint8_t w;
          uint8_t *chars;
        } ASCIIFont;
        
        extern const ASCIIFont afont8x6;
        extern const ASCIIFont afont12x6;
        extern const ASCIIFont afont16x8;
        extern const ASCIIFont afont24x12;
        
        /**
         * @brief 字体结构体
         * @note  字库前4字节存储utf8编码 剩余字节存储字模数据
         * @note 字库数据可以使用波特律动LED取模助手生成(https://led.baud-dance.com)
         */
        typedef struct Font {
          uint8_t h;              // 字高度
          uint8_t w;              // 字宽度
          const uint8_t *chars;   // 字库 字库前4字节存储utf8编码 剩余字节存储字模数据
          uint8_t len;            // 字库长度 超过256则请改为uint16_t
          const ASCIIFont *ascii; // 缺省ASCII字体 当字库中没有对应字符且需要显示ASCII字符时使用
        } Font;
        
        extern const Font font16x16;
        
        /**
         * @brief 图片结构体
         * @note  图片数据可以使用波特律动LED取模助手生成(https://led.baud-dance.com)
         */
        typedef struct Image {
          uint8_t w;           // 图片宽度
          uint8_t h;           // 图片高度
          const uint8_t *data; // 图片数据
        } Image;
        
        extern const Image bilibiliImg;
        
        #endif // __FONT_H
        
      • oled.h

        #ifndef __OLED_H__
        #define __OLED_H__
        
        #include "font.h"
        #include "main.h"
        #include "string.h"
        
        typedef enum {
          OLED_COLOR_NORMAL = 0, // 正常模式 黑底白字
          OLED_COLOR_REVERSED    // 反色模式 白底黑字
        } OLED_ColorMode;
        
        void OLED_Init();
        void OLED_DisPlay_On();
        void OLED_DisPlay_Off();
        
        void OLED_NewFrame();
        void OLED_ShowFrame();
        void OLED_SetPixel(uint8_t x, uint8_t y, OLED_ColorMode color);
        
        void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, OLED_ColorMode color);
        void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color);
        void OLED_DrawFilledRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color);
        void OLED_DrawTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color);
        void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color);
        void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color);
        void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color);
        void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b, OLED_ColorMode color);
        void OLED_DrawImage(uint8_t x, uint8_t y, const Image *img, OLED_ColorMode color);
        
        void OLED_PrintASCIIChar(uint8_t x, uint8_t y, char ch, const ASCIIFont *font, OLED_ColorMode color);
        void OLED_PrintASCIIString(uint8_t x, uint8_t y, char *str, const ASCIIFont *font, OLED_ColorMode color);
        void OLED_PrintString(uint8_t x, uint8_t y, char *str, const Font *font, OLED_ColorMode color);
        
        #endif // __OLED_H__
      • mpu6050.h

        #include <stdint.h>
        
        #ifndef INC_MPU6050_H_
        #define INC_MPU6050_H_
        
        // MPU6050 I2C地址
        #define MPU6050_ADDR 0xD0  // (0x68 << 1)
        
        // 寄存器定义
        #define SMPLRT_DIV   0x19
        #define CONFIG       0x1A
        #define GYRO_CONFIG  0x1B
        #define ACCEL_CONFIG 0x1C
        #define PWR_MGMT_1   0x6B
        #define WHO_AM_I     0x75
        #define ACCEL_XOUT_H 0x3B
        #define TEMP_OUT_H   0x41
        #define GYRO_XOUT_H  0x43
        
        // 加速度计灵敏度 (g/LSB)
        #define AFS_SEL_0 16384.0
        #define AFS_SEL_1 8192.0
        #define AFS_SEL_2 4096.0
        #define AFS_SEL_3 2048.0
        
        // 陀螺仪灵敏度 (°/s/LSB)
        #define GFS_SEL_0 131.0
        #define GFS_SEL_1 65.5
        #define GFS_SEL_2 32.8
        #define GFS_SEL_3 16.4
        
        typedef struct {
            int16_t Accel_X_RAW;
            int16_t Accel_Y_RAW;
            int16_t Accel_Z_RAW;
            int16_t Gyro_X_RAW;
            int16_t Gyro_Y_RAW;
            int16_t Gyro_Z_RAW;
            int16_t Temperature_RAW;
        
            float Ax, Ay, Az;       // 加速度 (g)
            float Gx, Gy, Gz;       // 角速度 (°/s)
            float Temp;             // 温度 (°C)
        
            float Gyro_Offset_X;    // 陀螺仪X轴零偏
            float Gyro_Offset_Y;    // 陀螺仪Y轴零偏
            float Gyro_Offset_Z;    // 陀螺仪Z轴零偏
        
            // DMA缓冲区
            uint8_t dma_buffer[14];
        } MPU6050_t;
        
        // 函数声明
        uint8_t MPU6050_Init();
        void MPU6050_Read_All_DMA(MPU6050_t *DataStruct);
        void MPU6050_Process_Data(MPU6050_t *DataStruct);
        void MPU6050_Calibrate(MPU6050_t *DataStruct, uint16_t numSamples);
        
        
        #endif /* INC_MPU6050_H_ */
        
      • madgwick.h

        #ifndef INC_MADGWICK_H_
        #define INC_MADGWICK_H_
        
        typedef struct {
            float q0, q1, q2, q3;   // 四元数
            float beta;              // 滤波器增益
            float sampleFreq;        // 采样频率
        } MadgwickFilter;
        
        // 函数声明
        void Madgwick_Init(MadgwickFilter *filter, float beta, float sampleFreq);
        void Madgwick_Update(MadgwickFilter *filter,
                             float gx, float gy, float gz,
                             float ax, float ay, float az);
        void Get_Euler_Angles(MadgwickFilter *filter,
                              float *roll, float *pitch, float *yaw);
        
        #endif /* INC_MADGWICK_H_ */
        
    • Src

      • font.c

        /**
         * @file font.c
         * @brief 字体库
         *
         * @attention
         * 本字体库与波特律动OLED驱动配套使用
         * 英文字库已包含
         * 中文字库请使用波特律动LED取模工具生成(https://led.baud-dance.com)
         * 图模也使用波特律动LED取模工具生成
         */
        // clang-format off
        #include "font.h"
        
        // 8*6 ASCII
        const unsigned char ascii_8x6[][6] = {
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // space 空格
            {0x00, 0x00, 0x00, 0x2f, 0x00, 0x00}, // !
            {0x00, 0x00, 0x07, 0x00, 0x07, 0x00}, // "
            {0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14}, // #
            {0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12}, // $
            {0x00, 0x62, 0x64, 0x08, 0x13, 0x23}, // %
            {0x00, 0x36, 0x49, 0x55, 0x22, 0x50}, // &
            {0x00, 0x00, 0x05, 0x03, 0x00, 0x00}, // '
            {0x00, 0x00, 0x1c, 0x22, 0x41, 0x00}, // (
            {0x00, 0x00, 0x41, 0x22, 0x1c, 0x00}, // )
            {0x00, 0x14, 0x08, 0x3E, 0x08, 0x14}, // *
            {0x00, 0x08, 0x08, 0x3E, 0x08, 0x08}, // +
            {0x00, 0x00, 0x00, 0xA0, 0x60, 0x00}, // ,
            {0x00, 0x08, 0x08, 0x08, 0x08, 0x08}, // -
            {0x00, 0x00, 0x60, 0x60, 0x00, 0x00}, // .
            {0x00, 0x20, 0x10, 0x08, 0x04, 0x02}, // /
            {0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0
            {0x00, 0x00, 0x42, 0x7F, 0x40, 0x00}, // 1
            {0x00, 0x42, 0x61, 0x51, 0x49, 0x46}, // 2
            {0x00, 0x21, 0x41, 0x45, 0x4B, 0x31}, // 3
            {0x00, 0x18, 0x14, 0x12, 0x7F, 0x10}, // 4
            {0x00, 0x27, 0x45, 0x45, 0x45, 0x39}, // 5
            {0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6
            {0x00, 0x01, 0x71, 0x09, 0x05, 0x03}, // 7
            {0x00, 0x36, 0x49, 0x49, 0x49, 0x36}, // 8
            {0x00, 0x06, 0x49, 0x49, 0x29, 0x1E}, // 9
            {0x00, 0x00, 0x36, 0x36, 0x00, 0x00}, // :
            {0x00, 0x00, 0x56, 0x36, 0x00, 0x00}, // ;
            {0x00, 0x08, 0x14, 0x22, 0x41, 0x00}, // <
            {0x00, 0x14, 0x14, 0x14, 0x14, 0x14}, // =
            {0x00, 0x00, 0x41, 0x22, 0x14, 0x08}, // >
            {0x00, 0x02, 0x01, 0x51, 0x09, 0x06}, // ?
            {0x00, 0x32, 0x49, 0x59, 0x51, 0x3E}, // @
            {0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C}, // A
            {0x00, 0x7F, 0x49, 0x49, 0x49, 0x36}, // B
            {0x00, 0x3E, 0x41, 0x41, 0x41, 0x22}, // C
            {0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C}, // D
            {0x00, 0x7F, 0x49, 0x49, 0x49, 0x41}, // E
            {0x00, 0x7F, 0x09, 0x09, 0x09, 0x01}, // F
            {0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A}, // G
            {0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F}, // H
            {0x00, 0x00, 0x41, 0x7F, 0x41, 0x00}, // I
            {0x00, 0x20, 0x40, 0x41, 0x3F, 0x01}, // J
            {0x00, 0x7F, 0x08, 0x14, 0x22, 0x41}, // K
            {0x00, 0x7F, 0x40, 0x40, 0x40, 0x40}, // L
            {0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F}, // M
            {0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F}, // N
            {0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E}, // O
            {0x00, 0x7F, 0x09, 0x09, 0x09, 0x06}, // P
            {0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q
            {0x00, 0x7F, 0x09, 0x19, 0x29, 0x46}, // R
            {0x00, 0x46, 0x49, 0x49, 0x49, 0x31}, // S
            {0x00, 0x01, 0x01, 0x7F, 0x01, 0x01}, // T
            {0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F}, // U
            {0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F}, // V
            {0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F}, // W
            {0x00, 0x63, 0x14, 0x08, 0x14, 0x63}, // X
            {0x00, 0x07, 0x08, 0x70, 0x08, 0x07}, // Y
            {0x00, 0x61, 0x51, 0x49, 0x45, 0x43}, // Z
            {0x00, 0x00, 0x7F, 0x41, 0x41, 0x00}, // [
            {0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55}, // 55
            {0x00, 0x00, 0x41, 0x41, 0x7F, 0x00}, // ]
            {0x00, 0x04, 0x02, 0x01, 0x02, 0x04}, // ^
            {0x00, 0x40, 0x40, 0x40, 0x40, 0x40}, // _
            {0x00, 0x00, 0x01, 0x02, 0x04, 0x00}, // '
            {0x00, 0x20, 0x54, 0x54, 0x54, 0x78}, // a
            {0x00, 0x7F, 0x48, 0x44, 0x44, 0x38}, // b
            {0x00, 0x38, 0x44, 0x44, 0x44, 0x20}, // c
            {0x00, 0x38, 0x44, 0x44, 0x48, 0x7F}, // d
            {0x00, 0x38, 0x54, 0x54, 0x54, 0x18}, // e
            {0x00, 0x08, 0x7E, 0x09, 0x01, 0x02}, // f
            {0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C}, // g
            {0x00, 0x7F, 0x08, 0x04, 0x04, 0x78}, // h
            {0x00, 0x00, 0x44, 0x7D, 0x40, 0x00}, // i
            {0x00, 0x40, 0x80, 0x84, 0x7D, 0x00}, // j
            {0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // k
            {0x00, 0x00, 0x41, 0x7F, 0x40, 0x00}, // l
            {0x00, 0x7C, 0x04, 0x18, 0x04, 0x78}, // m
            {0x00, 0x7C, 0x08, 0x04, 0x04, 0x78}, // n
            {0x00, 0x38, 0x44, 0x44, 0x44, 0x38}, // o
            {0x00, 0xFC, 0x24, 0x24, 0x24, 0x18}, // p
            {0x00, 0x18, 0x24, 0x24, 0x18, 0xFC}, // q
            {0x00, 0x7C, 0x08, 0x04, 0x04, 0x08}, // r
            {0x00, 0x48, 0x54, 0x54, 0x54, 0x20}, // s
            {0x00, 0x04, 0x3F, 0x44, 0x40, 0x20}, // t
            {0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C}, // u
            {0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C}, // v
            {0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C}, // w
            {0x00, 0x44, 0x28, 0x10, 0x28, 0x44}, // x
            {0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C}, // y
            {0x00, 0x44, 0x64, 0x54, 0x4C, 0x44}, // z
            {0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // horiz lines
        };
        
        const ASCIIFont afont8x6 = {8, 6, (unsigned char *)ascii_8x6};
        
        const unsigned char ascii_12x6[][12] = {
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*" ",0*/
            {0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}, /*"!",1*/
            {0x00, 0x0C, 0x02, 0x0C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*""",2*/
            {0x90, 0xD0, 0xBC, 0xD0, 0xBC, 0x90, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00}, /*"#",3*/
            {0x18, 0x24, 0xFE, 0x44, 0x8C, 0x00, 0x03, 0x02, 0x07, 0x02, 0x01, 0x00}, /*"$",4*/
            {0x18, 0x24, 0xD8, 0xB0, 0x4C, 0x80, 0x00, 0x03, 0x00, 0x01, 0x02, 0x01}, /*"%",5*/
            {0xC0, 0x38, 0xE4, 0x38, 0xE0, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02}, /*"&",6*/
            {0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"'",7*/
            {0x00, 0x00, 0x00, 0xF8, 0x04, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04}, /*"(",8*/
            {0x00, 0x02, 0x04, 0xF8, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00}, /*")",9*/
            {0x90, 0x60, 0xF8, 0x60, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, /*"*",10*/
            {0x20, 0x20, 0xFC, 0x20, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, /*"+",11*/
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00}, /*",",12*/
            {0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"-",13*/
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00}, /*".",14*/
            {0x00, 0x80, 0x60, 0x1C, 0x02, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00}, /*"/",15*/
            {0xF8, 0x04, 0x04, 0x04, 0xF8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"0",16*/
            {0x00, 0x08, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00}, /*"1",17*/
            {0x18, 0x84, 0x44, 0x24, 0x18, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x00}, /*"2",18*/
            {0x08, 0x04, 0x24, 0x24, 0xD8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"3",19*/
            {0x40, 0xB0, 0x88, 0xFC, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00}, /*"4",20*/
            {0x3C, 0x24, 0x24, 0x24, 0xC4, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"5",21*/
            {0xF8, 0x24, 0x24, 0x2C, 0xC0, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"6",22*/
            {0x0C, 0x04, 0xE4, 0x1C, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00}, /*"7",23*/
            {0xD8, 0x24, 0x24, 0x24, 0xD8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"8",24*/
            {0x38, 0x44, 0x44, 0x44, 0xF8, 0x00, 0x00, 0x03, 0x02, 0x02, 0x01, 0x00}, /*"9",25*/
            {0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}, /*":",26*/
            {0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00}, /*";",27*/
            {0x00, 0x20, 0x50, 0x88, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}, /*"<",28*/
            {0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"=",29*/
            {0x00, 0x02, 0x04, 0x88, 0x50, 0x20, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}, /*">",30*/
            {0x18, 0x04, 0xC4, 0x24, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}, /*"?",31*/
            {0xF8, 0x04, 0xE4, 0x94, 0xF8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00}, /*"@",32*/
            {0x00, 0xE0, 0x9C, 0xF0, 0x80, 0x00, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02}, /*"A",33*/
            {0x04, 0xFC, 0x24, 0x24, 0xD8, 0x00, 0x02, 0x03, 0x02, 0x02, 0x01, 0x00}, /*"B",34*/
            {0xF8, 0x04, 0x04, 0x04, 0x0C, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"C",35*/
            {0x04, 0xFC, 0x04, 0x04, 0xF8, 0x00, 0x02, 0x03, 0x02, 0x02, 0x01, 0x00}, /*"D",36*/
            {0x04, 0xFC, 0x24, 0x74, 0x0C, 0x00, 0x02, 0x03, 0x02, 0x02, 0x03, 0x00}, /*"E",37*/
            {0x04, 0xFC, 0x24, 0x74, 0x0C, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00}, /*"F",38*/
            {0xF0, 0x08, 0x04, 0x44, 0xCC, 0x40, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00}, /*"G",39*/
            {0x04, 0xFC, 0x20, 0x20, 0xFC, 0x04, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02}, /*"H",40*/
            {0x04, 0x04, 0xFC, 0x04, 0x04, 0x00, 0x02, 0x02, 0x03, 0x02, 0x02, 0x00}, /*"I",41*/
            {0x00, 0x04, 0x04, 0xFC, 0x04, 0x04, 0x06, 0x04, 0x04, 0x03, 0x00, 0x00}, /*"J",42*/
            {0x04, 0xFC, 0x24, 0xD0, 0x0C, 0x04, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"K",43*/
            {0x04, 0xFC, 0x04, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x02, 0x02, 0x03}, /*"L",44*/
            {0xFC, 0x3C, 0xC0, 0x3C, 0xFC, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00}, /*"M",45*/
            {0x04, 0xFC, 0x30, 0xC4, 0xFC, 0x04, 0x02, 0x03, 0x02, 0x00, 0x03, 0x00}, /*"N",46*/
            {0xF8, 0x04, 0x04, 0x04, 0xF8, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"O",47*/
            {0x04, 0xFC, 0x24, 0x24, 0x18, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00}, /*"P",48*/
            {0xF8, 0x84, 0x84, 0x04, 0xF8, 0x00, 0x01, 0x02, 0x02, 0x07, 0x05, 0x00}, /*"Q",49*/
            {0x04, 0xFC, 0x24, 0x64, 0x98, 0x00, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"R",50*/
            {0x18, 0x24, 0x24, 0x44, 0x8C, 0x00, 0x03, 0x02, 0x02, 0x02, 0x01, 0x00}, /*"S",51*/
            {0x0C, 0x04, 0xFC, 0x04, 0x0C, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00}, /*"T",52*/
            {0x04, 0xFC, 0x00, 0x00, 0xFC, 0x04, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00}, /*"U",53*/
            {0x04, 0x7C, 0x80, 0xE0, 0x1C, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00}, /*"V",54*/
            {0x1C, 0xE0, 0x3C, 0xE0, 0x1C, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00}, /*"W",55*/
            {0x04, 0x9C, 0x60, 0x9C, 0x04, 0x00, 0x02, 0x03, 0x00, 0x03, 0x02, 0x00}, /*"X",56*/
            {0x04, 0x1C, 0xE0, 0x1C, 0x04, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00}, /*"Y",57*/
            {0x0C, 0x84, 0x64, 0x1C, 0x04, 0x00, 0x02, 0x03, 0x02, 0x02, 0x03, 0x00}, /*"Z",58*/
            {0x00, 0x00, 0xFE, 0x02, 0x02, 0x00, 0x00, 0x00, 0x07, 0x04, 0x04, 0x00}, /*"[",59*/
            {0x00, 0x0E, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00}, /*"\",60*/
            {0x00, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x04, 0x04, 0x07, 0x00, 0x00}, /*"]",61*/
            {0x00, 0x04, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"^",62*/
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, /*"_",63*/
            {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"`",64*/
            {0x00, 0x40, 0xA0, 0xA0, 0xC0, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x02}, /*"a",65*/
            {0x04, 0xFC, 0x20, 0x20, 0xC0, 0x00, 0x00, 0x03, 0x02, 0x02, 0x01, 0x00}, /*"b",66*/
            {0x00, 0xC0, 0x20, 0x20, 0x60, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00}, /*"c",67*/
            {0x00, 0xC0, 0x20, 0x24, 0xFC, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x02}, /*"d",68*/
            {0x00, 0xC0, 0xA0, 0xA0, 0xC0, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00}, /*"e",69*/
            {0x00, 0x20, 0xF8, 0x24, 0x24, 0x04, 0x00, 0x02, 0x03, 0x02, 0x02, 0x00}, /*"f",70*/
            {0x00, 0x40, 0xA0, 0xA0, 0x60, 0x20, 0x00, 0x07, 0x0A, 0x0A, 0x0A, 0x04}, /*"g",71*/
            {0x04, 0xFC, 0x20, 0x20, 0xC0, 0x00, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"h",72*/
            {0x00, 0x20, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00}, /*"i",73*/
            {0x00, 0x00, 0x20, 0xE4, 0x00, 0x00, 0x08, 0x08, 0x08, 0x07, 0x00, 0x00}, /*"j",74*/
            {0x04, 0xFC, 0x80, 0xE0, 0x20, 0x20, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"k",75*/
            {0x04, 0x04, 0xFC, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x02, 0x02, 0x00}, /*"l",76*/
            {0xE0, 0x20, 0xE0, 0x20, 0xC0, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00}, /*"m",77*/
            {0x20, 0xE0, 0x20, 0x20, 0xC0, 0x00, 0x02, 0x03, 0x02, 0x00, 0x03, 0x02}, /*"n",78*/
            {0x00, 0xC0, 0x20, 0x20, 0xC0, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00}, /*"o",79*/
            {0x20, 0xE0, 0x20, 0x20, 0xC0, 0x00, 0x08, 0x0F, 0x0A, 0x02, 0x01, 0x00}, /*"p",80*/
            {0x00, 0xC0, 0x20, 0x20, 0xE0, 0x00, 0x00, 0x01, 0x02, 0x0A, 0x0F, 0x08}, /*"q",81*/
            {0x20, 0xE0, 0x40, 0x20, 0x20, 0x00, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00}, /*"r",82*/
            {0x00, 0x60, 0xA0, 0xA0, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x03, 0x00}, /*"s",83*/
            {0x00, 0x20, 0xF8, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00}, /*"t",84*/
            {0x20, 0xE0, 0x00, 0x20, 0xE0, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x02}, /*"u",85*/
            {0x20, 0xE0, 0x20, 0x80, 0x60, 0x20, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00}, /*"v",86*/
            {0x60, 0x80, 0xE0, 0x80, 0x60, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00}, /*"w",87*/
            {0x20, 0x60, 0x80, 0x60, 0x20, 0x00, 0x02, 0x03, 0x00, 0x03, 0x02, 0x00}, /*"x",88*/
            {0x20, 0xE0, 0x20, 0x80, 0x60, 0x20, 0x08, 0x08, 0x07, 0x01, 0x00, 0x00}, /*"y",89*/
            {0x00, 0x20, 0xA0, 0x60, 0x20, 0x00, 0x00, 0x02, 0x03, 0x02, 0x02, 0x00}, /*"z",90*/
            {0x00, 0x00, 0x20, 0xDE, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x00}, /*"{",91*/
            {0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00}, /*"|",92*/
            {0x00, 0x02, 0xDE, 0x20, 0x00, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00, 0x00}, /*"}",93*/
            {0x02, 0x01, 0x02, 0x04, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"~",94*/
        };
        
        const ASCIIFont afont12x6 = {12, 6, (unsigned char *)ascii_12x6};
        
        const unsigned char ascii_16x8[][16] = {
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*" ",0*/
            {0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00}, /*"!",1*/
            {0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*""",2*/
            {0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00, 0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00}, /*"#",3*/
            {0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00, 0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00}, /*"$",4*/
            {0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00}, /*"%",5*/
            {0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10}, /*"&",6*/
            {0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"'",7*/
            {0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00}, /*"(",8*/
            {0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00}, /*")",9*/
            {0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00, 0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00}, /*"*",10*/
            {0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00}, /*"+",11*/
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00}, /*",",12*/
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, /*"-",13*/
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00}, /*".",14*/
            {0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04, 0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00}, /*"/",15*/
            {0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00}, /*"0",16*/
            {0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"1",17*/
            {0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00}, /*"2",18*/
            {0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00, 0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"3",19*/
            {0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00}, /*"4",20*/
            {0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00, 0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"5",21*/
            {0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"6",22*/
            {0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, /*"7",23*/
            {0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00}, /*"8",24*/
            {0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00}, /*"9",25*/
            {0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00}, /*":",26*/
            {0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00}, /*";",27*/
            {0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00}, /*"<",28*/
            {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00}, /*"=",29*/
            {0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00}, /*">",30*/
            {0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00}, /*"?",31*/
            {0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00, 0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00}, /*"@",32*/
            {0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20}, /*"A",33*/
            {0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"B",34*/
            {0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00}, /*"C",35*/
            {0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00}, /*"D",36*/
            {0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00}, /*"E",37*/
            {0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00}, /*"F",38*/
            {0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00}, /*"G",39*/
            {0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20}, /*"H",40*/
            {0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"I",41*/
            {0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00}, /*"J",42*/
            {0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00, 0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00}, /*"K",43*/
            {0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00}, /*"L",44*/
            {0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00, 0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00}, /*"M",45*/
            {0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00}, /*"N",46*/
            {0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00}, /*"O",47*/
            {0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00}, /*"P",48*/
            {0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00}, /*"Q",49*/
            {0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20}, /*"R",50*/
            {0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00}, /*"S",51*/
            {0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00}, /*"T",52*/
            {0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00}, /*"U",53*/
            {0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00}, /*"V",54*/
            {0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00, 0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00}, /*"W",55*/
            {0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08, 0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20}, /*"X",56*/
            {0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00}, /*"Y",57*/
            {0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00}, /*"Z",58*/
            {0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00}, /*"[",59*/
            {0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00}, /*"\",60*/
            {0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00}, /*"]",61*/
            {0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"^",62*/
            {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, /*"_",63*/
            {0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"`",64*/
            {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20}, /*"a",65*/
            {0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"b",66*/
            {0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00}, /*"c",67*/
            {0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20}, /*"d",68*/
            {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00}, /*"e",69*/
            {0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"f",70*/
            {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00}, /*"g",71*/
            {0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20}, /*"h",72*/
            {0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"i",73*/
            {0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00}, /*"j",74*/
            {0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00}, /*"k",75*/
            {0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00}, /*"l",76*/
            {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F}, /*"m",77*/
            {0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20}, /*"n",78*/
            {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00}, /*"o",79*/
            {0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00}, /*"p",80*/
            {0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80}, /*"q",81*/
            {0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00}, /*"r",82*/
            {0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00}, /*"s",83*/
            {0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00}, /*"t",84*/
            {0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20}, /*"u",85*/
            {0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00}, /*"v",86*/
            {0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, 0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00}, /*"w",87*/
            {0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00}, /*"x",88*/
            {0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00}, /*"y",89*/
            {0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00}, /*"z",90*/
            {0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40}, /*"{",91*/
            {0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00}, /*"|",92*/
            {0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00}, /*"}",93*/
            {0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"~",94*/
        };
        
        const ASCIIFont afont16x8 = {16, 8, (unsigned char *)ascii_16x8};
        
        const unsigned char ascii_24x12[][36] = {
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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*/
            {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},
            /*"~",94*/                                                                                                                                                                                                                /*"~",94*/
        };
        
        const ASCIIFont afont24x12 = {24, 12, (unsigned char *)ascii_24x12};
        
        const uint8_t zh16x16[][36] = {
        /* 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,},
        /* 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,},
        /* 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,},
        /* 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,}
        };
        const Font font16x16 = {16, 16, (const uint8_t *)zh16x16, 4, &afont16x8};
        
        const uint8_t bilibiliData[] = {
        0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x86, 0x8f, 0x9f, 0xbf, 0xff, 0xfc, 0xf8, 0xf8, 0xe0, 0xe0, 0xc0, 0x80,
        0x80, 0x80, 0x80, 0x80, 0xc0, 0xe0, 0xe0, 0xf8, 0xf8, 0xfc, 0xfe, 0xbf, 0x9f, 0x8f, 0x86, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xf8, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
        0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
        0xff, 0xff, 0xff, 0xfe, 0xfc, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf8, 0xf8, 0xf8,
        0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0,
        0x20, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xc0, 0xc0, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
        0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x07, 0x07, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
        0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
        0x1f, 0x1f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x07, 0x07, 0x03,
        };
        const Image bilibiliImg = {51, 48, bilibiliData};
        
      • oled.c

        /**
         * @file oled.c
         * @brief 波特律动OLED驱动(SSD1306)
         * @anchor 波特律动(keysking 博哥在学习)
         * @version 1.0
         * @date 2023-08-19
         * @license MIT License
         *
         * @attention
         * 本驱动库针对波特律动·keysking的STM32教程学习套件进行开发
         * 在其他平台或驱动芯片上使用可能需要进行移植
         *
         * @note
         * 使用流程:
         * 1. STM32初始化IIC完成后调用OLED_Init()初始化OLED. 注意STM32启动比OLED上电快, 可等待20ms再初始化OLED
         * 2. 调用OLED_NewFrame()开始绘制新的一帧
         * 3. 调用OLED_DrawXXX()系列函数绘制图形到显存 调用OLED_Printxxx()系列函数绘制文本到显存
         * 4. 调用OLED_ShowFrame()将显存内容显示到OLED
         *
         * @note
         * 为保证中文显示正常 请将编译器的字符集设置为UTF-8
         *
         */
        #include "oled.h"
        #include "i2c.h"
        #include <math.h>
        #include <stdlib.h>
        
        // OLED器件地址
        #define OLED_ADDRESS 0x78
        
        // OLED参数
        #define OLED_PAGE 8            // OLED页数
        #define OLED_ROW 8 * OLED_PAGE // OLED行数
        #define OLED_COLUMN 128        // OLED列数
        
        // 显存
        uint8_t OLED_GRAM[OLED_PAGE][OLED_COLUMN];
        
        // ========================== 底层通信函数 ==========================
        
        /**
         * @brief 向OLED发送数据的函数
         * @param data 要发送的数据
         * @param len 要发送的数据长度
         * @return None
         * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他平台时应根据实际情况修改此函数
         */
        void OLED_Send(uint8_t *data, uint8_t len)
        {
          HAL_I2C_Master_Transmit(&hi2c2, OLED_ADDRESS, data, len, HAL_MAX_DELAY);
        }
        
        /**
         * @brief 向OLED发送指令
         */
        void OLED_SendCmd(uint8_t cmd)
        {
          static uint8_t sendBuffer[2] = {0};
          sendBuffer[1] = cmd;
          OLED_Send(sendBuffer, 2);
        }
        
        // ========================== OLED驱动函数 ==========================
        
        /**
         * @brief 初始化OLED (SSD1306)
         * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他驱动芯片时应根据实际情况修改此函数
         */
        void OLED_Init()
        {
          OLED_SendCmd(0xAE); /*关闭显示 display off*/
        
          OLED_SendCmd(0x20);
          OLED_SendCmd(0x10);
        
          OLED_SendCmd(0xB0);
        
          OLED_SendCmd(0xC8);
        
          OLED_SendCmd(0x00);
          OLED_SendCmd(0x10);
        
          OLED_SendCmd(0x40);
        
          OLED_SendCmd(0x81);
        
          OLED_SendCmd(0xDF);
          OLED_SendCmd(0xA1);
        
          OLED_SendCmd(0xA6);
          OLED_SendCmd(0xA8);
        
          OLED_SendCmd(0x3F);
        
          OLED_SendCmd(0xA4);
        
          OLED_SendCmd(0xD3);
          OLED_SendCmd(0x00);
        
          OLED_SendCmd(0xD5);
          OLED_SendCmd(0xF0);
        
          OLED_SendCmd(0xD9);
          OLED_SendCmd(0x22);
        
          OLED_SendCmd(0xDA);
          OLED_SendCmd(0x12);
        
          OLED_SendCmd(0xDB);
          OLED_SendCmd(0x20);
        
          OLED_SendCmd(0x8D);
          OLED_SendCmd(0x14);
        
          OLED_NewFrame();
          OLED_ShowFrame();
        
          OLED_SendCmd(0xAF); /*开启显示 display ON*/
        }
        
        /**
         * @brief 开启OLED显示
         */
        void OLED_DisPlay_On()
        {
          OLED_SendCmd(0x8D); // 电荷泵使能
          OLED_SendCmd(0x14); // 开启电荷泵
          OLED_SendCmd(0xAF); // 点亮屏幕
        }
        
        /**
         * @brief 关闭OLED显示
         */
        void OLED_DisPlay_Off()
        {
          OLED_SendCmd(0x8D); // 电荷泵使能
          OLED_SendCmd(0x10); // 关闭电荷泵
          OLED_SendCmd(0xAE); // 关闭屏幕
        }
        
        /**
         * @brief 设置颜色模式 黑底白字或白底黑字
         * @param ColorMode 颜色模式COLOR_NORMAL/COLOR_REVERSED
         * @note 此函数直接设置屏幕的颜色模式
         */
        void OLED_SetColorMode(OLED_ColorMode mode)
        {
          if (mode == OLED_COLOR_NORMAL)
          {
            OLED_SendCmd(0xA6); // 正常显示
          }
          if (mode == OLED_COLOR_REVERSED)
          {
            OLED_SendCmd(0xA7); // 反色显示
          }
        }
        
        // ========================== 显存操作函数 ==========================
        
        /**
         * @brief 清空显存 绘制新的一帧
         */
        void OLED_NewFrame()
        {
          memset(OLED_GRAM, 0, sizeof(OLED_GRAM));
        }
        
        /**
         * @brief 将当前显存显示到屏幕上
         * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他驱动芯片时应根据实际情况修改此函数
         */
        void OLED_ShowFrame()
        {
          static uint8_t sendBuffer[OLED_COLUMN + 1];
          sendBuffer[0] = 0x40;
          for (uint8_t i = 0; i < OLED_PAGE; i++)
          {
            OLED_SendCmd(0xB0 + i); // 设置页地址
            OLED_SendCmd(0x00);     // 设置列地址低4位
            OLED_SendCmd(0x10);     // 设置列地址高4位
            memcpy(sendBuffer + 1, OLED_GRAM[i], OLED_COLUMN);
            OLED_Send(sendBuffer, OLED_COLUMN + 1);
          }
        }
        
        /**
         * @brief 设置一个像素点
         * @param x 横坐标
         * @param y 纵坐标
         * @param color 颜色
         */
        void OLED_SetPixel(uint8_t x, uint8_t y, OLED_ColorMode color)
        {
          if (x >= OLED_COLUMN || y >= OLED_ROW)
            return;
          if (!color)
          {
            OLED_GRAM[y / 8][x] |= 1 << (y % 8);
          }
          else
          {
            OLED_GRAM[y / 8][x] &= ~(1 << (y % 8));
          }
        }
        
        /**
         * @brief 设置显存中一字节数据的某几位
         * @param page 页地址
         * @param column 列地址
         * @param data 数据
         * @param start 起始位
         * @param end 结束位
         * @param color 颜色
         * @note 此函数将显存中的某一字节的第start位到第end位设置为与data相同
         * @note start和end的范围为0-7, start必须小于等于end
         * @note 此函数与OLED_SetByte_Fine的区别在于此函数只能设置显存中的某一真实字节
         */
        void OLED_SetByte_Fine(uint8_t page, uint8_t column, uint8_t data, uint8_t start, uint8_t end, OLED_ColorMode color)
        {
          static uint8_t temp;
          if (page >= OLED_PAGE || column >= OLED_COLUMN)
            return;
          if (color)
            data = ~data;
        
          temp = data | (0xff << (end + 1)) | (0xff >> (8 - start));
          OLED_GRAM[page][column] &= temp;
          temp = data & ~(0xff << (end + 1)) & ~(0xff >> (8 - start));
          OLED_GRAM[page][column] |= temp;
          // 使用OLED_SetPixel实现
          // for (uint8_t i = start; i <= end; i++) {
          //   OLED_SetPixel(column, page * 8 + i, !((data >> i) & 0x01));
          // }
        }
        
        /**
         * @brief 设置显存中的一字节数据
         * @param page 页地址
         * @param column 列地址
         * @param data 数据
         * @param color 颜色
         * @note 此函数将显存中的某一字节设置为data的值
         */
        void OLED_SetByte(uint8_t page, uint8_t column, uint8_t data, OLED_ColorMode color)
        {
          if (page >= OLED_PAGE || column >= OLED_COLUMN)
            return;
          if (color)
            data = ~data;
          OLED_GRAM[page][column] = data;
        }
        
        /**
         * @brief 设置显存中的一字节数据的某几位
         * @param x 横坐标
         * @param y 纵坐标
         * @param data 数据
         * @param len 位数
         * @param color 颜色
         * @note 此函数将显存中从(x,y)开始向下数len位设置为与data相同
         * @note len的范围为1-8
         * @note 此函数与OLED_SetByte_Fine的区别在于此函数的横坐标和纵坐标是以像素为单位的, 可能出现跨两个真实字节的情况(跨页)
         */
        void OLED_SetBits_Fine(uint8_t x, uint8_t y, uint8_t data, uint8_t len, OLED_ColorMode color)
        {
          uint8_t page = y / 8;
          uint8_t bit = y % 8;
          if (bit + len > 8)
          {
            OLED_SetByte_Fine(page, x, data << bit, bit, 7, color);
            OLED_SetByte_Fine(page + 1, x, data >> (8 - bit), 0, len + bit - 1 - 8, color);
          }
          else
          {
            OLED_SetByte_Fine(page, x, data << bit, bit, bit + len - 1, color);
          }
          // 使用OLED_SetPixel实现
          // for (uint8_t i = 0; i < len; i++) {
          //   OLED_SetPixel(x, y + i, !((data >> i) & 0x01));
          // }
        }
        
        /**
         * @brief 设置显存中一字节长度的数据
         * @param x 横坐标
         * @param y 纵坐标
         * @param data 数据
         * @param color 颜色
         * @note 此函数将显存中从(x,y)开始向下数8位设置为与data相同
         * @note 此函数与OLED_SetByte的区别在于此函数的横坐标和纵坐标是以像素为单位的, 可能出现跨两个真实字节的情况(跨页)
         */
        void OLED_SetBits(uint8_t x, uint8_t y, uint8_t data, OLED_ColorMode color)
        {
          uint8_t page = y / 8;
          uint8_t bit = y % 8;
          OLED_SetByte_Fine(page, x, data << bit, bit, 7, color);
          if (bit)
          {
            OLED_SetByte_Fine(page + 1, x, data >> (8 - bit), 0, bit - 1, color);
          }
        }
        
        /**
         * @brief 设置一块显存区域
         * @param x 起始横坐标
         * @param y 起始纵坐标
         * @param data 数据的起始地址
         * @param w 宽度
         * @param h 高度
         * @param color 颜色
         * @note 此函数将显存中从(x,y)开始的w*h个像素设置为data中的数据
         * @note data的数据应该采用列行式排列
         */
        void OLED_SetBlock(uint8_t x, uint8_t y, const uint8_t *data, uint8_t w, uint8_t h, OLED_ColorMode color)
        {
          uint8_t fullRow = h / 8; // 完整的行数
          uint8_t partBit = h % 8; // 不完整的字节中的有效位数
          for (uint8_t i = 0; i < w; i++)
          {
            for (uint8_t j = 0; j < fullRow; j++)
            {
              OLED_SetBits(x + i, y + j * 8, data[i + j * w], color);
            }
          }
          if (partBit)
          {
            uint16_t fullNum = w * fullRow; // 完整的字节数
            for (uint8_t i = 0; i < w; i++)
            {
              OLED_SetBits_Fine(x + i, y + (fullRow * 8), data[fullNum + i], partBit, color);
            }
          }
          // 使用OLED_SetPixel实现
          // for (uint8_t i = 0; i < w; i++) {
          //   for (uint8_t j = 0; j < h; j++) {
          //     for (uint8_t k = 0; k < 8; k++) {
          //       if (j * 8 + k >= h) break; // 防止越界(不完整的字节
          //       OLED_SetPixel(x + i, y + j * 8 + k, !((data[i + j * w] >> k) & 0x01));
          //     }
          //   }
          // }
        }
        
        // ========================== 图形绘制函数 ==========================
        /**
         * @brief 绘制一条线段
         * @param x1 起始点横坐标
         * @param y1 起始点纵坐标
         * @param x2 终止点横坐标
         * @param y2 终止点纵坐标
         * @param color 颜色
         * @note 此函数使用Bresenham算法绘制线段
         */
        void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, OLED_ColorMode color)
        {
          static uint8_t temp = 0;
          if (x1 == x2)
          {
            if (y1 > y2)
            {
              temp = y1;
              y1 = y2;
              y2 = temp;
            }
            for (uint8_t y = y1; y <= y2; y++)
            {
              OLED_SetPixel(x1, y, color);
            }
          }
          else if (y1 == y2)
          {
            if (x1 > x2)
            {
              temp = x1;
              x1 = x2;
              x2 = temp;
            }
            for (uint8_t x = x1; x <= x2; x++)
            {
              OLED_SetPixel(x, y1, color);
            }
          }
          else
          {
            // Bresenham直线算法
            int16_t dx = x2 - x1;
            int16_t dy = y2 - y1;
            int16_t ux = ((dx > 0) << 1) - 1;
            int16_t uy = ((dy > 0) << 1) - 1;
            int16_t x = x1, y = y1, eps = 0;
            dx = abs(dx);
            dy = abs(dy);
            if (dx > dy)
            {
              for (x = x1; x != x2; x += ux)
              {
                OLED_SetPixel(x, y, color);
                eps += dy;
                if ((eps << 1) >= dx)
                {
                  y += uy;
                  eps -= dx;
                }
              }
            }
            else
            {
              for (y = y1; y != y2; y += uy)
              {
                OLED_SetPixel(x, y, color);
                eps += dx;
                if ((eps << 1) >= dy)
                {
                  x += ux;
                  eps -= dy;
                }
              }
            }
          }
        }
        
        /**
         * @brief 绘制一个矩形
         * @param x 起始点横坐标
         * @param y 起始点纵坐标
         * @param w 矩形宽度
         * @param h 矩形高度
         * @param color 颜色
         */
        void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color)
        {
          OLED_DrawLine(x, y, x + w, y, color);
          OLED_DrawLine(x, y + h, x + w, y + h, color);
          OLED_DrawLine(x, y, x, y + h, color);
          OLED_DrawLine(x + w, y, x + w, y + h, color);
        }
        
        /**
         * @brief 绘制一个填充矩形
         * @param x 起始点横坐标
         * @param y 起始点纵坐标
         * @param w 矩形宽度
         * @param h 矩形高度
         * @param color 颜色
         */
        void OLED_DrawFilledRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color)
        {
          for (uint8_t i = 0; i < h; i++)
          {
            OLED_DrawLine(x, y + i, x + w, y + i, color);
          }
        }
        
        /**
         * @brief 绘制一个三角形
         * @param x1 第一个点横坐标
         * @param y1 第一个点纵坐标
         * @param x2 第二个点横坐标
         * @param y2 第二个点纵坐标
         * @param x3 第三个点横坐标
         * @param y3 第三个点纵坐标
         * @param color 颜色
         */
        void OLED_DrawTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color)
        {
          OLED_DrawLine(x1, y1, x2, y2, color);
          OLED_DrawLine(x2, y2, x3, y3, color);
          OLED_DrawLine(x3, y3, x1, y1, color);
        }
        
        /**
         * @brief 绘制一个填充三角形
         * @param x1 第一个点横坐标
         * @param y1 第一个点纵坐标
         * @param x2 第二个点横坐标
         * @param y2 第二个点纵坐标
         * @param x3 第三个点横坐标
         * @param y3 第三个点纵坐标
         * @param color 颜色
         */
        void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color)
        {
          uint8_t a = 0, b = 0, y = 0, last = 0;
          if (y1 > y2)
          {
            a = y2;
            b = y1;
          }
          else
          {
            a = y1;
            b = y2;
          }
          y = a;
          for (; y <= b; y++)
          {
            if (y <= y3)
            {
              OLED_DrawLine(x1 + (y - y1) * (x2 - x1) / (y2 - y1), y, x1 + (y - y1) * (x3 - x1) / (y3 - y1), y, color);
            }
            else
            {
              last = y - 1;
              break;
            }
          }
          for (; y <= b; y++)
          {
            OLED_DrawLine(x2 + (y - y2) * (x3 - x2) / (y3 - y2), y, x1 + (y - last) * (x3 - x1) / (y3 - last), y, color);
          }
        }
        
        /**
         * @brief 绘制一个圆
         * @param x 圆心横坐标
         * @param y 圆心纵坐标
         * @param r 圆半径
         * @param color 颜色
         * @note 此函数使用Bresenham算法绘制圆
         */
        void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color)
        {
          int16_t a = 0, b = r, di = 3 - (r << 1);
          while (a <= b)
          {
            OLED_SetPixel(x - b, y - a, color);
            OLED_SetPixel(x + b, y - a, color);
            OLED_SetPixel(x - a, y + b, color);
            OLED_SetPixel(x - b, y - a, color);
            OLED_SetPixel(x - a, y - b, color);
            OLED_SetPixel(x + b, y + a, color);
            OLED_SetPixel(x + a, y - b, color);
            OLED_SetPixel(x + a, y + b, color);
            OLED_SetPixel(x - b, y + a, color);
            a++;
            if (di < 0)
            {
              di += 4 * a + 6;
            }
            else
            {
              di += 10 + 4 * (a - b);
              b--;
            }
            OLED_SetPixel(x + a, y + b, color);
          }
        }
        
        /**
         * @brief 绘制一个填充圆
         * @param x 圆心横坐标
         * @param y 圆心纵坐标
         * @param r 圆半径
         * @param color 颜色
         * @note 此函数使用Bresenham算法绘制圆
         */
        void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color)
        {
          int16_t a = 0, b = r, di = 3 - (r << 1);
          while (a <= b)
          {
            for (int16_t i = x - b; i <= x + b; i++)
            {
              OLED_SetPixel(i, y + a, color);
              OLED_SetPixel(i, y - a, color);
            }
            for (int16_t i = x - a; i <= x + a; i++)
            {
              OLED_SetPixel(i, y + b, color);
              OLED_SetPixel(i, y - b, color);
            }
            a++;
            if (di < 0)
            {
              di += 4 * a + 6;
            }
            else
            {
              di += 10 + 4 * (a - b);
              b--;
            }
          }
        }
        
        /**
         * @brief 绘制一个椭圆
         * @param x 椭圆中心横坐标
         * @param y 椭圆中心纵坐标
         * @param a 椭圆长轴
         * @param b 椭圆短轴
         */
        void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b, OLED_ColorMode color)
        {
          int xpos = 0, ypos = b;
          int a2 = a * a, b2 = b * b;
          int d = b2 + a2 * (0.25 - b);
          while (a2 * ypos > b2 * xpos)
          {
            OLED_SetPixel(x + xpos, y + ypos, color);
            OLED_SetPixel(x - xpos, y + ypos, color);
            OLED_SetPixel(x + xpos, y - ypos, color);
            OLED_SetPixel(x - xpos, y - ypos, color);
            if (d < 0)
            {
              d = d + b2 * ((xpos << 1) + 3);
              xpos += 1;
            }
            else
            {
              d = d + b2 * ((xpos << 1) + 3) + a2 * (-(ypos << 1) + 2);
              xpos += 1, ypos -= 1;
            }
          }
          d = b2 * (xpos + 0.5) * (xpos + 0.5) + a2 * (ypos - 1) * (ypos - 1) - a2 * b2;
          while (ypos > 0)
          {
            OLED_SetPixel(x + xpos, y + ypos, color);
            OLED_SetPixel(x - xpos, y + ypos, color);
            OLED_SetPixel(x + xpos, y - ypos, color);
            OLED_SetPixel(x - xpos, y - ypos, color);
            if (d < 0)
            {
              d = d + b2 * ((xpos << 1) + 2) + a2 * (-(ypos << 1) + 3);
              xpos += 1, ypos -= 1;
            }
            else
            {
              d = d + a2 * (-(ypos << 1) + 3);
              ypos -= 1;
            }
          }
        }
        
        /**
         * @brief 绘制一张图片
         * @param x 起始点横坐标
         * @param y 起始点纵坐标
         * @param img 图片
         * @param color 颜色
         */
        void OLED_DrawImage(uint8_t x, uint8_t y, const Image *img, OLED_ColorMode color)
        {
          OLED_SetBlock(x, y, img->data, img->w, img->h, color);
        }
        
        // ================================ 文字绘制 ================================
        
        /**
         * @brief 绘制一个ASCII字符
         * @param x 起始点横坐标
         * @param y 起始点纵坐标
         * @param ch 字符
         * @param font 字体
         * @param color 颜色
         */
        void OLED_PrintASCIIChar(uint8_t x, uint8_t y, char ch, const ASCIIFont *font, OLED_ColorMode color)
        {
          OLED_SetBlock(x, y, font->chars + (ch - ' ') * (((font->h + 7) / 8) * font->w), font->w, font->h, color);
        }
        
        /**
         * @brief 绘制一个ASCII字符串
         * @param x 起始点横坐标
         * @param y 起始点纵坐标
         * @param str 字符串
         * @param font 字体
         * @param color 颜色
         */
        void OLED_PrintASCIIString(uint8_t x, uint8_t y, char *str, const ASCIIFont *font, OLED_ColorMode color)
        {
          uint8_t x0 = x;
          while (*str)
          {
            OLED_PrintASCIIChar(x0, y, *str, font, color);
            x0 += font->w;
            str++;
          }
        }
        
        /**
         * @brief 获取UTF-8编码的字符长度
         */
        uint8_t _OLED_GetUTF8Len(char *string)
        {
          if ((string[0] & 0x80) == 0x00)
          {
            return 1;
          }
          else if ((string[0] & 0xE0) == 0xC0)
          {
            return 2;
          }
          else if ((string[0] & 0xF0) == 0xE0)
          {
            return 3;
          }
          else if ((string[0] & 0xF8) == 0xF0)
          {
            return 4;
          }
          return 0;
        }
        
        /**
         * @brief 绘制字符串
         * @param x 起始点横坐标
         * @param y 起始点纵坐标
         * @param str 字符串
         * @param font 字体
         * @param color 颜色
         *
         * @note 为保证字符串中的中文会被自动识别并绘制, 需:
         * 1. 编译器字符集设置为UTF-8
         * 2. 使用波特律动LED取模工具生成字模(https://led.baud-dance.com)
         */
        /**
         * @brief 绘制字符串
         * @param x 起始点横坐标
         * @param y 起始点纵坐标
         * @param str 字符串
         * @param font 字体
         * @param color 颜色
         *
         * @note 为保证字符串中的中文会被自动识别并绘制, 需:
         * 1. 编译器字符集设置为UTF-8
         * 2. 使用波特律动LED取模工具生成字模(https://led.baud-dance.com)
         */
        void OLED_PrintString(uint8_t x, uint8_t y, char *str, const Font *font, OLED_ColorMode color)
        {
          uint16_t i = 0;                                       // 字符串索引
          uint8_t oneLen = (((font->h + 7) / 8) * font->w) + 4; // 一个字模占多少字节
          uint8_t found;                                        // 是否找到字模
          uint8_t utf8Len;                                      // UTF-8编码长度
          uint8_t *head;                                        // 字模头指针
          while (str[i])
          {
            found = 0;
            utf8Len = _OLED_GetUTF8Len(str + i);
            if (utf8Len == 0)
              break; // 有问题的UTF-8编码
        
            // 寻找字符  TODO 优化查找算法, 二分查找或者hash
            for (uint8_t j = 0; j < font->len; j++)
            {
              head = (uint8_t *)(font->chars) + (j * oneLen);
              if (memcmp(str + i, head, utf8Len) == 0)
              {
                OLED_SetBlock(x, y, head + 4, font->w, font->h, color);
                // 移动光标
                x += font->w;
                i += utf8Len;
                found = 1;
                break;
              }
            }
        
            // 若未找到字模,且为ASCII字符, 则缺省显示ASCII字符
            if (found == 0)
            {
              if (utf8Len == 1)
              {
                OLED_PrintASCIIChar(x, y, str[i], font->ascii, color);
                // 移动光标
                x += font->ascii->w;
                i += utf8Len;
              }
              else
              {
                OLED_PrintASCIIChar(x, y, ' ', font->ascii, color);
                x += font->ascii->w;
                i += utf8Len;
              }
            }
          }
        }
        
      • mpu6050.c

        #include "i2c.h"
        #include "mpu6050.h"
        #include "oled.h"
        #include <math.h>
        
        // MPU6050初始化
        uint8_t MPU6050_Init() {
            uint8_t check;
            uint8_t Data;
        
            // 检测设备ID
            HAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, WHO_AM_I, 1, &check, 1, 100);
            if (check != 0x68) return 1; // 设备ID错误
        
            // 唤醒设备
            Data = 0x00;
            HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, PWR_MGMT_1, 1, &Data, 1, 100);
        
            // 设置采样率 = 1kHz / (1+7) = 125Hz
            Data = 0x07;
            HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, SMPLRT_DIV, 1, &Data, 1, 100);
        
            // 设置DLPF带宽为94Hz
            Data = 0x02;
            HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, CONFIG, 1, &Data, 1, 100);
        
            // 设置加速度计量程 ±4g
            Data = 0x08;
            HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, ACCEL_CONFIG, 1, &Data, 1, 100);
        
            // 设置陀螺仪量程 ±500°/s
            Data = 0x08;
            HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, GYRO_CONFIG, 1, &Data, 1, 100);
        
            return 0;
        }
        
        // 启动DMA传输读取MPU6050数据
        void MPU6050_Read_All_DMA(MPU6050_t *DataStruct) {
            // 启动I2C读取操作(DMA模式)
            HAL_I2C_Mem_Read_DMA(&hi2c1, MPU6050_ADDR, ACCEL_XOUT_H, 1, DataStruct->dma_buffer, 14);
        }
        
        // 处理DMA接收到的数据
        void MPU6050_Process_Data(MPU6050_t *DataStruct) {
            // 解析原始数据
            DataStruct->Accel_X_RAW = (int16_t)(DataStruct->dma_buffer[0] << 8 | DataStruct->dma_buffer[1]);
            DataStruct->Accel_Y_RAW = (int16_t)(DataStruct->dma_buffer[2] << 8 | DataStruct->dma_buffer[3]);
            DataStruct->Accel_Z_RAW = (int16_t)(DataStruct->dma_buffer[4] << 8 | DataStruct->dma_buffer[5]);
            DataStruct->Temperature_RAW = (int16_t)(DataStruct->dma_buffer[6] << 8 | DataStruct->dma_buffer[7]);
            DataStruct->Gyro_X_RAW = (int16_t)(DataStruct->dma_buffer[8] << 8 | DataStruct->dma_buffer[9]);
            DataStruct->Gyro_Y_RAW = (int16_t)(DataStruct->dma_buffer[10] << 8 | DataStruct->dma_buffer[11]);
            DataStruct->Gyro_Z_RAW = (int16_t)(DataStruct->dma_buffer[12] << 8 | DataStruct->dma_buffer[13]);
        
            // 转换为物理量 (使用±4g量程)
            DataStruct->Ax = DataStruct->Accel_X_RAW / AFS_SEL_1;
            DataStruct->Ay = DataStruct->Accel_Y_RAW / AFS_SEL_1;
            DataStruct->Az = DataStruct->Accel_Z_RAW / AFS_SEL_1;
        
            // 转换为物理量 (使用±500°/s量程)
            DataStruct->Gx = DataStruct->Gyro_X_RAW / GFS_SEL_1;
            DataStruct->Gy = DataStruct->Gyro_Y_RAW / GFS_SEL_1;
            DataStruct->Gz = DataStruct->Gyro_Z_RAW / GFS_SEL_1;
        
            // 应用零偏校准
            DataStruct->Gx -= DataStruct->Gyro_Offset_X;
            DataStruct->Gy -= DataStruct->Gyro_Offset_Y;
            DataStruct->Gz -= DataStruct->Gyro_Offset_Z;
        
            // 温度转换 (°C)
            DataStruct->Temp = (float)DataStruct->Temperature_RAW / 340.0 + 36.53;
        }
        
        // 陀螺仪校准
        void MPU6050_Calibrate(MPU6050_t *DataStruct, uint16_t numSamples) {
        	OLED_NewFrame();
        	OLED_PrintASCIIString(0, 26, "Calibrating...", &afont8x6, OLED_COLOR_NORMAL);
        	OLED_ShowFrame();
        	// 禁用所有中断
        	__disable_irq();
        
        	float sumX = 0, sumY = 0, sumZ = 0;
        	uint8_t Rec_Data[14]; // 用于轮询读取的缓冲区
        
        	// 使用轮询方式安全读取
        	for (uint16_t i = 0; i < numSamples; i++) {
        		// 阻塞式读取
        		HAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, ACCEL_XOUT_H, 1, Rec_Data, 14, 100);
        
        		// 解析原始数据
        		DataStruct->Gyro_X_RAW = (int16_t)(Rec_Data[8] << 8 | Rec_Data[9]);
        		DataStruct->Gyro_Y_RAW = (int16_t)(Rec_Data[10] << 8 | Rec_Data[11]);
        		DataStruct->Gyro_Z_RAW = (int16_t)(Rec_Data[12] << 8 | Rec_Data[13]);
        
        		// 转换为物理量 (使用±500°/s量程)
        		float gx = DataStruct->Gyro_X_RAW / GFS_SEL_1;
        		float gy = DataStruct->Gyro_Y_RAW / GFS_SEL_1;
        		float gz = DataStruct->Gyro_Z_RAW / GFS_SEL_1;
        
        		sumX += gx;
        		sumY += gy;
        		sumZ += gz;
        
        		HAL_Delay(5);
        	}
        
        	// 计算平均值作为零偏
        	DataStruct->Gyro_Offset_X = sumX / numSamples;
        	DataStruct->Gyro_Offset_Y = sumY / numSamples;
        	DataStruct->Gyro_Offset_Z = sumZ / numSamples;
        
        	// 重新启用中断
        	__enable_irq();
        }
        
      • madgwick.c

        /*
         * madgwick.c
         *
         *  Created on: Jul 30, 2025
         *      Author: yingzhuang.feng
         */
        
        
        #include "madgwick.h"
        #include <math.h>
        
        // 初始化滤波器
        void Madgwick_Init(MadgwickFilter *filter, float beta, float sampleFreq) {
            filter->q0 = 1.0f;
            filter->q1 = 0.0f;
            filter->q2 = 0.0f;
            filter->q3 = 0.0f;
            filter->beta = beta;
            filter->sampleFreq = sampleFreq;
        }
        
        // Madgwick滤波更新
        void Madgwick_Update(MadgwickFilter *filter,
                             float gx, float gy, float gz,
                             float ax, float ay, float az) {
            float q0 = filter->q0, q1 = filter->q1, q2 = filter->q2, q3 = filter->q3;
            float recipNorm;
            float s0, s1, s2, s3;
            float qDot1, qDot2, qDot3, qDot4;
            float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2, q3q3;
        
            // 速率转弧度/秒
            gx *= 0.0174533f; // deg/s to rad/s
            gy *= 0.0174533f;
            gz *= 0.0174533f;
        
            // 梯度下降算法校正
            if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
                // 归一化加速度计数据
                recipNorm = 1.0f / sqrtf(ax * ax + ay * ay + az * az);
                ax *= recipNorm;
                ay *= recipNorm;
                az *= recipNorm;
        
                // 辅助变量避免重复计算
                _2q0 = 2.0f * q0;
                _2q1 = 2.0f * q1;
                _2q2 = 2.0f * q2;
                _2q3 = 2.0f * q3;
                _4q0 = 4.0f * q0;
                _4q1 = 4.0f * q1;
                _4q2 = 4.0f * q2;
                _8q1 = 8.0f * q1;
                _8q2 = 8.0f * q2;
                q0q0 = q0 * q0;
                q1q1 = q1 * q1;
                q2q2 = q2 * q2;
                q3q3 = q3 * q3;
        
                // 梯度下降目标函数
                s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
                s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
                s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
                s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay;
        
                // 归一化步长
                recipNorm = 1.0f / sqrtf(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3);
                s0 *= recipNorm;
                s1 *= recipNorm;
                s2 *= recipNorm;
                s3 *= recipNorm;
        
                // 应用反馈
                qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz) - filter->beta * s0;
                qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy) - filter->beta * s1;
                qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx) - filter->beta * s2;
                qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx) - filter->beta * s3;
            } else {
                // 无加速度计数据时仅用陀螺仪
                qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
                qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
                qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
                qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);
            }
        
            // 积分四元数
            q0 += qDot1 * (1.0f / filter->sampleFreq);
            q1 += qDot2 * (1.0f / filter->sampleFreq);
            q2 += qDot3 * (1.0f / filter->sampleFreq);
            q3 += qDot4 * (1.0f / filter->sampleFreq);
        
            // 归一化四元数
            recipNorm = 1.0f / sqrtf(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
            q0 *= recipNorm;
            q1 *= recipNorm;
            q2 *= recipNorm;
            q3 *= recipNorm;
        
            // 存储更新后的四元数
            filter->q0 = q0;
            filter->q1 = q1;
            filter->q2 = q2;
            filter->q3 = q3;
        }
        
        // 获取欧拉角 (单位: 度)
        void Get_Euler_Angles(MadgwickFilter *filter,
                              float *roll, float *pitch, float *yaw) {
            float q0 = filter->q0, q1 = filter->q1, q2 = filter->q2, q3 = filter->q3;
        
            *roll = atan2f(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2) * 57.29578f; // rad to deg
            *pitch = asinf(-2.0f * (q1*q3 - q0*q2)) * 57.29578f;
            *yaw = atan2f(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3) * 57.29578f;
        }
        
      • main.c

        /* USER CODE BEGIN Header */
        /**
          ******************************************************************************
          * @file           : main.c
          * @brief          : Main program body
          ******************************************************************************
          * @attention
          *
          * Copyright (c) 2025 STMicroelectronics.
          * All rights reserved.
          *
          * This software is licensed under terms that can be found in the LICENSE file
          * in the root directory of this software component.
          * If no LICENSE file comes with this software, it is provided AS-IS.
          *
          ******************************************************************************
          */
        /* USER CODE END Header */
        /* Includes ------------------------------------------------------------------*/
        #include "main.h"
        #include "dma.h"
        #include "i2c.h"
        #include "tim.h"
        #include "gpio.h"
        
        /* Private includes ----------------------------------------------------------*/
        /* USER CODE BEGIN Includes */
        #include "stdio.h"
        #include "oled.h"
        #include "mpu6050.h"
        #include "madgwick.h"
        /* USER CODE END Includes */
        
        /* Private typedef -----------------------------------------------------------*/
        /* USER CODE BEGIN PTD */
        
        /* USER CODE END PTD */
        
        /* Private define ------------------------------------------------------------*/
        /* USER CODE BEGIN PD */
        
        /* USER CODE END PD */
        
        /* Private macro -------------------------------------------------------------*/
        /* USER CODE BEGIN PM */
        
        /* USER CODE END PM */
        
        /* Private variables ---------------------------------------------------------*/
        
        /* USER CODE BEGIN PV */
        MPU6050_t mpu6050;
        MadgwickFilter filter;
        char displayBuffer[40]; // 显示缓冲区
        volatile uint8_t data_status = 0; // 数据就绪标志
        volatile uint8_t calibration_status = 0; // 校准状态标志
        uint32_t display_timer = 0;     // 显示更新计时器
        uint32_t last_sample_time = 0;
        /* USER CODE END PV */
        
        /* Private function prototypes -----------------------------------------------*/
        void SystemClock_Config(void);
        /* USER CODE BEGIN PFP */
        
        /* USER CODE END PFP */
        
        /* Private user code ---------------------------------------------------------*/
        /* USER CODE BEGIN 0 */
        // TIM3中断处理
        void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
            if (htim->Instance == TIM3 && calibration_status) {
                // 启动DMA读取
                MPU6050_Read_All_DMA(&mpu6050);
            }
        }
        
        // DMA传输完成回调
        void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) {
            if (hi2c->Instance == I2C1 && calibration_status) {
                data_status = 1;
            }
        }
        
        // I2C错误处理
        void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) {
            if (hi2c->Instance == I2C1 && calibration_status) {
                // 错误计数
                static uint8_t error_count = 0;
                error_count++;
        
                if(error_count > 5) {
                    // 严重错误 - 重启I2C
                    HAL_I2C_DeInit(&hi2c1);
                    MX_I2C1_Init();
                    error_count = 0;
                }
        
                // 重新尝试读取
                MPU6050_Read_All_DMA(&mpu6050);
            }
        }
        /* USER CODE END 0 */
        
        /**
          * @brief  The application entry point.
          * @retval int
          */
        int main(void)
        {
        
          /* USER CODE BEGIN 1 */
        
          /* USER CODE END 1 */
        
          /* MCU Configuration--------------------------------------------------------*/
        
          /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
          HAL_Init();
        
          /* USER CODE BEGIN Init */
        
          /* USER CODE END Init */
        
          /* Configure the system clock */
          SystemClock_Config();
        
          /* USER CODE BEGIN SysInit */
        
          /* USER CODE END SysInit */
        
          /* Initialize all configured peripherals */
          MX_GPIO_Init();
          MX_DMA_Init();
          MX_I2C1_Init();
          MX_I2C2_Init();
          MX_TIM3_Init();
          /* USER CODE BEGIN 2 */
          HAL_Delay(20);
          OLED_Init();
        
          // 初始化MPU6050
          if (MPU6050_Init()){
        	  while(1) {
        		  OLED_NewFrame();
        		  OLED_PrintASCIIString(0, 1, "MPU6050 Error!", &afont8x6, OLED_COLOR_NORMAL);
        		  OLED_ShowFrame();
        	  }
          }
        
          // 上电自动校准陀螺仪
          MPU6050_Calibrate(&mpu6050, 500); // 采集500个样本
        
          // 初始化Madgwick滤波器
          Madgwick_Init(&filter, 0.1f, 125.0f);
        
          // 标记校准完成
          calibration_status = 1;
        
          // 启动定时器
          HAL_TIM_Base_Start_IT(&htim3);
        
          float roll, pitch, yaw;
          /* USER CODE END 2 */
        
          /* Infinite loop */
          /* USER CODE BEGIN WHILE */
          while (1)
          {
        	  // 检查数据是否就绪
        	  if (data_status) {
        		  data_status = 0; // 清除标志
        
        		  // 计算采样间隔 (用于滤波器)
        		  uint32_t current_time = HAL_GetTick();
        		  float dt = (current_time - last_sample_time) / 1000.0f;
        		  last_sample_time = current_time;
        
        		  // 更新采样频率
        		  if(dt > 0) filter.sampleFreq = 1.0f / dt;
        
        		  // 处理数据
        		  MPU6050_Process_Data(&mpu6050);
        
        		  // 更新姿态
        		  Madgwick_Update(&filter,
        						  mpu6050.Gx, mpu6050.Gy, mpu6050.Gz,
        						  mpu6050.Ax, mpu6050.Ay, mpu6050.Az);
        	  }
        
        	  // 20Hz更新显示
        	  if (HAL_GetTick() - display_timer >= 50) {
        		  display_timer = HAL_GetTick();
        
        		  // 获取欧拉角
        		  Get_Euler_Angles(&filter, &roll, &pitch, &yaw);
        
        		  // 在OLED上显示姿态和温度
        		  OLED_NewFrame();
        		  snprintf(displayBuffer, sizeof(displayBuffer), "T:%2.1fC",mpu6050.Temp);
        		  OLED_PrintASCIIString(0, 1, displayBuffer, &afont8x6, OLED_COLOR_NORMAL);
        		  snprintf(displayBuffer, sizeof(displayBuffer), "R:%4.1f", roll);
        		  OLED_PrintASCIIString(0, 11, displayBuffer, &afont8x6, OLED_COLOR_NORMAL);
        		  snprintf(displayBuffer, sizeof(displayBuffer), "P:%4.1f", pitch);
        		  OLED_PrintASCIIString(0, 21, displayBuffer, &afont8x6, OLED_COLOR_NORMAL);
        		  snprintf(displayBuffer, sizeof(displayBuffer), "Y:%4.1f", yaw);
        		  OLED_PrintASCIIString(0, 31, displayBuffer, &afont8x6, OLED_COLOR_NORMAL);
        		  OLED_ShowFrame();
        	  }
        
        	  // 低功耗等待
        	  __WFI();
            /* USER CODE END WHILE */
        
            /* USER CODE BEGIN 3 */
          }
          /* USER CODE END 3 */
        }
        
        /**
          * @brief System Clock Configuration
          * @retval None
          */
        void SystemClock_Config(void)
        {
          RCC_OscInitTypeDef RCC_OscInitStruct = {0};
          RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
        
          /** Initializes the RCC Oscillators according to the specified parameters
          * in the RCC_OscInitTypeDef structure.
          */
          RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
          RCC_OscInitStruct.HSEState = RCC_HSE_ON;
          RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
          RCC_OscInitStruct.HSIState = RCC_HSI_ON;
          RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
          RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
          RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
          if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
          {
            Error_Handler();
          }
        
          /** Initializes the CPU, AHB and APB buses clocks
          */
          RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                      |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
          RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
          RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
          RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
          RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
        
          if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
          {
            Error_Handler();
          }
        }
        
        /* USER CODE BEGIN 4 */
        
        /* USER CODE END 4 */
        
        /**
          * @brief  This function is executed in case of error occurrence.
          * @retval None
          */
        void Error_Handler(void)
        {
          /* USER CODE BEGIN Error_Handler_Debug */
          /* User can add his own implementation to report the HAL error return state */
          __disable_irq();
          while (1)
          {
          }
          /* USER CODE END Error_Handler_Debug */
        }
        #ifdef USE_FULL_ASSERT
        /**
          * @brief  Reports the name of the source file and the source line number
          *         where the assert_param error has occurred.
          * @param  file: pointer to the source file name
          * @param  line: assert_param error line source number
          * @retval None
          */
        void assert_failed(uint8_t *file, uint32_t line)
        {
          /* USER CODE BEGIN 6 */
          /* User can add his own implementation to report the file name and line number,
             ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
          /* USER CODE END 6 */
        }
        #endif /* USE_FULL_ASSERT */