系列文章目录

备战蓝桥杯(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;}}
}

至此项目主要的代码就写好啦,实测也没有问题,这题就圆满地结束了。

总结

  1. 使用EEPROM时,需要在CUBEMX配置好对应的I2C管脚,因为官方例程里没有IO口的初始化。
  2. 连续写入/读取EEPROM数据时,需要在两次操作间进行一个延时,10ms足矣,应为I2C通信会有应答,不延时的话就没法连续写入/读取。
  3. 对结构体的外部调用不太熟悉,需要多多查资料学习。

第九届省赛工程文件下载:下载链接

备战蓝桥杯(4)——第九届蓝桥杯嵌入式省赛赛题实战相关推荐

  1. 【蓝桥杯】第九届蓝桥杯单片机国赛 代码程序

    第九届蓝桥杯单片机国赛 程序 题目 hex文件 代码 工程文件 B站视频 更多资料 题目 历届的省赛和国赛的题目我已经在前面的文章(点击查看)里给大家分享了(网盘资源),需要的话,直接去下载,我在这里 ...

  2. 征战蓝桥 —— 2018年第九届 —— C/C++B组第3题——乘积尾零

    标题:乘积尾零 如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零? 5650 4542 3554 473 946 4114 3871 9073 90 4329 2758 7949 ...

  3. 征战蓝桥 —— 2018年第九届 —— C/C++B组第1题——第几天

    标题:第几天 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余内容. 代码 #include <iostre ...

  4. 第九届蓝桥杯java B组—第二题方格计数(详细介绍)

    文章目录 1.题目如下 2.解题思路 3.详细讲解 4.代码实例 5.答案 最近正在练习蓝桥杯的习题,做到第九届篮球杯的第二题(方格计数)的时候,发现没有思路,就上网上查了一些资料,感觉说的不够详细, ...

  5. 使用JavaScript解答2018第九届蓝桥杯C/C++省赛A组试题

    大三时参加过第七届蓝桥杯个人赛及团队赛,转眼已经两年,最近看了看第九届蓝桥杯试题,打算用JavaScript实现一下. 题目1 标题:分数 1/1 + 1/2 + 1/4 + 1/8 + 1/16 + ...

  6. 第jiu届蓝桥杯单片机省赛真题_第九届蓝桥杯单片机组省赛试题.pdf

    第九届蓝桥杯单片机组省赛试题 "彩灯控制器"的程序设计与调试 (70 分) 一.基本要求 1.1 使用CT107D 单片机竞赛板,完成"彩灯控制器"功能的程序设 ...

  7. problem b: 一年中的第几天_第九届蓝桥杯B组试题

    1. 标题:第几天 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余内容. "============= ...

  8. 第九届蓝桥杯C++B组

    第九届蓝桥杯C++B组C++与python解法 1.标题:第几天 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余 ...

  9. 阶乘末尾蓝桥杯java_Java实现第九届蓝桥杯阶乘位数

    阶乘位数 题目描述 小明维护着一个程序员论坛.现在他收集了一份"点赞"日志,日志共有N行.其中每一行的格式是: ts id 表示在ts时刻编号id的帖子收到一个"赞&qu ...

  10. 第九届蓝桥杯大赛软件类国赛

    文章目录 第九届蓝桥杯大赛软件类国赛 国赛C++ A组 三角形面积 阅兵方阵 找假币 约瑟夫环 -- important 自描述序列 -- todo 采油 -- todo 国赛C++ B组 换零钞 激 ...

最新文章

  1. python爬取哔哩哔哩视频_荐爬取哔哩哔哩中的cosplay小视频
  2. 智能经济的动力,从人工智能到超级智能
  3. 打印100-200之间的素数
  4. 【报告分享】2019Z世代消费力洞察报告-腾讯.pdf
  5. zabbix 2.2节点批量安装
  6. 我找到一个价值5.5万美元的 Facebook OAuth账户劫持漏洞
  7. C#——已知两点及半径,求圆心坐标
  8. 国培 计算机远程培训心得,国培远程培训感言3篇
  9. python数据挖掘介绍
  10. 不要迷恋哥,哥只是个传说 - 生活至上,美容至尚!
  11. 1、ZigBee 开发教程之基础篇—ZigBee简介和学习方法
  12. 站内SEO第三篇:降低网站跳出率的7种有效方法
  13. java播放器_Java视频播放器的制作
  14. 用matlab给图片标记区域,MATLAB二值图像连通区域标记(两步法)
  15. 掌中题库刷题神奇微信小程序介绍
  16. 如何在 Ubuntu 和其他 Linux 发行版中启动、停止和重启服务
  17. 基于K-Means聚类算法对NBA球员数据的聚类分析
  18. 心动不如行动, 盘点职场实干者的10大标志
  19. codeforces 1090B切题记录
  20. 【演讲实录+视频】走近40+世界级AI专家!第三届中国人工智能大会资料分享(直播进行中_不断更新)

热门文章

  1. 菜鸟和老鸟都无法避免的问题——未将对象引用设置到对象的实例
  2. 51单片机驱动——DS18B20
  3. 罗斯蒙特8712ESR1A1N0M4流量变送器
  4. 直流可逆调速控制系统matlab,逻辑无环流直流可逆调速系统的Matlab仿真
  5. java poi 合并单元格
  6. 收藏模板:开发工程师提测申请模板
  7. vue 创建项目使用npm还是yarn
  8. 常见概率模型在市场中的应用
  9. 美学心得(第二百二十五集)罗国正
  10. 电阻分压可以当作电源供电吗