STM32F103C8T6万年历Proteus仿真:时钟配置与日期边界全解析
简介基于STM32F103C8T6的多功能电子万年历Proteus仿真工程面向单片机初学者、电子设计竞赛备赛者及课程设计者。系统以STM32最小系统板为控制器使用DS1302时钟芯片记录日历DS18B20采集温度1602A液晶屏显示年月日、时分秒、星期等信息并支持按键调时、闹钟设定与闰年补偿掉电后纽扣电池维持走时闹钟参数保存于内部FLASH。资料包共167个文件包括C语言源码、头文件、Proteus仿真工程、Keil工程文件、HEX烧录文件及说明文档总大小4.08MB。已有3206人学习下载。内含完整硬件设计思路与模块化代码可直接导入Proteus验证便于移植实物通过学习DS1302驱动、DS18B20单总线通信和1602液晶读写可以较快掌握STM32典型外设应用适合课程设计与毕业设计扩展。1. 用 STM32F103C8T6 做万年历仿真真正的难点是时钟不是日期把标题里“基于STM32f103c8t6的万年历protues仿真”拆开看很多人以为难点在日期计算其实做过的都知道真正卡人的是仿真环境里那颗时钟源。Proteus 仿真和真实开发板不太一样代码里默认走外部晶振 HSE仿真电路却没接晶振程序会直接卡死在启动阶段LCD 连一个字都不显示。这个项目本质上是把三个最常见的外设能力串起来定时器中断、按键状态机、LCD1602 控制再套一层公历日期进位逻辑适合刚学完 GPIO 和中断、准备做课程设计或简历项目的开发者。标题里的 protues 是 Proteus 的常见误写搜索时两个写法都能找到。下面按我实际调通的顺序把方案选型、代码骨架、仿真连线、坑位和验证技巧一次讲透。2. 时间从哪来内置 RTC、DS1302 还是定时器模拟先选对再写代码2.1 三种时间方案的原理差异与仿真适配性做万年历第一件事是决定时间基准。STM32F103C8T6 有内置 RTC 外设也可以外挂 DS1302 时钟芯片还可以用普通定时器加软件日历。三者原理完全不同在 Proteus 里的表现也差很多先对比清楚再动手。内置 RTC 需要在代码里打开 PWR 和 BKP 时钟配置备份域寄存器还要依赖 VBAT 引脚供电。在 Proteus 里 VBAT 的供电模型比较麻烦每次重新加载仿真程序RTC 时间基本都会回到初始值你很难判断是程序问题还是仿真模型问题。实机用它做断电保持是合理的但课程设计阶段的调试成本偏高。DS1302 是外挂芯片时间计算由芯片完成主机只负责读写寄存器。Proteus 元件库里能直接找到 DS1302 模型需要外接 32.768kHz 晶振。这个方案逻辑清晰但要多写一套总线时序而且仿真中晶振模型偶尔不稳定初学者一旦读回全 0xFF很容易在这里耗掉两三天。我一般选第三种TIM2 定时器每 1 秒触发一次中断在中断里自己维护年月日时分秒和星期。不需要任何外部器件Proteus 连线最简日期逻辑完全可控也最能锻炼人。代价是断电丢时间和主频变了时间不准但这两点在仿真验证阶段都可以接受等你要做产品了再替换成 DS1302 或内置 RTC 也不迟。这三种方案没有绝对好坏主要看目标标题这个项目的交付重点是能把万年历跑起来、日期跳得对、按键调得动不是总线时序。方案选得越简单你越能把精力放在真正有区分度的日期边界逻辑上。2.2 日期处理闰年判断、每月天数和星期推算的通用写法无论选哪种时间方案公历计算都是绕不开的。STM32F103C8T6 的代码里我习惯放三个基础函数闰年判断、当月天数、星期计算。这三个函数写对万年历的地基就稳了。typedef struct { uint16_t year; // 完整年份例如 2024 uint8_t month; // 1~12 uint8_t day; // 1~31 uint8_t hour; // 0~23 uint8_t min; // 0~59 uint8_t sec; // 0~59 uint8_t week; // 0周日, 1周一 ... 6周六 } RTC_TimeType; // 闰年判断四年一闰百年不闰四百年再闰 uint8_t IsLeapYear(uint16_t year) { return (year % 4 0 year % 100 ! 0) || (year % 400 0); } // 返回某年某月的天数2 月单独处理 uint8_t GetMonthDays(uint16_t year, uint8_t month) { static const uint8_t days[13] {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (month 2 IsLeapYear(year)) { return 29; } return days[month]; }这个结构体贯穿整个项目时间字段全部独立方便按键逐项修改。GetMonthDays 里用 static const 把月天数表放在 Flash不占 RAM这是 STM32F103C8T6 上写查表类函数的习惯。注意 days[0] 是占位让数组下标和自然月一一对应代码可读性更好。星期计算我用“从 2000-01-01 起算天数差”的办法而不是蔡勒公式。蔡勒公式要处理 1、2 月变成上一年 13、14 月的情况少写一个分支就错一天。下面这个函数循环次数最多一百多次只在开机或设置完日期时调用一次放进 1 秒中断里反而浪费。// 以 2000-01-01 为星期六作为基准返回任意日期的星期 uint8_t CalcWeek(uint16_t y, uint8_t m, uint8_t d) { uint16_t yy; uint8_t mm; int total 0; for (yy 2000; yy y; yy) { total IsLeapYear(yy) ? 366 : 365; // 整年天数 } for (mm 1; mm m; mm) { total GetMonthDays(y, mm); // 当年已过月份天数 } total d - 1; // 当月已过天数 return (total 6) % 7; // 2000-01-01 是星期六记为 6 }这个函数只处理 2000 年以后的日期恰好覆盖 F103C8T6 做民品万年历的常用范围。C 语言里 int 是 32 位算到 2099 年累计约 3.6 万天完全不会溢出。如果你的项目要支持 1970 年前把基准年份改掉、循环变量换成 long 就行逻辑不用动。2.3 按键与状态机设置流程怎么划分才不乱万年历至少要有三个按键设置、加、减。如果不做状态机主循环里每检测一次按键就执行一次调整一秒钟能调整几十次根本没法用。我习惯用一个枚举类型把界面状态固定下来。typedef enum { MODE_RUN 0, // 正常运行显示当前时间 MODE_SET_YEAR, // 设置年 MODE_SET_MONTH, // 设置月 MODE_SET_DAY, // 设置日 MODE_SET_HOUR, // 设置时 MODE_SET_MIN, // 设置分 MODE_SET_SEC // 设置秒 } SetMode; SetMode mode MODE_RUN;状态机的核心原则SET 键只负责切换状态UP/DOWN 键只负责修改当前状态对应的字段。两者互不越权代码就不会出现“按一下 SET 直接从年跳到了日”的问题。每次进入新状态时LCD 上对应字段最好反白或闪烁否则调了半小时都不知道自己在改哪一个。设置过程中 date 合法性校验不能省。用户可能把日调到 31但这个月只有 30 天如果不做限制走到自动进位那天日期就会乱。常见的做法是调完一个字段后顺手做一次 clampvoid Time_Clamp(void) { if (timeVal.month 12) timeVal.month 12; if (timeVal.day GetMonthDays(timeVal.year, timeVal.month)) { timeVal.day GetMonthDays(timeVal.year, timeVal.month); } }把校验放在“调整后立即可见”的位置比等到凌晨 0 点进位时再发现问题要好处理得多。按键消抖方面仿真里按键模型没有机械触点抖动但主循环扫描频率很高同一个按下动作会被识别很多次所以仍然需要用边沿检测后面的避坑章节会专门展开。3. 核心代码落地定时器秒中断、日期进位、LCD1602 显示的骨架3.1 定时器 1 秒中断的初始化参数计算STM32F103C8T6 最高主频 72MHz。TIM2 挂在 APB1 总线上当 APB1 预分频设置为 2 时PCLK1 是 36MHz但定时器时钟会自动倍频到 72MHz。下面这段是标准外设库的写法换算关系最直观。void TIM2_Init(void) { TIM_TimeBaseInitTypeDef tb; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 打开 TIM2 总线时钟 tb.TIM_Prescaler 7200 - 1; // 72MHz / 7200 10kHz 计数频率 tb.TIM_CounterMode TIM_CounterMode_Up; tb.TIM_Period 10000 - 1; // 计满 10000 次 1 秒 tb.TIM_ClockDivision 0; tb.TIM_RepetitionCounter 0; TIM_TimeBaseInit(TIM2, tb); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能更新中断 TIM_Cmd(TIM2, ENABLE); // 启动定时器 NVIC_EnableIRQ(TIM2_IRQn); // 打开中断控制器通道 }计算式是(PSC1) × (ARR1) / 72MHz 7200 × 10000 / 72,000,000 1 秒。这里最容易被带偏的是把 PCLK1 的 36MHz 当成定时器时钟那样算出来就是 0.5 秒一跳。如果你用 CubeMX 生成 HAL 库工程初始化代码里 Prescaler 填 7199、Period 填 9999参数完全一致只是换了一套 API 包装。中断服务函数里只做一件事推进时间。不要在中断里刷 LCD、不要放延时否则 1 秒中断会被拉长时间立刻不准。void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); TimeTick(); // 时间推进函数详见 3.2 } }3.2 时间结构体与跨日进位函数TimeTick 的写法直接影响日期逻辑的可靠性。我采用多层 if 提前返回的结构每个进位层级独立读起来像流水账但不容易漏边界。void TimeTick(void) { timeVal.sec; if (timeVal.sec 60) return; timeVal.sec 0; timeVal.min; if (timeVal.min 60) return; timeVal.min 0; timeVal.hour; if (timeVal.hour 24) return; timeVal.hour 0; // 到这里才跨日星期先轮转 timeVal.day; timeVal.week (timeVal.week 1) % 7; if (timeVal.day GetMonthDays(timeVal.year, timeVal.month)) return; timeVal.day 1; timeVal.month; if (timeVal.month 12) return; timeVal.month 1; timeVal.year; if (timeVal.year 2099) timeVal.year 2000; // 仿真范围限制 }注意星期的维护方式开机时用 CalcWeek 初始化一次之后每次跨日自动加一并模 7。这和日期进位天然同步不需要在中断里跑完整的天数差循环这是 1 秒中断里能接受的最低开销。年份范围限制在 2000 到 2099是为了和 CalcWeek 的循环范围匹配避免溢出。这里有一个容易被忽略的实现细节timeVal 是全局结构体跨日进位涉及多个字段连续修改。仿真环境里单线程没竞争但在实机上如果主循环也在读时间做显示最好在进入临界区后读取或者把整个结构体拷贝一份再显示否则用户可能看到 23:59:60、2月30日这样的瞬间值。这个在仿真里看不太出来属于实机才暴露的问题。3.3 LCD1602 的 4 线驱动和显示刷新LCD1602 用 4 线模式可以省 4 个 IO这也是 Proteus 仿真中比较常见的接法。LM016L 模型对 4 线支持没问题关键是初始化握手顺序不能错。#define LCD_RS_PIN GPIO_Pin_4 // PA4 - RS #define LCD_E_PIN GPIO_Pin_5 // PA5 - E // 数据脚 D4~D7 接 PA0~PA3 static void LCD_WriteNibble(uint8_t nib) { GPIO_WriteBit(GPIOA, GPIO_Pin_0, (nib 0x01) ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOA, GPIO_Pin_1, (nib 0x02) ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOA, GPIO_Pin_2, (nib 0x04) ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOA, GPIO_Pin_3, (nib 0x08) ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOA, LCD_E_PIN, Bit_SET); delay_us(1); GPIO_WriteBit(GPIOA, LCD_E_PIN, Bit_RESET); } void LCD_Init(void) { delay_ms(20); LCD_WriteNibble(0x03); delay_ms(5); LCD_WriteNibble(0x03); delay_us(150); LCD_WriteNibble(0x03); delay_us(150); LCD_WriteNibble(0x02); delay_us(150); // 切换到 4 位模式 LCD_WriteCmd(0x28); // 4 位、2 行、5x7 点阵 LCD_WriteCmd(0x0C); // 显示开、光标关、不闪烁 LCD_WriteCmd(0x01); // 清屏 delay_ms(2); LCD_WriteCmd(0x06); // 写入后地址自动加 1 }初始化序列先写三次 0x03、再写一次 0x02这是 1602 从 8 位模式切换 4 位模式的标准握手。少一次或者顺序乱后面的命令全白写。Proteus 的 LM016L 模型对时序要求比实机宽容所以你在仿真里看到正常不代表实机也正常反过来如果仿真里就乱码那实机大概率更糟。显示刷新我放在主循环里每秒刷一次就够了不用每次中断都刷void Display_Update(void) { char buf[17]; sprintf(buf, %04d-%02d-%02d %s, timeVal.year, timeVal.month, timeVal.day, weekName[timeVal.week]); LCD_ShowString(0, 0, buf); // 第一行日期和星期 sprintf(buf, %02d:%02d:%02d, timeVal.hour, timeVal.min, timeVal.sec); LCD_ShowString(1, 0, buf); // 第二行时分秒 }用 sprintf 格式化最省事但 Keil 工程需要勾选 MicroLIB否则 printf 家族函数体积会膨胀近 10KB。F103C8T6 有 64KB Flash 装得下但没必要。weekName 是长度为 7 的字符串数组例如 {“Sun”,“Mon”,“Tue”,“Wed”,“Thu”,“Fri”,“Sat”}也可以直接存中文编码不过 Proteus 的 LM016L 模型对中文支持要看字库建议先用 ASCII。4. 在 Proteus 里搭电路器件清单、晶振配置与 hex 加载流程4.1 Proteus 器件清单与接线表Proteus 元件库里 STM32F103C8T6 和 LM016L 都能直接搜到。我用的最低配置清单如下全部是 Proteus 自带模型不需要额外下载库。器件Proteus 搜索名数量说明主控STM32F103C8T61双击加载 hexLCD1602LM016L1支持 4 线模式按键BUTTON3设置、加、减晶振CRYSTAL18MHz可选电容CAP222pF晶振负载电位器POT1调 LCD 对比度电源端子直接用 Proteus 电源工具2组3.3V、5V 各一组按键我不放上拉电阻直接启用 STM32 内部上拉接线更少。LCD 的 VDD 接 5VVO 脚接电位器中间抽头到 GND这样对比度是连续可调的。很多新手把 VO 直接接地某些型号 LCD 会黑屏接个电位器是通用做法。接线表如下PA0~PA3 给数据线PA4/PA5 给控制线避免使用 PA15、PB3、PB4 这些默认 JTAG 复用脚省去禁用 JTAG 的额外代码。MCU 引脚目标引脚说明PA0~PA3LM016L D4~D74 位数据总线PA4LM016L RS寄存器选择PA5LM016L E使能时钟PB5K1 设置键按键另一端接 GNDPB6K2 加键按键另一端接 GNDPB7K3 减键按键另一端接 GNDNRST10k 电阻到 3.3V防止仿真时复位脚悬空Proteus 中 STM32F103C8T6 的 VDD、VDDA、VBAT 都要接到电源网络上GND 和 VSSA 也一样只接主 VDD 会看到部分引脚悬空告警。NRST 上拉通常不是必须的但加上更稳妥避免仿真模型偶发复位。4.2 晶振和 PLLProteus 里最容易翻车的时钟配置这是整个项目最玄学的部分先说结论仿真环境里我建议直接用内部 HSI 作为 PLL 时钟源绕开外部晶振这样能在不接晶振的情况下稳定跑到 72MHz。void Clock_Init_HSI(void) { RCC_DeInit(); RCC_HCLKConfig(RCC_SYSCLK_Div1); // HCLK SYSCLK RCC_PCLK2Config(RCC_HCLK_Div1); // APB2 72MHz RCC_PCLK1Config(RCC_HCLK_Div2); // APB1 36MHzTIM2 会倍频到 72MHz FLASH_SetLatency(FLASH_Latency_2); // 72MHz 下 Flash 需要 2 个等待周期 RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_18); // 8MHz/2 * 18 72MHz RCC_PLLCmd(ENABLE); while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) RESET) {} // 等待 PLL 锁定 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while (RCC_GetSYSCLKSource() ! 0x08) {} // 等待系统时钟切换完成 }为什么强调仿真用 HSI因为标准外设库默认的 SetSysClockTo72 走的是 HSE而 Proteus 模型对 HSE 的模拟受版本和连线影响比较大。万一你只放 MCU 忘了放晶振程序会一直停在 RCC_WaitForHSEStartUp 的 while 循环里看起来像 LCD 不亮实则是固件根本没启动。用 HSI 这个函数系统里只要有两个电容和电源就能跑起来。用 HSI 后 TIM2 的参数不变因为最终 SYSCLK 还是 72MHz、PCLK1 还是 36MHz、定时器时钟还是 72MHz。时钟配置和定时器配置是一套联动参数不能只改一半。Proteus 里 MCU 属性面板有个时钟频率选项如果你的代码完全由 PLL 决定系统时钟这个属性对 HSI 路径影响不大不用太纠结。4.3 从 Keil 到 Proteus 的联调流程整个联调流程我分成六步每一都很机械但顺序错了就会浪费很多时间。Keil 工程里打开 Options for Target在 Output 页勾选 Create HEX File路径指向工程输出目录。把 2.2、3.1、3.3 的代码按顺序合进 main 工程编译零错误零警告。在 Proteus 里放置 STM32F103C8T6、LM016L、三个按键和电位器按 4.1 接线表连线。双击 Proteus 里的 MCU在 Program File 项选择 Keil 生成的 hex 文件。点击仿真运行观察 LCD 是否先显示初始时间 2000-01-01 或你设定的默认时间。按 K1 进入设置模式用 K2/K3 修改字段确认 LCD 显示能实时变化。如果第 5 步 LCD 没有任何反应优先排查时钟初始化在 Clock_Init_HSI 的 while 等待循环里打断点看寄存器是否被置上。很多新手直接把 Proteus 当黑匣子不应该。Proteus 的调试模式和在线仿真时序断点都能用遇到问题先确认程序跑到哪一行比反复改连接线有效得多。5. 万年历仿真避坑指南5 个让新手熬夜的问题5.1 现象仿真运行后 LCD 一直黑屏程序像根本没跑现象就是点击运行后LCD 无任何显示主控看起来没工作。原因十有八九是代码里启用了 HSE 外部晶振而仿真电路只放了 MCU 没放晶振或者放错了引脚程序卡在 RCC_WaitForHSEStartUp() 的 while 循环里不肯往下走。解决方法是统一改用 Clock_Init_HSI 做时钟初始化不依赖外部晶振如果你确实想用 HSE就在 OSC_IN/OSC_OUT 引脚接 8MHz 晶振和两个 22pF 负载电容并确认型号匹配。我个人的判断是仿真阶段没必要和晶振较劲HSI 完全够用实机再切 HSE 也不迟。5.2 现象秒走得太快或太慢过一分钟就能看出偏差现象是 LCD 上的秒数明显快于电脑时钟或者慢得离谱。原因多半是系统时钟根本不是 72MHz。比如 Proteus 里 MCU 属性频率被设成 8MHz或者 APB1 分频系数被代码改成了 1TIM2 的 PSC 和 ARR 按 72MHz 算实际输入时钟却不是 72MHz。解决方法是先用 SystemCoreClock 或者调试器确认 SYSCLK 值再确认 PCLK1 是否为 36MHz。注意 APB1 分频不为 1 时定时器时钟自动翻倍这个特性只有 PCLK136MHz 时 TIM2 才是 72MHz。这个坑属于原理清楚但动手时容易忽略建议把时钟链路写一行注释贴在 TIM2_Init 上方以后再看代码一眼就能想起来。5.3 现象按键按一下设置项跳了两个现象是按一次 SET年直接变成月或者按一次加数值加了三。原因是主循环每毫秒都扫描一次按键手指按下到松开这段时间足够循环跑两百次每次都执行了一次调整。解决方法是做边沿检测只在按键从“未按下”变成“按下”的瞬间执行一次动作uint8_t Key_ScanEdge(GPIO_TypeDef *port, uint16_t pin) { static uint8_t lastPress 0; uint8_t cur (GPIO_ReadInputDataBit(port, pin) Bit_RESET) ? 1 : 0; uint8_t edge (cur 1 lastPress 0) ? 1 : 0; lastPress cur; return edge; }这段代码的静态变量 lastPress 保存上次电平状态只有电平从 0 变 1 才返回 1。有了边沿检测按键会感觉“一下是一下”设置模式也能稳定切换。另外有人用分频方式让按键扫描频率降到 10Hz 来解决那效果不如边沿干净因为松开时的抖动仍可能触发一次误判。5.4 现象LCD1602 显示乱码或者整屏方块现象是初始化后 LCD 显示满屏方块或者首行正常、次行乱码。原因通常是初始化握手没按规定次数或者数据线引脚映射错位。LM016L 的 D4~D7 必须对应代码里指定的 PA0~PA3差一根线就乱一个字。解决方法是重新核对接线表并把 LCD_Init 里每次写完之后加 1ms 延时让 LCD 控制器有足够时间消化命令。Proteus 的 LCD 模型对时序很宽容所以仿真里乱码基本可以断定是引脚接错或初始化序列问题不是速度问题。还有一个容易忽视的是 VO 对比度脚悬空悬空会导致字符显示极淡看着像黑屏接上电位器并调出字符后再做别的。5.5 现象把日期调到非法值后跨月进位越来越乱现象是在设置模式里把日期调到 2025-02-31时间继续走几天后显示变成 2025-03-02中间跳过了 3 月 1 日。原因很简单设置阶段没有做日期合法性校验非法日期把 GetMonthDays 的判断打乱了。解决方法是设置完日字段后立即调用 Time_Clamp把非法值压回当月最大天数在设置模式里就消除坏数据。另一种情况是从 2 月 28 日跨到 3 月 1 日觉得“少了一天”那其实是正常的平年 2 月只有 28 天用 2.2 的 IsLeapYear 和 GetMonthDays 验证一遍就没问题。我建议把 Time_Clamp 放在 SET 键退出当前字段之前这样用户调完立刻看到被自动纠正的数值体验最直观。6. 把仿真时间加速一天万年历日期边界的快速验证技巧6.1 用 ARR 加速验证跨日进位万年历的真功夫都在日期边界上闰年 2 月、大月小月、跨年、星期回绕。如果每次验证都真实等一天项目根本排不下去。我的办法是临时修改 TIM2 的重装载值把 1 秒中断压缩成 0.1 秒仿真跑 10 秒就过一天。// 正常模式下 tb.TIM_Period 10000 - 1; // 1 秒 // 验证模式下临时改成 0.1 秒 tb.TIM_Period 1000 - 1; // 10 秒过一天配合一个初始化赋值函数直接指定目标时间省去按键一次次调void Time_SetInitDate(uint16_t y, uint8_t m, uint8_t d, uint8_t hh, uint8_t mm, uint8_t ss) { timeVal.year y; timeVal.month m; timeVal.day d; timeVal.hour hh; timeVal.min mm; timeVal.sec ss; timeVal.week CalcWeek(y, m, d); // 星期按基准公式算 }6.2 边界用例清单与我的验证习惯我把万年历的边界用例固定成下面这张清单每次改完日期逻辑先跑一遍再恢复成真实秒速用例编号初始时间预期结果12025-02-28 23:59:50跳转到 2025-03-012 月 28 天生效22024-02-28 23:59:50跳转到 2024-02-29闰年生效再走一天到 03-0132024-04-30 23:59:50跳转到 2024-05-01小月 30 天生效42099-12-31 23:59:50跳转到 2100-01-01年份进位加星期回绕验证用例跑完后把 ARR 改回 10000-1再上电观察一次 1 秒节奏是否正常。我自己的习惯是先把这些用例和期望结果写成一个表格贴在代码文件头部注释里隔了几个月回来改代码时打开文件就能想起当时为什么这么设计。仿真器最大的价值就在于可以人为制造极端日期这是实物开发板很难等到的。做万年历真正让评审眼前一亮的不是能显示时间而是你能把 2099 年 12 月 31 日跨到 2100 年 1 月 1 日时那个星期的轮转算得无可挑剔。希望这套参数和验证流程能帮到你。本文还有配套的精品资源点击获取

相关新闻

Kube-Bench实战:Kubernetes安全基线扫描与自动化合规实践

Kube-Bench实战:Kubernetes安全基线扫描与自动化合规实践

上周安全部门丢过来一张表格,密密麻麻列着:集群版本多少、API Server有没有开匿名访问、etcd是否启用了TLS、kubelet的只读端口关没关……要求一周内交一份基线合规报告。以前遇到这种事,基本都是对着官方文档一项一项人工核对,一…

2026/10/15 15:58:15 阅读更多 →
STM32CubeMX入门:图形化配置GPIO点亮LED实战教程

STM32CubeMX入门:图形化配置GPIO点亮LED实战教程

1. 什么时候开始,我建议你直接跳到CubeMX先说句实话,如果到现在你还在纠结“到底要不要先学寄存器再学CubeMX”,那我建议你放下这个执念。我就是从寄存器一路折腾过来的,当时翻手册、查寄存器位定义,光配置一个GPIO就要…

2026/10/15 15:58:15 阅读更多 →
石化罐区HART温度传感器联网改造实战:从模拟表到TCP以太网监控

石化罐区HART温度传感器联网改造实战:从模拟表到TCP以太网监控

做石化仓储电仪的人,几乎都遇到过这样的尴尬场景:罐区里一排带HART协议的防爆温度传感器,铭牌上明明白白写着“智能仪表”,可到了中控室,监控画面上孤零零一个温度数值,仪表内部的诊断信息、量程状态、设备…

2026/10/15 15:58:15 阅读更多 →

最新新闻

Transformer为什么强?解析全局建模、并行计算与多模态扩展优势

Transformer为什么强?解析全局建模、并行计算与多模态扩展优势

最近总有人问我同一个问题:Transformer 到底比其他模型强在哪。我做了几年序列建模相关的工作,文本分类、机器翻译、多模态对齐这些任务都反复折腾过,每次换架构、调参、对比实验做下来,对“强”字的理解其实和刚接触时不太一样。…

2026/10/15 16:46:53 阅读更多 →
Enriched MarkdownText组件详解:如何用一个组件呈现完整的Markdown富文本

Enriched MarkdownText组件详解:如何用一个组件呈现完整的Markdown富文本

【免费下载链接】enriched-markdown Multiplatform Markdown-Based Rich Text Solution 项目地址: https://gitcode.com/gh_mirrors/re/enriched-markdown 点击查看 免费下载 Enriched Markdown 是一个跨平台的 Markdown 富文本解决方案(Multiplatform …

2026/10/15 16:46:53 阅读更多 →
报纸杂志HTML5模板:语义结构、印刷排版与EPUB3全链路实践

报纸杂志HTML5模板:语义结构、印刷排版与EPUB3全链路实践

简介:这是一套专为新闻媒体类网站打造的HTML5响应式网页模板,面向中小型报社、杂志社及内容创作者,解决其快速搭建专业、易读、多媒体兼容的在线出版平台的需求。模板包含11个功能完备的子页面(如首页、文章详情页、分类页、作者页…

2026/10/15 16:46:53 阅读更多 →
Matlab多目标优化实战:gamultiobj与paretosearch从入门到避坑

Matlab多目标优化实战:gamultiobj与paretosearch从入门到避坑

简介:这份资源面向需要在 MATLAB 中处理多目标优化问题的学生、科研人员与工程技术人员,核心是借助遗传算法求解相互冲突目标之间的帕累托最优解集。压缩包共 15 个文件,以 8 个 m 脚本和 7 个 asv 自动备份文件为主,整体约 8KB&a…

2026/10/15 16:46:53 阅读更多 →
Pure Control 实战:从 P 到 PI 的快速整定与避坑指南

Pure Control 实战:从 P 到 PI 的快速整定与避坑指南

简介:这份资源是面向控制工程学习者与科研人员的MATLAB/Simulink实践资料包,围绕纯控制理论展开,适合已具备一定控制基础、希望借助仿真加深理解的中高级读者。压缩包共546个文件,约6.72MB,以296个mat数据文件、58个l与…

2026/10/15 16:46:53 阅读更多 →
USB-A转USB-C线缆选购指南:从电流规格到E-Marker芯片的全面解析

USB-A转USB-C线缆选购指南:从电流规格到E-Marker芯片的全面解析

1. 从抽屉里那根线说起:USB-A转USB-C到底解决了什么问题 几乎每个人的抽屉里都躺着几根线:一头是扁扁的矩形接口,另一头是椭圆形的正反插接口。前者是USB-A,后者是USB-C。很多人对这根线的认知停留在“能充电就行”,但…

2026/10/15 16:45:53 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →