嘉立创产业服务站群
站内搜索
发作品签到
专业版

基于立创地文星CW32的数字电压电流表

工程标签

110
0
0
1

简介

基于立创地文星CW32的数字电压电流表

简介:基于立创地文星CW32的数字电压电流表
电压电流表训练营【立创开发板&CW32】

开源协议

GPL 3.0

创建时间:2024-07-18 00:53:50更新时间:2024-08-22 06:43:40

描述

一、简介

系统以 CW32F030单片机作为系统核心处理器,可实现对电压电流的同时采集。可由外部电源DC供电,也可由被测电源供电,测量电源的同时给电压电流表供电。内置不同量程档位,且可以拿其他标准化器件进行手动校准,将校准的参数存入Flash中,确保掉电不丢失。

二、功能及实物图

电压能实现30V以内的测量,电流能实现3A以内的测量
本工程分为6个工作模式
模式0:实现压流同采,数码管上排显示电压,数码管下排显示电流,且可以切换电压测量的量程,30V档位,12V档位,3V档位之间切换,量程越小测量越精准。
模式1:高精度电流采集模式,可以显示到小数点后四位
模式2:5V标定设置,将电压表接入,ADC采集电压与电压表可能会有误差,拧动滑动变阻器,使电压表测量值为5V,将此时的读取出来的ADC值做保存处理并且与5V对应起来
模式3:对15V电压做校准
模式4:对0.5A电流做校准
模式5:对1A电流做校准

实物图展示

封面.jpg
96623f212262ea91497dd0c132785ba.jpg

abb76254380b7366c40bf3110d19ad3.jpg

三、核心电路PCB设计说明

1、供电电路

1.png
本项目使用LDO作为电源,考虑到实际的电压表头产品多在24V或36V供电的工业场景中应用,本项目选择了最高输入电压高达40V的SE8550K2作为电源。本项目没有使用DCDC降压电路来应对大压差的主要原因为避免设计过程中引入DCDC的纹波干扰,次要原因为降低项目成本。

2、电压采样电路

2.png
电压采样电路ADC读取选取1.5V为基准电压,由于MCU引脚无法测量这么大的电压,因此采用分压电路,读取完成之后根据分压电路的原理进行计算,可算出被测电压的大小,拿30v档位举例,具体计算如下:

d0c91ff6afd9626984b18712ff40609.png

3、电流采样电路

3.png
电流测量本质还是测电压,将测出的电压经过运算算出电流,量程是测0-3A的电流,最大电流为3A,经过P=IIR,算出功率为900mw,因此在次电阻选型时要注意。

4、电压电流的标定与校准

4.png
当插入跳线帽时,加入滑动变阻器,扭动滑动变阻器可改变电压电流大小,用标准化器件做对比可以进行数值标定校准。

5、核心板-CW32

5.png
测量电压电流,都是采集的模拟量,我们可以通过使用MCU进行模拟量的采集,然后转为数字量,也就是ADC模数转化,将数字量进行运算可以使用显示。

6、PCB注意事项

如图中蓝线走向,由于点解电容与陶瓷电容要进行一个滤波的作用,在进行PCB连线时,不能直接就近连接,需要按顺序依次经过电容的一侧,最后进入SE8550,做到滤波的作用
29d334fe4572277df7537ef26e23d43.png

四、重要程序

此程序采用FreeRTOS操作系统进行编程

a95cc30ffcc6d3ddae3fe0402d9b7e5.png

1、变量定义及注释

/*******
Mode=0:压流同采
Mode=1:高精度电流采集
Mode=2:5V标定调整模式
Mode=3:15V标定调整模式
Mode=4:0.5A标定调整模式
Mode=5:1.5A标定调整模式
*******/

/********
K1:切换模式加
K2:在模式0下可换电压档位 30V  12V  3V档位  
K3:在进入电压电流标定模式时候(Mode=2,3,4,5)
K4:切换模式减
*********/
TaskHandle_t Task1_Handle=NULL;
TaskHandle_t Task2_Handle=NULL;
TaskHandle_t Task3_Handle=NULL;

uint8_t Seg_Table[22] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f,
                         0xbf, 0x86, 0xdb, 0xcf, 0xe6, 0xed, 0xfd, 0x87, 0xff, 0xef,
                         0x77,0xBE}; //A U.
uint8_t Seg_Table_Buff[6]={9,8,7,6,5,4};

float V=0,I=0;    //真实电压
uint32_t V_d,I_d;  //降小数值转化为整数值
uint32_t V_ADC0,I_ADC;  //ADC读取出来的数字值


uint8_t Flag_Key1 = 0,Flag_Key2 = 0,Flag_Key3 = 0,Flag_Key4 = 0,Mode=0; 
uint8_t SMG_Display_Flag=0,KeyNum=0;

//5V与15V 校准          X轴为ADC读取数字量  Y轴为真实电压值
/*由于Y轴值用5V与15V作标定,此程序的x轴值,全部由30V档位测量得出的ADC值,其他档位不适用*/
/*想要每个量程做标定,只需选取两个量程之内的点,进行公式运算做适配即可*/
unsigned int X05=0;
unsigned int X15=0;

unsigned int Y15=15;          
unsigned int Y05=5;
float K; //斜率

/*电流也是用电压算,最后将算出来的电压值除阻值*/
//0.5A与1.5A 校准
unsigned int IX05=0;
unsigned int IX15=0;

//unsigned int IY15=150;
//unsigned int IY05=50;
float  IY15=0.15;
float  IY05=0.05;
float KI; //斜率


ADC_InitTypeDef ADC_InitStructure;
ADC_SingleChTypeDef ADC_SingleChStructure;
ADC_SerialChTypeDef ADC_SerialChStructure;

2、函数与线程的作用

5e60523184664a7315ba3d0cb8f637f.png

3、标定理论值计算

/*
  理论值计算过程,对应原理图30V挡位计算  设ADC读取电压值为VIN0  AD为读取的数字量   输入电压值以Y15=15V标准计算
   AD     VIN0     15         VIN0                15*4096
  —— =  ——     ——   =    ——         算出AD=-------       AD值也就是此时15V输入电压计算出对应的数字量X15,对应X轴
  4096    1.5			220+10       10									1.5*23
*/
void read_vol_cur_calibration(void)
{
    uint16_t da[5];
    flash_read(0,da, 5);
	  if(da[0]!=0xaa)//还没校准过时,计算理论值,并存储
		{
			X15=15.0/23/1.5*4096;                   
			X05=5.0/23/1.5*4096;
			IX05=0.5/1.5*4096;
			IX15=1.5/1.5*4096;

      save_calibration();
		}
		else 
		{
			X05=da[1];
			X15=da[2];
			IX05=da[3];
			IX15=da[4];
		}
		
}

4、模式的切换及标定值存储

void KEY(void *param)
{
  while(1)
	{
	  KeyNum=KEY_Scan();
		/*切换模式
		Mode=0:压流同采
		Mode=1:高精度电流采集
		Mode=2:5V标定调整模式
		Mode=3:15V标定调整模式
		Mode=4:0.5A标定调整模式
		Mode=5:1.5A标定调整模式
		*/
		if(KeyNum==1)                                   
		{
		  Mode++;
			if(Mode>5)
			{
			  Mode=0;
			}
			if(Mode==2||Mode==3)                                  //为电压标定模式时,自动切换为30V档位
			{
			  ADC_SerialChStructure.ADC_Sqr0Chmux  = ADC_SqrCh0; 
				ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure);
			}
		}
		
		else if(KeyNum==2&&Mode==0)                        //切换电压采集挡位
		{
		  SMG_Display_Flag++;			
			
			if(SMG_Display_Flag>2)
			{
			  SMG_Display_Flag=0;
			}
			
			if(SMG_Display_Flag==0)
			{
				ADC_SerialChStructure.ADC_Sqr0Chmux  = ADC_SqrCh0; 
				ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure);
			}
			else if(SMG_Display_Flag==1)
			{
				ADC_SerialChStructure.ADC_Sqr0Chmux  = ADC_SqrCh1; 
				ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure);
			}
			else if(SMG_Display_Flag==2)
			{
				ADC_SerialChStructure.ADC_Sqr0Chmux  = ADC_SqrCh2; 
				ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure);
			}

		}
		
		else if(KeyNum==3)
		{
		  if(Mode==2)
			{
				X05=Get_Adc_Average(10,ADC_GET_V);
				save_calibration();
				ComputeK();
			}
			else if(Mode==3)
			{
			  X15=Get_Adc_Average(10,ADC_GET_V);
				save_calibration();
				ComputeK();			
			}
			else if(Mode==4)
			{
			  IX05=Get_Adc_Average(10,ADC_GET_I);
				save_calibration();
				ComputeK();			
			}
			else if(Mode==5)
			{
			  IX15=Get_Adc_Average(10,ADC_GET_I);
				save_calibration();
				ComputeK();			
			}
		}
		
		else if(KeyNum==4)
		{
		  Mode--;
			if(Mode>10)
			{
			  Mode=5;
			}
			if(Mode==2||Mode==3)                                  //为电压标定模式时,自动切换为30V档位
			{
			  ADC_SerialChStructure.ADC_Sqr0Chmux  = ADC_SqrCh0; 
				ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure);
			}
		}
		vTaskDelay(1);
	}
}

5、ADC读取及转化

void ADCIN(void *param)
{
		float KT1;
	
	  __RCC_GPIOA_CLK_ENABLE();	// 使能GPIO时钟	
	  __RCC_ADC_CLK_ENABLE();   //使能ADC时钟
	

	  PA00_ANALOG_ENABLE(); 
	  PA01_ANALOG_ENABLE(); 
	  PA02_ANALOG_ENABLE(); 
	  
	  
	  ADC_InitStructure.ADC_OpMode = ADC_SingleChOneMode;  //单通道单次转换模式
		ADC_InitStructure.ADC_ClkDiv = ADC_Clk_Div128;         // 时钟频率 = PCLK / 4 = 64MHz / 4 = 16MHz
		ADC_InitStructure.ADC_SampleTime = ADC_SampTime10Clk; //10个ADC时钟周期
		ADC_InitStructure.ADC_VrefSel = ADC_Vref_BGR1p5;       //VDDA参考电压
		ADC_InitStructure.ADC_InBufEn = ADC_BufDisable;      //关闭跟随器
		ADC_InitStructure.ADC_TsEn = ADC_TsDisable;          //关闭内置温度传感器
		ADC_InitStructure.ADC_DMAEn = ADC_DmaDisable;        //不触发DMA
		ADC_InitStructure.ADC_Align = ADC_AlignRight;        //ADC转换结果右对齐
		ADC_InitStructure.ADC_AccEn = ADC_AccDisable;        //转换结果累加不使能
	  
			
		  
		ADC_SerialChStructure.ADC_Sqr0Chmux  = ADC_SqrCh0;      //配置ADC序列,PB01是ADC的第9通道
        ADC_SerialChStructure.ADC_Sqr1Chmux  = ADC_SqrCh4;
        ADC_SerialChStructure.ADC_SqrEns     = ADC_SqrEns01;
        ADC_SerialChStructure.ADC_InitStruct = ADC_InitStructure; //ADC初始化
	    ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure);

     ADC_ClearITPendingAll();          	
   	 ADC_Enable();
     ADC_SoftwareStartConvCmd(ENABLE);    //启动ADC转换
  while(1)
  {
		/*压流同采*/
		if(Mode==0)
		{
			if(SMG_Display_Flag==0)                             //30V档位
			{
				V_ADC0=Get_Adc_Average(10,ADC_GET_V);
				printf("Flag0:%d\r\n",V_ADC0);
//				V=(V_ADC0*1.5)/4096*(220+10)/10;
					if(V_ADC0>=X05)
					{
            V=(V_ADC0-X05)*K+Y05;   //y = k×(Xad– X1)+ y1    测量的ADC值是X,求出Y真实电压
					}
					else
					{ 
						KT1=5;
						KT1=KT1/X05;
						V=KT1*V_ADC0;
					}
			}
			else if(SMG_Display_Flag==1)                       //12V档位
			{
				V_ADC0=Get_Adc_Average(10,ADC_GET_V);
				printf("Flag1:%d\r\n",V_ADC0);
				V=(V_ADC0*1.5)/4096*(71.5+10)/10;
			}
			else if(SMG_Display_Flag==2)                       //3V档位
			{
				V_ADC0=Get_Adc_Average(10,ADC_GET_V);
				printf("Flag2:%d\r\n",V_ADC0);
				V=(V_ADC0*1.5)/4096*(10+10)/10;
			}
			
			I_ADC=Get_Adc_Average(10,ADC_GET_I);
			printf("电流:%d\r\n",I_ADC);
//			I=1.5/4096*I_ADC*10;
						
			if(I_ADC>=IX05)
			{
				I=(I_ADC-IX05)*KI+IY05;   //y = k×(Xad– X1)+ y1    测量的ADC值是X,求出Y真实电压
			}
			else
			{ 
				KT1=0.05;
				KT1=KT1/IX05;
				I=KT1*I_ADC;
			}
			I*=10;
			
			printf("V:%f\r\n",V);
			
			 if(V<10)
			 {
					V_d=V*100;
					Seg_Table_Buff[0]=V_d/100+10;
					Seg_Table_Buff[1]=V_d/10%10;
					Seg_Table_Buff[2]=V_d%10;
			 }	
			 else if(V>10)
			 {
					V_d=V*10;
					Seg_Table_Buff[0]=V_d/100;
					Seg_Table_Buff[1]=V_d/10%10+10;
					Seg_Table_Buff[2]=V_d%10;
			 }
			 
				I_d=I*100;
				Seg_Table_Buff[3]=I_d/100+10;
				Seg_Table_Buff[4]=I_d/10%10;
				Seg_Table_Buff[5]=I_d%10;
  
		}
		/*高精度5位测电流*/
		else if(Mode==1)
		{
				I_ADC=Get_Adc_Average(10,ADC_GET_I);
				printf("电流:%d\r\n",I_ADC);
//				I=1.5/4096*I_ADC*10;
				if(I_ADC>=IX05)
				{
					I=(I_ADC-IX05)*KI+IY05;   //y = k×(Xad– X1)+ y1    测量的ADC值是X,求出Y真实电压
				}
				else
				{ 
					KT1=0.05;
					KT1=KT1/IX05;
					I=KT1*I_ADC;
				}
				I*=10;
			
			
			  I_d=I*10000;
				Seg_Table_Buff[0]=20;
				Seg_Table_Buff[1]=I_d/10000+10;
				Seg_Table_Buff[2]=I_d/1000%10;
		    Seg_Table_Buff[3]=I_d/100%10;
				Seg_Table_Buff[4]=I_d/10%10;
				Seg_Table_Buff[5]=I_d%10;
		}
		
		/**5V标定模式**/
		else if(Mode==2)
		{
				V_ADC0=Get_Adc_Average(10,ADC_GET_V);
				if(V_ADC0>=X05)
				{
					V=(V_ADC0-X05)*K+Y05;   //y = k×(Xad– X1)+ y1    测量的ADC值是X,求出Y真实电压
				}
				else
				{ 
					KT1=5;
					KT1=KT1/X05;
					V=KT1*V_ADC0;
				}		  	
				Seg_Table_Buff[0]=21;
				Seg_Table_Buff[1]=5+10;
				Seg_Table_Buff[2]=0;

				if(V<10)
			 {
					V_d=V*100;
					Seg_Table_Buff[3]=V_d/100+10;
					Seg_Table_Buff[4]=V_d/10%10;
					Seg_Table_Buff[5]=V_d%10;
			 }	
			 else if(V>10)
			 {
					V_d=V*10;
					Seg_Table_Buff[3]=V_d/100;
					Seg_Table_Buff[4]=V_d/10%10+10;
					Seg_Table_Buff[5]=V_d%10;
			 }
		}
		
		else if(Mode==3)
		{
				V_ADC0=Get_Adc_Average(10,ADC_GET_V);
				if(V_ADC0>=X05)
				{
					V=(V_ADC0-X05)*K+Y05;   //y = k×(Xad– X1)+ y1    测量的ADC值是X,求出Y真实电压
				}
				else
				{ 
					KT1=5;
					KT1=KT1/X05;
					V=KT1*V_ADC0;
				}		  	
				Seg_Table_Buff[0]=21;
				Seg_Table_Buff[1]=1;
				Seg_Table_Buff[2]=5+10;

				if(V<10)
			 {
					V_d=V*100;
					Seg_Table_Buff[3]=V_d/100+10;
					Seg_Table_Buff[4]=V_d/10%10;
					Seg_Table_Buff[5]=V_d%10;
			 }	
			 else if(V>10)
			 {
					V_d=V*10;
					Seg_Table_Buff[3]=V_d/100;
					Seg_Table_Buff[4]=V_d/10%10+10;
					Seg_Table_Buff[5]=V_d%10;
			 }
		}
		
		else if(Mode==4)
		{
			I_ADC=Get_Adc_Average(10,ADC_GET_I);
						
			if(I_ADC>=IX05)
			{
				I=(I_ADC-IX05)*KI+IY05;   //y = k×(Xad– X1)+ y1    测量的ADC值是X,求出Y真实电压
			}
			else
			{ 
				KT1=0.05;
				KT1=KT1/IX05;
				I=KT1*I_ADC;
			}
			I*=10;
			I_d=I*100;
			
			Seg_Table_Buff[0]=20;
			Seg_Table_Buff[1]=0+10;
			Seg_Table_Buff[2]=5;

			Seg_Table_Buff[3]=I_d/100+10;
			Seg_Table_Buff[4]=I_d/10%10;
			Seg_Table_Buff[5]=I_d%10;
		}
		
   	else if(Mode==5)
		{
			I_ADC=Get_Adc_Average(10,ADC_GET_I);
						
			if(I_ADC>=IX05)
			{
				I=(I_ADC-IX05)*KI+IY05;   //y = k×(Xad– X1)+ y1    测量的ADC值是X,求出Y真实电压
			}
			else
			{ 
				KT1=0.05;
				KT1=KT1/IX05;
				I=KT1*I_ADC;
			}
			I*=10;
			I_d=I*100;
			
			Seg_Table_Buff[0]=20;
			Seg_Table_Buff[1]=1+10;
			Seg_Table_Buff[2]=5;

			Seg_Table_Buff[3]=I_d/100+10;
			Seg_Table_Buff[4]=I_d/10%10;
			Seg_Table_Buff[5]=I_d%10;
		}

		  vTaskDelay(300);
		
	}
}

五、实物验证

1、采用30V档位对电池进行测量,且用万用表做辅助

s1.png
可以看到万用表量出来是9.38V,而未进行标定的工程测出来是9.44V,误差较大

2、采用12V档位测电池

s2.png
可以看到与万用表两位精度丝毫不差,量程越小,测量电压越精准

3、使用3V量程

s3.png
超过限制之后显示最大量程

4、电流检测

s4.png
由于身边没有稳定电流源,读取拓展板内部产生的电流进行读取,用万用表做验证。采用高精度电流模式,可以看到万用表测出来电压是0.23V,由于器件使用100mΏ电阻,所以此时算出来电流是2.3A,数码管显示电流为2.2692A,在没有进行标定时,误差极小。

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

附件

序号文件名称下载次数
1
Voltage and current acquisition-FreeRTOS-EDA.zip
2
2
演示视频.mp4
1
克隆工程
添加到专辑
0
0
分享
侵权投诉

评论

全部评论(1)
按时间排序|按热度排序
粉丝0|获赞0
相关工程
暂无相关工程

底部导航