【蓝桥杯嵌入式】第七届省赛 - 模拟液位检测告警系统

代码开源,Gitee自取

代码开源,Gitee自取

代码开源,Gitee自取


目录

0 前言

1 展示

1.1 源码

1.2 演示视频

1.3 题目展示

2 工程配置

3 资源配置&代码实现

3.1 定时器

3.2 液位检测

3.3 液位阈值设定

3.4 液位阈值设定

3.5 串口通信

3.6 LCD显示

3.7 LED显示

3.8 逻辑处理

3.9 main函数

原版

修改

4 电路设计

5 总结


0 前言

  • 开发板:CT117E-M4(STM32G431RBT6)
  • 软件环境:CubeMX + Keil5
  • 涉及题目:第十三届蓝桥杯嵌入式省赛第二场真题

1 展示

1.1 源码

Gitee链接:

CSDN下载链接:https://download.csdn.net/download/weixin_63135906/89250289?spm=1001.2014.3001.5501

有需要的同学可以下载源码,对照本文说明使用! 

1.2 演示视频

B站链接:

1.3 题目展示

考察内容:

LED、LCD、按键、ADC、RTC、E2PROM

需要用到的外设比较多,但是逻辑非常简单


2 工程配置

下载

使能HSE

失能LSE,LSE不需要配置,如果配置了,那么PC14、PC15就不能配置了,这两个引脚还要控制LED,这里有冲突,所以不能配置LSE!!!

GPIO

时钟设置

24M晶振,80M主频

24M晶振一定要设置好,不然会卡在初始化里一直过不去!!!


3 资源配置&代码实现

3.1 定时器

我这里先配置定时器,分析题目

找一个定时器专门用作按键扫描,再用一个定时器处理各种标志位~

定时器3配置

定时器4配置

我的工程里时钟都是配置了80M,这里预分频系数给80-1,计数值给10000-1

计算一下

80M / 80 / 10000 = 100Hz = 10ms

10ms进入一次中断

我用了定时器4中断做按键的扫描,定时器3中断做各种标志位的处理~

定时器中断回调函数

//定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	static unsigned char tim4_count;
	static unsigned char adc_cnt;	//1S内检测ADC的标志位
	static unsigned char led1_cnt, led2_cnt, led3_cnt;
	//每次进入中断回调函数都会判断是哪个中断
	if (htim->Instance == TIM3)	//如果是定时器3的中断 - ADC - LED
	{
		adc_cnt ++ ;
		if (adc_cnt >= 65)	//650ms
		{
			flag.adc_readtime_ok = 1;
			adc_cnt = 0;
		}
		
		// 处理LED状态指示
		// 这里时间也没有自加,笨比……
		if (++ led1_cnt >= 100)	//1S
		{
			flag.LED1_State = ! flag.LED1_State;
			led1_cnt = 0;	//记得这里一定要软件复位啊,又忘记了,大聪明蛋!
		}
		
		if (++ led2_cnt >= 20)		//0.2S
		{
			flag.LED2_State = ! flag.LED2_State;
			
			if (flag.YW_IS_Change_forLED == 1)	//如果液位变化
			{
				if (++ flag.LED2_ON_Cnt >= 10)	//闪烁5次以上
				{
					flag.YW_IS_Change_forLED = 0;
					 flag.LED2_ON_Cnt = 0;
				}
			}
			led2_cnt = 0;
		}
		if (++ led3_cnt >= 20)		//0.2S
		{
			flag.LED3_State = ! flag.LED3_State;
			
			if (flag.IS_Receive == 1)	//收到查询指令了
			{
				if ( ++ flag.LED3_ON_Cnt >= 10)	//闪烁5次以上
				{
					flag.IS_Receive = 0;	//先清零接收标志位
					flag.LED3_ON_Cnt = 0;	//再清零闪烁次数,方便下一次闪烁指示
				}
			}
			led3_cnt = 0;
		}
	}
	
	if (htim->Instance == TIM4)	//如果是定时器4的中断 - 按键
	{
		tim4_count++;
		
		if (tim4_count >= 50)	//500ms
		{
			//led指示灯
		}		
		/* 按键 */
		key[0].Key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);	//按键1 PB0
		key[1].Key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);	//按键2 PB1
		key[2].Key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);	//按键3 PB2
		key[3].Key_sta = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);	//按键4 PA0
		
		//轮询按键
		for(int i = 0; i < 4; i ++)
		{
			//状态机
			switch (key[i].judge_sta)
			{
				case 0:
				{
					if (key[i].Key_sta == 0)	//如果按键按下了
					{
						key[i].judge_sta = 1;	//状态置1
						key[i].key_time = 0;	//按键按下的时间,初次按下先清零
					}
						
				}
				break;
				
				case 1:	//按键消抖
				{
					if (key[i].Key_sta == 0)	//如果状态保持按下
					{
						key[i].judge_sta = 2;	//跳转到状态2
						
					}
					else
						key[i].judge_sta = 0;	//否则按键按下
				}
				break;
				
				case 2:
				{
					if (key[i].Key_sta == 1)	//如果按键松手
					{
						key[i].judge_sta = 0;	//循环回到最开始

						if (key[i].key_time < 70)	//短按键
						{
							key[i].single_flag = 1;	//短按标志位置0,松手确认按键按下
							key[i].long_flag = 0;	//长按标志位置0
							key[i].key_time = 0;
						}
					}
					else 
					{
						key[i].key_time++;	//按下的时候,一直10ms计时++
						if (key[i].key_time >= 70)
						{							
							key[i].long_flag = 1;	//长按标志位置1
							key[i].single_flag = 0;	//短按标志位置0
						}
						
					}
				}
				break;

			}
		}
	}
}

定时器4用作按键扫描,这里的代码是我学习B站up主01stdio的~可以实现长按和短按

定时器3的操作后面慢慢讲

在定时器中断回调函数里要注意计时变量加加,时间到了也要清零,很久不写代码就会忘记这些基本操作

3.2 液位检测

通过电位器R37模拟液位传感器输出电压信号,设备以 1 秒为间隔采集 R37 输出电压, 并与用户设定的液位阈值进行比较。假定液位高度与 R37 输出电压之间具有正比例关系: H = VR37*K,当 VR37=3.3V 时,对应液位高度为 100cm。通过液晶显示当前的液位高度、 传感器(R37)输出状态和液位等级,液位检测显示界面如图 1 所示:

注意题目描述的关键词:

  • 1S为间隔采集R37电压
  • 液位高度与 R37 输出电压之间具有正比例关系: H = VR37*K
  • VR37=3.3V 时,对应液位高度为 100cm

 在定时器3的中断里,每隔650ms读取一次

每相隔650ms,将flag.adc_readtime_ok标志位置1

adc_cnt ++ ;
if (adc_cnt >= 65)	//650ms
{
	flag.adc_readtime_ok = 1;
	adc_cnt = 0;
}
	if (flag.adc_readtime_ok == 1)
	{
		Vol_1S = getADC(&hadc2);	//ADC采集一次
		height = (uint16_t)(Vol_1S / 3.3f * 100);
		flag.adc_readtime_ok = 0;
	}

ADC读取函数 

配置ADC引脚 PB15、PB12

新建关于ADC的.c.h 保存在BSP中,名称不要为adc,稍微修改一下

避免和HAL库自带的adc.c重复 - 报错

使用方法:定时读取调用ADC读取,刷新不用太快,这里我给了800ms

double getADC(ADC_HandleTypeDef *pin)
{
	uint adc;						//声明变量,ADC读取出来的原始值
	HAL_ADC_Start(pin);				//开启ADC
	adc = HAL_ADC_GetValue(pin);	//read adc
	return adc*3.355/4096;			//12位的ADC电压采集
}

这里有必要说明一下,返回值应该是return adc*3.3/4096;

虽然理论上的ADC电压是3.3V,但是事实上并不会真正的测到3.3V(实测滑动变阻器的拉满采集的最大电压值一直都是3.25V,经过我实际测试了几块蓝桥杯官方的开发板,都是3.25V)那么如果照着正常公式计算的话,最后转换的高度最大将会是98而不会到达100,因为就会面临着向下取整的问题,所以也无法满足保持高电平的要求,这点如果使用机器阅卷的话,不知道会怎么判。

所以我这里修改代码补偿一下硬件问题。虽然……但是……我现在就这样干了

电压显示,没有写题目给的中文 

// 显示ADC
sprintf(LCD_Show_text, "  ADC:    %.2fV ", Vol_1S);
LCD_DisplayStringLine(Line5, (uint8_t *)LCD_Show_text);

3.3 液位阈值设定

设备可设定三个液位阈值,对应四个液位等级,阈值由用户通过按键输入,设备保存阈值,并根据此阈值判断液位等级,假定用户输入的三个液位阈值为 10cm、20cm 和 30cm, 液位高度与液位等级的对应关系如下:

  1. 液位高度≤10cm 时,液位等级为 0;
  2. 10cm<液位高度≤20cm 时,液位等级为 1;
  3. 20cm<液位高度≤30cm 时,液位等级为 2;
  4. 液位高度>30cm 时,液位等级为 3。

设备初始液位阈值分别为 30cm、50cm 和 70cm,用户修改阈值后,设备应将此参数保存在 E2PROM 中,当设备重新上电时,可从 E2PROM 中获取

注意这里埋了个大坑:当设备重新上电时,可从 E2PROM 中获取。解决方法就是用E2PROM识别是否是第一次上电,后面讲这个~

3.4 液位阈值设定

  • B1 按键:“设置”按键,按下后进入阈值设定界面(如图 2 所示),再次按下 B1 按键时 退出设置界面,保存用户设定的结果到 E2PROM,并返回图 1 所示的液位检测界面。
  • B2 按键:切换选择 3 个待修改的阈值,被选中的阈值应突出显示
  • B3 按键:“加”按键,按下后,被选择的阈值增加 5cm,增加到 95cm 为止。
  • B4 按键:“减”按键,按下后,被选择的阈值减少 5cm,减少到 5cm 为止。

按键处理函数 void key_proc(void)

void key_proc(void)
{
	if(key[0].single_flag == 1)	//如果按键1按下 短按
	{
		key[0].single_flag = 0;	//按键清零
		lcd_view ++;
		if (lcd_view == 2) lcd_view = 0;//界面切换.两个界面
		LCD_Clear(White);//清LCD屏
	}
	if(key[1].single_flag == 1)	//如果按键PB2按下 短按
	{
		key[1].single_flag = 0;	//按键清零
		if (lcd_view == 1)	//如果是阈值设置界面
		{
			//切换选择 3 个待修改的阈值,被选中的阈值应突出显示
			select_param_set ++;	//选择参数设置 ++
			if (select_param_set == 3) select_param_set = 0;//参数设置切换.3个参数,一个保存写入
		}
	}
	if(key[2].single_flag == 1)	//如果按键PB3按下 短按
	{
		key[2].single_flag = 0;	//按键清零
		if (select_param_set == 0)		//设置第一个参数
		{
			Threshold_1 += 5;
			if (Threshold_1 >= 95)
				Threshold_1 = 95;
		}
		else if (select_param_set == 1)	//设置第二个参数
		{
			Threshold_2 += 5;			
			if (Threshold_2 >= 95)
				Threshold_2 = 95;
		}
		else if (select_param_set == 2)	//设置第三个参数
		{
			Threshold_3 += 5;			
			if (Threshold_3 >= 95)
				Threshold_3 = 95;
		}
	}	
	if(key[3].single_flag == 1)	//如果按键PB4按下 短按
	{
		key[3].single_flag = 0;	//按键清零		
		if (select_param_set == 0)		//设置第一个参数
		{
			Threshold_1 -= 5;			
			if (Threshold_1 <= 5)
				Threshold_1 = 5;
		}
		else if (select_param_set == 1)	//设置第二个参数
		{
			Threshold_2 -= 5;			
			if (Threshold_2 <= 5)
				Threshold_2 = 5;
		}
		else if (select_param_set == 2)	//设置第三个参数
		{
			Threshold_3 -= 5;			
			if (Threshold_3 <= 5)
				Threshold_3 = 5;
		}
	}
}

按键部分比较简单,注意越界判断,注意界面切换要清LCD屏

LCD高亮,这部分是我自己研究的,没有copy别人的代码,写的有些简陋了些

3.5 串口通信

每次接收一个字节

串口接收部分,没什么好讲的,看注释

这里需要使用sprintf函数,可以去菜鸟教程学一下

//串口的接收 回调函数
char rxdata[50];
uint8_t RX_Str_Data;
unsigned char rx_pointer;	//自己定义的指针,判断接收到哪了

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART1) // 如果是USART1
	{
		//数据处理部分

			if(RX_Str_Data == 'C')	//只接收了一个字符,用这个变量判断即可
			{
				flag.IS_Receive = 1;	//收到查询指令了
				sprintf(USART_tx_string, "C:H%d+L%d\r\n", height, yewei_level);
				HAL_UART_Transmit(&huart1, (uint8_t *)USART_tx_string, strlen(USART_tx_string), 50);
			}
			else if (RX_Str_Data == 'S')
			{
				flag.IS_Receive = 1;	//收到查询指令了
				sprintf(USART_tx_string, "S:TL%d+TM%d+TH%d\r\n", Threshold_1, Threshold_2, Threshold_3);
				HAL_UART_Transmit(&huart1, (uint8_t *)USART_tx_string, strlen(USART_tx_string), 50);
			}

		rxdata[rx_pointer++] = RX_Str_Data;				//接收到的字符串存放在这里
		HAL_UART_Receive_IT(&huart1, &RX_Str_Data, 1);	//最后这个参数只能写1
	}
}

3.6 LCD显示

void disp_proc(void)
{
	if (lcd_view == 0)
	{
		sprintf(LCD_Show_text, "     Liquid Level ");
		LCD_DisplayStringLine(Line1, (uint8_t *)LCD_Show_text);
		sprintf(LCD_Show_text, "  Height: %dcm ", height);
		LCD_DisplayStringLine(Line3, (uint8_t *)LCD_Show_text);

		// 显示ADC
		sprintf(LCD_Show_text, "  ADC:    %.2fV ", Vol_1S);
		LCD_DisplayStringLine(Line5, (uint8_t *)LCD_Show_text);
		
		sprintf(LCD_Show_text, "  Level: %d ", yewei_level);
		LCD_DisplayStringLine(Line7, (uint8_t *)LCD_Show_text);
		
		//测试用例,查看数据的,写完用不到了
//		sprintf(LCD_Show_text, "  LED:%d  %d  Rx:%c    ", flag.LED3_State,flag.LED3_ON_Cnt, RX_Str_Data);
//		LCD_DisplayStringLine(Line8, (uint8_t *)LCD_Show_text);
	}
	//阈值设定界面
	if (lcd_view == 1)
	{
		if (select_param_set == 0)		//第一个参数
		{
			sprintf(LCD_Show_text, "  Parameter Setup   ");
			LCD_DisplayStringLine(Line1, (uint8_t *)LCD_Show_text);
			
			LCD_SetTextColor(Red);		// 设置字体颜色
			sprintf(LCD_Show_text, " Threshold 1:  %dcm   ", Threshold_1);
			LCD_DisplayStringLine(Line4, (uint8_t *)LCD_Show_text);
			
			LCD_SetTextColor(Blue);		// 设置字体颜色			
			sprintf(LCD_Show_text, " Threshold 2:  %dcm   ", Threshold_2);
			LCD_DisplayStringLine(Line6, (uint8_t *)LCD_Show_text);
			sprintf(LCD_Show_text, " Threshold 3:  %dcm   ", Threshold_3);
			LCD_DisplayStringLine(Line8, (uint8_t *)LCD_Show_text);
		}
		
		else if (select_param_set == 1)	//第二个参数
		{
			sprintf(LCD_Show_text, "  Parameter Setup   ");
			LCD_DisplayStringLine(Line1, (uint8_t *)LCD_Show_text);
			
			LCD_SetTextColor(Blue);		// 设置字体颜色
			sprintf(LCD_Show_text, " Threshold 1:  %dcm   ", Threshold_1);
			LCD_DisplayStringLine(Line4, (uint8_t *)LCD_Show_text);
			
			LCD_SetTextColor(Red);		// 设置字体颜色
			sprintf(LCD_Show_text, " Threshold 2:  %dcm   ", Threshold_2);
			LCD_DisplayStringLine(Line6, (uint8_t *)LCD_Show_text);
			
			LCD_SetTextColor(Blue);		// 设置字体颜色
			sprintf(LCD_Show_text, " Threshold 3:  %dcm   ", Threshold_3);
			LCD_DisplayStringLine(Line8, (uint8_t *)LCD_Show_text);
		}
		else if (select_param_set == 2)	//第三个参数
		{
			sprintf(LCD_Show_text, "  Parameter Setup   ");
			LCD_DisplayStringLine(Line1, (uint8_t *)LCD_Show_text);
			
			LCD_SetTextColor(Blue);		// 设置字体颜色
			sprintf(LCD_Show_text, " Threshold 1:  %dcm   ", Threshold_1);
			LCD_DisplayStringLine(Line4, (uint8_t *)LCD_Show_text);
			
			LCD_SetTextColor(Blue);		// 设置字体颜色
			sprintf(LCD_Show_text, " Threshold 2:  %dcm   ", Threshold_2);
			LCD_DisplayStringLine(Line6, (uint8_t *)LCD_Show_text);
			
			LCD_SetTextColor(Red);		// 设置字体颜色
			sprintf(LCD_Show_text, " Threshold 3:  %dcm   ", Threshold_3);
			LCD_DisplayStringLine(Line8, (uint8_t *)LCD_Show_text);
			LCD_SetTextColor(Blue);		// 设置字体颜色
		}
	}
}

在不同的界面下显示不同的内容,用到一个标志位即可实现

3.7 LED显示

配置PC8~PC15、PD2

该板子是低电平点亮,8个led灯使用的是高8位所以需要左移8位,led等于几就是将高8位中第几位设置成低电平即点亮,由于led与lcd复用引脚最后打开锁存器让值被写入之后,立刻关闭锁存器防止影响LCD

void LED_proc(void)
{
	// LED1 数据
	if (flag.LED1_State == 0)
	{
		flag.LED1_Data = 0x00;	//0000 0000
	}
	else if (flag.LED1_State == 1)
	{
		flag.LED1_Data = 0x01;	//0000 0001
	}
	
	// LED2 数据
	if (flag.YeWei_IS_change == 1)	//如果液位变化
		flag.YW_IS_Change_forLED = 1;	//液位变化,LED的标志位置1
	
	if (flag.LED2_State == 0)
	{
		flag.LED2_Data = 0x00;	//0000 0000
	}
	else if (flag.LED2_State == 1)
	{
		if (flag.YW_IS_Change_forLED == 1)	//如果液位变化
		{
			flag.LED2_Data = 0x02;	//0000 0010
		}
	}

	// LED3 数据
	if (flag.LED3_State == 0)
	{
		flag.LED3_Data = 0x00;	//0000 0000
	}
	else if (flag.LED3_State == 1)
	{
		if (flag.IS_Receive == 1)	//收到查询指令了
		{
			flag.LED3_Data = 0x04;	//0000 0100
		}		
	}
	
	
//	if (flag.IS_Receive == 1)	//收到查询指令了
//	{
//		if (flag.LED3_State == 0)	//
//		{
//			flag.LED3_Data = 0x00;	//0000 0000
//		}
//		else if (flag.LED3_State == 1)
//		{
//			flag.LED3_Data = 0x04;	//0000 0100
//			 ++ flag.LED3_ON_Cnt;
//			if (flag.LED3_ON_Cnt > 5)	//闪烁5次以上
//			{
//				flag.IS_Receive = 0;	//先清零接收标志位
//				flag.LED3_ON_Cnt = 0;	//再清零闪烁次数,方便下一次闪烁指示
//			}
//		}
//	}		

	LED_Disp(flag.LED1_Data | flag.LED2_Data | flag.LED3_Data);
}

分析:

  • LED1以1S为间隔亮灭闪烁:亮1S、灭1S、亮1S、灭1S……
  • LED2以0.2S为间隔闪烁5次:0.2S亮灭一次,一秒内亮灭5次
  • LED3以0.2S为间隔闪烁5次:0.2S亮灭一次,一秒内亮灭5次

用几个标志位控制LED的闪烁频率和次数,都是51单片机的基础操作,这里我就不多讲了,具体可以看代码~ 

这里用三个标志位,分别控制三个灯,最后调用LED显示函数,这样操作LED,他们之间互不影响

3.8 逻辑处理

void control_fun(void)
{
	if (flag.adc_readtime_ok == 1)
	{
		Vol_1S = getADC(&hadc2);	//ADC采集一次
		height = (uint16_t)(Vol_1S / 3.3f * 100);
		flag.adc_readtime_ok = 0;
	}
	
	//液位等级判断
	if (height <= Threshold_1)
		yewei_level = 0;
	else if (height > Threshold_1 && height <= Threshold_2)
		yewei_level = 1;
	else if (height > Threshold_2 && height <= Threshold_3)
		yewei_level = 2;
	else if (height > Threshold_3)
		yewei_level = 3;
	
	//液位等级变化 判断,串口发送数据
	if (yewei_level - flag.last_YW_state == 0)		//液位等级没有变化
	{
		flag.YeWei_IS_change = 0;				//液位是否变化标志位置0
	}
	else if (yewei_level - flag.last_YW_state > 0)	//液位 上升
	{
		flag.YeWei_IS_change = 1;				//液位是否变化标志位置1
		flag.YeWei_change_Dir = 1;				//上升趋势
	}
	else if (yewei_level - flag.last_YW_state < 0)	//液位 下降
	{
		flag.YeWei_IS_change = 1;				//液位是否变化标志位置1
		flag.YeWei_change_Dir = 2;				//下降趋势
	}
	flag.last_YW_state = yewei_level;
	
	if (flag.YeWei_IS_change == 1)	//如果液位变化
	{
//		flag.YW_IS_Change_forLED = 1;	//液位变化,LED的标志位置1,放到后面的函数写了
		if (flag.YeWei_change_Dir == 1)	//上升趋势
		{
			sprintf(USART_tx_string, "A:H%d+L%d+U\r\n", height, yewei_level);
			HAL_UART_Transmit(&huart1, (uint8_t *)USART_tx_string, strlen(USART_tx_string), 50);
		}
		else if (flag.YeWei_change_Dir == 2)	//下降趋势
		{
			sprintf(USART_tx_string, "A:H%d+L%d+D\r\n", height, yewei_level);
			HAL_UART_Transmit(&huart1, (uint8_t *)USART_tx_string, strlen(USART_tx_string), 50);
		}
	}

	//EEPROM读写
	if (lcd_view == 0)
	{
		if (flag.E2PROM_Write == 1)
		{	
			//E2PROM 写入

			EEPROM_WriteByte(1, Threshold_1);	//写入阈值1
			HAL_Delay(10);	//延时10ms
			
			EEPROM_WriteByte(2, Threshold_2);	//写入阈值2
			HAL_Delay(10);	//延时10ms
			
			EEPROM_WriteByte(3, Threshold_3);	//写入阈值3
			HAL_Delay(10);	//延时10ms
			
			flag.E2PROM_Write = 0;	//复位,写一次即可,放在后面,写完
			flag.E2PROM_read = 1;
		}
	}
	else if (lcd_view == 1)
	{
		flag.E2PROM_Write = 1;	//调到阈值设定界面 置1,到第一个界面时候,才存储阈值参数
		
		if (flag.E2PROM_read == 1)
		{
			//读出来的三个阈值都是95 怀疑有问题,把阈值上下限限制的判断,挪到按键里了,发现读出来的数据都变成255了
			//突然想到这里我没有初始化I2C引脚,被自己蠢笑了~~~哈哈哈,初始化PB6、PB7,按键也忘记初始化过...以后先找CubeMX~~~
			
			Threshold_1 = EEPROM_ReadByte(1);
			Threshold_2 = EEPROM_ReadByte(2);
			Threshold_3 = EEPROM_ReadByte(3);
			flag.E2PROM_read = 0;
		}
	}	
}

3.9 main函数

原版

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* 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_TIM4_Init();
  MX_ADC2_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  
	LED_Disp(0x00);		//LED初始化,全部熄灭
	
	LCD_Init();			//LCD初始化
	
	//定时器初始化
	HAL_TIM_Base_Start_IT(&htim3);
	HAL_TIM_Base_Start_IT(&htim4);
	
	
	LCD_Clear(White);//清LCD屏
	LCD_SetBackColor(White);
	LCD_SetTextColor(Blue);
	
	//上电先读取一遍
	Threshold_1 = EEPROM_ReadByte(1);
	Threshold_2 = EEPROM_ReadByte(2);
	Threshold_3 = EEPROM_ReadByte(3);
	
	HAL_UART_Receive_IT(&huart1, &RX_Str_Data, 1);	//打开串口接收的初始化
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  
	  key_proc();
	  disp_proc();
	  control_fun();
	  LED_proc();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

初始化这里有点小Bug存在,我第一次写的时候没有解决这个问题,就是用E2PROM检测是否单片机为第一次上电

如果是第一次上电,那就初始化阈值参数,如果不是第一次上电,那就读取E2PROM里的数据作为阈值数据

修改

具体修改过的代码如下,我实际测试过,这样的写法可以实现我们预期效果,比较完美

直接放码过来~

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* 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_TIM4_Init();
  MX_ADC2_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  
	LED_Disp(0x00);		//LED初始化,全部熄灭
	LCD_Init();			//LCD初始化
	
	//定时器初始化
	HAL_TIM_Base_Start_IT(&htim3);
	HAL_TIM_Base_Start_IT(&htim4);
	
	LCD_Clear(White);//清LCD屏
	LCD_SetBackColor(White);
	LCD_SetTextColor(Blue);
	
	//上电先读取一遍
	if (EEPROM_ReadByte(123) != 0xA5)
	{
		Threshold_1 =30;
		Threshold_2 =50;
		Threshold_3 =70;
		EEPROM_WriteByte(123, 0xA5);
	}
	else
	{
		Threshold_1 = EEPROM_ReadByte(1);
		Threshold_2 = EEPROM_ReadByte(2);
		Threshold_3 = EEPROM_ReadByte(3);
	}

	HAL_UART_Receive_IT(&huart1, &RX_Str_Data, 1);	//打开串口接收的初始化
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  
	  key_proc();
	  disp_proc();
	  control_fun();
	  LED_proc();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

4 电路设计

不会

5 总结

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北国无红豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值