第十三届蓝桥杯嵌入式省赛题目相对于第十二届较为简单,没有那么多串口的数据处理以及判断!
第十三届省赛主要是制作一个可由串口设置密码的密码锁。本实验中,我们将用到LED模块、按键模块、串口模块、定时器的PWM模块以及官方会提供源码的LCD模块。
文末有小编写的代码
一、CubeMX配置
1.使能外部高速时钟:
2.配置时钟树:
3.GPIO输出
4.GPIO输入
5. TIM定时器
5.1 TIM2输出PWM脉冲
设置默认Pluse为50,即占空比为50%
5.2 TIM4实现定时5s
5.3 TIM3定时器实现微妙(us)延迟
6.USART(通信)
USART串口接收数据,我使用两种方法实现,大家选取其中一种即可!
1.定长接收数据
2,不定长接收数据+DMA方式
此种方法的核心是空闲中断!
二、代码实现
2.1 main函数实现
int main(void)
{
/* 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_TIM2_Init();
MX_USART1_UART_Init();
MX_TIM3_Init();
MX_TIM4_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
led(1,0);
led(2,0);
led(3,0);
led(4,0);
led(5,0);
led(6,0);
led(7,0);
led(8,0);
HAL_UART_Receive_IT(&huart1, buff, 7);
HAL_TIM_Base_Start_IT(&htim4);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Key_handle();
lcd();
if(key4_count>=3&&led_flag==0)
{
led(2,1);
delay_us(50000);
delay_us(50000);
led(2,0);
delay_us(50000);
delay_us(50000);
}
}
/* USER CODE END 3 */
}
2.2 lcd函数实现
void lcd()
{
if(pass_flag==1)
{
show_output();
if(systick-PWMTick>5000)
{
led(1,0);
pwm1();
first='@';
first_flag=-1;
second='@';
second_flag=-1;
third='@';
third_flag=-1;
pass_flag=0;
LCD_Clear(Black);
}
}
if(pass_flag==0)
{
show_input();
}
}
void show_input()
{
LCD_DisplayStringLine(Line1," PSD ");
sprintf(B1," B1:%c",first);
LCD_DisplayStringLine(Line3,B1);
sprintf(B2," B2:%c",second);
LCD_DisplayStringLine(Line4,B2);
sprintf(B3," B3:%c",third);
LCD_DisplayStringLine(Line5,B3);
}
char pl[50];
char zkb[50];
int c_pl;
int c_ARR;
int c_zkb;
int c_pluse;
void show_output()
{
LCD_DisplayStringLine(Line1," STA ");
c_ARR=__HAL_TIM_GET_AUTORELOAD(&htim2);
c_pl=100000/(c_ARR+1);
sprintf(pl," F:%dHz",c_pl);
LCD_DisplayStringLine(Line3,pl);
c_pluse=__HAL_TIM_GET_COMPARE(&htim2,TIM_CHANNEL_2);
c_zkb=c_pluse*100/(c_ARR+1);
sprintf(zkb," S:%d%%",c_zkb);
LCD_DisplayStringLine(Line4,zkb);
}
2.3 按键扫描功能实现
int Keynum()
{
int key=0;
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)==0)
{
HAL_Delay(5);
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)==0)
{
key=1;
LCD_Clear(Black);
}
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)==0);
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==0)
{
HAL_Delay(5);
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==0)
{
key=2;
LCD_Clear(Black);
}
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==0);
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2)==0)
{
HAL_Delay(5);
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2)==0)
{
key=3;
LCD_Clear(Black);
}
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2)==0);
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==0)
{
HAL_Delay(5);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==0)
{
key=4;
LCD_Clear(Black);
}
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==0);
}
return key;
}
char first='@';
int first_flag=-1;
char second='@';
int second_flag=-1;
char third='@';
int third_flag=-1;
char pass1='1';
char pass2='2';
char pass3='3';
int pass_flag=0;
int lcd_flag=0;
int ledTick=0;
void Key_handle()
{
int keynum=Keynum();
if(keynum==1)
{
first_flag++;
switch(first_flag%10)
{
case 0:first='0';break;
case 1:first='1';break;
case 2:first='2';break;
case 3:first='3';break;
case 4:first='4';break;
case 5:first='5';break;
case 6:first='6';break;
case 7:first='7';break;
case 8:first='8';break;
case 9:first='9';break;
}
}
if(keynum==2)
{
second_flag++;
switch(second_flag%10)
{
case 0:second='0';break;
case 1:second='1';break;
case 2:second='2';break;
case 3:second='3';break;
case 4:second='4';break;
case 5:second='5';break;
case 6:second='6';break;
case 7:second='7';break;
case 8:second='8';break;
case 9:second='9';break;
}
}
if(keynum==3)
{
third_flag++;
switch(third_flag%10)
{
case 0:third='0';break;
case 1:third='1';break;
case 2:third='2';break;
case 3:third='3';break;
case 4:third='4';break;
case 5:third='5';break;
case 6:third='6';break;
case 7:third='7';break;
case 8:third='8';break;
case 9:third='9';break;
}
}
if(keynum==4)
{
led_flag=0;
key4_count++;
if(pass1==first&&pass2==second&&pass3==third)
{
key4_count=0;
pass_flag=1;
pwm2();
led(1,1);
//定时器中断在回调函数中清屏函数不起作用
// __HAL_TIM_CLEAR_FLAG(&htim1,TIM_FLAG_UPDATE);
// HAL_TIM_Base_Start_IT(&htim1);
PWMTick=systick;
}
else
{
pass_flag=0;
first='@';
first_flag=-1;
second='@';
second_flag=-1;
third='@';
third_flag=-1;
}
if(key4_count>=3)
{
__HAL_TIM_CLEAR_FLAG(&htim4,TIM_FLAG_UPDATE);
HAL_TIM_Base_Start_IT(&htim4);
}
}
}
2.4 PWM脉冲转换
void pwm2()
{
__HAL_TIM_SET_AUTORELOAD(&htim2,50-1);
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,5);
}
//方波
void pwm1()
{
__HAL_TIM_SET_AUTORELOAD(&htim2,100-1);
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,50);
}
2.5 某种状态持续5s的两种操作方法
1.利用系统滴答计时器
本实验中 实现输出2KHz 10%占空比的脉冲信号,持续5秒钟,切换为 1KHz 方波信号输出的功能 使用的是系统滴答计时器。
2.TIM定时器定时5s
本实验中实现 指示灯 LD2 以 0.1秒为间隔亮、灭闪烁报警,5 秒后熄灭的功能使用的是TIM4定时器定时5s。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)
{
led_flag=1;
HAL_TIM_Base_Stop_IT(&htim4);
}
}
2.6 延迟100ms
delay_us函数实现定时微妙(us)
void delay_us(int delay)
{
HAL_TIM_Base_Start(&htim3); // 启动定时器
__HAL_TIM_SET_COUNTER(&htim3, 0); // 重置计数器
while (__HAL_TIM_GET_COUNTER(&htim3) < delay)
{}
HAL_TIM_Base_Stop(&htim3); // 停止定时器
}
delay_us(50000)代表延迟50000us即50ms,但因为定时器最大计数值为65535,如果delay_us(100000)就超过了定时器最大计数值,所以可以采取delay_us(50000)两次来实现100ms定时!
2.7 USART串口与修改密码
USART串口接收数据,我使用两种方法实现,大家选取其中一种即可!
1.接收固定长度数据
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(buff[0]==pass1&&buff[1]==pass2&&buff[2]&&buff[3]=='-'&&judge()==1)
{
pass1=buff[4];
pass2=buff[5];
pass3=buff[6];
HAL_UART_Transmit(&huart1,(unsigned char *)"Success\r\n", 9, 50);
}
else
{
HAL_UART_Transmit(&huart1,(unsigned char *)"error\r\n", 7, 50);
}
HAL_UART_Receive_IT(&huart1, buff, 7);
}
int judge()
{
for(int i=0;i<6;i++)
{
if((buff[i]<'0')||(buff[i]>'9'))//接收数据不为阿拉伯数字时不合法
return 0;
else
{
return 1;
}
}
}
2.接收不定长数据+DMA方式
此种方式使用的是空闲中断
串口空闲帧中断定义:空闲中断(IDLE),俗称帧中断,空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。当串口发送数据时是将一帧数据中的字符一个一个连续发送出的,两个字符间隔时间非常短产生不了空闲,正常情况下是第一帧数据接收毕到第二帧数据开始接收期间存在一个空闲状态,检测到此空闲状态后产生空闲空暇中断。优点:空闲中断的优点在于省去了帧头帧尾的检测,进入中断程序即意味着已经接收到一组完整数据,需及时对数据处理或将数据转移出缓冲区即可。
在mian函数中,while(1)之前增加两行代码
HAL_UART_Receive_DMA(&huart1,rx_buffer,100); // 开启DMA接收
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); // 开启串口的空闲中断
中断回调函数:
我是写在USART1_IRQHandler(void)这个里面,也可以直接写在由hal库提供的函数
HAL_UART_IRQHandler(&huart1)里面,如果写在HAL_UART_IRQHandler()里面的话,得找个地方放这个函数,所以直接在stm32f4xx_it.c中的这个函数
USART1_IRQHandler(void)里写省事一点!
最后,小编在此处附上获取我的源码链接:第十三届蓝桥杯嵌入式真题