手把手教你用STM32F103驱动DS1302时钟模块,再配上OLED屏显示(附完整代码)
2026/5/11 14:07:30 网站建设 项目流程

STM32F103与DS1302时钟模块实战:从硬件连接到OLED显示的完整指南

1. 项目概述与硬件准备

在嵌入式开发领域,实时时钟(RTC)模块的应用极为广泛。DS1302是一款低成本、高精度的实时时钟芯片,而STM32F103作为经典的Cortex-M3内核微控制器,两者结合可以构建各种需要时间记录功能的应用。本项目将展示如何通过STM32F103驱动DS1302时钟模块,并将时间信息显示在0.96寸OLED屏幕上。

所需硬件清单:

  • STM32F103C8T6最小系统板(或兼容开发板)
  • DS1302实时时钟模块(含32.768kHz晶振)
  • 0.96寸OLED显示屏(SSD1306驱动,SPI接口)
  • 杜邦线若干
  • 备用电池(CR2032,用于DS1302断电保持)

提示:DS1302模块上的备用电池槽务必安装电池,否则断电后时间信息将丢失。

硬件连接示意图:

STM32引脚DS1302引脚OLED引脚
PC11CE-
PC12SCLKD0(SCK)
PC10I/O-
PB8-CS
PB9-DC
PB10-RES
PA7-D1(MOSI)

2. DS1302驱动原理与实现

2.1 DS1302通信协议解析

DS1302采用三线制通信接口(CE、SCLK、I/O),其通信时序既非I2C也非SPI,需要GPIO模拟。关键时序参数如下:

  • 建立时间(tSU):CE拉高前,SCLK必须保持低电平至少4μs
  • 保持时间(tH):数据传输后,CE拉低前需保持至少4μs
  • 时钟周期(tCLK):最小周期为1μs(即最高1MHz时钟)

典型写时序流程:

  1. CE拉高使能通信
  2. 发送8位命令字节(LSB first)
  3. 发送8位数据字节(LSB first)
  4. CE拉低结束通信
// 向DS1302写入单字节函数示例 void DS1302_WriteByte(uint8_t data) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // 配置I/O为输出模式 GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); for(uint8_t i=0; i<8; i++) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, (data & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET); delay_us(1); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET); data >>= 1; } }

2.2 寄存器配置要点

DS1302内部有多个控制和时间寄存器,使用时需注意:

  1. 写保护寄存器(0x8E):修改时间前必须先清零WP位
  2. 时钟停止位(CH):秒寄存器的bit7,0=振荡器运行,1=停止
  3. 12/24小时模式:小时寄存器的bit7决定模式

时间寄存器均以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); }

3. OLED显示驱动实现

3.1 SSD1306初始化配置

0.96寸OLED通常使用SSD1306驱动芯片,通过SPI接口通信。关键初始化序列:

void OLED_Init(void) { OLED_RESET_HIGH(); delay_ms(100); OLED_RESET_LOW(); delay_ms(100); OLED_RESET_HIGH(); OLED_WriteCmd(0xAE); // 关闭显示 OLED_WriteCmd(0xD5); // 设置时钟分频 OLED_WriteCmd(0x80); // 建议值 OLED_WriteCmd(0xA8); // 设置复用率 OLED_WriteCmd(0x3F); // 1/64 duty OLED_WriteCmd(0xD3); // 设置显示偏移 OLED_WriteCmd(0x00); // 无偏移 // ...更多配置命令 OLED_WriteCmd(0xAF); // 开启显示 }

3.2 显示优化技巧

为提高显示效果,可采用以下方法:

  1. 双缓冲技术:在内存中完成画面绘制后再整体刷新
  2. 局部刷新:只更新变化部分,减少闪烁
  3. 字体压缩:使用自定义字体节省存储空间
// 显示时间函数示例 void OLED_ShowTime(uint8_t x, uint8_t y, RTC_TimeTypeDef *time) { char buf[9]; sprintf(buf, "%02d:%02d:%02d", time->Hours, time->Minutes, time->Seconds); OLED_ShowString(x, y, (uint8_t *)buf, 16); }

4. 系统整合与调试

4.1 主程序逻辑架构

完整的应用需协调三个模块:

  1. DS1302时间获取
  2. OLED显示更新
  3. 用户交互处理

典型主循环结构:

int main(void) { HAL_Init(); SystemClock_Config(); DS1302_Init(); OLED_Init(); RTC_TimeTypeDef currentTime; uint8_t lastMinute = 0; while(1) { DS1302_GetTime(&currentTime); if(currentTime.Minutes != lastMinute) { OLED_Clear(); OLED_ShowTime(30, 2, &currentTime); lastMinute = currentTime.Minutes; } HAL_Delay(200); } }

4.2 常见问题排查

问题1:DS1302读取时间全为零

  • 检查CE引脚是否正常拉高
  • 确认时序延时满足要求
  • 测量模块供电电压(2.0-5.5V)

问题2:OLED显示乱码

  • 确认SPI时钟极性(CPOL)和相位(CPHA)设置
  • 检查CS信号是否正常
  • 验证初始化序列是否正确

问题3:时间走时不准

  • 更换32.768kHz晶振
  • 检查晶振负载电容(通常6pF)
  • 避免长时间高温环境

5. 进阶功能扩展

5.1 添加温度补偿

DS1302精度受温度影响较大,可外接温度传感器进行补偿:

float temp_compensation(float temp) { // 典型补偿曲线:-0.035ppm/°C² return -0.035 * pow(temp - 25, 2); }

5.2 实现闹钟功能

利用STM32的RTC或定时器实现闹钟:

void check_alarm(RTC_TimeTypeDef *time) { if(time->Hours == alarm.hour && time->Minutes == alarm.minute && time->Seconds == 0) { trigger_alarm(); } }

5.3 低功耗优化

对于电池供电应用:

  1. 关闭未用外设时钟
  2. 使用STM32的睡眠模式
  3. 降低OLED刷新率
void enter_low_power_mode(void) { HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); }

通过本项目的实践,开发者可以掌握嵌入式系统中实时时钟的应用要点。在实际产品中,建议将DS1302驱动和OLED显示封装为独立模块,提高代码复用性。对于需要更高精度的场合,可考虑改用DS3231等带温度补偿的RTC芯片。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询