原题展示
考点精练
按键输入、LED、LCD显示、脉冲捕获、PWM、ADC、按键长按(红色为难点,橙色为较难)
CubeMax 配置
SYS配置:Serial Wire(串行调试)
RCC配置:内部时钟
时钟配置
按键配置:PC0~PC3设置为GPIO_Input
LED配置:PC8~PC15 GPIO_Ouput
PWM配置:PA1 TIM2_CH2
脉冲捕获:PA7 TIM3_CH2
ADC配置:PB15 ADC_IN15
原题题解
相关变量
#define pi 3.14
//lcd
uint8_t jiemian,lcd_buf[21]; //初始化界面
uint8_t r=1,k=1; //参数初始化
uint8_t i_flag;//标志位
//led
uint8_t led_buf1,led_buf2,led_buf3; //赋值led
uint32_t ledTick=0xffffff,led_flag;//led计时
//key
uint8_t pm[2]={'L','H'},Frq_Mode,n;//按键切换高低频
u32 KeyTick,ModeTick;//按键防抖
uint8_t rk;//R&K数值切换
uint8_t modekey; // 判断是否是首次按下
uint8_t flag_b4; //长按标志位
uint16_t timer2s; //延时超过两秒;
//pwm
uint8_t pc[2]; //
float v[2],v1[2];
uint8_t b4c;
uint32_t pwm_f=4000 ;//频率值
//adc
uint32_t value;
float volt;
uint8_t lock;
uint32_t AdcTick0=0,AdcTick1=0;
uint32_t tim3_cnt =0,tim3_frq;
ADC模块
void ADC_Proc(void)
{
HAL_ADC_Start (&hadc2 ); //开启ADC2
value= HAL_ADC_GetValue (&hadc2 ); //获取ADC2的值
volt = value/4096.0f*3.3f; //转换模拟值
if(b4c == 0&jiemian==0) //R37调节占空比
{
if(volt < 1) //ADC输出小于1V,保持10%占空比
{
Pwm_Control[Frq_Mode]=10;
}
if(volt>=1&volt<=3) //ADC输出在1~3V内,占空比线性增长
{
Pwm_Control[Frq_Mode]=(volt-1)*37.5f+10;
}
if(volt>3) //ADC输出大于3V,保持85%占空比
{
Pwm_Control[Frq_Mode]=85;
}
}
}
PWM模块
void PWM_Proc(void)
{
if(Frq_Mode==0) //低频模式
{
//调整频率
__HAL_TIM_SET_AUTORELOAD (&htim2 ,1000000/pwm_f-1);
//调节占空比
__HAL_TIM_SET_COMPARE (&htim2 ,TIM_CHANNEL_2 ,(uint8_t)(250*(Pwm_Control[Frq_Mode]/100.0f)));
}
if(Frq_Mode==1) //高频模式
{
//调整频率
__HAL_TIM_SET_AUTORELOAD (&htim2 ,1000000/pwm_f -1);
//调节占空比
__HAL_TIM_SET_COMPARE (&htim2 ,TIM_CHANNEL_2 ,(uint8_t)(125*(Pwm_Control[Frq_Mode]/100.0f)));
}
}
脉冲捕获
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
//PA7引脚捕获脉冲
if(htim->Instance ==TIM3 )
{
tim3_cnt = HAL_TIM_ReadCapturedValue (htim,TIM_CHANNEL_2);
TIM3->CNT=0;
tim3_frq = 80000000/(80*tim3_cnt);
}
if(Frq_Mode==0)
{
v1[0]= tim3_frq *2*pi*r/100.0f/k;
}
if(v[0]<v1[0]&&uwTick-AdcTick0 >2000) //统计大于2S赋值
{
v[0]=v1[0];
}
if(Frq_Mode==1)
{
v1[1]= tim3_frq*2*pi*r/100.0f/k;
}
if(v[1]<v1[1]&&uwTick-AdcTick1 >2000) //统计大于2S赋值
{
v[1]=v1[1];
}
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2 );
}
LCD模块
1.初始化背景
void LCD_Setting(void)
{
//设置LCD的背景色
LCD_Clear(Black);
//设置LCD字体颜色
LCD_SetTextColor(White);
//设置LCD字体的背景色
LCD_SetBackColor(Black);
}
2.显示功能
void LCD_Proc(void)
{
if(jiemian == 0) ///数据界面
{
LCD_DisplayStringLine (Line1,(u8 *)" DATA");
sprintf((char*)lcd_buf ," M=%c",pm[i_flag]);
LCD_DisplayStringLine (Line3,(u8 *)lcd_buf );
sprintf((char*)lcd_buf ," P=%d%%",Pwm_Control[Frq_Mode]);
LCD_DisplayStringLine (Line4,(u8 *)lcd_buf );
sprintf((char*)lcd_buf ," V=%.1f",v1[Frq_Mode]);
LCD_DisplayStringLine (Line5,(u8 *)lcd_buf );
}
if(jiemian == 1) //参数界面
{
LCD_DisplayStringLine (Line1,(u8 *)" PARA");
sprintf((char*)lcd_buf ," R=%d",r);
LCD_DisplayStringLine (Line3,(u8 *)lcd_buf );
sprintf((char*)lcd_buf ," K=%d",k);
LCD_DisplayStringLine (Line4,(u8 *)lcd_buf );
}
if(jiemian == 2) //统计界面
{
LCD_DisplayStringLine (Line1,(u8 *)" RECD");
sprintf((char*)lcd_buf ," N=%d",n);
LCD_DisplayStringLine (Line3,(u8 *)lcd_buf );
sprintf((char*)lcd_buf ," MH=%.1f",v[1]);
LCD_DisplayStringLine (Line4,(u8 *)lcd_buf );
sprintf((char*)lcd_buf ," ML=%.1f",v[0]);
LCD_DisplayStringLine (Line5,(u8 *)lcd_buf );
}
}
按键模块
1.Key_Read()子程序
#include "key.h"
#define KB1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define KB2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define KB3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define KB4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
#define KEYPORT KB1 | (KB2<<1) | (KB3<<2) | (KB4<<3) | 0xf0
uint8_t Trg;
uint8_t Cont;
void Key_Read()
{
uint8_t DataRead = (KEYPORT)^0xff;
Trg = DataRead & (DataRead ^ Cont);
Cont = DataRead;
}
2.Key_Proc()函数
void Key_Proc(void)
{
if(uwTick-KeyTick <10)return; //按键防抖
KeyTick = uwTick;
Key_Read (); //调用KEY_Read子程序
//B4按键控制PWM占空比
if(Trg&0x08 && Cont == 0x08 && jiemian==0) //在数据界面下,短按解锁PWM状态
{
flag_b4 = 1;
}
else if(Trg!=0x08 && Cont == 0x08 && jiemian==0) //数据界面下,长按锁定PWM状态
{
flag_b4 = 2;
}
else //其他情况,状态保持
{
flag_b4 = 0;
}
//B1按键功能
if(Trg & 0x01)
{
if(jiemian>1) //界面切换
jiemian=0;
else
jiemian++;
if(jiemian ==1) //从数据界面进入参数界面默认可调整参数为R
rk=0;
LCD_Clear (Black); //界面切换后自动清屏
}
//B2按键功能
if(Trg & 0x02)
{
if(jiemian==0) //切换高低频
{
if(modekey == 1)
{
if(Frq_Mode==0&&uwTick-ModeTick>=5000) //五秒内不能再次按下
{
Frq_Mode=1; //切换高频率
ModeTick = uwTick; //重置ModeTick
n++; //高低频率切换次数加一
ledTick=uwTick; //重置led
LCD_Clear (Black);
}
else if(Frq_Mode==1&&uwTick-ModeTick>=5000)
{
Frq_Mode=0; //切换低频率
ModeTick = uwTick; //重置MoedTick
n++; //高低频率切换次数加一
ledTick=uwTick; //重置LedTick
LCD_Clear (Black);
}
}
if(modekey==0) //首次切换
{
Frq_Mode=1;
modekey = 1;
ModeTick = uwTick;
n=1;
ledTick=uwTick;
LCD_Clear (Black);
}
led2_flag =1;
}
if(jiemian==1) //切换R&K调整参数
{
if(rk==0)
{
rk=1; //K可调
}
else
rk=0; //R可调
}
}
//B3按键功能
if(Trg & 0x04)
{
if(jiemian ==1) //参数界面下R&K加一
{
if(rk==0)
{
if(r<10)
r++;
else //循环置为1,清屏一次
{
r=1;
LCD_Clear (Black);
}
AdcTick0 =uwTick;
AdcTick1 =uwTick;
}
if(rk==1)
{
if(k<10)
k++;
else //循环置为1,清屏一次
{
k=1;
LCD_Clear (Black);
}
AdcTick1 =uwTick;
AdcTick0 =uwTick;
}
}
}
//B4按键功能
if(Trg & 0x08)
{
if(jiemian == 1) //参数界面下R&K减一
{
if(rk==0)
{
if(r>1&&r<10)
{
r--;
}
else if(r==10) //R减为9,清屏一次
{
r--;
LCD_Clear (Black);
}
else //循环置为10
{
r=10;
}
AdcTick0 =uwTick;
AdcTick1 =uwTick;
}
if(rk==1)
{
if(k>1&&k<10)
{
k--;
}
else if(k==10) //K减为9,清屏一次
{
k--;
LCD_Clear (Black);
}
else //循环置为10
{
k=10;
}
AdcTick1 =uwTick;
AdcTick0 =uwTick;
}
}
}
}
LED模块
1.Led_Read子程序
#include "led.h"
void Led_Read(uint8_t led_control1)
{
HAL_GPIO_WritePin(GPIOC,0xffff,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,led_control1<<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 Led_Read2(uint8_t led_control2)
{
HAL_GPIO_WritePin(GPIOC,led_control2<<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 Led_Read3(uint8_t led_control3)
{
HAL_GPIO_WritePin(GPIOC,led_control3<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
2.Led_Proc函数
void Led_Proc()
{
//DATA界面,长亮LD1
if(jiemian==0)
{
led_buf1 =0x01;
}
else //熄灭LD1
led_buf1 &=~0x01;
//高低频率切换5秒内,LD2间隔0.1秒闪烁
if(n>0&&uwTick-ModeTick<=5000)
{
if(uwTick-ledTick <=100) //点亮
{
led_buf2 = 0x02;
}
if(uwTick-ledTick2>100&&uwTick-ledTick<=200) //熄灭
{
led_buf2 &=~ 0x02;
}
if(uwTick-ledTick>200) //重置ledTick
{
ledTick=uwTick;
}
}
else //5秒后关闭LD2
led_buf2 &=~0x02;
//PWM锁住,常亮LD3
if(b4c==1)
{
led_buf3 = 0x04;
}
else
{
led_buf3 &=~0x04;
}
Led_Read(led_buf1);
Led_Read2(led_buf2 );
Led_Read3 (led_buf3 );
}
计时设计
void Timer_Proc()
{
//计时数据界面下,按下B4
if(flag_b4 ==2)
{
timer2s+=50;
if(timer2s >2000&jiemian==0) //数据界面,长按2s锁定PWM占空比状态
{
if(lock==0)
lock=1;
b4c=1;
}
else
{
if (jiemian == 0 & lock==1) //数据界面,短按解锁PWM占空比状态
{
lock=0;
b4c=0;
}
}
}
else if(flag_b4 == 0)
{
timer2s = 0; //置位timer2s
}
//PWM高低频率切换
if(n%2==0)
{
if(pwm_f >4000) //5秒内PWM输出频率均匀增加
{
pwm_f -=16;
AdcTick0 =uwTick;
AdcTick1 =uwTick;
}
}
else
{
if(pwm_f <8000) //5秒内PWM输出频率均匀降低
{
pwm_f+=16;
AdcTick1 =uwTick;
AdcTick0 =uwTick;
}
}
if(pwm_f ==8000)
i_flag = 1;
if(pwm_f==4000)
i_flag=0;
TIM2->ARR=1E6/pwm_f-1; //捕获脉冲频率
TIM2->CCR2 = (TIM2->ARR+1)*pc[i]/100; //捕获脉冲占空比
}
主函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC2_Init();
MX_TIM2_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
LCD_Setting();
//设置自动重载值
TIM2->ARR=245;
//设置定时器比较值
TIM2->CCR2=125;
//启动TIM2里的PWM
HAL_TIM_PWM_Start (&htim2 ,TIM_CHANNEL_2 );
//启动TIM3里的脉冲捕获
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2 );
//初次上电
modekey=0;
//初始AdcTick0和AdcTick1
AdcTick0 =uwTick;
AdcTick1 = uwTick;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
LCD_Proc();
ADC_Proc();
Key_Proc();
Timer_Proc();
Led_Proc();
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
总结
蓝桥杯第十四届题目相对较难,考点之间的联系比较紧密。脉冲捕获和按键长按是比较新的考点,很容易让人摸不同头脑,但最重要的还是有扎实的基础和清晰的逻辑。
好了,以上就是蓝桥杯嵌入式第十四届省赛的编程题目解析,如果有什么问题和建议都欢迎在评论区提出来喔。