B46 - STM32太阳能充电智能心率监测骑行仪

任务

本设计以STM32F103C8T6单片机作为主控元件,采用手握柄心率检测模块可以通过检测身体各个部位采集心跳信号,单片机采集模块发出的脉冲累加得到5秒内心脏跳动的次数再求平均,获得稳定的脉冲数后根据该模块的心率转换公式得到当前用户心率;利用YS-27/3144E霍尔传感器计数测速模块进行测速;由3.5英寸IPS串口屏显示心跳数及里程时间等;太阳能充电智能心率监测骑行仪由户外太阳能供电;锂电池充放电电路为系统供能,另外实现的功能有:语言提示功能、温湿度监测、微信小程序控制等。
1、任务描述
制作一款能实现太阳能充电的智能骑行仪,完成行驶过程中的即时速度/平均速度显示,总里程数显示,时间显示,实时心率显示,夜间行车自动开灯等。
2、基本要求
(1)显示电量不足时,通过光照自动充电;有光线,即充电;
(2)显示屏上能显示即时速度,平均速度,总里程数;
(3)显示骑行时长和当前时间,断电后系统时间保持持续更新;
(4)增加照明功能,当行驶环境变暗后,自动开启照明灯;
(5)自行车把手增加心率检测电极,用于实时监测心率。当设定目标心率达到后进行声音报警;(可采用按键或实现蓝牙连接设置目标心率)

实物

在这里插入图片描述

在这里插入图片描述
如图所示,实物上各个部分的划分如上。将该模块划分为8个区域,每个区域功能如表所示。
在这里插入图片描述
2.1按键使用说明
(1)按下电源开关,系统初始化,语音播报开机提示语,显示系统界面。
(2)按键1按下时,依次进入心率设置界面与设置速度界面;
(3)按键2按下时,系统可以从系统界面进入二维码界面,可以用过扫描二维码来连接小程序进行控制。在设置界面的话则启动“加”功能,心率和速度阈值最大可设置为200;
(4)按键3按下,系统再系统界面的话则执行LED照明的开和关功能,再设置界面的话则启动“减”功能, 心率和速度阈值最小可设置值为1;
2.2 温湿度检测使用
温湿度模块在实物图中的位置。用户可以直接在LCD显示屏上看出当前环境温湿度,也可以通过用手攥着温湿度模块来手动改变当前检测到的温湿度情况。来验证本设计的正确性。
2.3 光亮度检测
采用光敏电阻传感器模块,当环境光亮度变化的时候,光敏电阻阻值发生变化,通过LM358电压比较器获得输出高低电平。当环境光较暗时,模块输出高电平,当环境光较亮时,模块输出低电平。测试时,通过阻挡光敏电阻模块的光敏电阻采集光即可模拟白天和黑夜。
2.4 心率检测
本次采用的心率检测模块使用的是电极检测,通过双手握持即可读取使用者的心率对应的脉冲数值,单片机内部做转换并显示出当前心率。
2.5 速度检测
本次采用的是霍尔传感器测速,采集安装在车轮上的磁铁磁性输出周期的方波信号,霍尔传感器安装好后,打开电机进行模拟测试即可。
2.6 LCD显示内容解读
2.6.1系统正常界面
系统正常界面则是各个参数的检测,通过屏幕可以清晰看出主要检测内容,如温湿度,心率,速度,里程等。同时,将检测数据与设定的阈值进行比对,当大于设定阈值时,对应的数据字体将会变色,令用户容易非常显眼的看出超出数据的阈值。温湿度传感器检测为每200MS检测一次,速度每1S更新一次,心率5S更新一次。
在这里插入图片描述

系统设置界面

本界面用于设置心率阈值与即时速度的阈值。按下对应的按键或者通过小程序可以设置。
在这里插入图片描述

小程序

小程序界面解读
在这里插入图片描述

小程序对应界面如图所示,可以按下对应的按键设置对应参数。需要注意的是更新时间按键功能为将当前手机系统时间同步到本系统,因为如果本系统时间不准确,通过实体按键调节需要很多步骤,太过于繁杂,容易让用户产生使用疲劳。
本次小程序采用自定义指令,指令信息对照如下表所示。
在这里插入图片描述
在这里插入图片描述

原理图

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

心率模块

在这里插入图片描述

在这里插入图片描述

源代码

/*******************************************************************************

\* 文件名称:STM32太阳能充电智能心率监测骑行仪

\* 实验目的:1.

\* 2.

\* 程序说明:完整程序Q:277 227 2579;@: itworkstation@ hotmail.com

\* 日期版本:本项目分享关键细节,熟悉使用单片机的可做参考代码。完整讲解+源代码工程可联系获取,可定制。

*******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "main.h"   

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
  * @说明     主函数
  * @参数     None 
  * @返回值   None
  */
int main(void)
{
    
    
	u8 key_value=0xff;
	LED_Init();
	BEEP_Init();
	BEEP_Control(ON);  
	LED_Control(ON);
	Delay_ms(1000);
	BEEP_Control(OFF);  
	LED_Control(OFF);
	
	Proc_Init();
	
	USART1_Init(); 
	USART3_Init();  
	
	USART2_Init(); 
	Delay_ms(1000);//上电等待 1 秒是串口屏模块正常工作的前提,如果没有足够的等待时间模块有可能无法正常的接收指令而导致系统出错。	
	printf("CLR(%d);DIR(1);SBC(%d);\r\n",LCD_ColorStructure.LCD_BackColor,LCD_ColorStructure.LCD_SBCColor); 
	USART2_CheckBusy(); 
		
	if(DHT11_Init() == 1) 
	{
    
    
		BEEP_Control(ON);  	
	}
	else
	{
    
    	
		BEEP_Control(OFF);   
	}
		
	TIM3_Init(); 
	
	Ds1302_init();
	ds1032_read_time(); 
	
	LCD_Display();  
	Key_Init();
	Relay_Init();
	JQ8400_6x00SendCmd(SET_SOUND,0x1E);		   //音量,十六进制输入  
	JQ8400_6x00SendCmd(SELECTE_MODE,MODE_DQTZ);		   //设定循环模式,单曲停止
	JQ8400_6x00SendCmd(SELECTE_PLAY,1);    //	
	
	EXTIX_Init();  
	EXTIX_07_Init(); 
	while(1){
    
    		 
		if(TIM3_FlagStatus.Flag_200MS == SET){
    
    
		   TIM3_FlagStatus.Flag_200MS = RESET;
		   LED_Control(REVERSE);    
		   Proc_200Ms();  
		}
		if(TIM3_FlagStatus.Flag_1000MS == SET){
    
    
		   TIM3_FlagStatus.Flag_1000MS = RESET;		   
			Proc_1000Ms(); 			
		}	
		key_value=Key_Scan();  
		if(key_value!=0xff)
		{
    
    
			Key_Handle(key_value);
		}
		if(Flag_BloothReceive)
		{
    
    
			Delay_ms(100);
			Proc_Blooth(Check_Commend()); 
		}
		LCD_Display();  
    }     
}



心率传感器采集模块

#ifndef __EXTI_X_h
#define __EXTI_X_h

#include "stm32f10x.h"
#include "Def_config.h"


extern u16 EXTI1_COUNT;
extern u32 EXTI7_COUNT;

void EXTIX_Init(void);
void EXTIX_07_Init(void);


#endif 

#include "EXTI_X.h"
//外部中断1服务程序
void EXTIX_Init(void)
{
    
    
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	
    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA PA1 
	
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
   //GPIOA.1      中断线以及中断初始化配置  下降沿触发 //
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
    EXTI_InitStructure.EXTI_Line=EXTI_Line1; 
	 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;    
	 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	 EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);   //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;            //使能外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    //抢占优先级 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;                   //子优先级 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                             //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);       //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

}
//外部中断7服务程序
void EXTIX_07_Init(void)
{
    
    
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);	
    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
    GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO PB7
	
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
   //GPIOB.7      中断线以及中断初始化配置  下降沿触发 //
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource7);
    EXTI_InitStructure.EXTI_Line=EXTI_Line7; 
	 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;    
	 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	 EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);   //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;            //使能外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    //抢占优先级 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;                   //子优先级 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                             //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);       //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

}
//外部中断1服务程序
u16 EXTI1_COUNT=0;
void EXTI1_IRQHandler(void)
{
    
    
    EXTI1_COUNT++;   
	if(EXTI1_COUNT>50000)
	{
    
    
		EXTI1_COUNT=50000;		
	}
    EXTI_ClearITPendingBit(EXTI_Line1);  //清除LINE1上的中断标志位   
}
//外部中断7服务程序
u32 EXTI7_COUNT=0;
void EXTI9_5_IRQHandler(void)
{
    
    
	if(EXTI_GetITStatus(EXTI_Line7)){
    
     

		EXTI7_COUNT++;   
		if(EXTI7_COUNT>0xfffffffe)
		{
    
    
			EXTI7_COUNT=0xfffffffe;		
		}
		EXTI_ClearITPendingBit(EXTI_Line7);  //清除LINE7上的中断标志位  

	}
     
}

u16 HeartRate_Time=0;
#define HeartRate_Time_SET 5 //多久测一次方波数量
extern u16 EXTI1_COUNT;
void Proc_HeartRate(void)
{
    
    
	HeartRate_Time++;
	if(HeartRate_Time >= HeartRate_Time_SET)
	{
    
    
		HeartRate_Time=0;
		LCD_DefShow.heart_rate = (u16)(((double)(EXTI1_COUNT*1.0/HeartRate_Time_SET))*60); 
		EXTI1_COUNT=0;
	}
}

JQ8400语音播放模块

#ifndef __JQ_8400_H__
#define __JQ_8400_H__

#include "stm32f10x.h"
#include "usart1.h" 

#define  JQ8400_SendData   USART1_SendByte

#define SET_CMD    0X00	    //仅设置模式,并不送入数据指令

#define PLAY_ON    0X02	    //	播放
#define PLAY_SUSPEND   0X03			//暂停
#define PLAY_OFF   0X04			 //停止
#define PLAY_LAST   0X05		  //上一曲
#define PLAY_NEXT   0X06		  //下一曲


#define SELECTE_PLAY 0X07       //指定曲目  后面要加曲目号

#define SET_SOUND  0X13	   //音量设置	   1-30级,要送16进制即: 0-0X1E
#define SOUND_ADD  0X14	   //音量加
#define SOUND_DEC  0X15		  //音量减

#define SELECTE_MODE  0X18  //模式
#define MODE_DQXH  0X01  //单曲循环
#define MODE_DQTZ  0X02  //单曲停止
#define MODE_SJBF  0X03  //随机播放



#define RCC_PORT_JQ8400BUSY 	  RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define GPIO_PORT_JQ8400BUSY 	  GPIOB		                /* GPIO端口 */
#define PIN_JQ8400BUSY           GPIO_Pin_5		          /* 蜂鸣器 */


//sbit JQ8400_BUSY=P3^4;	//播放时为高电平,其余时间为低电平
void JQ8400_6x00SendCmd(u8 cmd,u16 dat);//发送命令

#endif




#include "JQ_8400.h"
#include "delay.h"
void GPIO_Q8400_Init(void)
{
    
    
  GPIO_InitTypeDef GPIO_InitStructure;
  /* GPIOx clock enable */
  RCC_APB2PeriphClockCmd(RCC_PORT_JQ8400BUSY, ENABLE);
  GPIO_InitStructure.GPIO_Pin = PIN_JQ8400BUSY;  // 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

  GPIO_Init(GPIO_PORT_JQ8400BUSY, &GPIO_InitStructure);
}
void JQ8400_SendMultiByte(u8 *Buff, u8 Len )
{
    
    
    u8 i;
    for ( i = 0 ; i < Len ; i++ )
    {
    
    
        JQ8400_SendData( Buff[i] ); 
    }
}
u8 JQ8400_SumCheck ( u8 *Str, u8 len ) //和校验
{
    
    
	u16 xorsum = 0;
	u8 i;
	for ( i = 0; i < len; i++ )
	{
    
    
		xorsum = xorsum + ( *Str++ );
	}
	return ( ( u8 ) ( xorsum & 0x00ff ) );
}
void JQ8400_6x00SendCmd(u8 cmd,u16 dat)//发送命令
{
    
    
	 u8 pbuff[6];
	 u8 xorsum;
	 pbuff[0]=0xAA;
	 pbuff[1]=cmd;
	 if(cmd==SET_SOUND)
	 {
    
    
	 	pbuff[2]=0x01;
		pbuff[3]=(u8)dat;
		xorsum=JQ8400_SumCheck(pbuff,4);
		pbuff[4]=xorsum;
		JQ8400_SendMultiByte(pbuff,5); 	
	 }
	 else if(cmd==SELECTE_PLAY)
	 {
    
    
	 	pbuff[2]=0x02;
		pbuff[3]=(u8)(dat>>16);
		pbuff[4]=(u8)(dat);
		xorsum=JQ8400_SumCheck(pbuff,5);
		pbuff[5]=xorsum;
		JQ8400_SendMultiByte(pbuff,6); 	
	 }
	 else if(cmd==SELECTE_MODE)
	 {
    
    
	 	pbuff[2]=0x01;
		pbuff[3]=(u8)dat;
		xorsum=JQ8400_SumCheck(pbuff,4);
		pbuff[4]=xorsum;
		JQ8400_SendMultiByte(pbuff,5); 	
	 }
	 else
	 {
    
     
	 	pbuff[2]=0x00;
		xorsum=JQ8400_SumCheck(pbuff,3);
		pbuff[3]=xorsum;
		JQ8400_SendMultiByte(pbuff,4); 	
	 }					 	
	Delay_ms(100);	
} 











蓝牙模块HC-05

#ifndef __BLOOTH_H__
#define __BLOOTH_H__

#include "stm32f10x.h"
#include "Def_config.h"

/*************	本地变量声明	**************/
extern u8  Flag_BloothReceive;

//蓝牙模块,连接手机后state引脚会变为高电平3.3V,没有连接的时候为低电平

void Blooth_ReceiveBuff(u8 date);
void CLR_Buf(void);
int  Find(u8 *a);

u8 Check_Commend(void);
void Blooth_ReceiveBuff(u8 date);


#endif


#include "Blooth.h"
#include <string.h>
#include "ds1302.h" 
#define Buf_Max 	 50 	//串口缓存长度  
extern u8 Uart_BloothBuf[Buf_Max]; 
extern u8 const WRITE_RTC_ADDR[7]; 
u8 Check_Commend(void)
{
    
    
	u8 value=99;u8 n=0;
	if(Find((u8 *)"$1"))
    {
    
    
		value = 1;   	
    }
	if(Find((u8 *)"$2"))
    {
    
    
		value = 2;   	
    }
	if(Find((u8 *)"$3"))
    {
    
    
		value = 3;   	
    }
	if(Find((u8 *)"$4"))
    {
    
    
		value = 4;   	
    }
	if(Find((u8 *)"$5"))
    {
    
    
		value = 5;   	
    }
	if(Find((u8 *)"$T"))  //时间更新例如: $T20210427054201 解析:2021年4月27日5点42分1秒
    {
    
    
		DS1302_TIME[year]= (Uart_BloothBuf[4]-0x30)*10 + (Uart_BloothBuf[5]-0x30);
		DS1302_TIME[month]= (Uart_BloothBuf[6]-0x30)*10 + (Uart_BloothBuf[7]-0x30);
		DS1302_TIME[day]= (Uart_BloothBuf[8]-0x30)*10 + (Uart_BloothBuf[9]-0x30);
		DS1302_TIME[hour]= (Uart_BloothBuf[10]-0x30)*10 + (Uart_BloothBuf[11]-0x30);
		DS1302_TIME[minute]= (Uart_BloothBuf[12]-0x30)*10 + (Uart_BloothBuf[13]-0x30);
		DS1302_TIME[second]= (Uart_BloothBuf[14]-0x30)*10 + (Uart_BloothBuf[15]-0x30);
//		DS1302_TIME[week]= (Uart_BloothBuf[16]-0x30)*10 + (Uart_BloothBuf[17]-0x30);	
		
		ds1302_wirte_rig(0x8E,0X00);		 //禁止写保护,就是关闭写保护功能
		for (n=0; n<7; n++)//写入7个字节的时钟信号:分秒时日月周年
		{
    
    
			ds1302_wirte_rig(WRITE_RTC_ADDR[n],DEC_BCD_conv(DS1302_TIME[n]));	  
		}
		ds1302_wirte_rig(0x8E,0x80);		 //打开写保护功能
    }
//	if(Find("Connected"))
//    {
    
    
		printf("OK");  	
//    }
	CLR_Buf();
	if(value!=99)
	{
    
    
//		printf("OK");
	}
	return value;
}

u8 First_Int = 0;
u8 Flag_BloothReceive=0;						 //需要获取到的标志位,全局	  
u8 Uart_BloothBuf[Buf_Max];
void Blooth_ReceiveBuff(u8 date)	 //需要放到串口中断去接收串口数据进行通信
{
    
    
	if(date == '$')
		First_Int=0;
	Uart_BloothBuf[First_Int] = date;  	  //将接收到的字符串存到缓存中
	First_Int++;                	  //缓存指针向后移动
	if(First_Int > Buf_Max)       	  //如果缓存满,将缓存指针指向缓存的首地址
	{
    
    
		First_Int = 0;
	}		
	if(First_Int > 1)
	{
    
    
		Flag_BloothReceive = 1;
	}	
}
/*******************************************************************************
* 函数名 : CLR_Buf
* 描述   : 清除串口数组缓存数据
* 输入   : 
* 输出   : 
* 返回   : 
* 注意   : 
*******************************************************************************/
void CLR_Buf(void)
{
    
    
	u8 k;
	for(k=0;k<Buf_Max;k++)      //将缓存内容清零
	{
    
    
		Uart_BloothBuf[k] = 0x00;
	}
    First_Int = 0;              //接收字符串的起始存储位置
	Flag_BloothReceive = 0;
}
/*******************************************************************************
* 函数名 : Find
* 描述   : 判断缓存中是否含有指定的字符串
* 输入   : 
* 输出   : 	 是子串返回1 否返回0
* 返回   : unsigned char:1 找到指定字符,0 未找到指定字符 
* 注意   : strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。
如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
*******************************************************************************/
int Find(u8 *a)
{
    
     
	if(strstr((char *)Uart_BloothBuf,(char *)a)!=NULL) 
	{
    
    
		return 1;
	}	
	else
	{
    
    
		return 0;
	}		
}



猜你喜欢

转载自blog.csdn.net/qq_20467929/article/details/126137467
今日推荐