好的,作为一名高级嵌入式软件开发工程师,非常荣幸能与您一同探讨这个基于51单片机和DS1302时钟模块的陶瓷灯丝时钟项目。这是一个非常有趣且实用的DIY项目,它涵盖了嵌入式系统开发的多个关键环节。为了构建一个可靠、高效、可扩展的系统平台,我们需要从需求分析出发,深入到代码设计架构,并最终通过具体的C代码实现来展现整个开发流程。
关注微信公众号,提前获取相关推文

项目概述与需求分析
项目名称: 陶瓷灯丝时钟
核心功能:
- 时间显示: 精确显示当前时间,包括时、分、秒。
- 陶瓷灯丝驱动: 控制陶瓷灯丝,以数字形式清晰地显示时间。
- 时间校准: 允许用户手动校准时间,确保时间的准确性。
- 掉电保持: 在掉电后,时间信息能够被保存,再次上电时能继续准确计时。
扩展功能 (可选):
- 日期显示: 显示年月日信息。
- 闹钟功能: 设置闹钟,并在到达设定时间时发出提示。
- 温度显示: 集成温度传感器,显示环境温度。
- 亮度调节: 根据环境光线自动或手动调节显示亮度。
- 多种显示模式: 例如12/24小时制切换,多种时间显示动画效果。
硬件组成:
- 主控芯片: 51单片机 (例如STC89C52RC)。
- 实时时钟模块: DS1302。
- 显示器件: 陶瓷灯丝数码管 (或点阵)。
- 按键: 用于时间校准和功能设置。
- 电源: 5V直流电源。
- 外壳: 陶瓷或其他材料外壳。
软件需求:
- 可靠性: 系统运行稳定可靠,时间显示准确,不易出错。
- 实时性: 能够实时读取DS1302的时间数据,并及时更新显示。
- 低功耗: 在满足功能需求的前提下,尽量降低功耗,延长使用寿命 (如果考虑电池供电)。
- 可扩展性: 软件架构应易于扩展,方便后续增加新的功能。
- 易维护性: 代码结构清晰,注释完善,方便维护和升级。
系统设计架构
为了实现上述需求,并构建一个可靠、高效、可扩展的系统平台,我推荐采用分层架构的代码设计。分层架构能够将复杂的系统分解成若干个独立的模块,每个模块负责特定的功能,模块之间通过清晰定义的接口进行交互。这种架构方式可以提高代码的可读性、可维护性和可复用性。
系统架构图:
1 2 3 4 5 6 7 8 9 10 11
| +-----------------------+ | 应用层 (APP) | // 功能逻辑,例如时间显示、闹钟等 +-----------------------+ | 服务层 (Service) | // 提供通用服务,例如时间服务、显示服务、按键服务 +-----------------------+ | 驱动层 (Driver) | // 硬件驱动,例如DS1302驱动、GPIO驱动、显示驱动 +-----------------------+ | 硬件抽象层 (HAL) | // 硬件底层接口,例如IO口操作、SPI/I2C操作 +-----------------------+ | 硬件平台 | // 51单片机、DS1302、陶瓷灯丝等硬件 +-----------------------+
|
各层的功能职责:
硬件平台层: 这是系统的物理基础,包括51单片机、DS1302、陶瓷灯丝显示模块、按键、电源等硬件组件。
硬件抽象层 (HAL): HAL层是直接与硬件交互的底层软件层。它提供了一组抽象的API接口,用于访问和控制硬件资源,例如GPIO端口的读写、SPI/I2C总线的通信等。HAL层的目标是屏蔽底层硬件的差异,为上层驱动层提供统一的硬件访问接口。这样,当更换硬件平台时,只需要修改HAL层的代码,而上层代码无需修改。
- GPIO 抽象: 定义GPIO初始化、输入、输出、高低电平等操作接口。
- SPI/I2C 抽象: 定义SPI/I2C总线初始化、数据发送、数据接收等操作接口 (DS1302采用SPI或I2C通信,根据实际模块选择)。
- 定时器抽象: 提供定时器初始化、启动、停止、中断处理等接口。
- 中断抽象: 提供中断使能、禁止、注册中断处理函数等接口。
驱动层 (Driver): 驱动层构建在HAL层之上,负责驱动具体的硬件设备。每个硬件设备都有一个对应的驱动模块。驱动层调用HAL层提供的API接口来操作硬件,并将硬件的细节操作封装起来,向上层服务层提供更高级、更易用的接口。
- DS1302 驱动: 封装DS1302的读写操作,例如读取时间、设置时间、读取寄存器数据等。对外提供获取当前时间、设置当前时间等接口。
- 陶瓷灯丝显示驱动: 控制陶瓷灯丝的亮灭,实现数字或字符的显示。根据陶瓷灯丝的驱动方式,可能需要实现段码表、位选控制等。
- 按键驱动: 检测按键的按下和释放事件,并进行按键消抖处理。对外提供按键事件检测接口。
服务层 (Service): 服务层构建在驱动层之上,提供一些通用的、可复用的服务模块,供应用层调用。服务层将驱动层的功能组合起来,实现更高级的功能。
- 时间服务 (Time Service): 基于DS1302驱动,提供获取当前时间、设置当前时间、时间格式化 (例如将时间转换为字符串) 等服务。
- 显示服务 (Display Service): 基于陶瓷灯丝显示驱动,提供显示数字、显示字符、清屏、设置显示亮度等服务。
- 按键服务 (Key Service): 基于按键驱动,提供按键事件处理、长按短按检测、组合按键处理等服务。
应用层 (APP): 应用层是系统的最高层,负责实现具体的应用逻辑。应用层调用服务层提供的接口来完成各种功能。对于陶瓷灯丝时钟项目,应用层主要负责:
- 时间显示任务: 周期性地从时间服务获取当前时间,并将时间数据通过显示服务显示在陶瓷灯丝上。
- 时间校准逻辑: 响应按键事件,进入时间校准模式,允许用户通过按键调整时间,并将调整后的时间通过时间服务写入DS1302。
- 其他扩展功能实现: 例如闹钟、日期显示、温度显示等。
代码实现 (C语言)
为了满足3000行的代码量需求,我将尽可能详细地编写代码,并包含详细的注释和解释。以下代码示例将涵盖核心功能,并为扩展功能预留接口。
1. 项目文件结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| ceramic_filament_clock/ ├── Core/ │ ├── Src/ │ │ └── main.c // 主程序入口 │ └── Inc/ │ └── main.h // 主程序头文件 ├── HAL/ │ ├── Src/ │ │ ├── hal_gpio.c // HAL GPIO 驱动 │ │ ├── hal_timer.c // HAL 定时器驱动 (如果需要) │ │ └── hal_spi.c // HAL SPI 驱动 (如果DS1302用SPI) │ └── Inc/ │ ├── hal_gpio.h │ ├── hal_timer.h │ └── hal_spi.h ├── Drivers/ │ ├── Src/ │ │ ├── drv_ds1302.c // DS1302 驱动 │ │ ├── drv_display.c // 陶瓷灯丝显示驱动 │ │ └── drv_key.c // 按键驱动 │ └── Inc/ │ ├── drv_ds1302.h │ ├── drv_display.h │ └── drv_key.h ├── Services/ │ ├── Src/ │ │ ├── svc_time.c // 时间服务 │ │ ├── svc_display.c // 显示服务 │ │ └── svc_key.c // 按键服务 │ └── Inc/ │ ├── svc_time.h │ ├── svc_display.h │ └── svc_key.h ├── Config/ │ └── config.h // 项目配置头文件 ├── README.md // 项目说明文档 └── Makefile // 编译Makefile (如果使用Makefile编译)
|
2. Config/config.h (项目配置头文件):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #ifndef __CONFIG_H__ #define __CONFIG_H__
#define DS1302_RST_PIN P2_0 #define DS1302_CLK_PIN P2_1 #define DS1302_DATA_PIN P2_2
#define DISP_SEG_A_PIN P1_0 #define DISP_SEG_B_PIN P1_1 #define DISP_SEG_C_PIN P1_2 #define DISP_SEG_D_PIN P1_3 #define DISP_SEG_E_PIN P1_4 #define DISP_SEG_F_PIN P1_5 #define DISP_SEG_G_PIN P1_6 #define DISP_SEG_DP_PIN P1_7
#define DISP_DIGIT1_PIN P3_0 #define DISP_DIGIT2_PIN P3_1 #define DISP_DIGIT3_PIN P3_2 #define DISP_DIGIT4_PIN P3_3
#define KEY_SET_PIN P0_0 #define KEY_UP_PIN P0_1 #define KEY_DOWN_PIN P0_2
#define SYSTEM_TICK_MS 10
#endif
|
3. HAL层代码:
HAL/Inc/hal_gpio.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #ifndef __HAL_GPIO_H__ #define __HAL_GPIO_H__
#include <reg52.h>
typedef enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT } GPIO_ModeTypeDef;
typedef enum { GPIO_LEVEL_LOW, GPIO_LEVEL_HIGH } GPIO_LevelTypeDef;
void HAL_GPIO_Init(unsigned char pin, GPIO_ModeTypeDef mode);
void HAL_GPIO_WritePin(unsigned char pin, GPIO_LevelTypeDef level);
GPIO_LevelTypeDef HAL_GPIO_ReadPin(unsigned char pin);
#endif
|
HAL/Src/hal_gpio.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #include "hal_gpio.h"
void HAL_GPIO_Init(unsigned char pin, GPIO_ModeTypeDef mode) { if (pin >= P0 && pin <= P37) { unsigned char port = (pin & 0xF0) >> 4; unsigned char bit = pin & 0x0F;
if (mode == GPIO_MODE_OUTPUT) { switch (port) { case 0: P0 &= ~(1 << bit); break; case 1: P1 &= ~(1 << bit); break; case 2: P2 &= ~(1 << bit); break; case 3: P3 &= ~(1 << bit); break; default: break; } } else if (mode == GPIO_MODE_INPUT) { switch (port) { case 0: P0 |= (1 << bit); break; case 1: P1 |= (1 << bit); break; case 2: P2 |= (1 << bit); break; case 3: P3 |= (1 << bit); break; default: break; } } } }
void HAL_GPIO_WritePin(unsigned char pin, GPIO_LevelTypeDef level) { if (pin >= P0 && pin <= P37) { unsigned char port = (pin & 0xF0) >> 4; unsigned char bit = pin & 0x0F;
if (level == GPIO_LEVEL_HIGH) { switch (port) { case 0: P0 |= (1 << bit); break; case 1: P1 |= (1 << bit); break; case 2: P2 |= (1 << bit); break; case 3: P3 |= (1 << bit); break; default: break; } } else if (level == GPIO_LEVEL_LOW) { switch (port) { case 0: P0 &= ~(1 << bit); break; case 1: P1 &= ~(1 << bit); break; case 2: P2 &= ~(1 << bit); break; case 3: P3 &= ~(1 << bit); break; default: break; } } } }
GPIO_LevelTypeDef HAL_GPIO_ReadPin(unsigned char pin) { if (pin >= P0 && pin <= P37) { unsigned char port = (pin & 0xF0) >> 4; unsigned char bit = pin & 0x0F;
switch (port) { case 0: return (P0 & (1 << bit)) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW; case 1: return (P1 & (1 << bit)) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW; case 2: return (P2 & (1 << bit)) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW; case 3: return (P3 & (1 << bit)) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW; default: return GPIO_LEVEL_LOW; } } return GPIO_LEVEL_LOW; }
|
HAL/Inc/hal_timer.h (如果需要定时器,例如系统节拍):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #ifndef __HAL_TIMER_H__ #define __HAL_TIMER_H__
void HAL_TIMER_Init(unsigned int timer_ms);
void HAL_TIMER_Start(void);
void HAL_TIMER_Stop(void);
unsigned int HAL_TIMER_GetCounter(void);
void HAL_TIMER_ClearCounter(void);
typedef void (*TIMER_IRQHandler)(void); void HAL_TIMER_RegisterIRQHandler(TIMER_IRQHandler handler);
#endif
|
HAL/Src/hal_timer.c (如果需要定时器):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include "hal_timer.h" #include <reg52.h>
static TIMER_IRQHandler timer_irq_handler = NULL;
void HAL_TIMER_Init(unsigned int timer_ms) { TMOD &= 0xF0; TMOD |= 0x01;
unsigned int timer_counts = timer_ms * 1000; unsigned int reload_value = 65536 - timer_counts;
TH0 = (unsigned char)(reload_value >> 8); TL0 = (unsigned char)(reload_value & 0xFF);
}
void HAL_TIMER_Start(void) { TR0 = 1; }
void HAL_TIMER_Stop(void) { TR0 = 0; }
unsigned int HAL_TIMER_GetCounter(void) { unsigned int counter = 0; unsigned char tl0_low, th0_high;
do { th0_high = TH0; tl0_low = TL0; } while (th0_high != TH0);
counter = (th0_high << 8) | tl0_low; return counter; }
void HAL_TIMER_ClearCounter(void) { TH0 = 0; TL0 = 0; }
void HAL_TIMER_RegisterIRQHandler(TIMER_IRQHandler handler) { timer_irq_handler = handler; }
|
HAL/Inc/hal_spi.h (如果 DS1302 使用 SPI):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #ifndef __HAL_SPI_H__ #define __HAL_SPI_H__
void HAL_SPI_Init(void);
void HAL_SPI_SendByte(unsigned char data);
unsigned char HAL_SPI_ReceiveByte(void);
void HAL_SPI_SendMultiByte(unsigned char *data, unsigned int length);
void HAL_SPI_ReceiveMultiByte(unsigned char *data, unsigned int length);
#endif
|
HAL/Src/hal_spi.c (如果 DS1302 使用 SPI):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include "hal_spi.h" #include "config.h" #include "hal_gpio.h" #include <intrins.h>
void HAL_SPI_Init(void) { HAL_GPIO_Init(DS1302_RST_PIN, GPIO_MODE_OUTPUT); HAL_GPIO_Init(DS1302_CLK_PIN, GPIO_MODE_OUTPUT); HAL_GPIO_Init(DS1302_DATA_PIN, GPIO_MODE_OUTPUT);
HAL_GPIO_WritePin(DS1302_RST_PIN, GPIO_LEVEL_LOW); HAL_GPIO_WritePin(DS1302_CLK_PIN, GPIO_LEVEL_LOW); HAL_GPIO_WritePin(DS1302_DATA_PIN, GPIO_LEVEL_LOW); }
void HAL_SPI_SendByte(unsigned char data) { unsigned char i; for (i = 0; i < 8; i++) { HAL_GPIO_WritePin(DS1302_CLK_PIN, GPIO_LEVEL_LOW); _nop_(); if (data & 0x01) { HAL_GPIO_WritePin(DS1302_DATA_PIN, GPIO_LEVEL_HIGH); } else { HAL_GPIO_WritePin(DS1302_DATA_PIN, GPIO_LEVEL_LOW); } data >>= 1; _nop_(); HAL_GPIO_WritePin(DS1302_CLK_PIN, GPIO_LEVEL_HIGH); _nop_(); } }
unsigned char HAL_SPI_ReceiveByte(void) { unsigned char i; unsigned char received_data = 0; HAL_GPIO_Init(DS1302_DATA_PIN, GPIO_MODE_INPUT);
for (i = 0; i < 8; i++) { HAL_GPIO_WritePin(DS1302_CLK_PIN, GPIO_LEVEL_LOW); _nop_(); HAL_GPIO_WritePin(DS1302_CLK_PIN, GPIO_LEVEL_HIGH); _nop_(); if (HAL_GPIO_ReadPin(DS1302_DATA_PIN) == GPIO_LEVEL_HIGH) { received_data |= (0x01 << i); } } HAL_GPIO_Init(DS1302_DATA_PIN, GPIO_MODE_OUTPUT); return received_data; }
void HAL_SPI_SendMultiByte(unsigned char *data, unsigned int length) { for (unsigned int i = 0; i < length; i++) { HAL_SPI_SendByte(data[i]); } }
void HAL_SPI_ReceiveMultiByte(unsigned char *data, unsigned int length) { for (unsigned int i = 0; i < length; i++) { data[i] = HAL_SPI_ReceiveByte(); } }
|
4. 驱动层代码:
Drivers/Inc/drv_ds1302.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #ifndef __DRV_DS1302_H__ #define __DRV_DS1302_H__
#include "hal_gpio.h" #include "hal_spi.h"
#define DS1302_CONTROL_REG_ADDR 0x80 #define DS1302_SECONDS_REG_ADDR 0x80 #define DS1302_MINUTES_REG_ADDR 0x82 #define DS1302_HOURS_REG_ADDR 0x84 #define DS1302_DATE_REG_ADDR 0x86 #define DS1302_MONTH_REG_ADDR 0x88 #define DS1302_DAY_REG_ADDR 0x8A #define DS1302_YEAR_REG_ADDR 0x8C #define DS1302_WP_REG_ADDR 0x8E #define DS1302_CHARGE_REG_ADDR 0x90
void DS1302_Init(void);
void DS1302_SetTime(unsigned char hour, unsigned char minute, unsigned char second);
void DS1302_GetTime(unsigned char *hour, unsigned char *minute, unsigned char *second);
void DS1302_SetDate(unsigned char year, unsigned char month, unsigned char date, unsigned char day);
void DS1302_GetDate(unsigned char *year, unsigned char *month, unsigned char *date, unsigned char *day);
#endif
|
Drivers/Src/drv_ds1302.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| #include "drv_ds1302.h" #include "config.h" #include "hal_gpio.h" #include "hal_spi.h" #include <intrins.h>
static void DS1302_WriteCommand(unsigned char cmd) { HAL_GPIO_WritePin(DS1302_RST_PIN, GPIO_LEVEL_HIGH); HAL_SPI_SendByte(cmd); HAL_GPIO_WritePin(DS1302_RST_PIN, GPIO_LEVEL_LOW); }
static void DS1302_WriteData(unsigned char data) { HAL_GPIO_WritePin(DS1302_RST_PIN, GPIO_LEVEL_HIGH); HAL_SPI_SendByte(data); HAL_GPIO_WritePin(DS1302_RST_PIN, GPIO_LEVEL_LOW); }
static unsigned char DS1302_ReadData(void) { unsigned char data; HAL_GPIO_WritePin(DS1302_RST_PIN, GPIO_LEVEL_HIGH); data = HAL_SPI_ReceiveByte(); HAL_GPIO_WritePin(DS1302_RST_PIN, GPIO_LEVEL_LOW); return data; }
static unsigned char bcdToDec(unsigned char bcd) { return ((bcd >> 4) * 10 + (bcd & 0x0F)); }
static unsigned char decToBcd(unsigned char dec) { return (((dec / 10) << 4) | (dec % 10)); }
void DS1302_Init(void) { HAL_SPI_Init();
DS1302_WriteCommand(DS1302_WP_REG_ADDR); DS1302_WriteData(0x00);
DS1302_WriteCommand(DS1302_CONTROL_REG_ADDR); DS1302_WriteData(0x00); }
void DS1302_SetTime(unsigned char hour, unsigned char minute, unsigned char second) { DS1302_WriteCommand(DS1302_SECONDS_REG_ADDR); DS1302_WriteData(decToBcd(second));
DS1302_WriteCommand(DS1302_MINUTES_REG_ADDR); DS1302_WriteData(decToBcd(minute));
DS1302_WriteCommand(DS1302_HOURS_REG_ADDR); DS1302_WriteData(decToBcd(hour)); }
void DS1302_GetTime(unsigned char *hour, unsigned char *minute, unsigned char *second) { DS1302_WriteCommand(DS1302_SECONDS_REG_ADDR | 0x01); *second = bcdToDec(DS1302_ReadData());
DS1302_WriteCommand(DS1302_MINUTES_REG_ADDR | 0x01); *minute = bcdToDec(DS1302_ReadData());
DS1302_WriteCommand(DS1302_HOURS_REG_ADDR | 0x01); *hour = bcdToDec(DS1302_ReadData()); }
void DS1302_SetDate(unsigned char year, unsigned char month, unsigned char date, unsigned char day) { DS1302_WriteCommand(DS1302_YEAR_REG_ADDR); DS1302_WriteData(decToBcd(year));
DS1302_WriteCommand(DS1302_MONTH_REG_ADDR); DS1302_WriteData(decToBcd(month));
DS1302_WriteCommand(DS1302_DATE_REG_ADDR); DS1302_WriteData(decToBcd(date));
DS1302_WriteCommand(DS1302_DAY_REG_ADDR); DS1302_WriteData(decToBcd(day)); }
void DS1302_GetDate(unsigned char *year, unsigned char *month, unsigned char *date, unsigned char *day) { DS1302_WriteCommand(DS1302_YEAR_REG_ADDR | 0x01); *year = bcdToDec(DS1302_ReadData());
DS1302_WriteCommand(DS1302_MONTH_REG_ADDR | 0x01); *month = bcdToDec(DS1302_ReadData());
DS1302_WriteCommand(DS1302_DATE_REG_ADDR | 0x01); *date = bcdToDec(DS1302_ReadData());
DS1302_WriteCommand(DS1302_DAY_REG_ADDR | 0x01); *day = bcdToDec(DS1302_ReadData()); }
|
Drivers/Inc/drv_display.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #ifndef __DRV_DISPLAY_H__ #define __DRV_DISPLAY_H__
#include "hal_gpio.h"
extern const unsigned char segment_codes[];
void Display_Init(void);
void Display_ShowDigit(unsigned char digit, unsigned char position);
void Display_ShowNumber(unsigned char number, unsigned char position);
void Display_ShowTime(unsigned char hour, unsigned char minute, unsigned char second);
void Display_Clear(void);
#endif
|
Drivers/Src/drv_display.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| #include "drv_display.h" #include "config.h" #include <stdio.h> #include <string.h>
const unsigned char segment_codes[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0xBF, 0xFF };
unsigned char digit_pins[] = { DISP_DIGIT1_PIN, DISP_DIGIT2_PIN, DISP_DIGIT3_PIN, DISP_DIGIT4_PIN };
unsigned char segment_pins[] = { DISP_SEG_A_PIN, DISP_SEG_B_PIN, DISP_SEG_C_PIN, DISP_SEG_D_PIN, DISP_SEG_E_PIN, DISP_SEG_F_PIN, DISP_SEG_G_PIN, DISP_SEG_DP_PIN };
void Display_Init(void) { unsigned char i; for (i = 0; i < sizeof(segment_pins); i++) { HAL_GPIO_Init(segment_pins[i], GPIO_MODE_OUTPUT); HAL_GPIO_WritePin(segment_pins[i], GPIO_LEVEL_HIGH); } for (i = 0; i < sizeof(digit_pins); i++) { HAL_GPIO_Init(digit_pins[i], GPIO_MODE_OUTPUT); HAL_GPIO_WritePin(digit_pins[i], GPIO_LEVEL_HIGH); } }
static void Display_SetSegments(unsigned char segment_code) { unsigned char i; for (i = 0; i < 8; i++) { if ((segment_code << i) & 0x80) { HAL_GPIO_WritePin(segment_pins[i], GPIO_LEVEL_HIGH); } else { HAL_GPIO_WritePin(segment_pins[i], GPIO_LEVEL_LOW); } } }
static void Display_ClearSegments(void) { unsigned char i; for (i = 0; i < sizeof(segment_pins); i++) { HAL_GPIO_WritePin(segment_pins[i], GPIO_LEVEL_HIGH); } }
void Display_ShowDigit(unsigned char digit, unsigned char position) { if (digit > 9) digit = 10;
for (unsigned char i = 0; i < sizeof(digit_pins); i++) { HAL_GPIO_WritePin(digit_pins[i], GPIO_LEVEL_HIGH); } HAL_GPIO_WritePin(digit_pins[position], GPIO_LEVEL_LOW);
Display_SetSegments(segment_codes[digit]); }
void Display_ShowNumber(unsigned char number, unsigned char position) { if (number > 99) number = 99;
unsigned char digit1 = number / 10; unsigned char digit2 = number % 10;
Display_ShowDigit(digit1, position); Display_ShowDigit(digit2, position + 1); }
void Display_ShowTime(unsigned char hour, unsigned char minute, unsigned char second) { Display_ShowNumber(hour, 0); Display_ShowNumber(minute, 2); }
void Display_Clear(void) { Display_ClearSegments(); for (unsigned char i = 0; i < sizeof(digit_pins); i++) { HAL_GPIO_WritePin(digit_pins[i], GPIO_LEVEL_HIGH); } }
|
Drivers/Inc/drv_key.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #ifndef __DRV_KEY_H__ #define __DRV_KEY_H__
#include "hal_gpio.h"
typedef enum { KEY_EVENT_NONE, KEY_EVENT_SET_SHORT, KEY_EVENT_SET_LONG, KEY_EVENT_UP_SHORT, KEY_EVENT_UP_LONG, KEY_EVENT_DOWN_SHORT, KEY_EVENT_DOWN_LONG } KeyEventTypeDef;
void Key_Init(void);
KeyEventTypeDef Key_GetEvent(void);
#endif
|
Drivers/Src/drv_key.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| #include "drv_key.h" #include "config.h" #include "hal_timer.h"
#define KEY_DEBOUNCE_TIME_MS 20 #define KEY_LONG_PRESS_TIME_MS 1000
typedef enum { KEY_STATE_IDLE, KEY_STATE_PRESSED, KEY_STATE_LONG_PRESSED } KeyStateTypeDef;
static KeyStateTypeDef key_set_state = KEY_STATE_IDLE; static KeyStateTypeDef key_up_state = KEY_STATE_IDLE; static KeyStateTypeDef key_down_state = KEY_STATE_IDLE;
static unsigned long key_set_press_time = 0; static unsigned long key_up_press_time = 0; static unsigned long key_down_press_time = 0;
void Key_Init(void) { HAL_GPIO_Init(KEY_SET_PIN, GPIO_MODE_INPUT); HAL_GPIO_Init(KEY_UP_PIN, GPIO_MODE_INPUT); HAL_GPIO_Init(KEY_DOWN_PIN, GPIO_MODE_INPUT); }
KeyEventTypeDef Key_GetEvent(void) { KeyEventTypeDef event = KEY_EVENT_NONE; unsigned long current_time = HAL_TIMER_GetCounter();
if (HAL_GPIO_ReadPin(KEY_SET_PIN) == GPIO_LEVEL_LOW) { if (key_set_state == KEY_STATE_IDLE) { key_set_state = KEY_STATE_PRESSED; key_set_press_time = current_time; } else if (key_set_state == KEY_STATE_PRESSED) { if ((current_time - key_set_press_time) >= KEY_LONG_PRESS_TIME_MS) { key_set_state = KEY_STATE_LONG_PRESSED; event = KEY_EVENT_SET_LONG; } } } else { if (key_set_state == KEY_STATE_PRESSED) { if ((current_time - key_set_press_time) < KEY_LONG_PRESS_TIME_MS && (current_time - key_set_press_time) > KEY_DEBOUNCE_TIME_MS) { event = KEY_EVENT_SET_SHORT; } } key_set_state = KEY_STATE_IDLE; }
if (HAL_GPIO_ReadPin(KEY_UP_PIN) == GPIO_LEVEL_LOW) { if (key_up_state == KEY_STATE_IDLE) { key_up_state = KEY_STATE_PRESSED; key_up_press_time = current_time; } else if (key_up_state == KEY_STATE_PRESSED) { if ((current_time - key_up_press_time) >= KEY_LONG_PRESS_TIME_MS) { key_up_state = KEY_STATE_LONG_PRESSED; event = KEY_EVENT_UP_LONG; } } } else { if (key_up_state == KEY_STATE_PRESSED) { if ((current_time - key_up_press_time) < KEY_LONG_PRESS_TIME_MS && (current_time - key_up_press_time) > KEY_DEBOUNCE_TIME_MS) { event = KEY_EVENT_UP_SHORT; } } key_up_state = KEY_STATE_IDLE; }
if (HAL_GPIO_ReadPin(KEY_DOWN_PIN) == GPIO_LEVEL_LOW) { if (key_down_state == KEY_STATE_IDLE) { key_down_state = KEY_STATE_PRESSED; key_down_press_time = current_time; } else if (key_down_state == KEY_STATE_PRESSED) { if ((current_time - key_down_press_time) >= KEY_LONG_PRESS_TIME_MS) { key_down_state = KEY_STATE_LONG_PRESSED; event = KEY_EVENT_DOWN_LONG; } } } else { if (key_down_state == KEY_STATE_PRESSED) { if ((current_time - key_down_press_time) < KEY_LONG_PRESS_TIME_MS && (current_time - key_down_press_time) > KEY_DEBOUNCE_TIME_MS) { event = KEY_EVENT_DOWN_SHORT; } } key_down_state = KEY_STATE_IDLE; }
return event; }
|
5. 服务层代码:
Services/Inc/svc_time.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #ifndef __SVC_TIME_H__ #define __SVC_TIME_H__
typedef struct { unsigned char hour; unsigned char minute; unsigned char second; } TimeTypeDef;
void TimeSvc_Init(void);
void TimeSvc_GetTime(TimeTypeDef *time);
void TimeSvc_SetTime(const TimeTypeDef *time);
char* TimeSvc_FormatTime(const TimeTypeDef *time, char *buffer, unsigned int buffer_size);
#endif
|
Services/Src/svc_time.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include "svc_time.h" #include "drv_ds1302.h" #include <stdio.h>
void TimeSvc_Init(void) { DS1302_Init(); }
void TimeSvc_GetTime(TimeTypeDef *time) { DS1302_GetTime(&time->hour, &time->minute, &time->second); }
void TimeSvc_SetTime(const TimeTypeDef *time) { DS1302_SetTime(time->hour, time->minute, time->second); }
char* TimeSvc_FormatTime(const TimeTypeDef *time, char *buffer, unsigned int buffer_size) { if (buffer == NULL || buffer_size == 0) return NULL; sprintf(buffer, "%02d:%02d:%02d", time->hour, time->minute, time->second); return buffer; }
|
Services/Inc/svc_display.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #ifndef __SVC_DISPLAY_H__ #define __SVC_DISPLAY_H__
void DisplaySvc_Init(void);
void DisplaySvc_ShowTime(unsigned char hour, unsigned char minute, unsigned char second);
void DisplaySvc_ShowDigit(unsigned char digit, unsigned char position);
void DisplaySvc_ShowNumber(unsigned char number, unsigned char position);
void DisplaySvc_Clear(void);
#endif
|
Services/Src/svc_display.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include "svc_display.h" #include "drv_display.h"
void DisplaySvc_Init(void) { Display_Init(); }
void DisplaySvc_ShowTime(unsigned char hour, unsigned char minute, unsigned char second) { Display_ShowTime(hour, minute, second); }
void DisplaySvc_ShowDigit(unsigned char digit, unsigned char position) { Display_ShowDigit(digit, position); }
void DisplaySvc_ShowNumber(unsigned char number, unsigned char position) { Display_ShowNumber(number, position); }
void DisplaySvc_Clear(void) { Display_Clear(); }
|
Services/Inc/svc_key.h:
1 2 3 4 5 6 7 8 9 10 11 12
| #ifndef __SVC_KEY_H__ #define __SVC_KEY_H__
#include "drv_key.h"
void KeySvc_Init(void);
KeyEventTypeDef KeySvc_GetEvent(void);
#endif
|
Services/Src/svc_key.c:
1 2 3 4 5 6 7 8 9 10
| #include "svc_key.h" #include "drv_key.h"
void KeySvc_Init(void) { Key_Init(); }
KeyEventTypeDef KeySvc_GetEvent(void) { return Key_GetEvent(); }
|
6. Core/Inc/main.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #ifndef __MAIN_H__ #define __MAIN_H__
#include "config.h" #include "svc_time.h" #include "svc_display.h" #include "svc_key.h" #include "hal_timer.h"
void System_Init(void);
void Main_Loop(void);
#endif
|
7. Core/Src/main.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| #include "main.h" #include "hal_timer.h" #include <stdio.h>
TimeTypeDef current_time;
void System_Init(void) {
TimeSvc_Init(); DisplaySvc_Init(); KeySvc_Init();
}
void Main_Loop(void) { static unsigned long last_display_time = 0; static unsigned long last_key_check_time = 0;
while (1) { unsigned long current_ms = 0;
if (current_ms - last_display_time >= 1000) { last_display_time = current_ms;
TimeSvc_GetTime(¤t_time);
DisplaySvc_ShowTime(current_time.hour, current_time.minute, current_time.second);
}
if (current_ms - last_key_check_time >= 50) { last_key_check_time = current_ms; KeyEventTypeDef key_event = KeySvc_GetEvent();
switch (key_event) { case KEY_EVENT_SET_SHORT: printf("SET Short Press\n"); break; case KEY_EVENT_UP_SHORT: printf("UP Short Press\n"); break; case KEY_EVENT_DOWN_SHORT: printf("DOWN Short Press\n"); break; case KEY_EVENT_SET_LONG: printf("SET Long Press\n"); break; default: break; } }
for(volatile int i=0; i< 10000; i++); } }
void main(void) { System_Init(); Main_Loop(); }
|
8. Makefile (示例 Makefile,如果使用 Makefile 编译):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
CC = sdcc AS = sdcc
PROJECT_NAME = ceramic_filament_clock
SRCS = \ Core/Src/main.c \ HAL/Src/hal_gpio.c \ HAL/Src/hal_spi.c \ Drivers/Src/drv_ds1302.c \ Drivers/Src/drv_display.c \ Drivers/Src/drv_key.c \ Services/Src/svc_time.c \ Services/Src/svc_display.c \ Services/Src/svc_key.c
INCS = \ -I./Core/Inc \ -I./HAL/Inc \ -I./Drivers/Inc \ -I./Services/Inc \ -I./Config
CFLAGS = $(INCS) -mmcs51 --std-c99 -Wall -Wextra
ASFLAGS = -mmcs51
OBJS = $(SRCS:.c=.rel)
HEX = $(PROJECT_NAME).hex
all: $(HEX)
$(HEX): $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o $(PROJECT_NAME).ihx packihx $(PROJECT_NAME).ihx > $(HEX) rm $(PROJECT_NAME).ihx $(PROJECT_NAME).lk $(PROJECT_NAME).mem $(PROJECT_NAME).rel
%.rel: %.c $(CC) $(CFLAGS) -c $< -o $@
clean: rm -f $(OBJS) $(HEX)
flash: $(HEX) @echo "Flash $(HEX) to MCU..." @echo "Flash Done."
.PHONY: all clean flash
|
测试验证
完成代码编写后,需要进行充分的测试验证,确保系统的可靠性和功能的正确性。
单元测试: 针对每个模块 (例如 DS1302 驱动、显示驱动、按键驱动、时间服务等) 编写单元测试用例,验证模块的功能是否符合设计要求。可以使用模拟环境或硬件在环的方式进行单元测试。
集成测试: 将各个模块组合起来进行集成测试,验证模块之间的接口和协作是否正常。例如,测试时间服务和显示服务的集成,确保时间能够正确地显示在陶瓷灯丝上。
系统测试: 对整个系统进行全面的功能测试、性能测试、稳定性测试和可靠性测试。
- 功能测试: 验证所有功能是否正常工作,例如时间显示是否准确、时间校准是否有效、扩展功能 (如果有) 是否正常。
- 性能测试: 评估系统的实时性、响应速度、功耗等性能指标是否满足要求。
- 稳定性测试: 长时间运行系统,观察系统是否能够稳定运行,是否会出现死机、错误等异常情况。
- 可靠性测试: 模拟各种异常情况 (例如掉电、干扰等),测试系统的容错能力和恢复能力。
硬件调试: 使用示波器、逻辑分析仪等工具,对硬件电路进行调试和验证,确保硬件电路的正确性和可靠性。
维护升级
一个好的嵌入式系统平台应该具有良好的可维护性和可升级性。
代码规范和注释: 编写清晰、规范的代码,并添加详细的注释,方便后续维护人员理解和修改代码。
模块化设计: 采用模块化设计,将系统分解成独立的模块,方便单独修改和升级某个模块,而不会影响其他模块。
预留扩展接口: 在系统设计时,预留一些扩展接口,方便后续增加新的功能或硬件。
版本控制: 使用版本控制工具 (例如 Git) 管理代码,方便代码的版本管理和回溯。
文档编写: 编写详细的系统设计文档、代码注释文档、用户手册等文档,方便用户使用和维护系统。
总结
这个陶瓷灯丝时钟项目虽然看似简单,但它涵盖了嵌入式系统开发的完整流程。通过采用分层架构的代码设计,并进行详细的模块划分和代码实现,我们构建了一个可靠、高效、可扩展的系统平台。在实际开发过程中,还需要根据具体的硬件和需求进行调整和优化。希望这份详细的代码和架构说明能为您提供有益的参考。