• 基于STM32跑步路径记录


    基于STM32跑步路径记录

      随着科技不断进步,电子化设备不断进入涌入我们的日常生活。生活水平的提高,各项健身运动应运而生,然后,健身运动不能盲目进行,科学的健身方式才能有效的提升我们自身的身体素质。
      现如今各自手环手表的出现, 通过智能手环,用户可以记录日常生活中的锻炼、睡眠、部分还有饮食等实时数据,并将这些数据与手机、平等同步,起到通过数据指导健康生活的作用。
       智能手环作为可穿戴设备,其功能还是比较强大的,其开发涉及智能手环MCU数据指令到蓝牙IC的传输、蓝牙到APP的数据通信协议、APP到手机内部的通信调试逻辑实现、APP数据到云端服务器的数据库算法设计等一系列的开发。支持多种运动监控模式,可以实时监控身体的各项性能指标。
      我国智能手环产品真正大范围进入消费市场是在2012年以后,一方面是这一时期步入4G时代以后手机智能化趋势加快,与手机一样具备数据分享的智能手环的出现给智能手环的研究和应用打开了另一个世界。另一方面是更多实用性功能快速出现,手环不仅仅是用来记录身体特征的单纯工具,同时也可以满足通话、移动支付、人体识别、智能提醒功能以及分享功能,极大的扩宽了手环的应用人群。佩戴智能手环带来健康、科技、自信有品位的良好感受,成为了高科技产品的典型之一。
    在这里插入图片描述
      基于STM32的跑步路记录主要用于记录用户在日常身体锻炼中,可设置跑步路径,跑步路线规划,在50~80m前提醒拐弯,蜂鸣器报警提示;记录跑步路径,显示当前位置,通过按键设置跑步距离。

    • 视频展示

    GPS定位路径保存

    1.模块选型

    2.1 主控MCU:STM32F103C8最小系统板

      STM32最小系统板:STM32系统板
    在这里插入图片描述

    2.2 OLED显示屏:0.96寸SPI/IIC接口

      显示屏幕:OLED屏幕
    在这里插入图片描述

    2.3 GPS定位模块

    GPS定位:GPS模块 北斗模块 双模定位 ATGM336H

    在这里插入图片描述

    1. 支持北斗/GPS/GLONASS卫星系统
    2. 支持3.3V-5V供电,可以方便接入3.3V或者5V单片机系统3、板载可充电电子,可加速热启动搜星过程
    3. 默认波特率9600,波特率可设置
    4. TTL电平UART接口,用户连接单片机的串口TTL电平或者USB-TTL模块测试。
    5. 带有SMA和 IPEX两种天线接口,方便选择自己需要的外置天线。
    6. 带有PPS授时输出引脚,方便做时钟同步等应用。
      在这里插入图片描述
      在这里插入图片描述

    2.4 蜂鸣器

      蜂鸣器:有源蜂鸣器
    在这里插入图片描述

    3.电路设计

    在这里插入图片描述

    3.实物展示

    在这里插入图片描述
    在这里插入图片描述

    4.软件设计

    4.1 GPS定位信息获取

      ATGM336H采用串口协议,直接接上电源,在空旷的地方只要接收到卫星信号即可返回卫星定位数据。
    在这里插入图片描述

    /*
    函数功能:分析BDGSV信息
    函数参数:GPS_DecodeInfo:nmea信息结构体
    		  buf:接收到的GPS数据缓冲区首地址
    */
    void GPS_BDGSV_InfoGet(GPS_Msg *GPS_DecodeInfo,u8 *buf)
    {
    	u8 *p,*p1,dx;
    	u8 len,i,j,slx=0;
    	u8 posx;
    	p=buf;
    	p1=(u8*)strstr((const char *)p,"$BDGSV");
    	if(!p1)return; //没有查找成功
    	len=p1[7]-'0';								//得到BDGSV的条数
    	posx=GPS_GetCommaOffset(p1,3); 					//得到可见北斗卫星总数
    	if(posx!=0XFF)GPS_DecodeInfo->beidou_svnum=GPS_StrtoNum(p1+posx,&dx);
    	for(i=0; i<len; i++)
    	{
    		p1=(u8*)strstr((const char *)p,"$BDGSV");
    		if(!p1)return; //没有查找成功
    		for(j=0; j<4; j++)
    		{
    			posx=GPS_GetCommaOffset(p1,4+j*4);
    			if(posx!=0XFF)GPS_DecodeInfo->beidou_slmsg[slx].beidou_num=GPS_StrtoNum(p1+posx,&dx);	//得到卫星编号
    			else break;
    			posx=GPS_GetCommaOffset(p1,5+j*4);
    			if(posx!=0XFF)GPS_DecodeInfo->beidou_slmsg[slx].beidou_eledeg=GPS_StrtoNum(p1+posx,&dx);//得到卫星仰角 
    			else break;
    			posx=GPS_GetCommaOffset(p1,6+j*4);
    			if(posx!=0XFF)GPS_DecodeInfo->beidou_slmsg[slx].beidou_azideg=GPS_StrtoNum(p1+posx,&dx);//得到卫星方位角
    			else break;
    			posx=GPS_GetCommaOffset(p1,7+j*4);
    			if(posx!=0XFF)GPS_DecodeInfo->beidou_slmsg[slx].beidou_sn=GPS_StrtoNum(p1+posx,&dx);	//得到卫星信噪比
    			else break;
    			slx++;
    		}
    		p=p1+1;//切换到下一个BDGSV信息
    	}
    }
    
    • 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

    4.2 卫星数量获取

    /*
    函数功能:分析GNGGA信息
    函数参数:
    		GPS_DecodeInfo:nmea信息结构体
    		buf:接收到的GPS数据缓冲区首地址
    */
    void GPS_GNGGA_InfoGet(GPS_Msg *GPS_DecodeInfo,u8 *buf)
    {
    	u8 *p1,dx;
    	u8 posx;
    	p1=(u8*)strstr((const char *)buf,"$GNGGA");
    	if(!p1)return; //没有查找成功
    	posx=GPS_GetCommaOffset(p1,6);								//得到GPS状态
    	if(posx!=0XFF)GPS_DecodeInfo->gpssta=GPS_StrtoNum(p1+posx,&dx);
    	posx=GPS_GetCommaOffset(p1,7);								//得到用于定位的卫星数
    	if(posx!=0XFF)GPS_DecodeInfo->posslnum=GPS_StrtoNum(p1+posx,&dx);
    	posx=GPS_GetCommaOffset(p1,9);								//得到海拔高度
    	if(posx!=0XFF)GPS_DecodeInfo->altitude=GPS_StrtoNum(p1+posx,&dx);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.3 移动速度获取

    /*
    函数功能:分析GNVTG信息
    函数参数:GPS_DecodeInfo:nmea信息结构体
    		  buf:接收到的GPS数据缓冲区首地址
    */
    
    void GPS_GNVTG_InfoGet(GPS_Msg *GPS_DecodeInfo,u8 *buf)
    {
    	u8 *p1,dx;
    	u8 posx;
    	p1=(u8*)strstr((const char *)buf,"$GNVTG");
    	if(!p1)return; //没有查找成功
    	posx=GPS_GetCommaOffset(p1,7);								//得到地面速率
    	if(posx!=0XFF)
    	{
    		GPS_DecodeInfo->speed=GPS_StrtoNum(p1+posx,&dx);
    		if(dx<3)GPS_DecodeInfo->speed*=GPS_GetPow(10,3-dx);	 	 		//确保扩大1000倍
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.4 主函数

    int main()
    {
      u8 key;
      char buff[100];
    	u32 E_data=0,E_data2=0,N_data=0,N_data2=0;
    	LEDInit();
      KEYInit();
      Beep_Init();
    	USARTx_Init(USART1,115200);
    	USARTx_Init(USART2,9600);//串口3初始化(接收GPS数据)
      TIMx_Init(TIM2,72,20000);//定时20ms,用定时器2辅助usart1接收
    	TIMx_Init(TIM3,72,20000);
    	USARTx_Sendstr(USART1,"串口1初始化成功\r\n");
    	OLED_Init();//OLED初始化
      u8 x=5;
      u8 y=16;
      u8 stat=0xff;
      u8 stat2=0;
      u16 time=0;
      u16 distance=400;
    AA:
      while(1)
      {
        /*距离选择*/
        OLED_ClearGram();
        OLED_Display_Font(32,0,16,2);
        OLED_Display_Font(32+16,0,16,3);
        OLED_Display_Font(32+16*2,0,16,4);
        OLED_Display_Font(32+16*3,0,16,5);
        OLED_Display_str(24,16,16,(u8 *)"400 M");
        OLED_Display_str(24,16*2,16,(u8 *)"1000 M");
        OLED_Display_str(24,16*3,16,(u8 *)"2000 M");
        OLED_Display_str(128-30,y,16,(u8 *)"<--");
        key=GetKeyVual(1);
        if(key==1)//距离选择
        {
          if(y<48)y+=16;
          else y=16;   
          OLED_RefreshGram();//更新数据到屏幕
        }
        else if(key==2)//确认
        {
          BEEP=1;
          if(y==16)distance=400;
          else if(y==32)distance=1000;
          else if(y==48)distance=2000;
          OLED_RefreshGram();//更新数据到屏幕
          DelayMs(50);
          BEEP=0;
          DelayMs(200);
          break;
        }
        DelayMs(1);
        time++;
        if(time>=50)
        {
          time=0;
          OLED_RefreshGram();//更新数据到屏幕
          LED1=!LED1;
        }
      }
      y=60;
      while(1)
      {
    		if(usart2_flag)//获取GPS数据
    		{
    			usart2_rx_buff[usart2_cnt]='\0';
    			//printf("%s\r\n",usart2_rx_buff);
    			usart2_flag=0;
    			usart2_cnt=0;
    			GPS_GNGGA_InfoGet(&GPS_DecodeInfo,usart2_rx_buff);//获取卫星数量
          GPS_GNVTG_InfoGet(&GPS_DecodeInfo,usart2_rx_buff);//获取移动速度
          GPS_GNRMC_InfoGet(&GPS_DecodeInfo,usart2_rx_buff);//获取经纬度		
    			printf("卫星数量: %d\r\n",GPS_DecodeInfo.posslnum);
    			E_data=GPS_DecodeInfo.longitude;
    			N_data=GPS_DecodeInfo.latitude;
    			printf("经度:%c,%d \r\n",GPS_DecodeInfo.ewhemi,E_data);
    			printf("纬度:%c,%d\r\n",GPS_DecodeInfo.nshemi,N_data);		
          printf("移动速度:%.1f. km/h\r\n",GPS_DecodeInfo.speed/1000.0);
        }
        if(GPS_DecodeInfo.posslnum>=3 && stat2==0)//卫星数量超过3颗开始定位
        {
          printf("stat2==%d\n",stat2);
          BEEP=1;
          DelayMs(1000);
          BEEP=0;
          stat2=1;
          
        }
        key=GetKeyVual(1);
        if(key==1 && stat2==1)//开始
        {
          LED1=!LED1;
          stat=0;
          x=5;
          y=60;
        }
        else if(key==2)//退出
        {
          LED3=LED2=1;
          LED1=!LED1;
          stat=0xff;
          stat2=0;
          y=16;
          goto AA;//重新选择距离
        }
        OLED_ClearGram();
        OLED_Display_Font(15,35,16,2);
        OLED_Display_Font(15+16,35,16,3);
        snprintf(buff,sizeof(buff),":%d M",distance);
        OLED_Display_str(15+16*2,35,16,(u8 *)buff);
        OLED_Display_Font(15,15,16,0);
        OLED_Display_Font(15+16,15,16,1);
        //snprintf(buff,sizeof(buff),":%.fkm/h",GPS_DecodeInfo.speed/1000.0);
        snprintf(buff,sizeof(buff),":%.fkm/h",GPS_DecodeInfo.speed/1000.0);
        OLED_Display_str(15+16*2,15,16,(u8 *)buff);
        OLED_DrawRectangle(5, 5, 124, 60);
        OLED_DrawRectangle(4, 4, 125, 61);
        gui_circle(x, y,1,3,1);
        if(stat==0)
        {
          if(N_data-N_data2>=4)
          {
            x+=2;
            N_data2=N_data;
          }
        }
        else if(stat==1)
        {
          if(N_data-N_data2>=2 || N_data2-N_data>=2)
          {
            N_data2=N_data;
            y--;
          }
        }
        else if(stat==2)
        {
          if(N_data2-N_data>=3)
          {
            N_data2=N_data;
            x-=2;
          }
        }
        else if(stat==3)
        {
          if(N_data-N_data2>=3 || N_data2-N_data>=3)
          {
            N_data2=N_data;
            y+=2;
          }
        }
        if(x>=124 && y==60)
        {
          x=124;
          stat=1;
        }
        else if(y==5 && x>=124)
        {
          x=124;
          stat=2;
        }
        else if(x<=5 && y==5)
        {
          x=5;
          stat=3;
        }
        else if(x==5 && y>=60 && (stat==3 || stat==4))
        {
          BEEP=1;
          DelayMs(100);
          BEEP=0;
          stat=4;
          LED1=LED2=1;
          LED3=!LED3;
          y=60;
          x=5;
          
        }
        if(time>=20)
        {
          time=0;
          if(stat==0xff)
          {
            LED3=LED2=1;
            LED1=!LED1;
          }
          else if(stat<=3)
          {
            LED1=LED3=1;
            LED2=!LED2;
          }
        }
        DelayMs(1);
        time++;
        OLED_RefreshGram();//更新数据到屏幕
      }
    }
    
    • 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
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197

    5.注意事项

      由于采用的是STM32F103实现本功能,该CPU主频仅有72MHZ,SRAM和flash均比较小,所以此示例并未调用地图接口实现,而是通过直接判断经纬度来决定当前位置。因此实现功能方式类似于公交报站实现方式,所有路径需要提交采集路线,进行路径保存。

  • 相关阅读:
    电容笔和触控笔有什么区别?ipad电容笔口碑好的品牌推荐
    git标签操作
    linux环境下如果掌控了系统root账户就能对上面安装的MySQL数据为所欲为了吗
    java毕业设计在线考试系统Mybatis+系统+数据库+调试部署
    Sevlet规范:HttpServlet类 和 HttpServletRequest接口 源码解析
    JVM学习五
    Qt-OpenCV学习笔记--人脸识别--基于Haar特征的cascade分类器
    Docker安装Redis
    16.Xaml WrapPanel控件 ---> 流面板
    8年经验之谈 —— 记一次接口压力测试与性能调优!
  • 原文地址:https://blog.csdn.net/weixin_44453694/article/details/126679452