单片机非ADC方式实现温度测量

背景

最近在一个新的开发项目中,因一个需要一个简小的外联功能模块,考虑到体积尺寸和单一的功能需求,故而思琢采用性价比较高的STC51单片机。项目需求中需要用到温度测量,但是后来发现,自身未带AD口,无奈,遂想到之前在图书馆借阅的老外著作的《模拟电路》中有讲解到使用GPIO+TIMER+RC实现温度测量的原理步骤,当时觉得就蛮新奇的,在此之前也听闻过网友使用此法实现了温度测量,效果说也不错,于是就决定尝试使用该方法。


原理介绍

如下,已知电容充电放电时间计算公式:Vt=V0+(Vu-V0)* [1-exp(-t/RC)]
其中:
V0 为电容上的初始电压值;
Vu 为电容充满终止电压值;
Vt为任意时刻t,电容上的电压值。

  • 如果电压为E的电池通过电阻R初值为0的电容C充电 V0=0,充电极限Vu=E
    故任意时刻t,电容上的电压为:
    Vt=E*[1-exp(-t/RC)] (2)
    t=RCLn[E/(E-Vt)] (3)
    如果已知某时刻电容上的电压Vt,根据常数可以计算出时间t
    分别计算两路的充电时间Tntc、Tref;
    式(3)有:Rntc=Rref*(Tntc/Tref)

然后根据手册查表即可得到当前温度。


硬件原理

可能当时记漏啦,后来PCB样板回来后,发现我画的原理,使用的是两个GPIO口,如图1,
在这里插入图片描述
但是后来翻看那本书,上面使用的是三个GPIO口,如图2。
在这里插入图片描述
为了保证先实现,遂在PCB上找一个空GPIO引脚飞一下。按照书上的步骤,最后总算实现了对环境温度的测量,无论是精度,稳定度,效果还是不错的。

在实现完这个之后,我就想了想,应该是可以用两个GPIO口实现温度测量,因为我那个漏掉的GPIO口,按书上逻辑是直接用来给电容C直接充放电通道的。
但是实际应该也可以使用上面两个自身的GPIO口就可以进行充放电动作。故而我更改了一下思路步骤,嘿嘿,最后还真实现啦,效果跟三线制一样的效果。

当然该方法可以在AD资源匮乏的情况下使用,如有AD建议使用AD采集方式更为可靠。


参考软件代码
  • 两线制法
//-----two wire
#define Rntc_CAP_DISCHARGE()  {P34_PP_MODE();GP1=0;Delay1ms(5);}  //GP1 //use for charging and discharging port//the capitor discharge equel to zero;
#define Rntc_CAP_CHARGE()     {P34_PP_MODE();GP1=1;}

#define Rref_CAP_DISCHARGE()  {P35_PP_MODE();GP2=0;Delay1ms(5);} 
#define Rref_CAP_CHARGE()     {P35_PP_MODE();GP2=1;}

void two_element_charge_action()
{
	ntc_charge_over_flag=0;//NTC路 充电完成标志置初值0
	ntc_charge_t=0;//NTC路 充电时间置初值0
	P35_HZ_MODE();//GPIO设置为高阻抗模式
	Rntc_CAP_DISCHARGE();//NTC路 电容放完电
	Rntc_CAP_CHARGE();//对电容充电
  	while(0==GP2)//开启计时并检测GPIO电平的翻转
	{
		T0_ON();
	}
 	T0_OFF();	//关闭计时 
	ntc_charge_over_flag=1;//充电完成标志置位
	ref_charge_over_flag=0;//REF路 充电完成标志置初值0
	ref_charge_t=0;//REF路 充电时间置初值0
	P34_HZ_MODE();//GPIO设置为高阻抗模式
	Rref_CAP_DISCHARGE();//REF路 电容放完电
	Rref_CAP_CHARGE();//对电容充电
  	while(0==GP1)//开启计时并检测GPIO电平的翻转
	{
		T0_ON();
	}
	 T0_OFF();	//关闭计时 		
	ref_charge_over_flag=1;		//充电完成标志置位
}
  • 三线制法
//-----three wire 
#define CAP_INIT_CLEAR()    {P12_PP_MODE();GP0=0;Delay1ms(1);}//set the GPIO mode as PP;//as discharge channal of the capitor 
#define NTC_TO_CAP_CHARGE() {P34_PP_MODE();GP1=1;}//the capitor start to charge
#define REF_TO_CAP_CHARGE() {P35_PP_MODE();GP2=1;}
void three_element_charge_action(void)//
{ 
	ntc_charge_over_flag=0;//NTC路 充电完成标志置初值0
	ntc_charge_t=0;//NTC路 充电时间置初值0
	P34_HZ_MODE();//GPIO设置为高阻抗模式
	P35_HZ_MODE();//GPIO设置为高阻抗模式
	CAP_INIT_CLEAR() ;//电容先放完电
	P12_HZ_MODE();//GPIO设置为高阻抗模式
	NTC_TO_CAP_CHARGE();//对电容充电
	while(0==GP0)//开启计时并检测GPIO电平的翻转
	{
		T0_ON();
	}
	T0_OFF();	//关闭计时
	ntc_charge_over_flag=1;//充电完成标志置位
	//-----------ntc resistor test over!-------------//
	ref_charge_over_flag=0;//对比路 充电完成标志置初值0
	ref_charge_t=0;//对比路 充电时间置初值0
	P35_HZ_MODE();//GPIO设置为高阻抗模式
	P34_HZ_MODE();//GPIO设置为高阻抗模式
	CAP_INIT_CLEAR() ;//电容先放完电
	P12_HZ_MODE();//GPIO设置为高阻抗模式
	REF_TO_CAP_CHARGE();//对电容充电
	while(0==GP0)//开启计时并检测GPIO电平的翻转
	{
	T0_ON();			
	}	
	T0_OFF();	//关闭计时
	ref_charge_over_flag=1;		//充电完成标志置位
	P35_HZ_MODE();//GPIO设置为高阻抗模式
//--------ref resistor test over!-------------------//	
}
  • 主函数
void main(void)
{
    EA=0;
	P37_PP_MODE();
	P36_PP_MODE();
	timer0_Init();
	timer1_Init();
	EA = 1;
	while(1)
	{
		two_element_charge_action();
//		three_element_charge_action();
	}
}

  • 定时时基配置
/* 
**t_base=(2^16-X)/f0/12(us)
**set:f0=12MHz
**x=65535  t_base=1us;
*/
#define T0_ON()  {TR0 = 1;ET0 =1;}
#define T0_OFF() {TR0 = 0;ET0 =0;}
void timer0_Init(void) //use for charge timer
{
    TMOD |= 0x00;   //模式0
    TL0 = 0xff;                
    TH0 = 0xff;
    T0_OFF();	
}

/*
** t_base=(2^16-X)/f0/12(us)
**set:f0=12MHz
**x=65535  t_base=1ms;
*/
#define T1_ON()  {TR1 = 1;ET1 =1;}
#define T1_OFF() {TR1 = 0;ET1 =0;}
void timer1_Init(void)
{
	TMOD|= 0x00;  //模式0	
	TL1 = 0x66;                 
	TH1 = 0xfc;
	T1_ON();
}
  • 定时中断处理
void TM0_Isr() interrupt 1 
{

	if(0==ntc_charge_over_flag)  ntc_charge_t++;
	if(0==ref_charge_over_flag)  ref_charge_t++;
}

void TM1_Isr() interrupt 3
{
	seg_fresh_t++;
	if(0==seg_fresh_t%5)//
	{
		seg_fresh_t=0;
		if(ntc_charge_over_flag&&ref_charge_over_flag)
		cur_temp_equel_resistor_val=(float)(ntc_charge_t*1.0/ref_charge_t*1.0)*10.0;//10.0即Rref的阻值(KR)
		cur_temp_val=Find_Tab(cur_temp_equel_resistor_val,tempH_tab,69);
		DisplayScanHc595();
     }
}
  • 注意这个是NTC热敏电阻datasheet中自带的R-T对应表,不同于AD采集方法变换后的ADC-R对应表
const float xdata tempH_tab[] = {
//0		    1		   2		 3		  4		   5		   6		   7	   8		  9	
32.116,30.570,29.105,27.716,26.399,25.150,23.965,22.842,21.776,20.764,
//10		11	 12		 13		 14		 15		 16		 17		 18		 19	
19.783,18.892,18.026,17.204,16.423,15.681,14.976,14.306,13.669,13.063,
//20   21    22    23    24   25   26   27    28    29
12.487,11.939,11.418,10.449,10,9.571,9.164,8.775,8.405,
//30    31    32    33    34   35    36     37   38    39  
8.052,7.716,7.396,7.090,6.798,6.520,6.255,6.002,5.760,5.529,
//40    41    42    43    44    45    46    47   48    49
5.309,5.098,4.897,4.704,4.521,4.345,4.177,4.016,3.863,3.716,
//50   51     52   53     54    55    56    57    58   59
3.588,3.440,3.311,3.188,3.069,2.956,2.848,2.744,2.644,2.548,
//60   61     62    63    64   65    66   67   68   69
2.457,2.369,2.284,2.204,2.126,2.051,1.980,1.911,1.845,1.782,
};
  • 相关变量定义及声明
float cur_temp_equel_resistor_val=0;
unsigned int ntc_charge_t=0; //ntc resistor  charge time 
unsigned int ref_charge_t=0; //ref resistor  charge time 
unsigned int seg_fresh_t=0;//segment fresh time
bit ntc_charge_over_flag=0;
bit ref_charge_over_flag=0;

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值