Arduino Nano 驱动 128x64 OLED 实时运行康威生命游戏 (元胞自动机)
元器件清单
| 元器件 | 数量 | 备注 |
|---|---|---|
| Arduino Nano (或 Uno) | 1 | 主控 |
| 128x64 OLED (SSD1306, I2C) | 1 | 0.96 英寸常见规格 |
| 半块无焊面包板 | 1 | 或全尺寸 |
| 跳线 | 4 | VCC, GND, SDA, SCL |
项目简介
在仅 128×64 像素的 OLED 上实时运行康威生命游戏——经典的二维元胞自动机。每个细胞只有生/死两种状态,遵循两条简单规则演化出复杂动态模式。项目展示了极简硬件跑迭代算法的魅力,适合学习数组操作、位运算优化、I2C 显示驱动。


背景知识:康威生命游戏规则
无限二维网格,每格为细胞,两种状态:存活(1) / 死亡(0)。每代同时更新:
- 诞生:死亡细胞周围恰好 3 个存活邻居 → 变为存活
- 存活:存活细胞周围有 2 或 3 个存活邻居 → 保持存活,否则死亡
邻居为周围 8 格(摩尔邻域)。简单规则产生振荡器、滑翔机、发射机等复杂结构。
硬件连接 (I2C)
| Arduino Nano | OLED (SSD1306) |
|---|---|
| 3.3V | VCC (确认模块支持 3.3V,部分需 5V) |
| GND | GND |
| A4 (SDA) | SDA |
| A5 (SCL) | SCL |
将两块半面包板背靠背拼接,形成紧凑“掌上电视”造型。
软件库依赖
- Adafruit GFX Library - 图形绘图核心
- Adafruit SSD1306 - OLED 驱动
库管理器搜索安装,或下载 ZIP 导入。完整代码见 GitHub 仓库。
关键代码片段
#include
#include
#include
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C // 可能是 0x3D,需扫描确认
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// 网格缓冲区:128x64 = 8192 bits = 1024 bytes
// 用 uint8_t 数组按位存储,节省内存
uint8_t grid[SCREEN_WIDTH * SCREEN_HEIGHT / 8];
uint8_t newGrid[SCREEN_WIDTH * SCREEN_HEIGHT / 8];
// 位操作宏
#define GET_GRID(x, y) (grid[(y)*SCREEN_WIDTH/8 + (x)/8] & (1 << ((x)%8)))
#define SET_GRID(x, y) (grid[(y)*SCREEN_WIDTH/8 + (x)/8] |= (1 << ((x)%8)))
#define CLR_GRID(x, y) (grid[(y)*SCREEN_WIDTH/8 + (x)/8] &= ~(1 << ((x)%8)))
void setup() {
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
for(;;); // 初始化失败死循环
}
display.clearDisplay();
randomSeed(analogRead(0));
initRandomGrid(); // 随机初始化
}
void loop() {
computeNextGeneration();
drawGrid();
delay(100); // 可调节演化速度
}
// 计算下一代:遍历每个细胞统计邻居
void computeNextGeneration() {
memcpy(newGrid, grid, sizeof(grid));
for (int y = 0; y < SCREEN_HEIGHT; y++) {
for (int x = 0; x < SCREEN_WIDTH; x++) {
int neighbors = countNeighbors(x, y);
bool alive = GET_GRID(x, y);
if (alive && (neighbors == 2 || neighbors == 3)) {
SET_GRID(x, y);
} else if (!alive && neighbors == 3) {
SET_GRID(x, y);
} else {
CLR_GRID(x, y);
}
}
}
memcpy(grid, newGrid, sizeof(grid));
}
// 环形边界条件 (环面拓扑)
int countNeighbors(int x, int y) {
int count = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (dx == 0 && dy == 0) continue;
int nx = (x + dx + SCREEN_WIDTH) % SCREEN_WIDTH;
int ny = (y + dy + SCREEN_HEIGHT) % SCREEN_HEIGHT;
if (GET_GRID(nx, ny)) count++;
}
}
return count;
}
void drawGrid() {
display.clearDisplay();
for (int y = 0; y < SCREEN_HEIGHT; y++) {
for (int x = 0; x < SCREEN_WIDTH; x++) {
if (GET_GRID(x, y)) display.drawPixel(x, y, WHITE);
}
}
display.display();
}
void initRandomGrid() {
for (int i = 0; i < sizeof(grid); i++) grid[i] = random(256);
}
优化技巧
- 位运算存储:128×64 网格仅占 1KB RAM (Nano 2KB),留足空间
- 双缓冲:`grid`/`newGrid` 避免就地更新破坏邻居计数
- 环形边界:取模运算实现环面拓扑,滑翔机不会撞墙消失
- 仅绘制存活像素:`drawPixel` 比填充矩形快
常见问题
- OLED 地址非 0x3C:运行 I2C 扫描器确认
- 显示乱码/花屏:检查拉高电阻 (模块通常板载)、供电稳定性
- 速度太慢:减少 `delay`、优化邻居计数查表法、用 U8g2 库替代
资源链接
制造工艺
- 用 Arduino UNO 和 OLED 显示屏制作经典 Pong 游戏:分步教程
- Arduino UNO 驱动 16x2 LCD 实现动画与小游戏
- Arduino 与智能手机打造智能电压表——简单易做的 DIY 项目
- Pixie:基于 Arduino Pro Mini 的 NeoPixel 智能腕表 - 极简 DIY LED 时钟
- Python 3 控制 Arduino UNO - 串口通信点亮内置 LED 入门教程
- Arduino 高速转速计 - 单次检测算法实测 6.5MHz 级响应,红外 + 红激光方案
- eDOT:基于 Arduino 的精密时钟与气象站,内置红外遥控
- 使用 Arduino Nano 和 RDA8057M 制作紧凑型 FM 收音机
- 基于 Arduino 与 Google Sheets 的实时 RFID 考勤系统
- 基于Arduino、Windows IoT与Azure云的高级跌倒检测系统
- 智能无钥匙门锁:Arduino + Android 蓝牙密码控制舵机开锁
- 信用卡大小 Arduboy 克隆机:Arduino Nano + I²C OLED 复古游戏机自制