0
点赞
收藏
分享

微信扫一扫

备战蓝桥杯(4)——第九届蓝桥杯嵌入式省赛赛题实战

梦幻之云 2022-03-12 阅读 70

系列文章目录

备战蓝桥杯(3)——第八届蓝桥杯嵌入式省赛赛题实战
备战蓝桥杯(2)——第七届蓝桥杯嵌入式省赛赛题实战
备战蓝桥杯(1)——第六届蓝桥杯嵌入式省赛赛题实战


文章目录


前言

这次第九届蓝桥杯的省赛的题比第八届省赛的题简单了,但是我在做的时候也有很多小问题导致卡壳,也是将之前学习过的东西在做了一次小复习,同时这次省赛的长短按键也是比第一次接触到,很有收获。本次仍然是基于CT117E-M4平台,用的CUBEMX与keil开发


一、第九届赛题蓝桥杯嵌入式省赛赛题展示

在这里插入图片描述
在这里插入图片描述


二、程序流程图

在这里插入图片描述程序流程图是在程序写完之后画的,根据我编的程序的逻辑关系绘制的。这道题的逻辑关系不复杂,思路很清晰。


三、CUBEMX配置

在这里插入图片描述这里的配置都是一些常规的配置,看着题目来,要啥配啥就行。这里要注意一下(也是我范的错误),官方给的EEPROM代码里面虽然有配置I2C的引脚(PA6,PA7),但是需要在CUBEMX里面配置一下,参数默认即可。我看了代码,我觉得是因为例程里的引脚没有初始化就直接拿来用了,所以如果没有在CUBEMX里面配置的话,是无法读取和写入EEPROM的


四、各部分代码编写

先上主函数和变量定义:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "i2c.h"
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
struct Time{
    uint8_t hour;
    uint8_t min;
    uint8_t sec;
} TT_1;
/* USER CODE END PD */
int go_stop = 1; //计时开始/结束标志位
int time_cnt4 = 0; //定时器4计数值
int long_pushB2 = 0;  //B2按键长按标志位
int long_pushB3 = 0;  //B3按键长按标志位
uint8_t str[20]; 
uint8_t str1[10];
uint8_t str2[20] = "      **          ";
uint8_t str3[20] = "         **       ";
uint8_t str4[20] = "            **    ";
unsigned char str5[10];
uint8_t re_loc = 1; //存储位置标志位,1至5
extern uint8_t time_cnt; //外部变量,在xxxxxit.c,定时器计数值
extern uint8_t sec_08;  //外部变量,在xxxxxit.c,0.8秒计时标志位
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
uint8_t x24c02_read(uint8_t address); //EEPROM读取函数
void x24c02_write(unsigned char address,unsigned char info);  //EEPROM写入函数
void loc_change(void);  //改变存储位置
void time_setting(void);  //设置时间
void time_go_stop(void);  //启动/结束定时器
void led_flash(void);  //led灯闪烁
void PWM_gen(void);  //PWM波产生函数
void res_time(void);//存储时间
void read_time(void);//读取时间
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
int main(void)
{
  /* USER CODE BEGIN 1 */
    TT_1.hour = 0;
    TT_1.min = 0;
    TT_1.sec = 0;    
  /* USER CODE END 1 */
  HAL_Init();
  /* USER CODE BEGIN Init */
    I2CInit();
    LCD_Init();
    LCD_Clear(White);
    LCD_SetBackColor(White);
    LCD_SetTextColor(Black);    
  /* 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_TIM2_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();
  /* USER CODE BEGIN 2 */
  read_time();
  sprintf((char *)str,"      %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);
  LCD_DisplayStringLine(Line4,str);
  sprintf((char *)str1,"   No %d",re_loc);
  LCD_DisplayStringLine(Line1,str1);
  LCD_DisplayStringLine(Line7,"       Standby");  
  /* USER CODE END 2 */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
      loc_change();
      time_setting();
      time_go_stop();
      //PWM_gen();
      HAL_Delay(5);
  }
  /* USER CODE END 3 */
}

接下来文末按各个要求来编写各个功能的函数

1.存储位置改变功能的实现

这个功能是比较基础的,只是短按,很常规,但是要注意每次更换存储位置都要重新读取一次EEPROM的时间。代码如下:

void loc_change(void)
{
    if(HAL_GPIO_ReadPin(KEY_B1_GPIO_Port,KEY_B1_Pin) == 0)
    {
        HAL_Delay(100);
        if(HAL_GPIO_ReadPin(KEY_B1_GPIO_Port,KEY_B1_Pin) == 0)//判断按键是否按下
        {
            re_loc++;
            if(re_loc > 5)//大于5变成1,循环
            {
                re_loc = 1;
            }
            read_time(); //变换了就要重新读取时间
            sprintf((char *)str1,"   No %d",re_loc); //下面都是LCD显示的函数
            LCD_DisplayStringLine(Line1,str1);
            sprintf((char *)str,"      %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);
            LCD_DisplayStringLine(Line4,str);
        }
    }
}

2.时间设置功能函数的编写

这一块是最复杂的,涉及了按键长短按按键嵌套等,逻辑关系比较复杂,这里我讲解一下我的思路:首先,按下B2后程序进入一个死循环,在里面扫描判断是否有按键按下,根据不同的按键做出不同的反应,同时计算按键按下的时间,若大于0.8秒九认为是长按并做出相应动作。大体思路是这样,接下来上程序,更具我的思路和程序的注释应该可以理解我编程的思路。

void time_setting(void)
{
    int i = 0;
    if(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0)
    {
        HAL_Delay(150);
        if(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0)//判断B2是否按下
        {
            LCD_DisplayStringLine(Line5,str4);
            LCD_DisplayStringLine(Line7,"       Setting");//显示Setting
            while(1) //进入死循环
            {
                if(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0)
                {
                    HAL_Delay(100);
                    if(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0)//判断B2是否按下
                    {
                        i++;   //这个是设置时分秒的标志位,0---秒,1---分,2---时
                        if(i > 2)
                        {
                            i = 0;
                        } 
                        if( i == 0)
                        {
                            LCD_DisplayStringLine(Line5,str4);
                        }
                        if( i == 1)
                        {
                            LCD_DisplayStringLine(Line5,str3);
                        }
                        if( i == 2)
                        {
                            LCD_DisplayStringLine(Line5,str2);
                        }
                        while(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0)//如果一直按着
                        {
                            HAL_TIM_Base_Start_IT(&htim2);//打开定时器
                            if(sec_08 == 1)//判断0.8秒的标志位是否为1,为1就说明0.8秒到了
                            {
                                long_pushB2 = 1;//长按标志位置1
                                break;
                            }
                        }
                        HAL_TIM_Base_Stop_IT(&htim2);
                        time_cnt = 0;
                        sec_08 = 0;

                    }
                }
                if(HAL_GPIO_ReadPin(KEY_B3_GPIO_Port,KEY_B3_Pin) == 0)
                {
                    HAL_Delay(100);
                    if(HAL_GPIO_ReadPin(KEY_B3_GPIO_Port,KEY_B3_Pin) == 0)//判断B3是否按下
                    {
                        if(i == 0 )//根据i的值来决定增长的数
                        {
                            TT_1.sec++;
                        }
                        if(i == 1 )
                        {
                            TT_1.min++;
                        }
                        if(i == 2 )
                        {
                            TT_1.hour++;
                        }
                        sprintf((char *)str,"      %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);
                        LCD_DisplayStringLine(Line4,str);
                        while(HAL_GPIO_ReadPin(KEY_B3_GPIO_Port,KEY_B3_Pin) == 0)//如果一直按着
                        {
                            HAL_TIM_Base_Start_IT(&htim2);//打开定时器
                            if(sec_08 == 1)//判断0.8秒的标志位是否为1,为1就说明0.8秒到了
                            {
                                long_pushB3 = 1;//长按标志位置1
                                break;
                            }
                        }
                        HAL_TIM_Base_Stop_IT(&htim2);
                        time_cnt = 0;
                        sec_08 = 0;
                        while(long_pushB3 == 1 && HAL_GPIO_ReadPin(KEY_B3_GPIO_Port,KEY_B3_Pin) == 0)//长按标志位为1并且一直按着
                        {
                         if(i == 0 )//根据i的值来决定快速增长的数
                        {
                            TT_1.sec++;
                            HAL_Delay(95);
                            if(TT_1.sec == 60)
                            {
                                TT_1.sec = 0;
                                TT_1.min++;
                            }
                        }
                        if(i == 1 )
                        {
                            TT_1.min++;
                            HAL_Delay(95);
                            if(TT_1.min == 60)
                            {
                                TT_1.min = 0;
                                TT_1.hour++;
                            }
                        }
                        if(i == 2 )
                        {
                            TT_1.hour++;
                            HAL_Delay(95);
                            if(TT_1.hour == 24)
                            {
                                TT_1.hour = 0;
                            }                                
                        }
                        sprintf((char *)str,"      %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);//更新时间
                        LCD_DisplayStringLine(Line4,str);
                    }
                    long_pushB3 = 0;
                    }  
                }
                if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0 || long_pushB2 == 1)
                {
                    HAL_Delay(60);
                    if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0 || long_pushB2 == 1)//判断B4是否按下或者B2是否长按
                    {
                        if(long_pushB2 == 0)//B2没有长按
                        {
                            LCD_DisplayStringLine(Line5,"                  ");
                            LCD_DisplayStringLine(Line7,"       Running");
                            go_stop = 1;//启动定时器
                            break;
                        }
                        if(long_pushB2 == 1)//B2长按了
                        {
                            LCD_DisplayStringLine(Line5,"                  ");
                            LCD_DisplayStringLine(Line7,"       Standby");
                            long_pushB2 = 0;
                            res_time();//将时间存到EEPROM里面
                            HAL_Delay(200);
                            break;
                        }
                    }
                }
            
           }
      }
  }
}

3.定时器启动/结束函数

这一块的逻辑关系比较简单,就是 按下按键启动定时器,再按一下停止定时器,长按就回到初始页面,也有按键嵌套按键长短按。但是这一块我遇到了一个问题,因为倒计时我用的是定时器中断,但是我的时间使用结构体定义的,用extern外部调用时发现不好使,查了资料发现结构体用extern的话很麻烦(我太懒了),所以我用的中断回调函数:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

用keil敲这个挺费劲,平时用vscode和clion的时候没这个感觉。定时器中断这块我就不多说了,直接上代码:

void time_go_stop(void)
{
    if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0)
        {
                    HAL_Delay(150);
                    if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0)//判断按键B4是否按下
                    {
                        while(1)//进入死循环
                        {
                            if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0)
                            {
                                HAL_Delay(50);
                                if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0)//判断按键4是否按下
                                {
                                    go_stop = 1-go_stop;
                                    while(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0)//如果一直按着
                                    {
                                        HAL_TIM_Base_Start_IT(&htim2);
                                        if(sec_08 == 1)//判断0.8秒的标志位是否为1,为1就说明0.8秒到了
                                        {
                                            long_pushB4 = 1;//判断0.8秒的标志位是否为1,为1就说明0.8秒到了
                                            break;
                                        }
                                    }
                                    HAL_TIM_Base_Stop_IT(&htim2);
                                    time_cnt = 0;
                                    sec_08 = 0;
                                }
                            }
                            if(long_pushB4 == 1)//长按就退出,回到初始页面
                            {
                                long_pushB4 = 0;
                                LCD_DisplayStringLine(Line7,"       Standby");
                                sprintf((char *)str,"      %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);
                                LCD_DisplayStringLine(Line4,str);
                                HAL_Delay(150);
                                break;
                            }
                            if(go_stop == 1)//定时器启动
                            {
                                LCD_DisplayStringLine(Line7,"       Running");
                                HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//启动PWM
                                led_flash();//开启LED闪烁
                                HAL_TIM_Base_Start_IT(&htim4);//启动定时器TIM4
                                if(TT_1.hour == 0 && TT_1.min==0 && TT_1.sec == 0)
                                {
                                    HAL_TIM_Base_Start_IT(&htim4);
                                    go_stop = 0;
                                }
                                sprintf((char *)str,"      %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);
                                LCD_DisplayStringLine(Line4,str);
                            }
                            if(go_stop == 0)//定时器结束
                            {
                                LCD_DisplayStringLine(Line7,"       Pause  ");
                                HAL_TIM_Base_Stop_IT(&htim4);
                                time_cnt4 = 0;
                                HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1);
                                HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);
                                HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
                                HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
                            }
                        }
                    }
        }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//中断回调函数
{
    
    if(htim -> Instance == htim4.Instance)//如果时TIM4中断
    {
        time_cnt4++;
        if(time_cnt4 == 100)//1秒计时到
        {
            TT_1.sec--;
            if(TT_1.sec == 255)//uint8类型 0-1==255,补码运算
            {
                TT_1.sec = 59;
                TT_1.min--;
            }
            if(TT_1.min == 255)
            {
                TT_1.min = 59;
                TT_1.hour--;
            }
            time_cnt4 = 0;
        }
    }
}

至此项目主要的代码就写好啦,实测也没有问题,这题就圆满地结束了。

总结

  1. 使用EEPROM时,需要在CUBEMX配置好对应的I2C管脚,因为官方例程里没有IO口的初始化。
  2. 连续写入/读取EEPROM数据时,需要在两次操作间进行一个延时,10ms足矣,应为I2C通信会有应答,不延时的话就没法连续写入/读取。
  3. 对结构体的外部调用不太熟悉,需要多多查资料学习。

第九届省赛工程文件下载:下载链接

举报

相关推荐

0 条评论