用MSP430F249采集B型热电偶的温度
本文阐述关于实现B型热电偶TCB温度测量的相关内容,其中使用到19位ADC的数字转换器MAX31856进行转换
前言
温度是工业环境中非常重要的数据,本课题采用热电偶来测量800~1200℃的工业环境温度,并通过串口形式发送到温度和时间的数据。
热电偶是根据热电效应制成的一种测温元件。它结构简单,坚固耐用,使用方便,精度高,测量范围宽,便于远距离、多点、集中测量和自动控制,是应用很广泛的一种温度计。通过19位ADC的MAX31856来对热电偶两端的电压进行数字转换,转换器温度分辨率达0.0078125°C,允许读取最高+1800°C、最低-210°C (取决于热电偶类型)的温度读数,热电偶电压测量精度达±0.15%。
一、热电偶的定义及原理
1、热电偶的定义
热电偶(thermocouple)是在温度测量中常用的测温元件,它可以直接测量温度,并将温度信号转换成热电动势信号,通过仪器仪表可以转换成被测介质的温度。
2、热电偶的测温原理
热电偶测温的基本原理是两种不同成份的材质导体组成闭合回路,当两端存在温度差时,回路中就会有电流通过,此时两端之间就存在电动势——热电动势。两种不同成份的均质导体为热电极,温度较高的一端为工作端,温度较低的一端为自由端,自由端通常处于某个恒定的温度下。根据热电动势与温度的函数关系,制成热电偶分度表;分度表是自由端温度在0℃时的条件下得到的,不同的热电偶具有不同的分度表,这个通过网络很轻松就可以查到。
3、热电偶的技术优势
热电偶测温范围广,性能稳定并且精度高。热电偶常常是与被测介质直接接触,不会受到中间介质的影响。性能牢靠,并且机械强度好。使用寿命长,装配也比较简单方便。
4、常见热电偶参数
热电偶的型号有主要有S型、R型、B型、N型、K型、E型、J型和T型几种。其中S、R、B属于贵金属热电偶(即铂铑热电偶),N、K、E、J、T属于廉金属热电偶。
常用热电偶型号热电偶分度号热电极材料 使用温度( ℃)
- S 铂铑合金(铑含量10 %) 纯铂 0-1600
- R 铂铑合金(铑含量13 %) 纯铂 0-1600
- B 铂铑合金(铑含量30%) 铂铑合金(铑含量6% ) 0-1800
- K 镍铬镍硅 0-1300
- T 纯铜 铜镍 0-350
- J 铁 铜镍 0-+500
- N 镍铬硅 镍硅 0-+800
- E 镍铬 铜镍 0-600
热电偶的种类: 装配热电偶,铠装热电偶,端面热电偶,压簧固定热电偶,高温热电偶,铂铑热电偶,防腐热电偶,耐磨热电偶,高压热电偶,特殊热电偶,手持式热电偶,微型热电偶,贵金属热电偶 ,快速热电偶,钨铼热电偶,单芯铠装热电偶等等。
常用热电偶温度电势转换公式及对应表
二、硬件设计
2.1主控芯片及外围电路
MSP430F249的XT2OUT、XT2IN接8MHz晶振电路,XOUT、XIN接32.768KHz晶振电路。电路图如下图所示。
2.2MAX31856及TCB测温模块
将MAX31856的CS、SCK、SDI、SDO分别与MSP430F249的P11、P33、P31、P32连接。将DRDY接到LED1的负极,将FAULT接到LED2的负极,用于分别示意两种情况的发生。其中,在T+、T-两端应该加上103、104的滤波电容滤除噪声,在数据高电平DVDD接104到地来滤除噪声。
当热电偶线与不同金属接触时,通常在连接处或在与PCB的焊接处“( 冷端”)形成额外的热电偶。为补偿这些额外的热电偶引起的误差,必须测量冷端温度。这是通过内部高精度温度传感器实现的,在-20°C至+85°C温度范围内,精度优于±0.7°C。通过将MAX31856安装在冷端附近,可测量冷端温度并用于补偿冷端效应。
MAX31856的位置,因为MAX31856包括内部冷端温度传感器,将其安装在温度尽量接近冷端的位置。如果热电偶引线直接焊接到PCB,MAX31856应尽量靠近热电偶引线连接,最大程度减小IC和热电偶连接之间的热梯度。如果热电偶引线端接在连接器上,将IC尽量靠近连接器安装,最大程度减小连接器和IC之间的热梯度。
2.3DS1302时间模块
DS1302需要一个32.768KHz的晶振来提高时钟频率,其电路图如图3-5所示。
2.4数码管模块
三、软件设计
软件程序源码,请见附录5。
3.1软件设计流程
软件开始需要进行初始化,然后读时间和对温度然后通过串口发送数据进行显示,之后进入低功耗休眠模式。低功耗模式下,定时器继续工作,当到达一定时间后,退出休眠模式。从而,可以实现周而复始地读取时间和温度,以及发送时间和温度数据并通过串口显示。软件设计流程图如图所示。
3.2 串口收发软件设计(及数码管)
由于串口的收发内容的形式是ASCII码,因此要进行发送时,首先将要发送的内容转换成为字符串格式。对此,可以使用stdio.h中的sprintf函数。其使用功能如下。
int sprintf(char *str, const char *format, …) 发送格式化输出到 str 所指向的字符串。但是,对于IAR平台而言,不支持对浮点数据的转换,所以在将浮点数转换成字符串时,可以将它分成整数和小数两个部分的整数进行转换。
将转换后的字符串依次送入发送寄存器UCA0TXBUF中,及实现了将时间和温度数据发送到串口显示的功能。
对于数码管,它仅用于显示温度数据。将获得的温度数据通过数学方法得到每一位数码管要显示的数字,然后通过定时器中断循环实现数码管的动态显示。
3.3MAX31856的软件设计
MAX31856的数据传输是通过SPI协议进行的,其通信的时序参数要求见附录2。
对于MSP430F249的USCI中的SPI模块,在进行通信时无法按照MAX31856的SPI通信时序来进行。故此,使用4个普通IO口来模拟SPI通信。下面展示读写MAX31856寄存器的程序。
uchar max31856_read_register(uchar addr)
{
CS_Low;
uchar i;
uchar dat;
addr &= ~0x80;
for (i = 0; i < 8; i++)
{
SCK_Low;
if (addr & 0x80)
SIMO_High;
else
SIMO_Low;
addr <<= 1;
SCK_High; //上升沿数据采样
}
for (i = 0; i < 8; i++)
{
SCK_Low;
SCK_High;
dat <<= 1; //高位在前
if (SOMI_IN)
dat |= 0x01;
else
dat &= ~0x01;
}
CS_High;
return dat;
}
void max31856_write_register(uchar addr, uchar data)
{
addr |= 0x80;
CS_Low;
uchar i;
for (i = 0; i < 8; i++)
{
SCK_Low;
if (addr & 0x80)
SIMO_High;
else
SIMO_Low;
addr <<= 1;
SCK_High; //上升沿数据采样
}
for (i = 0; i < 8; i++)
{
SCK_Low;
if (data & 0x80)
SIMO_High;
else
SIMO_Low;
data <<= 1;
SCK_High; //上升沿数据采样
}
CS_High;
}
与MAX31856的通信通过16个包含转换、状态和配置数据的8位寄存器实现,全部设置均通过选择相应寄存器单元的对应地址完成,寄存器存储器映射所示为温度、状态和配置寄存器的地址。存取寄存器时,使用地址0Xh为读操作,地址8Xh为写操作。读写数据时,寄存器MSB在前。如果对只读寄存器执行写操作,不改变该寄存器的值。
对于MAX31856简单初始化而言,需要设置的寄存器有CR0、CR1、MASK这三个寄存器,其详细位说明见附录3。而读取温度时,仅需要读取寄存器LTCBH、LTCBM、LTCBL即可。
MAX31856将冷端温度数据储存在寄存器0Ah和0Bh。使能冷端温度检测时,这些寄存器为只读,其中包含实测冷端温度加冷端失调寄存器的数值。冷端温度检测使能时,读取寄存器操作将DRDY引脚复位为高电平。应通过多字节传输读取该寄存器的两个字节,以确保两个字节的数据来自同一次温度更新。禁止冷端温度检测时,这些寄存器为可读/写寄存器,其中包含最新的实测温度值。如果需要,禁止内部冷端检测时,可将来自外部温度传感器的数据写入这些传感器。最大冷端温度箝位在128°C,最小温度箝位在-64°C。如果需要,可向冷端失调寄存器(09h)写入温度偏移值。那么寄存器0Ah和0Bh中储存的值将等于实测值加失调值。失调寄存器的MSB为4°C,LSB为0.0625°C。失调值应用到实测冷端温度后得到的结果范围为-8°C至+7.9375°C。默认失调值为0°C (00h)。
热电偶线性化及编码到温度的转换由于所有热电偶都具有非线性,必须对冷端补偿后的原始值进行线性修正,并转换为温度值。为实现这一处理,利用LUT产生经过线性化和冷端补偿的温度值;每次转换后,将其作为19位数据储存在线性化热电偶温度寄存器(0Ch、0Dh和0Eh)中。应通过多字节传输读取全部三个字节,以确保所有数据来自于同一次数据更新。
综上,将MAX31856的CR0寄存器配置为0x80,即设置为连续转换模式;CR1寄存器配置为0x40,即16个采样数据平均,B型热电偶;MASK寄存器配置为0xff;然后按照每5秒读一次线性化热电偶温度寄存器,并将二进制数据转换成对应的温度,通过串口发送和显示在数码管上。
上述寄存器配置的代码如下:
void Max31856_Init(void)
{
max31856_write_register(MAX31856_CR1_REG, 0x40);
max31856_write_register(MAX31856_MASK_REG, 0xff);
max31856_write_register(MAX31856_CR0_REG,0x088);
}
对于读取线性化热电偶温度寄存器(0Ch、0Dh和0Eh)的代码如下:
float thermocouple_read_temperature(void)
{
Unsigned long tc_temp;
temp = max31856_read_register24(MAX31856_LTCBH_REG);
tc_temp >>= 5; // bottom 5 bits are unused
float tc_temp_float = tc_temp;
tc_temp_float *= 0.0078125;
// max31856->thermocouple_c = tc_temp_float;
// max31856->thermocouple_f = (1.8 * tc_temp_float) + 32.0;
return tc_temp_float;
}
3.4 DS1302的软件设计
关于DS1302的通信时序,请见附录4。介绍完其通信时序后,当然少不了要对DS1302的相关控制寄存器的简要说明,其控制寄存器见4-2所示。
它一共有7个时间相关的寄存器,可读可写,读写地址不一样。寄存器地址从低到高分别对应的是秒、分、时、日、月、星期、年份的值。在设置时间和获取时间时,就是直接对这几个寄存器的值进行操作的。可以发现,关于时间的寄存器的格式是BCD码的形式,因此在对时间进行读写时要进行数据转换操作。
对于8F/8E寄存器的最高位WP为写保护,在对寄存器进行写入操作前(包括设置时间操作),应该将WP清零。当写操作结束后,为了避免DS1302不被误写操作,应当将WP置高。
对于串口发送实时时钟相关程序,只需要按照一定的格式发送到串口,然后MSP430F249通过串口接收中断依次获取收到的数据,当达到规定的格式的长度时,将这些数据(ASCII码)转为BCD码后重新写入到DS1302即可。例如串口发送“10:20:30”一共8个字符,(不需要换行,换行会产生错误),然后接收到数据后存入数组中,当数组存入8个数据后进行判断第3个和第5个字符是否j均为 ’ : ’ ,然后将这些数据转为BCD码后设置时间。当然,为了保证获取的时间数据不出错,可以按照时间的范围要求进一步进行判断后才决定是否使用接收到的时间进行设置,然后发送是否设置成功。具体代码如下:
#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX(void)
{
static uchar index = 0;
int hour, minite, second;
timedataset[index++] = UCA0RXBUF;
if (index == 8)
{
index = 0;
if (timedataset[2] == ':' && timedataset[5] == ':')
{
hour = (timedataset[0] - '0') * 10 + (timedataset[1] - '0');
minite = (timedataset[3] - '0') * 10 + (timedataset[4] - '0');
second = (timedataset[6] - '0') * 10 + (timedataset[7] - '0');
if ((hour <= 23 && hour >= 0) && (minite >= 0 && minite <= 59) && (second >= 0 && second <= 59))
{
wdata[2] = (timedataset[0] - '0') << 4 | (timedataset[1] - '0');
wdata[1] = (timedataset[3] - '0') << 4 | (timedataset[4] - '0');
wdata[0] = (timedataset[6] - '0') << 4 | (timedataset[7] - '0');
Set_DS1302(wdata); //设置时间
PutString("\r\nThe time you sent has been set!\r\n");
}
else
PutString("\r\nWorning:The time you sent is wrong!\r\n");
}
else
PutString("\r\nWorning:The time you sent is wrong!\r\n");
}
}
3.5 温度数据的处理
对于热电偶测量的温度可能存在一定误差,为了减小误差可以将采样的温度求平均值操作后输出。对于MAX31856而言,其内部可设置为1、2、4、8、16个采样点求均值的操作。对此,我们可以设置为16个采样点求均值操作。
附录
附录1 系统完整电路
附录2 MAX31856的SPI通信时序要求
MAX31856SPI接口要求
SPI读时序
SPI写时序
SPI单字节读时序
SPI单字节写时序
附录3 MAX31856寄存器
附录4 DS1302通信时序
DS1302读一个字节时序
DS1302写一个字节
附录5 程序源码
allfunc.h
#ifndef _ALL_FUNC_H_
#define _ALL_FUNC_H_
#include <msp430f249.h>
#define CPU (8000000)
#define delay_us(x) (__delay_cycles((double)x*CPU/1000000.0))
#define delay_ms(x) (__delay_cycles((double)x*CPU/1000.0))
typedef unsigned int uint;
typedef unsigned char uchar;
#endif
main.c
/**********************************************
程序功能:在八位数码管上显示温度并发送到串口,串口显示时间和温度
-----------------------------------------------
测试说明:观察数码管显示和串口接收的数据
***********************************************/
#include "uart0.h"
#include "stdio.h"
#include "ds1302.h"
#include "max31856.h"
#define wei_h P5OUT |= BIT6
#define wei_l P5OUT &= ~BIT6
#define duan_h P5OUT |= BIT5
#define duan_l P5OUT &= ~BIT5
#define wei \
{ \
wei_l; \
wei_h; \
wei_l; \
}
#define duan \
{ \
duan_l; \
duan_h; \
duan_l; \
}
//数码管7位段码:0--f
uchar scandata[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
uchar seg_show[8];
//记录显示位数的全局变量
uchar cnt = 0; //数码管显示位控制
const char timeStr[] = "time:";
const char temperStr[] = "temperature:";
uint temper_zheng; //温度整数部分
uint temper_xiao; //温度小数部分
float ftemper; //温度浮点数
char string[80]; //用于串口发送
uchar temp; //用于控制更新时间
max31856_cfg_t max31856 = {0}; //未用
//秒,分,时,日,月,星期,年
unsigned char wdata[7] = {0x59, 0x01, 0x20, 0x10, 0x04, 0x01, 0x22}; //写时间
unsigned char rdata[10]; //读时间
uchar timedataset[8] = {'1', '0', ':', '2', '5', ':', '3', '6'};
void Seg_Tran(void)
{
seg_show[0] = temper_zheng / 1000;
seg_show[1] = temper_zheng / 100 % 10;
seg_show[2] = temper_zheng / 10 % 10;
seg_show[3] = temper_zheng % 10;
seg_show[4] = temper_xiao / 1000;
seg_show[5] = temper_xiao / 100 % 10;
seg_show[6] = temper_xiao / 10 % 10;
seg_show[7] = temper_xiao % 10;
}
/********************主函数********************/
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
/*下面几行程序关闭所有的IO口*/
P2DIR = 0XFF;
P2OUT = 0XFF;
P4DIR = 0XFF;
P4OUT = 0XFF;
P5DIR = 0XFF;
P5OUT = 0XFF;
ClkInit(); //初始化时钟
InitUART(); //初始化
SPI_Init(); //初始化SPI
// Set_DS1302(wdata); //设置时间
Reset_DS1302(); //复位
PutString("Hellow! Welcome to use the temperature test system!\r\n"); //欢迎界面
Max31856_Init(); //初始化max31856
// max31856_oneshot_temperature();
// while (DRDY_IN);//等待温度转换成功
// ftemper = thermocouple_read_temperature(); //读温度
//初始化定时器中断
CCTL0 = CCIE; //使能CCR0中断
CCR0 = 1000;
TACTL = TASSEL_2 + ID_3 + MC_1; //定时器A的时钟源选择SMCLK,增计数模式
__enable_interrupt(); //使能全局中断
while (1)
{
Get_DS1302(rdata); //获取当前时间
if (temp != rdata[5])
{
temp = rdata[5];
/*调试SPI用*/
// max31856_write_register(MAX31856_MASK_REG,0x55);
// temper_zheng = max31856_read_register(MAX31856_MASK_REG);
ftemper = thermocouple_read_temperature(); //读取温度
temper_zheng = (uint)ftemper; //格式转换
temper_xiao = (uint)(ftemper - temper_zheng) * 10000; //获取小数部分
// temper_zheng = max31856_read_register(0x06);
Seg_Tran();
sprintf(string, "%s20%02d-%02d-%02d %02d:%02d:%02d\r\n%s%4d\r\n",
timeStr, rdata[0], rdata[1], rdata[2], rdata[3], rdata[4],
rdata[5], temperStr, temper_zheng); //将时间和温度及数据输出为字符串,方便串口发送
PutString(string); //发送字符串
}
LPM1; //进入休眠模式
}
}
#pragma vector = TIMERA0_VECTOR
/*
定时器中断服务函数
功能:主要实现数码管的动态显示以及退出休眠模式
*/
__interrupt void timer0(void)
{
static uchar uc100ms = 0;
uc100ms %= 300;
if ((!(uc100ms++)) | (temp != rdata[5]))
LPM1_EXIT;
P4OUT = 0x00; //消影
duan;
P4OUT = scandata[seg_show[cnt]]; //输出段选信号
if (cnt == 3)
P4OUT |= 0x80;
duan;
P4OUT = ~(1 << cnt); //输出位选信号
wei;
cnt++;
cnt &= 7; //位计数变量在0~7之间循环
}
/*
串口接收中断服务函数
功能:主要实现串口实时时钟设置和检测
*/
#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX(void)
{
static uchar index = 0;
int hour, minite, second;
timedataset[index++] = UCA0RXBUF;
if (index == 8)
{
index = 0;
if (timedataset[2] == ':' && timedataset[5] == ':')
{
hour = (timedataset[0] - '0') * 10 + (timedataset[1] - '0');
minite = (timedataset[3] - '0') * 10 + (timedataset[4] - '0');
second = (timedataset[6] - '0') * 10 + (timedataset[7] - '0');
if ((hour <= 23 && hour >= 0) && (minite >= 0 && minite <= 59) && (second >= 0 && second <= 59))
{
wdata[2] = (timedataset[0] - '0') << 4 | (timedataset[1] - '0');
wdata[1] = (timedataset[3] - '0') << 4 | (timedataset[4] - '0');
wdata[0] = (timedataset[6] - '0') << 4 | (timedataset[7] - '0');
Set_DS1302(wdata); //设置时间
PutString("\r\nThe time you sent has been set!\r\n"); //设置成功
}
else
PutString("\r\nWorning:The time you sent is wrong!\r\n"); //设置失败
}
else
PutString("\r\nWorning:The time you sent is wrong!\r\n"); //设置失败
}
}
max31856.h
#ifndef __MAX31856_H_
#define __MAX31856_H_
#include "allfunc.h"
//宏定义
#define DRDY_IN P1IN & 0x01
#define CS_High P1OUT |= 0x02
#define CS_Low P1OUT &= ~0x02
#define SIMO_High P3OUT |= 0x02
#define SIMO_Low P3OUT &= ~0x02
#define SCK_High P3OUT |= 0x08
#define SCK_Low P3OUT &= ~0x08
#define SOMI_IN P3IN & 0x04
#define MAX31856_CR0_REG 0x00
#define MAX31856_CR0_AUTOCONVERT 0x80
#define MAX31856_CR0_1SHOT 0x40
#define MAX31856_CR0_OCFAULT1 0x20
#define MAX31856_CR0_OCFAULT0 0x10
#define MAX31856_CR0_CJ 0x08
#define MAX31856_CR0_FAULT 0x04
#define MAX31856_CR0_FAULTCLR 0x02
#define MAX31856_CR1_REG 0x01
#define MAX31856_CR0_1AVG 0x00
#define MAX31856_CR0_2AVG 0x10
#define MAX31856_CR0_4AVG 0x20
#define MAX31856_CR0_8AVG 0x30
#define MAX31856_CR0_16AVG 0x40
#define MAX31856_MASK_REG 0x02
#define MAX31856_CJHF_REG 0x03
#define MAX31856_CJLF_REG 0x04
#define MAX31856_LTHFTH_REG 0x05
#define MAX31856_LTHFTL_REG 0x06
#define MAX31856_LTLFTH_REG 0x07
#define MAX31856_LTLFTL_REG 0x08
#define MAX31856_CJTO_REG 0x09
#define MAX31856_CJTH_REG 0x0A
#define MAX31856_CJTL_REG 0x0B
#define MAX31856_LTCBH_REG 0x0C
#define MAX31856_LTCBM_REG 0x0D
#define MAX31856_LTCBL_REG 0x0E
#define MAX31856_SR_REG 0x0F
#define MAX31856_FAULT_CJRANGE 0x80
#define MAX31856_FAULT_TCRANGE 0x40
#define MAX31856_FAULT_CJHIGH 0x20
#define MAX31856_FAULT_CJLOW 0x10
#define MAX31856_FAULT_TCHIGH 0x08
#define MAX31856_FAULT_TCLOW 0x04
#define MAX31856_FAULT_OVUV 0x02
#define MAX31856_FAULT_OPEN 0x01
//热电偶类型
typedef enum
{
MAX31856_TCTYPE_B = (0x00),
MAX31856_TCTYPE_E = (0x01),
MAX31856_TCTYPE_J = (0x02),
MAX31856_TCTYPE_K = (0x03),
MAX31856_TCTYPE_N = (0x04),
MAX31856_TCTYPE_R = (0x05),
MAX31856_TCTYPE_S = (0x06),
MAX31856_TCTYPE_T = (0x07),
MAX31856_VMODE_G8 = (0x08),
MAX31856_VMODE_G32 = (0x09),
} max31856_thermocoupletype_t;
//温度
typedef struct max31856_cfg_t
{
float coldjunction_c;
float coldjunction_f;
float thermocouple_c;
float thermocouple_f;
} max31856_cfg_t;
void SPI_Init(void); // SPI初始化
void max31856_write_register(uchar addr, uchar data); //写寄存器
uchar max31856_read_register(uchar addr); //读寄存器
uint max31856_read_register16(uchar addr); //读2字节
unsigned long max31856_read_register24(uchar addr); //读3字节
void max31856_oneshot_temperature(void); //单次转换温度设置
void thermocouple_set_type(max31856_thermocoupletype_t tc_type); //设置热电偶类型
max31856_thermocoupletype_t thermocouple_get_type(void); //获取寄存器热电偶类型
float thermocouple_read_coldjunction(max31856_cfg_t *max31856); //获取冷端温度
float thermocouple_read_temperature(void); //获取温度19位
float thermocouple_read_temperature_16(void); //获取温度16位
void thermocouple_set_temperature_fault(float temp_low, float temp_high); //设置温度上下限
void Max31856_Init(void); // MAX31856初始化
#endif
max31856.c
#include "max31856.h"
void SPI_Init(void)
{
// Data is changed on the first UCLK edge and captured on the following edge.
// The inactive state is low.
// MSB first
// 8-bit data
// Master mode
// 3-pin
// Asynchronous mode
P1DIR |= 0x02;
P3DIR |= 0x0a;
CS_High;
SCK_High;
}
void SPIWrite(uchar data)
{
CS_Low;
uchar i;
for (i = 0; i < 8; i++)
{
SCK_Low;
if (data & 0x80 == 0x80)
SIMO_High;
else
SIMO_Low;
data <<= 1;
SCK_High; //上升沿数据采样
}
CS_High;
}
uchar SPIRead(void)
{
CS_Low;
uchar i;
uchar dat;
for (i = 0; i < 8; i++)
{
SCK_Low;
SCK_High;
dat <<= 1; //高位在前
if (SOMI_IN)
dat |= 0x01;
else
dat &= ~0x01;
}
CS_High;
return dat;
}
void max31856_write_register(uchar addr, uchar data)
{
addr |= 0x80;
CS_Low;
uchar i;
for (i = 0; i < 8; i++)
{
SCK_Low;
if (addr & 0x80)
SIMO_High;
else
SIMO_Low;
addr <<= 1;
SCK_High; //上升沿数据采样
}
for (i = 0; i < 8; i++)
{
SCK_Low;
if (data & 0x80)
SIMO_High;
else
SIMO_Low;
data <<= 1;
SCK_High; //上升沿数据采样
}
CS_High;
}
uchar max31856_read_register(uchar addr)
{
CS_Low;
uchar i;
uchar dat;
addr &= ~0x80;
for (i = 0; i < 8; i++)
{
SCK_Low;
if (addr & 0x80)
SIMO_High;
else
SIMO_Low;
addr <<= 1;
SCK_High; //上升沿数据采样
}
for (i = 0; i < 8; i++)
{
SCK_Low;
SCK_High;
dat <<= 1; //高位在前
if (SOMI_IN)
dat |= 0x01;
else
dat &= ~0x01;
}
CS_High;
return dat;
}
uint max31856_read_register16(uchar addr)
{
CS_Low;
uint data, data1, data2;
data1 = max31856_read_register(addr);
data2 = max31856_read_register(addr + 1);
data = data1 << 8 | data2;
CS_High;
return data;
}
unsigned long max31856_read_register24(uchar addr)
{
long data, data1, data2, data3;
data1 = max31856_read_register(addr);
data2 = max31856_read_register(addr + 1);
data3 = max31856_read_register(addr + 2);
data = (data1 << 16) | (data2 << 8) | data3;
return data;
}
void max31856_oneshot_temperature(void)
{
// max31856_write_register(MAX31856_CJTO_REG, 0x00);
uchar val = max31856_read_register(MAX31856_CR0_REG);
val &= ~MAX31856_CR0_AUTOCONVERT;
val |= MAX31856_CR0_1SHOT;
max31856_write_register(MAX31856_CR0_REG, val);
}
void thermocouple_set_type(max31856_thermocoupletype_t tc_type)
{
uchar val = max31856_read_register(MAX31856_CR1_REG);
val &= 0xF0; // Mask off bottom 4 bits
val |= (uchar)tc_type & 0x0F;
max31856_write_register(MAX31856_CR1_REG, val);
}
max31856_thermocoupletype_t thermocouple_get_type(void)
{
uchar val = max31856_read_register(MAX31856_CR1_REG);
val &= 0x0F;
return (max31856_thermocoupletype_t)(val);
}
float thermocouple_read_coldjunction(max31856_cfg_t *max31856)
{
max31856_oneshot_temperature();
uint cj_temp = max31856_read_register16(MAX31856_CJTH_REG);
float cj_temp_float = cj_temp;
cj_temp_float /= 256.0;
max31856->coldjunction_c = cj_temp_float;
max31856->coldjunction_f = (1.8 * cj_temp_float) + 32.0;
return cj_temp_float;
}
float thermocouple_read_temperature(void)
{
unsigned long tc_temp = max31856_read_register24(MAX31856_LTCBH_REG);
tc_temp >>= 5; // bottom 5 bits are unused
float tc_temp_float = tc_temp;
tc_temp_float *= 0.0078125;
// max31856->thermocouple_c = tc_temp_float;
// max31856->thermocouple_f = (1.8 * tc_temp_float) + 32.0;
return tc_temp_float;
}
float thermocouple_read_temperature_16(void)
{
uint tc_temp = max31856_read_register16(MAX31856_LTCBH_REG);
float tc_temp_float = tc_temp;
tc_temp_float *= 0.0625;
// max31856->thermocouple_c = tc_temp_float;
// max31856->thermocouple_f = (1.8 * tc_temp_float) + 32.0;
return tc_temp_float;
}
void thermocouple_set_temperature_fault(float temp_low, float temp_high)
{
temp_low *= 16;
temp_high *= 16;
uint low = (uint)temp_low;
uint high = (uint)temp_high;
max31856_write_register(MAX31856_LTHFTH_REG, high >> 8);
max31856_write_register(MAX31856_LTHFTL_REG, high);
max31856_write_register(MAX31856_LTLFTH_REG, low >> 8);
max31856_write_register(MAX31856_LTLFTL_REG, low);
}
void Max31856_Init(void)
{
max31856_write_register(MAX31856_CR1_REG, 0x00);
max31856_write_register(MAX31856_MASK_REG, 0xff);
max31856_write_register(MAX31856_CR0_REG, 0x088);
}
uart0.h
#ifndef _UART0_H_
#define _UART0_H_
#include "allfunc.h"
void InitUART(void);
void UART_Init_38400(void);
void Send1Char(unsigned char sendchar);
void PutString(char *ptr);
unsigned char Get1Char(void);
void DisplayConfigMenu(void);
void HandleConfigMenu(unsigned char inputvalue);
void ClkInit(void);
#endif
uart0.c
#include "uart0.h"
/*******************************************
函数名称:InitUART
功 能:初始化UART端口
参 数:无
返回值 :无
********************************************/
void InitUART(void)
{
P3SEL |= 0x30; // P3.4,5 = USART0 TXD/RXD
UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 0x41; // 8MHz 9600
UCA0BR1 = 0x03; // 8MHz 9600
UCA0MCTL = UCBRS1; // Modulation UCBRSx = 2
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE;
}
/*******************************************
函数名称:Send1Char
功 能:向PC机发送一个字符
参 数:sendchar--要发送的字符
返回值 :无
********************************************/
void Send1Char(uchar sendchar)
{
while (!(IFG2 & UCA0TXIFG))
; //等待发送寄存器为空
UCA0TXBUF = sendchar;
}
/*******************************************
函数名称:PutSting
功 能:向PC机发送字符串
参 数:ptr--指向发送字符串的指针
返回值 :无
********************************************/
void PutString(char *ptr)
{
while (*ptr != '\0')
{
Send1Char(*ptr++); // 发送数据
}
}
void ClkInit(void)
{
char i;
/*------选择系统主时钟为8MHz-------*/
BCSCTL1 &= ~XT2OFF; //打开XT2高频晶体振荡器
do
{
IFG1 &= ~OFIFG; //清除晶振失败标志
for (i = 0xFF; i > 0; i--)
; //等待8MHz晶体起振
} while ((IFG1 & OFIFG)); //晶振失效标志仍然存在?
BCSCTL2 |= SELM_2 + SELS; // MCLK和SMCLK选择高频晶振
// TACTL |= TASSEL_2 + ID_3; //计数时钟选择SMLK=8MHz,1/8分频后为1MHz
}
ds1302.h
#ifndef _DS1302_H_
#define _DS1302_H_
#include "allfunc.h"
//宏定义
#define DS_RST BIT7 // DS_RST = P2.7
#define DS_SCL BIT5 // DS_SCL = P2.5
#define DS_SDA BIT6 // DS_SDA = P2.6
#define DS_RST_IN P2DIR &= ~DS_RST
#define DS_RST_OUT P2DIR |= DS_RST
#define DS_RST0 P2OUT &= ~DS_RST
#define DS_RST1 P2OUT |= DS_RST
#define DS_SCL_IN P2DIR &= ~DS_SCL
#define DS_SCL_OUT P2DIR |= DS_SCL
#define DS_SCL0 P2OUT &= ~DS_SCL
#define DS_SCL1 P2OUT |= DS_SCL
#define DS_SDA_IN P2DIR &= ~DS_SDA
#define DS_SDA_OUT P2DIR |= DS_SDA
#define DS_SDA0 P2OUT &= ~DS_SDA
#define DS_SDA1 P2OUT |= DS_SDA
#define DS_SDA_BIT P2IN &DS_SDA
void delay(unsigned int time);
void Reset_DS1302(void);
void Write1Byte(unsigned char wdata);
unsigned char Read1Byte(void);
void W_Data(unsigned char addr, unsigned char wdata);
unsigned char R_Data(unsigned char addr);
void BurstWrite1302(unsigned char *ptr);
void BurstRead1302(unsigned char *ptr);
void BurstWriteRAM(unsigned char *ptr);
void BurstReadRAM(unsigned char *ptr);
void Set_DS1302(unsigned char *ptr);
void Get_DS1302(unsigned char *ptr);
#endif
ds1302.c
#include "ds1302.h"
typedef unsigned long ulong;
/*******************************************
函数名称:delay
功 能:延时一段时间
参 数:time--延时长度
返回值 :无
********************************************/
void delay(uint time)
{
uint i;
for (i = 0; i < time; i++)
_NOP();
}
/*******************************************
函数名称:Reset_DS1302
功 能:对DS1302进行复位操作
参 数:无
返回值 :无
********************************************/
void Reset_DS1302(void)
{
DS_RST_OUT; // RST对应的IO设置为输出状态
DS_SCL_OUT; // SCLK对应的IO设置为输出状态
DS_SCL0; // SCLK=0
DS_RST0; // RST=0
delay(10);
DS_SCL1; // SCLK=1
}
/*******************************************
函数名称:Write1Byte
功 能:对DS1302写入1个字节的数据
参 数:wdata--写入的数据
返回值 :无
********************************************/
void Write1Byte(uchar wdata)
{
uchar i;
DS_SDA_OUT; // SDA对应的IO设置为输出状态
DS_RST1; // REST=1;
for (i = 8; i > 0; i--)
{
if (wdata & 0x01)
DS_SDA1;
else
DS_SDA0;
DS_SCL0;
delay(10);
DS_SCL1;
delay(10);
wdata >>= 1;
}
}
/*******************************************
函数名称:Read1Byte
功 能:从DS1302读出1个字节的数据
参 数:无
返回值 :读出的一个字节数据
********************************************/
uchar Read1Byte(void)
{
uchar i;
uchar rdata = 0X00;
DS_SDA_IN; // SDA对应的IO设置为输入状态
DS_RST1; // REST=1;
for (i = 8; i > 0; i--)
{
DS_SCL1;
delay(10);
DS_SCL0;
delay(10);
rdata >>= 1;
if (DS_SDA_BIT)
rdata |= 0x80;
}
return (rdata);
}
/*******************************************
函数名称:W_Data
功 能:向某个寄存器写入一个字节数据
参 数:addr--寄存器地址
wdata--写入的数据
返回值 :无
********************************************/
void W_Data(uchar addr, uchar wdata)
{
DS_RST0;
DS_SCL0;
_NOP();
DS_RST1;
Write1Byte(addr); //写入地址
Write1Byte(wdata); //写入数据
DS_SCL1;
DS_RST0;
}
/*******************************************
函数名称:R_Data
功 能:从某个寄存器读出一个字节数据
参 数:addr--寄存器地址
返回值 :读出的数据
********************************************/
uchar R_Data(uchar addr)
{
uchar rdata;
DS_RST0;
DS_SCL0;
_NOP();
DS_RST1;
Write1Byte(addr); //写入地址
rdata = Read1Byte(); //读出数据
DS_SCL1;
DS_RST0;
return (rdata);
}
/*******************************************
函数名称:BurstWrite1302
功 能:以burst方式向DS1302写入批量时间数据
参 数:ptr--指向时间数据存放地址的指针
返回值 :读出的数据
说 明:时间数据的存放格式是:
秒,分,时,日,月,星期,年,控制
【7个数据(BCD格式)+1个控制】
********************************************/
void BurstWrite1302(uchar *ptr)
{
uchar i;
W_Data(0x8e, 0x00); //允许写入
DS_RST0;
DS_SCL0;
_NOP();
DS_RST1;
Write1Byte(0xbe); // 0xbe:时钟多字节写入命令
for (i = 8; i > 0; i--)
{
Write1Byte(*ptr++);
}
DS_SCL1;
DS_RST0;
W_Data(0x8e, 0x80); // 禁止写入
}
/*******************************************
函数名称:BurstRead1302
功 能:以burst方式从DS1302读出批量时间数据
参 数:ptr--指向存放时间数据地址的指针
返回值 :无
说 明:时间数据的存放格式是:
秒,分,时,日,月,星期,年,控制
【7个数据(BCD格式)+1个控制】
********************************************/
void BurstRead1302(uchar *ptr)
{
uchar i;
DS_RST0;
DS_SCL0;
_NOP();
DS_RST1;
Write1Byte(0xbf); // 0xbf:时钟多字节读命令
for (i = 8; i > 0; i--)
{
*ptr++ = Read1Byte();
}
DS_SCL1;
DS_RST0;
}
/*******************************************
函数名称:BurstWriteRAM
功 能:以burst方式向DS1302的RAM中写入批量数据
参 数:ptr--指向存放数据地址的指针
返回值 :无
说明 :共写入31个字节的数据
********************************************/
void BurstWriteRAM(uchar *ptr)
{
uchar i;
W_Data(0x8e, 0x00); //允许写入
DS_RST0;
DS_SCL0;
_NOP();
DS_RST1;
Write1Byte(0xfe); // 0xfe:RAM多字节写命令
for (i = 31; i > 0; i--) // RAM共有31个字节
{
Write1Byte(*ptr++);
}
DS_SCL1;
DS_RST0;
W_Data(0x8e, 0x80); //禁止写入
}
/*******************************************
函数名称:BurstReadRAM
功 能:以burst方式从DS1302的RAM中读出批量数据
参 数:ptr--指向数据存放地址的指针
返回值 :无
说明 :共读出31个字节的数据
********************************************/
void BurstReadRAM(uchar *ptr)
{
uchar i;
DS_RST0;
DS_SCL0;
_NOP();
DS_RST1;
Write1Byte(0xff); // 0xff:RAM的多字节读命令
for (i = 31; i > 0; i--)
{
*ptr++ = Read1Byte();
}
DS_SCL1;
DS_RST0;
}
/*******************************************
函数名称:Set_DS1302
功 能:设置DS1302内部的时间
参 数:ptr--指向存放数据地址的指针
返回值 :无
说明 :写入数据的格式:
秒 分 时 日 月 星期 年 【共7个字节】
********************************************/
void Set_DS1302(uchar *ptr)
{
uchar i;
uchar addr = 0x80;
W_Data(0x8e, 0x00); //允许写入
for (i = 7; i > 0; i--)
{
W_Data(addr, *ptr++);
addr += 2;
}
W_Data(0x8e, 0x80); //禁止
}
/********************************************************************
*
* 名称: Get_DS1302
* 说明:
* 功能: 读取DS1302当前时间
* 调用: R_Data(uchar addr)
* 输入: ucCurtime: 保存当前时间地址。当前时间格式为: 秒 分 时 日 月 星期 年
* 7Byte (BCD码) 1B 1B 1B 1B 1B 1B 1B
* 返回值: 无
***********************************************************************/
/*******************************************
函数名称:Get_DS1302
功 能:读取DS1302内部的时间
参 数:ptr--指向数据存放地址的指针
返回值 :无
说明 :读出数据的格式:
秒 分 时 日 月 星期 年 【共7个字节】
********************************************/
void Get_DS1302(uchar *ptr)
{
uchar i;
uchar addr = 0x81;
uchar BCDbuf[7];
//读出数据的格式: 秒 分 时 日 月 星期 年 【共7个字节】
for (i = 0; i < 7; i++)
{
BCDbuf[i] = R_Data(addr);
addr += 2;
}
for (i = 0; i < 3; i++)
{
ptr[i + 3] = 10 * ((BCDbuf[2 - i] & 0xf0) >> 4) + (BCDbuf[2 - i] & 0x0f); //时分秒
}
ptr[0] = 10 * ((BCDbuf[6] & 0xf0) >> 4) + (BCDbuf[6] & 0x0f); //年月日
ptr[1] = 10 * ((BCDbuf[4] & 0xf0) >> 4) + (BCDbuf[4] & 0x0f); //年月日
ptr[2] = 10 * ((BCDbuf[3] & 0xf0) >> 4) + (BCDbuf[3] & 0x0f); //年月日
}