关于第十四届蓝桥杯嵌入式省赛内容分享(基于HAL)

目录

题解思路:

详细内容:  

一、系统初始化CubeMX配置 

  1.1 引脚配置

  1.2 ADC2配置

  1.3 TIM配置

     1.3.1 TIM2&PWM

     1.3.2 TIM3&IC 

二、 代码文件

  2.1 按键配置

   key.c/h

   2.2 封装各类函数

  box.c/h

  2.3 lcd.c文件修改

    lcd.c

  2.4 led灯功能实现 

  led.c/h

  2.5 修改占空比函数解释(box.c中的Personal_SetCompare)

  2.6 TIM3中断回调函数重写(box.c中) 

  2.7 main函数内容 

  main.c

三、 开发板调试展示

四、 补充 


题解思路:

  1. 使用STM32 CubeMX配置时钟80MHz、GPIO(包括PC8~PC15(LED)、PD2(锁存器),  PA1(PWM), PA7(输入捕获以测量频率和占空比),PA0、PB0~2(按键) ,PB15(ADC采集R37上电压)
  2. 用三行按键法区分长短按,用枚举定义高低频模式、界面模式、参数选择、锁定或解锁状态等,定义多个函数以实现和区分子功能。
  3. 修改频率时要同时修改ARR和CCR的值,以实现占空比不变;修改占空比只需使用函数HAL_SetCompare()。
  4. 注意功能要求,不同界面下的按键有不同功能,同时有时间限制(LCD_Clear()等会有延时)。

详细内容:  

一、系统初始化CubeMX配置 

  1.1 引脚配置

   这里展示的是除LED、按键之外的引脚设置(在十三届蓝桥杯省赛内容分享中有介绍)。

1.2 ADC2配置

  PB15引脚为ADC2通道15,使用单端输入模式,不使用外部中断触发。 

  使用异步时钟二分频 ,使能规则转化组(会出现Rank,不过只使用一个通道,不过以后可以使用多个),采样周期这里可以选择最大640.5周期(采样频率低一些),其余可以忽略(默认间断模式)。

  1.3 TIM配置

     1.3.1 TIM2&PWM

   

  PWM输出可以按上图配置就行,默认低频模式(装载值ARR为250-1),CCR后续还会修改。


    1.3.2 TIM3&IC 

  TIM3用于PWM输入捕获,即A7脚测量频率(占空比可以顺便测量,因为题目只说明要实时占空比,可以是实时输出也可以是实时测量的),通道2(IC2)与A7脚连接,则作为主通道,捕获上升沿,同时触发内部通道1(即通道1作为从通道,被间接触发),TIM3计数器(CCR1和CCR2)清零,当下降沿来临时计数器捕获到CCR1中,下个上升沿来临时触发到CCR2,具体可以参考大佬文章http://t.csdnimg.cn/bQ9E4。最后还要注意NVIC定时器(TIM3_IT)中断开启。

二、 代码文件

  2.1 按键配置

  按键需要区分长短按,先判断是否按下,是否是长按,再判断是否是短按,采用三行按键法,具体可以看十三届蓝桥杯省赛内容分享 ,详细代码如下。

   key.c/h
#include "key.h"
#include "box.h"
#include "lcd.h"
/* USER CODE BEGIN 0 */
unsigned char ucTrg1=0;
unsigned char ucCont=0;
unsigned char ucRead=0;
unsigned char uckey_num=0;
uint16_t uckey_times=0;
int  B1_pressTime=-5000;
uint32_t pressTime=0;

/* USER CODE END 0 */

/* 参数初始化 */
uint8_t R=1,K=1;
uint8_t N=0;
//判断是否发生转化的标志
extern uint8_t startFlag;
//各种定义的模式或状态
extern enum Mode mode;
extern enum Freq freq;
extern enum Para para;
extern enum State state;
/* USER CODE END 1 */

/**   CubeMX生成gpio.c中的初始化函数 进行重命名   **/

void KEY_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin : PA0 */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PB0 PB1 PB2 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}



/**   按键扫描  **/
void key_scan()
{
   ucRead=(KEYPORT)^0xff;
   ucTrg1=ucRead&(ucRead^ucCont);
	 ucCont=ucRead;
}
/**   短按+长按   **/
void key_Press()
{
  if(uwTick-pressTime<20)return;
	key_scan();
	//判断哪个按键被触发
	switch(ucCont)
	 {
		 //B1
	   case 0x01:uckey_num=1;uckey_times++;
		           if(uwTick-B1_pressTime<5000)return;
						     
						     if(mode==DataMode)mode=ParaMode;
								 else if(mode==ParaMode)mode=RecdMode;
					       else if(mode==RecdMode)mode=DataMode;
				         LCD_Clear(Black);
				        
				         B1_pressTime=uwTick;
				       break;
		 //B2
		 case 0x02:uckey_num=2;uckey_times++;
				       break;
		 //B3
	   case 0x04:uckey_num=3;uckey_times++;
				       break;
		 //B4长按需要 
		 case 0x08:uckey_times++;uckey_num=4;
		           if(uckey_times>50)             
							 if(mode==DataMode)
			         if(state==Unlock)state=Lock;   //长按锁定(50*20ms)
				       break;			
		 					 
	 }
	 /***   长按时不会执行下列语句,但是短按必定执行    ***/
	if(ucTrg1==0x00 && ucCont == 0x00)
	{
		if(uckey_times>0 && uckey_times<50)
	  {
		  
		   switch(uckey_num)
       { 
			   /*** 执行语句写入  ***/
		      
					case 1:break;
			    case 2:if(mode==DataMode)
					        {
									  startFlag=1;         //  开始频率转换
										N++;                 //  PWM模式切换次数加1
									}
									else if(mode==ParaMode)
									{
									  if(para==P_R)para=P_K;
										else para=P_R;
									}
									
				         break;
			    case 3:if(mode==ParaMode)
					        {
										if(para==P_R)
										{
											R++;
									    if(R>=11)
											{
												R=1;
												LCD_ClearLine(Line3);
											}
										}
										else if(para==P_K)
										{
										  K++;
									    if(K>=11)
											{
												K=1;
												LCD_ClearLine(Line4);
											}
										}
									}
									
				         break;
			    case 4:if(mode==ParaMode)               //参数界面
					        {
										if(para==P_R)
										{
											R--;
									    if(R==0)
											{
												R=10;
												
											}
											LCD_ClearLine(Line3);
										}
										else if(para==P_K)
										{
										  K--;
									    if(K==0)
											{
												K=10;
												
											}
											LCD_ClearLine(Line4);
										}
									  
									}
									else if(mode==DataMode)         //数据界面
									{
									  if(state==Lock)state=Unlock;  //短按解锁
									}
									
				         break;
			    
		   }     
      		 
	  }
		uckey_times=0;uckey_num=0;	
	}
	 pressTime=uwTick;
   
}


#ifndef __KEY_H__
#define __KEY_H__

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

/* USER CODE BEGIN Includes */

#define KEY1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define KEY2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define KEY3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define KEY4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
/**       将每个按键的状态放到低四位,组合成新的字节数据   **/
#define KEYPORT  KEY1|(KEY2 << 1)|(KEY3 << 2)|(KEY4 <<3)|0xf0


/***      参数初始化    ***/


void KEY_Init(void);
void KEY_Input();
void key_scan();
void key_Press();
/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#endif

   2.2 封装各类函数

  由于题目功能要求比较复杂,要保证互不影响需要的子程序很多,可以直接在一个代码文件里面定义和调用其它的板载驱动程序(如led.h、lcd.h)以及需要的PWM输出和输入捕获(tim.h)和ADC采集(adc.h)。封装的函数包含界面显示、切换,频率改变,占空比改变,最大速度统计等,写入box.c中。

  box.c/h
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "adc.h"
#include "box.h"
#include "tim.h"
#include "stdio.h"

uint8_t startFlag=0;
float D_Set=0.1,D_Get=0.1,v;
float MH,ML;
float frequency,Voltage;//调试观察频率
uint8_t Freq1_Tick=0,tempFlag=1;
u16 tempCount=249;

unsigned char P[20]="null",V[20]="null";
unsigned char Rstr[20]="null",Kstr[20]="null";
unsigned char Nstr[20]="null",MHstr[20]="null",MLstr[20]="null";

enum Mode mode=DataMode;
enum Freq freq=Low;
enum Para para=P_R;
enum State state=Unlock;
extern u8 R,K,N;

void LCD_Display()
{
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	v=frequency*2*3.14*R/(100*K);
  if(mode==DataMode)
	{ 
		sprintf((char*)P,"     P=%d%%",(int)(D_Get*100+0.5));
		sprintf((char*)V,"     V=%.1f",v);
		
	    LCD_DisplayStringLine(Line1,(u8*)"        DATA");
	  /**  显示 M  **/	
		if(freq==Low)LCD_DisplayStringLine(Line3,(u8*)"     M=L");
		else if(freq==High)LCD_DisplayStringLine(Line3,(u8*)"     M=H");
		 
		/**  显示 P **/	
	    LCD_DisplayStringLine(Line4,(u8*)P);
		/**  显示 V **/
		LCD_DisplayStringLine(Line5,(u8*)V);
	}
	else if(mode==ParaMode)
	{
		sprintf((char*)Rstr,"     R=%d",R);
		sprintf((char*)Kstr,"     K=%d",K);
		
	    LCD_DisplayStringLine(Line1,(u8*)"        PARA");
	    /**  显示 R **/	
	    LCD_DisplayStringLine(Line3,(u8*)Rstr);
		/**  显示 K **/
		LCD_DisplayStringLine(Line4,(u8*)Kstr);
		
		displayCursor();
	}
	else if(mode==RecdMode)
  { 
		sprintf((char*)Nstr,"     N=%d",N);
		sprintf((char*)MHstr,"     MH=%.1f",MH);
		sprintf((char*)MLstr,"     ML=%.1f",ML);
		
		LCD_DisplayStringLine(Line1,(u8*)"        RECD");
	  /**  显示 N **/	
	  LCD_DisplayStringLine(Line3,(u8*)Nstr);
		/**  显示 MH **/
		LCD_DisplayStringLine(Line4,(u8*)MHstr);
	  /**  显示 ML **/
		LCD_DisplayStringLine(Line5,(u8*)MLstr);
	}
	Freq1_Tick=uwTick;
}
/*  显示当前所选R还是K,方便调试  */
void displayCursor()
{
	  LCD_SetBackColor(Black);
		LCD_SetTextColor(Cyan);
	  if(para==P_R)
		{
			LCD_DisplayChar(Line3,100,'<');
			LCD_DisplayChar(Line4,100,' ');
		}
		else if(para==P_K)
		{
		  LCD_DisplayChar(Line4,100,'<');
			LCD_DisplayChar(Line3,100,' ');
		}
}
 


/***  频率变换  ***/
void PWM_Freq_Change()
{
	/*** 如果是从高频变为低频 ARR(124->249),或从低频变为高频 ARR(249->124) **/
    if(uwTick-Freq1_Tick<40)return;
	if(mode == DataMode && freq==High && startFlag==1)
	{
		
		//修改频率(因为要保持占空比D不变,但ARR发生改变了,所以也要改变CCR)
		++tempCount;
        __HAL_TIM_SetAutoreload(&htim2,tempCount);
        __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(int)((D_Set*(tempCount+1))+0.5));
		HAL_TIM_GenerateEvent(&htim2,TIM_EVENTSOURCE_UPDATE);
		
		//如果已经变为4000HZ,则认为是低频且停止转换
		if(tempCount>=249)
		{
		  freq=Low;
		  startFlag=0;

		}
		
	
   }
	else if(mode == DataMode && freq==Low && startFlag==1)
	{
		//修改频率(因为要保持占空比D不变,但ARR发生改变了,所以也要改变CCR)
		--tempCount;
        __HAL_TIM_SetAutoreload(&htim2,tempCount);
        __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(int)((D_Set*(tempCount+1))+0.5));
		HAL_TIM_GenerateEvent(&htim2,TIM_EVENTSOURCE_UPDATE);

		//如果已经变为8000HZ,则认为是高频且停止转换
		if(tempCount<=124)
		{
		  freq=High;
		  startFlag=0;

		}
	
	}
	Freq1_Tick=uwTick;
}
/****   获取R37上电压值   **/
float getVoltage()
{
	float voltage=0,getV=0;

	HAL_ADC_Start(&hadc2);
	getV=HAL_ADC_GetValue(&hadc2);

	voltage=getV*3.3/4095;
	return voltage;
}

/***   根据电压值修改占空比  **/
void Personal_SetCompare()
{
  float voltage=getVoltage();
  if(voltage>=0 && voltage<1)D_Set=0.1;
	else if(voltage>=1 && voltage <=3)D_Set=0.375*voltage-0.275;  //直线方程
  else if(voltage>3)D_Set=0.85;
	//修改输出比较值CCR,其中CCR=D*(ARR+1),再经过类型转换四舍五入
  __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(int)(D_Set*(htim2.Init.Period+1)+0.5));
	HAL_TIM_GenerateEvent(&htim2,TIM_EVENTSOURCE_UPDATE);
}


/*** 测量PWM频率重写回调函数  ***/
//上升沿和下降沿捕获值
u32 riseVal,fallVal,tick=0;
unsigned char tempString[20];
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim)
{
  if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2 && htim==&htim3)
	{
    if(uwTick-tick>100)
		{
			riseVal=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1;
	    fallVal=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1;
		  if(riseVal >1)
	   	{
				frequency=1000000.0/(riseVal);
			  D_Get=((float)fallVal)/(riseVal);
			
		  }
		  tick=uwTick;
	  }
  }
}
u32 record_Tick;
/***  找最大值需要考虑采样率、是否纳入统计  ***/
float maxV,tempV[100],record_v,record_flag=1;
u8 count=0;

void record()
{
  if(uwTick-record_Tick<20)return;
	tempV[count]=v;
	if(tempV[count]!=tempV[0])
	{
		count=0;
		record_Tick=uwTick;
	}
  else count++;
	
	if(count==100)
	{
    count=0;
    record_v=tempV[0];  //纳入统计
	}
  
	record_Tick=uwTick;
}

void findMAXv()
{
  if(record_v>maxV)maxV=record_v;
  if(freq==High)
	{
	 MH=maxV;
	 ML=maxV/2;
	}
	else
	{
	 ML=maxV;
	 MH=2*maxV;
	}
}
#ifndef _BOX_H
#define _BOX_H
/***  三种界面  **/
enum Mode{
  DataMode,
	ParaMode,
	RecdMode
};

/**  判断高低频  **/
enum Freq{
  High,
  Low
};
/**  选择R或K  **/
enum Para{
  P_R,
  P_K
};
/**  是否解锁  **/
enum State{
  Lock,
  Unlock
};


void LCD_Display();
void displayCursor();
void PWM_Freq_Change();
float getVoltage();
void Personal_SetCompare();
void record();
void findMAXv();
#endif

  2.3 lcd.c文件修改

  由于lcd中部分引脚和led引脚共用,lcd.c中写寄存器会对PC引脚产生影响,使得混乱,需要修改一下竞赛给的lcd.c的三个函数,恢复寄存器的值。参考大佬文章http://t.csdnimg.cn/S5rNT

    lcd.c
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)
{
	uint16_t temp=GPIOC->ODR;                     //补充

	GPIOB->BRR  |= GPIO_PIN_9;  	
	GPIOB->BRR  |= GPIO_PIN_8;  	
	GPIOB->BSRR |= GPIO_PIN_5; 	
	
	GPIOC->ODR = LCD_Reg; 
	GPIOB->BRR  |= GPIO_PIN_5; 
	__nop();
	__nop();
	__nop();	
	GPIOB->BSRR |= GPIO_PIN_5; 
	GPIOB->BSRR |= GPIO_PIN_8; 

	GPIOC->ODR = LCD_RegValue; 
	GPIOB->BRR  |= GPIO_PIN_5; 
	__nop();
	__nop();
	__nop();  
	GPIOB->BSRR |= GPIO_PIN_5; 
	GPIOB->BSRR |= GPIO_PIN_8; 
	
	GPIOC->ODR=temp;                            //补充
}
/*******************************************************************************
* Function Name  : LCD_WriteRAM_Prepare
* Description    : Prepare to write to the LCD RAM.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void LCD_WriteRAM_Prepare(void)
{ 
	uint16_t temp=GPIOC->ODR;    //补充

	GPIOB->BRR  |=  GPIO_PIN_9;  
	GPIOB->BRR  |=  GPIO_PIN_8; 
	GPIOB->BSRR |=  GPIO_PIN_5; 

	GPIOC->ODR = R34;     
	GPIOB->BRR  |=  GPIO_PIN_5; 
	__nop();
	__nop();
	__nop();    
	GPIOB->BSRR |=  GPIO_PIN_5;
	GPIOB->BSRR |=  GPIO_PIN_8; 
	__nop();
	__nop();
	__nop();  
	GPIOB->BSRR |=  GPIO_PIN_9; 
  
	GPIOC->ODR=temp;          //补充
}
/*******************************************************************************
* Function Name  : LCD_WriteRAM
* Description    : Writes to the LCD RAM.
* Input          : - RGB_Code: the pixel color in RGB mode (5-6-5).
* Output         : None
* Return         : None
*******************************************************************************/
void LCD_WriteRAM(u16 RGB_Code)
{
	uint16_t temp=GPIOC->ODR;     //补充

	GPIOB->BRR  |=  GPIO_PIN_9;  
	GPIOB->BSRR |=  GPIO_PIN_8; 
	GPIOB->BSRR |=  GPIO_PIN_5; 

	GPIOC->ODR = RGB_Code;
	GPIOB->BRR  |=  GPIO_PIN_5;
	__nop();
	__nop();
	__nop();  
	GPIOB->BSRR |=  GPIO_PIN_5; 
	GPIOB->BSRR |=  GPIO_PIN_8; 
	__nop();
	__nop();
	__nop();
	GPIOB->BSRR |=  GPIO_PIN_9; 


	GPIOC->ODR=temp;            //补充
}

  2.4 led灯功能实现 

  CubeMX生成的初始化函数和个人编写的锁定(LD3)、闪烁(LD2)、Data界面(LD3) ,同时还可在LED_Control()解锁状态下调用Personal_SetCompare()修改占空比。

  led.c/h
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "box.h"


#define LED1 GPIOC,GPIO_PIN_8
#define LED2 GPIOC,GPIO_PIN_9
#define LED3 GPIOC,GPIO_PIN_10
#define LOCK GPIOD,GPIO_PIN_2
/* USER CODE BEGIN 0 */
extern uint8_t startFlag;
extern enum Mode mode;
extern enum State state;

/*********  CubeMX 生成 gpio.c修改    ***/
void LED_Init(void)
{
  
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
	
	
  
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
                          |GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);

  /*Configure GPIO pins : PC13 PC14 PC15 PC8
                           PC9 PC10 PC11 PC12 */
  GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
                          |GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : PD2 */
  GPIO_InitStruct.Pin = GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
	

}

uint8_t LED_Tick=0;
void LED_Control()
{
  /***     Data模式界面下灯的状态改变   ***/
	if(mode==DataMode)
	{ 
	  HAL_GPIO_WritePin(LED1,GPIO_PIN_RESET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_RESET);
	}
	else
	{ 
	  HAL_GPIO_WritePin(LED1,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_RESET);
	}	
	/***  判断是否是锁状态   ***/
	if(state==Lock)
	{
	  HAL_GPIO_WritePin(LED3,GPIO_PIN_RESET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_RESET);
		
	}
	else if(state==Unlock)
	{
	  HAL_GPIO_WritePin(LED3,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_RESET);
	  Personal_SetCompare();
	}
	if(startFlag==1)
	{
		
	  if(uwTick-LED_Tick<100)return;
		
	  if(HAL_GPIO_ReadPin(LED2)==SET)HAL_GPIO_WritePin(LED2,GPIO_PIN_RESET);
	  else HAL_GPIO_WritePin(LED2,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_RESET);
	  LED_Tick=uwTick;
		
	}
	else
    {
	  HAL_GPIO_WritePin(LED2,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(LOCK,GPIO_PIN_RESET);
	
	}

}
#ifndef __LED_H__
#define __LED_H__

#ifdef __cplusplus
extern "C" {
#endif

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

void LED_Init(void);
void LED_Control();
/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */

  2.5 修改占空比函数解释(box.c中的Personal_SetCompare)

  根据折线图可以看出占空比D函数关系与ADC采集电压U的关系,如下:

  D=\begin{cases} 0.1,\hspace{4cm}U<1 \\0.375\times U-0.275,\hspace{1cm}1\le U\le3 \\0.85\hspace{3.93cm}U>3 \end{cases} 

  2.6 TIM3中断回调函数重写(box.c中) 

   PWM输入模式下(即TI2FP2)需要捕获下一个上升沿(计算频率)和上一个下降沿(计算占空比),需要重写捕获中断回调函数void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim)其中1000000.0是TIM3计数器经过分频后的计数频率(80M/80=1MHz)。

/*** 测量PWM频率重写回调函数  ***/
//上升沿和下降沿捕获值
u32 riseVal,fallVal,tick=0;
unsigned char tempString[20];
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim)
{
  if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2 && htim==&htim3)
	{
    if(uwTick-tick>100)
		{
		  riseVal=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1;
	      fallVal=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1;
		  if(riseVal >1)
	   	  {
			frequency=1000000.0/(riseVal);
			D_Get=((float)fallVal)/(riseVal);
			
		  }
		  tick=uwTick;
	    }
    }
}

  2.7 main函数内容 

  开启PWM需要使用HAL_TIM_PWM_Start(),开启PWM输入捕获需要开启时基中断HAL_TIM_Base_Start_IT(),输入捕获中断HAL_TIM_IC_Start() ,时钟配置程序在MX中自动生成。

  main.c
/* USER CODE BEGIN Header */

#include "main.h"
#include "adc.h"
#include "tim.h"
#include "lcd.h"
#include "led.h"
#include "key.h"
#include "box.h"

unsigned char temp[20];
extern float frequency;
extern enum State state;

void SystemClock_Config(void);
void System_Init();
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

int main(void)
{

  HAL_Init();

  SystemClock_Config();
  
  /* USER CODE BEGIN SysInit */
   System_Init();
  /* USER CODE END SysInit */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    LCD_Display();
		key_Press();
    LED_Control();
    PWM_Freq_Change();
	  
    record();
		findMAXv();
	
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

void System_Init()
{
  MX_ADC2_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
	
	LCD_Init();
	KEY_Init();
	LED_Init();
	
	LCD_Clear(Black);
	//开启PWM输出
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
	//开启中断,tim3用于捕获,即测量频率和占空比
	HAL_TIM_Base_Start_IT(&htim3);
    HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);
	HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
  
}

三、 开发板调试展示

十四届蓝桥嵌入式省赛调试

四、 补充 

  程序存在部分Bug,如占空比测得100%会使LCD上多出一个%符号,可以用if语句完善,由于刷新界面频繁,会造成一些延时;由于使用枚举,代码看起来更加冗杂,但是思路会清晰一些 。像key.c和led.c已经在十三届蓝桥杯省赛内容分享说明过,可以方便在工程中移植,如果在同样的开发板下建立不同工程,制作可移植代码文件是很方便的。希望文章对读者有所帮助。

   

  

最近更新

  1. TCP协议是安全的吗?

    2024-02-14 12:20:01       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-14 12:20:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-14 12:20:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-14 12:20:01       18 阅读

热门阅读

  1. gorm day6

    2024-02-14 12:20:01       25 阅读
  2. 树结构 严蔚敏 数据结构代码

    2024-02-14 12:20:01       29 阅读
  3. Spring Cloud 路由和消息传递 (HTTP 路由)

    2024-02-14 12:20:01       31 阅读
  4. vue3 + Babylon.js 实现3D场景

    2024-02-14 12:20:01       38 阅读
  5. 14.6 OpenGL图元装配和光栅化:多边形

    2024-02-14 12:20:01       25 阅读
  6. 14.5 OpenGL图元装配和光栅化:线段

    2024-02-14 12:20:01       27 阅读
  7. c语言练习

    2024-02-14 12:20:01       28 阅读
  8. MD5 哈希

    2024-02-14 12:20:01       25 阅读