找回密码
 立即注册
查看: 253|回复: 11

滑动图标问题。

[复制链接]
已绑定手机

8

主题

7

回帖

118

积分

注册会员

积分
118
发表于 2026-10-12 16:10:08 | 显示全部楼层 |阅读模式
  1. /* ============================================================
  2. * 浦晓明滑动菜单移植版 —— 统一布局 + 提速
  3. * 图标 x=4/48/92,滑动 44 列,滑完不重画,只更新文字
  4. * 滑动一次移 11 列,只刷前 4 页
  5. * 4 个菜单项共用同一个 32x32 实心方块图标
  6. * 4 针 I2C,按键 P3.5 左移 / P5.4 右移 / P3.4 清屏
  7. * 主频 36.864MHz
  8. * ============================================================ */
  9. #include "STC32G.h"
  10. #include <string.h>
  11. #include <stdlib.h>
  12. typedef unsigned char u8;
  13. typedef unsigned int  u16;
  14. typedef unsigned long u32;
  15. #define MAIN_Fosc 36864000L
  16. #define MENU_NUM  4
  17. /* ==================== 按键定义 ==================== */
  18. sbit KEY_LEFT  = P3^5;
  19. sbit KEY_RIGHT = P5^4;
  20. sbit KEY_EXIT  = P3^4;
  21. /* ==================== 显存缓冲(双页) ==================== */
  22. u8 xdata ShowBuf[8][256];
  23. /* ==================== 6x8 ASCII 字库 ==================== */
  24. u8 code ASC8[][6] = {
  25.     0x00,0x00,0x00,0x00,0x00,0x00,
  26.     0x00,0x00,0x00,0x2F,0x00,0x00,
  27.     0x00,0x00,0x07,0x00,0x07,0x00,
  28.     0x00,0x14,0x7F,0x14,0x7F,0x14,
  29.     0x00,0x24,0x2A,0x7F,0x2A,0x12,
  30.     0x00,0x62,0x64,0x08,0x13,0x23,
  31.     0x00,0x36,0x49,0x55,0x22,0x50,
  32.     0x00,0x00,0x05,0x03,0x00,0x00,
  33.     0x00,0x00,0x1C,0x22,0x41,0x00,
  34.     0x00,0x00,0x41,0x22,0x1C,0x00,
  35.     0x00,0x14,0x08,0x3E,0x08,0x14,
  36.     0x00,0x08,0x08,0x3E,0x08,0x08,
  37.     0x00,0x00,0x00,0xA0,0x60,0x00,
  38.     0x00,0x08,0x08,0x08,0x08,0x08,
  39.     0x00,0x00,0x60,0x60,0x00,0x00,
  40.     0x00,0x20,0x10,0x08,0x04,0x02,
  41.     0x00,0x3E,0x51,0x49,0x45,0x3E,
  42.     0x00,0x00,0x42,0x7F,0x40,0x00,
  43.     0x00,0x42,0x61,0x51,0x49,0x46,
  44.     0x00,0x21,0x41,0x45,0x4B,0x31,
  45.     0x00,0x18,0x14,0x12,0x7F,0x10,
  46.     0x00,0x27,0x45,0x45,0x45,0x39,
  47.     0x00,0x3C,0x4A,0x49,0x49,0x30,
  48.     0x00,0x01,0x71,0x09,0x05,0x03,
  49.     0x00,0x36,0x49,0x49,0x49,0x36,
  50.     0x00,0x06,0x49,0x49,0x29,0x1E,
  51.     0x00,0x00,0x36,0x36,0x00,0x00,
  52.     0x00,0x00,0x56,0x36,0x00,0x00,
  53.     0x00,0x08,0x14,0x22,0x41,0x00,
  54.     0x00,0x14,0x14,0x14,0x14,0x14,
  55.     0x00,0x00,0x41,0x22,0x14,0x08,
  56.     0x00,0x02,0x01,0x51,0x09,0x06,
  57.     0x00,0x32,0x49,0x59,0x51,0x3E,
  58.     0x00,0x7C,0x12,0x11,0x12,0x7C,
  59.     0x00,0x7F,0x49,0x49,0x49,0x36,
  60.     0x00,0x3E,0x41,0x41,0x41,0x22,
  61.     0x00,0x7F,0x41,0x41,0x22,0x1C,
  62.     0x00,0x7F,0x49,0x49,0x49,0x41,
  63.     0x00,0x7F,0x09,0x09,0x09,0x01,
  64.     0x00,0x3E,0x41,0x49,0x49,0x7A,
  65.     0x00,0x7F,0x08,0x08,0x08,0x7F,
  66.     0x00,0x00,0x41,0x7F,0x41,0x00,
  67.     0x00,0x20,0x40,0x41,0x3F,0x01,
  68.     0x00,0x7F,0x08,0x14,0x22,0x41,
  69.     0x00,0x7F,0x40,0x40,0x40,0x40,
  70.     0x00,0x7F,0x02,0x0C,0x02,0x7F,
  71.     0x00,0x7F,0x04,0x08,0x10,0x7F,
  72.     0x00,0x3E,0x41,0x41,0x41,0x3E,
  73.     0x00,0x7F,0x09,0x09,0x09,0x06,
  74.     0x00,0x3E,0x41,0x51,0x21,0x5E,
  75.     0x00,0x7F,0x09,0x19,0x29,0x46,
  76.     0x00,0x46,0x49,0x49,0x49,0x31,
  77.     0x00,0x01,0x01,0x7F,0x01,0x01,
  78.     0x00,0x3F,0x40,0x40,0x40,0x3F,
  79.     0x00,0x1F,0x20,0x40,0x20,0x1F,
  80.     0x00,0x3F,0x40,0x38,0x40,0x3F,
  81.     0x00,0x63,0x14,0x08,0x14,0x63,
  82.     0x00,0x07,0x08,0x70,0x08,0x07,
  83.     0x00,0x61,0x51,0x49,0x45,0x43,
  84.     0x00,0x00,0x7F,0x41,0x41,0x00,
  85.     0x00,0x55,0x2A,0x55,0x2A,0x55,
  86.     0x00,0x00,0x41,0x41,0x7F,0x00,
  87.     0x00,0x04,0x02,0x01,0x02,0x04,
  88.     0x00,0x40,0x40,0x40,0x40,0x40,
  89.     0x00,0x00,0x01,0x02,0x04,0x00,
  90.     0x00,0x20,0x54,0x54,0x54,0x78,
  91.     0x00,0x7F,0x48,0x44,0x44,0x38,
  92.     0x00,0x38,0x44,0x44,0x44,0x20,
  93.     0x00,0x38,0x44,0x44,0x48,0x7F,
  94.     0x00,0x38,0x54,0x54,0x54,0x18,
  95.     0x00,0x08,0x7E,0x09,0x01,0x02,
  96.     0x00,0x18,0xA4,0xA4,0xA4,0x7C,
  97.     0x00,0x7F,0x08,0x04,0x04,0x78,
  98.     0x00,0x00,0x44,0x7D,0x40,0x00,
  99.     0x00,0x40,0x80,0x84,0x7D,0x00,
  100.     0x00,0x7F,0x10,0x28,0x44,0x00,
  101.     0x00,0x00,0x41,0x7F,0x40,0x00,
  102.     0x00,0x7C,0x04,0x18,0x04,0x78,
  103.     0x00,0x7C,0x08,0x04,0x04,0x78,
  104.     0x00,0x38,0x44,0x44,0x44,0x38,
  105.     0x00,0xFC,0x24,0x24,0x24,0x18,
  106.     0x00,0x18,0x24,0x24,0x18,0xFC,
  107.     0x00,0x7C,0x08,0x04,0x04,0x08,
  108.     0x00,0x48,0x54,0x54,0x54,0x20,
  109.     0x00,0x04,0x3F,0x44,0x40,0x20,
  110.     0x00,0x3C,0x40,0x40,0x20,0x7C,
  111.     0x00,0x1C,0x20,0x40,0x20,0x1C,
  112.     0x00,0x3C,0x40,0x30,0x40,0x3C,
  113.     0x00,0x44,0x28,0x10,0x28,0x44,
  114.     0x00,0x1C,0xA0,0xA0,0xA0,0x7C,
  115.     0x00,0x44,0x64,0x54,0x4C,0x44,
  116.     0x14,0x14,0x14,0x14,0x14,0x14,
  117. };
  118. /* ==================== 8x16 ASCII 字库(只保留空格和冒号) ==================== */
  119. u8 code ASC16[][16] = {
  120.     {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  121.     {0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00},
  122. };
  123. /* ==================== 汉字字库(只保留 Mn 里用到的字) ==================== */
  124. #define TotalHZ16  29
  125. u8 code HZ16M[TotalHZ16][2] = {
  126.     "文","件","测","试","分","析","系","统","设","置",
  127.     "打","印","输","出","调","零","校","准",
  128.     "维","护","单","片","机","O","K","查","询","管","理"
  129. };
  130. u8 code HZ16[TotalHZ16][32] = {
  131.     {0x08,0x08,0x08,0x08,0x78,0x88,0x09,0x0E,0x08,0x88,0x78,0x08,0x08,0x08,0x08,0x00,
  132.      0x40,0x40,0x40,0x20,0x20,0x11,0x0A,0x04,0x0A,0x11,0x10,0x20,0x60,0x20,0x20,0x00},
  133.     {0x40,0x20,0xF8,0x0F,0x82,0x60,0x1E,0x14,0x10,0xFF,0x10,0x10,0x10,0x10,0x00,0x00,
  134.      0x00,0x00,0xFF,0x00,0x01,0x01,0x01,0x01,0x01,0xFF,0x01,0x01,0x01,0x01,0x01,0x00},
  135.     {0x08,0x31,0x86,0x60,0x00,0xFE,0x02,0xF2,0x02,0xFE,0x00,0xF8,0x00,0x00,0xFF,0x00,
  136.      0x04,0xFC,0x03,0x00,0x80,0x47,0x30,0x0F,0x10,0x67,0x00,0x07,0x40,0x80,0x7F,0x00},
  137.     {0x40,0x42,0xDC,0x08,0x00,0x90,0x90,0x90,0x90,0x90,0xFF,0x10,0x12,0x1C,0x10,0x00,
  138.      0x00,0x00,0x7F,0x20,0x10,0x20,0x20,0x1F,0x10,0x10,0x01,0x06,0x18,0x20,0x78,0x00},
  139.     {0x80,0x40,0x20,0x98,0x87,0x82,0x80,0x80,0x83,0x84,0x98,0x30,0x60,0xC0,0x40,0x00,
  140.      0x00,0x80,0x40,0x20,0x10,0x0F,0x00,0x00,0x20,0x40,0x3F,0x00,0x00,0x00,0x00,0x00},
  141.     {0x08,0x88,0x68,0xFF,0x48,0x88,0x00,0xFC,0x24,0x24,0x22,0xE2,0x22,0x22,0x20,0x00,
  142.      0x02,0x01,0x00,0xFF,0x00,0x41,0x30,0x0F,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00},
  143.     {0x00,0x00,0x02,0x22,0xB2,0xAA,0x66,0x62,0x22,0x11,0x4D,0x81,0x01,0x01,0x00,0x00,
  144.      0x00,0x40,0x21,0x13,0x09,0x05,0x41,0x81,0x7F,0x01,0x05,0x09,0x13,0x62,0x00,0x00},
  145.     {0x20,0x30,0x2C,0xA3,0x60,0x10,0x84,0xC4,0xA4,0x9D,0x86,0x84,0xA4,0xC4,0x84,0x00,
  146.      0x20,0x22,0x23,0x12,0x12,0x92,0x40,0x30,0x0F,0x00,0x00,0x3F,0x40,0x41,0x70,0x00},
  147.     {0x40,0x41,0xCE,0x04,0x00,0x80,0x40,0xBE,0x82,0x82,0x82,0xBE,0xC0,0x40,0x40,0x00,
  148.      0x00,0x00,0x7F,0x20,0x90,0x80,0x40,0x43,0x2C,0x10,0x10,0x2C,0x43,0xC0,0x40,0x00},
  149.     {0x00,0x20,0x2F,0xA9,0xA9,0xAF,0xE9,0xB9,0xA9,0xAF,0xA9,0xA9,0x2F,0x20,0x00,0x00,
  150.      0x80,0x80,0x80,0xFF,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xFF,0x80,0x80,0x80,0x00},
  151.     {0x10,0x10,0x10,0xFF,0x90,0x50,0x14,0x04,0x04,0x04,0xFC,0x04,0x04,0x04,0x04,0x00,
  152.      0x02,0x42,0x81,0x7F,0x00,0x00,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00},
  153.     {0x00,0xFC,0x44,0x44,0x42,0x42,0x42,0x00,0xFE,0x02,0x02,0x02,0x02,0xFE,0x00,0x00,
  154.      0x08,0x1F,0x08,0x04,0x04,0x02,0x00,0x00,0xFF,0x00,0x00,0x04,0x08,0x07,0x00,0x00},
  155.     {0x44,0xF4,0x4F,0xE4,0x44,0x64,0x90,0x98,0x94,0x93,0x14,0x98,0x08,0xD0,0x10,0x00,
  156.      0x04,0x04,0x04,0xFF,0x02,0x02,0xFF,0x4A,0x8A,0x7F,0x00,0x3F,0x80,0xFF,0x00,0x00},
  157.     {0x00,0x00,0xFC,0x40,0x40,0x40,0x40,0xFF,0x40,0x40,0x40,0x40,0x40,0xFC,0x00,0x00,
  158.      0x00,0x00,0x7E,0x20,0x20,0x20,0x20,0x3F,0x20,0x20,0x20,0x20,0x20,0x7E,0x00,0x00},
  159.     {0x20,0x21,0xEE,0x04,0x00,0x00,0xFF,0x29,0xA9,0xBF,0xA9,0xA9,0x01,0xFF,0x00,0x00,
  160.      0x00,0x00,0x1F,0x88,0x44,0x30,0x0F,0x00,0x0F,0x04,0x04,0x4F,0x80,0x7F,0x00,0x00},
  161.     {0x00,0x0C,0x04,0x55,0x55,0x55,0x85,0x7F,0x85,0x55,0x55,0x55,0x05,0x0C,0x04,0x00,
  162.      0x04,0x04,0x04,0x0A,0x0A,0x09,0x2A,0xCC,0x28,0x19,0x09,0x02,0x06,0x02,0x02,0x00},
  163.     {0x08,0x88,0x68,0xFF,0x28,0x48,0x00,0x88,0x68,0x09,0x0E,0x08,0xA8,0x48,0x88,0x00,
  164.      0x02,0x01,0x00,0xFF,0x00,0x40,0x42,0x21,0x26,0x18,0x18,0x26,0x61,0xC0,0x40,0x00},
  165.     {0x00,0x02,0x1C,0xC8,0x20,0x10,0xFF,0x4A,0x48,0x49,0xFE,0x48,0x68,0x4C,0x08,0x00,
  166.      0x02,0x02,0x7E,0x01,0x00,0x00,0x7F,0x22,0x22,0x22,0x3F,0x22,0x23,0x32,0x20,0x00},
  167.     {0x20,0x38,0xA7,0x62,0x10,0x20,0x18,0xFF,0x4A,0x48,0x4A,0xFC,0x48,0x48,0x48,0x00,
  168.      0x22,0x23,0x22,0x12,0x12,0x00,0x00,0xFF,0x22,0x22,0x22,0x3F,0x22,0x22,0x22,0x00},
  169.     {0x00,0x10,0x10,0xFF,0x90,0x50,0x10,0x00,0xF8,0x89,0x8A,0x8E,0x88,0x88,0xF8,0x00,
  170.      0x02,0x42,0x81,0x7F,0x80,0x40,0x20,0x18,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  171.     {0x00,0x00,0xF8,0x28,0x29,0x2E,0x2A,0xF8,0x28,0x2C,0x2B,0x2A,0xF8,0x00,0x00,0x00,
  172.      0x08,0x08,0x0B,0x09,0x09,0x09,0x09,0xFF,0x09,0x09,0x09,0x09,0x0B,0x08,0x08,0x00},
  173.     {0x00,0x00,0x00,0xFE,0x10,0x10,0x10,0x10,0x10,0x1F,0x10,0x10,0x10,0x18,0x10,0x00,
  174.      0x80,0x40,0x30,0x0F,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0x00,0x00,0x00,0x00},
  175.     {0x08,0x08,0xC8,0xFF,0x48,0x88,0x08,0x00,0xFE,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,
  176.      0x04,0x03,0x00,0xFF,0x00,0x41,0x30,0x0C,0x03,0x00,0x00,0x00,0x3F,0x40,0x78,0x00},
  177.     {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,
  178.      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  179.     {0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  180.      0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  181.     {0x80,0x88,0x48,0xC8,0xA8,0x98,0x88,0xFF,0x88,0x98,0xA8,0xA8,0x48,0xC8,0x48,0x00,
  182.      0x00,0x40,0x40,0x5F,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x5F,0x40,0x40,0x40,0x00},
  183.     {0x40,0x42,0xCC,0x04,0x00,0x10,0xE8,0x27,0x24,0x24,0xE4,0x04,0x04,0xFC,0x00,0x00,
  184.      0x00,0x00,0x7F,0x20,0x10,0x00,0x1F,0x09,0x09,0x09,0x1F,0x40,0x80,0x7F,0x00,0x00},
  185.     {0x08,0x64,0x23,0xA6,0xAA,0xA2,0xAA,0xB4,0xA3,0xA2,0xA6,0x2A,0x22,0x62,0x20,0x00,
  186.      0x00,0x00,0x00,0xFF,0x4A,0x4A,0x4A,0x4A,0x4A,0x4A,0x4B,0xF8,0x00,0x00,0x00,0x00},
  187.     {0x44,0x44,0xFC,0x44,0x44,0x00,0xFE,0x92,0x92,0xFE,0x92,0x92,0x92,0xFE,0x00,0x00,
  188.      0x10,0x10,0x0F,0x08,0x48,0x40,0x45,0x44,0x44,0x7F,0x44,0x44,0x44,0x45,0x40,0x00},
  189. };
  190. /* ==================== 菜单数据(4 项) ==================== */
  191. u8 Mn[MENU_NUM][9] = {
  192.     "文件管理", "测试分析", "系统设置", "打印输出"
  193. };
  194. /* ==================== 唯一的 32x32 实心方块图标 ==================== */
  195. u8 code Icon_Solid[128] = {
  196.     0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  197.     0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  198.     0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  199.     0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  200.     0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  201.     0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  202.     0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  203.     0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
  204. };
  205. u8 code * code MenuIcons[MENU_NUM] = {
  206.     Icon_Solid, Icon_Solid, Icon_Solid, Icon_Solid
  207. };
  208. /* ==================== 硬件 I2C ==================== */
  209. void I2C_Init(void)
  210. {
  211.     I2CCFG = 0xC1;
  212.     I2CMSST = 0x00;
  213. }
  214. void I2C_Wait(void)
  215. {
  216.     while(!(I2CMSST & 0x40));
  217.     I2CMSST &= ~0x40;
  218. }
  219. void I2C_WriteByte(u8 dat, u8 cmd)
  220. {
  221.     I2CMSCR = 0x01; I2C_Wait();
  222.     I2CTXD = 0x78; I2CMSCR = 0x02; I2C_Wait();
  223.     I2CMSCR = 0x03; I2C_Wait();
  224.     I2CTXD = cmd ? 0x40 : 0x00; I2CMSCR = 0x02; I2C_Wait();
  225.     I2CMSCR = 0x03; I2C_Wait();
  226.     I2CTXD = dat; I2CMSCR = 0x02; I2C_Wait();
  227.     I2CMSCR = 0x03; I2C_Wait();
  228.     I2CMSCR = 0x06; I2C_Wait();
  229. }
  230. void OLED_WR_CMD(u8 cmd)
  231. {
  232.     EA = 0;
  233.     I2C_WriteByte(cmd, 0);
  234.     EA = 1;
  235. }
  236. void OLED_WR_DAT(u8 dat)
  237. {
  238.     EA = 0;
  239.     I2C_WriteByte(dat, 1);
  240.     EA = 1;
  241. }
  242. /* ==================== OLED 初始化 ==================== */
  243. void OLED_Init(void)
  244. {
  245.     I2C_Init();
  246.     OLED_WR_CMD(0xAE);
  247.     OLED_WR_CMD(0x00);
  248.     OLED_WR_CMD(0x10);
  249.     OLED_WR_CMD(0x40);
  250.     OLED_WR_CMD(0x81);
  251.     OLED_WR_CMD(0xCF);
  252.     OLED_WR_CMD(0xA1);
  253.     OLED_WR_CMD(0xC8);
  254.     OLED_WR_CMD(0xA6);
  255.     OLED_WR_CMD(0xA8);
  256.     OLED_WR_CMD(0x3F);
  257.     OLED_WR_CMD(0xD3);
  258.     OLED_WR_CMD(0x00);
  259.     OLED_WR_CMD(0xD5);
  260.     OLED_WR_CMD(0x80);
  261.     OLED_WR_CMD(0xD9);
  262.     OLED_WR_CMD(0xF1);
  263.     OLED_WR_CMD(0xDA);
  264.     OLED_WR_CMD(0x12);
  265.     OLED_WR_CMD(0xDB);
  266.     OLED_WR_CMD(0x40);
  267.     OLED_WR_CMD(0x20);
  268.     OLED_WR_CMD(0x00);
  269.     OLED_WR_CMD(0x8D);
  270.     OLED_WR_CMD(0x14);
  271.     OLED_WR_CMD(0xA4);
  272.     OLED_WR_CMD(0xA6);
  273.     OLED_WR_CMD(0xAF);
  274. }
  275. /* ==================== 显存操作 ==================== */
  276. void OLED_ShowTop(void)
  277. {
  278.     u8 i, n;
  279.     for(i = 0; i < 4; i++)
  280.     {
  281.         OLED_WR_CMD((u8)(0xB0 + i));
  282.         OLED_WR_CMD(0x00);
  283.         OLED_WR_CMD(0x10);
  284.         for(n = 0; n < 128; n++)
  285.             OLED_WR_DAT(ShowBuf[i][n]);
  286.     }
  287. }
  288. void OLED_Show(u8 page)
  289. {
  290.     u8 i, n;
  291.     for(i = 0; i < 8; i++)
  292.     {
  293.         OLED_WR_CMD((u8)(0xB0 + i));
  294.         OLED_WR_CMD(0x00);
  295.         OLED_WR_CMD(0x10);
  296.         for(n = 0; n < 128; n++)
  297.             OLED_WR_DAT(ShowBuf[i][128 * page + n]);
  298.     }
  299. }
  300. void OLED_CLS(u8 page)
  301. {
  302.     u8 i;
  303.     for(i = 0; i < 8; i++)
  304.         memset(&ShowBuf[i][128 * page], 0, 128);
  305.     OLED_Show(page);
  306. }
  307. void OLED_BMP(u8 X, u8 Y, u8 W, u8 H, u8 code *BMP, u8 page, u8 mode)
  308. {
  309.     u16 num = 0;
  310.     u8 i, l;
  311.     if(page == 0)
  312.         for(i = 0; i < H; i++)
  313.             for(l = 0; l < W; l++)
  314.                 ShowBuf[Y + i][X + l] = BMP[num++];
  315.     else
  316.         for(i = 0; i < H; i++)
  317.             for(l = 0; l < W; l++)
  318.                 ShowBuf[Y + i][X + l + 128] = BMP[num++];
  319.     if(mode) OLED_ShowTop();
  320. }
  321. /* ==================== 字符显示 ==================== */
  322. void OLED_A16(u8 X, u8 Y, char Ch, u8 page)
  323. {
  324.     u8 i, j;
  325.     for(j = 0; j < 2; j++)
  326.     {
  327.         for(i = 0; i < 8; i++)
  328.         {
  329.             u8 dat;
  330.             if(Ch == ' ')      dat = 0;
  331.             else if(Ch == ':') dat = ASC16[1][i + j * 8];
  332.             else               dat = 0;
  333.             if(page == 0) ShowBuf[Y + j][X + i] = dat;
  334.             else          ShowBuf[Y + j][X + i + 128] = dat;
  335.         }
  336.     }
  337. }
  338. void OLED_HZ16(u8 X, u8 Y, u8 no, u8 page)
  339. {
  340.     u8 i, j;
  341.     if(no >= TotalHZ16) return;
  342.     for(j = 0; j < 2; j++)
  343.     {
  344.         for(i = 0; i < 16; i++)
  345.         {
  346.             if(page == 0) ShowBuf[Y + j][X + i] = HZ16[no][i + j * 16];
  347.             else          ShowBuf[Y + j][X + i + 128] = HZ16[no][i + j * 16];
  348.         }
  349.     }
  350. }
  351. void OLED_String(u8 X, u8 Y, u8 *s, u8 page, u8 mode)
  352. {
  353.     u8 hz[3];
  354.     u16 m;
  355.     while(*s != 0)
  356.     {
  357.         if(*s < 0x80)
  358.         {
  359.             OLED_A16(X, Y, *s, page);
  360.             X += 8; s++;
  361.         }
  362.         else
  363.         {
  364.             hz[0] = *s; hz[1] = *(s + 1); hz[2] = 0;
  365.             for(m = 0; m < TotalHZ16; m++)
  366.             {
  367.                 if(HZ16M[m][0] == hz[0] && HZ16M[m][1] == hz[1]) break;
  368.             }
  369.             if(m < TotalHZ16) OLED_HZ16(X, Y, (u8)m, page);
  370.             X += 16; s += 2;
  371.         }
  372.         if(X > 127) { X = 0; Y += 2; }
  373.     }
  374.     if(mode) OLED_Show(page);
  375. }
  376. void OLED_MoveL(u8 code *pic)
  377. {
  378.     u8 i, l;
  379.     OLED_BMP(4, 0, 32, 4, pic, 1, 0);   /* 待移入图标画到第二页 x=4 */
  380.     for(l = 0; l < 44; l++)              /* 一次移 1 列 */
  381.     {
  382.         for(i = 0; i < 172; i++)
  383.         {
  384.             ShowBuf[0][i] = ShowBuf[0][i + 1];
  385.             ShowBuf[1][i] = ShowBuf[1][i + 1];
  386.             ShowBuf[2][i] = ShowBuf[2][i + 1];
  387.             ShowBuf[3][i] = ShowBuf[3][i + 1];
  388.         }
  389.         OLED_ShowTop();
  390.     }
  391. }
  392. void OLED_MoveR(u8 code *pic)
  393. {
  394.     u8 i, l;
  395.     OLED_BMP(4, 0, 32, 4, pic, 1, 0);   /* 待移入图标画到第二页 x=4 */
  396.     for(l = 0; l < 44; l++)              /* 一次移 1 列 */
  397.     {
  398.         for(i = 128; i > 0; i--)
  399.         {
  400.             ShowBuf[0][i] = ShowBuf[0][i - 1];
  401.             ShowBuf[1][i] = ShowBuf[1][i - 1];
  402.             ShowBuf[2][i] = ShowBuf[2][i - 1];
  403.             ShowBuf[3][i] = ShowBuf[3][i - 1];
  404.         }
  405.         ShowBuf[0][0] = ShowBuf[0][171 - l];
  406.         ShowBuf[1][0] = ShowBuf[1][171 - l];
  407.         ShowBuf[2][0] = ShowBuf[2][171 - l];
  408.         ShowBuf[3][0] = ShowBuf[3][171 - l];
  409.         OLED_ShowTop();
  410.     }
  411. }
  412. /* ==================== 延时 ==================== */
  413. void delay_ms(u16 ms)
  414. {
  415.     u16 i;
  416.     do {
  417.         i = MAIN_Fosc / 6000;
  418.         while(--i);
  419.     } while(--ms);
  420. }
  421. /* ==================== 主函数 ==================== */
  422. void main(void)
  423. {
  424.     u8 m = 0;
  425.     u8 move;
  426.     WTST  = 0;
  427.     EAXFR = 1;
  428.     CKCON = 0;
  429.     P0M1 = 0x00; P0M0 = 0x00;
  430.     P1M1 = 0x00; P1M0 = 0x00;
  431.     P2M1 = 0x00; P2M0 = 0x00;
  432.     P3M1 = 0x00; P3M0 = 0x00;
  433.     P4M1 = 0x00; P4M0 = 0x00;
  434.     P5M1 = 0x00; P5M0 = 0x00;
  435.     OLED_Init();
  436.     OLED_CLS(1);
  437.     OLED_CLS(0);
  438.     /* 初始画面:三个图标画在 x=4/48/92 */
  439.     OLED_BMP( 4, 0, 32, 4, MenuIcons[0], 0, 0);
  440.     OLED_BMP(48, 0, 32, 4, MenuIcons[1], 0, 0);
  441.     OLED_BMP(92, 0, 32, 4, MenuIcons[2], 0, 0);
  442.     OLED_String(32, 6, Mn[0], 0, 1);
  443.     while(1)
  444.     {
  445.         move = 0;
  446.         /* ---------- 左键:按下滑一次,长按持续滑 ---------- */
  447.         if(KEY_LEFT == 0)
  448.         {
  449.             delay_ms(10);
  450.             if(KEY_LEFT == 0)
  451.             {
  452.                 /* 第一次滑动 */
  453.                 m = (m == 0) ? (MENU_NUM - 1) : (m - 1);
  454.                 OLED_MoveR(MenuIcons[(m + MENU_NUM - 1) % MENU_NUM]);
  455.                 OLED_String(32, 6, Mn[m], 0, 1);
  456.                 /* 长按:只要还按着就继续滑 */
  457.                 while(KEY_LEFT == 0)
  458.                 {
  459.                     m = (m == 0) ? (MENU_NUM - 1) : (m - 1);
  460.                     OLED_MoveR(MenuIcons[(m + MENU_NUM - 1) % MENU_NUM]);
  461.                     OLED_String(32, 6, Mn[m], 0, 1);
  462.                 }
  463.             }
  464.         }
  465.         /* ---------- 右键:按下滑一次,长按持续滑 ---------- */
  466.         if(KEY_RIGHT == 0)
  467.         {
  468.             delay_ms(10);
  469.             if(KEY_RIGHT == 0)
  470.             {
  471.                 /* 第一次滑动 */
  472.                 m = (m == MENU_NUM - 1) ? 0 : (m + 1);
  473.                 OLED_MoveL(MenuIcons[(m + 1) % MENU_NUM]);
  474.                 OLED_String(32, 6, Mn[m], 0, 1);
  475.                 /* 长按:只要还按着就继续滑 */
  476.                 while(KEY_RIGHT == 0)
  477.                 {
  478.                     m = (m == MENU_NUM - 1) ? 0 : (m + 1);
  479.                     OLED_MoveL(MenuIcons[(m + 1) % MENU_NUM]);
  480.                     OLED_String(32, 6, Mn[m], 0, 1);
  481.                 }
  482.             }
  483.         }
  484.         /* ---------- 退出键:清屏 ---------- */
  485.         if(KEY_EXIT == 0)
  486.         {
  487.             delay_ms(10);
  488.             if(KEY_EXIT == 0)
  489.             {
  490.                 while(KEY_EXIT == 0);
  491.                 OLED_CLS(0);
  492.             }
  493.         }
  494.     }
  495. }
复制代码
为什么第一次左移,右边两个图标的间距变小了,

DOP_ddc80794754d74ca81e02e509843f69e_W_.gif
回复

使用道具 举报 送花

已绑定手机

7

主题

445

回帖

6692

积分

论坛元老

积分
6692
发表于 2026-10-12 17:15:26 | 显示全部楼层
似乎是第2次变小的。
一个172,另一个128,可能造成边界处的数据丢失。
估计是OLED_MoveR的问题。
回复

使用道具 举报 送花

已绑定手机

2

主题

154

回帖

470

积分

中级会员

积分
470
发表于 2026-10-12 19:21:44 | 显示全部楼层
OLED_MoveL有问题。

出现右侧两图标变窄的原因在OLED_MoveL();
初始状态:
---4--[图1]--12--[图2]--12--[图3]--4--|右边界
--x--表示间距。
按下右键时:
在第二页画图3(这个图3逻辑不对,为方便我改叫图4吧),此时状态:
--4--[图1]--12--[图2]--12--[图3]--4--|--4--[图4]
|是页边界,注意看,图3与图4相距8,
之后全左移44像素,变为:
--4--[图2]--12--[图3]--8--[图4]。
右两图距离是窄的。

。
回复

使用道具 举报 送花

已绑定手机

2

主题

154

回帖

470

积分

中级会员

积分
470
发表于 2026-10-12 19:44:24 | 显示全部楼层
OLED_MoveR中的第二循环for(i=128要改为172,下面的171-1要改为172。
回复

使用道具 举报 送花

已绑定手机

8

主题

7

回帖

118

积分

注册会员

积分
118
 楼主| 发表于 2026-10-13 15:05:26 | 显示全部楼层
问题依旧
回复

使用道具 举报 送花

已绑定手机

2

主题

154

回帖

470

积分

中级会员

积分
470
发表于 2026-10-13 16:25:26 | 显示全部楼层
你怎么改的,OLED_MoveR(),OLED_MoveL()都改了吗, 发下改后的代码

另外:滑动动画的部分也可以优化,不需要循环移位像素,
你只需要改显示偏移就行,在OLED_ShowTop中OLED_WR_DAT(ShowBuf[i+偏移x][n]);
滑动完,再一次移就行。
回复

使用道具 举报 送花

已绑定手机

8

主题

7

回帖

118

积分

注册会员

积分
118
 楼主| 发表于 2026-10-15 10:19:27 | 显示全部楼层
jrc*** 发表于 2026-10-13 16:25
你怎么改的,OLED_MoveR(),OLED_MoveL()都改了吗, 发下改后的代码

另外:滑动动画的部分也可以优化,不需 ...

可以帮我改一下吗,谢谢
回复

使用道具 举报 送花

已绑定手机

2

主题

154

回帖

470

积分

中级会员

积分
470
发表于 2026-10-15 14:06:09 | 显示全部楼层
我没测试环境,只能根据理解来改,实际需要你自己测试。

修改1:
void OLED_MoveL(u8 code *pic)中的这一行”OLED_BMP(4, 0, 32, 4, pic, 1, 0);“
改为:OLED_BMP(8, 0, 32, 4, pic, 1, 0);  解决左移时,间隔变窄问题。

修改2:
void OLED_MoveR(u8 code *pic)中
for(i = 128; i > 0; i--) 改为for(i =172; i > 0; i--)
”ShowBuf[0][0] = ShowBuf[0][171 - l];
ShowBuf[1][0] = ShowBuf[1][171 - l];
ShowBuf[2][0] = ShowBuf[2][171 - l];
ShowBuf[3][0] = ShowBuf[3][171 - l];“ 这几句中的”[171 - l]“改为[172]
解决右移时,图4没有正确右环移。

修改3:
main()中,
while(1)前的这一句” OLED_String(32, 6, Mn[0], 0, 1);"
改为 OLED_String(32, 6, Mn[1], 0, 1);  //居中的是当前菜单。
并加一句 m=1;   解决上面我说的,本来要画图4,实际画了图3;


回复

使用道具 举报 送花

49

主题

611

回帖

5448

积分

荣誉版主

积分
5448
发表于 2026-10-16 14:26:45 | 显示全部楼层

看了下整个程序,关于改进有一些想法。

首先说一下错位的问题

从程序看,三个图标的分布的x坐标分别是 4, 48, 92
也就两边各留4列,三个图标间隔12列
列数分配就是 4 + 32 + 12 + 32 + 12 + 32 + 4 = 128
这其实是挺完美的布局。

但是按照 OLED_MoveL 函数的定义,左移之前,先把等待滑入的图标写进第二页缓存备用,
然后利用左移44列的方式,将图标滑进屏幕。

假设四个图标分别为 Icon0、Icon1、Icon2、Icon3,
也就是初始时,缓存里的图标位置应该是 4+【Icon0】+12+【Icon1】+12+【Icon2】+12+〖Icon3〗
此时的 Icon3 的坐标值应该是在第136列。

而函数将Icon3写入缓存时,用的是 OLED_BMP(4, 0, 32, 4, pic, 1, 0);
结果就成了第132列(x2=4),比应有的间隔少了4列。
这就是向左滑动回来的时候,间距变窄的原因
截图202610161349358284.jpg

========================================

至于改进,个人认为这个错位只是问题之一,另外还有几个地方需要改进

1、关于双缓存
不知道这个256列宽的缓存是否还有其他用处,如果只是为了滑动图标,那就有些浪费内存了。
在第二页缓存中,一个图标32列,加上前面的8列(凑足12列间距),顶多再加上12列空白区域,实际用到的是52列,也就是总共180列。
剩余76列是浪费的,算上四行,有304个字节没有用上。

2、关于图标滑动的操作
程序里滑动图标的过程,用的是逐个字节移动的方法。
这样在思路上比较直观,但是略微耗费算力。
看到清空缓存的函数 OLED_CLS 里用到了 memset 命令,


既然如此,那么用 memmove 命令来移动数据,速度上会更快很多。


不过还有一种思路,是把每个图标都单独显示,在移动之前,先把一边擦掉,然后在移动一列的位置上重新显示图片。
可以像OLED_BMP那样
单独创建一个显示图标的函数ShowIcon(x),和擦除一列的函数 ClearSide(x)。
前者用来将32×32的图标写在缓存对应的位置上,后者用来擦除图标上的一条边。
比如向左移动,以第一个图标为例,
x=4,图片占用 x=4~35列,
先把图标的最右边一列(第35列)擦掉(防止拖尾),然后在x=3的位置重新显示图标,新图标占用的就是3~34列。
这样,图标就左移了一列,连贯起来也是滑动的效果,其它图标也如此操作。
向右滑动的过程类似,区别就是擦掉的是原图标的左边一列,再把图标显示在x+1的位置上。
截图202610161424076169.jpg


使用这个方法的好处是,可以不依赖第二页缓存,只在128列缓存范围之内就可以实现。

虽然计算量上可能略高于直接移动缓存数据,但是对无效区域和空白区域的操作少了,而且对于STC32G的速度来说,滑动效果也不会有太大的影响。

再进一步,ShowIcon(x) 和 ClearSide(x) 函数还可以加上边界限制
比如 ShowIcon(x) 函数,
当 x 的值小于0(图标左边有一部分超出屏幕左边界) 或者 大于96(图标右边有一部分在屏幕右边界之外),则只显示有效部分。

ClearSide(x) 也可以加上限制,如果x小于0 或者大于127,则不进行擦除。
这样又可以提高一部分效率。


另外,用四个Solid方块,不太容易看出图标是否显示在正确的位置上,最好是用四个不同的图标进行测试

===================================




顺便把你的程序里的关键代码发给AI助手,分析结果跟现象相符



问题 1:图标落点用的是备用区,但滚动只滚可见区,图标永远进不了屏幕
  • page=1 使图标落在 列 132~163(备用区)
  • 滚动循环只处理 列 0~171,把 i+1 挪到 i
  • 当 i 达到 171 时,取的是 ShowBuf[x][172]
关键点:列 172 也是备用区,不属于"屏幕显示用"的 0~127 范围。所以:
  • 滚动的"入口"在最右侧列 171,读 172
  • 但图标画在 132~163,位于 172 的左边
这意味着图标在备用区里,但滚动方向的"喂入源"在它右边。图标需要被"推"进屏幕,但滚动方向会把列 132~163 的内容继续往左挪,而入口在最右 172 —— 图标会先往左移动,但初始时列 164~171 是空的,图标要靠 172 处的数据补进来,逻辑上图标还没到入口位置。
问题 2:入口取 172,但备用区图标在 132,位置对不上
要正确实现"图标从备用区平滑移入屏幕",通常做法是:把图标画在紧邻可见区右侧的备用区位置(即列 128 起),然后逐列整体左移。这样每移一列,列 128 的图标最左一列就"跨"进列 127,形成移入效果。
但你这里:
  • 图标画在 132(不是 128)
  • 左移的读取上界到 172(不是 128 或 256)
图标落点和滚动入口都不在正确位置,图标不会按预期从屏幕右边缘移入。
问题 3:< 172 的边界含义不明
如果可见区是 0~127,通常滚动只需处理 0~127(把 1~128 挪到 0~127),读取到 128 即可。写成 172 说明作者想连备用区一起参与滚动,但这样会把备用区内容也搅乱,且图标位置(132)与滚动范围(0~172)不匹配。
二、OLED_MoveR 的问题OLED_BMP(4, 0, 32, 4, pic, 1, 0);   /* 图标画到列 132~163 */for(l = 0; l < 44; l++){    for(i = 128; i > 0; i--)        ShowBuf[x] = ShowBuf[x][ i - 1];    ShowBuf[x][0] = ShowBuf[x][171 - l];}📋
问题 1:右移只处理可见区 0~128,但图标在备用区 132~163,图标进不了屏幕
  • 右移把 i-1 挪到 i,处理范围 i = 128 → 1,即把列 0~127 整体右移
  • 入口 ShowBuf[x][0] = ShowBuf[x][171-l],l=0 时取列 171,l=43 时取列 128
问题 2:入口数据 128~171 区间是"死数据"
l=0 取 171,l=43 取 128。但 128~131 和 164~171 之间,图标只占 132~163。所以前几列(l=0~7,取 164~171)取到的是空白,图标要到 l=8(取 163)才开始真正进入。
问题 3:171-l 的数据在右移过程中被覆盖
右移 i = 128 时,ShowBuf[x][128] = ShowBuf[x][127],列 128 被覆盖。而 l=43 时要读 ShowBuf[x][128],此时它已经是列 127 的旧值,不是原来备用区列 128 的数据了,回卷数据变脏。
问题 4:两个函数滚动范围不一致(172 vs 128)
  • MoveL 主力处理 0~171
  • MoveR 主力处理 0~128
宽度差这么多,来回移动画面必然错乱,不互为逆操作。
三、结论:两个函数的核心缺陷[td]
项目
MoveL
MoveR
图标落点(page=1)列 132~163(备用区)列 132~163(备用区)
主滚动范围0~1710~128
图标能否进屏入口在 172,图标在 132,位置错位入口取 128~171,但大部分是空白
数据污染—回卷读 171-l,该列已被覆盖
根本问题: 备用区图标画在 132,且入口/范围设置混乱,导致图标无法按预期平滑移入屏幕。
四、正确设计思路(供你确认)
要实现"图标从右侧备用区平滑移入屏幕",标准做法应为:
图标画在紧邻可见区右侧的位置:
OLED_BMP(0, 0, 32, 4, pic, 1, 0);   /* page=1,X=0 → 实际落在列 128~159 */📋
注意这里第一个参数 X 应传 0(而不是 4),使 X+128 = 128,图标正好从列 128 开始,贴着可见区右边缘。
左移(图标从右移入):
/* 把整体左移,可见区 0~127 读取 1~128 的数据 */for( i = 0; i < 128; i++){    ShowBuf[0] = ShowBuf[0][ i + 1];   /* 列 128 的图标数据会逐列进入列 127 */    ...}📋
右移(图标从右移出,或从右移入):
/* 先备份最右列,再整体右移,最后回填 */for(i = 128; i > 0; i--){    ShowBuf[0] = ShowBuf[0][ i - 1];    ...}ShowBuf[0][0] = 备份值;📋
关键原则:
  • 只有 列 0~127 参与屏幕显示,滚动时必须保证"进入屏幕的那一列"来自备用区(列 128)。
  • 图标应画在 列 128 起(即 OLED_BMP(0, ..., page=1, ...)),而不是 132。
  • 移动范围两者必须一致,才能互为逆操作


===============================================



能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

49

主题

611

回帖

5448

积分

荣誉版主

积分
5448
发表于 2026-10-16 14:32:17 | 显示全部楼层
根据上面第二点思路

不用缓存也可以实现图标的滑动,只是在眼力非常好的人看来,可能会有略微迟滞感。

下面就是在STC8H单片机上实验的单向滑动和循环滑动的效果,图标用了5个,没有使用缓存,直接发数据给屏幕。

首先是用 Solid小方块 当成图标的



然后是替换成五个不同图标的


点评

这个效果没有使用缓存,可以根据这个思路,把将数据发给屏幕的部分替换成改成写进缓存的 这里用到的几条关键代码,一部分让AI助手进行了优化和添加注释 图标数组部分: 显示图标和擦除拖尾的两个函数 单向  详情 回复 发表于 2026-10-16 15:00
能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-11-8 02:08 , Processed in 1.447623 second(s), 86 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表