K型热电偶采集温度是新项目中的一个小需求,本来是用NTC热敏电阻的但是温度最高只能到200度,而且到150度之后不是很灵敏了,所以改用了热电偶的办法,前面的部分是我搞热电偶的一些知识积累,和思路和遇到的问题可以给你们参考。
一、热电偶简介
热电偶是测量温度的一种元件,是一种无源传感器,测量时不需要外接电源。
常见的有上面几种,热电偶是两种不同的导体组成回路,两端相连接的温度不同,一端温度为T,称为热端;一端温度为T0,称为冷端。两种不同成分的导体两端接合形成回路,当两个接合点的温度不同时,回路中就会产生电动势,这种现象称为热电效应,这种电动势称为热电动势。热电偶就是利用这种原理进行测温的。直接用作测量截止温度的一端称为工作端(热端),另一端叫做冷端(也称为补偿端),冷端与显示仪表或配套仪表相连接。 (了解就好)
很重要的一点就是我们要进行温度测量的时候要保证冷端的温度保持在 0摄氏度,但是在室内常温下是肯定没办法保证0摄氏度的,所以网上就有了很多关于冷端补偿的方法,[1]魏元,吴璋,徐岱.K型热电偶冷端补偿研究[J].计测技术,2011,31(06):43-44+58.
二、K型热电偶测温方法
因为板卡的限制,没有多余空间也没有多余引脚,所以不想改板加新的东西,所以开始就想着直接用stm32内置ADC+中间温度定律能不能做,然后开始了试错之路
方法1: 内置ADC+K型热电偶分度表+中间温度定律
这种办法没行通,原因是如下热电偶分度表
这个表是怎么读的呢,首先看纵排,然后看横排,就像看坐标一样,比如上图红框圈出的1,纵坐标是20,横坐标是5,所以当电动势为1mV的时候,温度是25摄氏度
但是就有一个问题:我们stm32内置ADC的精度是12位,因此有这么一个模拟量X和数字量Y转换的公式
当模拟量为1mv的时候 发现当25度的时候数字量只有1.24,这个数字量的值太小了
改善的办法有用运放将模拟量放大,或者使用24位精度的ADC。
但是还有一个很重要的问题,上面的K型热电偶分度表的冷端温度是以0摄氏度为参考的,在室温下直接用这个办法肯定是不行的,那么可以去看看中间温度定律,中间温度定律解决了这个问题,唯一的缺陷就是,最好是室温变化小,并且已知室温。
来自百度百科 中间温度定律
方法二:MAX6675
除了max6675,还知道有一个叫max31855,但是价钱差不多
MAX6675的特点有 :
①内部集成有冷端补偿电路 ;
②带有简单的3位spi串行接口;
③可将温度信号转换成12位数字量,温度精度为0.25度摄氏度
④内含热电偶断线检测电路
电路连接方法:(采用的是spi通信方式)
时序图:
上图摘自 [1]虞致国 ,徐健健.MAX6675的原理及应用[J].国外电子元器件,2002(12):41-43.
三、代码编写
我自己是使用的模拟spi,因为硬件spi不够用了,下面两个代码都提供,并且进行了低温和高温测试,变化灵敏
硬件SPI方式:(这应该是网上通用的版本,博客里源码下载很多都是这个)
#include "stm32f10x.h"
#include "usart1.h"
#define MAX6675_CS GPIO_Pin_4
#define MAX6675_CSL() GPIOA->BRR = MAX6675_CS;
#define MAX6675_CSH() GPIOA->BSRR = MAX6675_CS;
/*
* 函数名:SPI1_Init
* 描述 :MAX6675 接口初始化
* 输入 :无
* 输出 :无
* 返回 :无
*/
void SPI_MAX6675_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
/* 使能 SPI1 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);
/* ---------通信I/O初始化----------------
* PA5-SPI1-SCK :MAX6675_SCK
* PA6-SPI1-MISO:MAX6675_SO
* PA7-SPI1-MOSI:MAX6675_SI
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* ---------控制I/O初始化----------------*/
/* PA4-SPI1-NSS:MAX6675_CS */ // 片选
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推免输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_4); // 先把片选拉高,真正用的时候再拉低
/* SPI1 配置 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
/* 使能 SPI1 */
SPI_Cmd(SPI1, ENABLE);
}
/*
*
*
*
*/
unsigned char MAX6675_ReadByte(void)
{
/* Loop while DR register in not emplty */
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);
/* Send byte through the SPI1 peripheral */
SPI_I2S_SendData(SPI1, 0xff);
/* Wait to receive a byte */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI1);
}
/*
* 函数名:main
* 描述 :主函数
* 输入 :无
* 输出 :无
*/
main (void)
{
unsigned int t,i;
unsigned char c;
unsigned char flag;
float temprature;
/* 配置系统时钟为72M */
SystemInit();
/* MAX6675 SPI 接口初始化 */
SPI_MAX6675_Init();
USART1_Config();
while(1)
{
MAX6675_CSL();
c = MAX6675_ReadByte();
i = c;
i = i<<8;
c = MAX6675_ReadByte();
MAX6675_CSH();
i = i|((unsigned int)c); //i是读出来的原始数据
flag = i&0x04; //flag保存了热电偶的连接状态
t = i<<1;
t = t>>4;
temprature = t*0.25;
if(i!=0) //max6675有数据返回
{
if(flag==0) //热电偶已连接
{
printf("原始数据是:%04X, 当前温度是:%4.2f。\r\n",i,temprature);
}
else //热电偶掉线
{
printf("未检测到热电偶,请检查。\r\n");
}
}
else //max6675没有数据返回
{
printf("max6675没有数据返回,请检查max6675连接。\r\n");
}
for(i=0;i<0x2fffff;i++); //max6675的转换时间是0.2秒左右,所以两次转换间隔不要太近
}
}
模拟spi:
void max6675_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化PB11
}
u16 max6675_readRawValue(void)
{
u16 tmp=0;
u16 i;
MAX6675_CS_L;
for(i=0;i<15;i++)
{
MAX6675_CK_H;
delay_us(10);
if(MAX6675_SO)tmp|=0x0001;
else tmp |= 0;
tmp<<=1;
MAX6675_CK_L;
delay_us(10);
}
MAX6675_CS_H;//cs高
if (tmp&0X04)
{
tmp = 4096; //未检测到热电偶
}
else
{
tmp>>=3;//去掉D0、1、2位
}
return tmp;
}
float max6675_readTemperature(void)
{
u16 d;
float i;
d=max6675_readRawValue();
delay_ms(1);
i=d*(1023.75/4095);
return i;
}