第八届省赛真题模拟升降控制器
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);
}