【STM32G4】备战蓝桥杯嵌入式---实战---第七届嵌入式模拟赛—“电压测量监控设备”


前言

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

一、题目

设计一个电压测量监控设备,定时通过串口向 PC 机发送电压值,通过串口接收系统配置参数并保存到 E2PROM 中。设备硬件部分主要由电源部分、控制器单元、串口部分、存储单元组成,系统框图如图 1 所示 :
在这里插入图片描述

设计任务及要求
1. RTC 实时时钟
使用 STM32 内部 RTC 完成相关功能,设备上电后,时间初始化为 23 时 59 分 55 秒,默认定时上报电压时间为 0 时 0 点 0 分
2. ADC 测量功能
设备采集电位器 R37 输出的电压信号 V1,并通过 LCD 显示。当 V1>VDD*k 时,指示灯LD1 以 0.2 秒为间隔闪烁,闪烁功能可以通过按键关闭;VDD为 3.3V;k 默认值为 0.1,保存在 E2PROM 中并可以通过串口修改配置。

3. 串行功能
3.1 设定 k 值,可设置范围 0.1 ~ 0.9
格式:【命令类型】【数值】【命令结束标志】
举例:
“k0.1\n”
设置 k 值为 0.1;
设备接收到命令执行后,回复“ok\n”。
3.2 定时上报电压 V1
格式:【V1 电压值】+【k 值】+【时间】【命令结束标志】
举例:
“2.21+0.1+123030\n”
12 时 30 分 30 秒上报电压值为 2.21V,k 值为 0.1
说明:串口设定 9600 波特,数据位 8,停止位 1,无校验位;没有发送或发送错误的控制命令时,设备不做回应。
4. LCD 显示
设备上电默认通过 LCD 显示电位器输出电压 V1(保留小数点后两位有效数字)、k 值、指示灯闪烁报警功能状态和系统时间,显示界面如图 1 所示:
在这里插入图片描述
5. 按键功能
“B1”按键设定为“功能”按键,打开/关闭指示灯闪烁报警功能,默认为打开状态;
“B2”按键设定为“设置”按键,设置设备自动上报电压时间,按下 B2 后,LCD 显示界面如图 2 所示,此时通过按键 B3 切换选择时、分、秒,通过按键 B4 进行调整,完成调整后,按下 B2 按键,更新自动上报时间,并返回图 1 所示的 LCD 显示界面。
在这里插入图片描述

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

1.模块的初始化

需要用的模块:LCD、IIC、四个按键、LED、ADC(PB15)、USART1

2.模块功能分析

LCD:显示-----》Display();
IIC:读取和存储信息到EEPROM-----》EEPROM_Write();EEPROM_Read();
按键:对参数进行调整-----》KEY_Handle();KEY_Scan();
LED:提示作用-----》LED();
ADC:读取电压值-----》Get_ADC();
USART1:与PC机交互-----》HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

三、函数实现

1.void Display(void);

当mode = 0时显示页面1;
当mode = 1时显示页面2:
Alarm_flag是打开/关闭指示灯闪烁报警功能

void Display(void)
{
	if(mode == 0)
	{
		sprintf((char *)str,"  V1: %.2fV",Adc);
		LCD_DisplayStringLine(Line1,str);
		
		sprintf((char *)str,"  k: %.1f",k);
		LCD_DisplayStringLine(Line3,str);
		
		if(Alarm_flag == 1)
			sprintf((char *)str,"  LED: ON");
		else
			sprintf((char *)str,"  LED: OFF");
		
		LCD_DisplayStringLine(Line5,str);
		
		sprintf((char *)str,"  T:%.2d-%.2d-%.2d",Time[0],Time[1],Time[2]);
		LCD_DisplayStringLine(Line7,str);
		
		sprintf((char *)str,"                  1");
		LCD_DisplayStringLine(Line9,str);
	}
	else if(mode == 1)
	{
		sprintf((char *)str,"       Setting");
		LCD_DisplayStringLine(Line1,str);
		
		sprintf((char *)str,"      %.2d-%.2d-%.2d",Time_Set[0],Time_Set[1],Time_Set[2]);
		LCD_DisplayStringLine(Line5,str);
		
		sprintf((char *)str,"                  2");
		LCD_DisplayStringLine(Line9,str);
	}
}

2.void EEPROM_Read(void);void EEPROM_Write(void);

Time为初始时间的时分秒,Time_Set为上报电压时间
k为系数(float)
IIC的读写函数在模块配置iic那一小节

void EEPROM_Write(void)
{
	IIC_Write(0x00,Time[0]);HAL_Delay(10);
	IIC_Write(0x01,Time[1]);HAL_Delay(10);
	IIC_Write(0x02,Time[2]);HAL_Delay(10);
	IIC_Write(0x03,Time_Set[0]);HAL_Delay(10);
	IIC_Write(0x04,Time_Set[1]);HAL_Delay(10);
	IIC_Write(0x05,Time_Set[2]);HAL_Delay(10);
	IIC_Write(0x06,k*10);HAL_Delay(10);
}
void EEPROM_Read(void)
{
	Time[0] = IIC_Read(0x00);HAL_Delay(10);
	Time[1] = IIC_Read(0x01);HAL_Delay(10);
	Time[2] = IIC_Read(0x02);HAL_Delay(10);
	Time_Set[0] = IIC_Read(0x03);HAL_Delay(10);
	Time_Set[1] = IIC_Read(0x04);HAL_Delay(10);
	Time_Set[2] = IIC_Read(0x05);HAL_Delay(10);
	k = IIC_Read(0x06)/10.0;HAL_Delay(10);
}

3.uint8_t KEY_Scan(uint8_t mode);

uint8_t KEY_Scan(uint8_t mode)
{
	static uint8_t flag=1;
	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) return B2_Press;
		else if (KEY_B3 == 0) return B3_Press;
		else if (KEY_B4 == 0) return B4_Press;
	}else if(KEY_B1 == KEY_B2 == KEY_B3 == KEY_B4 == 1)	flag = 1;
	return 0;
}

4.void KEY_Handle(uint8_t key);

在B1按下时打开/关闭指示灯闪烁报警功能(Alarm_flag变化)
在B2按下时显示页面切换(mode变化),切换回去时将参数写入EEPROM
在B3按下时切换参数选择的地址(location变化)
在B4按下时对应加1

void KEY_Handle(uint8_t key)
{
	if(key == B1_Press)
	{
		LCD_ClearLine(Line5);
		Alarm_flag = !Alarm_flag;
	}
	else if(key == B2_Press)
	{
		LCD_Clear(White);
		if(mode == 0)	mode = 1;
		else if(mode == 1){
			mode = 0;
			EEPROM_Write();
		}
	}
	else if(key == B3_Press)
	{
		if(mode == 1)
		{
			location++;
			location = location % 4;
		}
	}
	else if(key == B4_Press)
	{
		Time_Set[location - 1] = Time_Set[location - 1] + 1;
		Time_Set[0] = Time_Set[0] % 24;
		Time_Set[1] = Time_Set[1] % 60;
		Time_Set[2] = Time_Set[2] % 60;
	}
}

5.uint16_t Get_ADC(void);

读取ADC,Get_ADC()*3.3/4096才是电压值

uint16_t Get_ADC(void)
{
	uint16_t temp = 0 ;
	HAL_ADC_Start(&hadc2);
	temp = HAL_ADC_GetValue(&hadc2);
	HAL_ADC_Stop(&hadc2);
	
	return temp;
}

6.void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

接收到4个字符进入中断。判断后改变k的值。
memset是string中的字符串清空函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	char arr[20];
	if(Buffer[3] >= '0' && Buffer[3] <= '9')
	{
		k = (float)(Buffer[3]-'0')/10;
		sprintf(arr,"OK\r\n");
		HAL_UART_Transmit(&huart1,(unsigned char *)arr,strlen(arr),50);
	}
	memset(Buffer,0,sizeof(Buffer));
}

7.int main(void);

初始化;

在这里插入图片描述

	LCD_Init();
	I2CInit();
	
	LCD_Clear(White);
	LCD_SetTextColor(Black);
	
	IIC_Write(0x00,23);HAL_Delay(10);
	IIC_Write(0x01,59);HAL_Delay(10);
	IIC_Write(0x02,55);HAL_Delay(10);
	IIC_Write(0x03,00);HAL_Delay(10);
	IIC_Write(0x04,00);HAL_Delay(10);
	IIC_Write(0x05,00);HAL_Delay(10);
	IIC_Write(0x06,01);HAL_Delay(10);
	
	EEPROM_Read();
	
	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);
	HAL_Delay(100);
while(1);

在这里插入图片描述

	Display();
	HAL_UART_Receive_IT(&huart1,Buffer,4);
		
	key = KEY_Scan(0);
	KEY_Handle(key);
	if(Time[0] == Time_Set[0] && Time[1] == Time_Set[1] && Time[2] == Time_Set[2])
	{
		sprintf(arr,"%.2f+%.1f+%.2d%.2d%.2d\r\n",Adc,k,Time_Set[0],Time_Set[1],Time_Set[2]);
		HAL_UART_Transmit(&huart1,(unsigned char *)arr,strlen(arr),50);
		HAL_Delay(1000);
	}
	if(Count == 1)
	{
		Adc = Get_ADC()*3.3/4096;
		HAL_RTC_GetTime(&hrtc, &T, RTC_FORMAT_BIN);
		Time[0] = T.Hours;
		Time[1] = T.Minutes;
		Time[2] = T.Seconds;
		HAL_RTC_GetDate(&hrtc, &D, RTC_FORMAT_BIN);
	}
	if((Adc>(float)3.3*k) && (num == Alarm_flag == 1))
	{
		if(i++%2)
			HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
		else
			HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
			
		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);
	}
	else
	{
		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);
	}
}

8.void SysTick_Handler(void);

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

if(j++ >= 200)
{
	num = 1;
	j = 0;
}
if(i++ >= 1000)
{
	Count = 1;
	i = 0;
}

j是0.2s的计时,i是1s的计时


总结

以上就是本次模拟题的程序设计部分(变量定义部分,本人没有罗列出来嗷),在代码简洁方面本人做的不是很好,如有更好的设计方法可以在评论区留言。互相学习。👍
还等什么,赶快自己写一下,测试一下代码。嘿嘿

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值