【龙印】在龙芯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的误差,看是否在可接受的范围内。


0 0
原创粉丝点击