一.题目分析
(1).题目
(2).题目分析
1.按键功能分析----过程控制
a. 选择按键按下的个数和目标层数(每个按键都要在一秒之内按下,否则就结束)
b. 当升降机到达目标平台,LED灯熄灭
c. 按下当前平台对于按键无效
d.一次可以设定多个目标平台,上下平台都有按键的时候,运行顺序是先下后上
2.PWM输出
a.两路PWM输出控制上下行和开关门电机
b.频率转换成自动重装载值
c.比较值
- 逻辑导图
二.CubeMX配置
由于蓝桥杯使用的板子都是STM32G431RBT6,配置都是相同的,模板已经在https://blog.csdn.net/weixin_51089092/article/details/142604825?sharetype=blogdetail&sharerId=142604825&sharerefer=PC&sharesource=weixin_51089092&spm=1011.2480.3001.8118配置完成,大家可以前往学习
三.相关代码实现
(1)MAIN
- 全局变量声明
#include "main.h"
#include "RCC\bsp_rcc.h"
#include "KEY_LED\bsp_key_led.h"
#include "LCD\bsp_lcd.h"
//#include "UART\bsp_uart.h"
//#include "I2C\bsp_i2c.h"
//#include "ADC\bsp_adc.h"
#include "TIM\bsp_tim.h"
#include "RTC\bsp_rtc.h"
//***全局变量声明区
//*减速变量
__IO uint32_t uwTick_Key_Set_Point = 0;//控制Key_Proc的执行速度
__IO uint32_t uwTick_Led_Set_Point = 0;//控制Led_Proc的执行速度
__IO uint32_t uwTick_Lcd_Set_Point = 0;//控制Lcd_Proc的执行速度
//*按键扫描专用变量
uint8_t ucKey_Val, unKey_Down, ucKey_Up, ucKey_Old;
//*LED专用变量
uint8_t ucLed;
//*LCD显示专用变量
uint8_t Lcd_Disp_String[21];//最多显示20个字符
//*rtc相关变量
RTC_TimeTypeDef H_M_S_Time;
RTC_DateTypeDef Y_M_D_Date;
//全局变量
uint8_t ucPlat = 1;//1234表示当前所处的平台
//_Bool PA4_Voltage = 0;
//_Bool PA5_Voltage = 0;
uint8_t ucSet;//用于记录几号按键按下去了。只用低4位,分别对应LD4~LD1,按键B4~B1, _ _ _ _ [ _ _ _ _ ]
uint8_t ucState;//状态机,0~8.
uint8_t Dir;//电梯运行方向变量 0 -没运行,1-上,2-下
uint8_t Flow = 0x10;//流水的变量
__IO uint32_t uwTick_Set_Point = 0;//计时专用
//***子函数声明区
void Key_Proc(void);
void Led_Proc(void);
void Lcd_Proc(void);
//void Usart_Proc(void);
void Elev_Proc(void);
2.系统主函数
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/*bsp资源的初始化*/
KEY_LED_Init();
LCD_Init();
LCD_Clear(White);
LCD_SetBackColor(White);
LCD_SetTextColor(Blue);
PWM_OUTPUT_TIM3_Init(); // PA6 ------> TIM3_CH1
PWM_OUTPUT_TIM17_Init(); //PA7 ------> TIM17_CH1
RTC_Init();
while (1)
{
Key_Proc();
Led_Proc();
Lcd_Proc();
Elev_Proc();
}
}
3.子函数
按键扫描子函数
a.逻辑梳理
将平台和按键对应起来,根据对应的平台点灯
b.程序源码
void Key_Proc(void)
{
if((uwTick - uwTick_Key_Set_Point)<50) return;//减速函数
uwTick_Key_Set_Point = uwTick;
ucKey_Val = Key_Scan();
unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val);
ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);
ucKey_Old = ucKey_Val;
if(ucState == 0)//状态机,0~8.
{
if(unKey_Down == 1)//B1按压
{
if(ucPlat != 1) ucSet |= 0x01; //ucPlat的1234表示当前所处的平台
} //ucSet用于记录几号按键按下去了。只用低4位,分别对应LD4~LD1,按键B4~B1
else if(unKey_Down == 2)//B2按压
{
if(ucPlat != 2) ucSet |= 0x02;
}
else if(unKey_Down == 3)//B3按压
{
if(ucPlat != 3) ucSet |= 0x04;
}
else if(unKey_Down == 4)//B4按压
{
if(ucPlat != 4) ucSet |= 0x08;
}
ucLed &= 0xF0;
ucLed |= ucSet;
if(unKey_Down != 0)//当有按键按下去,启动计时
{
uwTick_Set_Point = uwTick;
}
}
}
LED扫描子函数
a.程序源码
void Led_Proc(void)
{
if((uwTick - uwTick_Led_Set_Point)<200) return;//减速函数
uwTick_Led_Set_Point = uwTick;
LED_Disp(ucLed);
}
LCD扫描子函数
a.逻辑梳理
显示到达平台和时间
b.程序源码
void Lcd_Proc(void)
{
if((uwTick - uwTick_Lcd_Set_Point)<100) return;//减速函数
uwTick_Lcd_Set_Point = uwTick;
//开机屏幕测试代码
sprintf((char *)Lcd_Disp_String, " Current Platform",ucLed);
LCD_DisplayStringLine(Line1, Lcd_Disp_String);
sprintf((char *)Lcd_Disp_String, " %1d",(unsigned int)ucPlat);
LCD_DisplayStringLine(Line3, Lcd_Disp_String);
//*RTC内容显示
HAL_RTC_GetTime(&hrtc, &H_M_S_Time, RTC_FORMAT_BIN);//读取日期和时间必须同时使用
HAL_RTC_GetDate(&hrtc, &Y_M_D_Date, RTC_FORMAT_BIN);
sprintf((char *)Lcd_Disp_String, " %02d-%02d-%02d",(unsigned int)H_M_S_Time.Hours,(unsigned int)H_M_S_Time.Minutes,(unsigned int)H_M_S_Time.Seconds);
LCD_DisplayStringLine(Line6, Lcd_Disp_String);
}
状态机子函数
a.逻辑框图
b.程序源码
void Elev_Proc(void)
{
if(ucSet)//如果用户没有设置目标层,不执行此程序。只有按键设置了,才可以。
{
switch(ucState)
{
case 0://等待按键按下后1s时间的到来
if((uwTick - uwTick_Set_Point) >= 1000)
ucState = 1;
else
break;
case 1://控制电梯门关闭 PA7 - PWM 2KHZ D=50% PA5= 0
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); //关门
__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 250);//修改占空比的基本操作 D=0.5
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1); //PA7 2khz 黄色波形
sprintf((char *)Lcd_Disp_String, " Door Closing ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
uwTick_Set_Point = uwTick;
ucState = 2;
case 2://等待电梯关门时间到达4s,到了之后,执行后边的操作,否则退出。
if((uwTick - uwTick_Set_Point) >= 4000)
{
HAL_TIM_PWM_Stop(&htim17,TIM_CHANNEL_1); //PA7 2khz 黄色波形
sprintf((char *)Lcd_Disp_String, " Door Closed ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
ucState = 3;
}
else
break;
//uint8_t ucPlat = 1;//1234表示当前所处的平台
case 3://判断当前层数变量和设定变量之间的关系,决定应该上行还是下行,并启动运行。
//ucPlat=1~4=1(0001) 2(0010) 3(0100) 4(1000) ucSet= 0010B
if(ucSet > (1<<(ucPlat-1)))//上行 PA6 - PWM 1KHZ D=80% PA4 = 1
{
Dir = 1;//上行
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 电梯上行
// PA4_Voltage = 1;//为了屏幕测试上行方便
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 800);//修改占空比的基本操作 D=0.8
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); //PA6 1khz 绿色波形
sprintf((char *)Lcd_Disp_String, "Elev Upping ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
}
else if(ucSet < (1<<(ucPlat-1)))//下行 PA6 - PWM 1KHZ D=60% PA4 = 0
{
Dir = 2;//下行
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 电梯下行
// PA4_Voltage = 0;//为了屏幕测试下行方便
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 600);//修改占空比的基本操作 D=0.6
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); //PA6 1khz 绿色波形
sprintf((char *)Lcd_Disp_String, "Elev Downing ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
}
uwTick_Set_Point = uwTick;
ucState = 4;
case 4://判断电梯走6s时间是否到来,如果到来,当前所在层数变化1个单位。如果没有到6s,流水灯效果。
if((uwTick - uwTick_Set_Point) >= 6000)
{
if(Dir == 1) ucPlat++;
else if(Dir == 2) ucPlat--;
sprintf((char *)Lcd_Disp_String, " %1d",(unsigned int)ucPlat);
LCD_DisplayStringLine(Line3, Lcd_Disp_String);
sprintf((char *)Lcd_Disp_String, "Elev Runned 1 Floor ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
ucLed &= 0x0F;
Flow = 0x10;
ucState = 5;
}
else
{
if(Dir == 1)//上行,左到右
{
ucLed &= 0x0F;
ucLed |= Flow;
Flow = (Flow >> 1);//0001 0000 - 0000 1000
if(Flow == 0x08)
Flow = 0x80;
}
else if(Dir == 2)//下行,右到左
{
ucLed &= 0x0F;
ucLed |= Flow;
Flow = (Flow << 1);//1000 0000 - 0000 0000
if(Flow == 0x00)
Flow = 0x10;
}
HAL_Delay(300);
break;
}
case 5://ucPlat当前层 3 ucSet 0000 1100
if((1<<(ucPlat-1))&ucSet)//当不为零的时候,表示到了目标层
{
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1); //PA6 1khz 绿色波形,停止波形的发生
HAL_Delay(300);
sprintf((char *)Lcd_Disp_String, " ");
LCD_DisplayStringLine(Line3, Lcd_Disp_String);
HAL_Delay(300);
sprintf((char *)Lcd_Disp_String, " %1d",(unsigned int)ucPlat);
LCD_DisplayStringLine(Line3, Lcd_Disp_String);
HAL_Delay(300);
sprintf((char *)Lcd_Disp_String, " ");
LCD_DisplayStringLine(Line3, Lcd_Disp_String);
HAL_Delay(300);
sprintf((char *)Lcd_Disp_String, " %1d",(unsigned int)ucPlat);
LCD_DisplayStringLine(Line3, Lcd_Disp_String);
//电梯开门, PA7 - PWM 2KHZ D=60% PA5= 1 。
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); //开门
// PA5_Voltage = 1;//为了屏幕测试关门方便
__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 300);//修改占空比的基本操作 D=0.6
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1); //PA7 2khz 黄色波形
sprintf((char *)Lcd_Disp_String, "Comed , Door Opening ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
uwTick_Set_Point = uwTick;
ucState = 6;
}
else //没有到达目标层
{
uwTick_Set_Point = uwTick;
ucState = 4;
break;
}
case 6://等待电梯开门4s时间是否到达
if((uwTick - uwTick_Set_Point) >= 4000)
{
HAL_TIM_PWM_Stop(&htim17,TIM_CHANNEL_1); //PA7 2khz 黄色波形
sprintf((char *)Lcd_Disp_String, "Door Opened ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
ucSet &= (~(1<<(ucPlat-1)));//用户设置的目标层达成目标 1000
ucLed &= 0xF0;
ucLed |= ucSet;
LED_Disp(ucLed);
ucState = 7;
}
else
break;
case 7://判别是否还有别的目标层
if(ucSet)//如果还有别的目标平台,等待2秒
{
uwTick_Set_Point = uwTick;
sprintf((char *)Lcd_Disp_String, "Waitting 2s ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
ucState = 8;
}
else //没有别的目标了
{
ucState = 0;
sprintf((char *)Lcd_Disp_String, " ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
break;
}
case 8://判断到达目标层之后,电梯开门后,是否等待了2s的时间已经到达。
if((uwTick - uwTick_Set_Point) >= 2000)
{
sprintf((char *)Lcd_Disp_String, " ");
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
ucState = 1;
}
}
}
}