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

原理图

照片

black wins

上面是一个完整的对局,我执黑。

下棋时用 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 来驱动。