基于STM32的燃气消费控制系统设计

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式

概要

  本文主要是基于STM32微控制器为控制核心,以RC522射频模块和IC卡作为燃气计费的载体。该系统通过气体流量传感器实时监测燃气流量信息,把得到的数据传给微控制器,微控制器对气体流速积分运算得到的燃气量,然后根据燃气单价换算成金额后,更新IC卡内余额。并在OLED屏实时显示用户的余额信息。当余额低于阈值时,提示用户IC卡充值。当可用额度为零时,关断继电器,关闭燃气的供应,实现对燃气消费的自动控制。
经过调试后,系统可以对燃气消费量进行扣费,以及IC卡充值。并可以实时显示用户消费信息,同时可以根据卡内余额控制燃气关断和提醒用户提前充值。

关键字: STM32;燃气消费;流量计;RC522射频模块;OLED屏

一、本设计研究的内容和主要工作

  本设计研究的主要内容是基于STM32微控制器和IC卡实现对燃气消费系统的控制,实现对燃气消费系统的安全可靠,方便快捷控制。
  本设计的主要工作为:
  首先,查阅相关文献资料,了解燃气消费系统的研究背景和意义及国内外现状;
  其次,了解燃气消费系统的运行模式,确定需要控制的系统,明确相关生产规范,初步分析并设计子系统;
  再次,了解相关硬件并选定合适的系列,实现各个模块与微控制的焊接与连接,然后,根据逻辑顺序设计程序流程图和程序框架,熟练掌握嵌入式的编程方法,设计控制程序,完成相应的功能,然后进行系统调试工作。

二、总体方案设计

2.1 系统设计要求

  以燃气消费系统控制为对象,采用合适的嵌入式硬件和软件系统,设计并实现对燃气消费的自动控制。实现燃气流量的计费:并结合燃气单位价格计算燃价格,再通过读卡设备更新IC卡中的金额;流量控制:从读卡器中获得用户的插卡信息及IC卡内的金额信息,以判断是否开启或关闭控制阀门以及提醒用户充值后继续使用;信息提示:通过显示设备将IC卡的余额、燃气表状态及其他状态信息通过显示设备显示给用户,余额不足提醒用户充值。

2.2 方案论证

方案一:采用51单片机作为主控制芯片,51单片机结构经典、总线完善,而且具有位操作系统,操作起来也方便。对于轻量的控制系统来说,51单片机也是很好的选择。但是51单片机在运行速度方面相对较慢,外设资源较少,芯片保护机制不好,对于本设计的运行速度和后期系统升级都存在限制。
方案二:选择STM32F1系列处理器作为主控芯片,该系列单片机内核频率相对较高,运行内存和FLASH充足,运行速度快。而且外设资源丰富,且具备常用通信总线。可以完成本设计控制,而且方便后期系统升级。
方案三: 选用STM32F4系列芯片,该系列芯片属于高端处理器,系统频率高,运行内存大,性能强大,而且加入DSP图像处理和浮点型运算。但是本设计对于图像处理要求不高,而且这款芯片价格相对F1系列比较昂贵。
所以综合上诉三种方案,基于本设计对控制器的要求,从控制器处理性能、后期的系统升级、以及对芯片价格三个方面的综合考虑。选择STM32F1系列芯片作为本设计的主控芯片。

2.3 总体方案

  本设计采用STM32F103C8T6主控芯片对数据进行采集处理,通过CFA100流量检测燃气的流速,CFA100流量计的信号是模拟信号,单片机通过内部16位ADC,进行模数转换,然后计算出燃气的使用量,结合燃气单价,算出总消费额后,对M1的射频卡进行扣费。
  RC522与STM32主控芯片通过SPI进行通信,经过寻卡→防冲突→选卡→读取IC卡中的金额,根据用户使用的燃气流量计算出消费金额后扣除IC卡中的费用,当余额减到设定阈值时,蜂鸣器会报警一段时间,提醒用户燃气充值。当卡内金额减到0.0时,通过IO口控制外部继电器模块关断电磁阀切断燃气供应,并提醒用户IC卡充值。
  12864OLED屏与STM32主控芯片采用SPI通信,显示模块主要显示3个界面的芯片:一是用户没有插入IC卡时,消费控制系统的主界面,提示用户插卡消费燃气;二是用户插入IC后,显示用户的基本信息、账户余额、以及燃气使用总量,方便用户实时知道自己的卡内余额和燃气使用量,方便用户提前充值,避免突然切断燃气给消费用户带来的不便;三是余额低于设定阈值时,显示余额不足,提示用户提前进行燃气充值;四是燃气消费额为0.0时,显示余额为0.0,提醒用户充值。五是充值界面,显示用户余额,以及充值金额信息。
  继电器通过IO和主控芯片进行通信,主要用于根据用户的卡内余额,打开或者关闭燃气管道电磁阀。无源蜂鸣器模块主要是给用户提示信息,如果用户余额不足,会提起给予报警,提示用户提前充值,以避免给用户带来不便。
  根据上述硬件组成,基于STM32的燃气消费控制系统总体框图如图2.1所示。
在这里插入图片描述

图2.1 系统总体框图

三、系统硬件设计

3.1 单片机系统电路设计

3.1.1 电源电路设计

   嵌入式设计系统电源电路的设计肯定是必不可少,本设计中采用USB 5V供电,经过ASM117-3.3V稳压芯片输出3.3V电压,3.3v电压STM32F103C8T6,12864OLED屏幕、继电器、LED灯、蜂鸣器、射频卡模块以及流量传感器CAF100提供工作电压。
   该系统中还提供了两个5V和两个3.3V的扩展供电模块,以方便连接其他外设。电源电路如图3.1所示。
在这里插入图片描述

图3.1 电源电路

四、软件设计

4.1 软件设计的总体思路

本系统设计的功能如下:
1.开机后正常运行界面,在OLED屏会显示本设计系统的名字,并且下方有提示用户插入IC卡的提示图片。当用户插入有效IC卡后系统进入IC卡计费界面,屏幕会显示用户的楼层后以及卡内余额信息。
2.充值功能:本设计采用按键对IC卡进行模拟充值操作,当按下按键1时,进入充值界面,每按下一次设置的为充值10元。当按下按键2时,退出充值页面。
3.燃气检测和IC卡扣费功能:通过燃气流量计CFA100和和STM32内部的ADC检测当前燃气的状态,如果有气体流动,根据气体流速计算出气体的体积,进行扣费。
4.关断功能:当读卡器检测不到IC卡,继电器断开,断开燃气通道电磁阀,断开燃气的供应。当检测到IC卡余额不足时,继电器同样断开。
5.报警提醒功能:当检测到IC卡的钱数小于阈值时,会在OLED屏幕余额显示界面下方显示余额不足的界面。提示用户进行充值。
软件整体设计流程图如图4.1所示。

在这里插入图片描述

图4.1 软件整体设计流程图

每部分外设调试完成之后,就可以把各个外设统一起来,实现整体功能,如图整个系统处于开机没有IC卡插入,系统和开机界面相同,显示系统名字,以及提示用户插卡消费的提示简图。燃气关断状态的实物图如图5.7所示。
在这里插入图片描述

图5.7 燃气关断状态系统实物图
当有效IC卡插入,金额充足,OLED屏显示住户楼层信息、账户余额、以及消费的总气体量。继电器闭合,指示灯亮起,打开燃气阀,供应燃气。燃气处于供应状态的实物图如图5.8所示。
在这里插入图片描述

图5.8 燃气处于供应状态的体统实物图
当有效IC卡插入,金额小于设定阈值10.0但大于0.0时,蜂鸣器会报警一小段时间,提示用户余额不足。OLED屏显示住户楼层信息、账户余额不足提醒、以及消费的总气体量。继电器依然闭合,指示灯亮起继续供应燃气。余额低于设定阈值时的实物图如图5.9所示。
在这里插入图片描述

图 5.9 余额低于设定阈值的实物图
当有效IC卡插入,金额为0.0时时,蜂鸣器会报警一段时间,提示用户余额为零请充值。OLED屏显示住户楼层信息、余额不足0.0、余额不足请充值提醒。继电器断开,继电器断开指示灯熄灭,关闭燃气供应。卡内余额为0.0时的实物图如图5.10所示。
在这里插入图片描述

图5.10 卡内余额为0.0时的实物图
当用户充值IC卡时,本设计采用按键充值,每次按下充值10.0元,充值界面依然显示用户楼层信息、账户余额,以及充值的金额。IC卡充值界面如图5.11所示。
在这里插入图片描述

图5.11 IC卡充值界面

主要函数

主函数程序:
#include "main.h"
#include "main.h"

char oled_ic_new_value[16] ;
char oled_increment[16];
extern float oled_read_value;
extern float ic_old_value;
char disply_read_value[16];
char oled_sum_gas[16];
char user_name[16] ;
char oled_add[16];
float add_money = 0;
u8 lst_10 = 0;
u8 increment = 0;
u8 ui_2 = 0;
u8 ui_1 = 1;
u8 ui_0 = 1;
u8 BEEP_flag = 0;
u8 ui_0_clear = 0;
u8 ui_1_clear = 0;
u8 ui_2_clear = 0;
u8 BEEP_once = 0;
u8 BEEP_two = 0;
u8 beep_three = 0;
u32 beep_count = 0;
u8 gas_read_flag = 0;
int Increment_ret = 0;
int decrement_ret = 0;
extern float sum_gas ;
int main()
{
	Delay_Init();			//滴答定时器初始化
	Led_Config();			//LED灯初始化
	USART1_Config(115200);	//串口初始化
	Adc_Config();
	KEY_Config();
	ADC1_DMAConfig();
	char KEY = 0;
	printf("串口初始化成功\r\n");
	OLED_Init();
	RFIDGPIO_Config();
	ply_Config();
	Beep_Config_TIM3_CH4(7200,3000,0);
	while(1)
	{
		

		if(timekey[0]>timekey[1])
		{
			switch(Get_KeysValue())//KEY
			{
				case KEY1_PUSH :
					if(ui_2_clear == 0)
					{
						ui_0_clear = 0;
						ui_1_clear = 0;
						ui_2_clear = 1;
						OLED_Clear_once(0,127,0,7);
					}
					if(ui_2 == 1)
					{
						increment = 1;
					}
					ui_0 = 0;
					ui_1 = 0;
					ui_2 = 1;
				break;
				case KEY2_PUSH :
					 ui_2 = 0;
				     ui_1 = 1;
					 ui_0 = 0;
					add_money = 0;
					OLED_Clear_once(0,127,0,7);
				
				break;
				default : break;	
			}
				
		  timekey[0] = 0;
		}
		if(timeply[0] > timeply[1])
		{
			if(IC_Read_float(4) == MI_OK) 
			{
				BEEP_once = 1;
			}	
				
			if(ic_no_card_count<2 && ply_flag_value == 1)  //成功寻到卡余额大于0
			{
				printf("ply_flag == 1\r\n");
				OPEN_PLY;
			}
			else if(ic_no_card_count >2 || ply_flag_value ==0)  //超过2次没有寻到卡,或者余额不大于0
			{
				printf("ply_flag == 0\r\n");
				CLOSE_PLY;
			}
			if(ic_no_card_count >2)  //没有读到卡
			{
				if(ui_0_clear == 0)
				{
					ui_1_clear = 0;
					ui_2_clear = 0;
					ui_0_clear = 1;
					OLED_Clear_once(0,127,0,7);
				}
				ui_0 = 1;
				add_money = 0;
				BEEP_once = 0;
				BEEP_two = 0;
				beep_three =0;
				gas_read_flag = 0;
				beep_count = 0;
				BEEP_flag =0;  //为下次读到卡蜂鸣器滴一声做准备
			}
			else if(ic_no_card_count <= 2 && ui_2 == 0)  //显示余额
			{
				
				ui_1 = 1;
				ui_0 = 0;
				if(ui_1_clear == 0)
				{
					ui_0_clear = 0;
					ui_2_clear = 0;
					ui_1_clear = 1;
					OLED_Clear_once(0,127,0,7);
				}
				if(oled_read_value <= 10.0)
				{
					lst_10 = 1;
				}
				else 
				{
					lst_10 = 0;
				}
				
			}
			timeply[0] = 0;
		}
		if(timeadc[0]>timeadc[1]) //20ms检测一次
		{
			//ADC_Display();//ADC采样显示
			if(ply_flag_value == 1)
			{
				Amount_gas();	
			}
			timeadc[0] = 0;
		}
		if(timerc522[0] >timerc522[1])
		{
			if(increment == 1  )
			{
				Increment_ret = IC_Increment_float(4,10.0);//按一次充值10元
				if(Increment_ret == MI_OK  && ic_new_value >ic_old_value)
				{
					add_money = add_money +10;
					increment = 0;
				}
				
			}
			
			if(cost_flag == 1 && ply_flag_value == 1 && oled_read_value>0) //每次扣0.1元
			{
				decrement_ret = IC_Decrement_float(4,0.1);
				if(decrement_ret == MI_OK  && ic_new_value <ic_old_value)
				{
					cost_flag = 0;
					lst_10 = 0;
				}
				if(decrement_ret == 1 && ic_new_value <ic_old_value )
				{
					cost_flag = 0;
					lst_10 = 1;
				}
			}
			timerc522[0] = 0;
		}
		if(timeoled[0] > timeoled[1])
		{
			if(ui_0 == 1)
			{
				ui_1 = 0;
				ui_2 = 0;
				OLED_Show_chinese(16,0,0,6,OLED_ui_0);
				OLED_showphoto(32,2,gImage_ui_0,80,48);
			}
			if(ui_1 == 1)
			{
				ui_0 = 0;
				ui_2 = 0;
				OLED_Show_chinese(0,0,6,3,OLED_ui_0);
				OLED_ShowStr1(50,0,user_name);
				sprintf(disply_read_value,"%.1f",oled_read_value);
				OLED_ShowStr1(60,3,disply_read_value);	
				if( ply_flag_value == 0) //余额为零
				{
					beep_three = 1;
					OLED_ShowStr1(96,6,"      ");
					OLED_Show_chinese(0,3,12,4,OLED_ui_0);
					OLED_Show_chinese(0,6,17,7,OLED_ui_0);	
				}
				else if(lst_10 == 1)
				{
					BEEP_two = 1;
					OLED_Show_chinese(0,3,12,4,OLED_ui_0);
					OLED_Show_chinese(0,6,24,4,OLED_ui_0);
					sprintf(oled_sum_gas,"%.1f",sum_gas);
					OLED_ShowStr1(60,6,oled_sum_gas);
				}
				else
				{
					BEEP_two = 0;
					OLED_Show_chinese(0,3,0,4,OLED_IC);
					OLED_Show_chinese(0,6,24,4,OLED_ui_0);
					sprintf(oled_sum_gas,"%.1f",sum_gas);
					OLED_ShowStr1(60,6,oled_sum_gas);
				}
				
			}
			if(ui_2 == 1)
			{
				ui_0 = 0;
				ui_1 = 0;
				OLED_Show_chinese(0,0,6,3,OLED_ui_0);
				OLED_ShowStr1(50,0,user_name);
				OLED_Show_chinese(0,3,0,4,OLED_IC);
				sprintf(oled_increment,"%.1f",ic_new_value);
				OLED_ShowStr1(60,3,oled_increment);
				OLED_Show_chinese(0,5,9,3,OLED_ui_0);
				sprintf(oled_add,"%.1f",add_money);
				OLED_ShowStr1(60,5,oled_add);
			}
			timeoled[0] = 0;
		}
		if(timebeep[0]> timebeep[1])
		{
			if( BEEP_flag == 0 && BEEP_once == 1)//读卡成功,蜂鸣器滴一声
			{
				OPEN_beep(50);
				Delay_ms(50);				
				OPEN_beep(0); 
				BEEP_flag = 1;
			}
			if(BEEP_two == 1 && beep_count <300)
			{
				beep_count++;
				printf("beep_count=%d\r\n",beep_count);
				OPEN_beep(500);
			}
			else if(beep_three == 1 && beep_count<450)
			{
				beep_count++;
				OPEN_beep(300);
			}
			else 
			{
				OPEN_beep(0);
			}
			timebeep[0] = 0;
		}
		//指示灯
		if(timeled[0]>timeled[1])
		{
			//IC_Init_float(4,100.0);
			if(IC_Read_sum_gas(5) == 0 && gas_read_flag == 0)
			{
				gas_read_flag = 1;
			}
			if(gas_read_flag == 1)
			{
				IC_write_sum_gas(5);
				IC_Read_user_name(12);
			}
			LEDx_TOGGLE(LED0_PORT,LED0_PIN);
			printf("beep_count=%d\r\n",beep_count);
			timeled[0] = 0;
		}
}


定积分估算燃气体积函数:

#define ADV_0SCCM 0.33
#define ADV_300SCCM 2.44
float gas_q[2] = {0.0,0.0};
float adc_v = 0.0;    //adc采样的电压值的模拟量值
uint8_t flag_q = 1;  
#define Dx   20  //分成20ms,认为20ms内,变化非常小,为线性 
#define PRECISION  0.02  //允许误差
#define ADC_RANGE 300    //测量范围 单位毫升 cc
float sum_gas = 0.0;     //使用天然气体的总量
#define PRICE_GAS 25.6  // 每立方米2.56元,扩大1000倍,每升2.56元
float sum_price = 0.0 ; //价格总量
float sum_price_temp_remain = 0.0; //每消费0.01元扣费,后的余额
float sum_gas_temp = 0.0;          //保存扣费0.01元前气体量
uint8_t cost_flag = 0;             //每消费0.01元,刷新卡内余额标志位置1
//定积分估算燃气总量函数:
int Amount_gas()
{
	uint32_t i = 0;
	float dx_gas_q = 0.0;
	float sum_price_temp = 0.0;
	uint32_t lenth=sizeof(dma_adcbuff)/(sizeof(dma_adcbuff[0]));
	flag_q = !flag_q;
	if(DMA1->ISR & (1<<1))
	{
		DMA1->ISR |=(1<<1);
		for (i=0;i<lenth;i++)
		{
			printf("dma_adcbuff[%d]=%d\r\n",i,dma_adcbuff[i]);
			adc_v = dma_adcbuff[i] * 3.3/4096.0;
			if(adc_v -ADV_0SCCM < PRECISION || ADV_0SCCM- adc_v > (-PRECISION))
			{
				 adc_v = ADV_0SCCM;
			}
			gas_q[flag_q] = (adc_v-ADV_0SCCM) * 300/(ADV_300SCCM-ADV_0SCCM);
			if(gas_q[flag_q] < 0.0 || gas_q[flag_q] - 0 < PRECISION || 0- gas_q[flag_q] > (-PRECISION))
			{
				gas_q[flag_q] = 0.0;
			}
			//printf("gas_q[%d] = %.2f\r\n",flag_q,gas_q[flag_q]);
			printf("V = %.6f\r\n",adc_v);
			
		}
		dx_gas_q = ((gas_q[!flag_q]+gas_q[flag_q]) * Dx /(2*60*100) );
		if(dx_gas_q < 0.004 ) //如果变化小于精度,认为没有变化
		{
			dx_gas_q = 0.0;
		}
		sum_gas = sum_gas+ dx_gas_q; 
		sum_price = sum_gas*PRICE_GAS/1000; //sum_gas单位是毫升
		//每消费满0.01元时,才会刷新IC卡账户余额,减少IC卡的擦写次数
		sum_price_temp = ((sum_gas-sum_gas_temp)*PRICE_GAS )/1000 + sum_price_temp_remain;
		sum_price_temp_remain = 0.0; //sum_price_temp_remain 只需要加1次
		if(sum_price_temp >=0.01)
		{
			sum_gas_temp  = sum_gas;
			sum_price_temp = 0; 
			sum_price_temp_remain = sum_price_temp -0.01;
			cost_flag = 1;
		}
		//printf("sum_price = %.3f\r\n",sum_price);
		
	}
	return 0;
}


RC522读余额程序:
int IC_Read_float(u8 Block)
{
	int read_sta = 0;
	if(Request_Anticoll_Select(PICC_REQALL,Card_Type,Card_Buffer,Card_ID)==MI_OK)
	{ 
		if(RC522_AuthState(PICC_AUTHENT1A,Block,IC_Default_Key,Card_ID)==MI_OK ) 
		{
			//读一遍余额
			if(Card_PurseGet_Float(Block,&oled_read_value)==MI_OK)
			{
					ic_no_card_count = 0;  //成功读取,失败寻卡次数清零
				//	printf("读取金额:%f\r\n",oled_read_value);//加之前的读出来打印
				if(oled_read_value >0.1)
				{
					ply_flag_value = 1;//打开继电器
					read_sta = 0;
					goto p1;
				}
				else
				{
					ply_flag_value = 0;
					read_sta = 0;
					goto p1;
					
				}
				
			}
		}			
	}
	else
	{
		read_sta = -1;
		ic_no_card_count ++;
		if(ic_no_card_count>200)
		{
			ic_no_card_count = 10;
		}
		goto p1;
	}
	p1:
	return read_sta;
}


RC522充值函数程序:
int IC_Increment_float(u8 Block,float increment_valve)
{
	if(Request_Anticoll_Select(PICC_REQALL,Card_Type,Card_Buffer,Card_ID)==MI_OK)
	{ 
		if(RC522_AuthState(PICC_AUTHENT1A,Block,IC_Default_Key,Card_ID)==MI_OK ) 
		{
			//加之前读一遍余额
			if(Card_PurseGet_Float(Block,&ic_old_value)==MI_OK)
			{
				printf("ic_old_value = %f",ic_old_value);//加之前的读出来打印
				
				if(ic_old_value>1000000.0)//判断是否还能再加 
				{
					return -1;
				}
				printf("add-go\r\n");
				//加值  
				if(Card_PurseIncrement_Float(Block,increment_valve) == MI_OK)
				{
					if(Card_PurseGet_Float(Block,&ic_new_value) == MI_OK )
					{
						flag_v = !flag_v;
						ic_v[flag_v] = ic_new_value;
						//Printing(Data_Buffer,16);
//						printf("+_OK\r\n");
						printf("ic_new_value = %f",ic_new_value);
						return MI_OK;
					}
				}
			}
		}
	}
	return -1;	
}


RC522扣费函数程序:
int IC_Decrement_float(u8 Block,float decrement_valve)
{
	int ret = 0;
	Request_Anticoll_Select(PICC_REQALL,Card_Type,Card_Buffer,Card_ID);
	RC522_AuthState(PICC_AUTHENT1A,Block,IC_Default_Key,Card_ID);
	if(Request_Anticoll_Select(PICC_REQALL,Card_Type,Card_Buffer,Card_ID)==MI_OK)
	{
	 
		if(RC522_AuthState(PICC_AUTHENT1A,Block,IC_Default_Key,Card_ID)==MI_OK ) 
		{
			//减之前读一遍余额
			if(Card_PurseGet_Float(Block,&ic_old_value)==MI_OK)
			{
				printf("ic_value = %f\r\n",ic_old_value);;//加之前的读出来打印
				
				//加值  
				if(Card_PurseDecrement_Float(Block,decrement_valve) == MI_OK)
				{
					if(Card_PurseGet_Float(Block,&ic_new_value) == MI_OK)
					{
						printf("ic_value = %f",ic_new_value);
						if(ic_new_value <= 10.0)
						{
							ret = 1;
						}
						else if(ic_new_value < 0.00001)
						{
							return 2;
						}
						else
						{
							ret = MI_OK;
						}
					}
			
				}
				return  ret ;
			}
		}
	}	
	return -1;
}


OELD 清屏程序:
void OLED_Clear(uint8_t col_start,uint8_t col_end,uint8_t page_start,uint8_t page_end)
{
	uint8_t i;
	uint8_t j;
	write_i(0x20,OLED_CMD);//-SET Page Addressing Mode (0x00/0x01/0x02)
	write_i(0x00,OLED_CMD);//horizontal 模式 
	//set column address
	write_i(0x21,OLED_CMD);
	write_i(col_start,OLED_CMD);//  column start
	write_i(col_end,OLED_CMD); // column end
	//set page address 
	write_i(0x22,OLED_CMD); 
	write_i(page_start,OLED_CMD);// page start
	write_i(page_end,OLED_CMD);// page end
	
	for(i=0;i<8;i++)
	{
		for(j=0;j<128;j++)
		{
			write_i(0x0,OLED_DATA);
		}
	}
}
OLED 显示图片函数:
/*
col_start 开始列
page_start  开始页
*p  图片模首地址
wide 图片宽度
high  图片高度
*/
void OLED_showphoto(uint16_t col_start, uint16_t page_start, const uint8_t *p,uint8_t wide,uint8_t high)
{
	uint8_t i = 0;
	uint8_t j = 0;
	//设置为水平地址模式
	write_i(0x20,OLED_CMD);
	write_i(0x00,OLED_CMD);
	//设置开始列地址和列结束地址
	write_i(0x21,OLED_CMD);
	write_i(col_start,OLED_CMD);  //列开始地址
	write_i(col_start+wide-1,OLED_CMD);//列结束地址
	//设置开始页地址和页结束地址
	write_i(0x22,OLED_CMD);
	write_i(page_start,OLED_CMD);
	write_i(page_start+high/8-1,OLED_CMD);
	for(i=0;i<(high/8);i++)  
	{
		for(j=0;j<wide;j++)
		{
		//写数据
			write_i(*(p++),OLED_DATA);
		}			
	}
}

OLED屏显示汉字函数:

//X--列地址,Y--页地址    Z--第几个字开始显示 
void OLED_Show_chinese(uint16_t x, uint16_t y,uint16_t Z,uint16_t count,uint16_t (* OLED_array)[16])
{
	uint8_t i = 0;
	uint8_t j = 0;
	uint8_t k=0;
  
	for(k=0;k<count;k++)  
	{
		
		for(i=0;i<2;i++)
		{
			OLED_SetPos (x+16*k, y+i);
			for(j=0;j<16;j++)
			{
			//写数据
				write_i(OLED_array[Z+i+2*k][j],OLED_DATA);
			}	
		}
		

	}
}

OLED屏显示字符函数:
// x 字符横坐标
// y 字符纵坐标
// c 要显示的字符

void OLED_ShowChar(uint16_t x, uint16_t y, char c)
{
  uint8_t i = 0;
  uint8_t j = 0;
  uint16_t count = 0;
  
	for(i=0;i<2;i++)  
	{
		OLED_SetPos (x, y+i);   
		for(j=0;j<8;j++)
		{
			//写数据
			write_i(ascii_8_16[c - 32][count++],OLED_DATA);
			//{0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x0F,0x01,0x01,0x01},/*"+",11*/43
		}			
	}
}


OLED屏显示字符串函数:
// x 字符横坐标
// y 字符纵坐标
// str 显示字符串首地址

void OLED_ShowStr1(uint16_t x, uint16_t y, char * str)
{
  uint16_t i = 0;
  while (str[i] != '\0')
  {
    OLED_ShowChar(x+=8, y, str[i]);
    if (x > OLED_WIDTH - 8)
    {
      x = 0;
      y += 2;
    }
    if (y > OLED_HIGH - 2)
    {
      y = 0;
    }
    
    i++;
  }
}


五、结论

  本设计中是基于燃气的消费控制,第一点要做的就是对气体的体积测量,因为传感器传送的是实时的流速信息,而且流速一般也不是均匀的,因此这些数据采集后的数据处理算法便成为了我的工作,我查阅资料,在高等数学中微分在工程计算中得到启发,采用数学的定积分近似运算–梯形法对燃气的总体积进行计算,又一次实践体验了数学在实际工程中的应用。在OLED屏显示界面中,因为要进行几个页面的切换,所以都要进行清屏处理,但是如果每刷新一次屏幕都清屏一次,会出现刷屏的问题,后来我想到UCOSII操作系统里的向量集思想,在每个界面刷新设置一个标志位,只在每次界面切换的时候刷新一次屏幕,这样就解决了刷屏的问题。在射频卡模块的学习过程中,一开始对这个模块没有了解,无从下手,后来请教他人,自己也在网上找资料,了解整个模块的工作流程,以及IC卡信息存储原理,IC卡每个块的相同与区别,最终成功驱动射频模块工作。
  经过不断地努力探索与调试,最终设计实现通过STM32和射频模块的燃气消费的自动控制,收获良多。
  本设计要在实际应用中发挥作用,当然还有很多地方值得去探索和发掘,比如对IC卡内的余额信息安全问题值得进一步提升,对仪表出现故障问题能即使的给出警告信息排查。对进一步的研究探索,我想加入网络信息,将用户燃气消费信息,以及卡内余额信息,都通过网络传输到数据库中,这样既能统计用户的燃气使用量也能增加消费者卡内信息安全。总体来说通过本次设计我收获的不仅仅是对专业知识的进一步学习,更学到了解决问题的办法,以及综合的理论实践能力。

六、 文章目录

目 录

目 录
1 绪论 1
1.1 研究的目的及意义 1
1.2 发展现状 1
1.3 本设计研究的内容和主要工作 2
2 总体方案设计 4
2.1 系统设计要求 4
2.2 方案论证 4
2.3 总体方案 4
3 系统硬件设计 6
3.1 单片机系统电路设计 6
3.2 系统显示模块 8
3.3 射频模块 10
3.4 流量计模块 12
3.5 继电器模块 13
3.6 按键模块 14
4 软件设计 15
4.1 软件设计的总体思路 15
4.2 定时器程序初始化 17
4.3 流量传感器程序设计 17
4.4 显示模块程序设计 18
4.5 射频卡模块程序设计 19
4.6 继电器、蜂鸣器和按键程序设计 24
5 系统调试与结果 27
5.1 各个模块的调试结果显示 27
5.2 整个系统调试结果显示 30
结 论 34
致 谢 35
参考文献 36
附录A 系统整体原理图 37
附录B 主要程序 38

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
水箱控制系统是一种用于自动控制水箱水位的系统,可以根据水箱水位的实时情况来自动开启或关闭水泵,以保持水箱水位在一个合适的范围内。基于STM32的水箱控制系统设计需要考虑到系统的稳定性、可靠性和实时性。 首先,我们需要选择STM32系列微控制器作为系统的核心,因为STM32具有高性能、低功耗、丰富的外设接口和强大的计算能力,可以满足水箱控制系统对于实时性和稳定性的需求。系统的硬件部分包括传感器模块、执行器模块和显示模块,用于检测水箱水位、控制水泵和显示系统运行状态。 其次,我们需要设计系统的软件部分,包括数据采集、处理和控制算法。通过STM32的ADC接口,可以实时采集水箱水位数据,并通过PWM控制水泵的启停状态。同时,需要设计合适的控制算法,根据水箱水位数据来自动调节水泵的运行状态,以保持水箱水位在设定范围内。 另外,为了方便用户对系统进行监控和操作,可以设计一个人机交互界面,通过STM32的串口接口连接显示模块,实时显示水箱水位和系统运行状态,并提供手动控制水泵的功能。 最后,需要对系统进行整体调试和优化,确保系统的稳定性和可靠性。同时,为了提高系统的扩展性,可以考虑通过网络接口将系统连接到上位机或云平台,实现远程监控和控制。 综上所述,基于STM32的水箱控制系统设计需要综合考虑硬件和软件两个方面,通过合理的系统架构和优化的算法实现水箱水位的自动控制,同时兼顾用户体验和系统的稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值