340
社区成员
发帖
与我相关
我的任务
分享
主函数部分
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Menu.h"
#include "Key.h"
#include "MyRTC.h"
#include "MPU6050.h"
#include "Timer.h"
/**
* 坐标轴定义:
* 左上角为(0, 0)点
* 横向向右为X轴,取值范围:0~127
* 纵向向下为Y轴,取值范围:0~63
*
* 0 X轴 127
* .------------------------------->
* 0 |
* |
* |
* |
* Y轴 |
* |
* |
* |
* 63 |
* v
*
*/
int main(void)
{
int menu2;
/*OLED初始化*/
OLED_Init();
Key_Init();
MyRTC_Init();
MPU6050_Init();
Timer_Init();
while (1)
{
menu2=menu1();
if (menu2==1){menu2_RencentTime();}
if (menu2==2){menu2_TodayStep();}
if (menu2==3){menu2_SetClock();}
if (menu2==4){menu2_SetStep();}
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否为定时器TIM2更新中断
{
Pedometer1_Tick();
Pedometer2_Tick();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除定时器TIM2更新中断标志位
}
}
菜单函数,其中包括主页面,可选择相应的功能。实时时钟部分通过RTC模块进行时钟显示,步数检测则是用mpu6050模块和定时器来获取获取三轴加速度,进而计算出步数。闹钟设置跟主菜单思想差不多。步数设置也是常规。
#include "stm32f10x.h" // Device header
#include "Key.h"
#include "OLED.h"
#include "MyRTC.h"
#include "MPU6050.h"
#include "Delay.h"
#include <math.h>
#include "pedometer.h"
#include "Timer.h"
uint8_t KeyNum,flag=1,stepnum=0,Setstep=10;
uint16_t alarmselect[]={00,00};
int16_t AX, AY, AZ, GX, GY, GZ;
int menu1(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
//使用各个外设前必须开启时钟,否则对外设的操作无效
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO引脚,赋值为第1号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数
GPIO_SetBits(GPIOA, GPIO_Pin_0); //函数内部会自动根据结构体的参数配置相应寄存器
//实现GPIOA的初始化
OLED_ShowString(0,0," 当前时间 ",OLED_8X16);
OLED_ShowString(0,16," 今日步数 ",OLED_8X16);
OLED_ShowString(0,32," 闹钟设置 ",OLED_8X16);
OLED_ShowString(0,48," 步数设置 ",OLED_8X16);
OLED_Update();
while(1)
{
KeyNum=Key_GetNum();
if(KeyNum==1) //上一项
{
flag--;
if(flag==0){flag=4;}
}
if(KeyNum==2) //下一项
{
flag++;
if(flag==5){flag=1;}
}
if(KeyNum==3) //确认
{
OLED_Clear();
OLED_Update();
return flag;
}
switch(flag)
{
case 1:
{
OLED_ShowString(0,0," 当前时间 ",OLED_8X16);
OLED_ShowString(0,16," 今日步数 ",OLED_8X16);
OLED_ShowString(0,32," 闹钟设置 ",OLED_8X16);
OLED_ShowString(0,48," 步数设置 ",OLED_8X16);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
break;
}
case 2:
{
OLED_ShowString(0,0," 当前时间 ",OLED_8X16);
OLED_ShowString(0,16," 今日步数 ",OLED_8X16);
OLED_ShowString(0,32," 闹钟设置 ",OLED_8X16);
OLED_ShowString(0,48," 步数设置 ",OLED_8X16);
OLED_ReverseArea(0,16,128,16);
OLED_Update();
break;
}
case 3:
{
OLED_ShowString(0,0," 当前时间 ",OLED_8X16);
OLED_ShowString(0,16," 今日步数 ",OLED_8X16);
OLED_ShowString(0,32," 闹钟设置 ",OLED_8X16);
OLED_ShowString(0,48," 步数设置 ",OLED_8X16);
OLED_ReverseArea(0,32,128,16);
OLED_Update();
break;
}
case 4:
{
OLED_ShowString(0,0," 当前时间 ",OLED_8X16);
OLED_ShowString(0,16," 今日步数 ",OLED_8X16);
OLED_ShowString(0,32," 闹钟设置 ",OLED_8X16);
OLED_ShowString(0,48," 步数设置 ",OLED_8X16);
OLED_ReverseArea(0,48,128,16);
OLED_Update();
break;
}
}
}
}
int menu2_RencentTime(void)
{
MyRTC_ReadTime(); //RTC读取时间,最新的时间存储到MyRTC_Time数组中
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_Printf(0,16,OLED_8X16,"Data:%d-%d-%d",MyRTC_Time[0],MyRTC_Time[1],MyRTC_Time[2]);
OLED_Printf(0,32,OLED_8X16,"Time:%d:%d:%d",MyRTC_Time[3],MyRTC_Time[4],MyRTC_Time[5]);
OLED_Update();
while(1)
{
MyRTC_ReadTime();
KeyNum=Key_GetNum();
if(KeyNum==3) //确认
{
OLED_Clear();
OLED_Update();
return flag;
}
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_Printf(0,16,OLED_8X16,"Data:%d-%d-%d",MyRTC_Time[0],MyRTC_Time[1],MyRTC_Time[2]);
OLED_Printf(0,32,OLED_8X16,"Time:%d:%d:%d",MyRTC_Time[3],MyRTC_Time[4],MyRTC_Time[5]);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
}
}
int menu2_TodayStep(void)
{
MPU6050_Init();
Timer_Init();
MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
OLED_ShowString(0,0,"<- ",OLED_8X16);
stepnum=calculate_steps(AX,AY,AZ);
OLED_ShowNum(48,32,stepnum,2,OLED_8X16);
OLED_Update();
while(1)
{
KeyNum=Key_GetNum();
MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
if(KeyNum==3) //确认
{
OLED_Clear();
OLED_Update();
return flag;
}
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ReverseArea(0,0,128,16);
stepnum=calculate_steps(AX,AY,AZ);
OLED_ShowNum(48,32,stepnum,2,OLED_8X16);
OLED_Update();
}
}
int menu2_SetClock(void)
{
uint8_t clockflag=0;
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowNum(32,16,alarmselect[0],2,OLED_8X16);
OLED_ShowString(48,16,":",OLED_8X16);
OLED_ShowNum(56,16,alarmselect[1],2,OLED_8X16);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
while(1)
{
KeyNum=Key_GetNum();
if(KeyNum==1)
{
clockflag++;
if(clockflag==3){clockflag=0;}
}
if(KeyNum==2)
{
if(clockflag==1)
{
alarmselect[0]++;
if(alarmselect[0]==24){alarmselect[0]=0;}
}
if(clockflag==2)
{
alarmselect[1]++;
if(alarmselect[1]==60){alarmselect[1]=0;}
}
}
if(KeyNum==3) //确认
{
OLED_Clear();
OLED_Update();
if(clockflag==0){return flag;}
}
switch(clockflag)
{
case 0:
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowNum(32,16,alarmselect[0],2,OLED_8X16);
OLED_ShowString(48,16,":",OLED_8X16);
OLED_ShowNum(56,16,alarmselect[1],2,OLED_8X16);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
break;
}
case 1:
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowNum(32,16,alarmselect[0],2,OLED_8X16);
OLED_ShowString(48,16,":",OLED_8X16);
OLED_ShowNum(56,16,alarmselect[1],2,OLED_8X16);
OLED_ReverseArea(32,16,16,16);
OLED_Update();
break;
}
case 2:
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowNum(32,16,alarmselect[0],2,OLED_8X16);
OLED_ShowString(48,16,":",OLED_8X16);
OLED_ShowNum(56,16,alarmselect[1],2,OLED_8X16);
OLED_ReverseArea(56,16,16,16);
OLED_Update();
break;
}
}
}
}
int menu2_SetStep(void)
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowNum(48,32,Setstep,3,OLED_8X16);
OLED_Update();
while(1)
{
KeyNum=Key_GetNum();
if(KeyNum==3) //确认
{
OLED_Clear();
OLED_Update();
return flag;
}
if(KeyNum==1)
{
Setstep+=5;
}
if(KeyNum==2)
{
Setstep-=5;
}
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowNum(48,32,Setstep,3,OLED_8X16);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
}
}
void Pedometer1_Tick(void)
{
static uint8_t i=0;
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否为定时器TIM2更新中断
{
if(stepnum>=Setstep && i<6)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0); //将PA1引脚设置为低电平,蜂鸣器鸣叫
i++;
}
else
{
GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
if(stepnum<Setstep){i=0;}
}
}
void Pedometer2_Tick(void)
{
static uint8_t j=0;
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否为定时器TIM2更新中断
{
if(MyRTC_Time[3]==alarmselect[0] && MyRTC_Time[4]==alarmselect[1] &&j<3)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0); //将PA1引脚设置为低电平,蜂鸣器鸣叫
j++;
}
else
{
GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
}
}
步数计算函数,通过检测前后两次的和加速度与阈值进行比较。
#include "pedometer.h" // 引入我们自己的头文件
// --- 核心计步函数实现 ---
/**
* @brief 使用三轴加速度数据计算步数。
*/
int calculate_steps(int16_t AccX, int16_t AccY, int16_t AccZ) {
// 使用静态变量存储状态,使得每次函数调用都能保留上一次的状态。
static int step_count = 0;
static int last_peak_index = -MIN_STEP_DISTANCE; // 初始化为允许第一次步进
static int current_sample_index = 0;
// 1. 计算合成加速度的平方 (Magnitude Squared)
// 使用 int32_t 或 int64_t 存储平方和,防止 16 位整数溢出。
int32_t accX_sq = (int32_t)AccX * AccX;
int32_t accY_sq = (int32_t)AccY * AccY;
int32_t accZ_sq = (int32_t)AccZ * AccZ;
// 合成加速度的平方 (单位是 LSB^2)
int32_t magnitude_squared = accX_sq + accY_sq + accZ_sq;
// 2. 峰值检测与时间检查
// 检查当前合成加速度平方是否超过了预设的动态冲击阈值。
if (magnitude_squared > DYNAMIC_THRESHOLD_SQ) {
// 检查与上次有效步进的时间距离是否足够
int distance = current_sample_index - last_peak_index;
if (distance >= MIN_STEP_DISTANCE) {
// 这是一个有效的步进峰值
step_count++;
// 更新上次峰值的位置
last_peak_index = current_sample_index;
}
}
// 更新采样点索引
current_sample_index++;
return step_count;
}
通过这次学习,比较系统地运用各个模块。在弄步数检测的时候,自己首先想到用mpu6050得到前后两次的和加速度,与某一阈值比较,但是自己写出来的代码还是不太完美,就利用了互联网,整合出更加符合预期的模块。在这个过程中,也学习到了如何实现非阻塞式按键。后面也会逐步学习各种模块,掌握更多编程技巧。