资讯动态

用Micro:bit制作二进制时钟:从原理到RTC模块应用

发布时间:2026/8/19 12:13:10 来源:尧图企业网站定制
1. 项目概述用Micro:bit打造一个极简的二进制时钟如果你手头有一块Micro:bit并且对嵌入式编程或者时间显示有独特的兴趣那么做一个“Micro Binary Clock”绝对是个能让你乐在其中的小项目。这玩意儿听起来有点极客但做起来其实挺直观的。它的核心就是用Micro:bit的5x5 LED点阵以一种非传统的方式——二进制——来显示当前的时间。你不再看“12:34”这样的数字而是看几行由亮灭LED组成的二进制码然后在大脑里快速换算成小时和分钟。这不仅仅是一个时钟更像是一个锻炼逻辑思维和快速心算的小玩具摆在桌面上也足够酷。这个项目非常适合刚接触Micro:bit和MakeCode图形化编程的初学者或者想给孩子找一个有趣编程启蒙项目的家长。它不涉及复杂的电路焊接只需要一块Micro:bit主板和一根USB数据线。通过这个项目你能深入理解二进制数的表示方法、学习如何用代码处理时间数据、以及如何将抽象的数据映射到具体的LED显示上。整个过程在MakeCode的积木块编程环境中就能完成门槛低成就感高。当然如果你愿意深入我们也会探讨如何为它加入一个独立的RTC实时时钟模块解决Micro:bit断电后时间丢失的问题让它变成一个真正实用的、可以长期运行的桌面摆件。2. 核心原理与设计思路拆解2.1 二进制时间表示法从数字到亮灭的转换要让Micro:bit显示二进制时间首先得搞清楚我们怎么把常规的“时:分”转换成二进制。这里我们采用最直观的“行列分离”法。以24小时制为例小时0-23和分钟0-59分别用两行或两列二进制数来表示。一种常见的方案是使用Micro:bit点阵的左侧3列表示小时右侧2列表示分钟。但5x5的点阵总共只有25个LED我们需要更高效的编码方式。我更喜欢另一种布局用最上面两行共10个LED表示小时下面三行共15个LED表示分钟。具体来说小时0-23需要5个二进制位2^532 23。我们可以用第一行的5个LED表示小时的“个位”低5位但2310111已经需要5位了。更合理的是小时用5个二进制位但我们需要10个LED来直观显示吗不我们可以用“数值直接对应LED亮灭”的方式。例如小时数18二进制是10010。我们可以约定点阵的某一行比如第0行的5个LED从左到右分别代表二进制的第4位到第0位即16, 8, 4, 2, 1。那么18162就点亮最左边16和右数第二个2的LED。分钟0-59需要6个二进制位2^664 59。我们可以用两行来显示例如第2行和第3行每行5个LED但只使用前3个和后3个组合起来表示6位二进制数。分钟数37二进制是100101就可以分别点亮对应位置的LED。这样设计的好处是逻辑清晰每个LED的亮灭直接对应二进制数的一个特定权值1,2,4,8,16...观看者只需要将亮着的LED对应的权值相加就能得到时间。当然这对观看者的心算能力是个小挑战但这也正是乐趣所在。2.2 方案选型纯软件计时 vs. 外置RTC模块这是本项目的一个关键决策点决定了时钟的实用性和复杂度。纯软件计时基于系统运行时间这是最简单的方法直接使用Micro:bit内置的系统计时器。在MakeCode中有“运行时间(ms)”的积木块。我们可以让程序在启动时记录一个开始时间戳然后通过不断计算当前运行时间与开始时间的差值来推算出已经过去的“秒”数再换算成时、分、秒。优点是无需任何额外硬件成本为零编程简单。缺点也非常明显一旦Micro:bit断电或重启这个“开始时间”就重置了时间会归零。它只能作为一个相对计时器或者需要每次上电手动“对时”的时钟实用性大打折扣。外置RTC实时时钟模块要做一个真正“守时”的时钟必须引入RTC模块。像DS1302、DS3231这类芯片自带一个精度很高的晶体振荡器和计时电路即使主控板Micro:bit完全断电它们靠一颗小小的纽扣电池也能继续走时几年都不需要校准。Micro:bit通过I2C或SPI等通信协议与RTC模块对话读取当前的真实年月日时分秒。优点是时间准确、断电保持是实用化产品的标配。缺点是需要额外购买模块虽然很便宜并且接线和编程会稍微复杂一点需要了解基本的I2C通信知识。对于初学者我建议先从“纯软件计时”版本做起快速验证显示逻辑获得成就感。当你对项目产生更大兴趣希望它成为一个真正的桌面时钟时再升级到“外置RTC”版本。网络热词中提到的“stm32f103 standby rtc唤醒”等其实是更复杂的低功耗嵌入式系统的设计思路对于Micro:bit这个级别的项目我们直接使用现成的RTC模块即可无需涉及芯片级的低功耗模式配置。2.3 Micro:bit显示策略优化Micro:bit的5x5 LED点阵资源有限如何清晰、稳定地显示二进制时间是个挑战。直接同时显示小时和分钟的二进制码可能会让点阵看起来过于拥挤信息难以快速读取。我采用的策略是分时复用显示默认状态同时显示小时和分钟的二进制编码。例如顶部一行5个LED显示小时底部两行共10个LED显示分钟。但这样可能还是太密。交替显示状态推荐让小时和分钟轮流显示。比如前3秒点阵只显示小时的二进制码用特定的一行或一个图案指示这是“小时”后3秒点阵只显示分钟的二进制码。这样每个数字都有更大的显示空间甚至可以配合一个闪烁的LED作为冒号分隔符的替代提示当前显示的是小时还是分钟。交互切换状态通过摇晃Micro:bit加速度计或按下A/B键手动切换显示模式显示小时/显示分钟/显示秒/全部显示。这增加了可玩性。在编程实现上我们需要一个主循环不断根据当前模式更新LED点阵的显示。同时为了确保时间更新的准确性更新时间的基础无论是读取系统运行时间还是查询RTC应该放在一个稳定的时间中断里或者至少保证主循环的执行周期是稳定且已知的。3. 基于MakeCode的纯软件版实现详解我们先从不需要额外硬件的版本开始这是最快上手的方式。3.1 项目初始化与变量定义打开MakeCode for Micro:bit在线编辑器新建一个项目。首先我们需要定义几个核心变量startTime记录程序开始运行的绝对时间戳毫秒。这个值在开机时设置一次。currentTotalSeconds计算出的从startTime开始到现在经过的总秒数。hours,minutes,seconds从总秒数中分解出的小时、分钟、秒。displayMode一个数字变量用于控制当前显示模式0显示小时1显示分钟2显示全部。在MakeCode的“变量”类别中创建这些变量。在“当开机时”积木块中我们需要初始化startTime。但是注意Micro:bit的“运行时间(ms)”积木块返回的是本次上电后的运行毫秒数它本身就是一个从0开始递增的值。因此我们其实不需要自己记录一个startTime直接以程序启动为时间原点即可。但为了做出一个可以“设置初始时间”的时钟我们可以把startTime理解为一个“时间偏移量”。例如我们希望时钟从“8点30分0秒”开始走那么我们可以计算8小时30分钟等于多少毫秒然后让currentTotalSeconds的计算公式为(运行时间 时间偏移量) / 1000。不过为了第一版的简单我们让时钟从“0时0分0秒”开始。那么currentTotalSeconds就直接等于运行时间(ms) / 1000。3.2 时间计算与分解逻辑在“无限循环”中我们需要不断执行以下步骤计算总秒数currentTotalSeconds运行时间(ms)/ 1000。这里注意运行时间(ms)返回的是整数除以1000后在MakeCode中会自动进行整数除法得到的是整数秒。如果你需要更精确到秒的小数部分需要稍复杂的处理但作为时钟整数秒足够了。分解出时、分、秒hourscurrentTotalSeconds/ 3600 的整数部分。因为一天24小时我们还需要对它取模24hours (currentTotalSeconds/ 3600) % 24。minutes (currentTotalSeconds% 3600) / 60 的整数部分。secondscurrentTotalSeconds% 60。在MakeCode中需要使用“数学”类别里的除法、取余数等积木块来组合实现这些公式。这是一个很好的练习能让你理解计算机是如何处理时间和进制转换的。3.3 二进制转换与LED映射函数这是项目的核心趣味点。我们需要编写一个函数输入一个十进制数比如小时或分钟和一个起始坐标函数会在点阵的特定位置用LED亮灭显示出这个数的二进制形式。例如我们创建一个函数plotBinary它接受三个参数num要显示的数row起始行startBit起始二进制位权值通常是1。假设我们决定在一行内显示最多5位二进制权值1,2,4,8,16。函数的逻辑是用一个循环从最低位权值1或最高位开始检查。对于每一个权值bitValue1,2,4,8,16计算num和bitValue进行“按位与”操作的结果。在MakeCode中没有直接的按位与积木但我们可以用数学方法判断如果(num / bitValue) % 2等于1那么就说明该二进制位是1。如果该位是1则点亮对应位置的LED如果是0则熄灭。LED的x坐标可以根据权值的顺序来排列。实际上MakeCode提供了“创建函数”的功能但逻辑判断需要仔细搭建。一个更直观但不那么“二进制”的方法是用多个“如果...那么...”积木分别判断数字是否大于等于16、8、4、2、1然后依次点亮LED。但这失去了通用性。我们可以追求一个更优雅的解法使用循环和数学运算。这里分享一个我调试时的心得由于Micro:bit的点阵坐标(x,y)中x从左到右是0到4y从上到下是0到4。如果我们想让二进制的高位在左低位在右那么对于一个5位的二进制数我们可以让权值16对应x0权值8对应x1依此类推权值1对应x4。在循环中我们可以用一个索引i从0到4对应的权值bitValue就是2^(4-i)即16,8,4,2,1。然后判断(num / bitValue) % 2是否为1。3.4 显示控制与用户交互有了plotBinary函数我们就可以在无限循环中根据displayMode变量来控制显示什么。如果displayMode为0调用plotBinary(hours, 0, 16)在顶部行显示小时。如果displayMode为1调用plotBinary(minutes, 2, 32)在中间行显示分钟分钟需要6位我们可以占用两行或者只显示低5位高1位用另一行的一个LED表示。如果displayMode为2可以同时调用两个函数或者用更紧凑的布局。为了切换模式我们可以给按键A和B分配功能“当按钮A被按下时”设置displayMode (displayMode 1) % 3。这样每按一次A模式就在0、1、2之间循环。“当按钮B被按下时”可以设计为“设置时间”模式但纯软件版设置时间意义不大因为一重启就没了。我们可以让B键用于切换12/24小时制或者点亮所有LED做一个自检。最后在无限循环的末尾一定要加一个“暂停(100毫秒)”之类的积木。这非常重要它有两个作用第一降低循环速度让显示稳定不闪烁第二让Micro:bit的系统有时间处理其他后台任务如按键检测。如果没有这个暂停循环会跑得太快可能导致系统响应迟缓甚至看起像卡死。注意纯软件版的时间依赖于Micro:bit内部时钟的精度。这个时钟精度一般可能会每天有几秒到几十秒的误差不适合用于对时间要求严格的场合。它的主要价值在于演示和娱乐。4. 升级版集成DS3231 RTC模块实现精准守时要让你的二进制时钟变成一个可靠的计时工具添加RTC模块是必由之路。DS3231是一款非常流行且高精度的I2C接口RTC模块价格低廉自带温度补偿年误差可以控制在几分钟内。4.1 硬件连接与准备工作你需要准备一块Micro:bit。一个DS3231 RTC模块。若干杜邦线母对母。可选一颗CR2032纽扣电池用于在Micro:bit断电时给DS3231供电保持计时。连接非常简单因为Micro:bit和DS3231都支持I2C通信DS3231的VCC-Micro:bit的3V引脚。DS3231的GND-Micro:bit的GND引脚。DS3231的SDA数据线-Micro:bit的P20引脚这是Micro:bit的I2C SDA默认引脚。DS3231的SCL时钟线-Micro:bit的P19引脚这是Micro:bit的I2C SCL默认引脚。实操心得连接时务必确认线序正确电源正负极接反可能会损坏模块。建议先用USB给Micro:bit供电连接好后再上电。DS3231模块上通常有一个电池座记得装上纽扣电池这样即使拔掉Micro:bit的USB线时间也不会丢失。4.2 在MakeCode中添加RTC扩展库MakeCode默认不包含DS3231的驱动我们需要手动添加社区开发的扩展库。在MakeCode编辑器中点击“扩展”按钮或“高级”-“扩展”。在搜索框中输入“DS3231”通常会找到由“Tinkercademy”或类似作者提供的扩展。点击添加它。添加成功后在积木区会出现一个新的“DS3231”类别里面包含了设置时间、读取时间等积木块。这个扩展库封装了与DS3231通信的底层细节我们只需要调用几个简单的积木块就能完成所有操作大大降低了难度。4.3 初始化RTC与时间设置程序程序的第一步是初始化并设置RTC的时间。我们需要编写一个“对时”函数或者只在第一次使用时设置。在“当开机时”积木块中首先可以加入一个判断比如“如果按钮A和B同时被按下则进入设置模式”。这样我们不需要每次都修改代码来对时。在设置模式下我们可以用一组固定的值来设置RTC。例如使用“DS3231设置日期时间”积木块将年、月、日、星期、时、分、秒都设置成我们想要的初始值。注意DS3231的“星期”需要根据日期正确计算扩展库通常提供了周一到周日的选项。在正常模式下直接跳过设置程序会直接从DS3231读取当前时间。一个更友好的做法是开机后先尝试从DS3231读取时间如果读取失败比如第一次使用模块未初始化则自动用一个默认时间初始化它或者通过串口调试工具来设置。但在MakeCode的积木环境下实现复杂的串口命令解析比较麻烦所以用按键组合触发设置是最简单可靠的。4.4 修改主循环从RTC读取时间升级了RTC后我们主循环里的时间来源就变了。不再需要计算运行时间而是直接调用“DS3231读取秒”、“DS3231读取分钟”、“DS3231读取小时”等积木块。修改后的无限循环结构如下读取DS3231的小时、分钟、秒分别存入hours,minutes,seconds变量。注意DS3231返回的小时可能是24小时制也可能是12小时制带AM/PM标志这取决于你之前设置的模式。建议统一设置为24小时制处理起来更简单。根据displayMode变量的值调用我们之前写好的plotBinary函数在点阵上显示对应的二进制时间。处理按键事件切换显示模式。暂停(100毫秒)或更长的时间比如500毫秒。因为现在时间是从外部模块读取的不需要非常频繁地更新每秒更新几次2-10次对于时钟显示来说已经足够流畅还能更省电。现在你的二进制时钟就拥有了“记忆”。拔掉USB线过几天再插上它显示的还是正确的时间前提是DS3231的纽扣电池有电。5. 显示优化与高级功能探索基础功能实现后我们可以让这个时钟变得更聪明、更美观。5.1 解决5x5点阵显示信息过载的问题同时显示小时和分钟的二进制可能需要最多11个LED会让5x5点阵非常拥挤识别困难。除了之前提到的交替显示还可以考虑以下优化压缩编码对于分钟0-59我们真的需要6个独立的LED吗可以尝试一种“混合进制”的简化表示。例如用两个数字来表示分钟十位0-5和个位0-9。十位用3个二进制位可表示0-7表示个位用4个二进制位可表示0-15表示。这样总共需要7个LED但逻辑上更符合人类的阅读习惯先看十位再看个位。当然这需要观看者进行两次二进制解码。利用LED亮度Micro:bit的LED可以调节亮度0-255。我们可以用高亮度表示“1”用低亮度甚至熄灭表示“0”。但亮度差异在光线充足的环境下可能不明显。引入滚动动画当时间变化时特别是小时或分钟的二进制表示中某一位从0变1或从1变0时可以让对应的LED有一个短暂的闪烁或亮度渐变动画提示用户时间发生了变化增加可读性和趣味性。5.2 添加整点报时或灯光提醒功能一个贴心的时钟应该有提醒功能。我们可以利用Micro:bit的蜂鸣器通过引脚P0连接或者简单的LED图案来实现。整点报时在程序中判断当minutes和seconds都为0时即整点触发一个动作。可以是用音乐积木块播放一小段简单的旋律或者让所有LED闪烁几次。自定义闹钟定义一组闹钟时间时、分。在主循环中不断检查当前时间是否与任何一个闹钟时间匹配。如果匹配则触发提醒。闹钟时间可以硬编码在程序里也可以通过按键组合来设置并保存在Micro:bit的“非易失性存储”中使用“存储”扩展这样断电后闹钟设置也不会丢失。灯光提醒比如在距离整点还有5分钟时让点阵四角的LED开始缓慢闪烁像一种安静的视觉提醒。5.3 通过蓝牙与手机App同步时间进阶这是真正让项目变得“现代化”的一步。Micro:bit支持蓝牙低功耗BLE。我们可以编写一个简单的手机App可以使用MIT App Inventor等图形化工具或者利用现有的串口调试App通过蓝牙向Micro:bit发送当前手机的时间从而校准RTC。实现思路在MakeCode中添加“蓝牙”扩展。配置Micro:bit的蓝牙服务例如创建一个用于接收时间数据的特征值Characteristic。编写程序当通过蓝牙接收到一串格式化的时间数据如“2024,5,27,14,30,0”代表2024年5月27日14点30分0秒时解析这串数据并调用DS3231的设置函数来校准时间。在手机上找一个支持蓝牙串口通信的App手动输入时间并发送或者更好的是写一个能自动获取手机系统时间并发送的小脚本。这个功能实现起来有一定复杂度涉及到蓝牙协议和数据解析但它彻底解决了手动对时的麻烦实现了时钟的自动校准极大地提升了产品的用户体验和科技感。6. 常见问题、调试技巧与项目总结6.1 硬件连接与通信故障排查问题1连接DS3231后Micro:bit点阵上显示一个“哭脸”或者程序没反应。检查电源首先确认VCC和GND是否接反或接触不良。用万用表测量DS3231的VCC和GND之间是否有3V左右的电压。检查I2C线路确认SDA和SCL是否分别接到了P20和P19。可以尝试交换一下这两根线虽然标准不能换但偶尔接触不良时试试。检查扩展库确认添加的DS3231扩展库是兼容你的Micro:bit版本的。有时社区扩展库更新不及时可以尝试搜索其他作者提供的类似扩展。简化代码测试写一个最简单的测试程序只在开机时向串口输出一条“Hello”信息然后尝试读取一次DS3231的时间并输出。如果连“Hello”都没有说明可能是更基础的电源或主板问题。问题2时间走不准误差很大。DS3231电池检查DS3231模块上的纽扣电池是否电量充足。电池没电时模块在主电源断开后可能无法保持计时或者计时不准。初始化代码确认你的设置时间代码只运行了一次而不是在每次循环中都运行。否则时间会被不断重置。晶振精度DS3231精度很高如果误差达到每天几分钟那可能是模块本身质量有问题。但更常见的是程序逻辑错误比如在计算时、分、秒时弄错了进制把小时当成十进制直接显示等。6.2 软件逻辑与显示异常排查问题3二进制显示错误亮灭的LED不对应正确的数字。验证转换函数单独测试你的plotBinary函数。可以创建一个测试循环让数字从0递增到31观察一行LED的亮灭规律是否正确。如果可能将转换后的二进制位和理论值通过串口打印出来对比。检查坐标映射确认你设定的LED坐标(x,y)与二进制位的权值对应关系是否正确。是高位在左还是低位在左这个约定在整个程序中必须统一。清除屏幕在每次更新LED显示前务必使用“清除屏幕”积木块。否则上一次点亮LED会残留造成显示混乱。问题4程序运行一段时间后变卡或者按键响应迟钝。检查循环延迟确保无限循环中有足够的暂停时间如暂停(100毫秒)。没有暂停的循环会独占处理器导致系统无法及时响应按键等中断事件。优化代码结构避免在无限循环中进行非常复杂的数学运算或字符串操作。将不变的计算提到循环外面。读取RTC时间虽然是I2C操作但DS3231扩展库已经优化通常不会造成卡顿。内存泄漏在MakeCode中较少见但如果你使用了“数组”或“字符串”进行频繁的动态操作要注意不要无限增长。6.3 项目扩展与个人心得这个“Micro Binary Clock”项目虽然小但它像一个微型的全栈工程涵盖了硬件连接、通信协议I2C、数据结构二进制/十进制转换、状态机编程显示模式切换和人机交互。做完它你对Micro:bit和嵌入式编程会有一个很扎实的入门。我个人在多次制作和教学中总结出几点心得迭代开发不要想着一口气做出完美版本。先从纯软件、固定显示开始确保二进制转换和显示核心逻辑正确。然后加入按键交互再引入RTC最后考虑蓝牙对时等高级功能。每步都测试步步为营。调试是最好的老师遇到问题善用Micro:bit的串口数据输出功能。把关键的变量值如读取到的小时、分钟、计算出的二进制位实时打印出来在电脑的串口监视器里查看这是定位逻辑错误最有效的方法。外观即体验代码工作后可以考虑为你的时钟做一个外壳。用卡纸、乐高或者3D打印一个精致的外壳能让这个项目从“实验原型”升级为“桌面艺术品”。良好的外观会极大增加你和他人对这个项目的喜爱程度。分享与改进将你的项目分享到社区如MakeCode项目库看看别人是如何实现类似功能的。你可能会发现更简洁的二进制显示算法或者更有创意的交互方式。开源硬件和编程的魅力就在于这种持续的交流和进步。最后关于网络热词中提到的“stm32cubemx rtc”等那是更专业的嵌入式开发环境针对STM32系列芯片中的配置工具。我们的Micro:bit项目基于MakeCode和现成模块避开了底层寄存器配置的复杂性让创造者更专注于想法和逻辑的实现。当你玩转了Micro:bit如果对底层如何驱动RTC芯片产生兴趣那么去学习STM32和CubeMX将是自然而然且充满挑战的下一步。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价