【STM32G4】备战蓝桥杯嵌入式---实战---第九届嵌入式省赛—“电子定时器”


前言

为了减少篇幅,各模块的初始化均在模块配置中,可以随时去翻看博客,还有function里面的函数,将不再罗列出来,只是截图展示。


一、题目

基本要求
1.1 使用 CT117E 嵌入式竞赛板,完成试题功能的程序设计与调试;
1.2 设计与调试过程中,可参考组委会提供的“资源数据包”;
1.3 Keil 工程文件以准考证号命名,完成设计后,提交完整、可编译的 Keil工程文件到服务器。

硬件框图
通过按键设置定时时间,启动定时器后,开始倒计时;计时过程中,可以暂停、取消定时器。在定时时间内,按要求输出 PWM 信号和控制 LED 指示灯。系统框图如图 1 所示:
在这里插入图片描述
功能描述

1、LCD 显示
LCD 显示存储位置、定时时间和当前状态。系统预留 5 个存储位置用于存储常用的定时时间。当定时器停止时,当前状态为 Standby;当系统正在设置时间时,当前状态为 Setting;当定时器运行时,当前状态为 Running,定时器暂停时,当前状态为 Pause。
在这里插入图片描述
2、按键功能
系统使用 4 个按键,B1、B2、B3 和 B4。
按键 B1 为存储位置切换键。每按一次,存储位置依次以 1、2、3、4、5循环切换,切换后定时时间设定为当前位置存储的时间。
按键 B2 为时间位置(时、分、秒)切换键和存储键。短按 B2 键进入时间设置状态。每次短按 B2 键,设置位置以时、分、秒循环切换,并突出显示(高亮)当前位置;设置完后,长按 B2 键(超过 0.8 秒)把设置的时间存储到当前的存储位置,并推出设置状态。如果是临时设置定时时间,则不需存储,直接按定时器启动按键。
按键 B3 为时、分、秒(按键 B2 确定当前位置)数字增加键。每短按B3 一次,数字递增一次;按住 B3 超过 0.8 秒,则数字快速递增,直到松开B3 按键。数字递增时,超出范围则从头循环。
按键 B4 为定时器启动键。短按 B4,定时器启动,开始运行;运行期间短按 B4,暂停定时器,再短按 B4,恢复定时器运行;长按 B4(超过 0.8 秒),则取消定时器运行,回到 Standby 状态。

3、PWM 输出和 LED 显示
定时器运行时,PA6 口输出 PWM 信号,同时 LED 灯(LD1)以 0.5 秒的频率闪烁。PWM 信号频率为 1KHz,占空比为 80%。
定时器停止或暂停时,停止输入 PWM 信号,LED 灯灭。

4、定时时间存储
设定好的定时时间存储在 EEPROM 中。
掉电重启后,显示存储位置 1 的定时时间。

二、模块初始化以及功能分析

1.模块的初始化

需要用的模块:LCD、IIC、四个按键、LED、TIM3(PA
6)

2.模块功能分析

LCD:显示-----》Display();
IIC:读取和存储信息到EEPROM-----》Write_Time();Read_Time();
按键:对参数进行调整-----》KEY_Handle();KEY_Scan();
LED:提示作用-----》LED();
TIM3:初始化
(本次赛题主要是考代码逻辑,特别是按键处理的方法。在模块方面用的比较少)

三、函数实现

1.void Display(void);

显示功能如下:
Location表示设置时间的地址,Location为4时,表示不设置时间。(单独点亮一个字符,方法如下,若有其他方法,欢迎评论)
mode表示定时器的四种状态:
0:Standby
1:Setting
2:Running
3:Pause

void Dispaly(void)
{
	sprintf((char *)str,"  N0 %d",signal + 1);
	LCD_DisplayStringLine(Line1, str);
	
	if(Location == 4)//设置部分
	{
		LCD_ClearLine(Line4);
		LCD_SetTextColor(Black);
		sprintf((char *)str,"       %02d: %02d: %02d",Now_Time[0],Now_Time[1],Now_Time[2]);
		LCD_DisplayStringLine(Line4, str);
	}
	else if(Location == 0)
	{
		LCD_SetTextColor(Black);
		sprintf((char *)str,"          : %.2d: %.2d",Now_Time[1],Now_Time[2]);
		LCD_DisplayStringLine(Line4, str);
		LCD_SetTextColor(Red);
		LCD_DisplayChar(Line4,210, Now_Time[0]/10 + 0x30);
		LCD_DisplayChar(Line4,195, Now_Time[0]%10 + 0x30);
		LCD_SetTextColor(Black);
	}
	else if(Location == 1)
	{
		LCD_SetTextColor(Black);
		sprintf((char *)str,"      %.2d:     : %.2d",Now_Time[0],Now_Time[2]);
		LCD_DisplayStringLine(Line4, str);
		LCD_SetTextColor(Red);
		LCD_DisplayChar(Line4,150, Now_Time[1]/10 + 0x30);
		LCD_DisplayChar(Line4,135, Now_Time[1]%10 + 0x30);
		LCD_SetTextColor(Black);
	}
	else if(Location == 2)
	{
		LCD_SetTextColor(Black);
		sprintf((char *)str,"      %.2d: %.2d:    ",Now_Time[0],Now_Time[1]);
		LCD_DisplayStringLine(Line4, str);
		LCD_SetTextColor(Red);
		LCD_DisplayChar(Line4,60, Now_Time[2]/10 + 0x30);
		LCD_DisplayChar(Line4,45, Now_Time[2]%10 + 0x30);
		LCD_SetTextColor(Black);
	}
	
	if(mode == 0)
		LCD_DisplayStringLine(Line7, "     Standby           ");
	else if(mode == 1)
		LCD_DisplayStringLine(Line7, "     Setting           ");
	else if(mode == 2)
		LCD_DisplayStringLine(Line7, "     Running           ");
	else if(mode == 3)
		LCD_DisplayStringLine(Line7, "     Pause           ");
}

2.void Read_Time(void);void Write_Time(void);

void Write_Time(void)
{
	IIC_Write(0x00,Hour[0]);	HAL_Delay(10);
	IIC_Write(0x01,Minute[0]);HAL_Delay(10);
	IIC_Write(0x02,Second[0]);HAL_Delay(10);
	
	IIC_Write(0x10,Hour[1]);	HAL_Delay(10);
	IIC_Write(0x11,Minute[1]);HAL_Delay(10);
	IIC_Write(0x12,Second[1]);HAL_Delay(10);
	
	IIC_Write(0x20,Hour[2]);	HAL_Delay(10);
	IIC_Write(0x21,Minute[2]);HAL_Delay(10);
	IIC_Write(0x22,Second[2]);HAL_Delay(10);
	
	IIC_Write(0x30,Hour[3]);	HAL_Delay(10);
	IIC_Write(0x31,Minute[3]);HAL_Delay(10);
	IIC_Write(0x32,Second[3]);HAL_Delay(10);
	
	IIC_Write(0x40,Hour[4]);	HAL_Delay(10);
	IIC_Write(0x41,Minute[4]);HAL_Delay(10);
	IIC_Write(0x42,Second[4]);HAL_Delay(10);
}
void Read_Time(void)
{
	Hour[0]   = IIC_Read(0x00);HAL_Delay(10);
	Minute[0] = IIC_Read(0x01);HAL_Delay(10);
	Second[0] = IIC_Read(0x02);HAL_Delay(10);
	
	Hour[1]   = IIC_Read(0x10);HAL_Delay(10);
	Minute[1] = IIC_Read(0x11);HAL_Delay(10);
	Second[1] = IIC_Read(0x12);HAL_Delay(10);
	
	Hour[2]   = IIC_Read(0x20);HAL_Delay(10);
	Minute[2] = IIC_Read(0x21);HAL_Delay(10);
	Second[2] = IIC_Read(0x22);HAL_Delay(10);
	
	Hour[3]   = IIC_Read(0x30);HAL_Delay(10);
	Minute[3] = IIC_Read(0x31);HAL_Delay(10);
	Second[3] = IIC_Read(0x32);HAL_Delay(10);
	
	Hour[4]   = IIC_Read(0x40);HAL_Delay(10);
	Minute[4] = IIC_Read(0x41);HAL_Delay(10);
	Second[4] = IIC_Read(0x42);HAL_Delay(10);
}

3.uint8_t KEY_Scan(uint8_t mode);

由于本实验牵扯到长按(大于0.8s)和短按,并且长按是需要实时操作对应变量,所以长按的操作需要检测到马上执行。不能等按完了才执行。所以这里用0.2s的延时,次数加到4以后就进行长按操作。
变量声明:
Now_Time是当前显示的时间。
Time是存储的时间。
考虑到有暂时设定时间且不进行存储的情况,后面也引入了Aim_Time,定时结束的目标时间。

uint8_t KEY_Scan(uint8_t mode)
{
	static uint8_t flag=1;
	uint8_t count = 0;
	if(mode)	flag = 1;
	if(flag &&(KEY_B1 == 0 || KEY_B2	== 0 || KEY_B3 == 0 ||	KEY_B4== 0 ))
	{
		HAL_Delay(10);
		flag = 0;
		if (KEY_B1 == 0)	return B1_Press;
		else if (KEY_B2 == 0)
		{
			while(KEY_B2 == 0){
				HAL_Delay(200);
				count++;//每0.2s加一
			}
			if(count <= 4)	return B2_Press;
			else	return B2_Press_Long;
		}
		else if (KEY_B3 == 0) 
		{
			while(KEY_B3 == 0){
				Dispaly();
				HAL_Delay(200);
				count++;//每0.2s加一
				if(count > 4)
				{
					Now_Time[Location] = Now_Time[Location]+1;
					Now_Time[0] = Now_Time[0]%24;
					Now_Time[1] = Now_Time[1]%60;
					Now_Time[2] = Now_Time[2]%60;
					
					Hour[signal]= Now_Time[0];
					Minute[signal] = Now_Time[1];
					Second[signal] = Now_Time[2];
				}
			}
			if(count <= 4)	return B3_Press;
		}
		else if (KEY_B4 == 0) 
		{
			while(KEY_B4 == 0){
				HAL_Delay(200);
				count++;//每0.2s加一
			}
			if(count <= 4)	return B4_Press;
			else return B4_Press_Long;
		}
	}else if(KEY_B1 == KEY_B2 == KEY_B3 == KEY_B4 == 1)	flag = 1;
	return 0;
}

4.void KEY_Handle(uint8_t key);

按键 B1 为存储位置切换键。每按一次,存储位置依次以 1、2、3、4、5循环切换,切换后定时时间设定为当前位置存储的时间。(signal 变化)
按键 B2 为时间位置(时、分、秒)切换键和存储键。短按 B2 键进入时间设置状态。每次短按 B2 键,设置位置以时、分、秒循环切换,并突出显示(高亮)当前位置;设置完后,长按 B2 键(超过 0.8 秒)把设置的时间存储到当前的存储位置,并推出设置状态。如果是临时设置定时时间,则不需存储,直接按定时器启动按键。(Location变化)
按键 B3 为时、分、秒(按键 B2 确定当前位置)数字增加键。每短按B3 一次,数字递增一次;按住 B3 超过 0.8 秒,则数字快速递增,直到松开B3 按键。数字递增时,超出范围则从头循环。(Now_Time变化)
按键 B4 为定时器启动键。短按 B4,定时器启动,开始运行;运行期间短按 B4,暂停定时器,再短按 B4,恢复定时器运行;长按 B4(超过 0.8 秒),则取消定时器运行,回到 Standby 状态。(mode 变化)

void KEY_Handle(uint8_t Key)
{
	if(Key == B1_Press)
	{
		Location = 4;
		signal++;
		signal = signal % 5;
		Now_Time[0] = Hour[signal];
		Now_Time[1] = Minute[signal];
		Now_Time[2] = Second[signal];
	}
	else if(Key == B2_Press)
	{
		mode = 1;
		if(Location == 4)
			Location = 0;
		else
		{
			Location++;
			Location = Location % 3;
		}
	}
	else if(Key == B2_Press_Long)
	{
		Location = 4;
		mode = 0;
		Hour[signal]= Now_Time[0];Minute[signal] = Now_Time[1];Second[signal] = Now_Time[2];
		Write_Time();
	}
	else if(Key == B3_Press)
	{
		Now_Time[Location] = Now_Time[Location]+1;
		Now_Time[0] = Now_Time[0]%24;
		Now_Time[1] = Now_Time[1]%60;
		Now_Time[2] = Now_Time[2]%60;
	}
	else if(Key == B4_Press)
	{
		if(mode == 1)
		{
			mode = 2;
			HAL_TIM_Base_Start(&htim3);
			Location = 4;
			Aim_Time[signal] = Now_Time[0];
			Aim_Time[signal] = Now_Time[1];
			Aim_Time[signal] = Now_Time[2];
			Now_Time[0] = Now_Time[1] = Now_Time[2] = 0;
		}
		else if(mode == 2)	mode = 3;//暂停
		else 
		{
			Aim_Time[signal] = Hour[signal];
			Aim_Time[signal] = Minute[signal];
			Aim_Time[signal] = Second[signal];
			mode = 2;
			HAL_TIM_Base_Start(&htim3);
		}
	}
	else if(Key == B4_Press_Long)
	{
		mode = 0;
	}
}

5.void LED(void);void LED_OFF(void);

void LED(void);每进入一次LD1变换一次(TOGGLE函数似乎用不了,本人也不清楚)
void LED_OFF(void);关闭所有LED

void LED(void)
{
	static uint16_t i;
	i++;
	if(i % 2)HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET);
	else 
	{
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
	}
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15
                          |GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_OFF(void)
{
	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,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

6.void ADD(void);

时间加1函数
定时到目标时间的结束标志mode = 0;

void ADD(void)
{
	Now_Time[2]++;
	if(Now_Time[2] >= 60)
	{
		Now_Time[2] = 0;
		Now_Time[1]++;
	}
	if(Now_Time[1] >= 60)	
	{
		Now_Time[1] = 0;
		Now_Time[0]++;
	}
	Now_Time[0] = Now_Time[0] % 24;
	
	if(Now_Time[0] == Aim_Time[0] && Now_Time[1] ==Aim_Time[1] && Now_Time[2] == Aim_Time[2])	mode = 0;
}

7.int main(void);

初始化;

在这里插入图片描述

LCD_Init();
LCD_Clear(White);
LCD_SetTextColor(Black);

I2CInit();

IIC_Write(0x00,0);HAL_Delay(10);
IIC_Write(0x01,0);HAL_Delay(10);
IIC_Write(0x02,0);HAL_Delay(10);

IIC_Write(0x10,0);HAL_Delay(10);
IIC_Write(0x11,1);HAL_Delay(10);
IIC_Write(0x12,0);HAL_Delay(10);

IIC_Write(0x20,5);HAL_Delay(10);
IIC_Write(0x21,20);HAL_Delay(10);
IIC_Write(0x22,00);HAL_Delay(10);

IIC_Write(0x30,13);HAL_Delay(10);
IIC_Write(0x31,14);HAL_Delay(10);
IIC_Write(0x32,52);HAL_Delay(10);

IIC_Write(0x40,23);HAL_Delay(10);
IIC_Write(0x41,59);HAL_Delay(10);
IIC_Write(0x42,59);HAL_Delay(10);

LED_OFF();
HAL_Delay(100);
Read_Time();
HAL_Delay(1000);

while(1);

在这里插入图片描述

	Dispaly();
	key = KEY_Scan(0);
	KEY_Handle(key);
	if(mode == 2 && num == 1)
	{
		LED();	
	}
	else if(mode != 2)
	{
		HAL_TIM_Base_Stop(&htim3);
		LED_OFF();
	}
	
	if(nCount == 1 && mode == 2)
	{
		nCount = 0;
		ADD();		
	}

8.void SysTick_Handler(void);

上面的num和nCount在哪变了,在哪计时呢?
就在滴答定时器里面咯。
大家打开stm32g4xx_it.c
在这里插入图片描述

if(mode == 2) 
{
	i++;j++;
}
if(i >= 1000)
{
	nCount = 1;
	i = 0;
}
if(j >= 500)
{
	num = 1;
	j = 0;
}

9.定时器输出PWM(Cubemx);

PWM未测试,有问题欢迎评论。
在这里插入图片描述
在这里插入图片描述


总结

以上就是本次赛题的程序设计部分(变量定义部分,本人没有罗列出来嗷),在代码简洁方面本人做的不是很好,如有更好的设计方法可以在评论区留言。互相学习。👍

代码改了好久,比第一次写的时候精简多了。。。太难了。。。(PS: 仍有不足,欢迎指导)

还等什么,赶快自己写一下,测试一下代码。实测有效,嘿嘿🤭

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值