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

博主分享了参与第八届蓝桥杯嵌入式设计省赛的编程经验,详细解析了从题目理解、程序流程、外设配置到关键功能实现的全过程。主要涉及电梯控制算法、RTC功能以及按键逻辑,强调了逻辑关系梳理和调试的重要性。文中还提供了完整的工程文件下载链接供读者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

往期文章目录

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


前言

寒假的时候写了第七届和第六届的蓝桥杯省赛的程序设计题,本来也想接着做下去的,但是过年了玩上头了,第八届的题就搁置了一个寒假,直到开学(2月28号)才动工。第八届的题目有难度,比前两届的难度大很多,我做了四个晚上,讲解9的多小时才完成,不过这次练习也让我收获颇丰。
本次实验仍然采用CT117E平台完成,用CUBEMX和Keil5编写和调试的程序。

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

第八届蓝桥杯程序设计题的题目如下:
在这里插入图片描述
在这里插入图片描述题目不长,但是当我读完题目以后我就觉得这个题目好难,思路都没有理得很清楚就直接上手做了。

二、程序流程图

根据题目所要求的功能,我同样绘制了一个程序流程图作为编程参考,这个是在我写完所有程序并且调试完成有画的。

在这里插入图片描述

三、配置所需外设

我的CUBEMX的配置图如下,在设计中前前后后改了很多次,思路也一直有在改变。根据题目要求,设置了定时器模块用来计时与产生PWM波,配置了LED等输出引脚和楼层按键引脚(根据原理图配置成上拉输入模式)
在这里插入图片描述

四、程序编写

这次的程序会比较大段,给出的程序只是部分程序我的项目工程下载链接会在文末给出,有需要的可以自行下载

先给出主函数和一些函数,变量的定义。

uint8_t level = 1; 
uint8_t tar_level[4];    //目标楼层,长度为4的数组
uint8_t now_level = 1;   //当前楼层
uint8_t up_down[3];      //判断出目标楼层的上下行方向并且放在这个数组里面
uint8_t go_up[3];        //存储上行的数组
uint8_t go_down[3];      //存储下行的数组
uint8_t str[20];         //LCD的显示字符串
uint8_t str1[20];        //LCD的显示字符串
uint8_t cnt = 0;
uint8_t up_cnt = 0;
uint8_t down_cnt = 0;
int flag_up = 0;     //上行标志位
int flag_down = 0;   //下行标志位
int push_flag = 0;   //按键操作完成标志位
int arrive_tar = 0;  //到达目标楼层标志位
extern int time_cnt;  //定时器的计数值
extern int sec_6;     //6秒计时标志位
extern int sec_4;     //4秒计时标志位
extern int sec_2;     //2秒计时标志位
extern int sec_1;     //1秒计时标志位
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void led_start(void);//初始化LED灯和引脚
void floor_led(int floor);//到达指定楼层亮灯函数
void floor_push(void);//按键判断函数
void led_up(void);//上行流水等函数
void led_down(void);//下行流水灯函数
void up_down_jug(void);//上行下行判断函数
void floor_add_dec(void);//楼层增减函数
void floor_rank(void);//将上行楼层和下行楼层分开来
void go_stop(void);//判断电梯是否到达目标楼层函数
void num_flashing(void);//楼层数字LCD闪烁函数
void door_open(void);//开门控制函数
void door_close(void);//关门控制函数
void EL_up(void);//升降机上行控制函数
void EL_down(void);//升降机下行控制函数
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
int main(void)
{
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_RTC_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();
  MX_TIM15_Init();
  /* USER CODE BEGIN 2 */
  HAL_RTC_Init(&hrtc);
  LCD_Init();
  LCD_Clear(White);
  LCD_SetTextColor(Black);
  LCD_DisplayStringLine(Line2,"        Level");
  LCD_DisplayStringLine(Line4,"          1");
  led_start();
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HAL_RTC_GetDate(&hrtc,&D,FORMAT_BIN);//获取RTC
      HAL_RTC_GetTime(&hrtc,&T,FORMAT_BIN);
      sprintf((char *)str,"       %d:%d:%d ",T.Hours,T.Minutes,T.Seconds);
      sprintf((char *)str1,"          %d",now_level);
      LCD_DisplayStringLine(Line4,str1);
      LCD_DisplayStringLine(Line7,str);
      floor_led(now_level);
      floor_push();
      floor_rank();
      up_down_jug();
      floor_add_dec();
      go_stop();
      HAL_Delay(5);
  }
  /* USER CODE END 3 */
}

1、电梯按键功能

电梯的按键总共有四个,分别对应1至4楼,按下楼梯以后电梯就会到对应的楼层。同时按照题目要求,按键按下一秒后电梯就要开始运行了,并且按下与当前楼层相同的楼层按键是无效的。根据这些要求我写了如下代码来完成这些要求。这块的程序编写比较简单,只是需要一些逻辑分析,不断调试就可以写出来。

void floor_push(void)
{
    time_cnt = 0;
    sec_1 = 0;
    if(push_flag == 0)
    {
    arrive_tar = 0;
    HAL_TIM_Base_Start_IT(&htim3);
    while(1)
    {
        HAL_RTC_GetDate(&hrtc,&D,FORMAT_BIN);
        HAL_RTC_GetTime(&hrtc,&T,FORMAT_BIN);
        sprintf((char *)str,"       %d:%d:%d ",T.Hours,T.Minutes,T.Seconds);
        LCD_DisplayStringLine(Line7,str);
        if(HAL_GPIO_ReadPin(F1_GPIO_Port,F1_Pin) == 0 && now_level !=1)//按下一楼按键并且当前不是1楼
        {
            HAL_Delay(300);
            if(HAL_GPIO_ReadPin(F1_GPIO_Port,F1_Pin) == 0)
            {
                tar_level[cnt] = 1;                                
                up_down[cnt]=2;                     //幅值为1时表示这个要上楼,幅值为2表示要下楼
                cnt++;
                sec_1 = 0;
                time_cnt = 0;
            }
        }
        if(HAL_GPIO_ReadPin(F2_GPIO_Port,F2_Pin) == 0)
        {
            HAL_Delay(300);
            if(HAL_GPIO_ReadPin(F2_GPIO_Port,F2_Pin) == 0 && now_level !=2)//按下一楼按键并且当前不是2楼
                {
                tar_level[cnt] = 2;                
                if(now_level<2)
                {
                    up_down[cnt]=1; //幅值为1时表示这个要上楼,幅值为2表示要下楼
                }
                if(now_level>2)
                {
                    up_down[cnt]=2; //幅值为1时表示这个要上楼,幅值为2表示要下楼
                }  
                cnt++;                
                sec_1 = 0;
                time_cnt = 0;
                }
        }
        if(HAL_GPIO_ReadPin(F3_GPIO_Port,F3_Pin) == 0)
        {
            HAL_Delay(300);
            if(HAL_GPIO_ReadPin(F3_GPIO_Port,F3_Pin) == 0 && now_level !=3)//按下一楼按键并且当前不是3楼
            {
                tar_level[cnt] = 3;
                if(now_level<3)
                {
                    up_down[cnt]=1; //幅值为1时表示这个要上楼,幅值为2表示要下楼
                }
                if(now_level>3)
                {
                    up_down[cnt]=2; //幅值为1时表示这个要上楼,幅值为2表示要下楼
                }    
                cnt++;                
                sec_1 = 0;
                time_cnt = 0;
            }
        }
        if(HAL_GPIO_ReadPin(F4_GPIO_Port,F4_Pin) == 0)
        {
            HAL_Delay(300);
            if(HAL_GPIO_ReadPin(F4_GPIO_Port,F4_Pin) == 0 && now_level !=4)//按下一楼按键并且当前不是4楼
            {
                tar_level[cnt] = 4;
                up_down[cnt]=1; //幅值为1时表示这个要上楼,幅值为2表示要下楼
                cnt++;                
                sec_1 = 0;
                time_cnt = 0;
            }
        }
        if(sec_1 == 1 && strlen((char *)tar_level) != 0) //按下按键后一秒内没有按下其他按键就说明按键操作已完成
        {
            HAL_TIM_Base_Stop_IT(&htim3);
            sec_1 = 0;
            time_cnt = 0;
            push_flag = 1;
            cnt = 0;
            break;
        }
    }
}
}

2、电梯上下行控制

这部分是我花时间最多也是我认为最困难的一部分。这一块的代码其实就是电梯调度算法,奈何我的算法学得太差,只能靠分析逻辑关系一点点码出来。我也上网了解了一下电梯调度算法,优快云上有很多大佬写得很好,有兴趣的同学可以取了解了解。
我编写这块代码的思路时,先判断目标篇楼层有哪些是上去的哪些是下去的,然后根据先上后下原则先跑上后跑下并产生升降机的PWM和上下行的流水灯,每6秒楼层数加1或减1同时判断是否到达目标楼层,若到达就执行开门关门的命令,都做完以后就整个LCD数字闪烁。 代码如下:

void floor_add_dec(void)
{
    
    time_cnt = 0;
    sec_6 = 0;
    if(push_flag == 1)
    {
        HAL_TIM_Base_Start_IT(&htim3);
        while(1)
    {  
        HAL_RTC_GetDate(&hrtc,&D,FORMAT_BIN);
        HAL_RTC_GetTime(&hrtc,&T,FORMAT_BIN);
        sprintf((char *)str,"       %d:%d:%d ",T.Hours,T.Minutes,T.Seconds);
        LCD_DisplayStringLine(Line7,str);
      if(flag_up == 1 && flag_down == 0)//若电梯上行,控制升降机
      {
          EL_up();
          led_up();
      }
      if(flag_up == 0 && flag_down == 1)//若电梯下行,控制升降机
      {
          EL_down();
          led_down();
      }
        if(sec_6 == 1 && (strlen((char *)go_up)>0 || strlen((char *)go_down)>0))//6秒到,楼层加一并且判断是否到达目标楼层
        {
            HAL_TIM_Base_Stop_IT(&htim3);//关闭定时器
            sec_6 = 0;
            time_cnt = 0;     
            if(flag_up == 1 && flag_down == 0 && arrive_tar == 0)//若是上行就加一
            {                
                now_level++;
                if(now_level == go_up[up_cnt] )//判断是否到达目标楼层
                {
                    up_cnt++;
                    arrive_tar = 1;
                    HAL_TIM_PWM_Stop(&htim4,TIM_CHANNEL_1);
                    door_open();
                    door_close();
                }
            }
            else if(flag_up == 0 && flag_down == 1 && arrive_tar == 0)//若是下行就减一
            {
                now_level--;
                if(now_level == go_down[down_cnt] )//判断是否到达目标楼层
                {
                    down_cnt++;
                    arrive_tar = 1;
                    HAL_TIM_PWM_Stop(&htim4,TIM_CHANNEL_1);

                }
            }
            break;
        }
    }
    }
}
/*----------------------------------------*/
void floor_rank(void)
{
    int t,i;
    int q = 0,p = 0;
    t = strlen((char *)up_down);//读取楼层个数
    for(i = 0;i<t;i++)
    {
        if(up_down[i] == 0x02)//判断下行
        {
            go_down[q] = tar_level[i];
            q++;
        }
        else if(up_down[i] == 1)//判断上行
        {
            go_up[p] = tar_level[i];
            p++;
        }
    }
    p = 0;
    q = 0;
    memset(up_down,0,3);
    memset(tar_level,0,3);
}
/*----------------------------------------*/
void up_down_jug(void)
{
    if(strlen((char *)go_up) > 0)//判断并设置上行标志位
    {
        flag_up = 1;
        flag_down = 0;
    }
    else if(strlen((char *)go_up)==0 && strlen((char *)go_down) > 0)//判断并设置下行标志位,同时符合先上后下规则
    {
        flag_down = 1;
        flag_up = 0;
    }
    else
    {
        flag_down = 0;
        flag_up = 0;
    }        
}
/*----------------------------------------*/
void go_stop(void)
{
    if(arrive_tar == 1)//到达楼层
    {
        if(up_cnt < strlen((char *)go_up) )//如果电梯还有楼层没有跑完,将到达标志位清零,电梯继续运行
        {
            arrive_tar = 0;
        }
        else if(up_cnt == strlen((char *)go_up) && up_cnt != 0)//所有上行楼层都跑完了,将上行标志位清零。
        {
            memset(go_up,0,3);
            up_cnt = 0;
            flag_up = 0;            
            if(strlen((char *)go_down) == 0)
            {
                push_flag = 0;
            }
            HAL_GPIO_WritePin(EL_UP_GPIO_Port,EL_UP_Pin,GPIO_PIN_RESET);
            num_flashing();
            door_open();
            door_close();
        }
        
        if(down_cnt < strlen((char *)go_down) )//如果电梯还有楼层没有跑完,将到达标志位清零,电梯继续运行
        {
            arrive_tar = 0;
        }
        else if(down_cnt == strlen((char *)go_down) && down_cnt !=0 )//所有下行楼层都跑完了,将下行标志位清零。
        {
            memset(go_down,0,3);
            num_flashing();
            push_flag = 0;
            down_cnt = 0;
            flag_down = 0;
            HAL_GPIO_WritePin(EL_DOWN_GPIO_Port,EL_DOWN_Pin,GPIO_PIN_RESET);
            door_open();
            door_close();
        }
    }
}

其实里面的好呢多工作都可以放到定时器中断里面完成,但是我不太喜欢写代码跳来跳去的,就都拿出来在主函数里面做完了

3、RTC功能

这个是最好做的了,不多说了,只要在CUBE上配置号就可以用了,这里我就不贴代码了。
还有一些函数向开关门控制,升降控制,LED控制等函数,比较基础,由于篇幅限制我就不贴出来了,有需要的可以通过下方的链接下载参考

总结

写了这么久的一道题,我也做了一个小总结。

  1. HAL_Delay()绝对不能写在任何中断里面,(虽然这个可以通过调整中断优先级解决),但是一但出现问题困难就会不知所措。
  2. 语句 int p,q = 0;表示q幅值0,p并没有被幅值,这个地方是个小细节(基础没打好),要注意。
  3. **【重点】**复杂的题目只要好好地理清逻辑关系,就可以做出来,千万不能放弃!!!!多调试,总会发现问题所在

工程文件下载链接

下载链接:第八届蓝桥杯嵌入式省赛编程赛题工程文件

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值