首页 > 代码库 > 【龙印】龙芯1C300A片内ADC的测试

【龙印】龙芯1C300A片内ADC的测试

本来是想用1c300a片内自带的ADC+ntc热敏电阻作为3d打印机的温度传感器的。后来测试发现精度不够,只有外挂adc了。测试如下

先用两个电阻串联,用万用表测试开路时的电阻,通电时的电压,并读取1C300A片内ADC的值,通过电压手动计算理论的ADC值。大概的示意图如下

技术分享

测试结果如下

技术分享

ADC是十位的,满量程1023,量程中间(512)附近的精度还可以,量程的两端就有点差了。

其中,第一项R1=4.6kR0=73.6k是把ntc热敏电阻接到ramps1.4扩展板上测试的结果。

下面是用脚本生成的ntc热敏电阻的ADC值与温度的对应关系。左边为adc值,右边为温度。

// Thermistor lookup table for RepRap Temperature Sensor Boards (http://make.rrr
f.org/ts)
// Made with createTemperatureLookup.py (http://svn.reprap.org/trunk/reprap/firm
ware/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 40
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}
};

由上表可知,读到的ADC989对应的温度为18度,计算得到的理论ADC962.64对应的温度大约为31度,这个误差是不是大了点啊。

从上表可知,温度18与温度30对应的ADC值分别是989和963,可以算出在此区间内大约ADC值变化2,温度就变化1度,18度到30度是很常见的温度范围,可见用ntc热敏电阻测试温度对ADC的要求。

脚本“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 Generator

Generates lookup to temperature values for use in a microcontroller in C format based on: 
http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.html

The main use is for Arduino programs that read data from the circuit board described here:
http://make.rrrf.org/ts-1.0

Usage: 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 sys
import getopt

class 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 = 3.3                     # ADC reference
        self.vcc = 3.3                      # 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 reading

def 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:])

adc的linux驱动文件“ls1c_3dprinter_temp_sensor.c”

/*
 * drivers\misc\ls1c_3dprinter_temp_sensor.c
 *
 * 用1c的adc+ntc热敏电阻实现温度采集
 * 用于测量3d打印机挤出头和热床的温度
 * 
 */



#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <loongson1.h>


#define ADC_POWER_DOWN              (1<<6)      // 寄存器adc_s_ctrl的power_down
#define ADC_POWER_UP                (~ADC_POWER_DOWN)
#define ADC_START_CONVERT           (1<<4)
#define ADC_SOFT_RESET              (1<<5)
#define ADC_BUSY                    (1<<31)


#define CHANNEL_0                   (0)


/* LS1C ADC register */
#define ADC_CNT			0x00
#define ADC_S_CTRL		0x04
#define ADC_C_CTRL		0x08
#define X_RANGE			0x10
#define Y_RANGE			0x14
#define AWATCHDOG_RANGE	0x18
#define AXIS			0x1c
#define ADC_S_DOUT0		0x20
#define ADC_S_DOUT1		0x24
#define ADC_C_DOUT		0x28
#define ADC_DEBOUNCE_CNT	0x2c
#define ADC_INT			0x30

#define LS1X_ADC_PRE(x)	(((x)&0xFFF)<<20)


struct printer_temp_sensor_priv
{
    void __iomem *adc_regs;
    struct mutex mutex_lock;
};
static struct printer_temp_sensor_priv printer_temp_sensor_priv_data;


// adc power up
static void printer_adc_powerup(void)
{
    unsigned int tmp;

    tmp = readl(printer_temp_sensor_priv_data.adc_regs+ADC_S_CTRL);
    writel(tmp&ADC_POWER_UP, printer_temp_sensor_priv_data.adc_regs+ADC_S_CTRL);

    return ;
}


// adc power down
static void printer_adc_powerdown(void)
{
    unsigned int tmp;

    tmp = readl(printer_temp_sensor_priv_data.adc_regs+ADC_S_CTRL);
    writel(tmp|ADC_POWER_DOWN, printer_temp_sensor_priv_data.adc_regs+ADC_S_CTRL);

    return ;
}


// Initialize the ADC controller.
static void printer_adc_init(void)
{
    // 使能adc模块 
    __raw_writel(__raw_readl(LS1X_MUX_CTRL0)&(~ADC_SHUT), LS1X_MUX_CTRL0);

    // powerdown
    printer_adc_powerdown();

    return ;
}


// 读取指定通道的adc值
// @channel 通道值,只有4个通道,分别0,1,2,3
static unsigned int printer_adc_read(unsigned int channel)
{
    unsigned int tmp;
    unsigned int adc_data = http://www.mamicode.com/0;>

温度传感器驱动
文件"ls1c_3dprinter_temp_sensor.c"放在目录"drivers\misc"下
在文件“arch\mips\loongson\ls1x\ls1c\platform.c”,增加

// 用1c的adc+ntc热敏电阻实现温度采集
#ifdef CONFIG_LS1C_3DPRINTER_TEMP_SENSOR

static struct resource ls1c_3dprinter_temp_sensor_resources[] = {
    {
        .start  = LS1X_ADC_BASE,
        .end    = LS1X_ADC_BASE + SZ_16K - 1,
        .flags  = IORESOURCE_MEM,
    },
};

static struct platform_device ls1c_3dprinter_temp_sensor = {
    .name       = "ls1c_3dprinter_temp_sensor",
    .id         = -1,
    .resource       = ls1c_3dprinter_temp_sensor_resources,
    .num_resources  = ARRAY_SIZE(ls1c_3dprinter_temp_sensor_resources),
};

#endif

在static struct platform_device *ls1b_platform_devices[] __initdata中,增加
#ifdef CONFIG_LS1C_3DPRINTER_TEMP_SENSOR
    &ls1c_3dprinter_temp_sensor,
#endif


在文件“drivers\misc\Kconfig”中增加配置项,内容如下
config LS1C_3DPRINTER_TEMP_SENSOR
    tristate "ls1c 3dprinter temperature sensor"
    depends on LS1C_MACH
    help
     Say Y here if you want to build a 3dprinter temperature sensor for ls1c
    
在文件“drivers\misc\Makefile”中,增加如下内容
obj-$(CONFIG_LS1C_3DPRINTER_TEMP_SENSOR)     += ls1c_3dprinter_temp_sensor.o

make menuconfig
Device Drivers  --->
    [*] Misc devices  --->
        <*>   ls1c 3dprinter temperature sensor  
       
(测试用的)应用层序

temp.c

// 温度相关


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "public.h"


#define TEMP_ADC_MAX                ((0x1<<10)-1)       // adc的最大值,adc是十位的
#define TEMP_IS_VALID_ADC(adc)      ((TEMP_ADC_MAX>=(adc)) && (0<=(adc)))       // 判断adc是在量程范围内



// 以下根据ntc热敏电阻参数用脚本生成的adc值与温度一一对应的表格
// 左边为adc值,右边为温度(单位:摄氏度)
// 详细请参考源码目录中的脚本"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 20
short temptable[NUMTEMPS][2] = {
   {1, 938},
   {54, 267},
   {107, 217},
   {160, 191},
   {213, 172},
   {266, 158},
   {319, 147},
   {372, 137},
   {425, 127},
   {478, 119},
   {531, 111},
   {584, 103},
   {637, 96},
   {690, 88},
   {743, 80},
   {796, 72},
   {849, 62},
   {902, 50},
   {955, 35},
   {1008, 2}
};


// 从驱动中获取adc的值
// @ret adc的值
unsigned int TempGetAdc(void)
{
    int fd_temp_sensor = 0;
    int ret = 0;
    unsigned int adc = 0;

    fd_temp_sensor = open("/dev/3dprinter_temp_sensor", O_RDWR);
    if (ERROR == fd_temp_sensor)
    {
        printf("[%s] open file /dev/3dprinter_temp_sensor fail.\n", __FUNCTION__);
        return ERROR;
    }

    ret = read(fd_temp_sensor, &adc, sizeof(adc));
    if (sizeof(adc) != ret)
    {
        printf("[%s] read adc fail. ret=%d\n", __FUNCTION__, ret);
        return ERROR;
    }
    if (!TEMP_IS_VALID_ADC(adc))
    {
        printf("[%s] adc convert fail. adc=%u\n", __FUNCTION__, adc);
        return ERROR;
    }

    return adc;
}


// 根据adc值计算温度值
// ntc热敏电阻的阻值温度曲线被分为n段,每段可以近似为直线,
// 所以温度值的计算就转变为查表再计算
// @adc adc值(取值范围为0-1023)
// @ret 对应的温度值(单位摄氏度)
int TempCalcFromAdc(unsigned int adc)
{
    float celsius = 0.0;        // 温度值,单位摄氏度
    int i = 0;
    
    // 判断adc值是否在量程范围内
    if (!TEMP_IS_VALID_ADC(adc))
    {
        return ERROR;
    }

    // 判断是否在表格所表示的范围内
    if (adc < temptable[0][0])              // 小于表格的最小adc
    {
        return temptable[0][1];             // 取最小值
    }
    if (adc > temptable[NUMTEMPS-1][0])     // 大于表格的最大adc
    {
        return temptable[NUMTEMPS-1][1];    // 取最大值
    }

    // 查表
    // 这里是从adc由低到高,逐个区间进行比较,没有采用折半查找
    for (i=1; i<NUMTEMPS; i++)          // 注意,这里是从1开始的,巧妙之处就在这里
    {
        if (adc < temptable[i][0])      // 判断是否在这个区间
        {
            // t = t0 + (adc-adc0)*k
            celsius = temptable[i-1][1] +                   // t0
                      (adc - 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__, adc, celsius);
            return celsius;
        }
    }

    return ERROR;
}


// 获取温度值
// @ret 温度值,单位摄氏度
int TempGet(void)
{
    return TempCalcFromAdc(TempGetAdc());
}



// 测试函数
void TempTest(void)
{
    float temp = 0.0;

    temp = TempGet();
    printf("[%s] current temp=%f\n", __FUNCTION__, temp);
/*
    temp = TempCalcFromAdc(961.43);     // 温度30.99
    printf("[%s] expect temp=30.99, calced temp=%f\n", __FUNCTION__, temp);
    temp = TempCalcFromAdc(1022);       // adc超过表格中的最大值,取adc为最大值,温度2
    printf("[%s] expect temp=2, calced temp=%f\n", __FUNCTION__, temp);
    temp = TempCalcFromAdc(0);          // adc小于表格中的最小值,取adc为最小值,温度938
    printf("[%s] expect temp=938, calced temp=%f\n", __FUNCTION__, temp);
*/
    return ;
}

temp.h

// 温度相关


#ifndef __TEMP_H
#define __TEMP_H


#include <stdio.h>




// 获取温度值
// @ret 温度值,单位摄氏度
int TempGet(void);


// 测试函数
void TempTest(void);



#endif


main.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "public.h"
#include "temp.h"


#define BUFF_LEN        (15)




int main(void)
{
    int fd_motor = 0;
    char buff[15] = {0};
    int count;
    int ret;

    fd_motor = open("/dev/3dPrinter_motor", O_RDWR);
    if (-1 == fd_motor)
    {
        printf("open file /dev/3dPrinter_motor fail.\n");
        return fd_motor;
    }

    while (1)
    {
/*        
        // 测试电机
        count = 16*200;         // 16细分,42步进电机的步进角为1.8度,200个脉冲转一圈
        while (count--)
        {
            write(fd_motor, buff, BUFF_LEN);
            usleep(5*1000);
        }
*/
        // 测死温度传感器
        TempTest();

        usleep(500*1000);
//        sleep(1);
    }
}


public.h

#ifndef __PUBLIC_H
#define __PUBLIC_H


enum
{
    ERROR = -1,
    SUCCESS = 0,
};



#endif




Makefile

SOURCE = $(wildcard *.c)

CROSS_COMPILE = mipsel-linux-
CXX = gcc

CFLAGS += -Wall

3dprinter_app : $(SOURCE)
	$(CROSS_COMPILE)$(CXX) $(CFLAGS) -o $@ $^
	cp $@ /nfsramdisk/LS1xrootfs-demo/test/
	
clean:
	rm -f *.o








【龙印】龙芯1C300A片内ADC的测试