STC8G1K08 实现ADC采集电压(主要是讲解思路)

该代码示例展示了如何使用STC8G1K08芯片内置的10位ADC来实时监测电池和电容的电压。当电池电压达到特定阈值时,点亮或关闭相应的电量指示灯。通过ADC初始化、电源控制和转换结果获取函数实现电压检测,并使用中断处理ADC转换结束。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.项目背景

使用STC8G1K08自带的10位ADC采集电池电压和电容电压,实时监测电池电压和电容电压的电量情况;

①当电池电压等于14.8V时则点亮电池电量指示灯,低于13.2V时则关闭,介于中间,则闪烁。

②当电容电压大于360V时则点亮电容电量指示灯,低于330V则关闭,介于中间,则闪烁。

电路连接情况:芯片的19脚、20脚分别连接电池和电容,16、15脚分别连接的是电池和电容指示灯。

二.相关代码

ADC.C

#include	"ADC.h"


//========================================================================
// 函数: void	ADC_Inilize(ADC_InitTypeDef *ADCx)
// 描述: ADC初始化程序.
// 参数: ADCx: 结构参数,请参考adc.h里的定义.
// 返回: none.
//========================================================================
void	ADC_Inilize(ADC_InitTypeDef *ADCx)
{
	ADCCFG = (ADCCFG & ~ADC_SPEED_2X16T) | ADCx->ADC_Speed;
	if(ADCx->ADC_Power == ENABLE)	ADC_CONTR |= 0x80;
	else							ADC_CONTR &= 0x7F;
	if(ADCx->ADC_AdjResult == ADC_RIGHT_JUSTIFIED)	ADCCFG |=  (1<<5);	//AD转换结果右对齐。
	else									ADCCFG &= ~(1<<5);	//AD转换结果左对齐。 
	if(ADCx->ADC_Interrupt == ENABLE)	EADC = 1;			//中断允许		ENABLE,DISABLE
	else								EADC = 0;
	if(ADCx->ADC_Priority > Priority_3)	return;	//错误
	ADC_Priority(ADCx->ADC_Priority);	//指定中断优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3

	if(ADCx->ADC_SMPduty > 31)	return;	//错误
	if(ADCx->ADC_CsSetup > 1)	return;	//错误
	if(ADCx->ADC_CsHold > 3)	return;	//错误
	P_SW2 |= 0x80;
	ADCTIM = (ADCx->ADC_CsSetup << 7) | (ADCx->ADC_CsHold << 5) | ADCx->ADC_SMPduty ;		//设置 ADC 内部时序,ADC采样时间建议设最大值
	P_SW2 &= 0x7f;
}


//========================================================================
// 函数: void	ADC_PowerControl(u8 pwr)
// 描述: ADC电源控制程序.
// 参数: pwr: 电源控制,ENABLE或DISABLE.
// 返回: none.
//========================================================================
void	ADC_PowerControl(u8 pwr)
{
	if(pwr == ENABLE)	ADC_CONTR |= 0x80;
	else				ADC_CONTR &= 0x7f;
}

//========================================================================
// 函数: u16	Get_ADCResult(u8 channel)
// 描述: 查询法读一次ADC结果.
// 参数: channel: 选择要转换的ADC.
// 返回: ADC结果.
//========================================================================
u16	Get_ADCResult(u8 channel)	//channel = 0~15
{
	u16	adc;
	u8	i;

	if(channel > ADC_CH15)	return	4096;	//错误,返回4096,调用的程序判断	
	ADC_RES = 0;
	ADC_RESL = 0;

	ADC_CONTR = (ADC_CONTR & 0xf0) | ADC_START | channel; 
	NOP(10);			//对ADC_CONTR操作后等待会儿再访问

	for(i=0; i<250; i++)		//超时
	{
		if(ADC_CONTR & ADC_FLAG)
		{
			ADC_CONTR &= ~ADC_FLAG;
			if(ADCCFG &  (1<<5))		//转换结果右对齐。 
			{
				adc = ((u16)ADC_RES << 8) | ADC_RESL;
			}
			else		//转换结果左对齐。 
			{
				#if ADC_RES_12BIT==1
					adc = (u16)ADC_RES;
					adc = (adc << 4) | ((ADC_RESL >> 4)&0x0f);
				#else
					adc = (u16)ADC_RES;
					adc = (adc << 2) | ((ADC_RESL >> 6)&0x03);
				#endif
			}
			return	adc;
		}
	}
	return	4096;	//错误,返回4096,调用的程序判断
}


//========================================================================
// 函数: void ADC_int(void) interrupt ADC_VECTOR
// 描述: ADC中断函数.
// 参数: none.
// 返回: none.
//========================================================================
void ADC_int (void) interrupt ADC_VECTOR
{
	ADC_CONTR &= ~ADC_FLAG;
}


ADC.h

#ifndef	__ADC_H
#define	__ADC_H

#include	"config.h"

#define ADC_RES_12BIT	1		//0: MCU为10位ADC; 1: MCU为12位ADC


#define	ADC_P10		0x01	//IO引脚 Px.0
#define	ADC_P11		0x02	//IO引脚 Px.1
#define	ADC_P12		0x04	//IO引脚 Px.2
#define	ADC_P13		0x08	//IO引脚 Px.3
#define	ADC_P14		0x10	//IO引脚 Px.4
#define	ADC_P15		0x20	//IO引脚 Px.5
#define	ADC_P16		0x40	//IO引脚 Px.6
#define	ADC_P17		0x80	//IO引脚 Px.7
#define	ADC_P1_All	0xFF	//IO所有引脚

#define ADC_POWER	(1<<7)	//ADC 电源
#define ADC_START	(1<<6)	//ADC 转换启动控制位。自动清0
#define ADC_FLAG	(1<<5)	//ADC 转换结束标志位。软件清0
#define ADC_EPWMT	(1<<4)	//使能 PWM 同步触发 ADC 功能
#define ADC_CH0		0
#define ADC_CH1		1
#define ADC_CH2		2
#define ADC_CH3		3
#define ADC_CH4		4
#define ADC_CH5		5
#define ADC_CH6		6
#define ADC_CH7		7
#define ADC_CH8		8
#define ADC_CH9		9
#define ADC_CH10	10
#define ADC_CH11	11
#define ADC_CH12	12
#define ADC_CH13	13
#define ADC_CH14	14
#define ADC_CH15	15

#define ADC_SPEED_2X1T		0			//SYSclk/2/1
#define ADC_SPEED_2X2T		1			//SYSclk/2/2
#define ADC_SPEED_2X3T		2			//SYSclk/2/3
#define ADC_SPEED_2X4T		3			//SYSclk/2/4
#define ADC_SPEED_2X5T		4			//SYSclk/2/5
#define ADC_SPEED_2X6T		5			//SYSclk/2/6
#define ADC_SPEED_2X7T		6			//SYSclk/2/7
#define ADC_SPEED_2X8T		7			//SYSclk/2/8
#define ADC_SPEED_2X9T		8			//SYSclk/2/9
#define ADC_SPEED_2X10T		9			//SYSclk/2/10
#define ADC_SPEED_2X11T		10		//SYSclk/2/11
#define ADC_SPEED_2X12T		11		//SYSclk/2/12
#define ADC_SPEED_2X13T		12		//SYSclk/2/13
#define ADC_SPEED_2X14T		13		//SYSclk/2/14
#define ADC_SPEED_2X15T		14		//SYSclk/2/15
#define ADC_SPEED_2X16T		15		//SYSclk/2/16

#define ADC_LEFT_JUSTIFIED		0		//ADC Result left-justified
#define ADC_RIGHT_JUSTIFIED		1		//ADC Result right-justified


typedef struct
{
	u8	ADC_SMPduty;		//ADC 模拟信号采样时间控制, 0~31(注意: SMPDUTY 一定不能设置小于 10)
	u8	ADC_Speed;			//设置 ADC 工作时钟频率	ADC_SPEED_2X1T~ADC_SPEED_2X16T
	u8	ADC_Power;			//ADC功率允许/关闭	ENABLE,DISABLE
	u8	ADC_AdjResult;	//ADC结果调整,	ADC_LEFT_JUSTIFIED,ADC_RIGHT_JUSTIFIED
	u8	ADC_Priority;			//优先级设置	Priority_0,Priority_1,Priority_2,Priority_3
	u8	ADC_Interrupt;	//中断允许	ENABLE,DISABLE
	u8	ADC_CsSetup;		//ADC 通道选择时间控制 0(默认),1
	u8	ADC_CsHold;			//ADC 通道选择保持时间控制 0,1(默认),2,3
} ADC_InitTypeDef;

void	ADC_Inilize(ADC_InitTypeDef *ADCx);
void	ADC_PowerControl(u8 pwr);
u16		Get_ADCResult(u8 channel);	//channel = 0~15

#endif

main.c

GPIO和ADC相关配置

#include  "Timer.h"

/******************* IO配置函数 *******************/
void	GPIO_config(void)
{
	GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义
	//AD口设置为输入口,U5的19脚为电池组电量检测-P1.0,U5的20脚为电容组电量检测-P1.1
	GPIO_InitStructure.Pin  = GPIO_Pin_0 | GPIO_Pin_1 ;		//指定要初始化的IO, GPIO_Pin_0 ~ GPIO_Pin_7
	GPIO_InitStructure.Mode = GPIO_HighZ;	//浮空输入 :被选择为ADC输入通道的IO扣,必须设置PxM0,PxM1寄存器将IO口模式设置为高阻输入模式。
	GPIO_Inilize(GPIO_P1,&GPIO_InitStructure);	//初始化

	//U5的15脚点亮CN2电容电量指示灯,低于330V时则关闭。16脚点亮CN1电池电量指示灯,低于13.2V时则关闭。15脚-P3.4,16脚-P3.5
	GPIO_InitStructure.Pin = GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStructure.Mode = GPIO_OUT_PP;//推挽输出
	GPIO_Inilize(GPIO_P3,&GPIO_InitStructure);
}

/******************* AD配置函数 *******************/
void	ADC_config(void)
{
	//使用的时ADC0、ADC1 - P1.0和P1.1
	ADC_InitTypeDef		ADC_InitStructure;		//结构定义
	ADC_InitStructure.ADC_SMPduty   = 31;		//ADC 模拟信号采样时间控制, 0~31(注意: SMPDUTY 一定不能设置小于 10)
	ADC_InitStructure.ADC_CsSetup   = 0;		//ADC 通道选择时间控制 0(默认),1
	ADC_InitStructure.ADC_CsHold    = 1;		//ADC 通道选择保持时间控制 0,1(默认),2,3
	ADC_InitStructure.ADC_Speed     = ADC_SPEED_2X1T;		//设置 ADC 工作时钟频率	ADC_SPEED_2X1T~ADC_SPEED_2X16T
	ADC_InitStructure.ADC_Power     = ENABLE;				//ADC功率允许/关闭	ENABLE,DISABLE 打开ADC电源
	ADC_InitStructure.ADC_AdjResult = ADC_RIGHT_JUSTIFIED;	//ADC结果调整,	ADC_LEFT_JUSTIFIED,ADC_RIGHT_JUSTIFIED
	ADC_InitStructure.ADC_Priority    = Priority_0;			//指定中断优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
	ADC_InitStructure.ADC_Interrupt = DISABLE;			//中断允许	ENABLE,DISABLE
	ADC_Inilize(&ADC_InitStructure);					//初始化
	ADC_PowerControl(ENABLE);							//单独的ADC电源操作函数, ENABLE或DISABLE
}

获取并计算电池、电容电压的方法:

这里特别注意的是:ADC采集的电压范围是0~Vmcu,即0V到芯片电压,本人使用的是10位ADC采集,即0~Vmcu分别对应0~1024,呈线性关系。那么如果采集的电压,比如我这里采集的是380V,超过了ADC采集电压,那应该如何换算呢?

原理很简单,只需要把ADC采集的电压(可以用万用表测得),最后乘以一个比例就能得到真实的电压,这个比例 是从电阻分压算得(因为一般ADC采集电路外接一个电压放大电路等),比如下图:

 这个比例就是:(R17+R14)/R17 = 83.3。也就是最后ADC采集到的电压,最后需要乘以83.3。

代码实现如下:

//获取电源组电量,大于14.8V点亮指示灯,低于13.2V关闭,介于中间闪烁
float Get_Vbat(void)
{
	unsigned int res;
	float Vbat;
	
	ADC_CONTR = 0x80;  //使能ADC,打开ADC电源,并选择ADC0-P1.0
	ADC_CONTR |= 0x40; //开始采集
	while(!(ADC_CONTR & 0x20)); //等待ADC转换结束
	ADC_CONTR &= ~0x20; //清空ADC转换结束标志位。
	res =  Get_ADCResult(0);//(ADC_RES << 8) | ADC_RESL; //获取ADC0采集结果。 或者寄存器操作:res = (ADC_RES <<8 ) | ADC_RESL
	//将12位ADC转换结果 反推 ADC被转换通道的输入电压Vin
	Vbat =  ((3.3 / ADC1024)*res)*11;
	return Vbat;  //
}
//获取电容组电量,大于360V 点亮指示灯,小于330V关闭,介于中间闪烁
float Get_Vcap(void)    
{
	unsigned int res;
	float Vcap;
	
	ADC_CONTR = 0x81;  //使能ADC,打开ADC电源,并选择ADC1-P1.1
	ADC_CONTR |= 0x40; //开始采集
	while(!(ADC_CONTR & 0x20)); //等待ADC转换结束
	ADC_CONTR &= ~0x20; //清空ADC转换结束标志位。
	res =  Get_ADCResult(0); //(ADC_RES<<8) | ADC_RESL; //获取ADC1采集结果。 或者寄存器操作:res = (ADC_RES <<8 ) | ADC_RESL
	Vcap = ((3.3 / ADC1024)*res)*84.33;
	return Vcap;
}

void Is_Vbat(float Vbat)
{
	if(Vbat >= 14.8)
	{
		delay_ms(10);
		if(Vbat >= 14.8)
			P35 = 0;
	}
	else if(Vbat < 13.2)
	{
		delay_ms(10);
		if(Vbat < 13.2)
		  P35 = 1;
	}
	else
	{
		P35 = 0;
		delay_ms(500);
		P35 = 1;
		delay_ms(500);
	}
}

void Is_Vcap(float Vcap)
{
	if(Vcap >= 360)
	{
		delay_ms(10);
		if(Vcap >= 360)
		P34 = 0;
	}
	else if(Vcap < 330)
	{
		delay_ms(10);
		if(Vcap < 330)
		P34 = 1;
	}
	else
	{
		P34 = 0;
		delay_ms(500);
		P34 = 1;
		delay_ms(500);
	}
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值