【龙印】在龙芯1c上用TM7705+NTC热敏电阻实现温度测量
来源:互联网 发布:production i.g 知乎 编辑:程序博客网 时间:2024/06/09 18:14
本文为在用龙芯1c做3D打印机过程中的笔记。龙芯1c做的3d打印机简称“龙印”,交流论坛地址是“http://www.openloongson.org/”,Git地址“http://git.oschina.NET/caogos/marlin_ls1c”
本文重点放在TM7705和ntc热敏电阻结合实现测量温度上,另外有一篇重点放在TM7705的驱动上。详见《【龙印】龙芯1c上双路16位AD芯片TM7705的linux驱动》http://blog.csdn.net/caogos/article/details/53034196
电路分析
手里只有在淘宝上买的“安富莱”的TM7705模块,接上NTC热敏电阻测了一下,测量电压精度还不错。测得ad=555,通过计算得到热敏电阻电压=5/1024*555=2.71v,用万用表测得NTC热敏电阻两端电压为2.66V,误差0.05V,精度还不错。
虽然这个模块测量电压精度还不错,但是用这个模块通过测量NTC热敏电阻的阻值来间接测量温度的精度确不理想。下面来看测试的一组结果
先介绍下背景知识,NTC热敏电阻测温的电路如下
如旁边的英文注释所说,通常上图的R1不接,R2=4.7k。开源3D打印机的扩展板ramps1.4中的电路也是这样的。
也就是说,只需要一个4.7k电阻和NTC热敏电阻就可以用TM7705测温了。好,接上后测得如下结果
AD=555
用万用表测得NTC热敏电阻的为138.1k。
查询“100K 3950 NTC热敏电阻阻值温度表”(这个表百度上很多)可知:当NTC阻值=138.1K时,温度约为18度。
然后用AD值查询脚本“createTemperatureLookup.py”生成的AD与温度对应的表得到的结果确不是18度。
脚本“createTemperatureLookup.py”来源于https://github.com/reprap/firmware/blob/master/createTemperatureLookup.py,注解在http://www.reprap.org/wiki/Thermistor
marlin中的“createTemperatureLookupMarlin.py”功能相同,都是生成AD与阻值的对应表。(个人)认为还是reprap firmware中的脚本“createTemperatureLookup.py”的源码更好理解。
可是脚本中的参数应该改为多少了?模块用的是2.5的基准电源,但是模拟输入端用两个10k电阻并联分压了,是不是可以认为基准电压应该为2.5*2呢?
由于龙芯1c的spi的sck线是3.3v的,所以TM7705的电源也是3.3v的,那NTC热敏电阻和4.7k电阻的电源是3.3v呢?还是5v呢?
首先可以用3.3v的,如果用5v的,可能超过tm7705测量的最大值,因为Tm7705是3.3v电源,也就是NTC热敏电阻两端电压超过3.3v后,不论是多少都是0xffffff。满量程了。
好,就接3.3v,可是把脚本“createTemperatureLookup.py”中的self.vcc改为3.3,不论把self.vadc改为2.5还是5,生成的AD和温度对应表(AD=555对应的温度)都不对。
后来发现在TM7705模块输入端不是还有两个10k电阻吗,再看看前面的NTC热敏电阻测温的标准电路图,发现TM7705模块模拟输入端的串联的两个10k电阻的“位置”有点像标准电路图中的R1.即R1=20k,如下图所示
脚本“createTemperatureLookup.py”中默认的是0,修改脚本后,重新生成,发现也不对。
没办法,只有分析脚本源码了,看看到底是哪里的问题。脚本也不复杂,关键是函数temp(self,adc),输入ad值,返回的就是温度,就三行代码如下
def temp(self,adc): "Convert ADC reading into a temperature in Celcius" v = adc * self.vadc / 1024 # convert the 10 bit ADC value to a voltage r = self.rs * v / (self.vs - v) # resistance of thermistor return (self.beta / log(r / self.k)) - 273.15 # temperature注释已经写得很清楚,第一行就是将ad值转换为电压值,第二行通过电压和流过的电流算出电阻,第三行就是根据电阻算出温度。
既然这样,那就手动算一下
AD=555,那么V=5/1024*555=2.71v,流过4.7k电阻的电流=(3.3-2.71)/4.7=0.125,流过TM7705模拟输入端两个串联的10k电阻的电流=2.71/20=0.1355。这时候你会发现,不对啊,怎么流过4.7k电阻的电流还要小一些呢?
是的,上面的计算都是理论值的计算,实际上,电源电压不是3.3v,而是3.38v;4.7k电阻的阻值是4.6k,两个串联的10k电阻的阻值应该也不是刚好20k。
基于此,把TM7705模拟输入端的两个串联的电阻取下来,并把基准电源引出来给4.7k电阻和NTC热敏电阻供电。电路如下
修改后的模块
修改前的模块
测试发现,效果不错。
我们要的是准确的AD值,与基准电压是多少没关系。可以直接给tm7705供电的3.3v电源作为基准电源,同时给4.7k电阻和NTC热敏电阻供电,只是不知道这种情况精度怎么样。
源码
好说了这么多,还是贴源码出来
temp.c
// 温度相关#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include "public.h"#define TEMP_AD_MAX ((0x1<<10)-1) // ad的最大值,ad是十位的#define TEMP_IS_VALID_AD(ad) ((TEMP_AD_MAX>=(ad)) && (0<=(ad))) // 判断ad是在量程范围内#define TEMP_BUFF_SIZE (64) // 缓存大小// TM7705的通道enum{ TM7705_CH_1 = 0, // 通道1 TM7705_CH_2 = 1 // 通道2};// 以下根据ntc热敏电阻参数用脚本生成的adc值与温度一一对应的表格// 左边为adc值,右边为温度(单位:摄氏度)// 详细请参考源码目录中的脚本"createTemperatureLookup.py"// python createTemperatureLookup.py// Thermistor lookup table for RepRap Temperature Sensor Boards (http://make.rrrf.org/ts)// Made with createTemperatureLookup.py (http://svn.reprap.org/trunk/reprap/firmware/Arduino/utilities/createTemperatureLookup.py)// ./createTemperatureLookup.py --r0=100000 --t0=25 --r1=0 --r2=4700 --beta=3950 --max-adc=1023// r0: 100000// t0: 25// r1: 0// r2: 4700// beta: 3950// max adc: 1023#define NUMTEMPS 40const short temptable[NUMTEMPS][2] = { {1, 938}, {27, 326}, {53, 269}, {79, 239}, {105, 219}, {131, 204}, {157, 192}, {183, 182}, {209, 174}, {235, 166}, {261, 160}, {287, 153}, {313, 148}, {339, 143}, {365, 138}, {391, 133}, {417, 129}, {443, 125}, {469, 120}, {495, 116}, {521, 113}, {547, 109}, {573, 105}, {599, 101}, {625, 98}, {651, 94}, {677, 90}, {703, 86}, {729, 82}, {755, 78}, {781, 74}, {807, 70}, {833, 65}, {859, 60}, {885, 54}, {911, 48}, {937, 41}, {963, 31}, {989, 18}, {1015, -8}};// 读取指定通道的ad值// @channel 通道号// @adc_p 读到的ad值// @ret 成功 or 失败int temp_get_ad(int channel, UINT16 *adc_p){ const char ch1_path[] = {"/sys/bus/spi/drivers/TM7705/spi0.1/ch1"}; const char ch2_path[] = {"/sys/bus/spi/drivers/TM7705/spi0.1/ch2"}; const char *dev_file_path = NULL; int fd = 0; int ret = 0; unsigned int value = 0; char buff[TEMP_BUFF_SIZE] = {0}; // 不同的通道对应/sys下不同的文件 if (TM7705_CH_1 == channel) { dev_file_path = ch1_path; } else { dev_file_path = ch2_path; } fd = open(dev_file_path, O_RDONLY); if (-1 == fd) { printf("[%s] open device file fail.\n", __FUNCTION__); return ERROR; } memset(buff, 0, TEMP_BUFF_SIZE); ret = read(fd, buff, TEMP_BUFF_SIZE-1); if (0 > ret) { printf("[%s] not read data. ret=%d\n", __FUNCTION__, ret); close(fd); return ERROR; } sscanf(buff, "%u\n", &value); value = value >> 6;// printf("[%s] value=%u, buff=%s\n", __FUNCTION__, value, buff); close(fd); if (!TEMP_IS_VALID_AD(value)) { printf("[%s] adc convert fail. ad=%u\n", __FUNCTION__, value); return ERROR; } *adc_p = value; // 输出ad值 return SUCCESS;}// 根据adc值计算温度值// ntc热敏电阻的阻值温度曲线被分为n段,每段可以近似为直线,// 所以温度值的计算就转变为查表再计算// @ad ad值(取值范围为0-1023)// @temp_p 温度值,单位摄氏度// @ret 成功 or 失败int temp_calc_from_ad(UINT16 ad, float *temp_p){ float celsius = 0.0; // 温度值,单位摄氏度 int i = 0; // 判断adc值是否在量程范围内 if (!TEMP_IS_VALID_AD(ad)) { return ERROR; } // 判断是否在表格所表示的范围内 if (ad < temptable[0][0]) // 小于表格的最小adc { *temp_p = temptable[0][1]; // 取最小值 return SUCCESS; } if (ad > temptable[NUMTEMPS-1][0]) // 大于表格的最大adc { *temp_p = temptable[NUMTEMPS-1][1]; // 取最大值 return SUCCESS; } // 查表 // 这里是从adc由低到高,逐个区间进行比较,没有采用折半查找 for (i=1; i<NUMTEMPS; i++) // 注意,这里是从1开始的,巧妙之处就在这里 { if (ad < temptable[i][0]) // 判断是否在这个区间 { // t = t0 + (adc-adc0)*k celsius = temptable[i-1][1] + // t0 (ad - temptable[i-1][0]) * // adc-adc0 ((float)(temptable[i][1]-temptable[i-1][1]) / (float)(temptable[i][0]-temptable[i-1][0])); // k// printf("[%s] adc=%u, celsius=%f\n", __FUNCTION__, ad, celsius); *temp_p = celsius; return SUCCESS; } } return ERROR;}// 获取温度值// @temp_p 温度值,单位摄氏度// @ret 成功 or 失败int temp_get(float *temp_p){ UINT16 ad = 0; int ret = ERROR; // 获取ad值 ret = temp_get_ad(TM7705_CH_1, &ad); if (SUCCESS != ret) { printf("[%s] get channel 1's ad fail.\n", __FUNCTION__); return ret; }// printf("[%s] ad=%u\n", __FUNCTION__, ad); // 根据ad计算温度值 return temp_calc_from_ad(ad, temp_p);}// 测试函数void temp_gest(void){ float temp = 0.0; int ret = ERROR; ret = temp_get(&temp); if (SUCCESS != ret) { printf("[%s] temp_get fail. ret=%d\n", __FUNCTION__, ret); return ; } printf("[%s] current temp=%f\n", __FUNCTION__, temp); return ;}createTemperatureLookup.py
#!/usr/bin/python## Creates a C code lookup table for doing ADC to temperature conversion# on a microcontroller# based on: http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.html"""Thermistor Value Lookup Table GeneratorGenerates lookup to temperature values for use in a microcontroller in C format based on: http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.htmlThe main use is for Arduino programs that read data from the circuit board described here:http://make.rrrf.org/ts-1.0Usage: python createTemperatureLookup.py [options]Options: -h, --help show this help --r0=... thermistor rating where # is the ohm rating of the thermistor at t0 (eg: 10K = 10000) --t0=... thermistor temp rating where # is the temperature in Celsuis to get r0 (from your datasheet) --beta=... thermistor beta rating. see http://reprap.org/bin/view/Main/MeasuringThermistorBeta --r1=... R1 rating where # is the ohm rating of R1 (eg: 10K = 10000) --r2=... R2 rating where # is the ohm rating of R2 (eg: 10K = 10000) --num-temps=... the number of temperature points to calculate (default: 20) --max-adc=... the max ADC reading to use. if you use R1, it limits the top value for the thermistor circuit, and thus the possible range of ADC values"""from math import *import sysimport getoptclass Thermistor: "Class to do the thermistor maths" def __init__(self, r0, t0, beta, r1, r2): self.r0 = r0 # stated resistance, e.g. 10K self.t0 = t0 + 273.15 # temperature at stated resistance, e.g. 25C self.beta = beta # stated beta, e.g. 3500 self.vadc = 2.5 # ADC reference self.vcc = 2.5 # supply voltage to potential divider self.k = r0 * exp(-beta / self.t0) # constant part of calculation if r1 > 0: self.vs = r1 * self.vcc / (r1 + r2) # effective bias voltage self.rs = r1 * r2 / (r1 + r2) # effective bias impedance else: self.vs = self.vcc # effective bias voltage self.rs = r2 # effective bias impedance def temp(self,adc): "Convert ADC reading into a temperature in Celcius" v = adc * self.vadc / 1024 # convert the 10 bit ADC value to a voltage r = self.rs * v / (self.vs - v) # resistance of thermistor return (self.beta / log(r / self.k)) - 273.15 # temperature def setting(self, t): "Convert a temperature into a ADC value" r = self.r0 * exp(self.beta * (1 / (t + 273.15) - 1 / self.t0)) # resistance of the thermistor v = self.vs * r / (self.rs + r) # the voltage at the potential divider return round(v / self.vadc * 1024) # the ADC readingdef main(argv): r0 = 100000; t0 = 25; beta = 3950; r1 = 0; r2 = 4700; num_temps = int(40); try: opts, args = getopt.getopt(argv, "h", ["help", "r0=", "t0=", "beta=", "r1=", "r2=", "num-temps="]) except getopt.GetoptError: usage() sys.exit(2) for opt, arg in opts: if opt in ("-h", "--help"): usage() sys.exit() elif opt == "--r0": r0 = int(arg) elif opt == "--t0": t0 = int(arg) elif opt == "--beta": beta = int(arg) elif opt == "--r1": r1 = int(arg) elif opt == "--r2": r2 = int(arg) elif opt == "--num-temps": num_temps = int(arg) if r1: max_adc = int(1023 * r1 / (r1 + r2)); else: max_adc = 1023 increment = int(max_adc/(num_temps-1)); t = Thermistor(r0, t0, beta, r1, r2) adcs = range(1, max_adc, increment);# adcs = [1, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100, 110, 130, 150, 190, 220, 250, 300] first = 1 print "// Thermistor lookup table for RepRap Temperature Sensor Boards (http://make.rrrf.org/ts)" print "// Made with createTemperatureLookup.py (http://svn.reprap.org/trunk/reprap/firmware/Arduino/utilities/createTemperatureLookup.py)" print "// ./createTemperatureLookup.py --r0=%s --t0=%s --r1=%s --r2=%s --beta=%s --max-adc=%s" % (r0, t0, r1, r2, beta, max_adc) print "// r0: %s" % (r0) print "// t0: %s" % (t0) print "// r1: %s" % (r1) print "// r2: %s" % (r2) print "// beta: %s" % (beta) print "// max adc: %s" % (max_adc) print "#define NUMTEMPS %s" % (len(adcs)) print "short temptable[NUMTEMPS][2] = {" counter = 0 for adc in adcs: counter = counter +1 if counter == len(adcs): print " {%s, %s}" % (adc, int(t.temp(adc))) else: print " {%s, %s}," % (adc, int(t.temp(adc))) print "};" def usage(): print __doc__if __name__ == "__main__": main(sys.argv[1:])
可以用万用表测量NTC热敏电阻阻值,然后查询阻值与温度对应关系表,得到的温度值T1,用AD查询脚本“createTemperatureLookup.py”生成表对应的温度值T2,比较T1和T2的误差,看是否在可接受的范围内。
- 【龙印】在龙芯1c上用TM7705+NTC热敏电阻实现温度测量
- 一种基于NTC热敏电阻测量的温度模块
- NTC热敏电阻温度计算公式
- NTC热敏电阻
- NTC(负温度)热敏电阻.阻值的计算方式
- Arduino 负温度系数热敏电阻(NTC)测温
- 10K负温度系数热敏电阻(NTC)温度与阻值对应关系表
- 如何选择NTC热敏电阻
- NTC热敏电阻设计高精度温度计的方案1
- NTC热敏电阻的基本特性
- NTC热敏电阻的B值
- NTC热敏电阻的主要技术参数
- 【C】通过红外实现温度的无线测量与传输
- NTC温度转换公式
- NTC温度转换公式 .
- 【龙印】龙芯1c上双路16位AD芯片TM7705的linux驱动
- NTC热敏电阻MF52E-3使用笔记
- 热敏电阻(NTC)的基本参数及其应用
- Java泛型
- unity笔记-20161111
- 查询的时候,字段用到函数的时候不会用使用到index
- 115个Java面试题和答案
- Linux内核线程之深入浅出
- 【龙印】在龙芯1c上用TM7705+NTC热敏电阻实现温度测量
- 高德地图API简单记录
- 序列化与反序列化
- testlink的使用
- 分布式
- 2016.11.11 HTML DOM(对象方法 属性 访问 修改)
- Android7.0 App层实现多Camera同时打开
- show profiles 分析sql耗时瓶颈
- 深入理解Android中的Binder机制