目录

​​​​​​​

前言

一、分析题型

二、解题步骤

1.大体思路

2.难点分析

1.长短按键处理

2.pwm控制(频率的变换)及输出

3.ADC的设置占空比

4.led的设置

3.详细代码及注释

1,变量

3,lcd_pro

4,key_pro

5,pwm_pro

6,led_pro

7,输入库

8,led(这个要开启Pd2喔)

总结



前言

我本溪流,奈何心有不甘,心向大海。固想改变自己,虽是小比赛,也是想记录自己成长的过程,行之所行,也是想与诸君探讨。

一、分析题型

第十四届省赛其实题目考的很常规,只不过刚接触这个开发板,所以花的时间久了点,不说废话了上正题。回归题目,考了定时器的pwm和输入捕获频率,考了adc的采集,还有按键的长按短按,等等,挺适合练手。系统框图如下。

二、解题步骤

1.大体思路

   显示界面->输入数据(按键,adc采集,频率采集)->逻辑处理->输出数据(pwm,led)

2.难点分析

1.长短按键处理

题目要求:

在数据界面下,长按 B4 按键超过 2 秒后松开(长按键),可以“锁定” 占空比调整功能,此时输出信号占空比保持不变,不受 R37 电位器输出电压控制;处于“锁定”状态后,再次按下 B4 按键(短按键),实现“解锁” 功能,恢复 R37 电位器对输出信号占空比的控制。
解析:

在使用按键时,在进行消抖处理时,我们一般用定时器(10ms)加状态机的思路来做的处理,那么思路打开一点,在结构体里面加上判断上下沿以及上下沿所用的时间,那么就可以通过时间长短来确定长(短)按。

代码如下(按键处理):

//按键判断消抖
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    uchar i;
    if(htim->Instance == TIM6)
    {
        /*------------------------- 按键识别与判断 --------------------*/
        key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
        key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
        key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
        key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
        for(i=0;i<4;i++)
        {
            switch(key[i].key_juadg_sta)//通过标志位key[i].key_juadg_sta的状态来实现状态机的思路
            {
                case 0:
                    if(key[i].key_sta==GPIO_PIN_RESET)
                    {
                        key[i].key_juadg_sta=1;
                        key[i].key_time=0;
                    }
                    break;
                case 1:
                    if(key[i].key_sta==GPIO_PIN_RESET)
                        {
                            key[i].key_juadg_sta=2;
                        }
                    else
                        key[i].key_juadg_sta=0;
                    break;
                case 2:
                    if(key[i].key_sta==GPIO_PIN_SET)
                    {
                        key[i].key_juadg_sta=0;
                        if(key[i].key_time<70)//短按
                            key[i].key_down=1;
                    }else
                    {
                        key[i].key_time++;
                        if(key[i].key_time>200)
                        {
                            key[i].key_up=1;//长按
                        }
                    }
                    break;
            }
        }
        
    
    }
}

2.pwm控制(频率的变换)及输出

题目要求:
        在保证占空比不变的前提下,频率在 5 秒内均匀的升高或降低 到目标频率,要求频率步进值小于 200Hz。
解析:
        我认为这个是本题最难的部分,先知道相关知识点:adc采集R37的电压,以及pwm修改占空比和修改arr的值
公式:频率 = 主频 / (预分频系数+1) / (重装载值+1)
相关函数:
__HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);//设置arr的值
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);//设置占空比
        在不改变占空比,那么在变化时就要不断设置占空比以及arr的值才能保证不变, 频率在 5 秒内均匀的升高或降低 到目标频率,要求频率步进值小于 200Hz。这个就要考验做题人对定时器的掌握了,我们可以通过滴答计时器来定时,通过40ms的进行一次设置占空比以及arr的值,40ms*100=4s<5s且 频率步进值小于 200Hz。符合要求,
代码如下(pwm处理)
static float pwm_bjz=125-1;//步进量
    static uint16_t pwm_bjs=0;//步进次数
/*-------------------------获取adc的值 ------------------*/
    if(!pwm_lolk)//非锁定
    {
        //边界处理
        /*y=75/2x-55/2*/
        pwm_duty=(uint8_t)(75*Get_adc_R37(&hadc2)/2-55/2);
        if(pwm_duty>85) pwm_duty=85;
        if(pwm_duty<10) pwm_duty=10;
    }
    
/*-------------------------设置占空比 ------------------*/
    if(time5s_flag)//模式切换 频率在 5 秒内均匀的升高或降低到目标频率
    {
        if(uwTick-time_40ms>40) //40ms进行一次步进
        {
            if(pwm_mode==0) 
                pwm_bjz+=1.25f;//步进量=125/100=1.25
            else 
                pwm_bjz-=1.25f;
            __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);//设置arr的值
            __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);//设置占空比
            time_40ms=uwTick;
            pwm_bjs++;
        }
        if(pwm_bjs>=100)
        {
            if(!pwm_mode) pwm_bjz=249;
                else 
                    pwm_bjz=125-1;
            __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);
            __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);
            pwm_mode_disp=pwm_mode;
            pwm_bjs=0;
            time5s_flag=0;
        } 
    }else//普通模式
    {
            if(!pwm_mode)
                pwm_bjz=250-1;
            else 
                pwm_bjz=125-1;
        __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);
        __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);
    }
    
    /*-------------------------处理速度 ------------------*/
    if(pwm_mode)//高频
    {
        if(times_2s_mh_flag==0&&MH_val<real_temp_v)
        {
            times_2s_mh_flag=1;
            time_2s=uwTick;
        }else
        {
            if(MH_val<real_temp_v)
            {
                if(uwTick-time_2s>2000)
                {
                    MH_val=real_temp_v;
                    times_2s_mh_flag=1;
                }
                
            }
            else
                 times_2s_mh_flag=0;
        }
    }else//低频
    {
        if(times_2s_mh_flag==0&&real_temp_v>ML_val)
        {
            times_2s_mh_flag=1;
            time_2s=uwTick;
        }else
        {
            if(real_temp_v>ML_val)
            {
                if(uwTick-time_2s>2000)
                {
                    ML_val=real_temp_v;
                    times_2s_mh_flag=1;
                }
                
            }
            else
                 times_2s_mh_flag=0;
        }
    }    
}

3.ADC的设置占空比

题目要求:

解析:

经典3段函数

纯代数问题

注意函数式换成c语言代码我的建议哈这样写

/*y=75/2x-55/2*/
pwm_duty=(uint8_t)(75*Get_adc_R37(&hadc2)/2-55/2);

还有记得边界处理喔!!

if(pwm_duty>85) pwm_duty=85;
if(pwm_duty<10) pwm_duty=10;

4.led的设置

其实我觉得单独控制led简单。这个需要不改变其他led的变化来改变其中一个led的状态还是有点意识我的实现方法,设置参数:进行&|运算在改变gpio如

led_sta=(led_sta&0xfe)|0x01;

3.详细代码及注释

1,变量

/*------------------------- 模式切换区 --------------------*/
uint8_t lcd_mode=0;//lcd界面切换
_Bool pwm_mode=0;//pwm模式切换
_Bool pwm_mode_disp=0;
_Bool pwm_lolk=0;//是否锁定
_Bool RK_mode_flag=0;//rk设置大小切换
_Bool time5s_flag=0;//时间_5s定时
_Bool time2s_flag=0;
_Bool times_2s_mh_flag=0;
_Bool led_2_flag=0;
extern IN_PUT_KEY key[4];

/*------------------------- 数据区 --------------------*/
uint8_t pwm_duty=0;//实时占空比
float real_v=0.0f;//实时速度(V)。
float real_temp_v;//测量速度
uint8_t R_val=1;//实际R 值和 K 值有效范围 1-10,整数。
uint8_t K_val=1;
uint8_t R_temp_val=1;//显示R 值和 K 值有效范围 1-10,整数。
uint8_t K_temp_val=1;
uint8_t N_val=0;//PWM 输出模式切换次数(N)
uint8_t led_sta=0;
float MH_val=0.0f;//高频和低频模式下的速度最大值。
float ML_val=0.0f;
uint32_t pwm_out_hz=4000;//输出频率
/*------------------------- 时间区 --------------------*/
uint32_t time_40ms=0;
uint32_t time_2s;
uint32_t time_100ms;

2,初始化

/*所有逻辑的初始化*/
void My_Init(void)
{
    
    LCD_Init();
    Led_turn_off();//记得使用lcd初始化完成之后关闭led喔
    LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);
    HAL_TIM_Base_Start_IT(&htim6);
    HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);//ADC R37	
    HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);//pwm输出 PA1 
    HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);//输入捕获PA7
    time_40ms=uwTick;
    time_2s=uwTick;
    time_100ms=uwTick;
}

3,lcd_pro

void lcd_pro(void)
{
    char text[60];
    
    switch (lcd_mode)
    {
        case 0:/*数据界面
显示要素包括界面名称(DATA)、PWM 输出模式(M)、实时占空比(P)、实
时速度(V)*/
            
            sprintf(text,"         DATA");
            LCD_DisplayStringLine(Line1, (uint8_t *)text);
            if(pwm_mode_disp) {//PWM 输出模式切换
                sprintf(text, "     M=H");
                LCD_DisplayStringLine(Line3, (uint8_t *) text);
            } else
            {
                sprintf(text, "     M=L");
                LCD_DisplayStringLine(Line3, (uint8_t *) text);
            }
            sprintf(text,"     P=%d%%  ",pwm_duty);
            LCD_DisplayStringLine(Line4, (uint8_t *)text);

            sprintf(text,"     V=%.1f  ",real_temp_v);
            LCD_DisplayStringLine(Line5, (uint8_t *)text);
            break;
        case 1:/*参数界面
显示要素包括界面名称(PARA)、参数 R 和 K 的当前值,R 值和 K 值有效范
围 1-10,整数。*/
            
            sprintf(text,"         PARA");
            LCD_DisplayStringLine(Line1, (uint8_t *)text);
            if(R_temp_val==1)//边界处理
            {
                sprintf(text, "     R=%d ",R_temp_val);//R
                LCD_DisplayStringLine(Line3, (uint8_t *) text);
            }else if(R_temp_val==9)//边界处理
            {
                sprintf(text, "     R=%d ",R_temp_val);//R
                LCD_DisplayStringLine(Line3, (uint8_t *) text);
            }
            sprintf(text, "     R=%d",R_temp_val);//R
            LCD_DisplayStringLine(Line3, (uint8_t *) text);
            if(K_temp_val==1)
            {
                sprintf(text, "     K=%d ",K_temp_val);//R
                LCD_DisplayStringLine(Line4, (uint8_t *) text);
            }else if(K_temp_val==9)
            {
                sprintf(text, "     K=%d ",K_temp_val);//R
                LCD_DisplayStringLine(Line4, (uint8_t *) text);
            }
            sprintf(text,"     K=%d",K_temp_val);//K
            LCD_DisplayStringLine(Line4, (uint8_t *)text);
            break;
        case 2:/*统计界面
显示要素包括:界面名称(RECD)、PWM 输出模式切换次数(N)、高频和低
频模式下的速度最大值*/
            sprintf(text,"         RECD");
            LCD_DisplayStringLine(Line1, (uint8_t *)text);
            sprintf(text, "     N=%d",N_val);//R
            LCD_DisplayStringLine(Line3, (uint8_t *) text);
            sprintf(text,"     MH=%.1f",MH_val);
            LCD_DisplayStringLine(Line4, (uint8_t *)text);
            sprintf(text,"     MH=%.1f",ML_val);
            LCD_DisplayStringLine(Line5, (uint8_t *)text);
            break;
    }
    
}

4,key_pro

void key_pro(void)
{
/*-------------------------短按逻辑处理 ------------------*/
    /*定义为“界面”按键,按下 B1 按键可以往复切换数据、参数和记录三
个界面,*/
    if(key[0].key_down==1)//b1逻辑
    {
        
        LCD_Clear(Black);
        if(++lcd_mode==3)
            lcd_mode=0;
        if(lcd_mode==1)
            RK_mode_flag=0;
        if(lcd_mode==2)
        {
            R_val=R_temp_val;
            K_val=K_temp_val;
        }
        key[0].key_down=0;
        
    }
    
    if(key[1].key_down==1)//b2逻辑
    {
        /*在数据界面下,用于切换选择低频或高频模式。按键按下后,5 秒内不
可再次触发切换功能。*/
        if(lcd_mode==0&&time5s_flag==0)
        {
            time5s_flag=1;
            pwm_mode^=1;
            N_val++;
        }
        if(lcd_mode==1)//切换选择R或K参数。
            RK_mode_flag^=1;
        key[1].key_down=0;
    }
    if(key[2].key_down==1)//b3逻辑
    {
        if(lcd_mode==1)/*在参数界面下,按下 B3 按键,当前可调整的参数加 1,参数调整模式:*/
        {
            if(!RK_mode_flag) {
               if (++R_temp_val == 11) {
                   R_temp_val = 1;
               }
           }
           else {
                 if (++K_temp_val == 11) {
                  K_temp_val = 1;
                  }
                }
        }
        key[2].key_down=0;
    }
    if(key[3].key_down==1)//b4逻辑
    {
        if(lcd_mode==1)/*在参数界面下,按下 B4 按键,当前可调整的参数减 1,参数调整模式:*/
        {
            if(!RK_mode_flag) {
                if (--R_temp_val == 0) {
                    R_temp_val = 10;
                }
            }
            else {
                if (--K_temp_val == 0) {
                    K_temp_val = 10;
                }
            }
        }
        if(lcd_mode==0&&time2s_flag==1)
        {
            time2s_flag=0;//解锁
            pwm_lolk=0;
        }
        key[3].key_down=0;
    }
    if(key[3].key_up==1)
    {
        time2s_flag=1;//锁定状态
        key[3].key_up=0;
        pwm_lolk=1;
    }
    
}

5,pwm_pro

void pwm_pro(void)
{
    static float pwm_bjz=125-1;//步进量
    static uint16_t pwm_bjs=0;//步进次数
/*-------------------------获取adc的值 ------------------*/
    if(!pwm_lolk)//非锁定
    {
        //边界处理
        /*y=75/2x-55/2*/
        pwm_duty=(uint8_t)(75*Get_adc_R37(&hadc2)/2-55/2);
        if(pwm_duty>85) pwm_duty=85;
        if(pwm_duty<10) pwm_duty=10;
    }
    
/*-------------------------设置占空比 ------------------*/
    if(time5s_flag)//模式切换 频率在 5 秒内均匀的升高或降低到目标频率
    {
        if(uwTick-time_40ms>40) //40ms进行一次步进
        {
            if(pwm_mode==0) 
                pwm_bjz+=1.25f;//步进量=125/100=1.25
            else 
                pwm_bjz-=1.25f;
            __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);//设置arr的值
            __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);//设置占空比
            time_40ms=uwTick;
            pwm_bjs++;
        }
        if(pwm_bjs>=100)
        {
            if(!pwm_mode) pwm_bjz=249;
                else 
                    pwm_bjz=125-1;
            __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);
            __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);
            pwm_mode_disp=pwm_mode;
            pwm_bjs=0;
            time5s_flag=0;
        } 
    }else//普通模式
    {
            if(!pwm_mode)
                pwm_bjz=250-1;
            else 
                pwm_bjz=125-1;
        __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);
        __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);
    }
    
    /*-------------------------处理速度 ------------------*/
    if(pwm_mode)//高频
    {
        if(times_2s_mh_flag==0&&MH_val<real_temp_v)
        {
            times_2s_mh_flag=1;
            time_2s=uwTick;
        }else
        {
            if(MH_val<real_temp_v)
            {
                if(uwTick-time_2s>2000)
                {
                    MH_val=real_temp_v;
                    times_2s_mh_flag=1;
                }
                
            }
            else
                 times_2s_mh_flag=0;
        }
    }else//低频
    {
        if(times_2s_mh_flag==0&&real_temp_v>ML_val)
        {
            times_2s_mh_flag=1;
            time_2s=uwTick;
        }else
        {
            if(real_temp_v>ML_val)
            {
                if(uwTick-time_2s>2000)
                {
                    ML_val=real_temp_v;
                    times_2s_mh_flag=1;
                }
                
            }
            else
                 times_2s_mh_flag=0;
        }
    }    
}

6,led_pro

/*-------------------------led灯的处理函数 ------------------*/
void led_pro(void)
{
    static uint8_t led_sta=0;
    /*LD1:处于数据界面,指示灯 LD1 点亮,否则熄灭。*/
    if(lcd_mode==0)
        led_sta=(led_sta&0xfe)|0x01;
    else
        led_sta=(led_sta&0xfe);
     LED_Disp(led_sta,1);
    /*LD2:低频模式、高频模式切换期间,指示灯 LD2 以 0.1 秒为间隔切换亮、
灭状态,模式切换完成后熄灭*/
    if(time5s_flag)
    {
        led_sta=(led_sta&0xfd);
        if(uwTick-time_100ms>100)
        {
            time_100ms=uwTick;
            led_2_flag^=1;
            if(led_2_flag)led_sta=(led_sta&0xfd)|0x02;
            else
                led_sta=(led_sta&0xfd);
        }
    }
    else
        led_sta=(led_sta&0xfd);
    /*LD3:占空比调整处于“锁定”状态时,指示灯 LD3 点亮,否则熄灭。*/
    if(pwm_lolk)
        led_sta=(led_sta&0xfb)|0x04;
    else
        led_sta=(led_sta&0xfb);
}

7,输入库

#include "lq_14_input.h"
/*typedef struct
{
  uint8_t key_juadg_sta;
  _Bool key_down;
  _Bool key_up;
  _Bool key_sta;
  uint32_t key_time;
}IN_PUT_KEY*/;//(我放在.h)

IN_PUT_KEY key[4];

extern uint8_t R_val;//实际R 值和 K 值有效范围 1-10,整数。
extern uint8_t K_val;
extern float real_temp_v;//测量速度
//按键判断消抖
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    uchar i;
    if(htim->Instance == TIM6)
    {
        /*------------------------- 按键识别与判断 --------------------*/
        key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
        key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
        key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
        key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
        for(i=0;i<4;i++)
        {
            switch(key[i].key_juadg_sta)
            {
                case 0:
                    if(key[i].key_sta==GPIO_PIN_RESET)
                    {
                        key[i].key_juadg_sta=1;
                        key[i].key_time=0;
                    }
                    break;
                case 1:
                    if(key[i].key_sta==GPIO_PIN_RESET)
                        {
                            key[i].key_juadg_sta=2;
                        }
                    else
                        key[i].key_juadg_sta=0;
                    break;
                case 2:
                    if(key[i].key_sta==GPIO_PIN_SET)
                    {
                        key[i].key_juadg_sta=0;
                        if(key[i].key_time<70)//短按
                            key[i].key_down=1;
                    }else
                    {
                        key[i].key_time++;
                        if(key[i].key_time>200)
                        {
                            key[i].key_up=1;//长按
                        }
                    }
                    break;
            }
        }
        
    
    }
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    uint16_t pwm_arr = 0;
    uint16_t pwm_frq=0;
    if(htim->Instance == TIM17)
    {
        pwm_arr = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
        __HAL_TIM_SetCounter(htim,0);
        pwm_frq = 1000000/pwm_arr/100;
        real_temp_v= pwm_frq/K_val*R_val*6.28;
        HAL_TIM_IC_Start_IT(htim, TIM_CHANNEL_1);
    }
    
}


double Get_adc_R37(ADC_HandleTypeDef *pin)
{
    uint16_t adc=0;
    HAL_ADC_Start(pin);
    adc=HAL_ADC_GetValue(pin);
    return adc*3.3/4096;
}

8,led(这个要开启Pd2喔)

#include "lq_14_led.h"
uint8_t led_on[]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
void Led_turn_off(void)
{
    uint32_t led_val = 0xff00;
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    GPIOC->ODR= led_val;
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_Disp(uint8_t led,_Bool flag)
{
    Led_turn_off();
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    if(flag)
        HAL_GPIO_WritePin(GPIOC,led<<8,GPIO_PIN_RESET);
    else
        HAL_GPIO_WritePin(GPIOC,led<<8,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_toop(uint8_t led)
{
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    HAL_GPIO_TogglePin(GPIOC,led<<8);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

总结

岁月漫长,心怀热爱,携手共赴星辰大海,内容结尾愿诸君保持热爱,一起努力,感谢各位看到这里,也希望在我大学生涯中,依旧保持对嵌入式的热爱,当然我也是小白,互相进步,感谢帮助我的人们,我也想将这份热情传承大家,以上就是蓝桥杯嵌入式第十四届省赛的题目解析了,如果有什么问题和建议都欢迎在评论区提出来喔。

点击阅读全文
Logo

欢迎加入西安开发者社区!我们致力于为西安地区的开发者提供学习、合作和成长的机会。参与我们的活动,与专家分享最新技术趋势,解决挑战,探索创新。加入我们,共同打造技术社区!

更多推荐