蓝桥杯嵌入式STM32G431——第八届省赛真题模拟升降控制器

这是一个基于STM32的电梯控制系统,通过状态机实现楼层选择、电梯运行方向控制以及门开关的PWM控制。系统使用RTC、TIM等外设,按键输入选择目标楼层,LCD显示当前楼层及时间,并通过流水灯指示电梯运动状态。状态机包括等待按键、设置楼层、关门、上行/下行、到达楼层、开门、等待再次设置楼层等多个状态。
摘要由CSDN通过智能技术生成

1、第八届省赛真题

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

2、状态机框图(供参考)

在这里插入图片描述

3、主函数代码(不包含各模块的初始化代码)

#include "main.h"
#include "rcc.h"
#include "led_key.h"
#include "lcd.h"
#include "rtc.h"
#include "tim.h"

//***执行速度控制变量***//
__IO uint32_t uwTick_LED_Speed_Ctrl;
__IO uint32_t uwTick_KEY_Speed_Ctrl;
__IO uint32_t uwTick_LCD_Speed_Ctrl;
__IO uint32_t uwTick_Time_Count;

//***全局变量区***//
uint8_t ucLED;
uint8_t key_value,key_up,key_down;
static uint8_t key_old;
uint8_t LCD_String_Disp[21];

uint8_t Current_floor = 1; //显示当前升降器所处楼层
uint8_t Set_floor;	//设置目标楼层 一层-0x01 二层-0x02 三层-0x04 四层-0x08
uint8_t Status_Ctrl; //状态机控制 0-8个状态
_Bool key_press_flag; //按键按下标志为 1-有按键按下 0-无按键按下
uint8_t lift_direction = 1; //升降器运动方向 1-上行 0-下行
uint8_t LED_flow_up = 4; //控制上行流水灯从右到左 流水 变量
uint8_t LED_flow_down = 7; //控制下行流水灯从左到右 流水 变量

//RTC结构体定义变量
RTC_TimeTypeDef T;
RTC_DateTypeDef D;


//***子函数声明区***//
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void LED_Proc(void);
void KEY_Proc(void);
void LCD_Proc(void);
void Lift_Ctrl(void);


//***主函数区***//
int main(void)
{
  
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();

	//*模块函数初始化区*/
	LED_KEY_Init();
	
	LCD_Init();
	LCD_Clear(Black);	
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);

	RTC_Init();
	//*** TIM2_CH1 -> PA5 高电平 电梯开门 PA5 低电平 电梯关门
	//*** TIM3_CH2 -> PA4 高电平 电梯上行 PA4 低电平 电梯下行
	//*** TIM3_CH1 -> PA6 电梯上下行 PWM输出频率1khz 上行 duty:80% 下行 duty:60%
	//*** TIM17_CH1 -> PA7 电梯开关门 PWM输出频率2khz 开门 duty:60% 关门 duty:50%
	TIM2_Init();
	TIM3_Init();
	TIM17_Init();
	
  while (1)
  {
    LED_Proc();
		KEY_Proc();
		LCD_Proc();
		Lift_Ctrl();
  }
  
}

//***LED处理子函数
void LED_Proc(void)
{
	if((uwTick - uwTick_LED_Speed_Ctrl)<200) return;
		uwTick_LED_Speed_Ctrl = uwTick; //200ms执行一次
	
	LED_Disp(ucLED);
}

//***按键处理子函数
void KEY_Proc(void)
{
	if((uwTick - uwTick_KEY_Speed_Ctrl)<50) return;
		uwTick_KEY_Speed_Ctrl = uwTick; //50ms执行一次
	
	//***按键三行代码***//
	key_value = KEY_Scan();
	key_down = key_value & (key_value ^ key_old);
	key_up= ~key_value & (key_value ^ key_old);
	key_old = key_value;
	
	
	if(Status_Ctrl==0||Status_Ctrl==1) //当状态机处在0和1状态时 按键有效
	{
		switch(key_down)
		{
			case 1: //按键B1按下
			{
				if(Current_floor != 1) //当前楼层下按键无效
				{
					Set_floor |= 0x01; //选择需要到达的楼层一
					ucLED |= 0x01; //对应楼层1-LED1点亮
					key_press_flag = 1; //按键按下标志位
					uwTick_Time_Count = uwTick; //按键按下后开始计时
				}
			}
			break;
			
			case 2: //按键B2按下
				if(Current_floor != 2) //当前楼层下按键无效
				{
					Set_floor |= 0x02; //选择需要到达的楼层二
					ucLED |= 0x02; //对应楼层2-LED2点亮
					key_press_flag = 1; //按键按下标志位
					uwTick_Time_Count = uwTick; //按键按下后开始计时
				}
			break;
			
			case 3: //按键B3按下
				if(Current_floor != 3) //当前楼层下按键无效
				{
					Set_floor |= 0x04;
					ucLED |= 0x04;
					key_press_flag = 1;
					uwTick_Time_Count = uwTick;
				}
			break;
			
			case 4:
				if(Current_floor != 4) //当前楼层下按键无效
				{
					Set_floor |= 0x08;
					ucLED |= 0x08;
					key_press_flag = 1;
					uwTick_Time_Count = uwTick;
				}
			break;
			
		}
		
	}
	
	if(key_press_flag)
	{
		Status_Ctrl = 1; //当有按键按下时切换到状态1
	}

}

void Lift_Ctrl(void) //升降机状态控制函数
{
	if(Status_Ctrl) //只在非0状态下有效
	{
		switch(Status_Ctrl) //选择状态值
		{
			case 1: //状态1
				if((uwTick - uwTick_Time_Count)>=1000) //当最后一个按键按下1s后则进入状态2
				{
					Status_Ctrl = 2;
					key_press_flag = 0; //清除按键标志位
				}
				else //若非最后一个按键按下且时间不到1s则返回状态0
					Status_Ctrl = 0;
			break;
			
			case 2: //状态2
				__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 0); //关门 PA5 低电平 
				HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //TIM2_CH1 -> PA5 低电平 升降器关门
			
//				HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);
				__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 250); //关门 PWM PA7 50% 2khz
				HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1); //TIM17_CH1 -> PA7 PWM输出频率2khz 关门 duty:50% PA7初始化频率设为2khz
			
				uwTick_Time_Count = uwTick; //开始计时 
				Status_Ctrl = 3; //进入状态3
			break;
			
			case 3: //状态3
				if((uwTick - uwTick_Time_Count)>=4000) //判断关门时间是否到达4s
				{
					HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1); //关闭 PA5
					HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1); //关闭 PA7 PWM输出
					Status_Ctrl = 4; //进入状态4
				}
			break;
				
			case 4: //状态4
				if(Set_floor > (1 << (Current_floor - 1))) //判断设置平台与当前平台的关系 上行 
				{
					lift_direction = 1; //上行标志
					
					__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 1000); //上行 PA4 高电平 
					HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); //TIM3_CH2 -> PA4 高电平 升降器上行 
					
					__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 800);
					HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);	    //TIM3_CH1 -> PA6 PWM输出频率1khz 上行 duty:80% 
				}
				else if(Set_floor < (1 << (Current_floor - 1))) //下行
				{
					lift_direction = 0;  //下行标志
					//此处用新版PA4与PA6定时器引脚冲突 可将PA4与PA5功能对换 交换位置便可解决此冲突用于练习验证 下行状态同理
					__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 0); //下行 PA4 低电平 
					HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); //TIM3_CH2 -> PA4 低电平 升降器下行 
					
					__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 600); 
					HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); //TIM3_CH1 -> PA6 PWM输出频率1khz 下行 duty:60% 
				}
				uwTick_Time_Count = uwTick; //开始计时
				Status_Ctrl = 5; //切换到状态5
			break;
				
			case 5: //状态5
				if((uwTick - uwTick_Time_Count)>=6000) //判断上下行时间6s是否到来
				{
					if(lift_direction) //上行
					{
						Current_floor += 1;	//6s到达后当前楼层加1			
					}
					else //下行
					{
						Current_floor -= 1;	//6s到达后当前楼层减1						
					}
			
					HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_2); //关闭 PA4
					HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1); //关闭PA6 PWM 信号
					ucLED &= 0x0f; //关闭流水灯
					LED_flow_up = 4; //恢复流水灯上下行标志变量
					LED_flow_down = 7;
					
					Status_Ctrl = 6; //进入状态6
				}
				else //6s未到
				{
					if(lift_direction) //上行 流水灯从右往左
					{
						ucLED &= 0x0f;
						ucLED |= (1<<LED_flow_up);
						LED_flow_up ++ ;
						if(LED_flow_up == 8)
							LED_flow_up = 4;
					}
					else //下行 流水灯从左往右
					{
						ucLED &= 0x0f;
						ucLED |= (1<<LED_flow_down);
						LED_flow_down -- ;
						if(LED_flow_down == 3)
							LED_flow_down = 7;
					}
					HAL_Delay(300); //控制流水灯速度

				}
			break;
				
			case 6: //状态6
				if((Set_floor & (1<<(Current_floor - 1))) == (1<<(Current_floor - 1))) //判断是否到达设置的楼层
				{
					__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 1000); //开门 PA5 高电平 
					HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //TIM2_CH1 -> PA5 高电平 升降器开门
			
//				HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);
					__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 300);  
					HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);	//TIM17_CH1 -> PA7 PWM输出频率2khz 开门 duty:60%				 
					
					Set_floor &= ~(1<<(Current_floor - 1)); //当前设置楼层到达 清除该楼层
					ucLED &= ~(1<<(Current_floor - 1)); //对应到达楼层LED熄灭
					uwTick_Time_Count = uwTick; //开始计时 用于判断关门时间4s
					Status_Ctrl = 7; //进入状态7
				}
				else //没有到达设置楼层
				{
					Status_Ctrl = 4; //返回状态4
				}
			break;

			case 7: //状态7
				if((uwTick - uwTick_Time_Count)>=4000) //等待关门时间4s
				{
					HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1); //关闭 PA5
					HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);  //关闭PA7 PWM 信号
					uwTick_Time_Count = uwTick; //开始计时 用于判断如果还有其他楼层时计时2s
					Status_Ctrl = 8; //进入状态8
				}
				break;
				
			case 8: //状态8
				if(Set_floor) //还有其他需要到达的楼层
				{
					if((uwTick - uwTick_Time_Count)>=2000) //等待2s后关门
						Status_Ctrl = 2; //返回状态2
				}
				else //无其他楼层
				{
					Status_Ctrl = 0; //回到状态0 等待按键设置需要到达的楼层信息
					
				}
			break;
		}
	
	}
	
}

//***LCD处理子函数
void LCD_Proc(void)
{
	if((uwTick - uwTick_LCD_Speed_Ctrl)<300) return;
		uwTick_LCD_Speed_Ctrl = uwTick;	
	
	memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
	sprintf((char*)LCD_String_Disp , "    Current Floor");
	LCD_DisplayStringLine(Line2 , LCD_String_Disp);
	
	memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
	sprintf((char*)LCD_String_Disp , "          %01d",Current_floor);
	LCD_DisplayStringLine(Line4 , LCD_String_Disp);
	
	HAL_RTC_GetTime(&hrtc , &T , RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc , &D , RTC_FORMAT_BIN);	
	memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
	sprintf((char*)LCD_String_Disp , "      %02d-%02d-%02d",T.Hours,T.Minutes,T.Seconds);
	LCD_DisplayStringLine(Line6 , LCD_String_Disp);	

	//用于观察状态机所处状态 方便判断升降器执行状态
	memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
	sprintf((char*)LCD_String_Disp , "  Status Test:%01d",Status_Ctrl);
	LCD_DisplayStringLine(Line9 , LCD_String_Disp);	
	
}




评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lzya.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值