写作目的

昨天参加了蓝桥杯嵌入式国赛,对我来说蓝桥杯正式落幕,写个省赛一等奖方案纪念一下,作为收尾。

程序题功能实现了大部分,有些要求比赛时没来得及写,功能算不上十全十美,代码和解析仅供参考,希望看到我这篇博客并且正在准备蓝桥杯嵌入式比赛的同学能取得好成绩。

省一奖状:

题目:

一、点灯

cubemx初始配置:PD2、PC8~PC15设置为GPIO_Output。

代码:

uint8_t led_state=0x00;
void led_show(uint8_t num,uint8_t mode)
{
	if(mode){
		led_state |= 1<<(num-1);
	}else{
		led_state &= ~(1<<(num-1));
	}
	
	HAL_GPIO_WritePin(GPIOC,0xff00,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOC,led_state<<8,GPIO_PIN_RESET);
	
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

二、按键

cubemx初始配置:PB0、PB1、PB2、PA0设置为GPIO_Input。

代码:

float v_R37,P,F,add_F=0.0,V=0.0,TarP=5.0,FH=20.0,FL=10.0,PL=1.0,k=0;
uint16_t Q=0,D=5,TarD=5,DH=65;
int page = 0,M_flag = 1,Tar_flag = 0,para_flag = 0,F_flag = 0,duty_flag=0;
void key_scan()
{
	static uint32_t key_press_cnt[4]={0};
	static uint32_t key_long_flag[4]={0};
	
	uint16_t key_state[4];
	key_state[0] = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
	key_state[1] = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
	key_state[2] = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
	key_state[3] = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
	
	for(int i=0;i<4;i++)
	{
		if(key_state[i] == 0){
			if(key_press_cnt[i] == 0){
				key_press_cnt[i] = HAL_GetTick();
			}
			if(HAL_GetTick()-key_press_cnt[i]>=2000 && key_long_flag[i]==0){//长按
				key_long_flag[i] = 1;
				if(i == 0){
					//led_show(1,0);
				}else if(i == 1){
					//led_show(2,0);
				}else if(i == 2){
					//led_show(3,0);
					V = v_R37;
				}else if(i == 3){
					//led_show(4,0);
					Q = 0;
				}
			}
		}else{
			if(key_press_cnt[i] != 0 && (HAL_GetTick()-key_press_cnt[i]<2000) && (HAL_GetTick()-key_press_cnt[i]>=20)){//短按
				if(i == 0){
					//led_show(1,1);
					page ++;
				}else if(i == 1){
					//led_show(2,1);
					if(page %3 == 0){
						M_flag ++;
					}else if(page %3 == 1){
						Tar_flag ++;
					}else if(page %3 == 2){
						para_flag ++;
					}
				}else if(i == 2){
					//led_show(3,1);
					if(page %3 == 1){
						if(Tar_flag %2 == 0){
							if(TarD <95){
								TarD +=5;
							}else{
								TarD = 5;
							}
						}else if(Tar_flag %2 == 1){
							if(TarP < 9.5){
								TarP += 0.5;
							}else{
								TarP = 1.0;
							}
						}
					}else if(page %3 == 2){
						if(para_flag %4 == 0){
							if(FH < 40){
								FH +=1;
							}else{
								FH = 4;
							}
						}else if(para_flag %4 == 1){
							if(FL < 40){
								FL +=1;
							}else{
								FL = 4;
							}
						}else if(para_flag %4 == 2){
							if(PL < 9.5){
								PL += 0.5;
							}else{
								PL = 1.0;
							}
						}else if(para_flag %4 == 3){
							if(DH < 95){
								DH += 5;
							}else{
								DH = 5;
							}
						}
					}
				}else if(i == 3){
					//led_show(4,1);
					
					if(page %3 == 1){
						if(Tar_flag %2 == 0){
							if(TarD >5){
								TarD -=5;
							}else{
								TarD = 95;
							}
						}else if(Tar_flag %2 == 1){
							if(TarP > 1.0){
								TarP -= 0.5;
							}else{
								TarP = 9.5;
							}
						}
					}else if(page %3 == 2){
						if(para_flag %4 == 0){
							if(FH > 4){
								FH -=1;
							}else{
								FH = 40;
							}
						}else if(para_flag %4 == 1){
							if(FL > 4){
								FL -=1;
							}else{
								FL = 40;
							}
						}else if(para_flag %4 == 2){
							if(PL > 1.0){
								PL -= 0.5;
							}else{
								PL = 9.5;
							}
						}else if(para_flag %4 == 3){
							if(DH > 5){
								DH -= 5;
							}else{
								DH = 95;
							}
						}
					}
					
				}
			}
			key_long_flag[i] = 0;
			key_press_cnt[i] = 0;
		}
	}
}

三、adc采样

cubemx初始配置:PB15设置为ADC_IN15

代码:

float v_R37;
void adc_getvalue()
{
	uint16_t temp;
	HAL_ADC_Start(&hadc2);
	HAL_ADC_PollForConversion(&hadc2,10);
	temp = HAL_ADC_GetValue(&hadc2);
	v_R37 = temp *3.3/4096.0;
}

四、测量输入频率

cubemx初始配置:PB4设置为TIM3_CH1,具体配置如下

代码:在main.c打开TIM3

HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);

在stm32g4xx_it.c获取TIM3计数值:

uint16_t cnt_R39;
uint16_t cnt_a3;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM3){
		cnt_R39 = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1);
	}
	cnt_a3 = HAL_TIM_ReadCapturedValue(&htim15,TIM_CHANNEL_2);
}

在自建的fun.c里计算捕获的输入频率:

float fre_R39;
int16_t fre_a3_ic;
extern uint16_t cnt_R39;
extern uint16_t cnt_a3;
void incap_fre()
{
	if(cnt_R39 != 0){
		fre_R39 = 1000000/cnt_R39;
	}
	else{
		fre_R39 = -1;
	}
	
	if(cnt_a3 != 0){
		fre_a3_ic = 1000000/cnt_a3;
	}
	else{
		fre_a3_ic = -1;
	}
}

五、生成PWM方波

cubemx初始设置:PA1引脚设置为TIM2_CH2,具体配置如下

代码:

uint16_t fre_A1_out,arr_A1;
uint16_t duty_A1=6,comp_A1;
void pwm_out()
{
	fre_A1_out = 10000;
	//duty_A1 = 5;
	if(M_flag %2 == 1){
		if(page %3 != 1){
			if(duty_A1 < TarD){
				if(duty_flag == 1){
					duty_A1 += 1;
					duty_flag = 0;
					led_show(4,1);
				}
			}else if(duty_A1 > TarD){
				if(duty_flag == 1){
					duty_A1 -= 1;
					duty_flag = 0;
					led_show(4,1);
				}		
			}else{
				led_show(4,0);
			}
		}	
	}else{
		if(page %3 != 1){
			if(P < (TarP - 0.5)){
				if(duty_A1 < 95){
					if(duty_flag == 1){
						duty_A1 += 1;
						duty_flag = 0;
						led_show(4,1);
					}
				}else{
					led_show(4,0);
				}
			}else if(P > (TarP + 0.5)){
				if(duty_A1>5){
					if(duty_flag == 1){
						duty_A1 -= 1;
						duty_flag = 0;
						led_show(4,1);
					}
				}else{
					led_show(4,0);
				}		
			}else{
				led_show(4,0);
			}		
		}

	}

	arr_A1 = 1000000/fre_A1_out -1;
	comp_A1 = (arr_A1+1) * duty_A1 / 100;
	
	if(__HAL_TIM_GET_COUNTER(&htim2)>(arr_A1+1)){
		__HAL_TIM_SET_COUNTER(&htim2,0);
	}
	
	__HAL_TIM_SET_AUTORELOAD(&htim2,arr_A1);
	__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,comp_A1);
}

六、LCD显示

cubemx初始配置:PA8、PB5、PB8、PB9、PC0~PC15设置为GPIO_Output

代码:

char text[20];
void lcd_main()
{
	sprintf(text,"       MAIN     ");
	LCD_DisplayStringLine(Line1,(uint8_t *)text);
	
	if(M_flag %2 == 0){
		sprintf(text,"   M=AUTO     ");
	}else{
		sprintf(text,"   M=MAN     ");
	}
	LCD_DisplayStringLine(Line3,(uint8_t *)text);
	
	sprintf(text,"   P=%.1fBAR     ",P);
	LCD_DisplayStringLine(Line4,(uint8_t *)text);
	
	sprintf(text,"   F=%.1fL/M     ",F);
	LCD_DisplayStringLine(Line5,(uint8_t *)text);
	
	sprintf(text,"   Q=%dL     ",Q);
	LCD_DisplayStringLine(Line6,(uint8_t *)text);
	
	sprintf(text,"   D=%d%%     ",D);
	LCD_DisplayStringLine(Line7,(uint8_t *)text);	
	
	sprintf(text,"   V=%.1fV     ",V);
	LCD_DisplayStringLine(Line8,(uint8_t *)text);
	
}

void lcd_outp()
{
	sprintf(text,"       OUTP     ");
	LCD_DisplayStringLine(Line1,(uint8_t *)text);	
	
	sprintf(text,"   TarD=%d%%    ",TarD);
	LCD_DisplayStringLine(Line3,(uint8_t *)text);	
	
	sprintf(text,"   TarP=%.1fBAR",TarP);
	LCD_DisplayStringLine(Line4,(uint8_t *)text);	
	
	LCD_ClearLine(Line5);
	LCD_ClearLine(Line6);
	LCD_ClearLine(Line7);
	LCD_ClearLine(Line8);	
}

void lcd_para()
{
	sprintf(text,"       PARA     ");
	LCD_DisplayStringLine(Line1,(uint8_t *)text);		
	
	sprintf(text,"   FH=%.1fL/M     ",FH);
	LCD_DisplayStringLine(Line3,(uint8_t *)text);		
	
	sprintf(text,"   FL=%.1fL/M     ",FL);
	LCD_DisplayStringLine(Line4,(uint8_t *)text);		

	sprintf(text,"   PL=%.1fBAR     ",PL);
	LCD_DisplayStringLine(Line5,(uint8_t *)text);			
	
	sprintf(text,"   DH=%d%%     ",DH);
	LCD_DisplayStringLine(Line6,(uint8_t *)text);		
}

void lcd_judge()
{
	main_look();
	D=duty_A1;
	if(page%3 == 0){
		lcd_main();
		Tar_flag = 0;
	}else if(page%3 == 1){
		lcd_outp();
		para_flag = 0;
	}else if(page%3 == 2){
		lcd_para();
	}
}

完整代码

//仅展示自己写的部分,cubemx生成的不做展示

main.c

#include "fun.h"
int main(void)
{
	LCD_Init();
	LCD_Clear(Black);
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	for(int i=1;i<=8;i++){
		led_show(i,0);
	}
	HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);
	HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
	HAL_TIM_IC_Start_IT(&htim15,TIM_CHANNEL_2);
	uint32_t tick = 0;
  while (1)
  {
		key_scan();
		if(HAL_GetTick()-tick>20){
			tick = HAL_GetTick();
			lcd_judge();
		}

  }
}

stm32g4xx_it.c

#include "fun.h"
uint16_t cnt_R39;
uint16_t cnt_a3;
uint16_t ms_100=0;
uint16_t ms_1000=0;
uint16_t plus_flag = 0;
extern int duty_flag;
void SysTick_Handler(void)
{
  HAL_IncTick();
	ms_100 ++;
	ms_1000 ++;
	if(ms_100 == 100){
		ms_100 = 0;
		plus_flag = 1;
	}
	if(ms_1000 == 1000){
		ms_1000 = 0;
		duty_flag = 1;
	}

}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM3){
		cnt_R39 = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1);
	}
	cnt_a3 = HAL_TIM_ReadCapturedValue(&htim15,TIM_CHANNEL_2);
}

fun.c

//自建的,专门用来放自己写的代码

#include "fun.h"
char text[20];
uint8_t led_state=0x00;
float v_R37,P,F,add_F=0.0,V=0.0,TarP=5.0,FH=20.0,FL=10.0,PL=1.0,k=0;
uint16_t Q=0,D=5,TarD=5,DH=65;
float fre_R39;
uint16_t fre_A1_out,arr_A1;
uint16_t duty_A1=6,comp_A1;
extern uint16_t cnt_R39;
int16_t fre_a3_ic;
extern uint16_t cnt_a3;
int page = 0,M_flag = 1,Tar_flag = 0,para_flag = 0,F_flag = 0,duty_flag=0;
extern uint16_t ms_100;
extern uint16_t plus_flag;
void led_show(uint8_t num,uint8_t mode)
{
	if(mode){
		led_state |= 1<<(num-1);
	}else{
		led_state &= ~(1<<(num-1));
	}
	
	HAL_GPIO_WritePin(GPIOC,0xff00,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOC,led_state<<8,GPIO_PIN_RESET);
	
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

void key_scan()
{
	static uint32_t key_press_cnt[4]={0};
	static uint32_t key_long_flag[4]={0};
	
	uint16_t key_state[4];
	key_state[0] = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
	key_state[1] = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
	key_state[2] = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
	key_state[3] = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
	
	for(int i=0;i<4;i++)
	{
		if(key_state[i] == 0){
			if(key_press_cnt[i] == 0){
				key_press_cnt[i] = HAL_GetTick();
			}
			if(HAL_GetTick()-key_press_cnt[i]>=2000 && key_long_flag[i]==0){//长按
				key_long_flag[i] = 1;
				if(i == 0){
					//led_show(1,0);
				}else if(i == 1){
					//led_show(2,0);
				}else if(i == 2){
					//led_show(3,0);
					V = v_R37;
				}else if(i == 3){
					//led_show(4,0);
					Q = 0;
				}
			}
		}else{
			if(key_press_cnt[i] != 0 && (HAL_GetTick()-key_press_cnt[i]<2000) && (HAL_GetTick()-key_press_cnt[i]>=20)){//短按
				if(i == 0){
					//led_show(1,1);
					page ++;
				}else if(i == 1){
					//led_show(2,1);
					if(page %3 == 0){
						M_flag ++;
					}else if(page %3 == 1){
						Tar_flag ++;
					}else if(page %3 == 2){
						para_flag ++;
					}
				}else if(i == 2){
					//led_show(3,1);
					if(page %3 == 1){
						if(Tar_flag %2 == 0){
							if(TarD <95){
								TarD +=5;
							}else{
								TarD = 5;
							}
						}else if(Tar_flag %2 == 1){
							if(TarP < 9.5){
								TarP += 0.5;
							}else{
								TarP = 1.0;
							}
						}
					}else if(page %3 == 2){
						if(para_flag %4 == 0){
							if(FH < 40){
								FH +=1;
							}else{
								FH = 4;
							}
						}else if(para_flag %4 == 1){
							if(FL < 40){
								FL +=1;
							}else{
								FL = 4;
							}
						}else if(para_flag %4 == 2){
							if(PL < 9.5){
								PL += 0.5;
							}else{
								PL = 1.0;
							}
						}else if(para_flag %4 == 3){
							if(DH < 95){
								DH += 5;
							}else{
								DH = 5;
							}
						}
					}
				}else if(i == 3){
					//led_show(4,1);
					
					if(page %3 == 1){
						if(Tar_flag %2 == 0){
							if(TarD >5){
								TarD -=5;
							}else{
								TarD = 95;
							}
						}else if(Tar_flag %2 == 1){
							if(TarP > 1.0){
								TarP -= 0.5;
							}else{
								TarP = 9.5;
							}
						}
					}else if(page %3 == 2){
						if(para_flag %4 == 0){
							if(FH > 4){
								FH -=1;
							}else{
								FH = 40;
							}
						}else if(para_flag %4 == 1){
							if(FL > 4){
								FL -=1;
							}else{
								FL = 40;
							}
						}else if(para_flag %4 == 2){
							if(PL > 1.0){
								PL -= 0.5;
							}else{
								PL = 9.5;
							}
						}else if(para_flag %4 == 3){
							if(DH > 5){
								DH -= 5;
							}else{
								DH = 95;
							}
						}
					}
					
				}
			}
			key_long_flag[i] = 0;
			key_press_cnt[i] = 0;
		}
	}
}

void adc_getvalue()
{
	uint16_t temp;
	HAL_ADC_Start(&hadc2);
	HAL_ADC_PollForConversion(&hadc2,10);
	temp = HAL_ADC_GetValue(&hadc2);
	v_R37 = temp *3.3/4096.0;
}

void incap_fre()
{
	if(cnt_R39 != 0){
		fre_R39 = 1000000/cnt_R39;
	}
	else{
		fre_R39 = -1;
	}
	
	if(cnt_a3 != 0){
		fre_a3_ic = 1000000/cnt_a3;
	}
	else{
		fre_a3_ic = -1;
	}
}

void pwm_out()
{
	fre_A1_out = 10000;
	//duty_A1 = 5;
	if(M_flag %2 == 1){
		if(page %3 != 1){
			if(duty_A1 < TarD){
				if(duty_flag == 1){
					duty_A1 += 1;
					duty_flag = 0;
					led_show(4,1);
				}
			}else if(duty_A1 > TarD){
				if(duty_flag == 1){
					duty_A1 -= 1;
					duty_flag = 0;
					led_show(4,1);
				}		
			}else{
				led_show(4,0);
			}
		}	
	}else{
		if(page %3 != 1){
			if(P < (TarP - 0.5)){
				if(duty_A1 < 95){
					if(duty_flag == 1){
						duty_A1 += 1;
						duty_flag = 0;
						led_show(4,1);
					}
				}else{
					led_show(4,0);
				}
			}else if(P > (TarP + 0.5)){
				if(duty_A1>5){
					if(duty_flag == 1){
						duty_A1 -= 1;
						duty_flag = 0;
						led_show(4,1);
					}
				}else{
					led_show(4,0);
				}		
			}else{
				led_show(4,0);
			}		
		}

	}

	arr_A1 = 1000000/fre_A1_out -1;
	comp_A1 = (arr_A1+1) * duty_A1 / 100;
	
	if(__HAL_TIM_GET_COUNTER(&htim2)>(arr_A1+1)){
		__HAL_TIM_SET_COUNTER(&htim2,0);
	}
	
	__HAL_TIM_SET_AUTORELOAD(&htim2,arr_A1);
	__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,comp_A1);
}

void lcd_test()
{
	adc_getvalue();
	sprintf(text,"adc37:%.1f",v_R37);
	LCD_DisplayStringLine(Line0,(uint8_t *)text);
	incap_fre();
	sprintf(text,"fre39:%f     ",fre_R39);
	LCD_DisplayStringLine(Line1,(uint8_t *)text);
	pwm_out();
	sprintf(text,"fre_a3:%d     ",fre_a3_ic);
	LCD_DisplayStringLine(Line2,(uint8_t *)text);	
	
}

void lcd_main()
{
	sprintf(text,"       MAIN     ");
	LCD_DisplayStringLine(Line1,(uint8_t *)text);
	
	if(M_flag %2 == 0){
		sprintf(text,"   M=AUTO     ");
	}else{
		sprintf(text,"   M=MAN     ");
	}
	LCD_DisplayStringLine(Line3,(uint8_t *)text);
	
	sprintf(text,"   P=%.1fBAR     ",P);
	LCD_DisplayStringLine(Line4,(uint8_t *)text);
	
	sprintf(text,"   F=%.1fL/M     ",F);
	LCD_DisplayStringLine(Line5,(uint8_t *)text);
	
	sprintf(text,"   Q=%dL     ",Q);
	LCD_DisplayStringLine(Line6,(uint8_t *)text);
	
	sprintf(text,"   D=%d%%     ",D);
	LCD_DisplayStringLine(Line7,(uint8_t *)text);	
	
	sprintf(text,"   V=%.1fV     ",V);
	LCD_DisplayStringLine(Line8,(uint8_t *)text);
	
}

void lcd_outp()
{
	sprintf(text,"       OUTP     ");
	LCD_DisplayStringLine(Line1,(uint8_t *)text);	
	
	sprintf(text,"   TarD=%d%%    ",TarD);
	LCD_DisplayStringLine(Line3,(uint8_t *)text);	
	
	sprintf(text,"   TarP=%.1fBAR",TarP);
	LCD_DisplayStringLine(Line4,(uint8_t *)text);	
	
	LCD_ClearLine(Line5);
	LCD_ClearLine(Line6);
	LCD_ClearLine(Line7);
	LCD_ClearLine(Line8);	
}

void lcd_para()
{
	sprintf(text,"       PARA     ");
	LCD_DisplayStringLine(Line1,(uint8_t *)text);		
	
	sprintf(text,"   FH=%.1fL/M     ",FH);
	LCD_DisplayStringLine(Line3,(uint8_t *)text);		
	
	sprintf(text,"   FL=%.1fL/M     ",FL);
	LCD_DisplayStringLine(Line4,(uint8_t *)text);		

	sprintf(text,"   PL=%.1fBAR     ",PL);
	LCD_DisplayStringLine(Line5,(uint8_t *)text);			
	
	sprintf(text,"   DH=%d%%     ",DH);
	LCD_DisplayStringLine(Line6,(uint8_t *)text);		
}

void lcd_judge()
{
	main_look();
	D=duty_A1;
	if(page%3 == 0){
		lcd_main();
		Tar_flag = 0;
	}else if(page%3 == 1){
		lcd_outp();
		para_flag = 0;
	}else if(page%3 == 2){
		lcd_para();
	}
}

void main_look()
{
	k=10/(3.3-V);
	adc_getvalue();
	if(v_R37 < V){
		P = 0.0;
	}else{
		P = k * v_R37;
	}
	
	incap_fre();
	if(fre_R39 < 800){
		F = 0;
		led_show(1,0);
	}else if(fre_R39 > 8000){
		F = 40;
		F_flag = 1;
		led_show(1,1);
	}else{
		F = fre_R39 / 200;
		led_show(1,0);
	}
	
	if(add_F < 1){
		if(plus_flag == 1){
			add_F += F;
		}else{
		
		}
		plus_flag = 0;
	}else{
		Q += add_F;
		add_F = 0;
	}
	
	pwm_out();
	
}

fun.h

//自建的,专门用来收录自己写的函数和调用的库,起到一个万能头文件的作用

#ifndef _fun_h
#define _fun_h
#include "stm32g4xx.h"
#include "stdio.h"
#include "string.h"
#include "stdint.h"

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

#include "lcd.h"

void led_show(uint8_t num,uint8_t mode);
void key_scan(void);
void adc_getvalue(void);
void incap_fre(void);
void lcd_test(void);
void pwm_out(void);
void lcd_main(void);
void lcd_outp(void);
void lcd_para(void);
void lcd_judge(void);
void main_look(void);
#endif

整体工程详细解析

1、硬件资源配置与外设初始化

main.c 负责系统启动与外设初始化。系统依次初始化 GPIO、ADC2、TIM2、TIM3 和 TIM15,对应的功能分别是 LED 与按键控制、电位器 R37 的模拟采集、PA1 的 PWM 输出、R39 的输入捕获测频,以及 A3 通道的输入捕获测频。LCD 在外设初始化完成后立即初始化并配置为黑底白字。主循环以 20ms 为刷新周期轮询调用 key_scan()lcd_judge(),兼顾按键响应实时性与显示刷新稳定性。

2、中断服务与计时机制

stm32g4xx_it.c 承担所有中断逻辑。SysTick 中断每 1ms 触发一次,其中累积到 100ms 时置位 plus_flag,用于驱动累计流量的定周期采样;累积到 1000ms 时置位 duty_flag,用于驱动 PWM 占空比的 1%/秒平滑调节。TIM3 和 TIM15 的输入捕获回调函数 HAL_TIM_IC_CaptureCallback 分别读取 R39 和 A3 的捕获计数值,存入 cnt_R39cnt_a3,供主循环计算频率使用。

3、压力监测与零点校准

adc_getvalue() 通过 HAL 库对 ADC2 进行单次触发采样,将 12 位 ADC 原始值线性换算为实际电压 v_R37(量程 0 ~ 3.3V)。

压力计算在 main_look() 中完成。系统维护一个零点偏置电压 V,映射斜率 k 根据公式 k = 10 / (3.3 - V) 动态计算,使压力值在 [V, 3.3V] 输入范围内线性映射至 [0, 10.0] BAR。当实时电压低于偏置电压时压力直接取 0,避免负压输出。长按 B3 键 2 秒触发校准,将当时的 v_R37 赋值给 V 并即时更新斜率,满足题目零点校准要求。

4、流量监测与累计流量计算

incap_fre() 根据输入捕获计数值计算频率:以定时器时钟基准(1MHz 计数频率)除以捕获周期计数 cnt_R39,得到 R39 信号的瞬时频率 fre_R39

main_look() 中,瞬时流量 F 按 F = fre_R39 / 200 计算,频率低于 800Hz 时 F 取 0,超过 8000Hz 时 F 固定为 40 并点亮 LD1 报警。

累计流量 Q 采用 100ms 定周期采样累加:每次 plus_flag 置位时,将本周期的流量增量 F/10(即 100ms 对应的升数)累加到中间变量 add_F;当 add_F 满 1L 后整体加入 Q 并清零,确保 Q 以整升为单位更新,避免频繁写入产生显示噪点。长按 B4 键 2 秒可将 Q 清零。

5、PWM 输出控制

pwm_out() 控制 TIM2 通道 2(PA1)输出固定 10kHz 的 PWM 信号。函数通过运算得到 ARR 和 CCR 寄存器的值,并使用 __HAL_TIM_SET_AUTORELOAD__HAL_TIM_SET_COMPARE 在运行时动态更新,无需重启定时器。

占空比平滑调节依赖 duty_flag:每隔 1 秒该标志由 SysTick 中断置位一次,pwm_out() 检测到标志后才对 duty_A1 执行 ±1% 的步进,实现题目要求的 1%/秒平滑过渡速率。

手动模式下(M_flag 为奇数),duty_A1TarD 方向步进;自动模式下,以实时压力 P 与目标压力 TarP 的偏差为依据,P 低于目标区间则增大占空比,P 高于目标区间则减小占空比,处于 ±0.5 BAR 容差带内则保持不变。占空比硬件上限和下限分别为 95% 和 5%。

6、三界面 LCD 显示

lcd_judge() 根据 page % 3 的值分发到三个显示函数,分别对应监控界面(MAIN)、输出配置界面(OUTP)和运行参数界面(PARA),每 20ms 刷新一次。

lcd_main() 显示模式(MAN/AUTO)、实时压力 P、瞬时流量 F、累计流量 Q、当前占空比 D 和零点偏置电压 V;lcd_outp() 显示目标占空比 TarD 和目标压力 TarP,并清除无关行;lcd_para() 显示四个安全参数 FH、FL、PL 和 DH。所有数值格式严格按照题目图示要求,使用 sprintf 格式化后通过 LCD_DisplayStringLine 定行输出。

7、 按键扫描与参数管理

key_scan() 使用静态计时数组实现全软件消抖与长短按识别,无需额外硬件资源。短按通过比较 HAL_GetTick() 差值是否在 20ms 至 2000ms 之间判定,长按则在持续超过 2000ms 时触发且通过 key_long_flag 保证每次长按只响应一次。

B1 控制 page 自增实现三界面循环切换;B2 在不同界面分别切换工作模式、输出配置选项(Tar_flag)和运行参数选项(para_flag);B3/B4 对应当前选中参数的加减操作。

参数"预设不立即生效"的逻辑通过界面切换时在 lcd_judge() 中重置 Tar_flagpara_flag 来实现——用户在 OUTP 或 PARA 界面调整的值在变量中实时保存,退出界面时自动生效,进入时重置选项指针,符合题目对"配置仅在退出界面时触发全局更新"的要求。

8、代码结构总结

stm32g4xx_it.c 专职处理中断与基础计时,fun.c 集中实现所有业务逻辑,main.c 仅负责初始化与主循环调度。三者通过少量 extern 变量共享状态,耦合度低,便于调试与功能扩展。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐