简介这是一套面向嵌入式初学者与电子爱好者的STM32综合实践项目围绕实时时间与天气信息的串口屏显示展开并加入桌面屏幕与倒计时报警功能。项目以STM32微控制器为核心配合DS1302实时时钟芯片获取准确时间借助ESP8266模块联网调用天气API获取温度、湿度等数据最终在串口屏上实时呈现同时支持用户设置倒计时提醒会议、约会或服药等重要事件适合作为课程设计、毕业设计或DIY桌面时钟的参考方案。资源包共256个文件约9.06MB包含45个c源文件与47个h头文件构成的核心驱动与业务代码、45个o编译产物、45个crf交叉引用文件以及uvprojx、uvoptx等Keil工程配置、axf与hex可执行文件、map映射文件和若干备份文件完整保留了从源码到固件的工程结构。目前已有4163人学习下载读者可据此理清STM32外设驱动、ESP8266通信与串口屏交互的整合思路并参考倒计时报警逻辑完成二次开发。1. 桌面时钟还能这么玩STM32 挂 ESP8266 和 DS1302 做串口屏天气站手头有块闲置的 STM32 最小系统板加上一个吃灰的 ESP8266 模块和 DS1302 时钟芯片能折腾出什么我最近把这三样凑在一起配上一块串口屏做成了一个桌面实时天气时钟顺带把倒计时报警也塞了进去。它解决的核心问题很具体本地 RTC 走时准、断网也不丢时间联网后自动校准并拉取天气串口屏负责把时间、天气、倒计时三块信息直观显示出来。适合谁适合已经点过 LED、跑过串口、想找一个能把 I2C、UART、AT 指令、定时器中断串起来的综合小项目的嵌入式新手也适合熟手拿它当桌面摆件顺便验证模块稳定性。整套东西不复杂但坑点集中在 DS1302 的时序、ESP8266 的 AT 交互和串口屏的协议解析上下面按我实际搭的顺序拆开讲。2. 硬件选型与接线为什么是这三件套2.1 主控、时钟、联网三块的分工逻辑STM32 在这套系统里是调度中心不是算力担当。它要干三件事通过 I2C 或三线接口读写 DS1302 的秒、分、时、日、月、年寄存器通过 UART 用 AT 指令驱动 ESP8266 连热点、发 HTTP 请求拿天气数据再通过另一个 UART 把整理好的字符串推给串口屏。选 DS1302 而不是 DS3231理由很直接——DS1302 便宜、资料多、3.3V 和 5V 都能凑合代价是精度一般需要外挂 32.768kHz 晶振和纽扣电池走时误差每天可能到几秒。如果你手头是 DS3231接口兼容思路一样精度更好但本文按 DS1302 的寄存器结构写。ESP8266 用 AT 固件版本就行不需要自己写 SDK。常见做法是把它当透传模块STM32 发ATCWMODE、ATCWJAP、ATCIPSTART、ATCIPSEND这一串等它回OK和IPD。串口屏选的是带协议解析的型号通常支持0x5A 0xA5帧头加指令区加数据区的格式具体帧结构以你手头屏幕的文档为准这里只讲通用解析思路。2.2 接线表与电平注意模块引脚接到 STM32说明DS1302CE/RST任意 GPIO片选软件控制DS1302I/O任意 GPIO数据线需上拉DS1302SCLK任意 GPIO时钟线软件模拟ESP8266TXUSART2_RX模块发单片机收ESP8266RXUSART2_TX单片机发模块收串口屏RXUSART3_TX单片机发屏幕收串口屏TXUSART3_RX屏幕发单片机收可选DS1302 的三根线是软件模拟时序不占用硬件 SPI随便挑三个 GPIO 都行但 I/O 线一定要上拉否则读回来全是 0xFF。ESP8266 和串口屏各占一个硬件串口STM32F103 一般有 USART1/2/3够用。注意 ESP8266 是 3.3V 电平别直接接 5V 串口会烧。串口屏多数是 5V 供电但 TTL 电平接之前确认它的 RX 能不能吃 3.3V不行就加电平转换。提示DS1302 的晶振尽量靠近芯片负载电容按晶振规格选通常是 6pF 或 12.5pF焊错电容走时会明显偏快或偏慢。3. DS1302 驱动时序、BCD 和掉电保护3.1 寄存器结构与 BCD 码转换DS1302 的寄存器地址是固定的秒寄存器地址 0x80分 0x82时 0x84日 0x86月 0x88周 0x8A年 0x8C。写操作地址加 0x00读操作地址加 0x01。数据是 BCD 格式比如秒寄存器读到 0x59 表示 59 秒不是十进制 89。所以驱动里必须做 BCD 和十进制互转。// BCD 转十进制 uint8_t bcd_to_dec(uint8_t bcd) { return (bcd 4) * 10 (bcd 0x0F); } // 十进制转 BCD uint8_t dec_to_bcd(uint8_t dec) { return ((dec / 10) 4) | (dec % 10); }这两个函数看着简单但漏了就会在串口屏上显示乱码时间。比如 23 点写成十进制 23直接塞进寄存器读回来会变成 0x17 即 17时间就飘了。参数上注意 DS1302 的小时寄存器第 7 位是 12/24 小时制选择位写的时候要保证 bit7 为 0 才是 24 小时制。3.2 三线时序的读写函数DS1302 是低位先出数据在 SCLK 上升沿被写入下降沿被读出。下面是最小可用的写字节和读字节实现。// 写一个字节到 DS1302低位先出 void ds1302_write_byte(uint8_t dat) { for (uint8_t i 0; i 8; i) { DS_IO_LOW(); // 先拉低数据线 if (dat 0x01) DS_IO_HIGH(); DS_SCLK_HIGH(); // 上升沿写入 DS_SCLK_LOW(); dat 1; } } // 从 DS1302 读一个字节 uint8_t ds1302_read_byte(void) { uint8_t dat 0; for (uint8_t i 0; i 8; i) { dat 1; if (DS_IO_READ()) dat | 0x80; DS_SCLK_HIGH(); // 下降沿读出 DS_SCLK_LOW(); } return dat; }逻辑说明写的时候先把 I/O 设成输出按位把数据放到线上每个 SCLK 上升沿 DS1302 采样。读的时候要把 I/O 切成输入在 SCLK 下降沿之后读电平。参数上每次读写前要拉低 CE 使能读写完拉高 CE。如果读回来一直是 0x00 或 0xFF先查 I/O 方向有没有切对再查上拉电阻。3.3 掉电后时间不丢的验证方法DS1302 接上纽扣电池后主电源断掉它靠电池维持走时。验证方法写完时间拔掉主电源等五分钟重新上电读时间看是否连续。如果每次上电都回到初始值检查电池电压是否低于 2.0V或者电池正极有没有通过二极管接到 VCC1。常见做法是电池正极串一个肖特基二极管再到 VCC1防止主电源给电池反充。4. ESP8266 联网取天气AT 指令的稳定交互4.1 AT 指令状态机与超时处理ESP8266 的 AT 交互最怕两件事一是模块还没准备好就发指令二是等回复时死等。我一般用一个简单的状态机加超时计数。每条指令发出后在串口中断里收数据主循环里判断是否收到预期关键字超过 3 秒没收到就重发重发三次失败就复位模块。// 发送 AT 指令并等待预期回复超时返回 0 uint8_t esp_send_cmd(char *cmd, char *expect, uint16_t timeout_ms) { uart2_send_string(cmd); uart2_send_string(\r\n); uint32_t start get_tick(); while ((get_tick() - start) timeout_ms) { if (strstr((char *)uart2_rx_buf, expect) ! NULL) { memset(uart2_rx_buf, 0, UART2_BUF_SIZE); return 1; } } memset(uart2_rx_buf, 0, UART2_BUF_SIZE); return 0; }逻辑说明uart2_rx_buf在串口中断里累加主循环轮询匹配。参数上timeout_ms给 3000 比较稳连热点那步可以给到 10000。注意每次匹配成功后要清空缓冲区否则下一条指令会匹配到上一条的残留。4.2 连热点、发 HTTP、解析天气连热点的顺序是ATCWMODE1设成 StationATCWJAPssid,password连路由ATCIPSTARTTCP,api.example.com,80建 TCPATCIPSEND长度发 HTTP GET最后从IPD数据里截取 JSON。天气接口返回的 JSON 里通常有温度、湿度、天气现象字段用字符串查找定位。// 从 IPD 数据里提取温度字段假设格式为 temp:25 int parse_temp(char *ipd_buf) { char *p strstr(ipd_buf, \temp\:); if (p NULL) return -999; // 解析失败标志 return atoi(p 7); }参数说明atoi遇到非数字会停所以temp:25,能正确拿到 25。如果接口返回的是temp:25带引号就得先跳过引号。解析失败返回 -999串口屏上显示--而不是乱跳的数字。注意 HTTP 请求里的 Host 和路径要按你用的接口写别照抄。4.3 断网重连与数据缓存网络不会一直稳ESP8266 偶尔会掉线。我的做法是每 10 分钟发一次ATCIPSTATUS查连接状态如果返回的不是STATUS:3已连接就重新走一遍 CIPSTART。天气数据拿到后存在一个全局结构体里串口屏刷新时优先用缓存避免网络卡顿导致屏幕闪烁。如果连续三次取天气失败屏幕上天气区域显示上次成功的数据加一个*标记提醒数据可能过期。5. 串口屏显示与倒计时报警协议解析和中断配合5.1 串口屏帧格式与控件寻址串口屏的通用帧格式一般是帧头加指令加控件 ID 加数据加帧尾。以常见的0x5A 0xA5帧头为例一条更新文本控件的指令可能是5A A5 05 82 00 01 31 32 33其中82是写文本指令00 01是控件地址后面是 ASCII 数据。不同厂家指令码不同但思路一致先查文档确认帧头、指令码、控件地址长度。// 更新串口屏文本控件addr 为控件地址str 为要显示的字符串 void screen_set_text(uint16_t addr, char *str) { uint8_t frame[64]; uint8_t len strlen(str); frame[0] 0x5A; frame[1] 0xA5; frame[2] len 5; // 数据长度 frame[3] 0x82; // 写文本指令 frame[4] addr 8; frame[5] addr 0xFF; memcpy(frame[6], str, len); uart3_send(frame, len 6); }逻辑说明len 5是长度域包含指令、地址和数据具体算法看屏幕文档。参数上控件地址是 16 位高字节在前。如果屏幕没反应先用厂家调试工具发同样帧确认地址对不对再查波特率是不是 115200。5.2 倒计时逻辑与定时器中断倒计时用 TIM2 做 1 秒基准中断在中断里递减一个全局变量。倒计时到 0 时置一个报警标志主循环检测到标志就驱动蜂鸣器或让屏幕闪红。volatile uint32_t countdown_sec 0; volatile uint8_t alarm_flag 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if (countdown_sec 0) { countdown_sec--; if (countdown_sec 0) alarm_flag 1; } } }参数说明TIM2 配置成 1Hz预分频和重装载值按系统时钟算。countdown_sec用volatile修饰防止编译器优化掉。主循环里检测alarm_flag执行完报警动作后手动清零。注意中断里不要做串口发送这种耗时操作只置标志。5.3 时间、天气、倒计时三块信息的刷新节奏三块信息刷新频率不一样时间每秒刷一次天气每 10 分钟刷一次倒计时每秒刷一次但只在变化时发串口指令。如果每秒都全屏刷新串口屏会闪。我的做法是时间控件每秒更新天气控件只在数据变化时更新倒计时控件每秒更新但和报警状态分开处理。这样串口带宽占用低屏幕也稳。6. 避坑排查DS1302 读回 0xFF、ESP8266 卡在 CWJAP、屏幕花屏现象一DS1302 读回来全是 0xFF。原因I/O 线没上拉或者读之前没把 I/O 切成输入模式。 解决在 I/O 线上加 4.7k 上拉电阻到 3.3V读函数开头把 GPIO 设成输入写完再设回输出。现象二ESP8266 卡在ATCWJAP一直回WIFI DISCONNECT。原因热点是 5GHzESP8266 只支持 2.4GHz或者密码里有特殊字符没转义。 解决确认路由 2.4GHz 频段开启密码先用纯数字字母测试连上后再换复杂密码。现象三串口屏显示花屏或部分控件不更新。原因帧长度域算错或者两条指令间隔太短屏幕没处理完。 解决用屏幕厂家工具核对帧格式指令之间加 20ms 延时或者等屏幕回OK再发下一条。现象四倒计时到 0 后蜂鸣器一直响不停。原因alarm_flag在主循环里没清零或者中断里重复置位。 解决主循环执行完报警动作后把alarm_flag 0并在中断里判断countdown_sec是否已经为 0 再置位。现象五断电后时间回到 2000 年 1 月 1 日。原因纽扣电池没电或者电池回路二极管装反。 解决量电池电压低于 2.0V 就换检查二极管方向确保主电源不掉电时不给电池充电。7. 进阶技巧把天气图标和报警日志也塞进串口屏基础功能跑通后我习惯再压榨一下串口屏的图形控件。天气现象可以用图标控件代替文字比如晴、云、雨各对应一个图标地址解析到天气字段后直接切图标比显示「多云转晴」这种长文本清爽。倒计时报警可以加一个日志区把最近三次报警时间写进屏幕的变量地址里方便回看。// 根据天气代码切换图标控件addr 为图标控件地址 void screen_set_icon(uint16_t addr, uint8_t icon_id) { uint8_t frame[8]; frame[0] 0x5A; frame[1] 0xA5; frame[2] 0x04; frame[3] 0x82; // 假设图标也用写指令 frame[4] addr 8; frame[5] addr 0xFF; frame[6] icon_id; uart3_send(frame, 7); }参数说明icon_id对应屏幕素材库里的图标编号具体编号在屏幕工程文件里查。如果屏幕不支持图标指令就退回用文本控件显示符号。验证整套系统稳不稳我一般会做三个测试连续通电 24 小时看时间漂移多少断网 10 分钟看重连是否自动完成倒计时设 1 分钟看报警是否准时。这三个测试过了基本就能放桌面上长期跑了。从那以后我每次做带 RTC 的项目都强制先单独验证 DS1302 走时一天再往上叠网络和显示不然出了问题根本分不清是哪层的锅。希望帮到你。本文还有配套的精品资源点击获取