- 사용 보드 : nRF52840-PDK
- 사용 IDE : SEGGER Embedded Studio for ARM (노르딕 제품 사용하는 경우 무료 라이센스 이용 가능)
- 작동 전압 : 2.6~3.6V (MLX90614Bxx, MLX90614Dxx 모델 기준, MLX90614Axx은 4.5~5.5V)
- (사용 모듈의 경우, Low Dropout Voltage 레귤레이터 탑재로 3~5V에서 작동)
- 로직 레벨 : Input High (Min : (VDD-0.1)V), Input Low (Max : 0.6V) (MLX90614Bxx, Dxx 기준)
- 통신 방식 : PWM, SMBus Compatible 2-Wire Interface (10~100KHz)
- Slave Address : 0x5A
- SDK 내 예제 프로젝트를 기반으로 작성 (\examples\peripheral\twi_master_using_nrf_twi_mngr)
- (SDK 다운로드 : https://developer.nordicsemi.com/)
[센서]
- Power On Reset 이후, 0.25초 지난 후부터 유효 출력 (RAM내 데이터)
- 주변 온도 -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 사이의 방사율로 조정할 수 있다
[작동 방식]
[사전 설정]
- TWI 통신에 사용할 TWI Sensor module 라이브러리를 이용하기 위해 필요
- nrf_twi_sensor.c 추가 (SDK 폴더 내 \components\libraries\twi_sensor 에 위치)
- 프로젝트에 twi_sensor 라이브러리 폴더 추가
- sdk_config.h 에 TWI Sensor 활성화 부분 추가 및 1로 설정 (nRF_Drivers와 nRF_Libraries 사이에 추가)
(<h>nRF_Drivers_External ~ </h> 까지)
// <e> UART1_ENABLED - Enable UART1 instance
//==========================================================
#ifndef UART1_ENABLED
#define UART1_ENABLED 0
#endif
// </e>
// </e>
// </h>
//==========================================================
// <h> nRF_Drivers_External
//==========================================================
// <q> NRF_TWI_SENSOR_ENABLED - nrf_twi_sensor - nRF TWI Sensor module
#ifndef NRF_TWI_SENSOR_ENABLED
#define NRF_TWI_SENSOR_ENABLED 1 //0 비활성화
#endif
// </h>
//==========================================================
// <h> nRF_Libraries
//==========================================================
// <e> APP_SCHEDULER_ENABLED - app_scheduler - Events scheduler
//==========================================================
#ifndef APP_SCHEDULER_ENABLED
#define APP_SCHEDULER_ENABLED 1
#endif
[코드]
- SDK 폴더 내 components\drivers_ext 에 위치한 CCS811, LPS22HB 드라이버 등을 바탕으로 작성됨
- mlx90614.c, mlx90614.h, mlx90614_internal.h 하나의 소스 파일, 두 개의 헤더 파일로 구성
- 매크로 선언
/*
main.c
*/
//MLX90614와의 통신에 사용할 TWI 인스턴스 ID
#define TWI_INSTANCE_ID_1 1
//TWI trasaction manager 인스턴스 Queue 최대 크기 (대기 전송 갯수)
#define MAX_PENDING_TRANSACTIONS 255
/*
- TWI transaction manager 인스턴스 정의 매크로
생성할 TWI transaction manager 인스턴스 이름, 트랜잭션 Queue 크기, 사용할 TWI 인스턴스
*/
NRF_TWI_MNGR_DEF(m_nrf_twi_mngr_1, MAX_PENDING_TRANSACTIONS, TWI_INSTANCE_ID_1);
/*
- 공통 TWI 센서 인스턴스 생성 매크로
공통 센서 인스턴스 이름, 사용할 TWI transaction manager 인스턴스, 버퍼 크기
(버퍼 크기의 경우 TWI manager Queue 크기보다 작거나 같아야 한다)
*/
NRF_TWI_SENSOR_DEF(sensor_instance_1, &m_nrf_twi_mngr_1, MAX_PENDING_TRANSACTIONS);
/*
- 센서 인스턴스 생성 매크로
센서 인스턴스 이름, 사용할 공통 TWI 센서 인스턴스, 센서 주소
*/
MLX90614_INSTANCE_DEF(mlx90614_instance, &sensor_instance_1, MLX90614_ADDR);
/*
mlx90614.h
*/
//MLX90614xCx 모델 사용하는 경우 주석 해제
//#define MLX90614xCx
//CRC-8 계산에 이용
#define MLX90614_Write 0
#define MLX90614_Read 1
//센서 주소
#define MLX90614_ADDR 0x5A
//통신에 사용할 TWI manager instance 에 요구하는 최소 Queue 크기
#define MLX90614_MIN_QUEUE_SIZE 4
//MLX90614 통신에 사용할 SDA, SCL PIN
#define MLX90614_SDA_PIN 28
#define MLX90614_SCL_PIN 29
//데이터 콜백 프로토타입
typedef void (* mlx90614_data_callback_t)(ret_code_t result, mlx90614_reg_data_t * p_raw_data);
//MLX90614 인스턴스 정의 매크로
#define MLX90614_INSTANCE_DEF(_mlx90614_inst_name, _p_twi_sensor, _sensor_address) \
MLX90614_INTERNAL_INSTANCE_DEF(_mlx90614_inst_name, _p_twi_sensor, _sensor_address)
/*
mlx90614_internal.h
*/
//전송할 데이터 바이트 크기 (송수신 동일. LSB+MSB+PEC)
#define MLX90614_REGISTER_BYTES 3
//MLX90614 RAM 및 EEPROM Register 주소
#define MLX90614_REG_AMBIENT_TEMP 0x06 //주변 온도
#define MLX90614_REG_OBJECT_1_TEMP 0x07 //물체 온도 1
#define MLX90614_REG_OBJECT_2_TEMP 0x08 //물체 온도 2
#define MLX90614_REG_EMISSIVITY_1 0x24 //방사율 주소
#define MLX90614_REG_EMISSIVITY_2 0x2F //MLX90614xCx 모델을 사용할 경우에만 이용할 추가 방사율 주소
#define MLX90614_REG_SLEEP 0xFF //슬립 모드 진입에 사용
/*
MLX90614 인스턴스 구조체
p_sensor_data : NRF_TWI_SENSOR_DEF을 통해 생성한 common twi sensor instance
sensor_addr : MLX90614_INSTANCE_DEF를 통해 MLX90614 인스턴스를 생성할 때 입력한 센서 주소
*/
typedef struct
{
nrf_twi_sensor_t * const p_sensor_data;
uint8_t const sensor_addr;
} mlx90614_instance_t;
/*
센서 인스턴스 생성 매크로
*/
#define MLX90614_INTERNAL_INSTANCE_DEF(_mlx90614_inst_name, _p_twi_sensor, _sensor_address)\
static mlx90614_instance_t _mlx90614_inst_name = \
{ \
.p_sensor_data = _p_twi_sensor, \
.sensor_addr = _sensor_address, \
}
- 구조체
/*
mlx90614.h
*/
/*
reg_data : MLX90614로부터 읽어 온 데이터를 저장하거나 MLX90614 EEPROM 레지스터에 입력할 데이터를 저장
RAM, EEPROM 의 각 레지스터는 16bit 크기를 갖는다
pec : MLX90614로부터 읽어 온 데이터가 제대로 전송이 됐는지 확인하기 위한 Packet Error Code
CRC-8 계산 필요
reg_addr : 해당 변수 구조체가 접근할 MLX90614의 레지스터 주소를 저장하는 변수
PEC 계산에도 이용된다 (단순 레지스터 주소 저장용일뿐. 데이터 전송에는 이용되지 않음)
구조체 변수를 통해 레지스터 데이터를 읽거나 쓸 때, reg_data의 LSB -> MSB -> pec 순으로 송수신 된다
MLX90614의 데이터 송수신은 LSB부터 시작되므로 reg_data 의 MSB, LSB 스왑은 필요없다
*/
typedef struct
{
uint16_t reg_data;
uint8_t pec;
uint8_t reg_addr;
} mlx90614_reg_data_t;
- TWI 설정 및 초기화 (main.c)
//MLX90614와의 통신에 사용할 TWI 통신 설정
static void twi_config(void)
{
uint32_t err_code;
//통신에 사용할 핀, 속도, 인터럽트 우선 순위 설정
nrf_drv_twi_config_t const config={
.scl=29,
.sda=28,
.frequency=NRF_DRV_TWI_FREQ_100K,
.interrupt_priority=APP_IRQ_PRIORITY_MID,
};
//앞서 생성한 설정을 바탕으로 TWI transaction manager instance 초기화
err_code=nrf_twi_mngr_init(&m_nrf_twi_mngr_1,&config);
APP_ERROR_CHECK(err_code);
}
- Register Data Read 함수
/*
mlx90614_instance_t : MLX90614_INSTANCE_DEF을 통해 생성한 MLX90614 센서 인스턴스
mlx90614_reg_data_t : 읽어 온 데이터를 저장할 센서 데이터 구조체
mlx90614_data_callback_t : 데이터 수신 완료 후 호출될 콜백 함수
*/
ret_code_t mlx90614_reg_read(mlx90614_instance_t const * p_instance,
mlx90614_reg_data_t * p_reg_data,
mlx90614_data_callback_t user_cb
)
{
ASSERT(p_instance != NULL);
ASSERT(p_reg_data != NULL);
/*
p_instance->p_sensor_data : NRF_TWI_SENSOR_DEF을 통해 설정된 공통 센서 인스턴스
p_instance->sensor_addr : MLX90614 센서 인스턴스에 설정된 MLX90614 센서 주소
p_reg_data->reg_addr : 전달받은 구조체에 저장된 레지스터 주소
해당 레지스터 주소에 접근해 데이터를 읽어온다
nrf_twi_sensor_reg_cb_t : 데이터 수신 완료 후 호출 할 콜백 함수
MLX90614_REGISTER_BYTES : 읽어 올 데이터의 바이트 크기 (uint8_t 기준)
(Data 16bit(LSB(8)+MSB(8)) + PEC(8bit)=24bit)
*/
return nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
p_instance->sensor_addr,
p_reg_data->reg_addr,
(nrf_twi_sensor_reg_cb_t) user_cb,
(uint8_t *) p_reg_data,
MLX90614_REGISTER_BYTES);
}
- MLX90614 RAM, EEPROM의 레지스터 주소에 액세스해 레지스터에 저장된 16bit 데이터를 읽어오는 함수
- nrf_twi_sensor_reg_read 함수는 nrf_twi_mngr_schedule 함수를 이용하므로 수신이 완료될 때까지 대기하는게 아니라 TWI transaction manager instance의 transaction queue에 데이터 수신을 대기시킨다
- transaction queue에 대기해있던 데이터 수신이 완료된 이후에 콜백 함수가 호출된다
- (콜백 함수 호출이 필요없다면 NULL 값을 입력)
- 온도 데이터, 방사율 데이터 변환 함수
//읽어 온 16bit 방사율 데이터를 데이터 시트에 나와있는 식을 통해 실제 방사율값으로 변환
inline float mlx90614_emissivity_conversion(uint16_t emissivity_val)
{
return (emissivity_val+1)/65536.0;
}
//읽어 온 16bit 온도 데이터를 데이터 시트에 나와있는 식을 통해 실제 온도값으로 변환 (섭씨 기준)
inline double mlx90614_temp_conversion(uint16_t temperature_val)
{
return (temperature_val*0.02)-273.15;
}
- 콜백 함수에서 사용
- CRC-8 테이블 및 계산 함수
/*
속도면에서 필요할 때마다 CRC 값을 계산하는 방식보다 계산된 값이 저장된 테이블을 통해
CRC 계산값을 찾는 것이 더 유리해 테이블 사용이 권장되므로 테이블 사용
*/
static const uint8_t crc8_table[256]=
{
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
/*
mlx90614_reg_data_t : 전달받은 구조체의 데이터값을 기반으로 PEC 계산
write_read : 읽어 온 데이터의 PEC 계산을 할 것인지
MLX90614에 입력할 PEC값을 계산할 것인지를 결정
*/
int mlx90614_crc_cal(mlx90614_reg_data_t * p_raw_data, uint8_t write_read)
{
//CRC-8 계산 결과값을 저장할 변수
uint8_t sum_val=0;
/*
읽어 온 데이터에 대한 CRC-8 계산을 순차적으로 실행하면서 sum_val 변수에 저장
*/
if(write_read==MLX90614_Read)
{
uint8_t pec_array[5]={(MLX90614_ADDR<<1), p_raw_data->reg_addr, ((MLX90614_ADDR<<1)|1),
LSB_16(p_raw_data->reg_data), MSB_16(p_raw_data->reg_data)};
for(int i=0; i<5; i++)
{
sum_val=crc8_table[(sum_val^pec_array[i])];
}
}
/*
레지스터에 입력할 PEC값을 순차적으로 CRC-8 계산하면서 sum_val 변수에 저장
*/
else if(write_read==MLX90614_Write)
{
uint8_t pec_array[4]={(MLX90614_ADDR<<1), p_raw_data->reg_addr,
LSB_16(p_raw_data->reg_data), MSB_16(p_raw_data->reg_data)};
for(int i=0; i<4; i++)
{
sum_val=crc8_table[(sum_val^pec_array[i])];
}
}
//CRC-8 계산 결과 반환
return sum_val;
}
- nRF5 SDK는 CRC-16, CRC-32 계산 라이브러리만 제공
- CRC-8 테이블은 http://www.sunshine2k.de/coding/javascript/crc/crc_js.html 사이트를 참고해 작성
- MLX90614로부터 읽어 온 PEC는
- (MLX90614 Address<<1+W(0)) + Register Address(Command) + (MLX90614 Address<<1+R(1)) + Register Data (LSB) + Register Data (MSB) 의 CRC-8 계산
- MLX90614에 데이터를 쓸 때 함께 입력할 PEC는
- (MLX90614 Address<<1+W(0)) + Register Address(Command) + Register Data (LSB) + Register Data (MSB) 의 CRC-8 계산
- 방사율 변경 함수
- EEPROM으로부터 데이터를 읽어오거나 데이터를 쓸 경우엔 데이터 시트의 EEPROM 테이블에 나와있는 레지스터 주소에서 0x20 의 값을 더해 준 값을 사용해야 한다
- ex) EEPROM 방사율 주소 = 0x04 번지, 실제 사용은 0x04 | 0x20 = 0x24 (CMD) 이용
- MLX90614xCx 모델의 경우, EEPROM 0x0F 번지의 데이터도 변경해줘야 온도가 제대로 측정되는데 이 데이터는 0x04 번지의 데이터를 계산할 때와는 다르게 기존 0x0F 번지에 저장되어있던 값을 이용해 계산되므로 우선적으로 0x0F에 저장된 값을 따로 기록해 둘 것을 권장 (사용 모듈의 경우, 0x0F = 0x079A 인 상태였음)
- EEPROM에 새로운 데이터를 입력하기 위해선 우선 해당 레지스터 주소에 0x0000 데이터를 입력해 기존 데이터를 지워줘야 한다
*
mlx90614_instance_t : MLX90614_INSTANCE_DEF을 통해 생성한 MLX90614 센서 인스턴스
new_emissivity : EEPROM에 입력할 새 방사율
*/
ret_code_t mlx90614_emissivity_write(mlx90614_instance_t const * p_instance,
float new_emissivity
)
{
ASSERT(p_instance != NULL);
//방사율은 0.1~1.0까지만 설정 가능
if(new_emissivity<0.10 || 1.0<new_emissivity)
{
NRF_LOG_WARNING("Invaild Emissivity Value");
return NRF_ERROR_INVALID_DATA;
}
//TWI transaction manager instance의 Queue 상태를 확인하기 위한 부울 변수
bool idle_chk=false;
//에러 코드 저장
ret_code_t err_code;
//레지스터에 새 방사율 데이터가 제대로 써졌는지 확인용
uint16_t emissivity_chk_data;
/*
새 방사율값을 저장할 구조체
reg_addr : 계산된 새 방사율값을 저장할 EEPROM 레지스터 주소 (0x04 | 0x20 = 0x24)
reg_data : 데이터 시트에 나와있는 식을 통해 계산된 16비트 데이터
pec : 데이터에 이어서 전송될 PEC 데이터
만약 제대로 계산된 PEC값이 전송되지 않는다면 EEPROM은 기존 데이터를 유지한다
*/
mlx90614_reg_data_t new_emissivity_1;
new_emissivity_1.reg_addr=MLX90614_REG_EMISSIVITY_1;
new_emissivity_1.reg_data=(int)(round(65536*(new_emissivity)-1.0));
new_emissivity_1.pec=mlx90614_crc_cal(&new_emissivity_1,MLX90614_Write);
//0x04 번지에 새 방사율 데이터가 제대로 저장이 됐는지 확인하기 위해 계산된 새 방사율 데이터 저장
emissivity_chk_data=new_emissivity_1.reg_data;
/*
EEPROM에 데이터를 쓰기 위해선 우선적으로 해당 레지스터에 0x0000 값을 갖는 16비트 데이터를 입력해야 한다
0x0000 의 값을 전송할 구조체 선언 후 레지스터 주소 입력, PEC 계산값 저장
*/
mlx90614_reg_data_t emissivity_zero;
emissivity_zero.reg_addr=MLX90614_REG_EMISSIVITY_1;
emissivity_zero.pec=mlx90614_crc_cal(&emissivity_zero,MLX90614_Write);
/*
- MLX90614xCx 모델을 사용하는 경우에만 실행되는 부분
현재 EEPROM 0x04 번지에 입력되어있는 방사율 데이터가 필요하다
*/
#ifdef MLX90614xCx
//현재 EEPROM에 저장되어있는 0x04 번지의 방사율 데이터를 저장할 구조체 변수 선언 및 레지스터 주소 입력
mlx90614_reg_data_t old_emissivity_1;
old_emissivity_1.reg_addr=MLX90614_REG_EMISSIVITY_1;
//old_emissivity_1 구조체에 EEPROM 0x04 (CMD 0x24) 번지에 저장된 현재 방사율값 읽어와 저장
err_code=nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_EMISSIVITY_1,
(nrf_twi_sensor_reg_cb_t) NULL,
(uint8_t *) &old_emissivity_1,
MLX90614_REGISTER_BYTES);
//nrf_twi_sensor_reg_read 함수 실행 에러 발생시
if (err_code != NRF_SUCCESS)
{
NRF_LOG_WARNING("MLX90614xCx : Old emissivity 1 read failed.");
return err_code;
}
/*
데이터 송수신에 사용하는 TWI Sensor module 라이브러리는
TWI trasaction manager 라이브러리의 nrf_twi_mngr_schedule 함수를 이용해 데이터를 전송한다
때문에 old_emissivity_1 구조체에 레지스터로부터 읽어 온 데이터가 저장되기까지 대기를 한 후
(=TWI transaction manager queue==idle 상태)
CRC 값을 계산 및 읽어 온 PEC값과 비교한다
*/
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
//수신이 제대로 됐는지 확인을 위해 읽어 온 PEC 값과 CRC-8 계산 결과를 비교
if(old_emissivity_1.pec!=mlx90614_crc_cal(&old_emissivity_1,MLX90614_Read))
{
NRF_LOG_WARNING("MLX90614xCx : Old emissivity 1 CRC doesn't match");
return NRF_ERROR_INVALID_DATA;
}
#endif
//모델 상관없이 공통적으로 진행 시작
//0x0000의 데이터값을 먼저 전송해 방사율 레지스터 주소(0x04)의 데이터를 지운다
err_code=nrf_twi_sensor_reg_write(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_EMISSIVITY_1,
(uint8_t *)&emissivity_zero,
sizeof(emissivity_zero)/sizeof(uint8_t)
);
//nrf_twi_sensor_reg_write 함수 실행 에러 발생시
if (err_code != NRF_SUCCESS)
{
NRF_LOG_WARNING("MLX90614 : Emissivity 1 erase failed");
return err_code;
}
/*
0x0000 데이터 입력 후, 새 방사율 데이터를 전송하기 위해선 최소 5ms, 권장 10ms의
여유 시간이 필요하므로 0x0000 데이터 전송 완료까지 기다린 뒤에 딜레이 함수를 이용해 10ms 대기
*/
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
nrf_delay_ms(10);
//데이터 시트에 나와있는 식을 통해 계산된 새 방사율값 EEPROM 0x04 번지에 전송
err_code=nrf_twi_sensor_reg_write(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_EMISSIVITY_1,
(uint8_t*)&new_emissivity_1,
sizeof(new_emissivity_1)/sizeof(uint8_t)
);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_WARNING("MLX90614 : New emissivity 1 write failed");
return err_code;
}
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
//새 방사율 데이터를 레지스터에 쓴 이후에도 똑같이 최소 5ms, 권장 10ms의 대기가 필요하다
nrf_delay_ms(10);
/*
EEPROM 0x04 번지에 저장된 방사율 데이터를 읽어와 앞서 emissivity_chk_data에 저장한
새 방사율 데이터와 비교해 EEPROM에 새 방사율 데이터가 제대로 입력됐는지 확인
*/
err_code=nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_EMISSIVITY_1,
(nrf_twi_sensor_reg_cb_t) NULL,
(uint8_t *) &new_emissivity_1,
MLX90614_REGISTER_BYTES);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_WARNING("MLX90614 : New emissivity 1 read failed", err_code);
return err_code;
}
/*
TWI transaction manager instance의 queue에 데이터 수신이 대기된 상태로 존재하기 때문에
수신 완료까지(Queue==0) 대기한 후에 데이터 비교를 진행한다
*/
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
//EEPROM 0x04 번지에 새 방사율 데이터가 제대로 입력됐는지 확인
if(new_emissivity_1.reg_data!=emissivity_chk_data)
{
NRF_LOG_INFO("New Emissivity 1 data doesn't match");
return NRF_ERROR_INVALID_DATA;
}
/*
- MLX90614xCx 모델을 사용하는 경우에만 실행되는 부분
MLX90614xCx 모델의 경우, 방사율 데이터를 변경할 때 EEPROM 0x04 번지의 데이터만 바꾸는 것이 아니라
0x0F 번지에 저장되어 있는 값도 데이터 시트에 나와있는 식을 통해 얻은 값으로 변경해야
방사율에 따른 옳바른 온도값이 측정된다
*/
#ifdef MLX90614xCx
/*
앞서 0x04 번지의 데이터를 지우기 위해 선언 및 사용한 0x0000 데이터를 가진 구조체 변수에
0x0F 주소 입력 및 PEC값 재계산해 저장
*/
emissivity_zero.reg_addr=MLX90614_REG_EMISSIVITY_2;
emissivity_zero.pec=mlx90614_crc_cal(&emissivity_zero, MLX90614_Write);
/*
new_emissivity_2 : EEPROM 0x0F 번지에 쓸 새 방사율 데이터를 저장할 구조체 변수
old_emissivity_2 : 현재 EEPROM 0x0F 번지에 저장된 방사율 데이터를 읽어와 저장할 구조체 변수
*/
mlx90614_reg_data_t new_emissivity_2, old_emissivity_2;
//EEPROM 0x0F 번지 주소 입력 (0x0F | 0x20 = 0x2F)
new_emissivity_2.reg_addr=MLX90614_REG_EMISSIVITY_2;
old_emissivity_2.reg_addr=MLX90614_REG_EMISSIVITY_2;
//현재 EEPROM 0x0F 번지에 입력되어있는 방사율 데이터를 읽어와 old_emissivity_2 구조체 변수에 저장
err_code=nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_EMISSIVITY_2,
(nrf_twi_sensor_reg_cb_t) NULL,
(uint8_t *) &old_emissivity_2,
MLX90614_REGISTER_BYTES);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_WARNING("MLX90614xCx : Old emissivity 2 read failed");
return err_code;
}
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
if(old_emissivity_2.pec!=mlx90614_crc_cal(&old_emissivity_2,MLX90614_Read))
{
NRF_LOG_WARNING("Old emissivity 2 CRC doesn't match");
return NRF_ERROR_INVALID_DATA;
}
/*
기존에 저장되어있는 0x04 번지의 방사율 데이터(old_emissivity.reg_data)와 앞서 0x04 번지에 입력한
새로운 방사율 데이터(new_emissivity_1.reg_data) 그리고 0x0F 번지에 저장되어있던 데이터
(old_emissivity_2.reg_data)를 이용해 0x0F 번지에 입력될 새로운 데이터 값을 구한다
*/
//0x0F 번지에 입력할 새로운 데이터값 계산 후 저장
new_emissivity_2.reg_data=
(round(((double)old_emissivity_1.reg_data/new_emissivity_1.reg_data)*old_emissivity_2.reg_data));
//0x0F 번지에 전송될 PEC값을 CRC-8 계산 후 저장
new_emissivity_2.pec=mlx90614_crc_cal(&new_emissivity_2,MLX90614_Write);
//위에서 계산된 0x0F에 입력할 새 방사율 데이터를 데이터 확인용 변수에 저장
emissivity_chk_data=new_emissivity_2.reg_data;
//0x000 데이터를 먼저 전송해 0x0F 번지 데이터 지움
err_code=nrf_twi_sensor_reg_write(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_EMISSIVITY_2,
(uint8_t *)&emissivity_zero,
sizeof(emissivity_zero)/sizeof(uint8_t)
);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_WARNING("MLX90614xCx : Emissivity 2 erase failed", err_code);
return err_code;
}
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
nrf_delay_ms(10);
//0x0F 번지에 새롭게 계산된 방사율 데이터를 전송
err_code=nrf_twi_sensor_reg_write(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_EMISSIVITY_2,
(uint8_t *)&new_emissivity_2,
sizeof(new_emissivity_2)/sizeof(uint8_t)
);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_WARNING("MLX90614xCx : New emissivity 2 write failed");
return err_code;
}
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
nrf_delay_ms(10);
//새로운 방사율 데이터가 제대로 써졌는지 확인하기 위해 0x0F 번지에 저장된 데이터를 읽어 옴
err_code=nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_EMISSIVITY_2,
(nrf_twi_sensor_reg_cb_t) NULL,
(uint8_t *) &new_emissivity_2,
MLX90614_REGISTER_BYTES);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_WARNING("MLX90614xCx : New emissivity 2 read failed");
return err_code;
}
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
//0x0F 번지에 새 방사율 데이터가 제대로 써졌는지 확인
if(new_emissivity_2.reg_data=!emissivity_chk_data)
{
NRF_LOG_INFO("New emissivity 2 data doesn't match");
return NRF_ERROR_INVALID_DATA;
}
#endif
return NRF_SUCCESS;
}
- Sleep 함수
/*
슬립 모드에 진입하기 위해선 Command=0xFF, PEC=0xE8 2바이트를 전송하면 된다
Command는 nrf_twi_sensor_reg_write의 세번째 인자로 쓰인 MLX90614_REG_SLEEP가 사용되므로
실제 전송할 바이트 크기는 PEC 1바이트가 된다
*/
ret_code_t mlx90614_sleep_enter(mlx90614_instance_t const * p_instance)
{
uint8_t sleep_pec=0xE8;
return nrf_twi_sensor_reg_write(p_instance->p_sensor_data,
p_instance->sensor_addr,
MLX90614_REG_SLEEP,
(uint8_t*)&sleep_pec,
sizeof(sleep_pec)/sizeof(uint8_t)
);
}
/*
슬립 모드에서 빠져나오기 위해선 SDA 핀을 33ms를 초과하는 시간동안 LOW로 유지해야 한다
이를 위해 MLX90614 와의 통신에 사용하던 TWI를 해제하고 SDA핀을 LOW 출력 설정으로 바꾼 후
딜레이 함수를 통해 34ms 동안 LOW 상태를 유지함으로 슬립 모드에서 탈출
이후에 다시 twi_config() 함수를 실행해 통신에 사용할 TWI를 재초기화해줘야 한다
하지 않을 경우, 센서와의 통신 불가
*/
void mlx90614_sleep_exit(mlx90614_instance_t const * p_instance, uint8_t sda_pin)
{
bool idle_chk;
//현재 TWI manager instance Queue에 대기해있는 전송이 완료될 때까지 대기
do
{
idle_chk=nrf_twi_mngr_is_idle(p_instance->p_sensor_data->p_twi_mngr);
nrf_delay_us(10);
}while(idle_chk!=true);
//TWI manager instance 해제
nrf_twi_mngr_uninit(p_instance->p_sensor_data->p_twi_mngr);
//SDA 핀 Output 설정 및 LOW 출력으로 설정
nrf_gpio_cfg_output(sda_pin);
nrf_gpio_pin_write(sda_pin,0);
//34ms 동안 SDA=LOW 유지
nrf_delay_ms(34);
}
- Sleep 실행 예시
1. 방사율, Object 1 온도 읽음
2. 슬립 모드 진입
3. 다섯 번의 Object 1 Read 실행
4. 슬립 모드 탈출
5. twi_config() 실행
6. Object 1 Read 시작
'nRF52' 카테고리의 다른 글
nRF52 - FreeRTOS, BLE 사용 TCS34725 제어 (2) | 2020.03.27 |
---|---|
nRF52 - TWI 이용 TCS34725 RGB 색상 검출 센서 제어 (0) | 2020.03.15 |
nRF52 - FreeRTOS, BLE 사용 MLX90614 제어 (0) | 2020.02.26 |
nRF52 - TWI(I2C) 이용 SSD1306 OLED 제어 (1) | 2020.01.13 |
nRF52 - I2S를 이용한 SK6812 RGBW LED 구동 (1) | 2020.01.06 |