第十三届蓝桥杯嵌入式省赛标准库版本
作者:是舒克呀
QQ:1664655231
比赛题目放链接了:https://pan.baidu.com/s/1hc9xGl94bkwn6sLuxzEYdA
提取码:txjd
理解题目意思
比赛要求通过按键输入密码,LCD屏幕现实输入的密码值,然后密码对就输出对应的PWM,密码不对则输出另外波形的PWM,通过系统硬件图我们可以看出这届比赛需要用到LCD屏幕、串口、按键、LED、PWM等外设所以我们需要写一下部分的外设驱动
编写外设驱动
LED部分外设驱动
void GPIO_AllInit(void)
{
GPIO_InitTypeDef GPIO_System;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD,ENABLE);
GPIO_System.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_System.GPIO_Pin = GPIO_Pin_All;
GPIO_System.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_System);
GPIO_System.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD,&GPIO_System);
GPIO_System.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_System.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;
GPIO_System.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_System);
GPIO_System.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_Init(GPIOB,&GPIO_System);
SCQ_ON(); //打开锁存器
LED_OFF(); //熄灭全部LED灯
SCQ_OFF(); //关闭锁存器
}
按键部分(定时器10ms调用一次)
void Read_Key(void)
{
u8 key_read = (Key_Start)^0xf0;
chufa = key_read&(key_read^chixu);
chixu = key_read;
Key_AddORSub(); //按密码函数
Cheek_Pasword(); //密码校验函数
}
定时器部分(1ms定时器)
void Time4_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM4 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* TIM4 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 999;
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
/* TIM IT enable */
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
/* TIM2 enable counter */
TIM_Cmd(TIM4, ENABLE);
}
PWM输出以及初始化函数,想要输出固定频率的PWM如果以72分频为例,则计数999次499占空比则为1kHz频率50占空比,计算公式为:72000000/72/999=1000Hz
void PWM_Out(u32 Period_Val,u32 Pluse_Val)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/*GPIOB Configuration: TIM3 channel1, 2, 3 and 4 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* TIM3 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = Period_Val; //传入计数值
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = Pluse_Val; //传入占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
/* TIM3 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
串口部分
void STM_EVAL_COMInit(USART_InitTypeDef* USART_InitStruct)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
/* Configure USART Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART configuration */
USART_Init(USART2, USART_InitStruct);
/* Enable USART */
USART_Cmd(USART2, ENABLE);
}
void Uart2_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* Enable the USARTz Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
STM_EVAL_COMInit(&USART_InitStructure);
/* Enable the USARTz Receive Interrupt */
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
//USART_ITConfig(USART2, USART_IT_IDLE|USART_IT_RXNE, ENABLE);
}
int fputc(int ch, FILE *f) //printf重定向函数使用必须勾选Keil MDK的Use Micro Lib选项
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
USART_SendData(USART2, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET)
{}
return ch;
}
功能逻辑部分
屏幕现实部分逻辑以及函数处理
密码初始显示在屏幕、返回密码输入界面、密码错误等行为都会使密码初始显示为@,博主在考场写的时候一开始是用的%d显示整数,后面才注意到有@然后显示结果为64,一下子启发了,直接使用ASCII码的进制来算就可以解决显示部分,具体代码如下
显示切换界面函数
void Display_Main(void)
{
if(Mode_Flag) //密码正确切换参数界面
{
Display_STA();
}
else //密码显示密码输入界面
{
Display_PSD();
}
}
密码输入界面显示函数 ,这里有个细节也就是,如果只调整了其中一个B1/B2/B3值没有调整其他值时,其他转载值应该为@, LCD屏幕函数和按键在修改结果时候屏幕会有白屏现象,这个时候需要考虑LCD显示函数是否显示过于频繁,也就是讲LCD显示函数加于条件例如按键按下才显示上去
void Display_PSD(void) //密码输入界面
{
u8 buff[20],buff1[20],buff2[20];
LCD_DisplayStringLine(Line1," PSD");
if(B1_Val == 64 && B2_Val == 64 && B3_Val == 64) //是否为初始状态显示初始密码为@
{
sprintf(buff," B1:%c",B1_Val);
sprintf(buff1," B2:%c",B2_Val);
sprintf(buff2," B3:%c",B3_Val);
LCD_DisplayStringLine(Line3,buff);
LCD_DisplayStringLine(Line4,buff1);
LCD_DisplayStringLine(Line5,buff2);
}
if(B1_Val != 64) //B1密码改变
{
sprintf(buff," B1:%c",B1_Val-17); //64-17+按键值刚刚好是48的十六进制为0x30就是ASCII的0
LCD_DisplayStringLine(Line3,buff);
}
if(B2_Val != 64) //B1密码改变
{
sprintf(buff1," B2:%c",B2_Val-17);
LCD_DisplayStringLine(Line4,buff1);
}
if(B3_Val != 64) //B1密码改变
{
sprintf(buff2," B3:%c",B3_Val-17);
LCD_DisplayStringLine(Line5,buff2);
}
}
参数界面显示不变因为没有参数要改变的
void Display_STA(void)
{
LCD_DisplayStringLine(Line1," STA");
LCD_DisplayStringLine(Line3," F:2000Hz");
LCD_DisplayStringLine(Line4," D:10%");
}
输入密码函数,需要考虑密码上限值后从0开始
void Key_AddORSub(void) //输入密码函数
{
if(chufa == 0x01) //B1密码值加
{
B1_Val+=1;
if(B1_Val-17 >= 58) //B1密码值超过9 9的ASCII码为58 0为48
{
B1_Val = 65;
}
}
if(chufa == 0x02) //B2密码值加
{
B2_Val+=1;
if(B2_Val-17 >= 58) //B2密码值超过9 9的ASCII码为58 0为48
{
B2_Val = 65;
}
}
if(chufa == 0x04) //B3密码值加
{
B3_Val+=1;
if(B3_Val-17 >= 58) //B3密码值超过9 9的ASCII码为58 0为48
{
B3_Val = 65;
}
}
}
校验密码函数,校验对后进入参数界面前需要清空一次屏幕,密码校验不对的时候也应该清空一次屏幕,或者选择覆盖,
void Cheek_Pasword(void) //校验密码函数
{
if(chufa == 0x08) //确认密码按下
{
Enter_Flag = 1;
LCD_Clear(Black);
}
if(Enter_Flag) //按下确认后判断
{
if(B1_Val-17 == Pasword[0] && B2_Val-17 == Pasword[1] && B3_Val-17 == Pasword[2]) //判断密码是否正确
{
Mode_Flag = 1;
Led1_Flag = 1;
LCD_Clear(Black);
Enter_Flag = 0;
}
else //密错误累加错误次数
{
Pasword_Val+=1;
Enter_Flag = 0;
if(Pasword_Val >= 3)
{
Led2_Flag = 1;
Pasword_Val = 0;
}
}
}
}
LED功能部分函数
void LED(void)
{
if(Led1_Flag) //密码正确后LED1亮
{
LED_OFF();
SCQ_ON();
GPIO_ResetBits(GPIOC,0x01<<8);
SCQ_OFF();
}
else
{
LED_OFF();
SCQ_ON();
GPIO_SetBits(GPIOC,0x01<<8);
SCQ_OFF();
}
if(Led2_Flag) //密码错误上限LED闪烁
{
if(Led2_Mode)
{
LED_OFF();
SCQ_ON();
GPIO_ResetBits(GPIOC,0x01<<9);
SCQ_OFF();
}
else
{
LED_OFF();
SCQ_ON();
GPIO_SetBits(GPIOC,0x01<<9);
SCQ_OFF();
}
}
}
串口中断以及串口修改密码函数
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
/* Read one byte from the receive data register */
Rx_Buff[Counter++] = USART_ReceiveData(USART2);
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
}
if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
{
USART2->SR;
USART2->DR;
Cheek_Rx();
}
}
void Cheek_Rx(void)
{
if(Counter > 7)
{
Counter = 0;
return;
}
else if (Counter == 7 && Rx_Buff[3] == '-') //接收7个长度数据判断数据是否正确
{
if(Rx_Buff[0] == Pasword[0] && Rx_Buff[1] == Pasword[1] && Rx_Buff[2] == Pasword[2]) //判断串口密码是否正确
{
Pasword[0] = Rx_Buff[4];
Pasword[1] = Rx_Buff[5];
Pasword[2] = Rx_Buff[6];
// printf("修改成功\r\n");
}
Counter = 0;
}
}
总结
本次省赛题目还是比较容易的,想做的好可能需要处理好细节,例如LCD屏幕的一些白屏,从参数界面返回输入密码界面的清屏如何清具体,以及题目没有提及的串口如果多发了的清空也应该写函数处理,最后希望这篇文章能给与大家一点点帮助。
写在最后
这也许可能是博主大学生涯最后一次打比赛了,感触还是蛮大的,从大一到现在大三经历过或多或少的比赛,蓝桥杯可能给大家的看法都是水份多,其实吧个人觉得比赛最重要是学到东西,如果是一无所知的新手参加比赛通过比赛学习到了东西其实就已经收获很大了,每个拿国一以及省一的同学肯定都是在大家看不到的地方努力了才取的的成就,希望大家好好努力,最后祝愿每位阅读文章的小伙伴都可以拿到满意的成绩。