文章目录
前言
相关说明:
开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板)
开发环境: CubeMX+Keil5
涉及题目:第八届蓝桥杯嵌入式省赛
题目难点:LCD显示与LED冲突(RTC秒中断),电梯运行逻辑
有关RTC秒中断详情请看STM32RTC-秒中断-基于HAL库(一文看懂如何配置并使用)
CubeMX配置、主要函数代码及说明:
一、CubeMX配置(第八届模拟题完整版)
1.使能外部高速时钟:
2.配置时钟树:
3.GPIO:
4.RTC:
5.PWM:
下图框图中的参数使用默认即可,后续在代码中配置
6.TIM4:
7.TIM6、TIM7(1s中断、2s中断):
TIM7配置同理
8.NVIC(中断配置):
二、代码相关定义、声明
1.函数声明
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//定时器中断函数
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc); //闹钟中断函数(RTC秒中断)
uint8_t Floor_CanSel(uint8_t floor_press); //判断当前楼层是否可被按下(判断是否为当前楼层或在将要前往的楼层中)
void PWM_OutPut(uint8_t statue);//PWM输出函数
void PWM_Stop(void); //PWM停止函数
void Elevator_Close(void); //电梯门关闭
void Elevator_Open(void); //电梯门打开
void Elevator_Down(void); //电梯下降
void Elevator_Rise(void); //电梯上升
void Elevator_Run(void); //电梯根据排序好的数组运行
void LCD_Init_Show(void); //LCD初始化界面
void LCD_Show(void); //LCD显示
void KEY_Scan(void); //按键扫描
void Sort_Buff(void); //选择楼层数组排序
2.宏定义
#define KEY1_GPIO_PORT GPIOB
#define KEY1_GPIO_PIN GPIO_PIN_0
#define KEY2_GPIO_PORT GPIOB
#define KEY2_GPIO_PIN GPIO_PIN_1
#define KEY3_GPIO_PORT GPIOB
#define KEY3_GPIO_PIN GPIO_PIN_2
#define KEY4_GPIO_PORT GPIOA
#define KEY4_GPIO_PIN GPIO_PIN_0
#define ON GPIO_PIN_RESET
#define OFF GPIO_PIN_SET
#define LED_GPIO_PORT GPIOC
#define LED1_GPIO_PIN GPIO_PIN_8
#define LED2_GPIO_PIN GPIO_PIN_9
#define LED3_GPIO_PIN GPIO_PIN_10
#define LED4_GPIO_PIN GPIO_PIN_11
#define LED5_GPIO_PIN GPIO_PIN_12
#define LED6_GPIO_PIN GPIO_PIN_13
#define LED7_GPIO_PIN GPIO_PIN_14
#define LED8_GPIO_PIN GPIO_PIN_15
#define ON GPIO_PIN_RESET
#define OFF GPIO_PIN_SET
#define LED1(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED1_GPIO_PIN,a)
#define LED2(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED2_GPIO_PIN,a)
#define LED3(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED3_GPIO_PIN,a)
#define LED4(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED4_GPIO_PIN,a)
#define LED5(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED5_GPIO_PIN,a)
#define LED6(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED6_GPIO_PIN,a)
#define LED7(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED7_GPIO_PIN,a)
#define LED8(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED8_GPIO_PIN,a)
#define LED5_TOGGLE() HAL_GPIO_TogglePin(LED_GPIO_PORT,LED5_GPIO_PIN)
#define LED6_TOGGLE() HAL_GPIO_TogglePin(LED_GPIO_PORT,LED6_GPIO_PIN)
#define LED7_TOGGLE() HAL_GPIO_TogglePin(LED_GPIO_PORT,LED7_GPIO_PIN)
#define LED8_TOGGLE() HAL_GPIO_TogglePin(LED_GPIO_PORT,LED8_GPIO_PIN)
3.变量定义
uint8_t sel_dex=0; //楼层选择数组下标,排完序后清零,等待下一次选择
uint8_t go_dex=0; //电梯运行数组下标,按照题目逻辑保存待运行楼层
uint8_t floor_sel[floor_canSel]; //楼层选择数组
uint8_t floor_go[floor_canSel]; //电梯运行数组
uint8_t floor_led[floor_canSel+2]; //哪个下标对应的值为1则对应哪层楼被选中,供LED点亮函数使用,保证所有已选择且未到的楼层对应的LED点亮
uint8_t floor_now=1; //当前楼层
uint8_t sort_flag=0; //数组排序标志位,最后一个按键按下一秒后在中断函数中置1,主函数判断为1则执行排序函数
uint8_t run_flag=0; //电梯运行标志位,每进入一次Time7中断置1,主函数判断为1继续让电梯工作,目的为控制等待时间
uint8_t delay; //等待秒数(开关门4s,电梯上下行6s,等待2s)
uint8_t delay_num=1; //等待次数,一次为2s,进入中断函数时已经经过一次等待,故初始值为1
uint8_t key_canPress=1; //当前按键按下有效
uint8_t led_dir=0; //LED方向标志位,1为上行,2为下行
uint8_t has_next_floor=0; //是否还有目标楼层,如果有则不排序当前选择楼层,等待上一次选择楼层走完后,再对本次选择进行排序
uint8_t door_open=1; //门是否打开标志位,1为开,0为关
uint8_t lcd_refresh=0; //LCD更新标志位
RTC_DateTypeDef GET_Date; //RTC获取日期结构体
RTC_TimeTypeDef GET_Time; //RTC获取时间结构体
TIM_OC_InitTypeDef sConfigOC = {0}; //PWM相关结构体,用于调整频率
char str[30]; //用于组合字符串
三、主要函数
1.按键扫描
void KEY_Scan()//按键扫描
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET && Floor_CanSel(key1_floor))
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
/*将该楼层存储进选择楼层数组中*/
floor_sel[sel_dex++]=key1_floor;
/*LED更新数组 对应楼层置1*/
floor_led[key1_floor]=1;
/*LED更新*/
Led_Change();
/*开启定时器 按下一秒之后进入定时器中断*/
TIM6->CNT=0;
HAL_TIM_Base_Start_IT(&htim6);
while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
}
}
else if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET && Floor_CanSel(key2_floor))
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)
{
floor_sel[sel_dex++]=key2_floor;
floor_led[key2_floor]=1;
Led_Change();
TIM6->CNT=0;
HAL_TIM_Base_Start_IT(&htim6);
while(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET);
}
}
else if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET && Floor_CanSel(key3_floor))
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)
{
floor_sel[sel_dex++]=key3_floor;
floor_led[key3_floor]=1;
Led_Change();
TIM6->CNT=0;
HAL_TIM_Base_Start_IT(&htim6);
while(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET);
}
}
else if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET && Floor_CanSel(key4_floor))
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)
{
floor_sel[sel_dex++]=key4_floor;
floor_led[key4_floor]=1;
Led_Change();
TIM6->CNT=0;
HAL_TIM_Base_Start_IT(&htim6);
while(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET);
}
}
}
2.数组排序
按照题目的逻辑对选择数组进行排序,当前为最低层则将选择数组从小到大排序即可;当前为最高层则将选择数组从大到小排序即可;当前为中间层时,先将选择数组按从大到小排序,找出比当前楼层高且距离最近的楼层下标,以该下标为分界线,将该下标之前的数据先放入运行数组,再将该下标之后的数据放入运行数组。
void Sort_Buff()//数组排序
{
uint8_t max_dex,temp,first_dex=0;
uint8_t floor_wait_go[floor_canSel]={0};
int i,j;
/*如果电梯不再执行*/
if(!has_next_floor)
{
sel_dex=0;//选择数组下标重置
has_next_floor=1;
for(i=0;i<floor_canSel;i++)//将选择的楼层储存到待前往楼层数组
{
floor_wait_go[i]=floor_sel[i];
floor_sel[i]=0;//选择楼层清零
}
}
else//如果电梯需要继续执行则先退出
{
return;
}
/*待前往楼层数组排序 从大到小*/
for(i=0;i<floor_canSel-1;i++)
{
max_dex=i;
for(j=i+1;j<floor_canSel;j++)
{
if(floor_wait_go[j]>floor_wait_go[max_dex])
{
max_dex=j;
}
}
temp=floor_wait_go[i];
floor_wait_go[i]=floor_wait_go[max_dex];//将大的换到前面
floor_wait_go[max_dex]=temp;
}
/*最高层*/
if(floor_now==floor_canSel+1)
{
for(i=0;i<floor_canSel;i++)
{
floor_go[i]=floor_wait_go[i];
}
}
/*最低层*/
else if(floor_now==1)
{
j=0;
for(i=floor_canSel;i>=0;i--)
{
if(floor_wait_go[i]!=0)
floor_go[j++]=floor_wait_go[i];
}
}
/*中间层*/
else if(floor_now!=floor_canSel+1 && floor_now!=1)
{
for(i=0;i<floor_canSel;i++)
{
if(floor_wait_go[i]>floor_now)
{
first_dex=i;
}
}
j=0;
for(i=first_dex;i>=0;i--)
{
floor_go[j++]=floor_wait_go[i];
}
for(i=first_dex+1;i<floor_canSel;i++)
{
floor_go[j++]=floor_wait_go[i];
}
}
/*Tim6关*/
HAL_TIM_Base_Stop_IT(&htim6);
/*关门*/
Elevator_Close();
door_open=0;
delay=4;
/*开定时器*/
TIM7->CNT=0;
HAL_TIM_Base_Start_IT(&htim7);
}
3.电梯运行
void Elevator_Run()//电梯根据排序好的数组运行
{
if(door_open)//门打开时
{
key_canPress=1;//按的楼层有效
}
else
{
key_canPress=0;
}
run_flag=0;
switch(delay)
{
case 2://开门等待
printf("wait people\n");
if(delay_num==1)//等待完成
{
delay_num=1;
PWM_Stop();
HAL_TIM_Base_Stop_IT(&htim7);
if(floor_go[++go_dex]!=0)//还有目标平台
{
/*下次为电梯关门等待*/
delay=4;
Elevator_Close();//电梯关门
has_next_floor=1;
}
else//无目标平台
{
has_next_floor=0;//无下一楼层
go_dex=0;//运行数组下标重置
}
}
break;//未达到2s 继续等待
case 4://开关门等待
printf("wait door\n");
if(delay_num==2)//开关完成
{
if(door_open)
{
printf("---open---\n");
}
else
{
printf("---close---\n");
}
delay_num=1;
PWM_Stop();
HAL_TIM_Base_Stop_IT(&htim7);
/*下次电梯升降或开门等待*/
if(door_open)//下次为等待
{
delay=2;
}
else//下次为升降
{
printf("%d->%d\n",floor_now,floor_go[go_dex]);
delay=6;
if(floor_go[go_dex]>floor_now)//高楼层
{
/*电梯上升*/
Elevator_Rise();
}
else//低楼层
{
/*电梯下降*/
Elevator_Down();
}
}
}
else//未达到4s 继续等待
{
delay_num++;
}
//开定时器
TIM7->CNT=0;
HAL_TIM_Base_Start_IT(&htim7);
break;
case 6://升降等待
if(led_dir==1)
{
printf("*rise*\n");
}
else
{
printf("*down*\n");
}
if(delay_num==3)//升降完成
{
delay_num=1;
PWM_Stop();
HAL_TIM_Base_Stop_IT(&htim7);
/*更新当前楼层*/
floor_now=floor_go[go_dex];
/*LED状态改变*/
Led_Change();
/*下次为开门*/
delay=4;
Elevator_Open();
}
else//未达到6s 继续等待
{
delay_num++;
}
TIM7->CNT=0;
HAL_TIM_Base_Start_IT(&htim7);//开定时器
break;
}
}
4.RTC秒中断
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
Get_Time(); //获取时间
Show_Time(); //显示时间
Set_Alarm(); //设置下一秒的中断
/*受到LCD影响 LED状态更新*/
Led_Change(); //LED改变
Led_Run(); //流水灯
}
5.PWM输出
void PWM_OutPut(uint8_t statue)//PWM输出
{
switch(statue)
{
case elevator_close:
htim3.Init.Period = 500-1;
sConfigOC.Pulse = 250-1;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2);
HAL_TIM_PWM_Init(&htim3);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
break;
case elevator_open:
htim3.Init.Period = 500-1;
sConfigOC.Pulse = 300-1;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2);
HAL_TIM_PWM_Init(&htim3);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
break;
case elevator_rise:
htim3.Init.Period = 1000-1;
sConfigOC.Pulse = 800;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Init(&htim3);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
break;
case elevator_down:
htim3.Init.Period = 1000-1;
sConfigOC.Pulse = 600;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Init(&htim3);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
break;
}
}
5.Main函数
int main(void)
{
uint8_t i;
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* 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_TIM6_Init();
MX_TIM7_Init();
MX_RTC_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
//初始化排序和运行数组
for(i=0;i<floor_canSel;i++)
{
floor_sel[i]=0;
floor_go[i]=0;
}
//中断请求标志位初始状态设置为关闭
TIM4->SR=0;
TIM6->SR=0;
TIM7->SR=0;
printf("usart test\n");
//定时器结构体初始化
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 800;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Init(&htim3);//重新初始化
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);//开启PWM
PWM_Stop();
//LCD初始化及显示
LCD_Init();
LCD_Init_Show();
//LED初始化状态为关闭
Led_All_Close();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(key_canPress)//如果按键可以被按下
{
KEY_Scan();
}
if(sort_flag)//如果排序标志位为1
{
sort_flag=0;
Sort_Buff();
}
if(run_flag)//如果电梯运行标志位为1
{
run_flag=0;
Elevator_Run();
}
if(lcd_refresh)//如果lcd更新标志位为1
{
lcd_refresh=0;
LCD_Show();
}
}
/* USER CODE END 3 */
}
四、实验结果
五、源码(转载请注明出处)
总结
以上就是全部内容,如有错误请批评指正。