备战蓝桥杯(4)——第九届蓝桥杯嵌入式省赛赛题实战
系列文章目录
备战蓝桥杯(3)——第八届蓝桥杯嵌入式省赛赛题实战
备战蓝桥杯(2)——第七届蓝桥杯嵌入式省赛赛题实战
备战蓝桥杯(1)——第六届蓝桥杯嵌入式省赛赛题实战
文章目录
- 系列文章目录
- 前言
- 一、第九届赛题蓝桥杯嵌入式省赛赛题展示
- 二、程序流程图
- 三、CUBEMX配置
- 四、各部分代码编写
- 1.存储位置改变功能的实现
- 2.时间设置功能函数的编写
- 3.定时器启动/结束函数
- 总结
前言
这次第九届蓝桥杯的省赛的题比第八届省赛的题简单了,但是我在做的时候也有很多小问题导致卡壳,也是将之前学习过的东西在做了一次小复习,同时这次省赛的长短按键也是比第一次接触到,很有收获。本次仍然是基于CT117E-M4平台,用的CUBEMX与keil开发。
一、第九届赛题蓝桥杯嵌入式省赛赛题展示
二、程序流程图
程序流程图是在程序写完之后画的,根据我编的程序的逻辑关系绘制的。这道题的逻辑关系不复杂,思路很清晰。
三、CUBEMX配置
这里的配置都是一些常规的配置,看着题目来,要啥配啥就行。这里要注意一下(也是我范的错误),官方给的EEPROM代码里面虽然有配置I2C的引脚(PA6,PA7),但是需要在CUBEMX里面配置一下,参数默认即可。我看了代码,我觉得是因为例程里的引脚没有初始化就直接拿来用了,所以如果没有在CUBEMX里面配置的话,是无法读取和写入EEPROM的。
四、各部分代码编写
先上主函数和变量定义:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "i2c.h"
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
struct Time{uint8_t hour;uint8_t min;uint8_t sec;
} TT_1;
/* USER CODE END PD */
int go_stop = 1; //计时开始/结束标志位
int time_cnt4 = 0; //定时器4计数值
int long_pushB2 = 0; //B2按键长按标志位
int long_pushB3 = 0; //B3按键长按标志位
uint8_t str[20];
uint8_t str1[10];
uint8_t str2[20] = " ** ";
uint8_t str3[20] = " ** ";
uint8_t str4[20] = " ** ";
unsigned char str5[10];
uint8_t re_loc = 1; //存储位置标志位,1至5
extern uint8_t time_cnt; //外部变量,在xxxxxit.c,定时器计数值
extern uint8_t sec_08; //外部变量,在xxxxxit.c,0.8秒计时标志位
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
uint8_t x24c02_read(uint8_t address); //EEPROM读取函数
void x24c02_write(unsigned char address,unsigned char info); //EEPROM写入函数
void loc_change(void); //改变存储位置
void time_setting(void); //设置时间
void time_go_stop(void); //启动/结束定时器
void led_flash(void); //led灯闪烁
void PWM_gen(void); //PWM波产生函数
void res_time(void);//存储时间
void read_time(void);//读取时间
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
int main(void)
{/* USER CODE BEGIN 1 */TT_1.hour = 0;TT_1.min = 0;TT_1.sec = 0; /* USER CODE END 1 */HAL_Init();/* USER CODE BEGIN Init */I2CInit();LCD_Init();LCD_Clear(White);LCD_SetBackColor(White);LCD_SetTextColor(Black); /* 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_TIM3_Init();MX_TIM4_Init();/* USER CODE BEGIN 2 */read_time();sprintf((char *)str," %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);LCD_DisplayStringLine(Line4,str);sprintf((char *)str1," No %d",re_loc);LCD_DisplayStringLine(Line1,str1);LCD_DisplayStringLine(Line7," Standby"); /* USER CODE END 2 *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */loc_change();time_setting();time_go_stop();//PWM_gen();HAL_Delay(5);}/* USER CODE END 3 */
}
接下来文末按各个要求来编写各个功能的函数
1.存储位置改变功能的实现
这个功能是比较基础的,只是短按,很常规,但是要注意每次更换存储位置都要重新读取一次EEPROM的时间。代码如下:
void loc_change(void)
{if(HAL_GPIO_ReadPin(KEY_B1_GPIO_Port,KEY_B1_Pin) == 0){HAL_Delay(100);if(HAL_GPIO_ReadPin(KEY_B1_GPIO_Port,KEY_B1_Pin) == 0)//判断按键是否按下{re_loc++;if(re_loc > 5)//大于5变成1,循环{re_loc = 1;}read_time(); //变换了就要重新读取时间sprintf((char *)str1," No %d",re_loc); //下面都是LCD显示的函数LCD_DisplayStringLine(Line1,str1);sprintf((char *)str," %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);LCD_DisplayStringLine(Line4,str);}}
}
2.时间设置功能函数的编写
这一块是最复杂的,涉及了按键长短按,按键嵌套等,逻辑关系比较复杂,这里我讲解一下我的思路:首先,按下B2后程序进入一个死循环,在里面扫描判断是否有按键按下,根据不同的按键做出不同的反应,同时计算按键按下的时间,若大于0.8秒九认为是长按并做出相应动作。大体思路是这样,接下来上程序,更具我的思路和程序的注释应该可以理解我编程的思路。
void time_setting(void)
{int i = 0;if(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0){HAL_Delay(150);if(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0)//判断B2是否按下{LCD_DisplayStringLine(Line5,str4);LCD_DisplayStringLine(Line7," Setting");//显示Settingwhile(1) //进入死循环{if(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0){HAL_Delay(100);if(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0)//判断B2是否按下{i++; //这个是设置时分秒的标志位,0---秒,1---分,2---时if(i > 2){i = 0;} if( i == 0){LCD_DisplayStringLine(Line5,str4);}if( i == 1){LCD_DisplayStringLine(Line5,str3);}if( i == 2){LCD_DisplayStringLine(Line5,str2);}while(HAL_GPIO_ReadPin(KEY_B2_GPIO_Port,KEY_B2_Pin) == 0)//如果一直按着{HAL_TIM_Base_Start_IT(&htim2);//打开定时器if(sec_08 == 1)//判断0.8秒的标志位是否为1,为1就说明0.8秒到了{long_pushB2 = 1;//长按标志位置1break;}}HAL_TIM_Base_Stop_IT(&htim2);time_cnt = 0;sec_08 = 0;}}if(HAL_GPIO_ReadPin(KEY_B3_GPIO_Port,KEY_B3_Pin) == 0){HAL_Delay(100);if(HAL_GPIO_ReadPin(KEY_B3_GPIO_Port,KEY_B3_Pin) == 0)//判断B3是否按下{if(i == 0 )//根据i的值来决定增长的数{TT_1.sec++;}if(i == 1 ){TT_1.min++;}if(i == 2 ){TT_1.hour++;}sprintf((char *)str," %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);LCD_DisplayStringLine(Line4,str);while(HAL_GPIO_ReadPin(KEY_B3_GPIO_Port,KEY_B3_Pin) == 0)//如果一直按着{HAL_TIM_Base_Start_IT(&htim2);//打开定时器if(sec_08 == 1)//判断0.8秒的标志位是否为1,为1就说明0.8秒到了{long_pushB3 = 1;//长按标志位置1break;}}HAL_TIM_Base_Stop_IT(&htim2);time_cnt = 0;sec_08 = 0;while(long_pushB3 == 1 && HAL_GPIO_ReadPin(KEY_B3_GPIO_Port,KEY_B3_Pin) == 0)//长按标志位为1并且一直按着{if(i == 0 )//根据i的值来决定快速增长的数{TT_1.sec++;HAL_Delay(95);if(TT_1.sec == 60){TT_1.sec = 0;TT_1.min++;}}if(i == 1 ){TT_1.min++;HAL_Delay(95);if(TT_1.min == 60){TT_1.min = 0;TT_1.hour++;}}if(i == 2 ){TT_1.hour++;HAL_Delay(95);if(TT_1.hour == 24){TT_1.hour = 0;} }sprintf((char *)str," %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);//更新时间LCD_DisplayStringLine(Line4,str);}long_pushB3 = 0;} }if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0 || long_pushB2 == 1){HAL_Delay(60);if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0 || long_pushB2 == 1)//判断B4是否按下或者B2是否长按{if(long_pushB2 == 0)//B2没有长按{LCD_DisplayStringLine(Line5," ");LCD_DisplayStringLine(Line7," Running");go_stop = 1;//启动定时器break;}if(long_pushB2 == 1)//B2长按了{LCD_DisplayStringLine(Line5," ");LCD_DisplayStringLine(Line7," Standby");long_pushB2 = 0;res_time();//将时间存到EEPROM里面HAL_Delay(200);break;}}}}}}
}
3.定时器启动/结束函数
这一块的逻辑关系比较简单,就是 按下按键启动定时器,再按一下停止定时器,长按就回到初始页面,也有按键嵌套和按键长短按。但是这一块我遇到了一个问题,因为倒计时我用的是定时器中断,但是我的时间使用结构体定义的,用extern外部调用时发现不好使,查了资料发现结构体用extern的话很麻烦(我太懒了),所以我用的中断回调函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
用keil敲这个挺费劲,平时用vscode和clion的时候没这个感觉。定时器中断这块我就不多说了,直接上代码:
void time_go_stop(void)
{if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0){HAL_Delay(150);if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0)//判断按键B4是否按下{while(1)//进入死循环{if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0){HAL_Delay(50);if(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0)//判断按键4是否按下{go_stop = 1-go_stop;while(HAL_GPIO_ReadPin(KEY_B4_GPIO_Port,KEY_B4_Pin) == 0)//如果一直按着{HAL_TIM_Base_Start_IT(&htim2);if(sec_08 == 1)//判断0.8秒的标志位是否为1,为1就说明0.8秒到了{long_pushB4 = 1;//判断0.8秒的标志位是否为1,为1就说明0.8秒到了break;}}HAL_TIM_Base_Stop_IT(&htim2);time_cnt = 0;sec_08 = 0;}}if(long_pushB4 == 1)//长按就退出,回到初始页面{long_pushB4 = 0;LCD_DisplayStringLine(Line7," Standby");sprintf((char *)str," %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);LCD_DisplayStringLine(Line4,str);HAL_Delay(150);break;}if(go_stop == 1)//定时器启动{LCD_DisplayStringLine(Line7," Running");HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//启动PWMled_flash();//开启LED闪烁HAL_TIM_Base_Start_IT(&htim4);//启动定时器TIM4if(TT_1.hour == 0 && TT_1.min==0 && TT_1.sec == 0){HAL_TIM_Base_Start_IT(&htim4);go_stop = 0;}sprintf((char *)str," %02d:%02d:%02d",TT_1.hour,TT_1.min,TT_1.sec);LCD_DisplayStringLine(Line4,str);}if(go_stop == 0)//定时器结束{LCD_DisplayStringLine(Line7," Pause ");HAL_TIM_Base_Stop_IT(&htim4);time_cnt4 = 0;HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1);HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);}}}}
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//中断回调函数
{if(htim -> Instance == htim4.Instance)//如果时TIM4中断{time_cnt4++;if(time_cnt4 == 100)//1秒计时到{TT_1.sec--;if(TT_1.sec == 255)//uint8类型 0-1==255,补码运算{TT_1.sec = 59;TT_1.min--;}if(TT_1.min == 255){TT_1.min = 59;TT_1.hour--;}time_cnt4 = 0;}}
}
至此项目主要的代码就写好啦,实测也没有问题,这题就圆满地结束了。
总结
- 使用EEPROM时,需要在CUBEMX配置好对应的I2C管脚,因为官方例程里没有IO口的初始化。
- 连续写入/读取EEPROM数据时,需要在两次操作间进行一个延时,10ms足矣,应为I2C通信会有应答,不延时的话就没法连续写入/读取。
- 对结构体的外部调用不太熟悉,需要多多查资料学习。
第九届省赛工程文件下载:下载链接
备战蓝桥杯(4)——第九届蓝桥杯嵌入式省赛赛题实战相关推荐
- 【蓝桥杯】第九届蓝桥杯单片机国赛 代码程序
第九届蓝桥杯单片机国赛 程序 题目 hex文件 代码 工程文件 B站视频 更多资料 题目 历届的省赛和国赛的题目我已经在前面的文章(点击查看)里给大家分享了(网盘资源),需要的话,直接去下载,我在这里 ...
- 征战蓝桥 —— 2018年第九届 —— C/C++B组第3题——乘积尾零
标题:乘积尾零 如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零? 5650 4542 3554 473 946 4114 3871 9073 90 4329 2758 7949 ...
- 征战蓝桥 —— 2018年第九届 —— C/C++B组第1题——第几天
标题:第几天 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余内容. 代码 #include <iostre ...
- 第九届蓝桥杯java B组—第二题方格计数(详细介绍)
文章目录 1.题目如下 2.解题思路 3.详细讲解 4.代码实例 5.答案 最近正在练习蓝桥杯的习题,做到第九届篮球杯的第二题(方格计数)的时候,发现没有思路,就上网上查了一些资料,感觉说的不够详细, ...
- 使用JavaScript解答2018第九届蓝桥杯C/C++省赛A组试题
大三时参加过第七届蓝桥杯个人赛及团队赛,转眼已经两年,最近看了看第九届蓝桥杯试题,打算用JavaScript实现一下. 题目1 标题:分数 1/1 + 1/2 + 1/4 + 1/8 + 1/16 + ...
- 第jiu届蓝桥杯单片机省赛真题_第九届蓝桥杯单片机组省赛试题.pdf
第九届蓝桥杯单片机组省赛试题 "彩灯控制器"的程序设计与调试 (70 分) 一.基本要求 1.1 使用CT107D 单片机竞赛板,完成"彩灯控制器"功能的程序设 ...
- problem b: 一年中的第几天_第九届蓝桥杯B组试题
1. 标题:第几天 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余内容. "============= ...
- 第九届蓝桥杯C++B组
第九届蓝桥杯C++B组C++与python解法 1.标题:第几天 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余 ...
- 阶乘末尾蓝桥杯java_Java实现第九届蓝桥杯阶乘位数
阶乘位数 题目描述 小明维护着一个程序员论坛.现在他收集了一份"点赞"日志,日志共有N行.其中每一行的格式是: ts id 表示在ts时刻编号id的帖子收到一个"赞&qu ...
- 第九届蓝桥杯大赛软件类国赛
文章目录 第九届蓝桥杯大赛软件类国赛 国赛C++ A组 三角形面积 阅兵方阵 找假币 约瑟夫环 -- important 自描述序列 -- todo 采油 -- todo 国赛C++ B组 换零钞 激 ...
最新文章
- python爬取哔哩哔哩视频_荐爬取哔哩哔哩中的cosplay小视频
- 智能经济的动力,从人工智能到超级智能
- 打印100-200之间的素数
- 【报告分享】2019Z世代消费力洞察报告-腾讯.pdf
- zabbix 2.2节点批量安装
- 我找到一个价值5.5万美元的 Facebook OAuth账户劫持漏洞
- C#——已知两点及半径,求圆心坐标
- 国培 计算机远程培训心得,国培远程培训感言3篇
- python数据挖掘介绍
- 不要迷恋哥,哥只是个传说 - 生活至上,美容至尚!
- 1、ZigBee 开发教程之基础篇—ZigBee简介和学习方法
- 站内SEO第三篇:降低网站跳出率的7种有效方法
- java播放器_Java视频播放器的制作
- 用matlab给图片标记区域,MATLAB二值图像连通区域标记(两步法)
- 掌中题库刷题神奇微信小程序介绍
- 如何在 Ubuntu 和其他 Linux 发行版中启动、停止和重启服务
- 基于K-Means聚类算法对NBA球员数据的聚类分析
- 心动不如行动, 盘点职场实干者的10大标志
- codeforces 1090B切题记录
- 【演讲实录+视频】走近40+世界级AI专家!第三届中国人工智能大会资料分享(直播进行中_不断更新)