本来是想用1c300a片内自带的ADC+ntc热敏电阻作为3d打印机的温度传感器的。后来测试发现精度不够,只有外挂adc了。测试如下
先用两个电阻串联,用万用表测试开路时的电阻,通电时的电压,并读取1C300A片内ADC的值,通过电压手动计算理论的ADC值。大概的示意图如下
测试结果如下
该ADC是十位的,满量程1023,量程中间(512)附近的精度还可以,量程的两端就有点差了。
其中,第一项R1=4.6k,R0=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}
};
由上表可知,读到的ADC值989对应的温度为18度,计算得到的理论ADC值962.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 = 0; // 判断入参 if (4<=channel) // 只有4个通道 { return -1; } // powerup printer_adc_powerup(); // 执行单次转换 tmp = readl(printer_temp_sensor_priv_data.adc_regs+ADC_S_CTRL); tmp &= (~0xf); tmp |= (0x1 << channel); writel(tmp, printer_temp_sensor_priv_data.adc_regs+ADC_S_CTRL); // 选择通道 writel(tmp|ADC_START_CONVERT, printer_temp_sensor_priv_data.adc_regs+ADC_S_CTRL); // 开始转换 udelay(10); // 根据测试,执行一个通道的单次转换需要5us左右,这里延迟10us后再读结果 switch (channel) { case 0: // 通道0 if ( !(readl(printer_temp_sensor_priv_data.adc_regs+ADC_S_DOUT0) & ADC_BUSY) ) // 判断是否转换成功 { // 没有转换成功,则powerdown后,返回 // 此处不能soft reset,否则后面采集到的值全为0(经验所得,可能是cpu的bug吧) printer_adc_powerdown(); printk(KERN_ERR "[%s] adc convert fail. channel=%d\n", __FUNCTION__, channel); return -1; } adc_data = readl(printer_temp_sensor_priv_data.adc_regs+ADC_S_DOUT0) & 0x3ff; // 读取结果 break; default: break; } // power down printer_adc_powerdown(); printk(KERN_DEBUG "[%s] channel=%d, adc_data=%u\n", __FUNCTION__, channel, adc_data); return adc_data; } static int printer_temp_sensor_open(struct inode *inode, struct file *filp) { return 0; } static int printer_temp_sensor_close(struct inode *inode, struct file *filp) { return 0; } static ssize_t printer_temp_senosr_read(struct file *filp, char __user *buff, size_t count , loff_t *offp) { int ret; unsigned int adc_data = 0; ret = mutex_lock_interruptible(&printer_temp_sensor_priv_data.mutex_lock); if (0 > ret) { return ret; } // 执行一次采集,并读取结果 adc_data = printer_adc_read(CHANNEL_0); mutex_unlock(&printer_temp_sensor_priv_data.mutex_lock); // 返回给用户空间 if (copy_to_user(buff, &adc_data, sizeof(adc_data))) { printk(KERN_ERR "[%s]copy adc data to user fail.\n", __FUNCTION__); return -EFAULT; } return sizeof(adc_data); } static struct file_operations printer_temp_sensor_ops = { .owner = THIS_MODULE, .open = printer_temp_sensor_open, .release= printer_temp_sensor_close, .read = printer_temp_senosr_read, }; static struct miscdevice printer_temp_sensor_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "3dprinter_temp_sensor", .fops = &printer_temp_sensor_ops, }; static int __devinit printer_temp_sensor_probe(struct platform_device *dev) { struct resource *mem_resource = NULL; printk(KERN_INFO "ls1c 3dprinter temperature sensor driver's build time %s %s\n", __DATE__, __TIME__); mem_resource = platform_get_resource(dev, IORESOURCE_MEM, 0); if (! mem_resource) { printk(KERN_ERR "failed to find registers.\n"); return -ENXIO; } printer_temp_sensor_priv_data.adc_regs = ioremap(mem_resource->start, resource_size(mem_resource)); if (! printer_temp_sensor_priv_data.adc_regs) { printk(KERN_ERR "failed to map registers.\n"); return -ENXIO; } mutex_init(&printer_temp_sensor_priv_data.mutex_lock); /* Initialize the ADC controller. */ printer_adc_init(); return 0; } static int __devexit printer_temp_sensor_remove(struct platform_device *dev) { iounmap(printer_temp_sensor_priv_data.adc_regs); return 0; } static struct platform_driver printer_temp_sensor_driver = { .driver = { .name = "ls1c_3dprinter_temp_sensor", .owner = THIS_MODULE, }, .probe = printer_temp_sensor_probe, .remove = printer_temp_sensor_remove, }; static int __init printer_temp_sensor_init(void) { if (misc_register(&printer_temp_sensor_miscdev)) { printk(KERN_ERR "could not register 3dprinter temp sensor driver.\n"); return -EBUSY; } return platform_driver_register(&printer_temp_sensor_driver); } static void __exit printer_temp_sensor_exit(void) { misc_deregister(&printer_temp_sensor_miscdev); platform_driver_unregister(&printer_temp_sensor_driver); } module_init(printer_temp_sensor_init); module_exit(printer_temp_sensor_exit); MODULE_AUTHOR("简单平安"); MODULE_DESCRIPTION("ls1c adc + ntc thermistor temperature sensor"); MODULE_LICENSE("GPL");
温度传感器驱动
文件"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 [email protected] $^ cp [email protected] /nfsramdisk/LS1xrootfs-demo/test/ clean: rm -f *.o