赞
踩
十三届省赛是要制作一个可由串口设置密码的密码锁。在本场比赛中,我们将用到LED模块、按键模块、串口模块、定时器的PWM模块以及官方会提供源码的LCD模块。下面就请看原题:
在正式题解前,大家需要注意以下几点:
- 由于LCD与LED有部分引脚是共用的,因此初始化完成LCD后最好手动关闭LED;
- 由于每次LCD显示的长度可能不同,因此在本次显示前,要不先清屏,要不跟上次显示一样长;
- 使用CubeMX配置完成串口USART1后需要更改默认引脚为PA9、PA10;
通过查询产品手册知,LED的引脚为PC8~PC15,外加锁存器74HC573需要用到的引脚PD2。(由于题目要求除LED1、LED2外的其他LED都处于熄灭状态,此处特意将所有的LED都初始化)
CubeMX配置:
代码样例
由于G431的所有LED都跟锁存器74HC573连接,因此每次更改LED状态时都需要先打开锁存器,写入数据后再关闭锁存器。
/***************************************************** * 函数功能:改变所有LED的状态 * 函数参数: * char LEDSTATE: 0-表示关闭 1-表示打开 * 函数返回值:无 ******************************************************/ void changeAllLedByStateNumber(char LEDSTATE) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8 |GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET)); //打开锁存器 准备写入数据 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET); //关闭锁存器 锁存器的作用为 使得锁存器输出端的电平一直维持在一个固定的状态 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET); } /***************************************************** * 函数功能:根据LED的位置打开或者是关闭LED * 函数参数: * uint16_t LEDLOCATION:需要操作LED的位置 * char LEDSTATE: 0-表示关闭 1-表示打开 * 函数返回值:无 ******************************************************/ void changeLedStateByLocation(uint16_t LEDLOCATION,char LEDSTATE) { HAL_GPIO_WritePin(GPIOC,LEDLOCATION,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET)); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET); }
通过查询产品手册知,开发板上的四个按键引脚为PB0~PB2、PA0。
CubeMX配置
代码样例
由于G431开发板上按键数量较少以及本次按键不涉及长短按、单击双击等复杂按键的设计,因此,我们直接使用含锁机制的if判断即可。
/********************************************* * 函数功能:按键扫描 含按键消抖 无长按短按设计 * 函数参数:无 * 函数返回值:按键的位置 * 返回值说明:B1-1 B2-2 B3-3 B4-4 *********************************************/ unsigned char scanKey(void) { //按键锁 static unsigned char keyLock = 1; //记录按键消抖时间 // static uint16_t keyCount = 0; //按键按下 if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET || HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET) && keyLock == 1){ //给按键上锁 避免多次触发按键 keyLock = 0; //按键消抖 这里最好不要使用延时函数进行消抖 会影响系统的实时性 // if(++keyCount % 10 < 5) return 0; // if(HAL_GetTick()%15 < 10) return 0; HAL_Delay(10); //按键B1 if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET){ return 1; } //按键B2 if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET){ return 2; } //按键B3 if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET){ return 3; } //按键B4 if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET){ return 4; } } //按键松开 if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == SET && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == SET && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == SET && HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == SET) && keyLock == 0){ //开锁 keyLock = 1; } return 0; }
本次试题中,串口功能比较简单,只需要能够完成简单的接收数据即可。
CubeMX配置
配置时一定一定记得改引脚!!!
代码样例
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)函数解析:
/***使用HAL_UART_Receive_IT中断接收数据 每次接收完成数据后就会执行该函数***/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1){
// 重新使能中断
HAL_UART_Receive_IT(huart,(uint8_t *)&Rxbuff,sizeof(Rxbuff));
}
}
CubeMX配置
修改PWM的频率及占空比样例代码(可直接调用)
/****************************************
* 函数功能:修改PWM频率工作
* 函数参数:
* unsigned int autoreloadDate:重装载值
* unsigned int compareDate:PWM的比较值
* 函数返回值:无
****************************************/
void pwmWorkByFre(unsigned int autoreloadDate,unsigned int compareDate)
{
//设置重装载值
__HAL_TIM_SetAutoreload(&htim2,autoreloadDate-1);
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,compareDate);
HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_UPDATE);
}
LCD模块官方会提供源码,内含初始化,大家会用即可。如下面是一段将LCD初始化成——文字颜色为白色、背景为蓝色的LCD屏:
/****************************************************************************** * 函数功能:LCD初始化 * 函数参数:无 * 函数返回值:无 *******************************************************************************/ void lcdInit(void) { //HAL库的初始化 LCD_Init(); //设置LCD的背景色 LCD_Clear(Blue); //设置LCD字体颜色 LCD_SetTextColor(White); //设置LCD字体的背景色 LCD_SetBackColor(Blue); }
文件说明:
- sysInit()函数:自己添加的初始化配置;
- sysWork()函数:系统工作逻辑函数;
#include "config.h" #include "stdio.h" #include "string.h" #include "stdlib.h" //外部定义变量 extern TIM_HandleTypeDef htim7,htim2; extern UART_HandleTypeDef huart1; extern TIM_HandleTypeDef htim3; //记录目前LCD处于的界面 0-密码输入界面 1-显示输出界面 int lcd_view_mod = 0; //定时器7计数 计数值加1表示定时器触发一次也就是过了10ms int time7_count = 0; //保存上一次定时器7计数值 int time7_count_start_flag = 0; //密码输入界面显示的数据 char psd_txt[20]; //输出状态界面显示数据 char sta_txt[20]; char temp[20]; unsigned int crrl_t = 0; uint32_t frd = 0; //用于循环 int i,j; //* 存储串口1接收的数据 uint8_t Rxbuff[7]; //密码锁密码 注意 这里一定一定要加上字符串结束符'\0' 否则会出现字符串拼接的情况 char passwd[4] = {'1','2','3','\0'}; //输入的密码 char passwd_test[4] = {'0','0','0','\0'}; //记录密码输错次数 unsigned int passwd_wrong_count = 0; //记录是否重头开始输入密码 0-是重头开始输入且没开始输入的 1-不是重头开始输入密码 char passwd_flag = 0; //记录按键的值 unsigned char key_num = 0; /*********************************************** * 函数功能:自定义的系统初始化 * 函数参数:无 * 函数返回值:无 ***********************************************/ void sysInit(void) { //LCD初始化 lcdInit(); //关闭所有的LED changeAllLedByStateNumber(0); //打开定时器7中断 HAL_TIM_Base_Start_IT(&htim7); //打开串口的中断接收功能 HAL_UART_Receive_IT(&huart1,(uint8_t *)&Rxbuff,sizeof(Rxbuff)); //打开定时器2通道2的PWM输出功能 HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2); } /*********************************************** * 函数功能:系统工作逻辑函数 * 函数参数:无 * 函数返回值:无 ***********************************************/ void sysWork(void) { //更新密码 getPasswdByUsart(); //密码的相关操作以及显示密码界面 if(lcd_view_mod == 0) { keyProc(); PSDViewDisplay(); pwmWorkByFre(1000,400); } //显示输出界面 else if(lcd_view_mod == 1) { STAViewDisplay(); pwmWorkByFre(500,50); passwd_flag = 0; time7_count_start_flag = 1; } //LED灯显示 ledDisplay(); //5秒时间到 定时器值以及标志位归零 界面显示密码界面 密码输错次数归零 if(time7_count>500) { time7_count = 0; time7_count_start_flag = 0; lcd_view_mod = 0; passwd_wrong_count = 0; } } /***非阻塞模式下定时器中断回调函数***/ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { //定时器7的中断回调函数 if(htim->Instance == TIM7) { key_num = scanKey(); if(time7_count_start_flag) time7_count++; } } /***使用HAL_UART_Receive_IT中断接收数据 每次接收完成数据后就会执行该函数***/ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1){ // 重新使能中断 HAL_UART_Receive_IT(huart,(uint8_t *)&Rxbuff,sizeof(Rxbuff)); } } /*********************************************** * 函数功能:显示密码输入界面 * 函数参数:无 * 函数返回值:无 ***********************************************/ void PSDViewDisplay(void) { LCD_DisplayStringLine(Line2,(uint8_t*)" PSD"); //在第一次输入密码前显示 if(passwd_flag == 0) { sprintf(psd_txt," B1 :%c ",'@'); LCD_DisplayStringLine(Line3,(uint8_t*)psd_txt); sprintf(psd_txt," B2 :%c ",'@'); LCD_DisplayStringLine(Line4,(uint8_t*)psd_txt); sprintf(psd_txt," B3 :%c ",'@'); LCD_DisplayStringLine(Line5,(uint8_t*)psd_txt); } else { sprintf(psd_txt," B1 :%c ",passwd_test[0]); LCD_DisplayStringLine(Line3,(uint8_t*)psd_txt); sprintf(psd_txt," B2 :%c ",passwd_test[1]); LCD_DisplayStringLine(Line4,(uint8_t*)psd_txt); sprintf(psd_txt," B3 :%c ",passwd_test[2]); LCD_DisplayStringLine(Line5,(uint8_t*)psd_txt); } } /*********************************************** * 函数功能:显示输出界面 * 函数参数:无 * 函数返回值:无 ***********************************************/ void STAViewDisplay(void) { LCD_DisplayStringLine(Line2,(uint8_t*)" STA"); LCD_DisplayStringLine(Line3,(uint8_t*)" F :2000Hz"); LCD_DisplayStringLine(Line4,(uint8_t*)" D :10%"); LCD_ClearLine(Line5); } /*********************************************** * 函数功能:LED的相关显示 * 函数参数:无 * 函数返回值:无 ***********************************************/ void ledDisplay(void) { static int LED2_state = 1; //为了避免LED显示受到LCD影响 应该先关闭所有LED changeAllLedByStateNumber(0); //密码输入错误三次 LED2以0.1秒间隔闪烁 (5秒后熄灭) if(passwd_wrong_count>=3) { time7_count_start_flag = 1; if(time7_count <= 500) { //定时器计数值刷新 时间到了 LED2状态需要反转 changeLedStateByLocation(LED2,LED2_state); LED2_state ^= 1; } } //密码验证成功 LED1点亮5秒 (5秒后熄灭) if(lcd_view_mod && time7_count <= 500) { changeLedStateByLocation(LED1,1); } } /******************************************** * 函数功能: * 函数参数:无 * 函数返回值:无 ********************************************/ void getPasswdByUsart(void) { //分析串口接收到数据 需:旧密码匹配才能够设置新密码 for(i=0;i<7;i++) { //判断输入是否合理 if(!((Rxbuff[i]>='0'&&Rxbuff[i]<='9') || Rxbuff[i]=='-')) return ; //判断旧密码是否正确 if(i < 3) { if(Rxbuff[i]!=passwd[i]) return ; } //设置新密码 if(i>3) { passwd[i%4] = Rxbuff[i]; } } //重置密码后也需要重置旧密码输入错误的次数 passwd_wrong_count = 0; } /******************************************** * 函数功能: * 函数参数:无 * 函数返回值:无 ********************************************/ void keyProc(void) { //按键按下 if(key_num) { //按键B0-B2 if(key_num!= 3) { if(++passwd_test[key_num]>'9') passwd_test[key_num] = '0'; passwd_flag = 1; } //按键B3 else { //输入密码正确 if(!strcmp(passwd,passwd_test)) { lcd_view_mod = 1; passwd_wrong_count = 0; } //输入密码错误 else { passwd_flag = 0; passwd_wrong_count++; } } } } /**************************************** * 函数功能:修改PWM的频率工作 * 函数参数: * unsigned int autoreloadDate:重装载值 * unsigned int compareDate:PWM的比较值 * 函数返回值:无 ****************************************/ void pwmWorkByFre(unsigned int autoreloadDate,unsigned int compareDate) { //设置重装载值 __HAL_TIM_SetAutoreload(&htim2,autoreloadDate-1); __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,compareDate); HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_UPDATE); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。