一个数控桌面电源项目
作为一个大学僧,成品可调电源笨重且占据桌面空间;而市售USB升压模块2、3W的功率是在不堪大用。于是,博主将从零开始,研发一款实用的桌面可调电源。
尺寸上,该桌面电源参考华为22.5WSCP充电器,与华为系列快充较好兼容。
为最大限度兼容PD/SCP/FCP/QC等协议,该电路模块需要有宽电压输入范围。主升压芯片选用XL6019。该芯片为TO263-5封装,最大电流5A,输出电压-0.3~60V,能够满足日常实用需求。
辅助电源供给STM32和OLED面板,要求并不高,选熟悉的3.3V线性稳压芯片即可。这里选用的是HT7333。
参考电源输入STM32的Vbat引脚,作为基准电压使用,一定程度上决定了电源的精度。若要求不高,可直接并入辅助电源。这里使用一颗TL431,分压电阻为1.33K和4.22k。(TL431分压电阻的计算小工具放在下面链接中)
由于本人不想搭模电电路,所以选用了德州仪器的电流检测芯片INA180A3IDBVR,该型号芯片可以将检流电阻上的微小电压放大100倍,使用十分方便。
选用温度传感器TC1047AVNBTR,该型号芯片输出与温度呈线性关系的电压值,使用起来较为方便。
通过分析,MCU至少具备3路ADC(电流、电压、温度),一路DAC(控制电压),一路SPI(OLED),一路UART(扩展蓝牙)。事实上,许多单片机不具备DAC,而因为懒不想增加DAC芯片,所以选型比较受限制。最终选择STM32F103RCT6。(千万不要买RBT6,RBT6没有DAC!!!)
没啥好说的,烂大街的0.91寸SPI OLED(中景园15脚),放个图,溜了。
PCB分为三层,采用排针及FCP排线连接,方便不同模块升级及替换。
事实上,任何一款市售的电源模块通过简单的改造均可实现数控。
5脚为电源的反馈引脚,查阅芯片手册,Vfb=1.25V,即该引脚上的电压高于1.25V,MOS管关闭,否则MOS导通,从而实现电压的控制。
分析电路可得公式:(Vo-Vfb)/R2 + ( Vdac-VF)/R6 = Vfb/R9(VF为二极管得压降)
即可得到输出电压。
在本方案中R2=4.7K,R9=91K,R6=5.6K,可实现4.5~25V的调压范围。
无非就是寻常的多通道ADC检测+多次采样平均+DMA传输。多通道ADC与DMA配置如下。
void Adc_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO, ENABLE );
//RCC_APB2Periph_GPIOx,x=GPIOx
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2; //PA0/1/2/3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //½«ÍâÉè ADC1 µÄÈ«²¿¼Ä´æÆ÷ÖØÉèΪȱʡֵ
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC¹¤×÷ģʽ:ADC1ºÍADC2¹¤×÷ÔÚ¶ÀÁ¢Ä£Ê½
ADC_InitStructure.ADC_ScanConvMode =ENABLE; //¶àÐŵÀɨÃèģʽ
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //Ä£Êýת»»¹¤×÷ÔÚÁ¬Ðøת»»Ä£Ê½
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //Íⲿ´¥·¢×ª»»¹Ø±Õ
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADCÊý¾ÝÓÒ¶ÔÆë
ADC_InitStructure.ADC_NbrOfChannel = 3; //´Ë´¦¿ª6¸öÐŵÀ£¨¿É¿ªµÄΪ1~16£©
ADC_Init(ADC1, &ADC_InitStructure); //¸ù¾ÝADC_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉèADCxµÄ¼Ä´æÆ÷
//ADC³£¹æÐŵÀÅäÖÃ
//ADC1,ADCͨµÀx,¹æÔò²ÉÑù˳ÐòֵΪy,²ÉÑùʱ¼äΪ239.5ÖÜÆÚ
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_13Cycles5 );
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_13Cycles5 );
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_13Cycles5 );
// ¿ªÆôADCµÄDMAÖ§³Ö£¨ÒªÊµÏÖDMA¹¦ÄÜ£¬»¹Ðè¶ÀÁ¢ÅäÖÃDMAͨµÀµÈ²ÎÊý£©
ADC_DMACmd(ADC1, ENABLE); //ʹÄÜADC1µÄDMA´«Êä
ADC_Cmd(ADC1, ENABLE); //ʹÄÜÖ¸¶¨µÄADC1
ADC_ResetCalibration(ADC1); //¸´Î»Ö¸¶¨µÄADC1µÄУ׼¼Ä´æÆ÷
while(ADC_GetResetCalibrationStatus(ADC1)); //»ñÈ¡ADC1¸´Î»Ð£×¼¼Ä´æÆ÷µÄ״̬,ÉèÖÃ״̬ÔòµÈ´ý
ADC_StartCalibration(ADC1); //¿ªÊ¼Ö¸¶¨ADC1µÄУ׼״̬
while(ADC_GetCalibrationStatus(ADC1)); //»ñÈ¡Ö¸¶¨ADC1µÄУ׼³ÌÐò,ÉèÖÃ״̬ÔòµÈ´ý
}
void DMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{ DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA_CHx); //½«DMAµÄͨµÀ1¼Ä´æÆ÷ÖØÉèΪȱʡֵ
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMAÍâÉèADC»ùµØÖ·
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMAÄÚ´æ»ùµØÖ·
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //ÄÚ´æ×÷ΪÊý¾Ý´«ÊäµÄÄ¿µÄµØ
DMA_InitStructure.DMA_BufferSize = cndtr; //DMAͨµÀµÄDMA»º´æµÄÊý¾Ýµ¥Ôª´óС
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //ÍâÉèµØÖ·¼Ä´æÆ÷²»±ä
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //ÄÚ´æµØÖ·¼Ä´æÆ÷µÝÔö
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //Êý¾Ý¿í¶ÈΪ16λ
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //Êý¾Ý¿í¶ÈΪ16λ
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //Ñ»·¹¤×÷ģʽ
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMAͨµÀ xÓµÓиßÓÅÏȼ¶
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMAͨµÀxûÓÐÉèÖÃΪÄÚ´æµ½ÄÚ´æ´«Êä
DMA_Init(DMA_CHx, &DMA_InitStructure); //¸ù¾ÝDMA_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯DMAµÄͨµÀ
}
算法没啥稀奇,普通PID,输入电压,调整DAC。直接放代码。
#ifndef _pid_
#define _pid_
#include "stm32f10x_conf.h"
#define MODEL_P 1
#define MODEL_PI 2
#define MODEL_PID 3
typedef struct
{
u8 choose_model;
float curr;
float set; //Óû§É趨ֵ
float En; //µ±Ç°Ê±¿Ì
float En_1; //ǰһʱ¿Ì
float En_2; //Ç°¶þʱ¿Ì
float Kp; //P
float T; //²ÉÑùÖÜÆÚ
u16 Tdata; //ÅжÏÊÇ·ñµ½²ÉÑùÖÜÆÚ
float Ti; //i
float Td; //d
float Dout; //PIDÔöÁ¿
float OUT0; //άÎÈÊä³ö
u16 currdac; //µ±Ç°dacÖµ
u16 daccycle; //pwm??
}PID;
extern u8 STATUS;
extern PID pid;
void PIDParament_Init(void);
void pid_calc(void);
#endif
#include "pid.h"
#include "dac.h"
#include "dac.h"
#include "led.h"
PID pid;
void PIDParament_Init() //
{
pid.choose_model = MODEL_PID;
pid.T=50; //¶¨Ê±Æ÷1ms ×îС²ÉÑùÖÜÆÚ330ms
pid.set =5.0; //Óû§É趨ֵ
pid.Kp=0.4; //±ÈÀýϵÊý
pid.Ti=80; //΢·ÖϵÊý³£Êý
pid.Td=2; //»ý·Öʱ¼ä³£Êý
pid.OUT0=0; //ά³ÖϵÊý
pid.daccycle = 50; //PWM
pid.currdac=2000;
}
void pid_calc()
{
float dk1,dk2;
float t1,t2,t3;
u16 temp;
//
// if(pid.Tdata < (pid.T)) //×îС¼ÆËãÖÜÆÚδµ½
// {
// return ;
// }
// pid.Tdata = 0;
pid.En=pid.set-pid.curr; //±¾´ÎÎó²î
dk1=pid.En-pid.En_1; //±¾´ÎÎó²îÓëÉÏ´ÎÎó²îÖ®²î
dk2=pid.En-2*pid.En_1+pid.En_2;
t1=pid.Kp*dk1; //±ÈÀý
t2=(pid.Kp*pid.T)/pid.Ti; //»ý·Ö
t2=t2*pid.En;
t3=(pid.Kp*pid.Td)/pid.T; //΢·Ö
t3=t3*dk2;
switch(pid.choose_model)
{
case MODEL_P: pid.Dout= t1;
break;
case MODEL_PI: pid.Dout= t1+t2;
break;
case MODEL_PID: pid.Dout= t1+t2+t3; //¼ÆËãPIDÔöÁ¿
break;
}
pid.currdac-=77.69*pid.Dout; //Êä³ödacÖµ
if(pid.currdac>4090) //УÑ鷶Χ
{
pid.currdac=4090;
}
if(pid.currdac<2000)
{
pid.currdac=2000;
}
pid.En_2=pid.En_1;
pid.En_1=pid.En;
temp=pid.currdac;
temp=(int)pid.currdac;
temp=(short)temp;
DAC_SetChannel1Data(DAC_Align_12b_R,temp );
}
开篇说了,要和手头华为快充配套,那自然是大小,材质一样咯。
话说立创EDA不能导出STEP是真蛋疼,三维模型折腾了好久,话说有兴趣可以开一贴,如何从立创EDA导出三维模型(很烦很烦)。
调压测试,最高25V.
平平无奇的STM32开发板
囊中羞涩的我并没有买蓝牙模块,蓝牙原计划选的是易佰特E14 BT05。
显示面板焊接不要太久,我有个贼漂亮的橙色OLED被我焊的光衰了,基本看不出了字,谁有橙色0.91 SPI OLED的购买方式麻烦私我一下,谢谢啦。
警报灯亮起,屏幕显示温度计图标。
警报灯亮起,屏幕显示电流,输出切断。
看官老爷对不住,实在没抓拍到。
输出10.10V,实测10.08~10.11浮动,可以接受,加个大电容会好一些。
该表通过中键切换模式,有电流/电压、温度/功率、循环三档。
左右键选择电压,步进0.5v.
https://download.csdn.net/download/YVONNEOH/12740947
https://download.csdn.net/download/YVONNEOH/12740952
https://download.csdn.net/download/YVONNEOH/12740960
外壳还没加工好,待更,勿催。。。
最后,在评论区评论+暗号awsl,抽一个人送一套PCB邮费自理,截止日期2020.9.10.