1. 项目概述当单片机遇上创意一张会“呼吸”的地球名片最近在捣鼓合宙的Air001开发板这块板子虽然小巧但功能挺全价格也亲民很适合用来做一些有趣的创意项目。我就在想能不能用它做点不一样的东西比如一张“名片”——但不是那种纸质的而是一张能发光、能显示信息、甚至能互动的电子名片。这个想法最终落地成了“地球名片”核心就是用Air001驱动一块圆形OLED屏幕显示一个动态旋转的地球动画并可以切换显示个人信息比如姓名、联系方式、技能标签等。它不仅仅是一个静态的展示品更是一个融合了硬件编程、图形显示和创意设计的DIY作品无论是放在桌面上作为个性摆件还是在技术聚会中作为有趣的社交工具都能让人眼前一亮。这个项目的魅力在于它把冰冷的单片机技术和有温度的个性化表达结合在了一起。你不需要是嵌入式专家只要对Arduino生态有一些了解就能跟着一步步实现。整个过程会涉及到开发环境搭建、图形库的使用、动画帧的绘制、电源管理优化等实用知识点。我会把从硬件选型、软件编写到外壳制作的完整流程以及我踩过的坑和优化技巧都详细分享出来。你会发现用几十块钱的成本和一下午的时间创造出一个独一无二的、属于自己的“数字身份标识”是一件非常有成就感的事。2. 核心硬件选型与电路设计思路2.1 主控芯片为什么是合宙Air001在众多单片机中选中合宙Air001是经过一番考量的。首先它基于ARM Cortex-M0内核主频48MHz性能对于驱动OLED播放简单动画绰绰有余远比传统的8位单片机如ATmega328P要流畅。其次它原生支持Arduino IDE开发这对于广大爱好者来说极大地降低了入门门槛丰富的社区库资源可以直接调用。最后也是非常重要的一点它的功耗控制做得不错有多种低功耗模式这对于一个可能依赖电池供电的“名片”设备来说至关重要。相比ESP8266/ESP32等Wi-Fi模块Air001在单纯实现显示功能时没有无线模块带来的额外功耗和复杂度更纯粹、更省电。注意Air001有多个封装和版本对于这个项目推荐使用“Air001核心板”或“Air001最小系统板”。它们集成了USB转串口芯片只需一根USB线即可完成供电和程序下载非常方便。避免使用只有芯片的模块那样需要自己外接调试器和电源会增加初学者的焊接和调试难度。2.2 显示核心圆形OLED屏幕的选型与驱动“地球名片”的视觉灵魂就在于这块圆形OLED屏。我选择的是0.96英寸、128x64分辨率的SSD1306驱动芯片的OLED屏并且特意选了圆形外观的版本。为什么是OLED而不是LCD首要原因是OLED每个像素自发光显示黑色时完全不发光因此对比度极高显示星空、地球等深色背景图案时效果极其震撼且可视角度广。其次OLED屏幕通常非常薄有利于整个作品做得小巧精致。SSD1306是一种非常通用的OLED驱动芯片有I2C和SPI两种通信接口。为了节省IO口和简化接线本项目强烈推荐使用I2C接口的版本。Air001的I2C接口使用起来很简单只需要连接4根线VCC, GND, SCL, SDA即可。在软件上有现成的库如Adafruit_SSD1306和Adafruit_GFX可以极大地简化图形绘制工作。接线示意图关键部分Air001核心板 - I2C OLED屏 3.3V/VCC - VCC GND - GND PB6 (默认I2C SCL) - SCL PB7 (默认I2C SDA) - SDA这里需要注意电平匹配。Air001和大多数OLED屏都是3.3V逻辑电平直接连接即可。如果你的屏幕是5V的可能需要电平转换模块不过目前市面上常见的0.96寸OLED屏基本都已兼容3.3V。2.3 电源与交互设计如何让它更“好用”一个完整的作品不能只是插着USB线工作。我设想了两种使用场景一是桌面常亮展示二是随身携带偶尔亮屏。因此电源部分我设计了两套方案。方案AUSB供电桌面模式最简单直接使用一根Micro-USB或Type-C数据线取决于你的核心板接口连接充电宝、电脑或手机充电器。为了美观可以使用一根短的弯头数据线。在这种模式下我们可以让程序以全速运行展示最流畅的动画。方案B电池供电便携模式这是实现“名片”功能的关键。我选用了一颗小巧的3.7V、500mAh的锂电池搭配一个微型充电管理模块如TP4056和一个升压模块。充电模块负责给电池充电升压模块将电池的3.7V稳定升压到5V或3.3V给整个系统供电。这里有一个关键技巧Air001的工作电压范围是2.0V-3.6V如果升压到5V需要再通过一个低压差线性稳压器LDO降到3.3V更优的方案是选择一款支持3.3V输出的升压模块直接输出3.3V这样效率更高电路更简洁。关于交互为了让名片不只是“看”还能“操作”我添加了一个轻触开关触摸按键或微动开关。实现的功能很简单短按切换显示模式如地球动画模式、个人信息模式、二维码模式等长按进入深度睡眠此时整机电流可以降到极低几十微安再次短按唤醒。这个设计极大地延长了电池续航也增加了产品的实用感和科技感。3. 软件开发与环境搭建全流程3.1 开发环境配置让Air001在Arduino IDE中跑起来合宙Air001对Arduino IDE的支持非常友好。首先你需要安装最新版的Arduino IDE1.8.x或2.0.x均可。然后按照以下步骤添加开发板支持打开Arduino IDE点击“文件” - “首选项”。在“附加开发板管理器网址”中填入合宙的板支持地址https://cdn.openluat.com/arduino/package_airm2m_cn_index.json如果地址有变请以合宙官方文档为准。可以点击输入框右侧的图标添加多个URL。点击“工具” - “开发板” - “开发板管理器...”。在搜索框中输入“Air001”你会找到“AirMCU Air001 Boards”。点击并安装它。安装完成后在“工具” - “开发板”列表中就能选择“Air001”了。接下来需要安装必要的库。点击“项目” - “加载库” - “管理库...”搜索并安装以下两个库Adafruit SSD1306用于驱动OLED屏幕。Adafruit GFX Library这是前一个库依赖的图形核心库提供了画点、画线、画圆、显示文字等基础函数。安装时务必注意库的版本兼容性。如果遇到编译错误可以尝试安装稍旧一点的稳定版本。3.2 图形与动画编程绘制旋转的地球这是整个项目的软件核心也是最有趣的部分。我们要在128x64的圆形区域内让一个地球图案旋转起来。直接绘制一张地球的位图并旋转对M0内核来说计算量较大且效果生硬。我采用了一种更取巧也更有“代码创作感”的方法绘制帧动画。第一步准备地球帧图找一张清晰的地球平面展开图墨卡托投影。使用图像处理软件如Photoshop、GIMP甚至是在线的Pixlr将图片裁剪、缩放到一个合适的大小比如宽度128像素高度若干根据你的动画帧数决定。将这张长条图垂直切割成若干份例如8帧每一帧就是地球旋转一个角度时的画面。注意由于是球体左右边缘需要能衔接上形成无缝循环。使用取模软件如LCD Assistant将每一帧的图片转换为C语言数组。在转换时设置输出格式为“垂直字节”扫描方式为“垂直”这样生成的数组才能被Adafruit_SSD1306库正确识别和显示。第二步编写动画显示函数在Arduino代码中你会得到一个二维数组比如const unsigned char earth_frames[8][1024]假设8帧每帧128*64/81024字节。动画循环的逻辑很简单#include Wire.h #include Adafruit_GFX.h #include Adafruit_SSD1306.h #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 // Reset pin not used Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, OLED_RESET); // 假设你的地球帧数据放在这里 extern const unsigned char earth_frames[8][1024]; int currentFrame 0; unsigned long lastFrameTime 0; const int frameInterval 100; // 每帧间隔100毫秒 void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // 初始化OLEDI2C地址通常是0x3C display.clearDisplay(); } void loop() { if (millis() - lastFrameTime frameInterval) { lastFrameTime millis(); // 清除显示或者采用局部刷新优化 display.clearDisplay(); // 绘制当前帧的地球 // 注意drawBitmap函数参数 (x坐标, y坐标, 位图数据, 宽, 高, 颜色) // 由于我们的屏幕是圆形的需要计算位置使其居中 int xOffset (SCREEN_WIDTH - 64) / 2; // 假设地球图宽64 int yOffset (SCREEN_HEIGHT - 64) / 2; display.drawBitmap(xOffset, yOffset, earth_frames[currentFrame], 64, 64, SSD1306_WHITE); // 可以在地球周围画一些星星或装饰文字 drawStars(); display.display(); // 将缓冲区内容发送到屏幕 // 切换到下一帧 currentFrame (currentFrame 1) % 8; } // 这里可以加入按键检测代码用于切换模式 checkButton(); }通过调整frameInterval可以改变地球旋转的快慢。drawBitmap函数是性能关键它直接将预编译的位图数据写入显示缓冲区速度非常快。3.3 多模式切换与信息显示逻辑一个模式只显示动画未免有些单调。我们可以设计2-3个显示模式通过按键循环切换。模式1地球动画模式。如上所述展示旋转的地球作为主视觉。模式2个人信息模式。清屏后使用display.setTextSize(),display.setCursor(),display.println()等函数分多行显示你的姓名、职位、邮箱、电话等信息。为了美观可以使用不同大小的字体并添加简单的分隔线。模式3二维码模式。这个模式非常实用。你可以生成一个包含你个人主页、电子名片vCard或社交媒体链接的二维码。同样使用取模软件将二维码图片转换为位图数组然后在这个模式下调用drawBitmap显示。别人用手机一扫就能直接保存你的联系方式或访问你的主页。按键处理逻辑示例#define BUTTON_PIN PA0 // 假设按键接在PA0下拉电阻到GND按键另一端接3.3V int displayMode 0; // 0:地球1:信息2:二维码 bool buttonPressed false; unsigned long pressStartTime 0; void checkButton() { int buttonState digitalRead(BUTTON_PIN); if (buttonState HIGH !buttonPressed) { // 按键刚被按下 buttonPressed true; pressStartTime millis(); } else if (buttonState LOW buttonPressed) { // 按键被释放 buttonPressed false; unsigned long pressDuration millis() - pressStartTime; if (pressDuration 1000) { // 短按切换模式 displayMode (displayMode 1) % 3; // 假设有3种模式 switchMode(displayMode); // 切换到新模式的函数 } else { // 长按进入睡眠 enterSleepMode(); } } } void switchMode(int newMode) { display.clearDisplay(); switch(newMode) { case 0: // 初始化地球动画模式 currentFrame 0; break; case 1: // 显示个人信息 display.setTextSize(1); display.setCursor(10, 10); display.println(John Doe); // ... 更多信息 break; case 2: // 显示二维码位图 display.drawBitmap(x, y, qr_code_bitmap, width, height, WHITE); break; } display.display(); }这个逻辑实现了短按切换、长按休眠的交互代码结构清晰易于扩展更多模式。4. 结构设计与组装工艺4.1 外壳设计与3D打印为了让“地球名片”从一个开发板堆叠的“飞线”原型变成一个可以放在口袋里的精致产品一个定制的外壳必不可少。我使用Fusion 360进行建模设计思路如下分层结构外壳分为前盖、中框和后盖三部分。前盖开一个精确的圆形窗口刚好露出OLED屏幕窗口边缘做一点倒角让观感更柔和。中框用于固定Air001核心板、电池和按键内部设计立柱和卡槽确保各部件稳固不晃动。后盖密封留出USB充电口和复位键的小孔。尺寸精确建模前务必用游标卡尺精确测量每一个元件的尺寸包括开发板的长宽高、螺丝孔位、OLED屏的显示区域和外部边框、电池的厚度和宽度等。在软件中建模时关键配合尺寸要留出0.2-0.3mm的间隙防止打印误差导致装不进去。美学考虑在前后盖的边缘可以设计一些装饰性的条纹或镂空。我在地球名片的背面设计了一个凹陷的、类似经纬线网的纹理呼应“地球”主题。打印材料可以选择白色、黑色或半透明的PLA效果都不错。设计完成后导出为STL文件用切片软件如Cura进行切片。打印参数建议层高0.2mm保证表面光洁度填充率15%-20%兼顾强度和重量支撑仅针对外壳内部可能悬空的结构如固定柱的顶部生成支撑外表面尽量避免支撑以减少后期打磨工作量。4.2 内部布局与焊接组装打印好的外壳部件需要稍作打磨特别是接口和卡扣处确保能顺滑装配。接下来是内部的“总装”固定核心板使用M2规格的尼龙螺丝和螺母将Air001核心板固定在中框的立柱上。切记螺丝不要拧得过紧防止压坏PCB或导致短路。可以在PCB和螺丝之间加一个小垫片。连接OLED屏使用4根细长的杜邦线母对母或直接焊接排针将OLED屏与Air001的对应引脚连接。连接后用一点点热熔胶或双面泡棉胶将OLED屏从背面粘在前盖的窗口内侧确保其显示区域正对窗口。安装电池与充电模块将微型锂电池用双面胶固定在中框的空余位置。TP4056充电模块可以用热熔胶固定。连接时注意电池正负极接到充电模块的B和B-充电模块的OUT和OUT-接到升压模块的输入升压模块的输出3.3V接到Air001的3.3V和GND。务必反复检查正负极接反极易烧毁模块安装按键选用一款小巧的贴片轻触开关或带帽的微动开关。将开关焊接在一小块洞洞板上再用导线引到Air001的指定GPIO和GND。在中框对应位置开孔将按键帽部分伸出。理线与测试用扎带或胶带整理好内部导线避免杂乱。在完全合盖之前先接上USB线测试所有功能充电指示灯是否正常、按键切换是否灵敏、各显示模式是否完好。确认无误后再用螺丝将前盖、中框、后盖锁紧。实操心得焊接和组装时养成“一步一测试”的习惯。比如焊好OLED屏后先上传一个简单的测试程序看看屏幕是否点亮接好电池后先用万用表测量升压模块输出电压是否正确。这样可以快速定位问题避免全部装好后才发现故障拆装非常麻烦。5. 功耗优化与性能调试实录5.1 深度睡眠与唤醒实战对于电池供电的设备功耗是生命线。Air001提供了多种低功耗模式我们主要使用停止模式Stop Mode。在这种模式下核心时钟停止大部分外设关闭仅保留少量唤醒源如外部中断的功能功耗可以降到10微安以下。实现长按进入睡眠的代码如下#include Air001.h void enterSleepMode() { display.clearDisplay(); display.display(); display.ssd1306_command(SSD1306_DISPLAYOFF); // 关闭OLED显示这是省电大头 // 配置唤醒引脚假设按键接在PA0同时也是唤醒引脚 pinMode(PA0, INPUT_PULLDOWN); // 配置为下拉输入确保电平稳定 // 注意Air001的唤醒引脚通常是特定的如PA0需查阅数据手册确认 // 进入停止模式通过PA0上升沿唤醒即按键按下产生高电平 Air001.stop(WAKEUP_PIN_PA0, RISING); // 单片机唤醒后会从这里开始继续执行相当于一次复位后的setup() // 但RAM中的数据会保留如果设置了寄存器保持 setup(); // 重新初始化外设 // 或者你可以设计一个更精细的唤醒恢复函数只初始化必要部分 } void setup() { // 正常的初始化代码... // 注意唤醒后也会执行这里所以初始化代码需要是幂等的执行多次效果相同 }这里有几个关键点关闭显示屏SSD1306屏幕本身也有功耗进入睡眠前一定要发送关闭显示的命令。唤醒源配置必须查阅Air001的参考手册确认你使用的引脚如PA0是否支持作为唤醒源以及对应的唤醒模式。唤醒后处理唤醒后程序会从setup()开始执行吗这取决于低功耗模式的配置。有些模式下程序会从进入睡眠的下一句开始执行有些则像复位一样从头开始。需要测试验证。最稳妥的方法是在setup()开始时检查一个在睡眠时被保持的变量比如RTC备份寄存器或SRAM中特定标记来判断是冷启动还是唤醒启动从而决定是全新初始化还是恢复现场。5.2 显示刷新率与动画流畅度优化在128x64的分辨率下播放动画如果刷新率太低会感觉卡顿太高又会增加功耗。我们需要找到一个平衡点。优化技巧1局部刷新地球动画的背景是黑色的星空。我们不需要在每一帧都清除整个屏幕clearDisplay()再重画所有像素。可以只刷新地球图案所在的矩形区域。Adafruit_GFX库本身不直接支持局部刷新但我们可以通过直接操作SSD1306的底层缓冲区来实现。不过对于8帧的简单动画全屏刷新的开销在48MHz的Air001上是可以接受的。一个更简单的折中方法是在绘制新一帧地球前用黑色矩形块覆盖掉上一帧地球的位置然后再画新帧。这比全屏清除的计算量小。优化技巧2计算与绘制分离在loop()函数中我们使用millis()进行非阻塞延时来控制帧率。确保所有图形计算和drawBitmap调用都在一帧时间内完成。如果发现计算导致帧率下降比如绘制非常复杂的矢量图形就要考虑将一些预先计算好的结果如坐标、位图索引存储在数组里运行时直接读取。优化技巧3降低通信频率I2C通信本身也有开销。确保display.display()函数只在所有绘制命令完成后调用一次将整个缓冲区一次性发送到屏幕而不是画一点发一点。通过逻辑分析仪或简单的计时代码可以测量每帧的实际耗时unsigned long frameStart micros(); // ... 执行绘制代码 ... display.display(); unsigned long frameEnd micros(); Serial.print(Frame time (us): ); Serial.println(frameEnd - frameStart);确保这个时间小于你设定的frameInterval例如100ms留出足够的余量给其他任务如按键扫描。5.3 常见问题与排查技巧在制作过程中你可能会遇到以下问题这里给出排查思路问题现象可能原因排查步骤与解决方案OLED屏幕不亮1. 电源接反或电压不对。2. I2C地址错误。3. 初始化代码有误。1. 用万用表测量VCC和GND之间电压是否为3.3V。2. 尝试常见的I2C地址0x3C和0x3D。可以用I2C扫描程序确认。3. 检查begin()函数参数确认复位引脚配置如果不用就设为-1。地球动画显示错位或花屏1. 取模软件设置错误。2.drawBitmap函数参数错误。3. 屏幕缓冲区溢出。1. 确认取模时设置正确宽度、高度、垂直扫描、字节垂直。2. 检查drawBitmap中的x, y坐标以及位图数据的宽高是否与数组声明一致。3. 确保位图数组大小字节数等于(width * height) / 8。按键切换不灵敏或无效1. 引脚配置模式错误应用上拉/下拉。2. 按键消抖处理不当。3. 长按/短按判断阈值不合理。1. 硬件上按键一端接GPIO另一端接VCC则GPIO应配置为INPUT_PULLDOWN接地则用INPUT_PULLUP。2. 在代码中增加简单的消抖如检测到按下后延时20ms再确认状态。3. 调整长按判断的阈值如1000ms使其符合操作习惯。电池续航极短1. 未进入低功耗模式。2. 外围电路如LED、电平转换芯片静态功耗大。3. 电池容量虚标或老化。1. 确认睡眠函数被正确调用用万用表串联测量睡眠时的整机电流应低于100微安。2. 检查电路断开所有非必要外设如调试用的LED。3. 对电池进行充放电测试或更换新电池。3D打印外壳装配过紧或过松1. 建模尺寸公差设置不当。2. 打印材料收缩率影响。3. 打印精度不足。1. 重新测量实物修改模型配合处留出0.2-0.4mm间隙。2. 了解所用PLA/ABS的收缩率在切片软件中设置补偿。3. 校准3D打印机检查皮带松紧和步进电机电流。最后一点个人体会DIY项目的乐趣不仅在于最终成果更在于解决问题的过程。这个“地球名片”从构思到完成我反复调整了三次电路布局重写了两次动画生成脚本打了四版外壳才达到满意的效果。每一次调试和修改都加深了对硬件、软件和结构设计的理解。当你亲手做出一个既能展示技术又能表达个性的作品并且看到别人对它产生兴趣时那种满足感是无与伦比的。不妨从这个小项目开始大胆尝试把更多有趣的创意变成现实。