蓝桥杯第十三届嵌入式省赛1(github源码 + 使用STM32CubeMX)

详细代码放在github里自行下载github

本次蓝桥杯省赛有led、串口、定时器等模块。题目简单不难。

led模块:

查看原理图可知,该led模块由PD2所控制,当PD2为高电平时,led模块才能够启用,在这个时候输入是有效的;当为低电平时,led模块会锁住当前PC口的值。所以在你要控制led灯时,就需要操控PD2。

点击pc8

选择输出模式

同样的操作配置好其他PC口和PD2口,如图:

这个user label 是自己定义的名字,STM32CubeMX会根据你定义的名字来宏定义这些端口。

void led_turndown(void)
{
	HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED5_GPIO_Port,LED5_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED6_GPIO_Port,LED6_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED7_GPIO_Port,LED7_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED8_GPIO_Port,LED8_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

需要注意的是在操作led时要先写好端口的值,然后操控开关PD2,这一步骤必须连续,否则回合lcd显示屏模块冲突,因为他们都是用的pc口。

串口模块:

串口按照图中配置即可,波特率题目要求为9600,然后记得去NVIC中把中断打开。

//打开串口的中断接收功能 这句代码放在主函数while循环前
HAL_UART_Receive_IT(&huart1,(uint8_t *)&Rxbuff,sizeof(Rxbuff));

//中断回调函数接收传输来的密码,触发串口中断后系统会自动调用这个函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1){//串口传输密码,密码替换
		// 重新使能中断
		HAL_UART_Receive_IT(huart,(uint8_t *)&Rxbuff,sizeof(Rxbuff)); 
		if(Rxbuff[3] == '-' && Rxbuff[0] == password[0] && Rxbuff[1] == password[1] && Rxbuff[2] == password[2]
		&& Rxbuff[4] >= '0' && Rxbuff[4] <= '9' && Rxbuff[5] >= '0' && Rxbuff[5] <= '9'
			&& Rxbuff[6] >= '0' && Rxbuff[6] <= '9'){
				password[0] = Rxbuff[4];
				password[1] = Rxbuff[5];
				password[2] = Rxbuff[6];
			}
	}
}

按键:

按键端口选择输入模式,按键按下是读取会是低电平,上拉输入最好。

key.h

#ifndef __KEY_H
#define __KEY_H

#include "stm32g4xx_hal.h"
#include "stm32g4xx_it.h"
#include "main.h"

// 按键结果结构体 需要注意的是任意俩者不能够同时为1
struct keys{
	//按键单次按下  0-无效   1-按键按下
	int flag;
	//双击按键的标志 0-无效  1-双次按下
	int doubleflag;
	//长按按键的标志 0-无效   1-长按
	int longflag;
};

//按键状态判断的结构体
struct keyState{
	//记录按键状态机的状态
	unsigned char judgeSate;
	//记录按键的状态
	unsigned char keyState;
	//记录按键是否双击 双击就会计时
	unsigned char doubleClickTimerFlag;
	//记录按键按下的时间
	int keyTime;
	//记录按键双击的时间
	unsigned char doubleClickTime;
};

void key_init(void);

//定义一个按键变量 key[0]=B1 key[1]=B2 key[2]=B3 key[3]=B4 
extern struct keys key[];

/***************************************************************************************************
 * 函数功能:按键扫描函数 注意此函数放在定时器中断(10ms)中的使用效果最佳 否则双击与长按会出现问题
 * 函数参数:无
 * 函数返回值:无
****************************************************************************************************/
void key_scanf(void);

#endif

key.c

#include "key.h"

struct keys key[4] = {0,0,0,0};


/****************************************************************************************************
 * 函数功能:按键扫描函数 注意此函数放在定时器中断(10ms)中的使用效果最佳 否则双击与长按会出现问题
 * 函数参数:无
 * 函数返回值:无 
*****************************************************************************************************/
void key_scanf(void)
{
	static struct keyState _key[4];
	
	//获取按键的最新状态
	_key[0].keyState = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
	_key[1].keyState = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
	_key[2].keyState = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
	_key[3].keyState = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
	
	//处理按键的最新状态
	for(int i=0;i<4;i++){
		switch (_key[i].judgeSate){
			//按键第一次按下
			case 0:
				if(_key[i].keyState == 0){
					//跳转按键的状态
					_key[i].judgeSate=1;
					//清空按键时间
					_key[i].keyTime=0;
				}
				break;
			//按键第二次按下 两次相隔10ms可以起到消抖作用	
			case 1:
				//按键再次按下 跳转按键状态
				if(_key[i].keyState == 0)
					_key[i].judgeSate=2;
				//上一次按键按下是抖动按下 属于无效状态 应该退回最开始的状态
				else 
					_key[i].judgeSate=0;
				break;
			//确定按键按下后的处理过程
			case 2:
				//等待松开过程,且非长按键
				if((_key[i].keyState==1) && _key[i].keyTime<30){	
					//可能双击按键的第一次,进入计时
					if(_key[i].doubleClickTimerFlag == 0) {
						_key[i].doubleClickTimerFlag = 1;
						_key[i].doubleClickTime = 0;
					}
					//在计时范围内又按了一次
					else{
						key[i].doubleflag=1;//双击情况
						_key[i].doubleClickTimerFlag = 0;
					}
					_key[i].judgeSate = 0;
				}
				//松开且是长按键
				else if(_key[i].keyState==1 && _key[i].keyTime>=30)
				{
					_key[i].judgeSate = 0;
					key[i].longflag = 1;
				
				}
				//按下 且为长按键
				else 
					_key[i].keyTime++;
				break;
		}
		//按键单次按下
		if(_key[i].doubleClickTimerFlag == 1 && _key[i].doubleClickTime >= 25) {
			key[i].flag = 1;
			_key[i].doubleClickTimerFlag = 0;
		}
		//按键双击 双击计时
		else if(_key[i].doubleClickTimerFlag == 1){
			_key[i].doubleClickTime++;
		}
	}
}

按键函数包含了单击、双击和长按,自行按需修改。

定时器输出pwm 

此处有误,我并未使用PA1作为输出引脚,请大家自己看一PA1哪个定时器更改即可。

我的系统是170MHZ 所以170分频,这里填170 - 1;自动重装载值填1000 - 1。

如何计算:

        系统频率是170M = 170 000 000,我要1000HZ频率的方波就除以1000.结果为170 000 然后分频系数填170,170 000 / 170 = 1 000这两个数就是这么来的。

后面还要在程序里修改为2000HZ再算。 170 000 000 / 2000 = 850 000 然后 850 000 / 170(分频) = 500(重装载)。

最后设置占空比以及打开中断。方波就是高一半第一半,2000HZ占空比10%设置200即可。

HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1);//打开定时器


void Pwm_chage(void)//切换输出波形
{
	TIM_OC_InitTypeDef sConfigOC = {0};
	
	if(pwm_time != 0 && pwm_flag == 0){//pwm切换到2KHZ 5s
		led1_turnOn();
		htim2.Init.Period = 500 - 1;
		sConfigOC.OCMode = TIM_OCMODE_PWM1;
		sConfigOC.Pulse = 200;
		sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
		sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
		if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
		{
			Error_Handler();
		}
		pwm_flag = 1;
	}
	else if(pwm_flag == 1 && pwm_time >= 50)//pwm切换到1KHZ
	{
		led1_turnDown();
		htim2.Init.Period = 1000 - 1;
		sConfigOC.Pulse = 500;
		if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
		{
			Error_Handler();
		}
		pwm_flag = 2;
	}
}

lcd模块是系统提供的就不多说了

			if(password_HZ == 0){//启动界面显示
				sprintf((char*)lcd_str0,"       PSD           ");
				sprintf((char*)lcd_str1,"    B1:%c            ",'@');
				sprintf((char*)lcd_str2,"    B2:%c            ",'@');
				sprintf((char*)lcd_str3,"    B3:%c            ",'@');
				HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
				LCD_DisplayStringLine(Line2,lcd_str0);
				LCD_DisplayStringLine(Line4,lcd_str1);
				LCD_DisplayStringLine(Line5,lcd_str2);
				LCD_DisplayStringLine(Line6,lcd_str3);
			}
			else{//密码锁显示
				sprintf((char*)lcd_str0,"       PSD           ");
				sprintf((char*)lcd_str1,"    B1:%c            ",(uint8_t)(password_text[0] + 48));
				sprintf((char*)lcd_str2,"    B2:%c            ",(uint8_t)(password_text[1] + 48));
				sprintf((char*)lcd_str3,"    B3:%c            ",(uint8_t)(password_text[2] + 48));
				HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
				LCD_DisplayStringLine(Line2,lcd_str0);
				LCD_DisplayStringLine(Line4,lcd_str1);
				LCD_DisplayStringLine(Line5,lcd_str2);
				LCD_DisplayStringLine(Line6,lcd_str3);
			}
		}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值