MLX90614ESF-BCC 모델을 탑재한 모듈

  • 작동 전압 : 2.6~3.6V
  • 통신 방식 : PWM, I2C(10~100KHz)
  • 주변 온도 -40°C~+125°C, 물체 온도 -70°C~+380°C 까지 측정 가능
  • 측정 정확도 : 0.5°C
  • 측정 해상도 : 0.02°C
  • PWM 출력의 경우, 0.14°C 해상도로 -20~120°C까지 온도 측정 가능
  • Factory calibrated 된 출력을 갖는다
  • 측정값은 FOV 내 모든 물체의 평균 온도값
  • 센서 장착 주변부에 센서를 뜨겁게 or 차갑게 만드는 요소가 있어선 안되고 뜨겁거나 차가운 측정 물체에 너무 가깝게 근접 시켜서도 안된다
  • xCx(ex:MLX90614BCC) 버전의 경우 내부적으로 측정된 열경사도와 측정된 온도를 보상에 사용한다
  • 이 경우 xCx버전은 열경사도에 덜 민감해지지만 영향이 완전히 없어진 상태는 아니다
  • 따라서 열경사도의 원인을 가능한 피하거나 원인으로부터 센서를 보호해주는 것이 중요하다
  • 물체 방사율 1에 대해 교정(Calibration)되어 있지만 재교정없이 0.1~1.0 사이의 방사율로 조정할 수 있다

[작동]

  • 측정된 온도값은 RAM내 지정된 주소에 16비트 16진수 데이터로 저장된다
  • 명령어를 통해 RAM, EEPROM에 저장된 각종 데이터들을 읽거나 필요에 따라 변경할 수 있다
  • 작동 시작 후 첫번째 데이터는 0.25초 뒤에 얻을 수 있다
  • 각 주소는 16bit의 데이터를 갖는다
  • 읽기/쓰기 둘 다 LSByte(16비트 데이터의 하위 8비트), MSByte(16비트 데이터의 상위 8비트) 순으로 데이터 전송이 진행된다

- EEPROM

EEPROM 주소표

  • EEPROM 내에 제한된 주소들만이 변경 가능하지만 전체 주소를 읽는 것은 가능하다
  • 원하는 주소에 값을 쓰기 전에 반드시 해당 주소에 0을 입력해 기존 데이터를 지워준 뒤 원하는 값을 입력해야 한다
  • 0x04 방사율의 경우, 0.1~1.0 까지 조정 가능하고 변경에 따른 재교정은 하지 않아도 된다
  • Factory calibration 을 유지하기 위해 특별한 도구 없이는 다음 비트와 레지스터를 변경해서는 안된다
  • Emissivity [15...0]; Config Register1 [14...11;7;3]; addresses 0x0F and 0x19

- RAM

RAM 주소표

  • 쓰기는 불가능하고 제한된 램 레지스터만 읽는 것이 가능하다
  • 0x06 - 주변 온도, 0x07 - 물체 온도1, 0x08 - 물체 온도2 가 저장된다

- Command

명령어표

  • RAM, EEPROM의 접근을 원하는 경우, x xxxx 부분에 Read/Write를 원하는 주소를 입력한다
  • (ex : RAM Obj1 온도 : 0x07, EEPROM Emissivity(방사율) : 0x24)

- Read

데이터 읽기 예) 램 0x07 주소에 저장된 Object1 온도 측정값을 읽어올 때

  • 센서 주소 : 0x5A (MSB부터 주소가 채워져야 하므로 MSB로 1비트 시프트 한 값이 0xB4)
  • 사용자가 수신할 데이터는 LSByte, MSByte, PEC 총 3 바이트
  • SDA 라인에서 첫번째 바이트는 0xB4(0x5A<<1+W(0))
  • 두번째 바이트는 사용자가 입력한 명령어
  • Repeated Start Condition 이후 세번째 바이트는 센서 주소+Read 비트=0xB5 (0x5A<<1+R(1))
  • 네번째 해당 주소의 LSByte, 다섯번째 해당 주소의 MSByte (LSB가 먼저 오기 때문에 이후에 순서를 바꿔줘야 한다)
  • 마지막 여섯번째 바이트는 0xB4(첫번째 바이트)~MSByte까지 총 5바이트를 CRC-8 계산을 통해 얻은 CRC값

-Write

데이터 쓰기 예) EEPROM 0x02 주소(PWMCTRL)에 데이터를 입력할 때

  • EEPROM에만 데이터 쓰기가 가능하다
  • 원하는 주소에 데이터를 쓰려면 반드시 해당 주소에 0x0000(LSByte(0x00)+MSByte(0x00))을 먼저 입력해 기존 16비트 데이터를 지운 뒤에 원하는 데이터를 입력해야 한다
  • 0x0000을 입력하고 최소 5ms(권장 10ms) 기다린 뒤 원하는 16비트 데이터를 입력하고 다시 5ms(권장 10ms) 기다린 후에 제대로 값이 입력됐는지 Read 명령어 통해 확인
  • 사용자가 전송할 데이터는 Command, LSByte, MSByte, PEC 총 4바이트
  • 첫번째 바이트는 센서 주소 0x5A<<1 + W(0) = 0xB4
  • 두번째 바이트는 명령어 (위의 경우 EEPROM 액세스 001x xxxx + PWMCTRL에 데이터를 쓸 것이므로 0x02 -> 0010 0000 + 0000 0010 = 0010 0010 = 0x22 가 된다)
  • 세번째 바이트는 입력할 16비트 데이터의 하위 8비트
  • 네번째 바이트는 입력할 16비트 데이터의 상위 8비트
  • 다섯번째 바이트는 첫번째 0xB4 바이트부터 네번째 MSByte까지 총 4바이트를 CRC-8 계산을 통해 얻은 CRC 값

[CUBE MX 설정]

  • 센서와의 통신에 사용할 I2C 설정 및 데이터 검증에 CRC-8 사용하므로 CRC 사용 설정

 Standard Mode(100KHz) 사용
인터럽트 활성화
CRC 설정 (8-bit, X2+X1+X0, Init Value=0)

[코드]

- 사용에 필요한 함수들 정의

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <math.h>	//pow 함수 사용
/* USER CODE END Includes */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define mlx90614xCx	//MLX90614xCx 타입을 사용하지 않는 경우 마스크 처리 필요
#define MLX90614_addr 0x5A	// MLX90614 Slave Address
#define mlx_cmd_pointer 1	// 수신, 전송 버퍼내 명령어(CMD)를 입력할 배열 위치
#define mlx_cmd_amb 0x06	// Ta(Ambient Temperature) Address (RAM)
#define mlx_cmd_obj_1 0x07	// Tobj1(Object1 Temperature) Address (RAM)
#define mlx_cmd_obj_2 0x08	// Tobj2(Object1 Temperature) Address (RAM)
#define mlx_cmd_emissivity 0x24	// Emissivity Address (EEPROM)
#define mlx_cmd_emissivity_2 0x2F	// MLX90614xCx 버전에 한해 방사율을 변경했을 때 함께 조정해줘야 하는 레지스터 (EEPROM)
/* USER CODE END PD */

- 전역 변수 선언

/* USER CODE BEGIN PV */
uint8_t mlx_read_buffer[6]={0xB4,0x00,0xB5,0};	//ADDR((0x5A<<1)+W(0)), CMD, ADDR((0x5A<<1)+R(1)), LSByte, MSByte, PEC
uint8_t mlx_write_buffer[5]={0xB4,0};	//ADDR((0x5A<<1)+W(0)), CMD, LSByte, MSByte, PEC
uint8_t mlx_write_zero[5]={0xB4,0};	//ADDR((0x5A<<1)+W(0)), CMD, LSByte, MSByte, PEC
/* USER CODE END PV */
  • Read/Write에 사용할 전역 변수 배열 선언
  • 용이한 PEC 계산을 위해 Read/Write에서 PEC에 사용되는 데이터의 갯수만큼 배열 선언
  • Read의 경우 첫번째, 세번째 배열은 0xB4, 0xB5의 값을 사용하므로 그에 맞춰 배열 선언 및 초기화
  • Write의 경우 첫번째 바이트만 0xB4로 고정되므로 그에 맞춰 배열 선언 및 초기화

- 측정된 온도를 읽어오는 함수

void mlx_read_temperature(void)
{
	double temp_obj_1, temp_obj_2, temp_amb; //물체1, 물체2, 주변 온도를 저장할 변수
	uint8_t crc_obj_1,crc_obj_2,crc_amb;	 //물체1, 물체2, 주변 온도 CRC값 저장할 변수

	/*
	Ambient temperature(센서 주변 온도), RAM-0x06
	*/
	mlx_read_buffer[mlx_cmd_pointer]=mlx_cmd_amb;
	//데이터 버퍼의 CMD 부분에 데이터 수신에 사용할 명령어 입력 (단순 PEC계산용)
    
	HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,mlx_cmd_amb,I2C_MEMADD_SIZE_8BIT,
			(uint8_t *)&mlx_read_buffer[3],3);
	/*
	인터럽트 사용해 데이터 수신, 슬레이브의 특정 메모리 주소로부터 데이터를 읽어오는 함수
	사용할 I2C, 슬레이브 주소(MSB로 1비트 시프트 필요), 접근할 내부 메모리 주소(CMD), 내부 메모리 주소 크기, 
	수신할 데이터 저장할 포인터 주소(mlx_read_buffer[3]), 수신할 데이터 갯수(mlx_read_buffer[3]~[5])
	*/
    
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	//수신 완료(I2C Ready 상태)까지 대기
    
	temp_amb=(mlx_read_buffer[4]<<8|mlx_read_buffer[3]);
	/*
	16비트로 구성된 센서 데이터가 하위 8비트->상위 8비트 순으로 수신되므로 mlx_read_buffer[4]에
	저장된 MSByte를 MSB로 8비트 시프트 한 뒤 LSByte(mlx_read_buffer[3])와 합친다
	*/
    
	temp_amb=(temp_amb*0.02)-273.15;
	//10진수로 변환한 온도 측정값을 섭씨 온도로 변환하는 과정이 필요하다
	//Ta[°C] = (Tareg*0.02)-273.15	//(Ta register*0.02)=켈빈온도
    
	crc_amb=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_read_buffer,5);
	//mlx_read_buffer에 저장된 0xB5, 0x07(CMD), 0xB4, LSByte, MSByte를 CUBE MX를 통해 설정한
	//값들을 기준으로 CRC값을 계산한다
    
	if(crc_amb==mlx_read_buffer[5])
	{
		printf("Ambient Temperature : %.2lf\n",temp_amb);
        //센서로부터 수신한 PEC(Packet Error Code, mlx_read_buffer[5])와 계산한 CRC값이 일치하는 경우 출력
	}
	else
	{
    		//PEC값과 CRC계산값이 일치하지 않는 경우
	}
	
	/*
	Object 1 temperature, RAM-0x07
	*/
	mlx_read_buffer[mlx_cmd_pointer]=mlx_cmd_obj_1;
	HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,mlx_cmd_obj_1,I2C_MEMADD_SIZE_8BIT,
    	(uint8_t *)&mlx_read_buffer[3],3);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	temp_obj_1=(mlx_read_buffer[4]<<8|mlx_read_buffer[3]);
	temp_obj_1=(temp_obj_1*0.02)-273.15;
	crc_obj_1=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_read_buffer,5);

	if(crc_obj_1==mlx_read_buffer[5])
	{
		printf("Obj 1 Temperature : %.2lf\n",temp_obj_1);
	}
	else
	{
    		//PEC값과 CRC계산값이 일치하지 않는 경우
	}

	/*
	Object 2 temperature, RAM-0x07
	*/
	mlx_read_buffer[mlx_cmd_pointer]=mlx_cmd_obj_2;
	HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,mlx_cmd_obj_2,I2C_MEMADD_SIZE_8BIT,
    	(uint8_t *)&mlx_read_buffer[3],3);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	temp_obj_2=(mlx_read_buffer[4]<<8|mlx_read_buffer[3]);
	temp_obj_2=(temp_obj_2*0.02)-273.15;
	crc_obj_2=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_read_buffer,5);

	if(crc_obj_2==mlx_read_buffer[5])
	{
		printf("Obj 2 Temperature : %.2lf\n",temp_obj_2);
	}
	else
	{
    		//PEC값과 CRC계산값이 일치하지 않는 경우
	}
}

 

  • 센서로부터 온도값을 읽어오는 함수
  • 값을 읽어오는데 사용할 데이터 버퍼의 형태는
  • {0xB4, 0x00, 0xB5, 0x00, 0x00, 0x00} //(센서주소<<1+W(0), CMD, 센서주소<<1+R(1), LSByte, MSByte, PEC)
  • 용이한 CRC 계산을 위해 첫번째 0xB4, 세번째 0xB5는 값을 고정한 상태로 두고 두번째 0x00(CMD)는 사용할 명령어에 따라 변경. 그리고 나머지 배열 세칸에 센서로부터 수신한 데이터를 저장한 뒤 [0]~[4]까지 CRC 계산
  • RAM에 저장된 Ambient Temperature, Object1 Temperature, Object2 Temperature 데이터의 수신 및 처리 방식은 명령어(CMD)를 제외하고는 전부 동일

- 방사율(Emissivity) 변경 함수

void mlx_change_emissivity(float new_emissivity)
{
	/*
	방사율(Emissivity) 변경은 EEPROM 내 0x04 번지의 16비트 데이터를 변경하는 것으로 이뤄질 수 있다
	(xCx 모델의 경우 0x0F 번지의 데이터 변경도 필요하다)
	*/
    
	uint8_t crc_emissivity, crc_emissivity_2;	
	/*
	MLX90614xCx 센서의 경우, 방사율을 변경할 때 EEPROM - 0x04(Emissivity)에 더해
	0x0F 번지에 저장된 데이터값도 변경해야 되기 때문에 0x04, 0x0F CRC 값을 저장하는 변수를 두개 생성
	*/
    
	uint16_t new_04, new_0f;//변경할 방사율에 따른 계산값을 0x04, 0x0F 번지에 저장하는데 사용할 변수
	double old_04,old_0f;	//기존에 저장된 0x04, 0x0F 주소의 데이터를 저장할 변수 (xCx 타입에만 필요한 부분)

	printf("New E : %.2lf\r\n",new_emissivity);	//사용자가 입력한 새 방사율값 출력
	new_04=round(65536*(new_emissivity)-1.0);	
	//데이터 시트에 나와있는 공식을 사용해 EEPROM-0x04 번지에 저장할 새 방사율값 계산
	printf("New E (Calculated) : %d, %X\r\n",new_04,new_04);	//계산된 새 방사율값 출력

/*
MLX90614xCx 타입을 사용하는 경우 0x0F 번지의 데이터 변경도 필요하므로 아래의 과정들이 필요하다
xAx, xBx 타입을 사용하는 경우 사용할 필요가 없는 부분
*/
#ifdef mlx90614xCx
	//EEPROM - 0x04 번지에 저장된 현재 방사율값을 읽어온다 (새 방사율값 계산에 필요, xCx 모델만 필요한 부분)
	mlx_read_buffer[mlx_cmd_pointer]=mlx_cmd_emissivity;
	HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,mlx_cmd_emissivity,I2C_MEMADD_SIZE_8BIT,
    	(uint8_t *)&mlx_read_buffer[3],3);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	crc_emissivity=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_read_buffer,5);
	old_04=((mlx_read_buffer[4]<<8)|mlx_read_buffer[3]);
	if(mlx_read_buffer[5]==crc_emissivity)	//CRC 계산값과 수신한 PEC값 비교
	{
		printf("Old 0x04 : %d, %X\r\n",(int)old_04,(int)old_04);	
		//계산된 CRC값과 수신한 PEC값이 일치, 현재 0x04 번지에 저장된 방사율 출력
	}
	else	
	{
    		//계산된 CRC값과 수신한 PEC값이 일치하지 않는 경우
	}

	//EEPROM - 0x0F 번지에 저장된 새로운 방사율값을 계산하는데 필요한 데이터를 읽어온다 (xCx 모델만 필요한 부분)
	mlx_read_buffer[mlx_cmd_pointer]=mlx_cmd_emissivity_2;
	HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,mlx_cmd_emissivity_2,I2C_MEMADD_SIZE_8BIT,
    	(uint8_t *)&mlx_read_buffer[3],3);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	crc_emissivity_2=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_read_buffer,5);
	old_0f=((mlx_read_buffer[4]<<8)|mlx_read_buffer[3]);
	if(mlx_read_buffer[5]==crc_emissivity_2)	//CRC 계산값과 수신한 PEC값 비교
	{
		printf("Old 0x0F : %d, %X\r\n",(int)old_0f,(int)old_0f);
		//계산된 CRC값과 수신한 PEC값이 일치, 현재 0x0F 번지에 저장된 데이터 출력
	}
	else	
	{
    		//계산된 CRC값과 수신한 PEC값이 일치하지 않는 경우
	}
	
	new_0f=round((old_04/new_04)*old_0f);	//xCx 타입만 해당 (xAx, xBx는 이 변수 자체가 필요 없음)
	//제조사에서 제공하는 데이터 시트에 나와있는 식을 기반으로 EEPROM-0x0F 번지에 저장할 값 계산
    printf("New 0x0F : %d, %X\r\n",new_0f,new_0f);
    
#endif

	/*
	EEPROM에 데이터를 쓰기 전에 우선적으로 데이터를 쓸 주소에 0x000 값을 입력해
	저장된 16비트 데이터를 지워야 할 필요가 있다
	mlx_write_zero 배열의 첫번째 바이트만 0xB4 (센서 주소0x5A<<1+W(0), PEC 계산에 사용)로 고정해 두고
	mlx_write_zero[1] - CMD(필요에 따라 변경, PEC 계산뿐만이 아니라 실제 전송에서 CMD로 사용된다)
	mlx_write_zero[2], [3] - LSByte, MSByte (0x0000을 입력해야되므로 둘 다 0x00의 값을 가진다)
	mlx_write_zero[4] - PEC (0xB4, CMD를 CRC 계산한 결과)
	mlx_write_zero[1]~mlx_write_zero[4]까지의 배열이 센서로 전송된다
	*/
	mlx_write_zero[1]=mlx_cmd_emissivity;	//Write CMD, 0x24(0010 0000 + 0x04)
	mlx_write_zero[4]=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_write_zero,4);  //[0]~[3] CRC 계산 결과
    
	/*
	-센서에 전송될 데이터 버퍼 설정, mlx_write_buffer[0] - 0xB4로 고정 (단순 PEC 계산용)
	mlx_write_buffer[1] - CMD 입력 (실제 전송에서 CMD로 사용 및 PEC 계산에 사용됨) 
	mlx_write_buffer[2] - 새로 계산한 방사율의 LSByte 저장
	mlx_write_buffer[3] - 새로 계산한 방사율의 MSByte 저장
	mlx_write_buffer[4] - mlx_write_buffer[0]~[3]까지의 CRC 계산값 저장
	mlx_write_buffer[1]~mlx_write_buffer[4]까지의 배열이 센서로 전송된다
	*/
	mlx_write_buffer[1]=mlx_cmd_emissivity;	//0x24(0010 0000 + 0x04) (EEPROM - 0x04 (Emissivity)
	mlx_write_buffer[2]=new_04&0xff;	//LSByte
	mlx_write_buffer[3]=(new_04>>8)&0xff;	//MSByte
	mlx_write_buffer[4]=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_write_buffer,4);

	printf("New_04 : %d, %X, %X, %X	crc : %X\r\n",new_04,new_04,mlx_write_buffer[2],mlx_write_buffer[3],mlx_write_buffer[4]);
    
	HAL_I2C_Master_Transmit_IT(&hi2c1,MLX90614_addr<<1,(uint8_t *)&mlx_write_zero[1],4);
	//쓰기를 원하는 주소에 먼저 0x0000 16비트 데이터를 써서 현재 저장된 값을 지운다
	//값을 지울 때도 PEC를 같이 전송해야 한다 (CMD, 0x00, 0x00, PEC)
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	//I2C Ready 상태까지 대기
	HAL_Delay(10);
	//딜레이 사용해 쓰기 완료 후 10ms 대기 (최소 5ms, 권장 10ms)
    
	HAL_I2C_Master_Transmit_IT(&hi2c1,MLX90614_addr<<1,(uint8_t *)&mlx_write_buffer[1],4);
	//CMD, 계산된 방사율을 상/하 8비트씩 나눈 값과 계산된 PEC값을 전송
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	HAL_Delay(10);
	//딜레이 사용해 쓰기 완료 후 10ms 대기 (최소 5ms, 권장 10ms)

	/*
	EEPROM - 0x0F 주소에 데이터 전송
	xCx 타입만 해당되는 사항으로 new_0f=round((old_04/new_04)*old_0f); 를 통해 얻은 16비트 데이터를
	0x0F 주소에 전송한다. 전송 순서 및 방법은 0x04에 데이터 쓸 때와 동일
	*/
    
//MLX90614xCx 타입을 사용할 경우, 0x0F 번지에 위에서 계산한 새로운 데이터를 입력
//과정은 위의 0x04 번지에 데이터를 쓸 때와 CMD가 다른 것 빼고는 동일
#ifdef mlx90614xCx
	mlx_write_zero[1]=mlx_cmd_emissivity_2;	//0x2F (001x xxxx + 0x0F)
	mlx_write_zero[4]=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_write_zero,4);
	mlx_write_buffer[1]=mlx_cmd_emissivity_2;	//0x2F (001x xxxx + 0x0F)
	mlx_write_buffer[2]=new_0f&0xff;
	mlx_write_buffer[3]=(new_0f>>8)&0xff;
	mlx_write_buffer[4]=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_write_buffer,4);
    
	HAL_I2C_Master_Transmit_IT(&hi2c1,MLX90614_addr<<1,(uint8_t *)&mlx_write_zero[1],4);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	HAL_Delay(10);

	HAL_I2C_Master_Transmit_IT(&hi2c1,MLX90614_addr<<1,(uint8_t *)&mlx_write_buffer[1],4);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	HAL_Delay(10);
#endif

	//EEPROM-0x04 번지에 값이 변경됐는지 확인
	mlx_read_buffer[mlx_cmd_pointer]=mlx_cmd_emissivity;
	HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,mlx_cmd_emissivity,I2C_MEMADD_SIZE_8BIT,
    	(uint8_t *)&mlx_read_buffer[3],3);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	crc_emissivity=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_read_buffer,5);
	if((mlx_read_buffer[3]==(new_04&0xff))&&(mlx_read_buffer[4]==((new_04>>8)&0xff)))
	{
		printf("0x04 : Successfully Changed\r\n");	
		//0x04번지에 입력한 새 방사율 데이터와 읽어 온 0x04번지의 방사율 데이터가 동일할 때
	}
	else
	{	
		//0x04번지에 저장된 방사율이 사용자가 입력한 방사율과 다를 때
	}

#ifdef mlx90614xCx
	//EEPROM-0x0F 번지에 값이 변경됐는지 확인
	mlx_read_buffer[mlx_cmd_pointer]=mlx_cmd_emissivity_2;
	HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,mlx_cmd_emissivity_2,I2C_MEMADD_SIZE_8BIT,
    	(uint8_t *)&mlx_read_buffer[3],3);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	crc_emissivity=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_read_buffer,5);
	if((mlx_read_buffer[3]==(new_0f&0xff))&&(mlx_read_buffer[4]==((new_0f>>8)&0xff)))
	{
		printf("0x0F : Successfully Changed\r\n");
		//0x0F번지에 입력한 새 방사율 데이터와 읽어 온 0x0F번지의 방사율 데이터가 동일할 때
	}
	else
	{
		//0x0F 번지에 저장된 데이터가 사용자가 입력한 데이터와 다른 경우
	}
#endif
}
  • Emissivity(방사율) 변경에 사용할 함수
  • 0.1~1.0까지 변경 가능하고 방사율 변경에 따른 온도 측정값 교정은 필요하지 않다
  • EEPROM에 데이터를 쓰기 전에 반드시 해당 주소에 0x0000을 전송해 데이터를 지운 뒤 새롭게 입력할 데이터를 전송해야 한다
  • MLX90614xAx, xBx 타입은 EEPROM - 0x04(Emissivity) 번지의 값만을 주어진 계산식을 통해 얻은 새 방사율값으로 변경하면 되지만 xCx 타입은 0x04 및 0x0F 번지에 현재 저장되어있는 데이터를 기반으로 하는 공식(0x04와는 다른)을 활용해 값을 산출한 뒤 이를 0x0F 번지에 추가로 전송해줘야 한다
  • xCx 타입을 사용할 경우, 혹시 모를 상황을 대비해 EEPROM-0x0F 번지에 저장되어있는 데이터를 따로 기록해 두는 것을 권장 (0x0F 번지에 저장되는 값은 0x04, 0x0F 번지의 데이터를 기반으로 계산된 값이라 한번 잘못 저장하면 돌이킬 수 없다)
  • EEPROM에 데이터를 쓴 직후와 그 다음 쓰기 or 읽기 사이에 적어도 5ms(10ms-안전)의 대기 시간이 필요하다
  • Read 함수와는 달리 센서 내 특정 메모리에 접근해서 데이터를 쓰는 방식이 아니기 때문에 CMD도 전송 데이터 버퍼 안에 포함되어 있어야 한다. (CMD, LSByte, MSByte, PEC 순으로 총 4개의 배열을 전송)
  • 센서로부터 데이터를 읽어 올 때와 동일하게 LSByte->MSByte 순으로 전송이 진행된다
  • 쉬운 CRC 계산을 위해 첫번째 배열([0],0x5A<<1)을 고정값으로 둔 채 두번째 배열([1])부터 마지막 배열까지만을 센서에 데이터를 쓰는데 사용
  • 방사율을 변경한 이후, 전원을 껏다 켜서 변경한 방사율값이 유지되는지 확인한다
  • (방사율을 변경하고 전원을 재인가 하지 않았을 때 온도 측정이 부정확해지는 현상 겪음, 재인가 후 온도 측정 정상 및 변경된 방사율 유지 확인)

- 현재 센서에 설정된 방사율을 읽어오는 함수

void mlx_read_emissivity()
{
	uint8_t crc_emissivity;		//CRC 계산값 저장에 사용
	uint16_t read_emissivity;	//EEPROM-0x04 번지로부터 읽어 방사율 데이터를 저장할 변수
	float cur_emissivity;		//읽어 온 방사율 데이터를 실제 방사율로 변환한 값을 저장할 변수
    
	mlx_read_buffer[mlx_cmd_pointer]=mlx_cmd_emissivity;	//EEPROM-0x04
	HAL_I2C_Mem_Read_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,mlx_cmd_emissivity,I2C_MEMADD_SIZE_8BIT,
    	(uint8_t *)&mlx_read_buffer[3],3);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
	crc_emissivity=HAL_CRC_Calculate(&hcrc,(uint32_t *)mlx_read_buffer,5);	//CRC 계산
	
	if(crc_emissivity==mlx_read_buffer[5])	//계산한 CRC값과 읽어 온 PEC 값이 일치할 때
	{
		read_emissivity=(mlx_read_buffer[4]<<8|mlx_read_buffer[3]);	
		//8비트씩 나누어진 방사율 데이터를 비트 시프트와 OR을 사용해 하나로 합침
        
		cur_emissivity=(read_emissivity+1)/65536.0;	
		//식을 통해 실제 방사율 값으로 변환
        
		printf("Current Emissivity : %.2f\r\n",cur_emissivity);	
	}
	else
	{
    		//읽어 온 PEC값과 계산한 CRC값이 일치하지 않을 때
	}
}

- 슬립 모드

슬립모드 예시

  • 슬립 모드에 진입하기 위해선 0xFF 명령어와 0xB4+0xFF를 CRC 계산한 결과값인 0xE8만 센서에 전송하면 된다
  • 전류 소비를 2.5μA로 제한하기 위해서 슬립 상태를 유지하는 동안 SCL핀을 Low로 유지하는 것을 권장. SCL 핀에 연결된 내부 통합 제너 다이오를 통한 누설 전류가 있기 때문. SCL 핀을 Push-Pull로 설정하고 SCL 핀에 Pull-Up 레지스터가 연결되어있지 않은 상태여야 한다.
  • 슬립 모드를 종료하기 위해선 SCL이 High인 상태에서 SDA를 최소 33ms 동안 Low로 유지하거나
  • or POR (Power on Reset)
void mlx_sleep()
{
	uint8_t enter_sleep[2]={0xFF,0xE8};	//0xFF-Sleep CMD, 0xE8-PEC (0xB4+0xFF)
	HAL_I2C_Master_Transmit_IT(&hi2c1,(uint16_t)MLX90614_addr<<1,(uint8_t *)&enter_sleep,2);
	while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY)
	{
	}
}

void mlx_wakeup()
{
	/*
	MLX90614와의 통신에 사용하는 I2C를 해제한 뒤, SDA로 사용하던 GPIO 핀을 새로운 설정으로 초기화
	SDA핀이 Low를 출력하도록 설정한 뒤 딜레이 함수를 이용해 33ms를 대기함으로써 슬립 모드에서 벗어난다
	이후 SDA핀에 대한 GPIO설정을 해제한 뒤 MLX90614를 사용하기 위해 I2C를 다시 초기화한다
	*/
	HAL_I2C_DeInit(&hi2c1);	//MLX90614와의 통신에 사용하는 I2C 해제
    
	/*
	SCL로 사용되던 GPIO 핀을 새롭게 설정
	*/
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);	//통신에 사용하던 I2C SDA 핀과 핀이 속한 포트
	GPIO_InitStruct.Pin = GPIO_PIN_9;	//SDA로 사용하던 핀 번호
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;	//Output Mode
	GPIO_InitStruct.Pull = GPIO_NOPULL;	//No Pull-Up
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);	//GPIO 초기화 시작
	HAL_Delay(33);	//33ms 대기 (SDA핀은 0인 상태)
	HAL_GPIO_DeInit(GPIOB,GPIO_PIN_9);	//GPIO 설정 해제
    
	HAL_I2C_Init(&hi2c1);	//MLX90614 통신에 사용하던 I2C 초기화 시작
	HAL_Delay(250);	
	//센서가 정상 작동을 시작하고 0.25초 후에 유효한 첫 데이터를 얻을 수 있으므로 250ms 동안 대기
}
  • I2C 통신을 통해 슬립 모드를 벗어나는 방법은 찾지 못했기 때문에 I2C 설정을 해제하는 방법으로 진행
  • MLX90614와의 통신에 사용하던 I2C를 해제한 뒤 I2C SDA핀으로 사용되던 GPIO핀을 LOW 출력 설정
  • (SCL핀의 경우 High 상태를 유지하는 것을 확인했으므로 SDA핀만 재설정)
  • 딜레이 함수를 통해 슬립 모드에서 벗어나기 위한 최소 시간인 33ms 동안 대기 (SDA핀 LOW 상태)
  • 이후 GPIO핀 설정을 해제하고 센서와의 통신을 위해 I2C를 다시 초기화한다

mlx_sleep() 함수 실행 이후 바로 wakeup 함수 호출한 상황
wake up 이후 0.25초 대기
대기 후 온도 mlx_read_temperature() 함수 시작

- 예시

void main(void)
{
	...
	/* USER CODE BEGIN 2 */
	HAL_Delay(1000);
	mlx_read_emissivity();	//EEPROM-0x04 번지에 현재 설정되어있는 방사율 읽어옴
	mlx_change_emissivity(1.0);	//방사율 1.0으로 변경
	printf("\n");
  	mlx_read_emissivity();	//현재 설정되어있는 방사율 읽기 (설정 변경 확인용)
	mlx_change_emissivity(0.9);	//방사율 0.9로 변경
	printf("\n");
	mlx_read_emissivity();	//현재 설정되어있는 방사율 읽기 (설정 변경 확인용)
	mlx_sleep();	//슬립 모드 진입
	printf("Sleep\n");
	mlx_wakeup();	//슬립 모드에서 빠져나옴
	printf("Wake up\n");
	mlx_read_emissivity();	//현재 설정되어있는 방사율 읽기 (설정 변경 확인용)
  	/* USER CODE END 2 */

  	/* Infinite loop */
  	/* USER CODE BEGIN WHILE */
  	while (1)
  	{
  	/* USER CODE END WHILE */

  	/* USER CODE BEGIN 3 */

  		mlx_read_temperature();
  		HAL_Delay(1000);
  	}
  	/* USER CODE END 3 */
}
  • 방사율 1.0 설정 후, 0.9로 재변경

출력 결과

- xCx 타입의 경우

  • 딜레이가 없는 경우, 디버그가 실행 직후부터 main() 함수가 재실행되어 mlx_change_emissivity() 함수가 호출 및 실행되다가 디버그 창이 뜨면서 도중에 중단되는 상황이 발생
  • 이로인해, EEPROM 내에 데이터를 쓰던 중에 통신이 중단되면서 내부 데이터가 기존 데이터+새로운 데이터가 혼재된 상태가 되었다
  • EEPROM-0x04 번지의 방사율은 고정된 값과 주어진 방사율을 기반으로 계산된 값이 들어가기 때문에 이에따른 문제가 없었지만 0x0F 번지의 데이터는 기존에 저장된 0x04, 0x0F 번지의 데이터를 기반으로 새로운 값이 만들어지기에 비정상적인 값이 저장되는 현상이 발생했다
  • 때문에 딜레이 함수를 사용해 mlx_change_emissivity() 함수가 실행되는 시기를 늦춤으로써 디버그 준비 직후부터 실행 직전까지의 mlx_change_emissivity() 함수 호출이 안되게끔 했다

 

+ Recent posts