[Blue Bridge Cup Embedded] Answers to the real questions of the 14th Provincial Competition of Blue Bridge Cup Embedded in 2023

 

Table of contents

0 topic introduction

1 Topic Analysis

2 Cubemx configuration

4 codes

5 effect display


 

0 topic introduction

The specific requirements are as follows

775a27b2985b4fdbabccfd01b2bd04a6.png

 93c99c4eab41493f8d9364108cd38e5f.png

 5c8d99aa31344d39b2497c25bf1ecd59.png5a99c3226b9a41299bf0679445491a79.png

 b0b57759057c47e7b08669e14136a5ca.png

1 Topic Analysis

At first glance, when you get the topic, it is the basic operation. In practice, a bunch of timer operations are easy to make people dizzy.

First look at the peripherals that need to be used

1. GPIO(key/led)

2.LCD

3. Input capture (TIM3 channel 2) and PWM (TIM2 channel 2)

4.ADC(R37)

First of all, there must be a concept here. When you encounter timing and the like, you should think of the combination of flag bits + counters, use flag bits to trigger timer counting, and separate the initial process from the final execution process to reduce code coupling.

2 Cubemx configuration

The configuration is done as follows:

2efd736556fd4bf6a2d24099df8befaf.png

 

The clock tree configuration is as follows (make sure SYSCLK=80M)

8cb74e0d758549cba6ed45c97db6d933.png

GPIO configuration (LED setting INPUT initial state high, PD12 enable bit INPUT, KEY setting bit OUTPUT) 

The ADC configuration is as follows

6520b0c7ff9847cc9de3bbcd35163ca4.png

 73becc45b7144549ade436510c53b7c6.png

Timer 2 TIM2 channel 2 (PWM output configuration PA1) here 80M prescaled by 79 is 1M, and the initial frequency is 1M/(999+1)=1Khz (the title requires 4000, and the specific settings in the following code)

87881957a43d47439f0985a6c804c92c.png

 94182d4fe1ce42ce86f4bdd7c8ff4bcf.png

 Timer 3 TIM3 channel 2 (input capture output configuration PA7)

Here is a brief introduction: select TI2FP2 as the trigger source (because PA7 is TIM3_CH2), after selecting this, PA7 will turn green, select the internal clock (80M) as the clock, and channel 2 (Channel) as the main channel (PA7 is for channel 2), Channel one acts as a slave channel.

The master channel (channel 2) detects the rising edge, and the slave channel (channel 1) detects the falling edge (the calculation frequency only needs to calculate the capture value of the rising edge, and the calculation duty cycle is calculated according to the proportional relationship between the rising edge and the falling edge).

The prescaler is still 79 (+1) to 1M.

9a1bf77ea4004a0eabb3c49826b233c9.png

 ebed3370306b4be0836b2127eaeaa586.png

 tim2 interrupt is enabled (the only interrupt used in the entire topic)935e6d229aa1458e960f6eaac8289cd3.png

 generate file

478e290bfb51429787b823328f61fe54.png

 b76be019249e4011828ba18d2f4c5414.png

 Transplant the header files of lcd related files, create a user.c and user.h (personal habit) to put some function codes, the main codes are all written in the main.c file (if the .s file is missing, please add it yourself)299db79e8e2147a5bc2c042e2a0c92cb.png

 The compiler guarantees that no errors will be reported.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbGnmn5DlvIDov4flhYnvvIg15Z2X77yJ,size_12,color_FFFFFF,t_70,g_se,x_16

4 codes

There are only two files that I need to write

1. user.c和user.h

2.main.c

The user file is as follows, there is nothing to say about this

user.h:

#include "main.h"

void Led_Disp(unsigned char c);
unsigned char Key_Scan(void);

uint16_t getADC2(void);

user.c:

#include "user.h"
#include "adc.h"

//灯
void Led_Disp(unsigned char c)
{
	//全部熄灭
	  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);
	  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
	//点亮对应位
	  HAL_GPIO_WritePin(GPIOC, c<<8, GPIO_PIN_RESET);
	  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

//按键扫描
unsigned char Key_Scan(void)
{
	unsigned char c;
	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
	{
		c = 1;
	}
	else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)
	{
		c = 2;
	}
	else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)
	{
		c = 3;
	}
	else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
	{
		c = 4;
	}
	
	return c;
}

//adc
uint16_t getADC2(void)
{
	uint16_t adc = 0;
	
	HAL_ADC_Start(&hadc2);
	adc = HAL_ADC_GetValue(&hadc2);
	
	return adc;
}

The main function here uses a counter that executes once in 100ms and a counter that executes once in 50ms. 100ms mainly controls the need for the light to flash, and other timings are placed in the 50ms function.

The function of the timer can be observed according to the test data

497a4d13eb2a4eaf8a79256cf6df1952.png

 timer_b2 is the timer related to button 2, timer_b4 is the timer related to button 4, and timer_MH is the timer that keeps the 2s requirement in the title

flag is the corresponding flag bit, see notes for specific functions

The ninth line of the screen is the adc voltage value and the frequency of cap acquisition

main.c


#include "main.h"
#include "adc.h"
#include "tim.h"
#include "gpio.h"



#include "lcd.h"
#include "user.h"
#include <stdio.h>

void keyPro(void);
void lcdPro(void);
void ledPro(void);
void ADCPro(void);
void timer_100(void);
void timer_50(void);


//tim2   pwm
//tim3   cap

__IO uint32_t uwtick_Key,uwtick_Lcd,uwtick_Led,uwtick_ADC,uwtick_timer_200,uwtick_timer_100,uwtick_timer_50;

//key
unsigned char Key_Value,Key_Old,Key_Up,Key_Dowm;
unsigned char page = 1;


//lcd
unsigned char str[25];
unsigned char test;

//*pwm相关变量

unsigned char PWM_Mode = 0;
unsigned char PWM_p = 0;
float V = 10;  //V值
float V_old = 10;  //上一次的V值,用于判断V是否稳定

uint16_t CAP1_UP_Count;  //上升沿捕获值
uint16_t CAP1_DOWM_Count;  //下降沿捕获值
float CAP1_Duty;  //捕获占空比

int32_t PWM1_Duty = 10;  //PWM输出占空比控制
int32_t PWM1_F = 249;   //PWM输出频率控制


unsigned char R = 1;  //R
unsigned char K = 1;  //K
unsigned char N = 0;  //N
float MH = 10;  //低频最大值
float ML = 10;  //高频最大值

unsigned char select_R_K = 0;  //选择R K
unsigned char lock_b4 = 0;  //ADC上锁
float adc_value = 0;  //ADC值


//timer 
int timer_b2 = 0;
int timer_b4 = 0;
int timer_MH = 0; 
int timer_ML = 0; 
//标志位
unsigned flag_b2 = 0;  //1代表正在运行中
int flag_b4 = 0;  //0 没有按下  1上升沿 2下降沿
int flag_MHL = 0; //0没超过  1低超过 2高超过



void SystemClock_Config(void);

int main(void)
{

  HAL_Init();

  SystemClock_Config();

	LCD_Init();

  MX_GPIO_Init();
  MX_ADC2_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();

		
	//cap
	HAL_TIM_Base_Start(&htim3);
	HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
	HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);

	//pwm
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);

	__HAL_TIM_SET_AUTORELOAD(&htim2,124);   //4k=249   8k=124
	//__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,124/10);  

	LCD_Clear(Black);
	LCD_SetBackColor(Black); 
	LCD_SetTextColor(White);

  while (1)
  {
		 ADCPro();
		 lcdPro();
		 keyPro();
		 ledPro();

		 timer_100();//灯闪烁时用
		 timer_50();
  }

}

void lcdPro(void)
{
	if(uwTick - uwtick_Lcd<100) return;
	uwtick_Lcd = uwTick;
	
	if(page == 1)  //数据界面
	{
		sprintf((char *)str,"        DATA  ");			
		LCD_DisplayStringLine(Line1,str);	
		if(PWM_Mode == 0)
		{
			sprintf((char *)str,"     M=L ");	
		}
		else
		{
			sprintf((char *)str,"     M=H ");	
		}	
		
		PWM_p = PWM1_Duty;
		LCD_DisplayStringLine(Line3,str);	
		sprintf((char *)str,"     P=%02d%%     ",PWM_p);			
		LCD_DisplayStringLine(Line4,str);	
		
		
		sprintf((char *)str,"     V=%3.1f     ",V);			
		LCD_DisplayStringLine(Line5,str);	
		

	}
	else if(page == 2)  //参数界面
	{
		sprintf((char *)str,"        PARA  ");		
		LCD_DisplayStringLine(Line1,str);		
		
		if(select_R_K == 0)
		{
			LCD_SetTextColor(Green);
		}
		sprintf((char *)str,"     R=%d   ",R);			
		LCD_DisplayStringLine(Line3,str);		
		LCD_SetTextColor(White);  //恢复
		
		if(select_R_K == 1)
		{
			LCD_SetTextColor(Green);
		}
		sprintf((char *)str,"     K=%d   ",K);	
		LCD_DisplayStringLine(Line4,str);		
		LCD_SetTextColor(White);  //恢复
		
	}
	else if (page == 3)  //统计界面
	{
		sprintf((char *)str,"        RECD  ");			
		LCD_DisplayStringLine(Line1,str);		
		sprintf((char *)str,"     N=%d  ",N);			
		LCD_DisplayStringLine(Line3,str);		
		sprintf((char *)str,"     MH=%4.1f  ",MH);			
		LCD_DisplayStringLine(Line4,str);		
		sprintf((char *)str,"     ML=%4.1f  ",ML);			
		LCD_DisplayStringLine(Line5,str);				
	}
	

	//测试用  提交时屏蔽
	sprintf((char *)str,"t1:%dt2:%dt3:%dt4:%d    ",timer_b2,timer_b4,timer_ML,timer_MH);			
	LCD_DisplayStringLine(Line7,str);
	
	sprintf((char *)str,"b2:%d b4:%d MHL:%d   ",flag_b2,flag_b4,flag_MHL);			
	LCD_DisplayStringLine(Line8,str);
			//adc
	sprintf((char *)str, "R37:%4.2fV PWM:%d ",adc_value,1000000/CAP1_UP_Count);
	LCD_DisplayStringLine(Line9, str);		
}


void keyPro(void)
{
	if(uwTick - uwtick_Key<50) return;
	uwtick_Key = uwTick;
	
	Key_Value = Key_Scan();
	Key_Dowm = Key_Value&(Key_Value^Key_Old);
	Key_Up = ~Key_Value&(Key_Value^Key_Old);
	
	if(Key_Value==4&&Key_Old==4&&page==1)
	{
		flag_b4 = 1;
	}
	else if(Key_Value!=4&&Key_Old==4&&page==1)
	{
		flag_b4 = 2;
	}
	else
	{
		flag_b4 = 0;
	}
	Key_Old = Key_Value;
	
	if(Key_Dowm == 1)
	{
		LCD_Clear(Black);
		if(page == 1)
		{
			page = 2;
		}
		else if(page == 2)
		{
			page = 3;
		}
		else
		{
			page = 1;
		}
	}
	else if(Key_Dowm == 2)
	{
		if(page == 1)  //数据界面
		{
				if(flag_b2 == 0)
				{
					flag_b2 =1;
				}
		}
		if(page == 2)	//参数界面
		{
			if(select_R_K == 0)
			{
				select_R_K = 1;
			}
			else
			{
				select_R_K = 0;
			}
		}
	}
	else if(Key_Dowm == 3)
	{
		if(page == 2)
		{
			if(select_R_K == 0) //R++
			{
				R++;
				if(R>10)
				{
					R=1;
				}
			}
			else  //K++
			{
				K++;
				if(K>10)
				{
					K=1;
				}		
			}
		}
	}
	else if(Key_Dowm == 4)
	{
		if(page == 2)
		{
			if(select_R_K == 0) //R--
			{
				R--;
				if(R==0)
				{
					R=10;
				}
			}
			else  //K--
			{
				K--;
				if(K==0)
				{
					K=10;
				}		
			}
		}		
	}
}

//定时相关处理 50ms/次
void timer_50(void)
{
	if(uwTick - uwtick_timer_50<50) return;
	uwtick_timer_50 = uwTick;
	
	//时间累加
	if(flag_b4==1)
	{
		timer_b4+=50;
	}
	if(flag_b2 == 1)
	{
		timer_b2+=50;
		
		if(PWM_Mode ==1)//4000-8000对应249-124  125/5次 
		{
			PWM1_F += 2;   
			if(PWM1_F>249)
			{
				PWM1_F = 249;
			}
		}
		else
		{
			PWM1_F -= 2; 
			if(PWM1_F<124)
			{
				PWM1_F=124;
			}
		}
	}
	if(flag_MHL == 1)
	{
		timer_ML+=50;
	}
	else if(flag_MHL == 2)
	{
		timer_MH+=50;
	}
	else
	{
		timer_ML = 0;
		timer_MH = 0;
	}
	
	//判断执行
	
	//b4长短按2s
	if(flag_b4==2)
	{
		if(page==1&&timer_b4>2000)//长按
		{
			if(lock_b4 == 0)
			{
				lock_b4 = 1;						
			}
		}
		else  //短按
		{
			if(page==1&&lock_b4 == 1)
			{
				lock_b4 = 0;
			}
		}
	}
	else if(flag_b4==0)
	{
		timer_b4 = 0;
	}
	
	//b2记时5s PWM高低模式切换	
	if(flag_b2 == 1&&timer_b2>5000)
	{
		flag_b2 = 0;
		timer_b2 = 0;
		N++;
		if(PWM_Mode == 0)
		{
			PWM_Mode = 1;					
		}
		else
		{
			PWM_Mode = 0;
		}
	}
	//MH2s
	if(timer_MH>2000)//低频最大值
	{
		MH = V;
		flag_MHL = 0;
	}
	else if(timer_ML>2000)
	{
		ML = V;
		flag_MHL = 0;
	}
}


unsigned char lednum;  

void ledPro(void)
{
	if(uwTick - uwtick_Led<100) return;
	uwtick_Led = uwTick;
	
	if(page == 1)
	{
		lednum|=0x1;
	}
	else
	{
		lednum&=0xfe;
	}
	if(lock_b4 == 1)
	{
		lednum|=0x04;
	}
	else
	{
		lednum&=0xfb;
	}
		Led_Disp(lednum);
}

unsigned char led2flag;  //闪烁标志

void timer_100(void)
{
		if(uwTick - uwtick_timer_100<100) return;
		uwtick_timer_100 = uwTick;
	
		if(timer_b2>0) //闪烁
		{
			if(led2flag ==0)
			{
				led2flag = 1;
			lednum |=0x2;
			}
			else if(led2flag == 1)
			{
				led2flag = 0;
				lednum &=0xfd;
			}
		}	
		else
		{
			lednum &=0xfd;
		}	
}

void ADCPro(void)
{
	if(uwTick - uwtick_ADC<150) return;
	uwtick_ADC = uwTick;
	
	adc_value = getADC2()*3.3/4096;
	
	//ad转换成占空比
	if(lock_b4 ==0)
	{
		if(adc_value<1.0)
		{
			PWM1_Duty = 10;
		}
		else if(adc_value>3.0)
		{
			PWM1_Duty = 85;
		}
		else
		{
			PWM1_Duty = 10+ (adc_value-1)*75/2;
		}
  }
	
	V = (1000000/CAP1_UP_Count)*2*3.14*R/100/K;
	//需要判断V值是否保持
	if(V==V_old&&PWM_Mode==0&&V>ML)  //低频下
	{
		flag_MHL = 1;
	}
	else if(V==V_old&&PWM_Mode==1&&V>MH) //高频下
	{
		flag_MHL = 2;
	}
	else
	{
		flag_MHL = 0;
	}
	V_old = V;
		//频率
		__HAL_TIM_SET_AUTORELOAD(&htim2,PWM1_F);   //4k
		//占空比
		__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,PWM1_F*PWM1_Duty/100);  
	
}

//中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
			if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
			{
				CAP1_UP_Count =  HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1;
				CAP1_Duty = (float)CAP1_DOWM_Count/CAP1_UP_Count;
			}
				else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
			{
				CAP1_DOWM_Count =  HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1;
			}		
}



void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
  RCC_OscInitStruct.PLL.PLLN = 20;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the peripherals clocks
  */
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12;
  PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

5 effect display

Douyin with the same name

 

 

 

 

 

Guess you like

Origin blog.csdn.net/qq_49552487/article/details/130046702