蓝桥杯嵌入式开发经验分享(第七届省赛——“模拟液位检测告警系统”)

蓝桥杯 专栏收录该内容
22 篇文章 25 订阅

作者:马一飞                                                         QQ:791729359       

在学习过程中有什么问题可以联系

(本篇博客有很多还没讲解很细致,先贴出代码,等有时间再进行注解)

一、题目

        “模拟液位检测告警系统”通过采集模拟电压信号计算液位高度,并根据用户设定的液位阈值执行报警动作,在液位等级发生变化时,通过串行通讯接口将液位信息发送到 PC 机。系统框图如图 1 所示:

                                                 

设计任务及要求

1. 液位检测

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

                                                                 

        AD 采集得到的结果应经过软件滤波算法处理,显示结果保留小数点后两位有效数字

2. 液位阈值设定

        设备可设定三个液位阈值,对应四个液位等级,阈值由用户通过按键输入,设备保存阈值,并根据此阈值判断液位等级,假        定用户输入的三个液位阈值为 10cm、20cm 和 30cm,液位高度与液位等级的对应关系如下:
                2.1 液位高度≤10cm 时,液位等级为 0;
                2.2 10cm<液位高度≤20cm 时,液位等级为 1;
                2.3 20cm<液位高度≤30cm 时,液位等级为 2;
                2.4 液位高度>30cm 时,液位等级为 3。
        设备初始液位阈值分别为 30cm、50cm 和 70cm,用户修改阈值后,设备应将此参数保
        存在 E2PROM 中,当设备重新上电时,可从 E2PROM 中获取。

3. 液位阈值设定

        B1 按键:“设置”按键,按下后进入阈值设定界面(如图 2 所示),再次按下 B1 按键时退出设置界面,保存用户设定的结果到 E2PROM,并返回图 1 所示的液位检测界面。

                                                                      

        B2 按键:切换选择 3 个待修改的阈值,被选中的阈值应突出显示。
        B3 按键:“加”按键,按下后,被选择的阈值增加 5cm,增加到 95cm 为止。
        B4 按键:“减”按键,按下后,被选择的阈值减少 5cm,减少到 5cm 为止。

4. 串口查询与输出功能

’        使用 STM32 USART2 完成以下串口功能,波特率设置为 9600。

        4.1 查询
                通过 PC 机向设备发送字符‘C’,设备返回当前液位高度和液位等级;
                通过 PC 机向设备发送字符‘S’,设备返回当前设定的三个阈值。
               液位高度和等级返回数据格式举例:
                        “C:H55+L2\r\n”
                        解析:应答高度、等级查询,液位高度为 55cm,液位等级为 2。
               阈值返回数据格式举例:
                        “S:TL30+TM50+TH70\r\n”
                         解析:应答阈值查询,设备内保存的三个阈值分别为 30cm、50cm 和 70cm。
4.2 输出
        当液位等级发生变化时,设备自动向 PC 机发送当前液位等级、液位高度和液位变化趋势(上升或下降)。
        输出数据格式举例:
                “A:H55+L2+D\r\n”
                解析:液位变化自动发送,液位高度 55cm,液位等级为 2,变化趋势下降。
                “A:H55+L2+U\r\n”
                解析:液位变化自动发送,液位高度 55cm,液位等级为 2,变化趋势上升。

5. 状态指示

        LED 指示灯功能定义如下:
        LD1:运行状态指示灯,以 1 秒为间隔亮灭闪烁;
        LD2:液位等级变化指示灯,当液位等级发生变化时,LD2 以 0.2 秒为间隔闪烁 5 次;
        LD3:通讯状态指示灯,当设备接收到查询指令时,LD3 以 0.2 秒为间隔闪烁 5 次

二、模块化代码分析

1、初始化部分

#include "stm32f10x.h"
#include "lcd.h"
#include "i2c.h"
#include "IO.h"
#include "stdio.h"
u32 TimingDelay = 0;
u8 string[20];
u8 ADC_Flag = 0;
u8 KEY_Flag = 0;
u8 LED_Flag = 0;
u8 Display_Flag = 0;
u8 Level_Last = 0;
u8 Shan2_sum = 0;
u8 Shan3_sum = 0;
float ADC_Val;
u8 Height;
u8 Threshold1 = 30;
u8 Threshold2 = 50;
u8 Threshold3 = 70;
u8 Level = 0;
u16 LED_MODE = 0XFFFF;
extern u8 RXCUNT;
extern u8 RXOVER;
extern u8 RX_BUF[20];
extern u8 Set_Flag;
extern u8 Threshold_Flag;
void Delay_Ms(u32 nTime);
u8 i;
u8 LD1_Flag = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
STM3210B_LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
	
SysTick_Config(SystemCoreClock/1000);
i2c_init();
ADC1_Init();
USART2_Init(9600);
LED_Init();
KEY_Init();
Threshold1 = _24c02_Read(0x00);
Delay_Ms(5);
Threshold2 = _24c02_Read(0x01);
Delay_Ms(5);
Threshold3 = _24c02_Read(0x02);
Delay_Ms(5);

2、ADC部分

if(ADC_Flag)
{
	ADC_Val = Get_Adc() * 3.3 / 4096.0;
	Height = 100 * ADC_Val / 3.3;
	if(Height <= Threshold1)
	{
		Level = 0;
	}else
	if((Height > Threshold1) && (Height <= Threshold2))
	{
		Level = 1;
	}else
	if((Height > Threshold2) && (Height <= Threshold3))
	{
		Level = 2;
	}else
	if(Height > Threshold3)
	{
		Level = 3;
	}
	ADC_Flag = 0;
}

3、串口部分

if(RXOVER)
{
	Shan3_sum = 10;
	if(RX_BUF[0] == 'C')
	{
		for(i = 0;i < 20;i++)
		{
			string[i] = 0;
		}
		sprintf((char*)string,"C:H%d+L%d\r\n",Height,Level);
		USART2_SendString(string);
	}
	if(RX_BUF[0] == 'S')
	{
		for(i = 0;i < 20;i++)
		{
			string[i] = 0;
		}
		sprintf((char*)string,"S:TL%d+TM%d+TH%d\r\n",Threshold1,Threshold2,Threshold3);
		USART2_SendString(string);
	}
	for(i = 0;i < 20;i++)
	{
		RX_BUF[i] = 0;
	}
	USART_ITConfig(USART2,USART_IT_RXNE, ENABLE);
	RXOVER = 0;
}

4、按键改变

if(KEY_Flag)
{
	KEY_Read();
	KEY_Flag = 0;
}
void KEY_Read(void)
{
	static u16 key1_sum,key2_sum,key3_sum,key4_sum;
	// KEY1
	if(KEY1 == 0)
	{
		key1_sum++;
		if(key1_sum == 1)
		{
			Set_Flag ^= 1;
			LCD_ClearLine(Line0);
			LCD_ClearLine(Line1);	
			LCD_ClearLine(Line2);
			LCD_ClearLine(Line3);
			LCD_ClearLine(Line4);
			LCD_ClearLine(Line5);
			LCD_ClearLine(Line6);
			LCD_ClearLine(Line7);
			LCD_ClearLine(Line8);
			LCD_ClearLine(Line9);
		}
	}else
	{
		key1_sum = 0;
	}
	// KEY2
	if((KEY2 == 0) && Set_Flag)
	{
		key2_sum++;
		if(key2_sum == 1)
		{
			Threshold_Flag++;
			if(Threshold_Flag >= 4) Threshold_Flag = 1;
		}
	}else
	{
		key2_sum = 0;
	}
	// KEY3
	if((KEY3 == 0) && Set_Flag)
	{
		key3_sum++;
		if(key3_sum == 1)
		{
			if(Threshold_Flag == 1)
			{
				Threshold1 += 5;
			}
			if(Threshold_Flag == 2)
			{
				Threshold2 += 5;
			}
			if(Threshold_Flag == 3)
			{
				Threshold3 += 5;
			}
			if(Threshold1 > 95) Threshold1 = 95;
			if(Threshold2 > 95) Threshold2 = 95;
		    if(Threshold3 > 95) Threshold3 = 95;
			_24c02_Write(0x00,Threshold1);
			Delay_Ms(5);
			_24c02_Write(0x01,Threshold2);
			Delay_Ms(5);
			_24c02_Write(0x02,Threshold3);
			Delay_Ms(5);
		}
	}else
	{
		key3_sum = 0;
	}
	// KEY4
	if((KEY4 == 0) && Set_Flag)
	{
		key4_sum++;
		if(key4_sum == 1)
		{
			if(Threshold_Flag == 1)
			{
				Threshold1 -= 5;
			}
			if(Threshold_Flag == 2)
			{
				Threshold2 -= 5;
			}
			if(Threshold_Flag == 3)
			{
				Threshold3 -= 5;
			}
			if(Threshold1 < 5) Threshold1 = 5;
			if(Threshold2 < 5) Threshold2 = 5;
			if(Threshold3 < 5) Threshold3 = 5;
			_24c02_Write(0x00,Threshold1);
			Delay_Ms(5);
			_24c02_Write(0x01,Threshold2);
			Delay_Ms(5);
			_24c02_Write(0x02,Threshold3);
			Delay_Ms(5);
		}
	}else
	{
		key4_sum = 0;
	}
}

5、LED部分

if(LED_Flag)  // 100ms
{
	LD1_Flag++;
	if(LD1_Flag == 10)
	{
		LD1_Flag = 0;
		LED_MODE ^= (1<<8);
	}
	if(Level_Last != Level)
	{
		Shan2_sum = 10;
		if(Level > Level_Last)
		{
			for(i = 0;i < 20;i++)
			{
				string[i] = 0;
			}
			sprintf((char*)string,"A:H%d+L%d+U\r\n",Height,Level);
			USART2_SendString(string);
		}else
		if(Level < Level_Last)
		{
			for(i = 0;i < 20;i++)
			{
				string[i] = 0;
			}
			sprintf((char*)string,"A:H%d+L%d+D\r\n",Height,Level);
			USART2_SendString(string);
		}
		Level_Last = Level;
	}
	if(!(LD1_Flag % 2))  // 200ms
	{
		if(Shan2_sum)
		{
			LED_MODE ^= (1<<9);
			Shan2_sum--;
		}
		if(Shan3_sum)
		{
			LED_MODE ^= (1<<10);
			Shan3_sum--;
		}
	}
	GPIOC-> ODR = LED_MODE;
	GPIOD-> ODR |= (1<<2);
	GPIOD-> ODR &=~(1<<2);
	LED_Flag = 0;
}

6、LCD显示部分

if(Display_Flag)
{
	if(!Set_Flag)
	{
		LCD_SetTextColor(White);
		LCD_DisplayStringLine(Line1, (u8*)"    Liquid Level     ");
		sprintf((char*)string," Height : %dcm              ",Height);
		LCD_DisplayStringLine(Line3, string);
		sprintf((char*)string," ADC : %.2fV              ",ADC_Val);
		LCD_DisplayStringLine(Line5, string);
		sprintf((char*)string," Level : %d               ",Level);
		LCD_DisplayStringLine(Line7, string);
	}else
	{
		LCD_SetTextColor(White);
		LCD_DisplayStringLine(Line1, (u8*)"  Parameter Setup     ");
		if(Threshold_Flag == 1)
		{
			LCD_SetTextColor(Green);
		}else
		{
			LCD_SetTextColor(White);
		}
		sprintf((char*)string," Threshold 1 : %dcm       ",Threshold1);
		LCD_DisplayStringLine(Line3, string);
		if(Threshold_Flag == 2)
		{
			LCD_SetTextColor(Green);
		}else
		{
			LCD_SetTextColor(White);
		}
		sprintf((char*)string," Threshold 2 : %dcm       ",Threshold2);
		LCD_DisplayStringLine(Line5, string);
		if(Threshold_Flag == 3)
		{
			LCD_SetTextColor(Green);
		}else
		{
			LCD_SetTextColor(White);
		}
		sprintf((char*)string," Threshold 3 : %dcm       ",Threshold3);
		LCD_DisplayStringLine(Line7, string);
	}
	Display_Flag = 0;
}

三、通用初始化部分

#include "IO.h"
extern u16 LED_MODE;
u8 Set_Flag = 0;
u8 Threshold_Flag = 1;
extern u8 Threshold1;
extern u8 Threshold2;
extern u8 Threshold3;
///  24c02  ///
void _24c02_Write(u8 address,u8 data)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(address);
	I2CWaitAck();
	I2CSendByte(data);
	I2CWaitAck();
	I2CStop();
}

u8 _24c02_Read(u8 address)
{
	u8 temp;		
    I2CStart();		
    I2CSendByte(0xa0);		
    I2CWaitAck();
	I2CSendByte(address);
	I2CWaitAck();

	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	temp = I2CReceiveByte();
	I2CWaitAck();
	I2CStop();
	return temp;
}
  ADC  ///
void ADC1_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_ADC1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( GPIOB, &GPIO_InitStructure);

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1,&ADC_InitStructure);

	ADC_Cmd( ADC1, ENABLE);

	ADC_ResetCalibration( ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	 
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
}

u16 Get_Adc(void)
{
	u16 temp;		
    ADC_RegularChannelConfig( ADC1,ADC_Channel_8, 1, ADC_SampleTime_239Cycles5);
	ADC_SoftwareStartConvCmd( ADC1, ENABLE);
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == 0);
	temp = ADC_GetConversionValue( ADC1);
	ADC_SoftwareStartConvCmd( ADC1, DISABLE);
	return temp;
}


///  USART2  ///
void USART2_Init(u32 bound)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( GPIOA, &GPIO_InitStructure);

	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	USART_InitStructure.USART_BaudRate = bound;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_Init(USART2, &USART_InitStructure);
	
	USART_Cmd(USART2, ENABLE);
	USART_ITConfig(USART2,USART_IT_RXNE, ENABLE);
}
void USART2_SendString(u8 *str)
{
	u8 index = 0;	
	do
	{
		USART_SendData(USART2, str[index]);
		while(USART_GetFlagStatus( USART2,USART_FLAG_TXE) == 0);
		index++;
	}while(str[index] != 0);
}
   LED   /
void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( GPIOD, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = 0XFF00;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( GPIOC, &GPIO_InitStructure);

	GPIOC-> ODR = 0XFFFF;
	GPIOD-> ODR |= (1<<2);
	GPIOD-> ODR &=~(1<<2);
}
//   KEY   //
void KEY_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init( GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
	GPIO_Init( GPIOB, &GPIO_InitStructure);
}

 

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页

打赏作者

fei...

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值