AVR Chess
简介
SZDIY 在邮件列表里发起一个叫”新年Hackathon online“的,所说能治疗拖延症的活动,活动前大家到 SZDIY hackspace 去领取 hack42 的徽章,利用 2016 年的元旦假期 3 天时间做一个项目并发布到网上。为了治疗我的拖延症,我决定做点什么 :)
在 2014 年底,刘达捐赠了两箱 LCD 模块给 SZDIY,有两个型号,2015年4月我成功驱动了其中型号为 JYG-12864K8G 的模块。我们应该用这些 LCD 做些什么?这是一个问题。我一直想用它做一个游戏机,比如经典的俄罗斯方块。但一直没有动手。
图标控制
最开始成功驱动这个 LCD 时发现它会显示一些类似于电话上的图标,但并未细究。在这三天间,通过阅读 ST7565R 的数据手册,并经过测试,弄清楚了图标的控制方法。
ST7565R datasheet V1.9 第 16 页 Fig 8. DDRAM Format 图,放到下面:
ST7565R datasheet pg 17 提到:
Page Address “8” is a special RAM area for the icons with only one valid bit: D0. 这个特殊的页就是用来控制光标的,它只有一位数据 D0. 也就是第 65 行。(ST7565R 可以控制 132x64 像素,64 方向又分为 8 页,因为一个 byte 是 8 位)
下面是一段用来测试图标时的代码,先选择 page 和 SEG 的值,再写 0x01 (因为 page 8 只有 1 bit 宽) 就能点亮相应的图标。比如调用 icon_control(4); 就可以点亮天线符号。
void icon_control(uint8_t column)
{
uint8_t count, msb, lsb, page;
//column += 4;
lsb = ((column)&(0x0F));
msb = ((column >> 4) & (0x0F));
msb |= 0x10;
page = 0xB8; // select the 8th page
st7565_command(page);
st7565_command(lsb); // Set Column Address
st7565_command(msb); // Set Column Address
st7565_data(0x01); // set, turn it on
}
u8glib
提到 LCD 的驱动,必须提到 u8glib. 它是一个非常优秀的 MCU 图形库,成功点亮 JYG-12864K8G 时想做的一件事就是用 u8glib 来驱动这款 LCD.
得益于 u8glib 良好的代码组织,加一个新的 LCD 进去是很方便的。具体方法是在 src 目录里寻找和自己手上的 LCD 近似的 LCD 的代码(主要是 LCD 的 controller 型号和分辨率),然后复制一份,修改文件名,再作相应的修改。另外,u8glib 里已经有很多的已经支持的 LCD 型号,有时不用自己新加文件也可以驱动。
比如,JYG-12864K8G 的代码是在 u8g_dev_st7565_dogm128.c 的基础上修改的。只修改了简单的几个地方:
- JYG-12864 的 SEG 是从 SEG4 开始的。要把 col address 的低 4 位改成 0x004
- SEG 的扫描方向(ADC)。
- common output mode 的扫描方向。
- 对比度的参数设置。
u8g_dev_st7565_jyg12864k8g.c 的内容:
https://pastebin.mozilla.org/8855726
新增了设备文件,对应地在 u8g.h 里也要加入一些内容:
https://pastebin.mozilla.org/8855727
国际象棋
下国际象棋是一项十分有意思的活动。Claude Shannon 就对其有很多研究。2015年初,有人打破了尘封 32 年的纪录,写出了世界上最小的国际象棋程序并引发了一段恩怨情仇,不管怎么样,把国际象棋程序浓缩成 400 多个字节,真是让人印象深刻!
在 8 位 AVR 上运行国际象棋是完全可以的(在我很多年前初学 MCU 时就曾把一个 AVR 国际象棋程序的源代码和原理图打印出来阅读过,但并未真正实践),只要棋子的图案设计得当,只要很小的 LCD 就能完成显示工作。u8glib 正好内置了一个用 C 语言编写的国际象棋引擎,叫 "Little Rook Chess" (lrc), 文件是 chessengine.c. 那用这个 LCD 做国际象棋就是一个可行的项目。
u8glib 同时也支持 Arduino,在 Arduino 版本的例程里就有一个 Chess 项目。把 Arduino 代码移植成 C 代码是很容易的。
源代码
https://pastebin.mozilla.org/8855725
原理图
照片
上面是一个完整的对局,我执黑。
下棋时用 4 个键来控制走棋:
- Prev 键:前一个棋子;当处于选中棋子状态,选择前一个有效的走棋目标位置。
- Next 键:下一个棋子;当处于选中棋子状态,选择下一个有效的走棋目标位置。
- Select 键:选中棋子;当处于选中棋子状态,放下棋子。
- Back 键:如果已经选中一个棋子,按此键撤消选择。
- 别的功能有待探索和阅读源码。
Bug
- 按键的代码需要改进。
- LCD 的显示效果需要作进一步微调。
- 没有专用的 PCB 和外壳。
- LCD 自身的闹钟图标可以用来作下棋时的提醒功能?
后记
这场 hackathon 一开始,原本计划是做俄罗斯方块。于是花了很多时间修改一个现有的 AVR Tetris 程序。 他们的 LCD 是基于 KS0108。和我所用的 ST7565R 的有些不一样。但麻烦之处在于做俄罗斯方块需要把 LCD 旋转成 portrait 模式(垂直方向)使用。在 portrait 模式写文字不是很方便的,这涉及到取字模和对页的切换的一些算法。我在这上面花费了很多时间,始终无法达到想要的效果。于是在 1 月 3 日决定切换方案。改做国际象棋,因为 u8glib 内置了国际象棋的例子。
除了用 LCD 来做东西本身,另外我也想用 Raspberry Pi 和 FPGA 来直接驱动这种 LCD. 这个过程本身就是一个学习的过程。
这次的 hackathon 也得到一些教训,那就是:抓紧时间,选择方案时不要摇摆不定,应当机立断。比如,我明明知道一场 hackathon 的时间永远不够用,但还是在 2 号下午陪家人去公园(这个无法避免),晚上又去朋友 Matt 家参加 party,幸运的是在去的地铁上也在读代码,而且整个 party 的 80% 的时候也在写代码 :)
接下来,另一个任务是要把俄罗斯方块完成,配合上音乐效果,一定会很不错。
后来,SZDIY 成员 DF 专门为这个 LCD 设计了一款硬件,可以用 Arduino 编程。
Todo
- 用 Raspberry Pi 来驱动它。
- 为它写一个 Amforth 驱动。
- 用 FPGA 来驱动。