基于STM32跑步路径记录

描述

基于STM32跑步路径记录

      随着科技不断进步,电子化设备不断进入涌入我们的日常生活。生活水平的提高,各项健身运动应运而生,然后,健身运动不能盲目进行,科学的健身方式才能有效的提升我们自身的身体素质。

      现如今各自手环手表的出现, 通过智能手环,用户可以记录日常生活中的锻炼、睡眠、部分还有饮食等实时数据,并将这些数据与手机、平等同步,起到通过数据指导健康生活的作用。

      智能手环作为可穿戴设备,其功能还是比较强大的,其开发涉及智能手环MCU数据指令到蓝牙IC的传输、蓝牙到APP的数据通信协议、APP到手机内部的通信调试逻辑实现、APP数据到云端服务器的数据库算法设计等一系列的开发。支持多种运动监控模式,可以实时监控身体的各项性能指标。

      我国智能手环产品真正大范围进入消费市场是在2012年以后,一方面是这一时期步入4G时代以后手机智能化趋势加快,与手机一样具备数据分享的智能手环的出现给智能手环的研究和应用打开了另一个世界。另一方面是更多实用性功能快速出现,手环不仅仅是用来记录身体特征的单纯工具,同时也可以满足通话、移动支付、人体识别、智能提醒功能以及分享功能,极大的扩宽了手环的应用人群。佩戴智能手环带来健康、科技、自信有品位的良好感受,成为了高科技产品的典型之一。

 

  基于STM32的跑步路记录主要用于记录用户在日常身体锻炼中,可设置跑步路径,跑步路线规划,在50~80m前提醒拐弯,蜂鸣器报警提示;记录跑步路径,显示当前位置,通过按键设置跑步距离。

2.模块选型

2.1 主控MCU:STM32F103C8最小系统板

  STM32最小系统板:STM32系统板

单片机

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

  显示屏幕:OLED屏幕

单片机

2.3 GPS定位模块

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

单片机

支持北斗/GPS/GLONASS卫星系统

支持3.3V-5V供电,可以方便接入3.3V或者5V单片机系统3、板载可充电电子,可加速热启动搜星过程

默认波特率9600,波特率可设置

TTL电平UART接口,用户连接单片机的串口TTL电平或者USB-TTL模块测试。

带有SMA和 IPEX两种天线接口,方便选择自己需要的外置天线。

带有PPS授时输出引脚,方便做时钟同步等应用。

单片机单片机

2.4 蜂鸣器

  蜂鸣器:有源蜂鸣器

单片机

3.电路设计

单片机

4.实物展示

单片机单片机

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; ibeidou_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信息
	}
}

 


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);
}

 

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倍
	}
}

 

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();//更新数据到屏幕
  }
}

 

5.注意事项

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




审核编辑:刘清


 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分