14届蓝桥杯嵌入式国赛

目录

前言

1.使用CUbeMX进行基础初始化配置

(1)选则芯片与基本初始化

(2)LED配置

(3)按键配置

(4)定时器和PWM以及频率

(5)ADC电压检测

2.各外设使用代码。

(1)LED灯

 (2)按键代码(长短按都包含)

(3)定时器

1.定时器中断溢出函数

2.用于定时器中断溢出函数的定时器启用函数(没错,在CubeMX设定了之后还要在函数里初始化)

 3.输入捕获回调函数

 3.用于输入捕获回调函数的定时器启用函数

4. 负责PWM波的定时器的开启

 5.负责PWM波的定时器的赋值使用(分HAL库操作及汇编操作)

 (4)ADC转移(可测电压)直接使用型代码。

(5)LCD显示屏

 3.题目逻辑代码实现

 4.总实现照片

总结

实现代码


前言:

此版是本人使用CUbeMx的创建库函数的练手之作,仅仅涉及到了adc,pwm,频率,LED,LCD,回访记录,按键等操作。

题目:14届蓝桥杯嵌入式国赛

硬件框图:

总体框架较为简单,这个题目考到了DS18B20、单路PWM捕获、ADC采集、PWM生成等模块。细节上东西比较多,处理记录波形和产生波形的时序、温度的温定读取、双按键长按、时间准确控制等,很多都可以在哔站的网站看速成——B站推荐速成总结
14届国赛省赛都在PWM生成上作文章,控制好变化时间还是可以很丝滑的,这题目就是记录电压与PWM波,频率的时候有点怪,其他都非常简单!

由于一些设备原因,本人没有做DS18B20的温度读取(写了一个壳子),其他均符合题目要求。

1.使用CUbeMX进行基础初始化配置

(1)选则芯片与基本初始化

这个操作不多赘述,想了解的可以看看这个博主的博客。嵌入式开发--CubeMX使用入门教程-CSDN博客,值得注意的是我们所要用到芯片主频是80,这个帧率配置过程B站的视频教程里面有。

(2)LED配置

在STM32G431RBTx的芯片图上,LED1~LED8对应的是PC8~15

所以在CUbeMX中,我们要对PC8~15进行配置,

(3)按键配置

同样的,在STM32G431RBTx的芯片图上,B1~B4对应的是PB0~2和PA0。

所以要将所用的引脚配置成输入(GPIO_Input)模式,并设置成上拉输入。

(4)定时器和PWM以及频率

题目要求用PA1进行频率采集,同PA7进行频率输出,我看了一下使用PA1采集PWM就要使用TIM2这个定时器的CH2通道,而PA7用定时器17就可以了。所以定时器的开启也是要注意看题目要求,不要占用定时器。

这个是我配置定时器的设定,通道一采集上升沿,通道二采集下降沿

我们配置的时候修改PSC与Counter,他们遵循公式

工作频率=外部总线频率/(PSC+1)

定时频率=定时器工作频率/(counter+1)=外部总线频率/(PSC+1)*(counter+1)

 由于我设置的外部总线频率为80MHZ(80*10^6HZ),所以可以计算得我设置的TIM2频率为100HZ,0.01秒触发一次中断,注意要设置中段哦!一定要勾选Enabled!

接着是PA7的PWM输出定时器TIM17,这个定时器像我这么设置就可以了,注意输出PWM波的话就不要设置中断,会出问题的!

接下来是其他TIM定时器,这是我代码中所用到的所有定时器。

(5)ADC电压检测

ADC的话直接在Analog里面去选择ADC1,然后再IN11里像我一样配置就好了,其他直接默认就行了。

2.各外设使用代码。

(1)LED灯

#include "led.h"

void LED_Disp(uchar dsLED)
{
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
      HAL_GPIO_WritePin(GPIOC, dsLED<<8, GPIO_PIN_RESET);//
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

这个是本次使用的点亮LED灯的代码,但因为不知道什么原因,只能亮灭0~2的LED灯,所以我是把它当LED初始化全灭代码。

 (2)按键代码(长短按都包含)

struct keys key[4]={0,0,0};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance ==TIM4){
            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(int i=0;i<4;i++){
               switch(key[i].judge_sta){
                     case 0:
                     {
                         if(key[i].key_sta==0) key[i].judge_sta=1;
                           key[i].key_time= 0;
                     }
                     break;
                     case 1:
                     {
                         if(key[i].key_sta==0)
                             {
                                key[i].judge_sta=2;
                             }else {
                                 key[i].judge_sta=0;
                    //           key[i].key_time =0;
                             }
                     }
                     break;
                     case 2:
                     {
                        if(key[i].key_sta==1)
                            {
                                key[i].judge_sta=0;
                                if(key[i].key_time<70){
                                       key[i].single_flag=1;
                                    }                                
                            }else 
                                {
                                    key[i].key_time++;
                                  if(key[i].key_time>70){
                                       key[i].long_flag=1;
                                    }
                                  
                                }
                     }
                     break;
                 }
            }
        }

}

struct keys
{
  uchar judge_sta;
    bool key_sta;
    bool single_flag;
    bool long_flag;
    uint key_time; 
};

按键用swicth进行了消抖操作,并把按键函数设里在了定时器溢出函数里面,只要TIM定时器溢出中断触发,就会检测查看有没有按键被按下,由于函数溢出时间非常快,处理中断时间非常短,可以看成按键一直在检测,且由于是中断,所以不耽误主进程。

(3)定时器

定时器有基本,通用,高级定时器,我们主要了解如何去用这TIM,CUbeMX去配置初始化就不用多说,前文有描述,这里主要是如何在代码中使用TIM。

1.定时器中断溢出函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

      if(htim->Instance ==TIM4){//通过这个代码,可以判断是否是想要的TIM定时器溢出中断                   

      }

}

2.用于定时器中断溢出函数的定时器启用函数(没错,在CubeMX设定了之后还要在函数里初始化)

HAL_TIM_Base_Start_IT(&htim4);

 3.输入捕获回调函数

HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)

{

   if(htim->Instance ==TIM2){

     if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2)

//这里是查看所用的通道有没有信号,毕竟捕获是数据输入,如果没有数据输入就不触发。
         {

         }

   }

}

 3.用于输入捕获回调函数的定时器启用函数

HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);

4. 负责PWM波的定时器的开启

HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);//负责PWM波的定时器开启代码

__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,pa7_duty);

 5.负责PWM波的定时器的赋值使用(分HAL库操作及汇编操作)

HAL库版

set_pwm_param(TIM_HandleTypeDef htim, uint32_t Channel, uint32_t freq, uint16_t duty);

该函数可直接调整占空比和频率

汇编语言版

 TIM17->ARR=1e6/freq_r[cnt_index]*FP-1;//直接调TIM17的ARR
 TIM17->CCR1=duty_r[cnt_index]*(1e6/freq_r[cnt_index]*FP)/100.0f;//直接调TIM17的RCC

 (4)ADC转移(可测电压)直接使用型代码。

#include "dadc.h"

double getADC(ADC_HandleTypeDef *pin)
{
  uint adc;
    HAL_ADC_Start(pin);
  adc =HAL_ADC_GetValue(pin);
    return adc*3.3/4096;
}

这里的4096是2^12的值,通过这一串带码,实现adc的读取

使用方式:getADC(&hadc1);//这里的&hadc1通过CubeMX开启

(5)LCD显示屏

LCD显示屏蓝桥杯官方已经公布了.c与.h文件,也可以去我的bsp文件里面找,可以直接拿来用。

我这里主要是讲LCD的代码初始化,以及如何使用。

LCD_Init();//LCD初始化
    LCD_Clear(Black);//黑色清屏
    LCD_SetBackColor(Black);//黑色背景颜色
    LCD_SetTextColor(White);//白色字体

 char text[30];//需要数组转存
 sprintf(text,"     change:FH      ");
 LCD_DisplayStringLine(Line8, (uint8_t *)text);操作显示在多少行。

 3.题目逻辑代码实现

关于逻辑代码这一块,非常感谢GHHHHHH!-CSDN博客大佬,我看了他的处理可谓是茅塞顿开,用一个数加加去进行定时处理,非常给力,在处理下图板块的时候,通过一个int数加加,进行判断,存储,再输出的时候,继续进行数的加加,非常巧妙。

下面是我看了大佬的博客之后的函数逻辑处理

  if(flag_Record==1){//记录标志位
           if(++cnt_Record>10*TT)
                 {
                     cnt_Record=0;
                     flag_Record=0;//控制LED灯的信号
                       cnt_index=0;
                       flag_jilu=1;//释放频率与占空比的通过信号
                     return ;
                 }
                 if(cnt_Record%10==0)//0.1s
            {
                freq_r[cnt_index]=frq1;//记录频率
                duty_r[cnt_index]=duty1;//记录占空比
                if(getADC(&hadc1)>=VP)//记录频率电压
                    duty_Adc[cnt_index]=90.0f/(3.3f-VP)*(getADC(&hadc1)-3.3f)+100.0f;
                 else
                      duty_Adc[cnt_index]=10;
                 cnt_index++;
                    }
                if(++led_cnt==10){
                    led_cnt=0;
                  LED_Disp(0x01);
                }else LED_Disp(0x00);
                }

 4.总实现照片

界面一和界面二

界面三和界面四

 

总结

作为一个从标准库转HAL库的程序猿,确实是觉得HAL库比标准库方便很多,主要是有CubeMx这个工具的存在,同时,现在一些芯片只能识别HAL库函数,而不能识别标准库,这更体现了HAl库的优势。但这并不意味着标准库就不行,且不说网上资料还是标准库的多,而且标准库可以更好的帮我们了解单片机的原理,打好基础。

希望我的这篇文章能帮助到大家!

实现代码

通过百度网盘分享的文件:14jie4.zip
链接:https://pan.baidu.com/s/1tzceKMszDdTQTxK8tLkfUA 
提取码:fwwz

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值