蓝桥杯嵌入式九届省赛

本文详细介绍了蓝桥杯嵌入式竞赛中关于RCC、SYS、按键、EEPROM、PWM、LCD及LED的配置和代码编写过程,包括LCD函数的修改、按键处理、定时器回调函数等关键步骤,旨在实现特定功能并展示项目结果。
摘要由CSDN通过智能技术生成

百度云:https://pan.baidu.com/s/1feVfEzPXrLFPB18flcg4Ew?pwd=6666 
提取码:6666

目录

1.赛题

2.分析及配置

2.1 RCC配置 

2.2 SYS配置

2.3 按键配置

2.4 EEPROM配置

2.5 PWM配置

2.6LCD及LED的配置

3.编写代码及解释

3.1 数据定义

 3.2 LCD的移植

 3.3 EEPROM的移植

3.4 界面显示的编写

3.5 按键

3.6 PWM的编写

 3.7 LED的编写

3.8 时间编写

3.9 初始化

4.结果展示

 5.结尾


1.赛题

 

 

2.分析及配置

直接浏览一遍观察用到的引脚和模块。

按键、LED、LCD、PWM、EEPROM。

看原理图直接配置即可,配置完毕直接生成代码。

2.1 RCC配置 

2.2 SYS配置

2.3 按键配置

2.4 EEPROM配置

配置为输出直接一直官方的历程即可。

 

2.5 PWM配置

题目要设置为1KHz,我们这里方便设置占空比把计数值设为100-1,时钟源为80MHz,它要经过两次相除的到1KHz。

80M/100/x=1K

则 x = 800

所以我们分频为800-1,计数值为100-1

占空比为80

2.6LCD及LED的配置

用到的引脚配置为输出即可,LCD的显示移植官方历程。

3.编写代码及解释

3.1 数据定义

typedef struct
{
	unsigned char Set_Time;//判断那个高亮
	int Time_H;//显示的时间
	int Time_M;//显示的分钟
	int Time_S;//显示的小时
	unsigned char Place;//存储的位置
	unsigned char State;//定时器运行状态
	unsigned char LED_Flag;
	unsigned char Time_Count;
}Data_TypeDef;
Data_TypeDef Data;

typedef struct
{
	unsigned int Value;//按键的值
	unsigned char State;//判断按键进行到那一步
	bool Short_Flag;//短按标志位
	bool Long_Flag;//长按标志位
	unsigned int Time;//按键按下的时间
}Key_TypeDef;
Key_TypeDef B[4];//四个按键

//屏幕显示的数字
extern uc16 ASCII_Table[];

 3.2 LCD的移植

找到官方给的LCD,打开它

         

放到自己对应的工程中。

放进去之后添加即可。

双击Application/User/Core找到你的lcd.c添加

用记事本打开main.c,可以找到初始化以及显示的函数,其他的功能到对应的.c和.h文件查看

 3.3 EEPROM的移植

找到HAL_08_EE打开,打开IncSrc找到i2c.hi2c.c放到自己工程对应的IncSrc即可

 

 

 然后双击添加

 打开记事本打开对应的main.c

 可以找到

3.4 界面显示的编写

我们更改了一个函数进行局部的显示,以及高亮的改变。

void Show(void)
{
	char arr[30];
	
	//显示存储位置
	sprintf(arr,"  No %d          ",Data.Place);
	LCD_DisplayStringLine(Line1, (uint8_t *)arr);
	//因为LCD和LED公用引脚所以这里加了一点空格,防止数据前面出现点点
	LCD_DisplayStringLine(Line4, (uint8_t *)"      ");
	//显示时间
	if(Data.Set_Time == 1)//高亮小时
	{
		//取十位和各位传入参数
		LCD_DrawChar1(Line4,6,Data.Time_H/10,Blue);
		LCD_DrawChar1(Line4,7,Data.Time_H%10,Blue);
	}
	else
	{
		LCD_DrawChar1(Line4,6,Data.Time_H/10,Black);
		LCD_DrawChar1(Line4,7,Data.Time_H%10,Black);
	}
	if(Data.Set_Time == 2)//高亮分钟
	{
		LCD_DrawChar1(Line4,10,Data.Time_M/10,Blue);
		LCD_DrawChar1(Line4,11,Data.Time_M%10,Blue);
	}
	else
	{
		LCD_DrawChar1(Line4,10,Data.Time_M/10,Black);
		LCD_DrawChar1(Line4,11,Data.Time_M%10,Black);
	}
	if(Data.Set_Time == 3)//高亮秒
	{
		LCD_DrawChar1(Line4,14,Data.Time_S/10,Blue);
		LCD_DrawChar1(Line4,15,Data.Time_S%10,Blue);
	}
	else
	{
		LCD_DrawChar1(Line4,14,Data.Time_S/10,Black);
		LCD_DrawChar1(Line4,15,Data.Time_S%10,Black);
	}
	//显示定时器状态
	if(Data.State == 0)//当定时器停止时
		LCD_DisplayStringLine(Line7, (uint8_t *)"      Standby      ");
	if(Data.State == 1)//当系统正在设置时间时
		LCD_DisplayStringLine(Line7, (uint8_t *)"      Setting      ");
	if(Data.State == 2)//当定时器运行时
		LCD_DisplayStringLine(Line7, (uint8_t *)"      Running      ");
	if(Data.State == 3)//定时器暂停时
		LCD_DisplayStringLine(Line7, (uint8_t *)"      Pause      ");
}

这里我们更改了官方给的LCD函数。点开lcd.c

找到

 复制一下改成

void LCD_DrawChar1(u8 Xpos, u16 Ypos,unsigned char Number,unsigned int BackColor)
{
	//Y = 319时显示在最左边,每减16就会右移1个字符
	Ypos = 319-16*Ypos;
	uc16 *c;
	//我们显示的是数组中的数据,这里16对应的是0
	c = &ASCII_Table[(16+Number) * 24];
	u32 index = 0, i = 0;
	u8 Xaddress = 0;
	
	Xaddress = Xpos;
	LCD_SetCursor(Xaddress, Ypos);
	
	for(index = 0; index < 24; index++)
	{
		LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
		for(i = 0; i < 16; i++)
		{
			if((c[index] & (1 << i)) == 0x00)
			{
				/*这里改成形参,因为我们要改变高亮*/
				LCD_WriteRAM(BackColor);
			}
			else
			{
				LCD_WriteRAM(White);
			}
		}
		Xaddress++;
		LCD_SetCursor(Xaddress, Ypos);
	}
}

因为是有高亮所以我们增加了一个形参,因为显示的是数字我们把原来的指针换成了数字。

根据

 这个函数我们知道了第三个参数原来是传的一个数组的地址,一个一个的试一试的话可以知道,Ascii=16时显示的是0,也就是17显示1,一次类推。或者直接看数组也会知道16对应的是0

所以我们

我们把指针写到了里面传入的参数还是一样的,Number就代表要显示的数字。

根据

这里我们就可以判断

因为要高亮所以我们也有颜色的形参

3.5 按键

找到

 

 右击进入对应的头文件,然后滑到最下面

找到

 找到void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);复制一下编写回调函数。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM4)
	{
		Time();//定时减时间
		LED();//定时闪烁LED
		B[0].Value = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		B[1].Value = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		B[2].Value = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		B[3].Value = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		
		for(int i=0;i<4;i++)
		{
			switch(B[i].State)
			{
				case 0:	if(B[i].Value == 0)
						{
							B[i].State = 1;
							B[i].Time = 0;
						}
				break;
				case 1:	if(B[i].Value == 0)
							B[i].State = 2;
						else
							B[i].State = 0;
				break;
				case 2:	if(B[i].Value == 1)
						{
						//10ms进来一次,0.8s = 800ms
						//进来80次,也就是10ms*80 = 800ms = 0.8s
							if(B[i].Time<80)
								B[i].Short_Flag = 1;
							else
								B[i].Long_Flag = 1;
							B[i].State = 0;
						}
						else
						{
							B[i].Time++;
							if(i == 2)
								if(B[2].Time>=80)//如果是按键B3长按就进入
									Set_Time();//进行时间连加
						}
				break;
				default:break;
			}
		}
	}
}

找到stm32g4xx_hal_gpio.c右击找到对应的头文件,滑倒最后可以找到HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

按键功能的编写

void Key_Handle(void)
{
	if(B[0].Short_Flag == 1)
	{
		//进来之后把能初始化的都初始化,以免出现bug
		Data.Set_Time = 0;//取消高亮
		Data.State = 0;//状态设为取消
		Data.Place++;
		if(Data.Place>5)
			Data.Place = 1;
		if(Data.Place == 1)
		{
			//读取EEPROM的值
			Data.Time_H = x24c02_read(0);HAL_Delay(10);
			Data.Time_M = x24c02_read(1);HAL_Delay(10);
			Data.Time_S = x24c02_read(2);
		}
		if(Data.Place == 2)
		{
			Data.Time_H = x24c02_read(3);HAL_Delay(10);
			Data.Time_M = x24c02_read(4);HAL_Delay(10);
			Data.Time_S = x24c02_read(5);
		}
		if(Data.Place == 3)
		{
			Data.Time_H = x24c02_read(6);HAL_Delay(10);
			Data.Time_M = x24c02_read(7);HAL_Delay(10);
			Data.Time_S = x24c02_read(8);
		}
		if(Data.Place == 4)
		{
			Data.Time_H = x24c02_read(9);HAL_Delay(10);
			Data.Time_M = x24c02_read(10);HAL_Delay(10);
			Data.Time_S = x24c02_read(11);
		}
		if(Data.Place == 5)
		{
			Data.Time_H = x24c02_read(12);HAL_Delay(10);
			Data.Time_M = x24c02_read(13);HAL_Delay(10);
			Data.Time_S = x24c02_read(14);
		}
		B[0].Short_Flag = 0;
	}
	if(B[1].Short_Flag == 1)
	{
		Data.State = 1;//当系统正在设置时间时
		Data.Set_Time++;
		if(Data.Set_Time>3)
			Data.Set_Time = 1;
		B[1].Short_Flag = 0;
	}
	if(B[1].Long_Flag == 1)
	{
		//数据进行清除
		Data.State = 0;//状态设置为取消
		Data.Set_Time = 0;//取消高亮
		if(Data.Place == 1)
		{
			//存储EEPROM
			x24c02_write(0,Data.Time_H);HAL_Delay(10);
			x24c02_write(1,Data.Time_M);HAL_Delay(10);
			x24c02_write(2,Data.Time_S);
		}
		if(Data.Place == 2)
		{
			x24c02_write(3,Data.Time_H);HAL_Delay(10);
			x24c02_write(4,Data.Time_M);HAL_Delay(10);
			x24c02_write(5,Data.Time_S);
		}
		if(Data.Place == 3)
		{
			x24c02_write(6,Data.Time_H);HAL_Delay(10);
			x24c02_write(7,Data.Time_M);HAL_Delay(10);
			x24c02_write(8,Data.Time_S);
		}
		if(Data.Place == 4)
		{
			x24c02_write(9,Data.Time_H);HAL_Delay(10);
			x24c02_write(10,Data.Time_M);HAL_Delay(10);
			x24c02_write(11,Data.Time_S);
		}
		if(Data.Place == 5)
		{
			x24c02_write(12,Data.Time_H);HAL_Delay(10);
			x24c02_write(13,Data.Time_M);HAL_Delay(10);
			x24c02_write(14,Data.Time_S);
		}
		B[1].Long_Flag = 0;
	}
	if(B[2].Short_Flag == 1)
	{
		Set_Time();
		B[2].Short_Flag = 0;
	}
	if(B[3].Short_Flag == 1)
	{
		switch(Data.State)
		{
			//当定时器停止时,打开定时器
			case 0:Data.State = 2;break;
			//如果为设置模式,直接运行
			//并且清除高亮
			case 1:Data.State = 2;Data.Set_Time=0;break;
			//当定时器运行时,暂停定时器
			case 2:Data.State = 3;break;
			//当定时器暂停时,运行定时器
			case 3:Data.State = 2;break;
			default:break;
		}
		B[3].Short_Flag = 0;
	}
	if(B[3].Long_Flag == 1)
	{
		//定时器关闭
		Data.State = 0;
		Data.Set_Time=0;//清除高亮
		B[3].Long_Flag = 0;
	}
}

3.6 PWM的编写

根据标志位开启和关闭PWM即可用。

void PWM(void)
{
	switch(Data.State)
	{
		//当定时器停止时,关闭PWM
		case 0:	HAL_TIM_PWM_Stop(&htim16,TIM_CHANNEL_1);break;
		//当定时器运行时,开启PWM
		case 2:HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);break;
		//当定时器暂停时,关闭PWM
		case 3:HAL_TIM_PWM_Stop(&htim16,TIM_CHANNEL_1);break;
		default:break;
	}
}

找到stm32g4xx_hal_tim.c打开,右击到对应的头文件,滑到最后。

 

对于参数的填写可以按F12跳转到该函数,上方就会出现参数。

 3.7 LED的编写

void LED(void)
{
	//定时器运行时
	if(Data.State == 2)
	{
		Data.LED_Flag++;
		//0.5s = 500ms >> 计数值为50
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
		if(Data.LED_Flag>50&&Data.LED_Flag<100)
			HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
		if(Data.LED_Flag>100)
			Data.LED_Flag = 0;
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	}
	//定时器取消和暂停时
	else if((Data.State == 0)||(Data.State == 3))
	{
		Data.LED_Flag = 0;
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	}
}

根据标志位改变灯的变化即可。

对应的函数到stm32g4xx_hal_gpio.c,右击到对应的头文件,滑倒最后。

 

和上面一样参数可以按F12进行跳转填写 。

3.8 时间编写

我们把时间递减放到了按键的中断里10ms一次计数一百就开始递减。

void Time(void)
{
	if(Data.State == 2)
	{
		
		if(Data.Time_H==0&&Data.Time_M==0&&Data.Time_S==0)
			Data.State = 0;
		else
		{
			Data.Time_Count++;
			//十毫秒进来一次,进100次为1s
			if(Data.Time_Count>100)
			{
				Data.Time_Count = 0;
				if(Data.Time_S>0)
					Data.Time_S--;
				else
				{
					if(Data.Time_M>0)
					{
						Data.Time_S = 59;
						Data.Time_M--;
					}
					else
					{
						if(Data.Time_H>0)
						{
							Data.Time_H--;
							Data.Time_M = 59;
							Data.Time_S = 59;
						}
						else
						{
							Data.Time_H = 0;
							Data.Time_M = 0;
							Data.Time_S = 0;
							Data.State = 0;
						}
					}
				}
			}
		}
	}
}

3.9 初始化

开启一些定时器和LCD的初始化,以及设置变量的初始值,和EEPROM的读取。

void Init(void)
{
	//开启定时器中断
	HAL_TIM_Base_Start_IT(&htim4);
	LCD_Init();
	LCD_Clear(Black);
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	//初始化存储位置
	Data.Place = 1;
	//读取时间
	Data.Time_H = x24c02_read(0);
	Data.Time_M = x24c02_read(1);
	Data.Time_S = x24c02_read(2);
	//因为我们显示时间的时候是局部的,所以这里直接显示 :
	LCD_DisplayStringLine(Line4, (uint8_t *)"        :   :       ");
}

 HAL_TIM_Base_Start_IT和上面的一样找到stm32g4xx_hal_tim.c对应的头文件在最后面。

4.结果展示

 

5.结尾

到此蓝桥杯嵌入式九届省赛完毕,其中需要修改LCD的一个函数所以在练习过程中,要了解一下官方给的历程方便改写。在编写过程中我们先移植再编写,写一个模块就验证一个模块。当全部写完了之后也要看一下是否存在bug,然后再进行改写。代码水平有限还请见谅。

如有错误还请指正,谢谢。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值