【STM32篇】驱动MXL90614红外测温模块

2023-11-13

本次实验使用的测温模块型号GY-906-DCC模块,测距为10cm左右。

一、简介

MLX90614 是一款红外非接触温度计。TO-39 金属封装里同时集成了红外感应热电堆探测器芯片和信处理专用集成芯片。

由于集成了低噪声放大器、17 位模数转换器和强大的数字信号处理单元,使得高精度和高分辨度的温计得以实现。温度计具备出厂校准化,有数字 PWM 和 SMBus(系统管理总线)输出模式。

作为标准,配置为 10 位的 PWM 输出格式用于连续传送温度范围为-20…120 ˚C 的物体温度,其分辨率为 0.14 ˚C。POR 默认模式是 SMBus 输出格式。

图1.典型应用电路图

(一)引脚定义和描述

图2.顶视图

引脚名

功能

VSS

地。 金属罐也连接到该引脚上。

SCL/Vz

两线通信协议的串行时钟信号。该引脚上的 5.7V 齐纳二极管用于连接外部双极晶体管

以实现对器件高电源电压 8 …16V 的应用。

PWM/SDA

数字信号输入 / 输出。正常模式下该引脚以脉宽调制方式输出测量的物体温度。

SMBus 兼容模式下自动配置为开漏 NMOS。

VDD

外部电源电压。

(二)MLX90614 存储器

EEPROM

EEPROM 里只有限定数目的地址是允许客户改写的。整个 EEPROM 可通过 SMBus 接口读和写。

图3.EEPROM

MLX90614 的 EEPROM 有 32 个 16 位存储单元,其中存储单元 Tomax,Tomin,Ta 分别是用户物体温度上下限和环境温度范围,PWMCTRL 是 PWM 配置寄存器。

MLX90614 可通过 PWM 或是 SMBus 兼容接口读取,由 EEPROM 配置选择 PWM 输出模式。 (出厂默认为SMBus) PWM 输出有两种可编程的格式,单个和双重数据传送,提供单线读取两个温度的功能。(双重可以选择物体或是物体和环境温度) PWM 周期通过片上振荡器驱动并可编程。

RAM

RAM 中不能写入数据,只能进行读取,并且只有有限数目是客户感兴趣的。

图4.RAM

MLX90614 的RAM有 32 个 17 位存储单元,其中TA,TOBJ1,TOBJ2是环境温度和物体温度,在SMBus方式下,可以从这几个存储单元读出环境和被测物体的温度。

命令 Command

图5.命令码

xxxxx 代表要读取/写入的内存地址的 低5 位。

(三)两线协议

SMBus 接口为两线协议,允许主控器件 (MD) 和一个或是一个以上的从动器件 (SD) 通信。系统在给定的时刻只有一个主控器件。MLX90614 只作为从动器件使用。

使用模拟IIC实现SMBus通信。

图6.IIC通信协议

起始信号和停止信号

图7.起始信号和停止信号

起始信号:在 SCL 线是高电平时 SDA 线从高电平向低电平切换。

停止信号:SCL 是高电平时 SDA 线由低电平向高电平切换。

起始和停止条件一般由主机产生 总线在起始条件后被认为处于忙的状态 在停止条件的某段时间后总线被认为再次处于空闲状态。如果产生重复起始 Sr 条件而不产生停止条件 总线会一直处于忙的状态。

//起始信号
void MLX906_START_Sign(void)
{
    MLX906_SDA_OUT();//输出模式
    MLX906_SDA_H;
    MLX906_SCL_H;
    Delay_us(2);
    MLX906_SDA_L;
    Delay_us(2);
    MLX906_SCL_L;//钳住总线,准备开始数据传输
    Delay_us(1);
}
//停止信号
void MLX906_STOP_Sign(void)
{
    MLX906_SDA_OUT();//输出模式
    MLX906_SCL_L;
    MLX906_SDA_L;
    Delay_us(2);
    MLX906_SCL_H;
    MLX906_SDA_H;
    Delay_us(1);
}

应答信号和非应答信号

图8.应答信号

SCL在高电平期间SDA始终处于低电平,需要在传输完毕一个字节后发送。

//主机应答(ACK信号)
void MLX906_Ack(void)
{ 
    MLX906_SDA_OUT();//输出模式
    MLX906_SDA_L;
    MLX906_SCL_L;
    Delay_us(2);
    MLX906_SCL_H;
    Delay_us(2);
    MLX906_SCL_L;
}

图9.非应答信号

SCL在高电平期间SDA始终处于高电平,需要在传输完毕一个字节后发送。

//不产生ACK应答信号
void MLX906_Nack(void)
{
    MLX906_SDA_OUT();//输出模式
    MLX906_SCL_L;
    MLX906_SDA_H;
    Delay_us(2);
    MLX906_SCL_H;
    Delay_us(2);
    MLX906_SCL_L;
    Delay_us(2);
}

响应

图10.IIC总线的响应

数据传输必须带响应。相关的响应时钟脉冲由主机产生。在响应的时钟脉冲期间,发送器释放SDA线(高)。

在响应的时钟脉冲期间 ,接收器必须将SDA 线拉低 ,使它在这个时钟脉冲的高电平期间保持稳定的低电平。

uint8_t MLX906_Wait_Ack(void)
{
    MLX906_SDA_IN();//输入模式
    uint8_t utime=0;
    MLX906_SDA_H;
    Delay_us(1);
    MLX906_SCL_H;
    Delay_us(1);
    while(MLX906_READ_SDA())
    {
        utime++;
        if(utime>=255)
        {
            MLX906_STOP_Sign();
            return 1;//非应答信号
        }
    }
    MLX906_SCL_L;
    return 0;//应答信号
}

数据传输

在数据传输过程中,数据线SDA需在时钟线SCL为高电平时保持稳定,在低电平期间可修改或读取数据线电平状态。

发送到 SDA 线上的每个字节必须为8位 ,每次传输可以发送的字节数量不受限制 ,每个字节后必须跟一个响应位。

发送数据:

//发送一个字节数据
uint8_t MLX906_SendByte(uint8_t byte1)
{
    uint8_t i,byte;
    byte=byte1;
    MLX906_SDA_OUT();//输出模式
    for(i=0;i<8;i++)//高位先发
    {
        if(byte&0x80)
        {
            MLX906_SDA_H;
        }
        else
        {
            MLX906_SDA_L;
        }
        MLX906_SCL_H;
        Delay_us(2);
        MLX906_SCL_L;
        Delay_us(2);
        byte<<=1;
    }
    MLX906_SDA_H;//释放总线
    return MLX906_Wait_Ack();//等待响应
}
读数据:
//读取一个字节
uint8_t MLX906_ReadByte(void)
{
    uint8_t byte=0;
    uint8_t i;
    MLX906_SDA_IN();//输入模式
    for(i=0;i<8;i++)
    {
        MLX906_SCL_H;//SCL高电平时读取数据
        if(MLX906_READ_SDA())
        {
            byte|= 1<<(7-i);
        }
        MLX906_SCL_L;//下降沿时从机修改数据
        Delay_us(2);
    }
    MLX906_Nack();
    return byte;
}

二、总线协议

图11.SMBus 数据包组成

在 SD 接收到每个 8 位数据后,会回复 ACK/NACK 信息。当 MD 初始化通信,将首先发送受控地址,只有能识别该地址的 SD 会确认,其它的会保持沉默。如果 SD 未确认其中的任意字节,MD 应停止通信并重新发送信息。NACK 也会在 PEC 接收后出现,这意味着在接收的信息有错误并且 MD 应重新发送信息。

PEC 的计算结果是基于 除 START,REPEATED START,STOP,ACK, 和 NACK 位外的所有位。 PEC 是 CRC-8 的多项式 aX8+X2+X1+1。每个字节的最高有效位首先传送。

(一)读数据

图12.SMBus读数据格式

(二)软件流程

多个MLX90614可以用于一个系统中,通过地址不同区分器件,器件默认的地址为5AH,因此在多 MLX90614 系统中,需要给每个 MLX90614 分配一个不同的地址,在只有一个MLX90614 的系统中,MLX90614 识别地址 00h,即在单个 MLX90614 系统中,可以使用该地址访问它。

图13.读数据流程图

从MLX90614 种读出的数据是 16 位的,由高 8 位(DataH)和低 8 位(DataL)两部分组成,其中RAM地址 07H单元存储的是TOBJ1数据,数据范围从 0x27AD到 0x7FFF,表示的温度范围是-70.01℃到+382.19℃。

//读内存
uint16_t MLX906_ReadMemory(void)
{
    uint8_t Pec,PecReg,ErrorCounter;
    uint8_t TempL=0;
    uint8_t TempH=0;
    uint8_t arr[6];
    ErrorCounter=0;
    do
    {
        ErrorCounter++;
        if(ErrorCounter==10)
        {
            return 0;
        }            
        MLX906_START_Sign();//起始信号
        if(MLX906_SendByte(0x00))//发送MLX90614地址
            continue;
        if(MLX906_SendByte(0x07))//发送读MLX90614 RAM地址
            continue;
        MLX906_START_Sign();//重新启动
        if(MLX906_SendByte(0x01))//发送数据采集命令
            continue;
        TempL=MLX906_ReadByte();//读取地位数据
        TempH=MLX906_ReadByte();//读取高位数据
        Pec=MLX906_ReadByte();//读取校验位
        MLX906_STOP_Sign();//停止信号
        arr[5]= 0x00;
        arr[4]= 0x07;
        arr[3]= 0x01;
        arr[2]= TempL;
        arr[1]= TempH;
        arr[0]= 0;
        PecReg=CRC_Calculation(arr);//计算CRC校验
    }while(PecReg!=Pec);
    return (uint16_t)((TempH<<8)|TempL);
}

(三)环境温度和物体温度的计算

环境温度Ta

物体温度To

//读温度
float MLX906_Read_Temp(void)
{
    return (float )MLX906_ReadMemory()*0.02-273.15;
}

三、实验结果

手腕温度测量(未校准),可能有所误差。

图14.手腕温度测量

mian函数中轮询打印。

图15.UART读取温度数据

四、附件

mlx906.h

#ifndef _MLX906_H_
#define _MLX906_H_

#include "stm32f10x.h"
#include "systick.h"

/*
引脚连接:

    SCL  时钟线  - PB10
    SDA     数据线  - PB11

*/
#define MLX906_SCL_H GPIO_WriteBit(GPIOB,GPIO_Pin_10,Bit_SET)
#define MLX906_SCL_L GPIO_WriteBit(GPIOB,GPIO_Pin_10,Bit_RESET)
#define MLX906_SDA_H GPIO_WriteBit(GPIOB,GPIO_Pin_11,Bit_SET)
#define MLX906_SDA_L GPIO_WriteBit(GPIOB,GPIO_Pin_11,Bit_RESET)
#define MLX906_READ_SDA() GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)

//配置SDA数据线的输入输出模式
#define MLX906_SDA_OUT() ?????//输出模式 自己配置哦
#define MLX906_SDA_IN()  ?????//输入模式

//此处为笔者小设计中才用到的,不用管
#define MLX906_STATE_READ 1
#define MLX906_STATE_UNREAD 0
#define ANOMALY_TEMP 37     //体温异常

extern float MLX906_Temp;
extern uint8_t  MLX906_Temp_State;

void MLX906_Init(void);
float MLX906_Read_Temp(void);

#endif

mlx906.c


#include "mlx906.h"

//MLX初始化函数
void MLX906_Init(void)
{
    //初始化引脚
    GPIO_InitTypeDef mlx906_gpio_init;
    mlx906_gpio_init.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
    mlx906_gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
    mlx906_gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&mlx906_gpio_init);
    
    MLX906_SCL_H;//拉高引脚电平
    MLX906_SDA_H;
}

/*    IIC   时序    */

//起始信号
void MLX906_START_Sign(void)
{
    MLX906_SDA_OUT();//输出模式
    MLX906_SDA_H;
    MLX906_SCL_H;
    Delay_us(2);
    MLX906_SDA_L;
    Delay_us(2);
    MLX906_SCL_L;//钳住总线,准备开始数据传输
    Delay_us(1);
}
//停止信号
void MLX906_STOP_Sign(void)
{
    MLX906_SDA_OUT();//输出模式
    MLX906_SCL_L;
    MLX906_SDA_L;
    Delay_us(2);
    MLX906_SCL_H;
    MLX906_SDA_H;
    Delay_us(1);
}
uint8_t MLX906_Wait_Ack(void)
{
    MLX906_SDA_IN();//输入模式
    uint8_t utime=0;
    MLX906_SDA_H;
    Delay_us(1);
    MLX906_SCL_H;
    Delay_us(1);
    while(MLX906_READ_SDA())
    {
        utime++;
        if(utime>=255)
        {
            MLX906_STOP_Sign();
            return 1;//非应答信号
        }
    }
    MLX906_SCL_L;
    return 0;//应答信号
}
//主机应答(ACK信号)
void MLX906_Ack(void)
{
    
    MLX906_SDA_OUT();//输出模式
    MLX906_SDA_L;
    MLX906_SCL_L;
    Delay_us(2);
    MLX906_SCL_H;
    Delay_us(2);
    MLX906_SCL_L;
}
//不产生ACK应答信号
void MLX906_Nack(void)
{
    MLX906_SDA_OUT();//输出模式
    MLX906_SCL_L;
    MLX906_SDA_H;
    Delay_us(2);
    MLX906_SCL_H;
    Delay_us(2);
    MLX906_SCL_L;
    Delay_us(2);
}

//读取一个字节
uint8_t MLX906_ReadByte(void)
{
    uint8_t byte=0;
    uint8_t i;
    MLX906_SDA_IN();//输入模式
    for(i=0;i<8;i++)
    {
        MLX906_SCL_H;//SCL高电平时读取数据
        if(MLX906_READ_SDA())
        {
            byte|= 1<<(7-i);
        }
        MLX906_SCL_L;//下降沿时从机修改数据
        Delay_us(2);
    }
    MLX906_Nack();
    return byte;
}
//发送一个字节数据
uint8_t MLX906_SendByte(uint8_t byte1)
{
    uint8_t i,byte;
    byte=byte1;
    MLX906_SDA_OUT();//输出模式
    for(i=0;i<8;i++)//高位先发
    {
        if(byte&0x80)
        {
            MLX906_SDA_H;
        }
        else
        {
            MLX906_SDA_L;
        }
        MLX906_SCL_H;
        Delay_us(2);
        MLX906_SCL_L;
        Delay_us(2);
        byte<<=1;
    }
    MLX906_SDA_H;//释放总线
    return MLX906_Wait_Ack();//等待响应
}

//crc校验
uint8_t CRC_Calculation(uint8_t pec[])
{
    uint8_t crc[6];//存放多项式
    uint8_t BitPosition = 47;
    uint8_t shift;
    uint8_t i,j,temp;
    do
    {
        crc[5]=0;
        crc[4]=0;
        crc[3]=0;
        crc[2]=0;
        crc[1]=0x01;
        crc[0]=0x07;
        BitPosition = 47;
        shift = 0;
        i=5;
        j=0;
        while((pec[i]&(0x80>>j))==0 && i>0)
        {
            BitPosition--;
            if(j<7)
            {
                j++;
            }
            else
            {
                j= 0x00;
                i--;
            }
        }
        shift= BitPosition-8;
        while(shift)
        {
            for(i=5;i<0xFF;i--)
            {
                if((crc[i-1]&0x80)&&(i>0))
                {
                    temp=1;
                }
                else
                {
                    temp=0;
                }
                crc[i]<<=1;
                crc[i]+=temp;
            }
            shift--;
        }
        for(i=0;i<=5;i++)
        {
            pec[i]^=crc[i];
        }
    }while(BitPosition>8);
    return pec[0];
}
//读内存
uint16_t MLX906_ReadMemory(void)
{
    uint8_t Pec,PecReg,ErrorCounter;
    uint8_t TempL=0;
    uint8_t TempH=0;
    uint8_t arr[6];
    ErrorCounter=0;
    do
    {
        ErrorCounter++;
        if(ErrorCounter==10)
        {
            return 0;
        }            
        MLX906_START_Sign();//起始信号
        if(MLX906_SendByte(0x00))//发送MLX90614地址
            continue;
        if(MLX906_SendByte(0x07))//发送读MLX90614 RAM地址
            continue;
        MLX906_START_Sign();//重新启动
        if(MLX906_SendByte(0x01))//发送数据采集命令
            continue;
        TempL=MLX906_ReadByte();//读取地位数据
        TempH=MLX906_ReadByte();//读取高位数据
        Pec=MLX906_ReadByte();//读取校验位
        MLX906_STOP_Sign();//停止信号
        arr[5]= 0x00;
        arr[4]= 0x07;
        arr[3]= 0x01;
        arr[2]= TempL;
        arr[1]= TempH;
        arr[0]= 0;
        PecReg=CRC_Calculation(arr);//计算CRC校验
    }while(PecReg!=Pec);
    return (uint16_t)((TempH<<8)|TempL);
}
//读温度
float MLX906_Read_Temp(void)
{
    return (float )MLX906_ReadMemory()*0.02-273.15;
}

注:GPIO时钟已在main.c文件中做了相关配置。

SDA数据线的输入输出模式配置 由 读者自行配置。

望有助于各位理解,如有侵权,请联系删除。

2023/1/7

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

【STM32篇】驱动MXL90614红外测温模块 的相关文章

  • 使用分支——Git Checkout

    这篇文章写的挺好 https zhuanlan zhihu com p 465954849 这里要注意 git 新的命令 通过 git switch 切换分支 虽然git checkout 分支 还可以用 游离状态的HEADS 在我们已经见
  • pikachu靶场搭建以及搭建问题

    前言 pikachu是一个适合Web渗透测试学习的小白们进行训练的本地靶场 并且已经在github上开源了 它是一个综合性的靶场 非常适合新手练习 接下来就简单的看一下它如何在Windows上搭建吧 Apache与MySQL环境搭建 然后这
  • 如何判断合法标识符

    题目描述 给出一个标识符 请你判断它是否是C语言合法的标识符 输入 输入一个标识符 长度不超过100 输出 判断是否合法 如果是输出YES 否则输出NO 示例输入 123You 示例输出 NO 提示 C语言规定 标识符只能由字母 数字和下划

随机推荐

  • SSH 和 SSL 加密协议

    SSH和SSL都是加密协议 用于保护网络通信的安全性和完整性 但它们用途和实现方式有所不同 SSH Secure Shell 是一种网络协议 用于远程访问和管理服务器 它提供了加密的连接和认证机制 使得数据传输更加安全 SSH通常用于远程登
  • Linux下使用STM32CUBEMX的makefile,报multiple defination错误的解决办法

    之所以报这个错是因为stm32cubemx生成makefile的一个bug 在C SOURCES部分会重复添加Src 下的c文件 上图是没有修改makefile之前 下图为修改后 要修改的部分
  • pthon代码实现在linux下对siebel服务器换包重启

    siebel服务器的换包重启 需要输入多个命令 而且中间需要等待 经常停了服务忘了启动 之前项目的TA有写过一些shell脚本 启服务的 停服务的 包括自动换包重启的 但是因为里面有个mount目录经常出问题 所以平时也没有使用 最近刚好在
  • css基本语法

    1 background background image url image jpg background color ccf background position center background repeat no repeat
  • 数据库:什么是主键

    数据库主键 主键 表中经常有一个列或列的组合 其值能唯一地标识表中的每一行 通俗叫 一个表中只能有一个主键 不接受空值 能唯一的表示表中的每一行 例如 银行卡的卡号就是主键 不存在重复的情况
  • Java 数据类型转换(Casting)

    Java中 经常可以遇到类型转换的场景 从变量的定义到复制 数值变量的计算到方法的参数传递 基类与派生类间的造型等 随处可见类型转换的身影 Java中的类型转换在Java编码中具有重要的作用 本文主要介绍一下Java 数据类型转换 Cast
  • 华为防火墙默认密码是什么?

    华为默认密码是什么 分享至 小王邀请您加入牛B的IT关键业务推动者社区 点击领取12G的软考 PMP资料包 特训营名额 gt gt 售前工程师系列 教你写解决方案 gt gt ld11235813 荣誉会员 Rank 12Rank 12Ra
  • 华为动态pnat配置

  • 微软宣布IE9正式版发布日期

    微软上月曾透露会在3月14日于美国德克萨斯州奥斯汀市SXSW音乐电影节上举办一个庆祝派对 从那时起就有很多猜想 我们才曾猜测微软届时会正式发布IE9 今天 微软终于不再卖关子 3月14日 也就是下周一 微软将正式发布IE9 微软证实 美国太
  • TensorFlow之双隐含层多层感知器(MLP)

    程序改自上一篇博客 使用了双隐含层 第二层隐含层初始w需要和第一层类似 否则程序正确率一直在0 1左右 修改后的程序正确率也在98 左右 coding utf 8 from tensorflow examples tutorials mni
  • 整理一些windows的bat命令及语法

    ver 在DOS窗口下显示版本信息 winver 弹出一个窗口显示版本信息 内存大小 系统版本 补丁版本 计算机名 format 盘符 FS 类型 格式化磁盘 类型 FAT FAT32 NTFS 例 Format D FS NTFS md
  • python基础系列之元组

    元组应用场景 储存多个数据 但是这些数据不可修改 我们知道列表可以储存多个数据 但是数据可增加 修改 删除 这也是元组和列表不一样的地方 如何定义一个元组 多个数据元组 t1 10 20 30 单个数据元组 t2 10 注意在定义单个数据的
  • 常量表达式函数

    我们可以在函数返回类型前加入关键字constexpr来使其成为常量表达式函数 但并非所有的函数都有资格成为常量表达式函数 事实上 常量表达式函数的要求非常严格 总结如下 函数体只有单一的return返回语句 函数必须返回值 不能是void函
  • 合并排序-递归分治

    按我的想法 简单地说 合并排序的思路就是 先递归 后排序 include
  • Java中的运算符

    1 算术运算符 基本四则运算符 1 规则比较简单 需要注意的是除法 int int 的结果还是 int 要想结果中有小数需要使用 double 类型 0 不能用来做除数 2 在Java中 表示取余 不仅可以对 int 求模 也可以对 dou
  • Tomcat Server处理一个http请求的过程

    Tomcat Server处理一个http请求的过程 假设来自客户的请求为 http localhost 8080 wsota wsota index jsp 1 请求被发送到本机端口8080 被在那里侦听的Coyote HTTP 1 1
  • 爬虫逆向实战(13)-某课网登录

    一 数据接口分析 主页地址 某课网 1 抓包 通过抓包可以发现登录接口是user login 2 判断是否有加密参数 请求参数是否加密 通过查看 载荷 模块可以发现有一个password加密参数 还有一个browser key这个可以写死不
  • C++ Pat甲级1013 Battle Over Cities (25 分)(求图的连通分量dfs)

    1013 Battle Over Cities 25 分 It is vitally important to have all the cities connected by highways in a war If a city is
  • 数字SOC设计之低功耗设计入门(六)——门级电路低功耗设计优化

    三 门级电路低功耗设计优化 1 门级电路的功耗优化综述 门级电路的功耗优化 Gate Level Power Optimization 简称GLPO 是从已经映射的门级网表开始 对设计进行功耗的优化以满足功耗的约束 同时设计保持其性能 即满
  • 【STM32篇】驱动MXL90614红外测温模块

    本次实验使用的测温模块型号GY 906 DCC模块 测距为10cm左右 一 简介 MLX90614 是一款红外非接触温度计 TO 39 金属封装里同时集成了红外感应热电堆探测器芯片和信处理专用集成芯片 由于集成了低噪声放大器 17 位模数转