外设驱动库开发笔记53:MAX31856热偶变送器驱动

2023-11-15

  在我们的产品中经常有需要温度检测的地方,而热电偶温度检测电路是我们常用的。热电偶温度检测的方法很多,有时出于简单方便的考虑我们会选择热偶温度变送器来实现,这一篇我们就来讨论使用MAX31856热电偶温度变送器实现温度的检测。

1、功能概述

  MAX31856可以对任何类型热电偶的信号进行冷端补偿和数字转换,输出数据以摄氏度为单位。转换器温度分辨率达0.0078125°C,允许读取最高+1800°C、最低-210°C (取决于热电偶类型)的温度读数,热电偶电压测量精度达±0.15%。热电偶输入端提供±45V过压保护。

  MAX31856内部的查找表(LUT)储存不同类型热电偶(K、J、N、R、S、T、E和B)的线性修正数据。而且MAX31856还具备50H和60Hz电网频率滤波,也是热电偶故障检测频率。SPI兼容接口允许选择热电偶类型并设置转换和故障检测过程。
热电偶功能是检测热电偶线两端的温度差。可在热电偶的额定工作温度范围内测量其检测端(常称为“热”端),关于热电偶测温范围:

  MAX31856将冷端温度数据储存在寄存器0Ah和0Bh。使能冷端温度检测时,这些寄存器为只读,其中包含实测冷端温度加冷端失调寄存器的数值。冷端温度检测使能时,读取寄存器操作将DRDY引脚复位为高电平。应通过多字节传输读取该寄存器的两个字节,以确保两个字节的数据来自同一次温度更新。禁止冷端温度检测时,这些寄存器为可读/写寄存器,其中包含最新的实测温度值。如果需要,禁止内部冷端检测时,可将来自外部温度传感器的数据写入这些传感器。最大冷端温度箝位在128°C,最小温度箝位在-64°C。

  由于所有热电偶都具有非线性,必须对冷端补偿后的原始值进行线性修正,并转换为温度值。为实现这一处理,利用LUT产生经过线性化和冷端补偿的温度值;每次转换后,将其作为19位数据储存在线性化热电偶温度寄存器(0Ch、0Dh和0Eh)中。应通过多字节传输读取全部三个字节,以确保所有数据来自于同一次数据更新。关于线性化热电偶温度数据格式,

  与MAX31856的通信通过16个包含转换、状态和配置数据的8位寄存器实现,全部设置均通过选择相应寄存器单元的对应地址完成,寄存器存储器映射所示为温度、状态和配置寄存器的地址。存取寄存器时,使用地址0Xh为读操作,地址8Xh为写操作。读写数据时,寄存器MSB在前。如果对只读寄存器执行写操作,不改变该寄存器的值。 

2、驱动设计与实现

  我们了解了MAX31856热偶温度变送器的基本情况,接下来我们考虑如何实现MAX31856热偶温度变送器的驱动程序。

2.1、对象定义

  我们依然是基于对象的概念来实现驱动程序的设计。所以我们首先来考虑对象类型的定义。
  作为一个对象至少包含有属性和操作。我们先来分析一下MAX31856热偶温度变送器对象的属性有哪些。MAX31856热偶温度变送器拥有16个寄存器,这些寄存器标识了MAX31856热偶温度变送器当前时刻所处的状态,所以我们将它们定义为属性。同时考虑到记录当前时刻读取的温度转换值和根据物理量程转换的温度值,所以我们将目标温度及冷端温度的ADC转换值及物理量值作为MAX31856热偶温度变送器对象的属性。
  接下来我们考虑一下MAX31856热偶温度变送器对象需要实现哪些操作。我们只考虑与具体平台依赖性较强的操作。对于MAX31856热偶温度变送器对象,当其完成AD转换回给出一个就绪指示,我们需要实时的检测这个信号,并且这个过程依赖于具体的软硬件平台,所以我们将检测过程设计为对象的操作。我们使用MAX31856热偶温度变送器需要对其进行读写,这一过程也同样依赖于具体的软硬件平台,所以我们也将其作为对象的操作。另外MAX31856用一个片选信号,在实现总线操作时我们需要以此来选择目标器件,所以我们也将其作为对象的操作。根据前述对属性和操作的分析,我们可以定义对象类型如下:

/*定义MAX31856对象类型*/
typedef struct Max31856Object {
    uint8_t regValue[16];
    uint32_t mDataCode;
    uint32_t rDataCode;
    float mTemperature;     //TC测量温度
    float rTemperature;      //冷端温度
    uint8_t (*Ready)(void);
    void (*ReadData)(uint8_t *rData,uint16_t rSize);
    void (*WriteData)(uint8_t *wData,uint16_t wSize);
    void (*ChipSelcet)(Max31856CSType cs);     //片选信号
}Max31856ObjectType;

  我们已经定义了对象类型,使用对象类型l可以声明类型变量,但类型变量必须要初始化才能使用,所以我们还需要设计一个对象的初始化函数。在这个初始化函数中,我们需要将对象变量以及具体应用相关的属性及操作作为参数传入,并对对象的各个属性及操作函数指针赋初值。具体实现如下;

/*初始化MAX31855对象*/
void Max31856Initialization(Max31856ObjectType *tc,         //MAX31856对象变量
                            Max31856Ready ready,            //就绪信号
                            Max31856ReadDataType read,      //读MAX31856函数指针
                            Max31856WriteDataType write,    //写MAX31856函数指针
                            Max31856ChipSelcetType cs       //片选操作函数指针
                                )
{
    uint8_t regValue=0;
    uint8_t rData[16]={0};
    
    if((tc==NULL)||(ready==NULL)||(read==NULL)||(write==NULL))
    {
        return;
    }
    
    tc->Ready=ready;
    tc->ReadData=read;
    tc->WriteData=write;
    
    if(cs!=NULL)
    {
        tc->ChipSelcet=cs;
    }
    else
    {
        tc->ChipSelcet=DefaultChipSelect;
    }
    
    tc->mDataCode=0;
    tc->rDataCode=0;
    tc->mTemperature=0.0;
    tc->rTemperature=0.0;
    
    tc->ChipSelcet(Max31856CS_Disable);
    
    regValue=0x81;
    
    WriteRegister(tc,REG_CR0,regValue);
    
    ReadRegister(tc,REG_CR0,rData,16);
    for(int i=0;i<16;i++)
    {
        tc->regValue[i]=rData[i];
    }
}

2.2、对象操作

  我们定义了对象类型并实现了初始化函数,接下来我们需要考虑要对MAX31856热偶温度变送器执行整么样的操作,毕竟得到数据才是我们的目的。我们考虑到需要设置相应的寄存器以实现相应功能,同时也需要获取寄存器的值以得到设备状态,或者从MAX31856热偶温度变送器获取测量数据。
  我们先来看怎么读取寄存器的值。我们读取寄存器的值用于判断MAX31856当前的运行状态,前面我们已经叙述过寄存器的地址及功能,而读寄存器的时序要求如下:

  根据前面的描述及上述时序图的要求可以编写读寄存器的函数如下:

/*读寄存器操作*/
static void ReadRegister(Max31856ObjectType *tc,uint8_t regAddr,uint8_t *rData,uint8_t rSize)
{
    uint8_t wData=regAddr;

    if(rSize<1)
    {
        return;
    }
    
    tc->ChipSelcet(Max31856CS_Enable);
    
    tc->WriteData(&wData,1);
    
    tc->ReadData(rData,rSize);
    
    tc->ChipSelcet(Max31856CS_Disable);

}

  同样我们写寄存器时,我们根据前述寄存器的相关描述和写寄存器的时序图来实现。写寄存器的时序图如下:

  根据上述描述我们可以实现写寄存器值的函数如下:

/*写寄存器操作*/
static void WriteRegister(Max31856ObjectType *tc,uint8_t regAddr,uint8_t value)
{
    uint8_t wData[2];
    
    if(regAddr>11)
    {
        return;
    }
    
    
    wData[0]=regAddr+0x80;
    wData[1]=value;
    
    tc->ChipSelcet(Max31856CS_Enable);
    tc->WriteData(wData,2);
    tc->ChipSelcet(Max31856CS_Disable);
}

  我们使用MAX31856的目的就是为了得到温度测量数据,所以我们来看看如何读取温度数据。温度转换值可以一次读取测量数据和冷端数据,其时序图如下:

  根据上述描述我们一次性读取6个字节的数据,具体实现如下:

/*获取MAX31855测量数据*/
void Max31856GetDatas(Max31856ObjectType *tc)
{
    uint8_t rData[6]={0};

    if(tc->Ready())
    {
        if((tc->regValue[REG_CR0]&0x80) != 0x80)
        {
            WriteRegister(tc,REG_CR0,0x81);
            ReadRegister(tc,REG_CR0,rData,1);
            tc->regValue[REG_CR0]=rData[0];
        }
        return;
    }
    
    ReadRegister(tc,REG_CJTH,rData,6);

    tc->rDataCode=(rData[0]<<8)+rData[1];
    
    tc->mDataCode=(rData[2]<<16)+(rData[3]<<8)+rData[4];

    tc->regValue[REG_SR]=rData[5];
    
    tc->mTemperature=CalcMeasureTemperature(tc->mDataCode);
    tc->rTemperature=CalcColdEndTemperature(tc->rDataCode);

}

3、驱动的使用

  我们已经实现了MAX31856热偶温度变送器的驱动程序,这一节我们来使用该驱动程序实现一个简单应用,以验证驱动程序的正确性。

3.1、声明并初始化对象

  首先我们需要使用前面定义的MAC31856热偶温度变送器对象类型声明一个对象变量。在我们的系统中,总线上挂载了4个MAX31856,所以我们声明如下:

Max31856ObjectType tcObj[4];

  声明的对象变量需要先初始化方可使用,而初始化函数有5个参数。第一个参数是需要要初始化的对象变量的指针,而余下的4个参数则是平台相关的操作函数指针。这些函数的原型定义如下:

typedef uint8_t (*Max31856Ready)(void);
typedef void (*Max31856ReadDataType)(uint8_t *rData,uint16_t rSize);
typedef void (*Max31856WriteDataType)(uint8_t *wData,uint16_t wSize);
typedef void (*Max31856ChipSelcetType)(Max31856CSType cs);     //片选信号

  这几个函数则是我们需要根据具体的软硬件平台来实现的。由于是在同一总线上,所以读写函数只需统一定义就好,但偏选信号和就绪信号则需根据模块单独定义。具体的函数实现如下:

/*温度模块1就绪操作函数*/
static uint8_t Tc1Ready(void)
{
    uint8_t result=1;
    result=HAL_GPIO_ReadPin(TC1_RDY_GPIO_Port,TC1_RDY_Pin);
    return result;
}

/*温度模块2就绪操作函数*/
static uint8_t Tc2Ready(void)
{
    uint8_t result=1;
    result=HAL_GPIO_ReadPin(TC2_RDY_GPIO_Port,TC2_RDY_Pin);
    return result;
}

/*温度模块3就绪操作函数*/
static uint8_t Tc3Ready(void)
{
    uint8_t result=1;
    result=HAL_GPIO_ReadPin(TC3_RDY_GPIO_Port,TC3_RDY_Pin);
    return result;
}

/*温度模块4就绪操作函数*/
static uint8_t Tc4Ready(void)
{
    uint8_t result=1;
    result=HAL_GPIO_ReadPin(TC4_RDY_GPIO_Port,TC4_RDY_Pin);
    return result;
}

/*SPI1写数据操作*/
static void WriteData(uint8_t *wData,uint16_t wSize)
{
    HAL_SPI_Transmit(&hspi1, wData, wSize, 1000);
}

/*温度模块1片选操作函数*/
static void Tc1ChipSelcet(Max31856CSType cs)
{
    if(Max31856CS_Enable == cs)
    {
        HAL_GPIO_WritePin(TC1_CS_GPIO_Port, TC1_CS_Pin, GPIO_PIN_RESET);
        return;
    }
    HAL_GPIO_WritePin(TC1_CS_GPIO_Port, TC1_CS_Pin, GPIO_PIN_SET);
}

/*温度模块2片选操作函数*/
static void Tc2ChipSelcet(Max31856CSType cs)
{
    if(Max31856CS_Enable == cs)
    {
        HAL_GPIO_WritePin(TC2_CS_GPIO_Port, TC2_CS_Pin, GPIO_PIN_RESET);
        return;
    }
    HAL_GPIO_WritePin(TC2_CS_GPIO_Port, TC2_CS_Pin, GPIO_PIN_SET);
}

/*温度模块3片选操作函数*/
static void Tc3ChipSelcet(Max31856CSType cs)
{
    if(Max31856CS_Enable == cs)
    {
        HAL_GPIO_WritePin(TC3_CS_GPIO_Port, TC3_CS_Pin, GPIO_PIN_RESET);
        return;
    }
    HAL_GPIO_WritePin(TC3_CS_GPIO_Port, TC3_CS_Pin, GPIO_PIN_SET);
}

/*温度模块4片选操作函数*/
static void Tc4ChipSelcet(Max31856CSType cs)
{
    if(Max31856CS_Enable == cs)
    {
        HAL_GPIO_WritePin(TC4_CS_GPIO_Port, TC4_CS_Pin, GPIO_PIN_RESET);
        return;
    }
    HAL_GPIO_WritePin(TC4_CS_GPIO_Port, TC4_CS_Pin, GPIO_PIN_SET);
}

  完成这些函数的定义后我们就可以初始化对象变量了!将对象变量的指针以及这些函数的指针作为参数传递给初始化函数,具体实现如下:

/*初始化MAX31856对象*/
    Max31856Initialization(&tcObj[3],
                           Tc4Ready,
                           ReadData,
                           WriteData,
                           Tc4ChipSelcet
                               );
    Max31856Initialization(&tcObj[0],
                           Tc1Ready,
                           ReadData,
                           WriteData,
                           Tc1ChipSelcet
                               );
    Max31856Initialization(&tcObj[1],
                           Tc2Ready,
                           ReadData,
                           WriteData,
                           Tc2ChipSelcet
                               );
    Max31856Initialization(&tcObj[2],
                           Tc3Ready,
                           ReadData,
                           WriteData,
                           Tc3ChipSelcet
                               );

  至此我们就完成了对象变量的声明及初始化,在后续操作中就可以使用对象变量对对应的MAX32856热偶温度变送器进行各种操作。

3.2、基于对象进行操作

  现在我们就可以在应用中使用驱动程序完成我们想要对MAX31856进行的操作了!在这个例子中我们分别读取4个MAX31856对象去测量值,并对这个测量值进行滤波处理。

Max31856GetDatas(&tcObj[0]);
    tFilter[0].newValue=tcObj[0].mTemperature;
    if(tcObj[0].mTemperature<0.0)
    {
        kF[0]=1.125;
    }
    aPara.phyPara.tc1Temp=Power3Polyfit(BandSmoothingFilter(&tFilter[0]),0.0,0.0,kF[0],0.0);
    
    Max31856GetDatas(&tcObj[1]);
    tFilter[1].newValue=tcObj[1].mTemperature;
    if(tcObj[1].mTemperature<0.0)
    {
        kF[1]=1.126;
    }
    aPara.phyPara.tc2Temp=Power3Polyfit(BandSmoothingFilter(&tFilter[1]),0.0,0.0,kF[1],0.0);
    
    Max31856GetDatas(&tcObj[2]);
    tFilter[2].newValue=tcObj[2].mTemperature;
    if(tcObj[2].mTemperature<0.0)
    {
        kF[2]=1.125;
    }
    aPara.phyPara.tc3Temp=Power3Polyfit(BandSmoothingFilter(&tFilter[2]),0.0,0.0,kF[2],0.0);
    
    Max31856GetDatas(&tcObj[3]);
    tFilter[3].newValue=tcObj[3].mTemperature;
    if(tcObj[3].mTemperature<0.0)
    {
        kF[3]=1.125;
    }
    aPara.phyPara.tc4Temp=Power3Polyfit(BandSmoothingFilter(&tFilter[3]),0.0,0.0,kF[3],0.0);

  到这里我们就完成了整个测试程序的编写,运行后能够正确读取温度数据,说明我们设计的驱动程序是正确的。

4、应用总结

  在这一篇中我们设计并实现了MAX31856热偶温度变送器的驱动程序,也编写了测试应用来验证这一驱动程序,测试的结果符合我们的预期。事实上,这一测试应用是从我们的实际项目中提取出来的,我们设计的MAX31856热偶温度变送器驱动程序在实际项目中运行也完全符合要求。
  在使用驱动程序时需要注意,在我们的应用中是一条SPI总线挂载了4个MAX31856模块,所以需要偏选信号。如果在应用中MAX31856是硬件设定的偏选信号,则可以在初始化时使用NULL或者空函数替代。

欢迎关注:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

外设驱动库开发笔记53:MAX31856热偶变送器驱动 的相关文章

  • 图论17(Leetcode864.获取所有钥匙的最短路径)

    用二进制表示获得的钥匙 假设n 钥匙个数 000000000代表没有钥匙 0000000001代表有idx为1的钥匙 0000000011代表有idx 1 2的钥匙 这方法巧妙又复杂 代码 class Solution static int
  • 补码的求法

    补码 源码取反再加一 eg CAN通信的一部分 1024对应0xfcc 第一种方法 1024二进制 0000010000000000 注意数据类型 需要加上0 取反 1111101111111111 加一 1111110000000000
  • Base64编码相关知识总结

    Base64编码是什么 Base64 顾名思义 就是包括小写字母a z 大写字母A Z 数字0 9 符号 一共64个字符的字符集 另加一个 实际是65个字符 任何符号都可以转换成这个字符集中的字符 这个转换过程就叫做base64编码 Bas
  • jdbc的内容以及如何在5秒钟后自动跳转到login.html页面

    首先 在5秒钟之后跳转到login html 最初的解决方法 try Thread sleep 5000 response sendRedirect login html catch Exception e e printStackTrac
  • STM32 GPIO |CSDN创作打卡

    GPIO结构框图 推挽输出 0 3 3 在该结构中输入高电平时 上方的P MOS导通 下方的N MOS截止 对外输出高电平 而在该结构中输入低电平时 N MOS管导通 P MOS截止 对外输出低电平 当引脚高低电平切换时 两个管子轮流导通
  • 如何下载b站视频到本地

    传送门 转载于 https www cnblogs com yaoling1997 p 10793366 html
  • assert()理解

    源自一道CTF题 理解全部写在注释里面 if isset GET page page GET page else page home file templates page php I heard is dangerous strpos通过
  • JAVA中字符串长度和(字符串)数组长度的函数【string.length和string.length()】

    字符串数组strs 获取数组的长度是利用数组的属性length 所以就是 strs length 字符串strs 获取长度的话是调用strs length 方法 字符串长度 字符串数组长度 字符串数组的长度的 因为任意的数组都有length
  • 五十七.斐波那契数列JAVA

    public class Main public static int fibonacci int n if n 0 n 1 return n else return fibonacci n 1 fibonacci n 2 public s
  • 2021-07-01

    2021年已经过去七个月了 学习目标 把vue element echarts给撸下来 学习内容 7 1 7 4 学习 Echarts 7 5 7 11 学习封装router 7 12 7 18 学习如何部署网站到gitee 7 19 7
  • 对象池学习

    概念 为了避免大量创建 构造 对象 销毁 析构 对象带的性能开销 设计 对象队列 初始化时 指定队列长度 出队 入队操作需要加锁保护 对象的构造 在初始化对象池时构造好 对象并不是一开始全部构造好 而是在获取对象的过程中构造 构造之后便保存
  • Linux平台下安全编译

    1 操作系统的软件堆栈 内存映射等区域配置 mmap vdso页 共享库等 实现方式 修改操作系统文件 proc sys kernel randomize va space 内容改为2 原因 ASLR针对缓冲区溢出攻击 此处将堆栈 共享库映
  • jsp体质表_国家学生体质健康网数据上报平台:http://www.csh.edu.cn/MOETC/index.jsp

    国家学生体质健康网数据上报平台即 国家学生体质健康标准数据管理分析系统 数据上报流程 1 登录中国学生体质健康网 www csh edu cn 进行上报学校网上登记并下载验证文件 ebm 2 使用国家数据库上报软件进行数据上报 3 登录中国
  • Java面向对象(基础)

    面向对象内容的三条主线 Java类及类的成员 重点 属性 方法 构造器 熟悉 代码块 内部类 面向对象的特征 封装 继承 多态 抽象 其他关键字的使用 this super package import static final inter
  • springboot启动报错 Failed to configure a DataSource: ‘url‘ attribute is not specified and no embedded 。。

    一 错误如下 二 错误的原因 项目启动是出现该错误的原因 SpringBoot SpringBootApplication该注释会有数据库的自动配置 但在项目配置文件中没有找到相关的配置导致的 三 解决方法 如果是配置错误修改配置就可以 如
  • 线上一次JVM FullGC搞得整晚都没睡,彻底崩溃~

    V xin ruyuanhadeng获得600 页原创精品文章汇总PDF 这篇文章给大家聊一次线上生产系统事故的解决经历 其背后代表的是线上生产系统的JVM FullGC可能引发的严重故障 一 业务场景介绍 先简单说说线上生产系统的一个背景

随机推荐