锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

STM32模拟I2C协议获取MLX90615红外温度传感器测温数据(Open Drain管脚配置)

时间:2022-12-03 13:00:00 f0温度传感器传感器pec09高精度红外传感器温度

STM32模拟I2C协议获取MLX90615红外温度传感器测温数据(Open Drain管脚配置)

STM32的GPIO可配置管脚Open Drain输出模式有两个功能:

  1. 内部上拉可以设置,所以对于I2C访问速度不是特别高,不需要外部I2C上拉电阻;
  2. 虽然是Open Drain输出管脚可以直接读取管脚的电平状态,就像读取输入管脚一样,无需将输出管脚切换成输入管脚。

MLX90615是无接触红外温度传感器DAA医疗级高精度型号,也有不同温度测量距离的型号,适合不同场景产品的应用。MLX90615可以采用PWM方式或者I2C数据获取的方法是模拟I2C实现方法。MLX90615内部有300KΩ需要弱上拉STM32侧内部上拉可以更好地保证访问速度。否则会出现时序抖动错误。

MLX90615是一积较小的红外温度测温模块MLX与以下不同:

  1. 管脚排列顺序不同
  2. 从PWM进入I2C控制时间长度因模式而异,MLX90614需要大于1.44ms,MLX90615需要大于39ms
  3. 除了默认0地址访问外,还有另一个默认访问地址,MLX90614是0x5A, 而MLX90615是0x5B
  4. 访问温度数据存放的寄存器地址不同,MLX90614是0x07, 而MLX90615是0x27

硬件连接

低成本的STM32F030F4P6.读取开发板作为控制器MLX90615温度数据。连接关系如下:
在这里插入图片描述

软件工程配置

这里采用STM32CUBEIDE开发环境和HAL库。首先建立STM32F030F4P工程和配置时钟。如果在测试中发现HCLK高于4MHz(无论是外部时钟源还是内部时钟源)I2C即时序无法保证时序功能读取错误数据。推测低端芯片相环电路性能较差,HCLK高频抖动相对较大。因此将HCLK频率配置为4MHz。另外在STM32F3和STM32F如果4系列进行相同的代码测试,则没有HCLK频率配置引起的问题也说明是STM32F0系列性能问题。

然后配置PA5和PA6作为Open Drain输出带上拉,默认为高电平输出:


然后配置USART1用于串口数据输出:


保存并生成初始工程代码。

FLASH比较小的MCU需要设置“size优化编译模式,避免编译代码占用空间超过FLASH最大空间 STM32 region `FLASH‘ overflowed by xxx bytes 问题解决。

软件工程代码

需要在代码中使用HAL工程微秒延时,HAL库工程微秒延迟实现原理参考STM32 HAL us delay(微秒延迟)指令延迟的实现和优化。

代码里I2C_Init()初始化函数用于保证MLX90615进入I2C控制模式,然后在while连续读取循环中的温度并串口输出。

MLX在90615的标准文档中有一个阅读时间序列bug,多画一个周期时钟,包括时钟周期中读取的温度数据、计算后的校准值和发送的PEC这个时钟周期应该去掉,读时序和时钟周期说明如下图所示:

而MLX在90614的规范文档中,读时序不多,MLX90614阅读时间如下:

PEC是MLX90615发出的CRC-8校验字节,MCU前五个字节内容可以在侧面做CRC-计算,得到CRC-8计算校验字节,和MLX90615发出的CRC-8验证字节比较,判断传输和接收是否正确。因此,设计是针对MLX90615读操作的CRC-8验证函数如下:

uint8_t PY_CRC_MLX90615_READ(uint8_t daddr, uint8_t Raddr, uint8_t dl, uint8_t dh) { 
           //Written by Pegasus Yu 2022/02/22   uint64_t cdata = 0; //Computed total data  uint16_t data_t = 0; //Process data of CRC computing  uint16_t crc_poly = 0x0107; //X^8 X^2 X^1 1 total 9 effective bits. Computed total data shall be compensated 8-bit '0' before CRC computing from 9-1=8.   uint16_t index_t = 47;  ///bit shifting index for initial '1' searching  uint16_t index = 47;    //bit shifting index for CRC computing   uint8_t rec = 0; //bit number needed to be compensated for next CRC computing   cdata |= (((uint64_t)daddr)<<40);       //device write address
	cdata |= (((uint64_t)Raddr)<<32);       //register access address
	cdata |= (((uint64_t)(daddr+1))<<24);   //device read address
	cdata |= (((uint64_t)dl)<<16);          //data LSB
	cdata |= (((uint64_t)dh)<<8);           //data HSB
	//8-bit '0' compensated into cdata so cdata involves 48 bits stored in 64-bit format.

	while(index_t>0)
	{ 
        
		if( (cdata>>index_t)&1 )
		{ 
        
			index = index_t;
			index_t = 0;

			data_t |= (cdata>>(index-8));
			{ 
        
				data_t = data_t ^ crc_poly;
			}

            while(index!=0xffff)
            { 
        
    			if ((data_t>>7)&1) rec = 1;
    			else if ((data_t>>6)&1) rec = 2;
    			else if ((data_t>>5)&1) rec = 3;
    			else if ((data_t>>4)&1) rec = 4;
    			else if ((data_t>>3)&1) rec = 5;
    			else if ((data_t>>2)&1) rec = 6;
    			else if ((data_t>>1)&1) rec = 7;
    			else if ((data_t>>0)&1) rec = 8;
    			else rec = 9; ///

    			if((index-8)<rec)
    			{ 
        
    				data_t = data_t<<(index-8);
    				index = 0xffff;
    			}
    			else
    			{ 
        
        			for(uint8_t i=1;i<=rec;i++)
        			{ 
        
        				data_t = (data_t<<1)|((cdata>>(index-8-i))&1) ;
        			}

        			if(rec!= 9)
        			{ 
        
        				data_t = data_t ^ crc_poly;
        				index -= rec;
        			}
        			else
        			{ 
        
        				data_t = 0;
        				index_t = index-8-1;
        				index = 0xffff;

        			}

    			}


            }

		}
		else
		{ 
        
			index_t--;
			if(index_t<8) break;
		}
	}
	return (uint8_t)data_t;
}

代码设计上,通过串口将温度数据的高字节和低字节输出,可以对高字节和低字节按照公式计算,得到浮点格式的温度数据。主要的实现代码(main.c)如下:

/* USER CODE BEGIN Header */
/** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2022 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** *Written by Pegasus Yu in 2022 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//us delay functions
float usDelayBase;
void PY_usDelayTest(void)
{ 
        
  uint32_t firstms, secondms;
  uint32_t counter = 0;

  firstms = HAL_GetTick()+1;
  secondms = firstms+1;

  while(uwTick!=firstms) ;

  while(uwTick!=secondms) counter++;

  usDelayBase = ((float)counter)/1000;
}

void PY_Delay_us_t(uint32_t Delay)
{ 
        
  uint32_t delayReg;
  uint32_t usNum = (uint32_t)(Delay*usDelayBase);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}

void PY_usDelayOptimize(void)
{ 
        
  uint32_t firstms, secondms;
  float coe = 1.0;

  firstms = HAL_GetTick();
  PY_Delay_us_t(1000000) ;
  secondms = HAL_GetTick();

  coe = ((float)1000)/(secondms-firstms);
  usDelayBase = coe*usDelayBase;
}

void PY_Delay_us(uint32_t Delay)
{ 
        
  uint32_t delayReg;

  uint32_t msNum = Delay/1000;
  uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);

  if(msNum>0) HAL_Delay(msNum);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}


//MLX90615 I2C access protocol

#define us_num 20 //available

#define SCL_OUT_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET)
#define SCL_OUT_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET)
#define SDA_OUT_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET)
#define SDA_OUT_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET)
#define SDA_IN HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)

void I2C_Init(void)
{ 
        
	SDA_OUT_H;
	SCL_OUT_L;
	PY_Delay_us_t(50000) ;  //to enable i2c if previous mode PWM
	SCL_OUT_H;
	SDA_OUT_H;
	PY_Delay_us_t(2000000) ;
}

void I2C_Start(void)
{ 
        
	PY_Delay_us_t(us_num) ;
	SDA_OUT_H;
	SCL_OUT_H;
	PY_Delay_us_t(us_num/2) ;
	SDA_OUT_L;
	PY_Delay_us_t(us_num/2) ;
	SCL_OUT_L;
}

void I2C_Stop(void)
{ 
        
	SCL_OUT_L;
	PY_Delay_us_t(us_num) ;
	SDA_OUT_L;
	PY_Delay_us_t(us_num) ;
	SCL_OUT_H;
	PY_Delay_us_t(us_num) ;
	SDA_OUT_H;
	PY_Delay_us_t(us_num) ;
}

void I2C_Write_Ack(void)
{ 
        

    PY_Delay_us_t(us_num/2) ;
	SDA_OUT_L;
	PY_Delay_us_t(us_num/2) ;
	SCL_OUT_H;
	PY_Delay_us_t(us_num) ;
	SCL_OUT_L;
	SDA_OUT_H;

}

uint8_t I2C_Read_Ack(void)
{ 
        
	uint8_t status=0;

	SCL_OUT_L;
	PY_Delay_us_t(us_num/2) ;
	SDA_OUT_H;
	PY_Delay_us_t(us_num/2) ;
	status = SDA_IN;
	SCL_OUT_H;
	PY_Delay_us_t(us_num) ;
	SCL_OUT_L;
	SDA_OUT_L;

	return status;

}


void I2C_Send_Byte(uint8_t txd){ 
        


    for(uint8_t i=0;i<8;i++)
    { 
        
    	PY_Delay_us_t(us_num/2) ;
        if((txd&0x80)>>7) SDA_OUT_H;
        else SDA_OUT_L;
        txd<<=1;
        PY_Delay_us_t(us_num/2) ;
        SCL_OUT_H;
        PY_Delay_us_t(us_num) ;
		SCL_OUT_L;
    }

    SDA_OUT_L;
}

uint8_t I2C_Read_Byte(unsigned char rdack)
{ 
        
	uint8_t rxd=0;


    for(uint8_t i=0;i<8;i++ )
	{ 
        
    	SCL_OUT_L;
    	PY_Delay_us_t(us_num/2) ;
    	SDA_OUT_H;
    	PY_Delay_us_t(us_num/2) ;
    	SCL_OUT_H;
        rxd<<=1;
        if(SDA_IN) rxd++;
        PY_Delay_us_t(us_num) ;
    }

    SCL_OUT_L;
    SDA_OUT_H;

    if (rdack) I2C_Write_Ack();

    return rxd;
}

uint8_t PY_CRC_MLX90615_READ(uint8_t daddr, uint8_t Raddr, uint8_t dl, uint8_t dh)
{ 
           //Written by Pegasus Yu 2022/02/22

	uint64_t cdata = 0; //Computed total data
	uint16_t data_t = 0; //Process data of CRC computing
	uint16_t crc_poly = 0x0107; //X^8+X^2+X^1+1 total 9 effective bits. Computed total data shall be compensated 8-bit '0' before CRC computing from 9-1=8.

	uint16_t index_t = 47;  ///bit shifting index for initial '1' searching
	uint16_t index = 47;    //bit shifting index for CRC computing

	uint8_t rec = 0; //bit number needed to be compensated for next CRC computing

	cdata |= (((uint64_t)daddr)<<40);       //device write address
	cdata |= (((uint64_t)Raddr)<<32);       //register access address
	cdata |= (((uint64_t)(daddr+1))<<24);   //device read address
	cdata |= (((uint64_t)dl)<<16);          //data LSB
	cdata |= (((uint64_t)dh)<<8);           //data HSB
	//8-bit '0' compensated into cdata so cdata involves 48 bits stored in 64-bit format.

	while(index_t>0)
	{ 
        
		if( (cdata>>index_t)&1 )
		{ 
        
			index = index_t;
			index_t = 0;

			data_t |= (cdata>>(index-8));
			{ 
        
				data_t = data_t ^ crc_poly;
			}

            while(index!=0xffff)
            { 
        
    			if ((data_t>>7)&1) rec = 1;
    			else if ((data_t>>6)&1) rec = 2;
    			else if ((data_t>>5)&1) rec = 3;
    			else if ((data_t>>4)&1) rec = 4;
    			else if ((data_t>>3)&1) rec = 5;
    			else if ((data_t>>2)&1) rec = 6;
    			else if ((data_t>>1)&1) rec = 7;
    			else if ((data_t>>0)&1) rec = 8;
    			else rec = 9; ///

    			if((index-8)<rec)
    			{ 
        
    				data_t = data_t<<(index-8);
    				index = 0xffff;
    			}
    			else
    			{ 
        
        			for(uint8_t i=1;i<=rec;i++)
        			{ 
        
        				data_t = (data_t<<1)|((cdata>>(index-8-i))&1) ;
        			}

        			if(rec!= 9)
        			{ 
        
        				data_t = data_t ^ crc_poly;
        				index -= rec;
        			}
        			else
        			{ 
        
        				data_t = 0;
        				index_t = index-8-1;
        				index = 0xffff;

        			}

    			}


            }

		}
		else
		{ 
        
			index_t--;
			if(index_t<8) break;
		}
	}
	return (uint8_t)data_t;
}

uint32_t Get_Temp_DATA( uint8_t ReaAd)
{ 
        
     uint8_t Pecreg = 0;
	 uint8_t DataL = 0 ,DataH = 0;
	 uint32_t Result = 0;


	 uint8_t daddr = 0x00; //0x00 or 0xB6(0x5B<<1) for MLX90615 default device address

	  I2C_Start();
	  I2C_Send_Byte(daddr);
	  I2C_Read_Ack();
	  //I2C_Write_Ack();
  	  I2C_Send_Byte(ReaAd);
  	  //I2C_Write_Ack();
  	  I2C_Read_Ack();

  	  /* Don't add this erroneous clock timing, placed here just for reminder, from spec. PY_Delay_us_t(us_num) ; SCL_OUT_H; PY_Delay_us_t(us_num) ; SCL_OUT_L; */

	  I2C_Start();
	  I2C_Send_Byte(daddr+1);
	  I2C_Read_Ack();

  	  DataL=I2C_Read_Byte(1);
  	  DataH=I2C_Read_Byte(1);

  	  Pecreg=I2C_Read_Byte(1);

  	  I2C_Stop();

  	  Result |= (((uint32_t)DataH)<<24);
  	  Result |= (((uint32_t)DataL)<<16);
  	  Result |= (((uint32_t)Pecreg)<<8);
  	  Result |= PY_CRC_MLX90615_READ(daddr, ReaAd, DataL, DataH);
	  return Result;
}

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
 UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */ 

相关文章