比完省赛,写了几道国赛题,贴在这里和大家分享一下。
以上是第十一届的赛题,这届题目元素包括lcd,led,key,adc,ic,pwm,后面是我的解答思路
首先是cubemx的配置,必考的lcd, led,与key三大元素
打开高速时钟
给led命名
key
方法是通过定时器中断扫描按键状态,由于题目要求按键响应小于0.3秒,所以在配置psc与arr的时候需要计算一下定时器产生中断的时间。
使用记得开启相关定时器中断
回调函数中写按键扫描,为防止抖动,多次判断key_state这个状态,如果真的按下,才会给key_flag置1,而case 3中的time是为了确定按键按下的时间,可以根据题目要求,为time赋值。
key_function这个函数是按键的具体作用,flag1是界面的切换标志,为0是数据界面,为1是参数界面,再按下按键,则跳转到数据界面,切换界面时需要使用清屏函数,并且每一次执行完按键操作时,需要清除按键的标志位。
题目要求按键B2与B3只能在参数界面起作用,所以我们设置一个前提flag1==1,此时,如果我们在数据界面按下时,按键的标志位置1,并且会在参数界面起作用,所以我们需要在flag1==0时,清除按键的标志位。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM16)
{
key[0].key_state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
key[1].key_state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
key[2].key_state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
key[3].key_state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(int i=0;i<4;i++)
{
switch(key[i].state)
{
case 0:
if(key[i].key_state==0)
{
key[i].state=1;
}
case 1:
if(key[i].key_state==0)
{
key[i].key_sflag=1;
key[i].state=2;
}
case 2:
if(key[i].key_state==1)
{
key[i].state=0;
}
if(key[i].key_state==0)
{
key[i].key_time++;
if(key[i].key_time>500)
key[i].key_lflag=1;
}
}
}
}
}
//在中断回调里写扫描
void key_function()
{
if(key[0].key_sflag==1)
{
LCD_Clear(Black);
flag1++;
if(flag1>1)
flag1=0;
key[0].key_sflag=0;
}
if(flag1==0)
{
if(key[1].key_sflag==1)
{
key[1].key_sflag=0;
}
if(key[2].key_sflag==1)
{
key[2].key_sflag=0;
}
}
if(flag1==1)
{
if(key[1].key_sflag==1)
{
led1_last=led1-1;
led1++;
if(led1==led2)
{
led1++;
}
if(led1>8)
{
if(led2==1)
led1=2;
else
led1=1;
}
key[1].key_sflag=0;
}
if(key[2].key_sflag==1)
{
led2_last=led2-1;
led2++;
if(led1==led2)
{
led2++;
}
if(led2>8)
{
if(led1==1)
led2=2;
else
led2=1;
}
key[2].key_sflag=0;
}
}
if(key[3].key_sflag==1)
{
if(pwm_mode==0)
pwm_mode=1;
else
pwm_mode=0;
key[3].key_sflag=0;
}
}
//此函数写按键的作用,例如切换显示屏幕,数字加减
led
蓝桥杯led与lcd共用8个io口,所以是通过锁存器将led的状态存起来,而pd2就是锁存器的关键,使能后,才会将数据存入。
uint16_t led_state[8]={1,1,1,1,1,1,1,1};
void control_led(uint16_t pin,uint8_t level)
{
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,1);
HAL_GPIO_WritePin(GPIOC,led1_Pin|led2_Pin|led3_Pin|led4_Pin|led5_Pin|led6_Pin|led7_Pin|led8_Pin,1);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,0);
for(int i=0;i<8;i++)
{
if((pin&(led1_Pin<<i))==led1_Pin<<i)
{
led_state[i]=level;
}
}
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,1);
for(int i=0;i<8;i++)
{
HAL_GPIO_WritePin(GPIOC,led1_Pin<<i,led_state[i]);
}
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,0);
}
void led_light()
{
if(data[0]>data[1])
{
if(led1-1!=led1_last)
control_led(led[led1_last],1);
control_led(led[led1-1],0);
}
else
{
control_led(led[led1-1],1);
}
if(f1>f2)
{
if(led2-1!=led2_last)
control_led(led[led2_last],1);
control_led(led[led2-1],0);
}
else
{
control_led(led[led2-1],1);
}
}
adc采集与频率测量
adc采集
本届题目使用到一个adc的两个通道,所以需要使用扫描模式,转换次数设为两个,通道13与通道17。
data[0]与data[1]就是采集到的值,需要进行处理,data[0]*3.33/4096,我之前写模拟题时,只将数据乘了3.3,后面精度不够,改为了3.33。
void get_adc(uint16_t *data,uint8_t size)
{
for(int i=0;i<size;i++)
{
HAL_ADC_Start(&hadc2);
if(HAL_ADC_PollForConversion(&hadc2,100)==HAL_OK)
{
data[i]=HAL_ADC_GetValue(&hadc2);
}
}
HAL_ADC_Stop(&hadc2);
}
频率测量
配置TIM2通道2
同样要勾选NVIC
输入捕获测量频率,只需要开启对应通道的输入捕获模式,设置为上升沿触发,用传入的时钟80M/psc/捕获到上升沿时存入寄存器的值,即CCR2,捕获完毕需将CNT清零,等待下一次捕获。
uint16_t f1;
uint16_t f2;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM2)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2)
{
f1=1000000/TIM2->CCR2;
TIM2->CNT=0;
}
}
if(htim->Instance==TIM15)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
{
f2=1000000/TIM15->CCR1;
TIM15->CNT=0;
}
}
}
pwm输出
extern uint16_t pwm_mode;
//同频信号
void pwm_change(uint16_t f1,uint16_t f2)
{
uint16_t psc;
if(pwm_mode==0)
psc=80000/f1;
else
psc=80000/f2;
__HAL_TIM_PRESCALER(&htim17,psc);
}
//由于只是频率改变,所以只改变psc
pwm有三个值可以更改,psc,arr,ccr,psc只与频率有关,arr与频率和占空比有关,ccr与占空比有关。