AutoLeaders 控制组 周嘉鑫 第一次任务

控制组-周嘉鑫 2025-11-29 01:46:45

演示

img


源码

手表.zip 7.42M

代码

主函数部分

#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得到前后两次的和加速度,与某一阈值比较,但是自己写出来的代码还是不太完美,就利用了互联网,整合出更加符合预期的模块。在这个过程中,也学习到了如何实现非阻塞式按键。后面也会逐步学习各种模块,掌握更多编程技巧。

...全文
18 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

340

社区成员

发帖
与我相关
我的任务
社区描述
一个供Leaders学习交流的地方
c++javapython 技术论坛(原bbs) 广东省·深圳市
社区管理员
  • 叫我胡萝北
  • CheungZzzc
  • szu_gexu
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧