原题:
代码:
main.c
extern struct key keys[4];
extern uint frq;
extern double duty;
extern char rxdata[30];
extern uint8_t rxdat;
extern uchar rx_p;
int F=2000,D=10;
int view=1;
char B1='@',B2='@',B3='@';
char right_pwd[5]="123";
char old_pwd[5]="123";
char new_pwd[5];
int pwd=0;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM4_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);//PWM脉冲输出初始化
HAL_TIM_Base_Start_IT(&htim4);//按键的定时器
LCD_Init();
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
LCD_Clear(Black);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);//输入捕获
HAL_UART_Receive_IT(&huart1,&rxdat,1);//开启中断
LED_Display(0x00);
while (1)
{
lcd_proc();//LCD
key_proc();//按键部分
if(rx_p!=0)//串口 检测串口接收数据位数
{
int temp=rx_p;
HAL_Delay(1);
if(temp==rx_p) uart_rx();//接收完毕 开始处理
}
}
}
main函数主要为初始化。
void lcd_proc(void)
{
char text[30];
if(view)
{
sprintf(text," PSD ");
LCD_DisplayStringLine(Line1,(unsigned char*)text);
sprintf(text," B1:%c ",B1);
LCD_DisplayStringLine(Line3,(unsigned char*)text);
sprintf(text," B2:%c ",B2);
LCD_DisplayStringLine(Line4,(unsigned char*)text);
sprintf(text," B3:%c ",B3);
LCD_DisplayStringLine(Line5,(unsigned char*)text);
}else
{
sprintf(text," STA ");
LCD_DisplayStringLine(Line1,(unsigned char*)text);
sprintf(text," F:%dHz ",F);
LCD_DisplayStringLine(Line3,(unsigned char*)text);
sprintf(text," D:%d%% ",D);
LCD_DisplayStringLine(Line4,(unsigned char*)text);
LED_Display(0x01);
HAL_Delay(5000);
LED_Display(0x00);
view=!view;
}
}
根据题目所需显示相应界面。
在此题目中,由于STA界面是静态的,只会有一种结果,所以其实可以直接将页面写死,直接输出2000Hz和10%。
void key_proc(void)
{
if(keys[0].single_flag==1)//密码 B1
{
if(!view)//保证按键只在PSD界面有效,下同
{
keys[0].single_flag=0;
return;
}
if(B1=='@')
B1='0';
else
{
if(B1=='9') B1='0';
else B1++;
}
keys[0].single_flag=0;//预防一次按下多次响应
}else if(keys[1].single_flag==1)//密码 B2
{
if(!view)
{
keys[0].single_flag=0;
return;
}
if(B2=='@')
B2='0';
else
{
if(B2=='9') B2='0';
else B2++;
}
keys[1].single_flag=0;
}else if(keys[2].single_flag==1)//密码 B3
{
if(!view)
{
keys[0].single_flag=0;
return;
}
if(B3=='@')
B3='0';
else
{
if(B3=='9') B3='0';
else B3++;
}
keys[2].single_flag=0;
}else if(keys[3].single_flag==1)//密码确认
{
char input[3];
sprintf(input,"%c%c%c",B1,B2,B3);
//密码正确
if(!strcmp(input,right_pwd))
{
pwd=0;
//将PA1的PWM输出调整为:2000Hz,占空比为:10%
//F=主频/预分配系数/重装载值
//F=80000000/(40-1)/(1000-1)
//Duty=pulse/重装载值
//Duty=100/(1000-1)
__HAL_TIM_SET_PRESCALER(&htim2, 40-1); //设置预分频系数
__HAL_TIM_SET_AUTORELOAD(&htim2, 1000-1); //设置重装载值
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,100);
B1='@',B2='@',B3='@';//重置密码
view=!view;//切换界面
LCD_Clear(Black);
}else
{
pwd++;
if(pwd>=3)//密码连续输入错误三次及以上
{
for(int i=0;i<25;i++)//LED2闪烁5s
{
LED_Display(0x02);
HAL_Delay(100);
LED_Display(0x00);
HAL_Delay(100);
}
LED_Display(0x00);
}
B1='@',B2='@',B3='@';
}
keys[3].single_flag=0;
}
}
如注释。
void uart_rx(void)
{
//接收到了
if(rx_p>0)
{
if(rx_p==7)//接收的数据符不符合题目要求
{
sscanf(rxdata,"%3s-%3s",old_pwd,new_pwd);//数据格式为:xxx-xxx
if(!strcmp(old_pwd,right_pwd))//判断一下旧密码是否与当前密码一致
{
strcpy(right_pwd,new_pwd);//密码更改
}else//否则报错
{
char temp[20];
sprintf(temp,"Old password is wrong!\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
}
}else
{
char temp[20];
sprintf(temp,"Error\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
}
rx_p=0;//很重要!要置0
memset(rxdata,0,30);
}
}
如注释。
interrupt.c
#include "interrupt.h"
#include "usart.h"
struct key keys[4];
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)//判断中断来源
{
//读取引脚的值
keys[0].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
keys[1].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
keys[2].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
keys[3].key_status=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
//状态机消抖
for(int i=0;i<4;i++)
{
switch(keys[i].judge_status)
{
case 0:
{
if(keys[i].key_status==0)
{
keys[i].judge_status=1;
keys[i].key_time=0;
}
}
break;
case 1:
{
if(keys[i].key_status==0)
{
keys[i].judge_status=2;
}else
{
keys[i].judge_status=0;
}
}
break;
case 2:
{
if(keys[i].key_status==0)
{
keys[i].key_time++;
if(keys[i].key_time>=70)
{
keys[i].long_flag=1;
}
}else
{
keys[i].judge_status=0;
if(keys[i].key_time<70)
{
keys[i].single_flag=1;
}
}
}
break;
}
}
}
}
double ccrl_vala=0,ccrl_valb=0;
uint frq=0;
double duty=0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1) //中断消息来源 选择直接输入的通道
{
//读取定时器的计时值
ccrl_vala=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
ccrl_valb=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);
//将定时器的计时值清零
__HAL_TIM_SetCounter(htim,0);
//计算频率 主频/分频系数/计时值
frq=(80000000/80)/ccrl_vala;
//占空比计算
duty=(ccrl_valb/ccrl_vala)*100;
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);
HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);
}
}
}
char rxdata[30];
uint8_t rxdat;
uchar rx_p;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
rxdata[rx_p++]=rxdat;
HAL_UART_Receive_IT(&huart1,&rxdat,1);
}
关于状态机消抖的详细解释,请移步:蓝桥杯嵌入式第十二届省赛--程序设计部分-优快云博客
interrupt.h
#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_
#include "main.h"
#include "stdbool.h"
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
struct key
{
bool single_flag;
bool long_flag;
uint key_time;
uint key_status;
uint judge_status;
};
#endif
led.c
#include "led.h"
void LED_Display(uchar dsLED)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,dsLED<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
led.h
#ifndef __LED_H__
#define __LED_H__
#include "main.h"
void LED_Display(uchar dsLED);
#endif
----THE END-----
有关蓝桥杯嵌入式的模块知识请参考如下:
蓝桥杯嵌入式模块学习系列
(还有几个模块没更新~)
有关蓝桥杯嵌入式历届真题请参考如下: