紫外消毒烘干器设计思路

 #一、 整体思路
      紫外消毒烘干器是一个集紫外杀菌和加热板烘干功能、数码管时间显示功能于一体的日常家用小电器。它的原理比较简单,就是在32位ARM Cortex-M0内核的微处理器上读取时钟芯片,通过与读取到的时钟信息对比在固定时间微处理器驱动紫外灯板和加热板模块工作。这样最基本的能按固定时间消杀和烘干的紫外消毒烘干器就做好了。如果需要的话(看起来更上档次一些)可以加上时间显示模块(LCD1602、OLED或LCD12864都可以)、按键时间调整模块、电源状态监测模块、电量显示模块等,最后在程序里面实现用户可以自己设定消杀时间和电压报警监测这样的话高档次的紫外消毒烘干器就实现了。
       

# 二、 各个模块设计

        根据整体思路,紫外消毒烘干器暂时细分为分为电源检测模块、数码管时间显示模块(包括时间采集、时间显示)、时间调整模块(包括系统时间、用户自定义时间)、整体工作模块(消杀、烘干、除湿)、电源检测及报警模块等五个大的模块,如果想各个模块更简洁可以将括号里面的内容都细分一下(这样虽然程序条理更清楚但每个模块的.c、.h文件内容就不会很多只有一两百行代码,看起来会给人一种代码量不多的感觉)。


## 1、电源检测模块

        电源检测模块主要适用于有想把消毒架做成电池供电的类型,考虑到电池待机时长和电池容量(本就是个体积不大的小产品,不可能在内部整个几万毫安的电池)。为了增加电池供电时的待机时长,在电池供电时必须要砍掉或优化一些高耗能的功能,例如显示部分可以调低显示的亮度等级(这里用的数码管显示时间)、减小除湿风扇的驱动频率、烘干模组如果功率过高就可以直接砍掉或者优化一下(但现在市面上的产品基本上是把这个功能砍掉了)等。只要思想不滑坡,方法总比困难多嘛!具体要优化那些东西就根据自己的需求来,我这里是把烘干功能关闭了、调低了显示数码管的亮度等级。

        电源检测模块其实很简单,只要在外部供电和电池供电时有明显且可以利用的区别就可以实现。相信大家已经想到了,不错就是充电宝。充电宝在充电和不充电的时候不就是这样的吗?再加上现在充电宝方案这么成熟而且充点电宝的电源芯片又不贵,简直就是最完美的选择。至于选择何种芯片就看你自己了。我是用的以前一个坏的充电宝的电源芯片IP5306,它的资料在网上一大堆,它的数据手册不仅有各种电气信息而且还给出了典型的应用原理图,最重要的是它灯在亮的时候是用PWM控制的这也太方便检测状态了,简直不要太舒服。我用的是双灯显示模式。在程序里面是在固定时间检测两个灯的电平变化次数,然后通过次数来确定状态。

        电源检测参考程序:

        Flip_Level_1=GPIOC_ReadPortPin(GPIO_Pin_1);
		Flip_Level_2=GPIOC_ReadPortPin(GPIO_Pin_2);
		while(Computing_Time(Get_Time(),Time_Power_LED)<2000)
		{
			if(Flip_Level_1!=GPIOC_ReadPortPin(GPIO_Pin_1))	    {i++;}	
			if(Flip_Level_2!=GPIOC_ReadPortPin(GPIO_Pin_2))		{j++;}
			Flip_Level_1=GPIOC_ReadPortPin(GPIO_Pin_1);
			Flip_Level_2=GPIOC_ReadPortPin(GPIO_Pin_2);
//			PRINT("电平变化次数:i=%ld j=%ld\r\n",i,j);
		}
        //数值经供参考,以实际测量为准
		if((i>40)&&(i<160))	POWER_LED1=2;
		if(i>200)			POWER_LED1=1;	
		if(i==0) 			POWER_LED1=0;		
				
		//数值经供参考,以实际测量为准
		if((j>40)&&(j<160)) POWER_LED2=2;		
		if(j>200)			POWER_LED2=1;		
		if(j==0) 			POWER_LED2=0;		
		
		if(POWER_LED2==0)                       POWER_Flag=3;    //充电状态(包括充满状态)
		if((POWER_LED1==0)&&(POWER_LED2==1)) 	POWER_Flag=2;    //放电未充状态
		if((POWER_LED1==0)&&(POWER_LED2==2))    POWER_Flag=1;    //低电报警状态

## 2、时间显示模块

        时间显示模块就是“时钟芯片+数码管显示模块”。

        时钟芯片就是一种具有时钟特性,能够获取到现实时间的芯片。时钟芯片属于是集成电路的一种,其主要有可充电锂电池、充电电路以及晶体振荡电路等部分组成。时钟芯片大致分为三类,分别为并行接口、串行接口以及三线接口,常用的DS1302、DS1307属于串行接口。

        在我看来,时钟芯片的主要作用就是提供精确时间和记录时间以及记录数据和断电保护这两个(我目前只用到过这两个作用,不排除还有更好的作用但我没用过)。

  • 提供精确时间与记录时间的功能是时钟芯片最基本的作用,而且时钟芯片的的时钟功能很强大,可以提供精确的年、月、日、星期、时、分、秒所有的时间单位(这需要硬件电路没问题,我遇到的跑时不准基本上都是硬件电路的原因),而且时钟芯片还具有着闰年补尝功能。时钟芯片具有数据记录和断电保护作用。
  • 记录数据和断电保护这就是时钟芯片的神来之笔了。意外掉电不掉数据,上电还能直接可以读到精确的时间信息,别提有多省事了。时钟芯片这个功能是源于其内部有一个 RAM 单元,此 RAM 单元一部分用于对时钟显示的控制,绝大部用于单元数据的存储。锂电池是时钟芯片电路中的重要组成部件之一,在时钟芯片断电或者关机之后,锂电池可以通过芯片内部电路实现给芯片供电,使时钟芯片在断电后仍可以运行,确保时钟芯片内部记录的数据不丢失。

        时钟芯片介绍就到此为止,芯片选取各自随意。本来就是个闲暇时间做的小工艺品,本着不浪费原则时钟芯片也是从其它地方拆下来的。我这里使用的是DS1307,DS1307和DS1302在功能上差不多,主要是通信上面有一些区别(DS1302是半SPI接口,DS1307是I2C接口)。至于谁更好用,这两个都一样,网上相关例程一大堆,基本上改改例程里面定义的引脚都能读到数据了,然后就是对数据进行处理。这里程序上其实没什么太大的技术含量,主要是硬件电路上(跑时不准真的好难解决,忙活了好久看了好多博客还是没很好地解决这个问题)。

        DS1307读写参考程序:

//DS1307写数据
void Write1307(unsigned char add,unsigned char dat)
{
  unsigned char temp;
  temp=dat/10; 
  temp<<=4;
  temp=dat%10+temp;
  IIC_Start_DS1307();
  IIC_Send_Byte_DS1307(0xD0);   //呼叫芯片DS1307,并定义为写动作  
  IIC_Wait_Ack_DS1307();
  IIC_Send_Byte_DS1307(add);    //发送起始地址
  IIC_Wait_Ack_DS1307();    
  IIC_Send_Byte_DS1307(temp);   //发送待写入数据
  IIC_Wait_Ack_DS1307();	
  IIC_Stop_DS1307(); 
}

//DS1307读数据
unsigned char Read1307(unsigned char add)
{
  unsigned char temp;
  unsigned char dat;
  IIC_Start_DS1307();         
  IIC_Send_Byte_DS1307(0xD0);	//呼叫芯片DS1307,并定义为写动作
  IIC_Wait_Ack_DS1307();		//等待信号响应
  IIC_Send_Byte_DS1307(add);	//发送起始地址
  IIC_Wait_Ack_DS1307();		
  IIC_Stop_DS1307();
	
  IIC_Start_DS1307();
  IIC_Send_Byte_DS1307(0xD1);	//呼叫芯片DS1307,并定义为读动作
  IIC_Wait_Ack_DS1307();
  dat=IIC_Read_Byte_DS1307(0); 
  IIC_Stop_DS1307();
  temp=dat/16;
  dat=dat%16;
  dat=dat+temp*10;
  return(dat);
}

        显示模块,这都算是滥大街的东西了。随便一搜时间显示模块,各种万年历就出来了(不仅有各种电路图而且例程这些东西还很齐全)。

        显示模块的原理都差不多,都是在显示器件(LCD12864、LCD1602、OLED、数码管)上循环显示数字。唯一的区别就是各个显示器件的通信方式可能不一样而已。显示模块其实也没什么太大的技术含量,各种显示程序和电路图都有而且基本上都是可以直接复制粘贴使用的。

        参考程序:


//此段选编码为根据实际硬件线路做出的编码,分别对应0-9、全灭
UINT8 Data_Code[]={0xdd,0x09,0xd3,0x5b,0x0f,0x5e,0xde,0x19,0xdf,0x5f,0x00};

void AiP650_Set(UINT8 add,UINT8 dat) //数码管显示
{
	//写显存必须从高地址开始写
	IIC_Start_AIP650();
	IIC_Send_Byte_AIP650(add); //第一个显存地址
	IIC_Wait_Ack_AIP650();
	IIC_Send_Byte_AIP650(dat);
	IIC_Wait_Ack_AIP650();
	IIC_Stop_AIP650();
}

//小时+中间点亮灭间隔1s
void AiP650_HourAdd_Display(UINT8 hour,UINT8 sec)
{
    //小时
    UINT8 data=(hour%24)/10;
	AiP650_Set(0x68,Data_Code[data]);//十位
	data=(hour%24)%10;
	if(sec%2==0)
    {
		AiP650_Set(0x6a,Data_Code[data]|(UINT8)(1<<5));//个位	
	}
	else
	{
		AiP650_Set(0x6a,Data_Code[data]);//个位
	}
}

//分钟单独显示
void AiP650_Min_Display(UINT8 min)
{
	//分钟
    UINT8 data=(min%60)/10;
	AiP650_Set(0x6c,Data_Code[data]);//十位
	data=(min%60)%10;
	AiP650_Set(0x6e,Data_Code[data]);//个位
}

//小时: 分钟同时显示
void AiP650_DisPlay(UINT8 hour, UINT8 min,UINT8 sec)
{ 
    //小时+中间点亮灭间隔1s
	AiP650_HourAdd_Display(hour,sec);
     //分钟
	AiP650_Min_Display(min);
}

void Light_Level_Set(UINT8 Level)//设置亮度等级 1-8级
{
	Level&=0x07;	//Level = (0x0111 & Level) 
	Level<<=4;		//Level = (0x0111 & Level) 0000
	Level++;		//Level = (0x0111 & Level) 0000 + 0x0000 0001
	AiP650_Set(0x48,Level);
}

## 3、时间调整模块

        时间调整模块算是这个里面比较有点技术含量的了,两个按键实现系统时间修改,自定义消杀时间设定供能。老实说这个模块花费时间最长但到现在为止只能说实现了功能。两个按键要实现五个功能(系统时间和设定时间小时、分钟的加减以及设定时间的保存),最开始的时候打算用四个按键实现这个功能,后来看见了一篇介绍多功能按键的博客,https://www.cnblogs.com/mcumagic/p/5892995.html,之前有了解过多功能按键但一直没有用过,本着学习的原则直接将四个按键改成两个按键,然后参考这篇博客开始了艰难的调试之旅(此处调试过程省略,自己动手调试一下真的很有收获)。

        按键状态参考程序:只有长按和短按功能

//按键状态检测
Key_Mode_Typedef Button_Scan(Whitch_Button_Typedef Button_Count)
{
    static UINT8 Button_Sta[Button_Sum]={0,0};
	UINT32 Button[Button_Sum]=  {GPIOC_ReadPortPin(GPIO_Pin7),GPIOC_ReadPortPin(GPIO_Pin_6)};
	Button_Mode_Typedef keyStatus;
	switch(Button_Sta[Button_Count])
	{
	    case 0:
			if(Button[Button_Count]==0)
			{
				Button_Sta[Button_Count]=1;
				Set_Delay(Button_Long_Time,Long_Press_Time);
			}
			else
			{
				Button_Sta[Button_Count]=0;
			}
			ButtonStatus=No_Press;
		break;

		case 1:
			if(Button[Button_Count]==0)
		    {
				if(Delay(Button_Long_Time,Long_Press_Time))
				{
					Button_Sta[Button_Count]=2;
					ButtonStatus=Long_Press;
				}
				else
				ButtonStatus=No_Press;
			}
			else
			{
				Button_Sta[Key_Count]=0;
				ButtonStatus=Short_Press;
			}
		break;
		    
        case 2:
			if(Button[Button_Count]==0)
			{
				if(Delay(Button_Long_Time,Long_Press_Time))    ButtonStatus=Long_Press;
				else    ButtonStatus=No_Press;
			}
			else
			{
				Button_Sta[Button_Count]=0;
				ButtonStatus=No_Press;
			}
		break;

		default:
		break;
	}
    return ButtonStatus;
}

        按键状态能检测出来短按和长按,就基本能实现功能了。长短按区分时间设定还是系统时间修改,按键位置辅助确定调节的是小时还是分钟——>左右短按分别对应时间加减——>长按保存(无触发2秒后返回不保存)。但调试双击的时候一直不理想,所以没法用上。后续有时间再改进吧!


## 4、紫外消毒烘干器工作模块

        紫外消毒烘干器工作模块其实说白了就是通过IO驱动相关模块通断,就像点灯一样。加热模块、紫外灯模块、风扇这些都是买的成品,网上都有,成本也比高(主要是时间不充足)。这里需要注意的是理清工作逻辑和异常处理而已。

        这个部分的工作逻辑不算复杂。系统走时到设定时间点或系统定义的消杀时间点,先启动紫外模块杀菌然后启动加热板烘干和风扇除湿工作——>杀菌时间到关闭紫外灯模块,继续烘干和除湿工作——>烘干和除湿时间到,关闭加热板模块和风扇——>然后循环检测是否到下一个启动时间点。

        异常处理主要是断电后过了时间点但又没过多久,这里就可以把到时间点后左右检测时间放宽一些(2分钟、5分钟、10分钟)自己设定即可,如果满足还是可以继续工作。当然肯定还有其他更好的方案。

        这部分程序太杂了,就不每个都贴出来了,主逻辑参考程序:

void main()
{
	Syserm_Init();
	Syserm_Power_off();    //系统掉电检测
	Syserm_Power_Check();
    while(1)	
    {
        low_voltage_state();
		Key_Set();
		if(Key_flag==0)		
		{
			Syserm_Power_Check();	
			Time_Display();	
		}	
		if(POWER_Flag!=1)
		{	
			Sterilization(); 
			Heating(); 
			Dehumidify(); 	
		}
    }	
}


# 三、 调试问题

        逻辑理清才算起步,调试才是问题的重灾区。真的是不调试以为都是些现成的没什么难度,So easy。一调试真的就是各种牛鬼蛇神都出来了(大部分都是细节错误)。关于调试部分的硬件就不说了(硬件太菜,遇到的问题基本都是请教认识的硬件大佬们解决的)。软件调试就几个遇到的比较感觉很容易踩坑的点给大家参考一下。

        1、数码管显示乱码?

        这种问题很常见但也很好解决;首先确认数码管是共阴极还是共阳极——>确定位选,段选全打开看所有段显示是否正常——>段选正常,分别修改每一段的显示值确定各段的排序(一定要做,我这里就是因为几个段顺序错了,导致显示乱码,导致我熬了一个夜才解决这个问题)——>根据段选确定数字0—9的编码值。这样走下来数码管显示基本上就不会再出现乱码问题了(除非硬件变化)。

        2、DS1307读数异常?(包括但不限于读不到数、读数不变、读数不准、上电时读数有延迟等)

        DS1307读不到数:调试时,用串口打印读数但串口没有反应,这种情况一般是通信没成功,通信成功始终有数值的只是不会变而已(我只遇到过这种情况下没有输出,但不排除还有其他情况);通信不成功包括串口通信不成功(在程序里用串口1打印数据却在串口2上读数据或者串口通信没调)、时钟芯片IIC没调通(完全读不到东西)。遇到这种问题先检查串口,确定串口无误后再检查时钟芯片IIC通信。

        3、DS1307读数不变:如果避过了上面那个坑能读到数据,但是数据没有变化,那恭喜你进入了一个大坑。数据读到但没变化这个我看了一下,大家都遇到过这种问题但是原因又各不相同(但终归是芯片没工作和读取数据的地址不对这两种)。下面主要说明我觉得可能存在的几个方向:

        时钟芯片供电部分:供电一般使用3.3~5.0V左右(供电要超过3.2V),前期由于没注意导致DS1307供电只有3.1V左右,串口打印出来的数据会存在不变的情况,推测是时钟芯片供电电压太低导致芯片没工作所以读不到数据,但有时候重启后又能正常变化。所以具体是不是这个原因不确定。

        时钟芯片晶振部分:读数不变还有可能是晶振不起振导致芯片不工作。晶振不起振的原因就不深究了(原因太多了,而且还不好找),我们可以用示波器检测晶振是否工作正常。用示波器测量芯片的晶振接口,如果有正常的方波说明晶振是能起振的。如果晶振起振但读数还是不变,那就可能是晶振接反了(有的晶振是有正反的)、芯片挂了或IIC没接上拉电阻(可优先考虑)。

        时钟芯片读数不准确:时钟芯片跑时有误差只能是晶振电路电容不匹配。可以适当改变晶振电容(网上有相关计算公式公式但我换了按照公式计算出来的电容也还是没改进,好在跑时问题不是很严重大概一个星期天会有2分钟的误差)。

        时钟芯片上电时读数有延迟:用示波器监测发现上电时晶振起振响应很慢(大概要2~3s才能波形才稳定,每次断电重启时都这样),在网上查了一些资料都说这个好像是芯片本身的原因,没有办法改善。那就只能在每次初始化的时候加个延时函数了。

        3、其他问题

        其他调试问题都是一些粗心大意惹的祸,像引脚定义不正确、引脚模式设置错误、模块通断控制时置1和复位弄错、ADC采集时通道号定义错误、AD采集时模块和单片机没共地等。这些常见的错误不注意的话都容易出现而且没考虑到这些细节出问题的话很难找到解决方法,。所以为了在调试的时候更快速更高效,在调试前最好先检查一下电路主控和各模块是否接地、确认一下模块控制引脚的连接和定义情况,只要细节做得到位调试一般不会出什么问题。

#总结

        紫外消毒烘干器虽不是个很复杂的东西,但还是有很多可以学习的地方。最深的感受就是搞软件一定要会一些硬件知识。不说精通至少要能找到是哪里除了问题,就算去问大佬也不至于受很多白眼。

        就这样吧!看模电和电路去了... ...

posted @ 2022-07-22 14:58  归依龙井  阅读(83)  评论(0编辑  收藏  举报  来源