本项目旨在实现一个基于嵌入式系统的智能计步提醒装置,主要功能包括:
界面1:显示当前时间和已走步数。
界面2:进入闹钟设置界面。
界面3:用于设置每日目标步数。
系统由多个功能模块组成,各模块分别封装为独立的C文件和头文件,便于维护与调试。
1. BUZZER.c
蜂鸣器驱动代码,负责控制蜂鸣器在不同事件下的发声行为(如运动达标提示或闹钟提醒)。
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Buzzer_Init (void ){
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIOStructure;
GPIOStructure.GPIO_Mode =GPIO_Mode_Out_PP;
GPIOStructure.GPIO_Pin =GPIO_Pin_14 ;
GPIOStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init (GPIOB ,&GPIOStructure);
GPIO_SetBits ( GPIOB,GPIO_Pin_14);
}
void Buzzer_ON (void ){
GPIO_ResetBits ( GPIOB ,GPIO_Pin_14);
}
void Buzzer_OFF (void ){
GPIO_SetBits ( GPIOB,GPIO_Pin_14);
}
2. BUZZER.h
蜂鸣器模块对应的头文件,声明相关函数接口与宏定义。
#ifndef __BUZZER_H
#define __BUZZER_H
void Buzzer_Init (void );
void Buzzer_ON (void );
void Buzzer_OFF (void );
#endif
3. KEY.c
按键处理程序,检测用户对SET键等输入的操作,实现界面切换及参数设置功能。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "KEY.h"
void KEY_Init(void )
{
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIOStructure;
GPIOStructure.GPIO_Mode =GPIO_Mode_IPU;//上拉输入
GPIOStructure.GPIO_Pin =GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_6 |GPIO_Pin_4 ;
GPIOStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init (GPIOA ,&GPIOStructure);
GPIO_Init (GPIOB ,&GPIOStructure);
}
KeyValue_t KEY_Scan(void)
{
static uint8_t key_up =1;//按键松开标志,记住上次按键是否已松开
//判断是否有按键按下
if(key_up &&(GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_6)==0||
GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_4)==0||
GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_0)==0||
GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_2)==0)){
Delay_ms (10);
key_up =0;
//判断具体是哪个按键,且key_up==1,避免重复检测
if(GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_6) ==0)return KEY_SET;
if(GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_4)==0)return KEY_UP;
if (GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_2)==0)return KEY_DOWN;
if (GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_0)==0)return KEY_OK;
}
//else按键松开,检测是否全部按键松开
else if(GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_6)==1 &&
GPIO_ReadInputDataBit (GPIOA,GPIO_Pin_4)==1 &&
GPIO_ReadInputDataBit (GPIOB,GPIO_Pin_2)==1 &&
GPIO_ReadInputDataBit (GPIOB,GPIO_Pin_0)==1 ){
key_up =1;}
return KEY_NONE;
}
4. KEY.h
按键模块的头文件,提供外部调用所需的函数声明。
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h" // Device header
//定义不同功能的按键变量名
typedef enum {
KEY_NONE=0,//无按键按下
KEY_SET,//切换模式
KEY_UP,//增加数值
KEY_DOWN,//减少数值
KEY_OK//确认操作
}KeyValue_t;
void KEY_Init(void);
KeyValue_t KEY_Scan(void);
#endif
5. RTC.c
实时时钟驱动实现,初始化RTC并获取年、月、日、时、分、秒等时间信息。
//读取软件时间变量的当前时间
void RTC_GetTime(calendar_obj*nowtime)
{
//将软件当前时间显示在要输出的变量上
nowtime->hour =current_time.hour;
nowtime->min =current_time.min ;
nowtime->sec =current_time.sec;
nowtime->date =current_time.date ;
nowtime->month =current_time.month ;
nowtime->year=current_time.year ;
nowtime->week =current_time.week ;
}
//判断闹钟是否应该触发
uint8_t RTC_CheckAlarm(void )
{
//先判断闹钟有没有使能
if(alarm_enabled ==0)
{return 0;}
return (
current_time.hour ==alarm_time.hour&&
current_time.min==alarm_time.min &&
current_time.sec ==0);
//逻辑运算符真为1,加=假为0
//只在第0秒判断避免同一分钟内重复触发
}
//时间递增函数,定时器中断中每秒调用一次
void Time_Increment(void)
{
current_time.sec++;
if(current_time.sec>=60){
current_time.sec=0;
current_time.min++;
if(current_time.min>=60){
current_time.min=0;
current_time.hour++;
if(current_time.hour>=24){
current_time.hour=0;
current_time.date++;}
}
}
}
6. RTC.h
RTC模块头文件,定义时间结构体及相关操作函数。
#ifndef __RTC_H
#define __RTC_H
typedef struct {
uint8_t hour;
uint8_t min;
uint8_t sec;
uint16_t year;
uint8_t month;
uint8_t date;
uint8_t week;
}calendar_obj;//定义结构体类型
extern calendar_obj current_time ;
extern calendar_obj alarm_time;
void RTC_Init (void);
void RTC_SetTime(calendar_obj*giventime);
void RTC_GetTime(calendar_obj*nowtime);
void Time_Increment(void);
uint8_t RTC_CheckAlarm(void );
#endif
7. main.c
主程序入口,协调各模块运行逻辑,完成状态机切换、数据显示更新以及事件响应。
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "RTC.h"
#include "KEY.h"
#include <stdio.h>
#include "BUZZER.h"
#include "Delay.h"
#include "MAIN.h"
#include "stdio.h"
SystemMode_t system_mode= MODE_CLOCK;//系统初始模式闹钟显示
uint8_t cursor_pos=0;//光标位置
char display_buffer[20];//存储的字符可以在OLED上显示
void System_Init(void ){
OLED_Init();
KEY_Init( );
RTC_Init ();
Buzzer_Init ();
OLED_Clear();
}
//在正常时钟模式下显示时间,日期和闹钟状态
void Display_Clock(void ){
calendar_obj time;
RTC_GetTime(&time );//获取当前时间
OLED_ShowString(1,1,"CLOCK_MODE" );
sprintf(display_buffer ,"T:%02d:%02d:%02d",time .hour ,time .min ,time .sec );
OLED_ShowString(3,1,display_buffer );//格式化并显示当前时间
sprintf(display_buffer ,"D:%04d-%02d-%02d",time .year ,time .month,time .date);
OLED_ShowString(2,1,display_buffer );//格式化并显示当前日期
if(alarm_enabled){
sprintf(display_buffer ,"AL:%02d:%02d",alarm_time .hour ,alarm_time .min );
OLED_ShowString(4,1,display_buffer );//格式化并显示当前时间
}else {
OLED_ShowString(4,1,"AL:OFF");
}
}
//时间设置界面
void Display_SetTime(void ){
OLED_Clear();
//标明什么模式
OLED_ShowString(1,1,"SET TIME");
//显示当前设置的时间
sprintf(display_buffer ,"T:%02d:%02d:%02d",current_time .hour ,current_time .min ,current_time .sec );
OLED_ShowString(2,1,display_buffer );
//根据光标位置显示不同的光标指示
switch (cursor_pos){
case 0:
OLED_ShowString(3,6,"^^" );//小时光标
break ;
case 1:
OLED_ShowString(3,9, "^^");//分钟光标
break ;
case 2:
OLED_ShowString(3,12, "^^");//秒钟光标
break ;
}
}
//显示闹钟设置界面
void Display_SetAlarm(void )
{
OLED_ShowString(1,1, "SET ALARM");
sprintf(display_buffer ,"AL:%02d:%02d",alarm_time .hour ,alarm_time .min );
OLED_ShowString(3,1,display_buffer );//显示闹钟时间
OLED_ShowString (2,1,alarm_enabled ?"ENABLED":"DISABLED");//显示闹钟开关状态
//根据光标位置显示不同的光标指示
switch (cursor_pos){
case 0:
OLED_ShowString(4,4,"^^" );//小时光标
break ;
case 1:
OLED_ShowString(4,7, "^^");//分钟光标
break ;
case 2:
OLED_ShowString(4,1, ">>");//秒钟光标
break ;
}
}
//显示闹钟响铃界面
void Display_AlarmRinging(void )
{
OLED_Clear();
OLED_ShowString (1,1,"ALARM!");
OLED_ShowString (2,1,"Press OK to stop");
Buzzer_ON();
}
//根据系统模式调用相应的显示函数
void Display_Update(void )
{
switch (system_mode){
case MODE_CLOCK:
Display_Clock();
break ;
case MODE_SET_TIME:
Display_SetTime();
break ;
case MODE_SET_ALARM:
Display_SetAlarm();
break ;
case MODE_ALARM_RINGING:
Display_AlarmRinging();
break ;
}
}
//扫描按键并根据当前模式和按键值执行相应操作
void Process_Keys(void ){
KeyValue_t key =KEY_Scan();//扫描按键
if(key==KEY_NONE)return;//结束当前函数执行,返回调用这个函数的地方
//按当前系统模式处理按键
switch (system_mode){
case MODE_CLOCK:
if(key==KEY_SET){//时钟模式下,SET键进入时间设置
system_mode=MODE_SET_TIME;
cursor_pos=0;//重置光标位置
}
break ;
case MODE_SET_TIME:
//进入时间设置界面,每个按键都有特定功能
switch(key) {
case KEY_SET:
cursor_pos = (cursor_pos+1)%3;
break ;
case KEY_UP :
if(cursor_pos==0){
current_time.hour=(current_time.hour+1)%24;}
else if(cursor_pos==1){
current_time.min=(current_time.min+1)%60;}
else {
current_time.sec=(current_time.sec+1)%60;}
RTC_SetTime(¤t_time);//更新当前时间
break ;
case KEY_DOWN:
if(cursor_pos==0){
current_time.hour=(current_time.hour+23)%24;}
else if(cursor_pos==1){
current_time.min=(current_time.min+59)%60;}
else {
current_time.sec=(current_time.sec+59)%60;}
RTC_SetTime(¤t_time);//更新当前时间
break ;
case KEY_OK:
system_mode = MODE_SET_ALARM;//确认时间设置完成,进入下一个设置界面
cursor_pos=0;//每进入新界面重置光标位置
break ;
}
break ;
case MODE_SET_ALARM:
switch(key) {
case KEY_SET:
cursor_pos = (cursor_pos+1)%3;
break ;
case KEY_UP ://增加数值或开启时钟
if(cursor_pos==0){
alarm_time.hour=(alarm_time.hour+1)%24;}
else if(cursor_pos==1){
alarm_time.min=(alarm_time.min+1)%60;}
else {
alarm_enabled =1 ; }//闹钟开启
break ;
case KEY_DOWN://减少数值或关闭时钟
if(cursor_pos==0){
alarm_time.hour=(alarm_time.hour+23)%24;}
else if(cursor_pos==1){
alarm_time.min=(alarm_time.min+59)%60;}
else {
alarm_enabled =0 ; }//闹钟开启
break ;
case KEY_OK:
system_mode = MODE_CLOCK;//确认闹钟设置,返回时钟模式
break ;
}
break ;
case MODE_ALARM_RINGING:
if (key==KEY_OK){
system_mode = MODE_CLOCK;
Buzzer_OFF ( );
}
break ;
}
}
//配置定时器2产生1秒定时中断
void Timer_Init (void){
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//开定时器2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//配置定时器参数
TIM_TimeBaseStructure.TIM_Period=1000-1;//自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler=7200-1;//预分频值(72MHz/7200=10kHz)
TIM_TimeBaseStructure.TIM_ClockDivision=0;//时钟分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能更新中断
TIM_Cmd(TIM2,ENABLE);//使能定时器
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//定时器2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能中断通道
NVIC_Init(&NVIC_InitStructure);
}
void TIM2_IRQHandler(void ){
if (TIM_GetITStatus (TIM2,TIM_IT_Update )!=RESET )
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update );
Time_Increment ();
if(system_mode!=MODE_ALARM_RINGING&&RTC_CheckAlarm()){
system_mode=MODE_ALARM_RINGING;
Buzzer_ON();
}
}
}
//系统主循环,初始化后不断处理按键和更新显示
int main (void )
{
System_Init();
Timer_Init();
while(1)
{
Process_Keys();//处理按键输入
Display_Update();//更新显示内容
Delay_ms(100);//延时控制显示刷新率
}
}
8. MAIN.h
主函数依赖的全局头文件,整合所有模块的引用与配置定义。
#ifndef __MAIN_H
#define __MAIN_H
#include "OLED.h"
#include "RTC.h"
#include "KEY.h"
#include "stm32f10x.h" // Device header
typedef enum {
MODE_CLOCK=0,
MODE_SET_TIME,
MODE_SET_ALARM,
MODE_ALARM_RINGING
}SystemMode_t;
extern SystemMode_t system_mode;//全局变量
extern uint8_t alarm_enabled;
void System_Init(void );
void Display_Update(void );
#endif
程序烧录完成后,OLED屏幕首先显示图1所示的默认时钟界面。按下连接至A6引脚的SET按键后,界面跳转至图2的时间设置页面。目前实际运行结果仅实现了基础的时间显示与部分界面切换功能,其余设定功能尚未完全达成预期效果。
本次开发过程中,主要依赖AI生成的代码作为参考进行编写,这一方式带来了两个显著问题:一是无法确保所生成代码在硬件平台上的可行性;二是容易导致自身程序逻辑混乱,影响调试效率。
经过此次实践,我认识到更合理的开发流程应为:首先根据需求独立编写可实现的部分代码 → 参考他人成功的案例进行比对优化 → 在遇到具体技术难点时再借助AI辅助解答。这种“自主为主、参考为辅”的方式更有助于提升编程能力与系统理解深度。
尽管当前程序未能完全实现全部预定功能,但后续将持续优化代码逻辑与模块交互,逐步完善整体功能。
扫码加好友,拉您进群



收藏
