diff --git a/README.md b/README.md index 2b1118b..8639473 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,136 @@ -# mini_desktop — GEC6818 Desktop System +# mini_desktop — GEC6818 桌面系统 -800×480 framebuffer + touch desktop with photo album and IR remote control. +粤嵌 GEC6818(ARM Cortex-A53 armv7l, Linux 3.4.39-gec) +嵌入式桌面系统。800×480 LCD 触摸屏,集成电子相册、红外遥控和游戏启动器。 -## Architecture +## 功能 ``` -desktop → album (swipe photo browser) - → ir_app (learn / send / receive IR codes) +桌面 ── 图库 (触摸滑动 / 遥控翻页浏览照片) + ├─ 游戏 (3D 光线投射游戏) + └─ 红外 (学习 / 发送 / 接收红外遥控码) + └─ 后台接收 → 遥控桌面导航 ``` -## Non-blocking I/O pattern +- **桌面**:3 个圆角图标(图库、游戏、红外),支持触摸点击和遥控框选 +- **图库**:BMP 照片浏览,触摸左右滑动或遥控翻页,上键/点顶栏退出 +- **游戏**:通过 `system()` 启动外部 `raycast_game_static`,退出后 fb_reset 恢复 +- **红外**:5 键学习(上下左右中),发送/接收页面,后台常驻识别遥控码 +- **遥控导航**:左右键移动白色选框,中键进入应用 -All I/O uses `O_NONBLOCK` + drain-events-into-cache. Never block on `read()`. +## 硬件 -Touch events are drained per-frame via `ts_read()` — after the `while(ts_read > 0)` -loop exits (no more events), remaining logic (serial polling, etc.) runs once, -then the outer loop repeats. This keeps the UI responsive to both touch and serial. +| 项目 | 详情 | +|------|------| +| 开发板 | 粤嵌 GEC6818 | +| 主控 | Samsung S5P6818, ARM Cortex-A53, armv7l | +| 系统 | Linux 3.4.39-gec, BusyBox | +| 屏幕 | 800×480 LCD, 32bpp framebuffer (/dev/fb0) | +| 触摸 | gslX680, 1024×600 raw → 800×480 校准 (/dev/input/event0) | +| 红外 | 串口 /dev/ttySAC1, 9600 8N1, 模块自动去 A1F1 头,输出 3 字节用户码 | +| 蜂鸣器 | /sys/kernel/gec_ctrl/beep(4 字节小端 int 写入;shell echo 会写入 `0\n` 误解析为非零值)| +| LED | /sys/class/leds/ledN/brightness(1 字节 ASCII '0'/'1' 写入)| -### Do NOT let `ts_read()` return 1 with stale data +## 项目结构 -`ts_read()` uses a `static int has_new` flag. It MUST be reset to `0` at the top -of every call. If you forget this, `has_new` sticks at `1` after the first touch, -every `ts_read()` call returns 1 forever, and `while (ts_read(...) > 0)` loops -spin endlessly — **code after the loop is never reached**. This bit us when -`page_learn()` could never read from the IR serial port. +``` +mini_desktop/ +├── main.c # 入口 +├── Makefile +├── common/ # 硬件抽象层 +│ ├── device.c/.h # framebuffer / 触摸 / BMP / 字体 / 蜂鸣器 / LED +│ ├── param.h # 屏幕参数、触摸校准 +│ └── tools.c/.h +├── button/ # 按钮系统 +│ └── button.c/.h # 圆角矩形、文字标签、BMP 背景、绿色已学标记 +├── album/ # 电子相册 +│ └── album.c/.h +├── ir_app/ # 红外功能 +│ └── ir_app.c/.h # 学习 / 发送 / 接收 / 后台遥控 +├── desktop/ # 桌面 +│ └── desktop.c/.h # 图标布局、触摸分发、遥控导航 +├── fonts/ +│ └── font_16x16.h # 16×16 等宽字模(A-Z + 0-9, Monaco 14pt 生成) +├── images/ # 相册照片(800×480 BMP) +├── icon/ # 桌面图标 + 背景(BMP) +└── test/ # 分步测试 +``` -## Build +## 板子初始化 + +首次拿到板子或烧录系统后,需要配置网络和 SSH。 + +### 固定 IP + +编辑 `/etc/profile`,在末尾添加: + +```bash +ifconfig eth0 up +ifconfig eth0 192.168.1.88 netmask 255.255.255.0 +route add default gw 192.168.1.1 +``` + +重启或执行 `source /etc/profile` 生效。注意板子 IP 需要和电脑在同一网段。 + +### 启用 eth0 + +如果 `ifconfig` 看不到 eth0,检查网线是否插好、交换机/路由器是否通电: + +```bash +# 查看所有网口 +ifconfig -a + +# 手动启用 +ifconfig eth0 up +``` + +某些镜像的 eth0 默认是 down 状态,加到 `/etc/profile` 里开机自动启用即可。 + +### 安装 SSH(dropbear) + +板子默认可能没有 SSH 服务。检查: + +```bash +ls /usr/sbin/dropbear # dropbear 服务端 +ls /usr/bin/dbclient # dropbear 客户端(SSH 登录用) +``` + +如果缺失,从交叉编译工具链或同型号板子拷贝: + +```bash +# 在能 SSH 的板子上导出 +scp /usr/sbin/dropbear /usr/bin/dbclient root@<目标IP>:/tmp/ + +# 在目标板子上 +cp /tmp/dropbear /usr/sbin/dropbear +cp /tmp/dbclient /usr/bin/dbclient +chmod +x /usr/sbin/dropbear /usr/bin/dbclient +``` + +创建 host key 并启动: + +```bash +mkdir -p /etc/dropbear +dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key +dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key +dropbear -p 22 +``` + +确认启动: + +```bash +ps | grep dropbear +``` + +设置开机自启(加到 `/etc/profile` 末尾): + +```bash +dropbear -p 22 & +``` + +之后即可从电脑 SSH 登录:`ssh root@192.168.1.88`。 + +## 编译 ```bash docker run --platform linux/amd64 --rm \ @@ -34,9 +139,84 @@ docker run --platform linux/amd64 --rm \ gec6818-dev:ubuntu18 bash -c 'make static' ``` -## Deploy +工具链路径:[`gec6818_env_toolchain/toolchain/`](../gec6818_env_toolchain/toolchain/),ARM 32-bit soft-float,GCC 5.4.0。 + +## 部署 + +板子 IP `192.168.1.88`,目标目录 `/home/cyy/`。 ```bash +# 停止旧进程 ssh root@192.168.1.88 'killall mini_desktop 2>/dev/null; sleep 1' + +# 主程序 scp -O mini_desktop root@192.168.1.88:/home/cyy/ + +# 资源文件 +ssh root@192.168.1.88 'mkdir -p /home/cyy/icon /home/cyy/images' +scp -O icon/*.bmp root@192.168.1.88:/home/cyy/icon/ +scp -O images/*.bmp root@192.168.1.88:/home/cyy/images/ + +# 游戏(单独部署) +scp -O raycast_game_static root@192.168.1.88:/home/cyy/ ``` + +### 需要部署的 BMP 清单 + +``` +icon/ +├── background.bmp # 桌面背景(800×480) +├── photo_icon.bmp # 图库图标(~80×80,圆角) +├── game_icon.bmp # 游戏图标 +├── ir_icon.bmp # 红外图标 +├── bg_page.bmp # 红外子页面背景(800×480) +└── bar_top.bmp # 红外顶栏(800×44) + +images/ +├── photo_1.bmp # 相册照片(800×480) +├── photo_2.bmp +└── photo_3.bmp +``` + +IR 学习数据 `ir_send_*.txt` / `ir_recv_*.txt` 由程序自动生成,无需预部署。 + +## 运行 + +在板子上: + +```bash +cd /home/cyy +./mini_desktop +``` + +触摸顶栏返回上级菜单,触摸图标进入应用。遥控器需先在「红外 → Learn → RECV 模式」学习 5 个键(上下左右中),之后桌面和图库中可用遥控操作。 + +## 设计约定 + +### 非阻塞 I/O + +所有 I/O 使用 `O_NONBLOCK` + 事件排空模式。每帧先 `while(ts_read > 0)` 排空触摸, +循环退出后执行其他逻辑(串口轮询等),最后回到外层循环。UI 对触摸和串口同时响应。 + +### `ts_read()` 的 `has_new` 陷阱 + +`ts_read()` 内有 `static int has_new`,**每次调用必须重置为 0**。 +忘记重置会导致首次触摸后 `ts_read()` 永远返回 1, +`while(ts_read > 0)` 死循环,后续代码永不执行。 +此前曾导致红外学习页面无法读到串口数据。 + +### 触摸排空 + +子应用返回时务必排空触摸 + 红外缓冲,防止上一个触摸/遥控事件泄漏到下个页面。 +使用 `drain_both()`(排空触摸 + `ir_flush()`)。 + +### 游戏退出后的 framebuffer + +游戏使用双缓冲 `FBIOPAN_DISPLAY` 翻页,退出后 yoffset 可能非零。 +必须在游戏返回后调 `fb_reset()` 复位 yoffset 并重读参数。 + +### BMP 渲染性能 + +- `bmp_display`(全屏背景):stride 匹配时一次 `memcpy`,否则逐行 `memcpy` +- `show_bmp`(图标/局部):无裁剪时一次 `memcpy`,有裁剪时逐行 `memcpy` +- 不再逐像素处理透明(alpha 字节由 LCD 控制器忽略)