全部版块 我的主页
论坛 新商科论坛 四区(原工商管理论坛) 商学院 管理科学与工程
96 0
2025-12-01

项目目标

本项目旨在实现一个基于嵌入式系统的智能计步提醒装置,主要功能包括:

  • 利用RTC实时时钟模块记录当前时间,精确到年、时、分、秒。
  • 通过MPU6050传感器采集运动数据,并统计实时步数。
  • 支持按键设置目标步数与闹钟时间。当用户完成设定的步数目标时,蜂鸣器持续响铃3秒,表示当日运动达标;当到达闹钟设定时间时,蜂鸣器响铃6秒进行提醒。
  • 使用OLED显示屏展示信息,并可通过按键切换不同显示模式。界面设计包含以下三种模式:

界面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(&current_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(&current_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辅助解答。这种“自主为主、参考为辅”的方式更有助于提升编程能力与系统理解深度。

尽管当前程序未能完全实现全部预定功能,但后续将持续优化代码逻辑与模块交互,逐步完善整体功能。

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群