资讯详情

[开源]STC8A8K64S可调/固定电压源

主图

正视图

(原型机只提供灵感和思路,不易复制)

该电源由STC8A8K64SMCU有:

  1. 两种输出模式可调固定;
  2. 0.96寸OLED电压显示和Led工作状态指示灯;
  3. 短路保护、输出过压、低压警告、输入过压保护;
  4. 硬件保护电路的程序调试过程;
  5. AC 220V市电输入和DC 4.5~8V输入宽电压。

1.控制层(顶层)

顶层主要负责将四位二进制控制信号传输到中间层,分别连接四个电源模块的使能端,上旋钮控制待机和3.3V”、 “5.0V”、“12V”、“ADJ切换三种状态,然后控制模块的工作状态,改变输出电压。

  1. MCU控制

MCU的ADC模数转换口与电位器相连,为转换结果设置不同的范围,实现旋钮(电位器)控制工作模式。在不同的工作模式下,MCU输出不同的位二进制信号输出到下一级的3-8译码器(74HC238)输入端,选择8个输出端口中的4个,作为上述位二进制控制信号,不仅传递给下一层,还作为led的控制信号,驱动led指示当前工作状态OLED或是MCU离线时,指示当前工作状态或错误原因。

为什么使用数字?IC作为MCU电源模块之间的缓冲?为什么不直接连接?MCU简化电源模块设计?首先,由于电源是原型机,在硬件编程过程中,如果两个电源模块同时错误,可能会损坏电源,因此使用类似于互的3-8翻译原理,以确保只有一个电源模块同时使用。这一步可以在程序和硬件可行的情况下删除。二是可以作为装饰品,所以大块空也是空的。

  1. ADC电压采样

使用了STC8A8K的ADC采样功能,采用原电阻分压采样,带SMBJ5.0保护ADC采样口。设置两个采样通道,先烧录试验程序(已上传附件),在可调模式下使用更准确的电压表记录不同电压对应的通道ADC转换结果(理论上是线性变化)计算回归方程,写入正式程序,通过两个通道计算平均值,最终获得当前电压值。最后通过结果OLED屏幕上显示了驱动程序。

采样通道(下)

上层PCB

(上层电路图有误,但是PCB准确)

  1. (被)驱动层(中层)

中层主要负责根据顶层传输的控制信号启用相应的电源模块,输出正确的电平。

控制信号 输出电压

000 0V(闲置)

001 3.3V

010 5V

011 12V

100nbsp;  1.8~13V(可调)

111                           0V(保护状态)

  1. 固定输出电源模块

固定输出模式包括3.3V、5V、12V固定电压的输出模式,采用了对应型号的LM2596作为电源模块,按照正确的接线方式连接使能端和信号线即可实现功能。

  1. 输出可调电源模块

选择LM2596-ADJ开关电源模块,使用一大一小两个阻值的旋钮电位器串联作为反馈电阻,旋转旋钮改变反馈电压大小,实现输出电压的调节功能,一大一小的阻值则可以实现电压粗调和微调。

:这种简单地设计并不属于数字电源的范畴,若想要实现纯数字控制的数字可调电源,可以用分立元件重建中间层,搭建一个大型降压开关电源,将MCU的IO口连接到开关管的栅极,输出不同占空比的高频方波信号(PWM调制),并设计闭环反馈系统,来输出并维持不同的Vout。

中层PCB

中层电路图

  1. 电源层(底层)

底层主要负责上面两层的供能,将220V的AC市电转化为15V的DC直流电,作为各电源模块降压前的VDD。再通过LDO将DC 15V转化为DC 5V提供给MCU、OLED等器件。此外,还设置了直流输入的备用路径,将DC 4.5~8V的输入电压提升到15V,再作为上述的VDD。

  1. AC 220V to DC 15V

220V市电通过220:12的变压器和整流桥转化为16V左右(≤12*√2 V),再通过1个LM2596-ADJ电源模块降为更稳定的15V电压,提供给中层作为VDD。同时VDD通过LDO(ASM1117-5.0)转化为5V的控制信号电源,提供给中上两层。

  1. DC 4.5~8V to 15V

为了防止突然断电或者没有市电接口,但又急需一个电压源的情况出现,该电源设计了针对USB、DC 2.1和DC 1.3等传统5V电源接口的输入路径。5V电压通过升压电源模块XL6019,升压到15V,直接作为VDD提供给旁边的5V LDO和中层电源模块。并且设有肖特基二极管作为PN结隔离,阻断备用路径,防止同时接入AC 220V和5V DC时可能产生的冲突或意料之外的逆流。值得注意的是,碍于功率(能量)的守恒,并且没有配备任何快充协议,该路径输出功率极其小。

  1. 短路保护

除每层的GND都带有自恢复保险外,出于对220V市电基本的尊重,底层市电的变压器输入脚还带有一个玻璃管保险丝座,可根据需求选择不同参数的保险丝。

  1. 过压保护(硬件)

在顶层关键器件(MCU和OLED)的5V和GND之间加入TVS瞬态抑制管的齐纳二极管过压保护电路。

过压保护电路

(5)继电器开关

由LDO产生的5V电源供电,控制5V电源和中上两层的开关,继电器关闭时导通,继电器启动时关断,因此不参与输出分流(这种设计纯粹是为了安全,其实完全没有必要)。

底层PCB

底层电路图

(代码风格和性能不具有任何参考性,故下文仅介绍控制原理)

  1. ADC和模式切换

(上文已介绍,不再赘述)

  1. 过压、低压保护

保护机制的设想是:当Vout偏离预计值太远时,输出电源应该被掐断,进入预设的保护状态,当Vout归零后,电源应该尝试自动重启。理论上,通过ADC输出结果,保护功能非常容易实现,但实际上存在许多个特例,让保护机制出现很多间断点。

特例:

(1)档位切换时的充放电过程;

(2)接入负载时的瞬时降压和恢复过程;

(3)脱离保护状态(VEM,V_Emergency)并尝试重启时的充电过程。

因此,需要设置多个定时器,应用多个延时规则为充放电留足时间(百毫秒级),防止保护机制误报。在程序中的中断表现为:

T0:动态保护规则,针对容性元件的充电过程编写规则,由于电容充电V-T曲线是微分方程,因此模拟了近似的三段线性规则来逼近(其实多几段更好,但没有专业设备只能止步于此);

T1:静态保护规则,当档位切换停止一段时间后,恢复到正常的保护规则——即设置Vout的上下限,一旦越线直接进入T2的规则。

T2:瞬态保护规则,当出现Vout越线时,开始计时,当计时结束时若电压仍未恢复到正确区间,触发统一的保护机制:进入保护模式(VEM),OLED显示屏提示警告原因,并在电容放完电后(Vout = 0 V时)尝试重启。计时的时间长短决定保护机制的敏感程度,一般设置为数百毫秒。

低压(负载太小)保护

  1. OLED驱动

参考OLED商家提供的代码。

  1. 由于选择的变压器最大输出400mA电流,并且两级开关电源的转换效率最多80%,导致该电源输出能力很弱,输出功率仅2.2W,只是第一版的原型机。下层中许多反馈电阻的取值比较精确,由于设计上的忽视,错误的取值可能导致5V LDO的报废,因此该方案仅提供灵感,不宜复刻;
  2. 疯狂旋转档位旋钮可能出现误报,重启电源恢复的过程中请顺便思考一下,为什么要这么疯狂地转它;

4.顶层电路图有误,但PCB正确(改着改着不想改了,也变不回去了)

 

立创开源STC8A8K可调固定电压源 - 嘉立创EDA开源硬件平台

(不含oled.c、oled.h等oled驱动程序)

#include "STC8.h"
#include "oled.h"
#include "bmp.h"
#include <intrins.h>
#define unchar unsigned char
#define unint unsigned int

//138code
sbit A1  = P4^0;
sbit A2  = P4^1;
sbit A3  = P4^2;
unchar VSB = 0;
unchar V33 = 33;
unchar V50 = 50;
unchar V12 = 120;
unchar VADJ = 122;
unchar VEM = 9;
unchar VBK = 1;
unchar state = 0;

int voltage = 0;  //100 * Voltage value
unchar V1 = 0;
unchar V2 = 0;

	
unchar a1 = 0;  //timer0 count
unint a2 = 0;		//timer1 count
unint a3 = 0;		//timer2 count

int limitation_t = 50;  //max
int limitation_b = -10;  //min

unchar cap = 0;  //capture charging state 0:Vooc charging 1:fast charging 2:slow charging 3:finish charging
bit fuse = 1; //1:work  0:break

//string
char Warning[] ={'W','a','r','n','i','n','g','!','\0'}; //loading
char Low[] = {' ','L','o','w',' ','V','o','l','t','!','\0'}; //low voltage
char Over[] = {'O','v','e','r',' ','V','o','l','t','!','\0'}; //over voltage
char Volt[] = {'V','O','L','T',':','\0'};
char space[]={' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','\0'};
//0:standby
//1:3.3v
//2:5.0v
//3:12.0V
//4:adj
//5:emergency

unchar C[8]=0xff;  //mode knob
unchar Q[8]=0xff;  //Vout ADC channel 1
unchar G[8]=0xff;  //Vout ADC channel 2





//delay 1ms 24MHz
void delay() 
{
unsigned char i, j;
	_nop_();
	i = 32;
	j = 8;
	do
	{
		while (--j);
	} while (--i);
}

void Timer0Init()		//10ms@24.000MHz
{
	AUXR &= 0x7F;
	TMOD &= 0xF0;
	TL0 = 0xE0;		
	TH0 = 0xB1;		
	TF0 = 0;		
	TR0 = 1;		
	EA = 1;
	ET0 = 1;
}

void Timer1Init(void)		//10ms@24.000MHz
{
	AUXR &= 0xBF;		//?????12T??
	TMOD &= 0x0F;		//???????
	TL1 = 0xE0;		//???????
	TH1 = 0xB1;		//???????
	TF1 = 0;		//??TF1??
	TR1 = 1;		//???1????
	ET1 = 1;
}

void Timer2Init(void)		//10ms@24.000MHz
{
	AUXR &= 0xFB;		//?????12T??
	T2L = 0xE0;		//???????
	T2H = 0xB1;		//???????
	AUXR |= 0x10;		//???2????
	IE2 &=0xfb;
}


//simplify assignment process
void code138(unchar VX)
{
	switch(VX)
	{
		case(0):A1=0;A2=0;A3=0;break;
		case(9):A1=1;A2=1;A3=1;break;  //emergency protection
		case(33):A1=1;A2=0;A3=0;break;
		case(50):A1=0;A2=1;A3=0;break;
		case(120):A1=1;A2=1;A3=0;break;
		case(122):A1=0;A2=0;A3=1;break;
		default:A1=0;A2=0;A3=0;break;
	}
}

//standby function
void standby()
{
	code138(VSB);
	limitation_b = -10;
	limitation_t = 90;
	OLED_ShowString(28,0,space);
	OLED_ShowString(18,3,Volt);
	
	OLED_DrawBMP(1,0,23,2,BAT);
	OLED_ShowChar(110,3,'V');
	OLED_ShowCHinese(100,6,2);
}
//3.3V function
void VV33()
{
	code138(V33);

	limitation_b = 300;
	limitation_t = 355;
	OLED_ShowString(18,3,Volt);
	
	OLED_DrawBMP(1,0,23,2,BAT);
	OLED_ShowChar(110,3,'V');
	OLED_ShowCHinese(100,6,0);
}
//5.0V function
void VV50()
{
	code138(V50);

	limitation_b = 455;
	limitation_t = 545;
	
	OLED_ShowString(18,3,Volt);
	
	OLED_DrawBMP(1,0,23,2,BAT);
	OLED_ShowChar(110,3,'V');
	OLED_ShowCHinese(100,6,0);
}
//12.0V function
void VV120()
{
	code138(V12);

	limitation_b = 1145;
	limitation_t = 1255;
	
	OLED_ShowString(18,3,Volt);
	
	OLED_DrawBMP(1,0,23,2,BAT);
	OLED_ShowChar(110,3,'V');
	OLED_ShowCHinese(100,6,0);
}
//adjst function
void VVadj()
{
	code138(VADJ);
	limitation_b = 90;
	limitation_t = 1350;
	
	OLED_ShowString(18,3,Volt);
	
	OLED_DrawBMP(1,0,23,2,BAT);
	OLED_ShowChar(110,3,'V');
	OLED_ShowCHinese(100,6,1);
	
}
//emergency function
void VVEM()
{
	fuse = 0;
	code138(VEM);
	OLED_DrawBMP(0,5,25,8,Warn);  //Warning logo
	OLED_ShowString(28,6,Warning);
	OLED_ShowCHinese(100,6,3);
	cap = 0;
	ET0 = 0;
	if(voltage <= 20)              //recover rules
	{	
	 fuse = 1;
	 state = VSB;
	 VBK = VEM;
		standby();
	}
}

//ADC for varistor controler
void ADC_1()
{
	ADC_CONTR =0xc2;
                             //??AD??
   delay();
   while (!(ADC_CONTR & 0x20));            //??ADC????
   ADC_CONTR &= ~0x20;                     //?????
	 *C= ADC_RES;
	
	if(*C <= 20)
		state = VADJ;
	else if(*C <= 85)
		state = V12;
	else if(*C <= 160)
		state = V50;
	else if(*C <= 245)
		state = V33;
	else
		state = VSB;
}


//evaluate power source state, leading to each function
void state_evaluate(unchar state)
{
	if(fuse == 1)
{
	switch(state)
	{
	case(0):standby();break;
	case(9):VVEM();break;
	case(33):VV33();break;
	case(50):VV50();break;
	case(120):VV120();break;
	case(122):VVadj();break;
	default:state = VSB;break;
	}
	VBK = state;
}
else       //emergency protection
	VVEM();
}

//initialization function
void Initialization()
{
	                   //initialize OLED		
	state = VSB;
	state_evaluate(state);
	                   //initialze ADC
	ADCCFG = 0x0f;                             
  ADC_CONTR = 0x80;   
  OLED_Init();	
	delay();
	OLED_Clear();
	
	OLED_ShowString(18,3,Volt);
	
	OLED_DrawBMP(1,0,23,2,BAT);
	OLED_ShowChar(110,3,'V');
	OLED_ShowCHinese(100,6,2);
	
}
void prot()
{
	switch(cap)
	{
		case(0):  //Vooc charging protection
		{
			if(voltage >= 1350)
		{	
			fuse=0;
			state=VEM;
			VVEM();
			OLED_ShowString(28,0,Over);
		}
			break;
		}
		case(1):    //fast charging protection
		{	
			if(voltage < limitation_b - 200)
					{	
						fuse=0;
						state=VEM;
						VVEM();
						
				
						OLED_ShowString(28,0,Low);
					}
				else if(voltage >= limitation_t + 300 || voltage >= 1350)
					{
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Over);
					}
					break;
		}
		case(2):  //slow charging protection
		{
				
					if(voltage < limitation_b - 100)
					{	
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Low);
					}
					else if(voltage >= limitation_t + 200 || voltage >= 1350)
					{
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Over);
					}
					break;
				
		}	
		case(3):  //finish charging protection
		{
			
				if((voltage < limitation_b) || (voltage >= limitation_t))
					{	
						IE2 |= 0x04;
				
					}
				else 
				{
					IE2 &= 0xfb;
					a3=0;
					
				}
					break;
		}
	}

}
void prot_VSB()   //special protection rule for rapidly switching 12V to standby(very slow discharge)
{
{
	switch(cap)
	{
		case(0):  //Vooc charging protection
		{
			if(voltage >= 1350)
		{	
			fuse=0;
			state=VEM;
			VVEM();
			OLED_ShowString(28,0,Over);
		}
			break;
		}
		case(1):    //fast charging protection
		{	
			if(voltage < limitation_b)
					{	
						fuse=0;
						state=VEM;
						VVEM();
						
				
						OLED_ShowString(28,0,Low);
					}
				else if(voltage >= 800)
					{
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Over);
					}
					break;
		}
		case(2):  //slow charging protection
		{
				
					if(voltage < limitation_b )
					{	
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Low);
					}
					else if(voltage >=  700)
					{
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Over);
					}
					break;
				
		}	
		case(3):  //finish charging protection
		{
				if(voltage < limitation_b)
					{	
						fuse=0;
						state=VEM;
						VVEM();

						OLED_ShowString(28,0,Low);
					}
				else if(voltage >= 580)
					{
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Over);
					}
					break;
		}
	}

}
}
//ADC for Vout
void calculate()
{
	static unchar round = 0;  //ADCtimes : 4 times / refresh
	 static unint  c1=0;
	 static unint  c2=0;
	 ADC_CONTR =0xc0;
                        //??AD??
   delay();
   while (!(ADC_CONTR & 0x20));            //??ADC????
   ADC_CONTR &= ~0x20;                     //?????
		*Q= ADC_RES;
		c1 += *Q;
	
		ADC_CONTR =0xc1;
                        //??AD??
   delay();
   while (!(ADC_CONTR & 0x20));            //??ADC????
   ADC_CONTR &= ~0x20;                     //?????
		*G= ADC_RES;
	 c2 += *G;

	round++;
 
if(round >= 4)                //calculate pre 4 times
{		
	
			voltage = (9.5430*(c1/round) - 54.2867 + 9.5514 *(c2/round) - 55.1111) / 2;
			//voltage = (9.5430*(c1/round) - 54.2867 + 9.5514 *(c2/round) - 55.1111) / 2;
		
		V1 = voltage / 100;
		V2 = voltage % 100 /10;

		round = 0;
		c1=0;
		c2=0;
		OLED_ShowNum(70,3,V1,2,16);  //10+1
	OLED_ShowNum(100,3,V2,1,16); //0.1
OLED_ShowChar(90,3,'.');
}
}

void main()
{
	
	Initialization();
	Timer0Init();
	Timer1Init();
	Timer2Init();
	//OLED_ShowString(18,3,Volt);
	//OLED_ShowNum(70,3,V1,2,16);  //10+1
	//OLED_ShowChar(90,3,'.');
	//OLED_ShowNum(100,3,V2,1,16); //0.1
	//OLED_DrawBMP(1,0,23,2,BAT);
	//OLED_DrawBMP(0,5,25,8,Warn);
	//OLED_ShowChar(110,3,'V');
	//OLED_ShowString(28,6,Warning);
	//OLED_ShowString(28,0,Over);
	//OLED_ShowCHinese(100,6,0);
	
	while(1)
	{
		ADC_1();
		calculate();
		if(VBK != state)  //run judgment when backup != state
		{
			OLED_Clear();	
		state_evaluate(state);
			a1 = 0;
			a2 = 0;
			cap = 0;
			ET0 = 1;
			ET1 = 1;
		}
	if(state != VSB)
		prot();
  else
		prot_VSB();
	}
	
}
void Dynamic0() interrupt 1  //Dynamic protection
{
		
	a1++;
	
	if(a1 == 30)
		cap = 1; //slow charge state
	else if(a1 == 60)	
		cap = 2; //finish charging
		
	else if(a1 == 125)
	{
		cap = 3;
		a1 = 0;
		ET0 = 0;
	}
}

void Static1() interrupt 3  //Static protection (after 1.5~2.5 sencond)
{
	a2++;
	if(state != VSB)
	{
		if(a2 == 150)
		{
			a2 = 0;
			ET1 = 0;
			if(voltage < limitation_b)
					{	
						fuse=0;
						state=VEM;
						VVEM();

						OLED_ShowString(28,0,Low);
					}
				else if(voltage >= limitation_t)
					{
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Over);
					}
					
		}
	}
		else 
			if(a2 == 250)
		{
			a2 = 0;
			ET1 = 0;
			if(voltage < limitation_b)
					{	
						fuse=0;
						state=VEM;
						VVEM();

						OLED_ShowString(28,0,Low);
					}
				else if(voltage >= limitation_t)
					{
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Over);
					}
					
		}
}
void Tran2() interrupt 12  //transient protection
{
	a3++;
	
	if(a3==50)  //500ms for transient charging, after 500ms consider as over/below normal Volt
	{
		
		IE2 &=0xfb;
		a3=0;
		if(voltage < limitation_b)
					{	
						fuse=0;
						state=VEM;
						VVEM();

						OLED_ShowString(28,0,Low);
					}
				else if(voltage >= limitation_t)
					{
						fuse=0;
						state=VEM;
						VVEM();
				
						OLED_ShowString(28,0,Over);
						
	        }
  }
}

上层PCB 3D效果图

顶层PCB

中层PCB

下层PCB

标签: tr35w功率电阻25v1f电容27肖特基二极管dov33计数输出数字光纤传感器e112l静态中间继电器smbj5364b二极管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台