/** 20/05/10 - <TCS34725 레지스터 Read 콜백 함수> Timing, Wait Time 설명 추가
*
** 20/06/09
* 1. tcs34725_read_threshold() 함수 수정
* 2. tcs34725_read_all_config() 함수 삭제
* 3. timer_handler() 함수내 tcs34725_read_rgbc() 함수에서 사용되는 구조체 변수를 전역 변수에서
* malloc() 함수를 사용한 메모리 할당 방식으로 변경
*/
- 사용 보드 : nRF52840-PDK
- 사용 IDE : SEGGER Embedded Studio for ARM (노르딕 제품 사용하는 경우 무료 라이센스 이용 가능)
- 작동 전압 : 2.7~3.6V (사용 모듈의 경우, 3.3~5.0V)
- 검출 색상 : Red, Green, Blue, Clear (센서에 감지되는 빛의 세기)
- 통신 : TWI (I2C), 최대 속도 400kbps,
SCL, SDA - Input Low Voltage 최대 0.3 VDD, Input High Voltage 최소 0.7 VDD
- SDK 내 예제 프로젝트를 기반으로 작성
(TWI 통신, APP TIMER : examples\peripheral\twi_master_using_nrf_twi_mngr)
(GPIO 인터럽트 : examples\peripheral\pin_change_int)
(APP BUTTON : examples\ble_peripheral\ble_app_blinky)
(SDK 다운로드 : https://developer.nordicsemi.com/)
- 제조사 제품 페이지 : https://ams.com/ko/tcs34725 (데이터시트 다운 가능)
[작동 방식]
<기본 작동>
- 전원 인가시, 내부 power-on-reset이 디바이스를 초기화하고 low-power Sleep 상태로 만듦
- I2C 버스에서 스타트 컨디션이 감지되면 디바이스는 아이들 상태로 전환되어 Enable 레지스터(0x00)의 PON 비트(Power ON)를 체크함
- PON 비트가 비활성화 되어 있다면 전력 감소를 위해 슬립 상태로 전환
- 활성화되어 있다면 RGBC 기능(AEN 비트)이 활성화 되기 전까지 아이들 상태를 유지한다
- RGBC 기능이 활성화되면 디바이스는 대기 및 RGBC 감지를 순서대로 실행한다
- (대기 시간(Wait Time)이 길어질 수록 색상 감지 간격이 길어지므로 빠른 색상 감지를 위해서는 낮은 대기 시간을 설정할 필요가 있다)
- 완료되면 아이들 상태로 돌아오고 PON, AEN 비트가 활성화 되어있는 한 디바이스는 자동으로 Wait-RGBC 주기를 반복한다
<RGBC 작동>
- RGBC 엔진은 RGBC 게인 컨트롤(AGAIN)과 RGBC 포토 다이오드를 위한 네 개의 통합 ADC를 갖는다
- RGBC 통합 시간(ATIME)은 RGBC의 해상도 및 감도에 영향을 준다
- 네 개 채널의 통합이 동시에 이뤄지며 변환이 완료되면 결과값은 컬러 데이터 레지스터로 전송된다
- 전송 중 인식 불가능한 데이터가 읽히는 것을 막기 위해 더블 버퍼링 된다
- 전송 이후, 디바이스는 설정된 상태 머신에 따라 다음 상태로 자동으로 이동한다
<인터럽트>
- 인터럽트 기능은 사용자가 설정한 범위를 벗어나는 빛의 세기에 대해 센서를 폴링할 필요를 없앰으로써 시스템을 간소화하고 효율을 상승시킨다.
- 인터럽트 기능이 활성화 되어있는 동안 스테이터스 레지스터(0x13)에서 인터럽트 상태를 확인 가능하다
- 인터럽트 아웃풋 상태는 Enable 레지스터(0x00)의 RGBC 인터럽트 활성화(AIEN) 영역을 사용해 활성화 할 수 있다
- 사용자는 두개의 16비트 인터럽트 쓰레스홀드 레지스터를 통해 원하는 빛의 세기의 하한 및 상한 제한을 설정할 수 있다
- 인터럽트는 클리어 데이터(CDATA)가 클리어 인터럽트 로우 쓰레스홀드(AILTx) 보다 낮거나 클리어 인터럽트 하이 쓰레스홀드(AITHx) 보다 클 때 발생된다
- 쓰레스홀드는 로우 쓰레스홀드, 하이 쓰레스홀드순으로 비교되므로 만약, 로우 쓰레스홀드가 하이 쓰레스홀드 보다 높은 쓰레스홀드 값이 설정되있다면 하이 쓰레스홀드는 무시되고 로우 쓰레스홀드만이 비교된다
- 인터럽트 발생 시기를 추가 제어하기 위해 디바이스는 지속성 필터를 제공한다
- 지속성 필터를 통해 사용자는 인터럽트 발생까지의 클리어 쓰레스홀드 범위를 벗어나는 측정값의 연속 발생 횟수를 지정 할 수 있다
- 지속성 필터 레지스터(0x0C)를 통해 사용자는 Clear 지속성 필터(APERS) 값을 지정할 수 있다
- 지속성 필터가 인터럽트를 발생시킨 이후, 스페셜 펑션 인터럽트 클리어 커맨드를 받기 전까지는 인터럽트 핀의 LOW 상태가 유지된다 (인터럽트 핀이 HIGH->LOW 로 전환되며 인터럽트 발생)
<시스템 타이밍>
- 시스템 상태 머신은 상태 개요 및 시스템의 디바이스 컨트롤을 제공하는 상태로의 전환을 제공한다
- 전원 관리 기능(WEN(Wait time Enable))이 활성화 되어있을 때, 상태 머신은 대기 상태로 전환된다
- 대기 시간은 WTIME과 대기 시간을 12배로 연장하는 WLONG에 의해 결정된다
- RGBC(AEN) 기능이 활성화 되어있을 때, 상태 머신은 RGBC 초기화 및 RGBC ADC 상태를 통해 전환된다
- RGBC 초기화 상태는 2.4ms 가 걸리고 RGBC ADC 시간은 통합 시간(ATIME)에 의존한다
- RGBC 주기의 결과로 인터럽트가 발생하면 RGBC ADC 끝에 실행된다
<전원 관리>
- 전력 소비는 대기 상태를 통해 관리 가능
- 대기 상태는 일반적으로 64uA 를 소모
- 최악의 전류 소모는 대기 상태를 사용하지 않는 것
[TWI 통신]
- Write 가능한 모든 레지스터(커맨드 레지스터 제외)는 전원 재인가시 설정값이 초기화된다
- Wait Time 레지스터만 0xFF의 초기값을 갖고 나머지 레지스터는 0x00의 초기값을 갖는다
<커맨드 레지스터 (레지스터 주소 X)>
- 읽거나 쓸 타겟 레지스터 주소를 특정화하는데 사용됨
- 타겟 레지스터 주소 + CMD 명령어가 장치 주소를 제외한 첫번째 바이트에 와야 한다
- 7번 비트 CMD는 특정 레지스터 접근이던 인터럽트 클리어를 위한 목적이던 1이여야 한다
- 5, 6번 비트는 레지스터 주소 접근 방식 및 인터럽트 클리어 명령어를 사용하는데 쓰이는 비트
00일 경우 : 지정한 레지스터만 반복해서 읽거나 쓴다
(예 : 0x00 번지로부터 3바이트 Read를 실행한 경우, 0x00 번지만 세 번 읽는다)
01일 경우 : 지정한 레지스터 주소 사용 이후, 자동으로 레지스터 주소값이 1 증가된다
(예 : 0x00 번지로부터 3바이트 Read를 실행한 경우, 0x00, 0x01, 0x02 레지스터 주소에 저장된 데이터를 읽어 온다)
11일 경우 : 4~0 번 비트에 00110를 함께 설정 및 전송해 인터럽트 클리어를 실행한다
(인터럽트 후 인터럽트 핀은 LOW 상태가 되는데 클리어를 실행하면 다시 HIGH로 전환되어 인터럽트 재실행 가능)
<Enable 레지스터 0x00>
- 주로 TCS34725 전원 ON, OFF 에 사용되는 레지스터
- BIT 4 (AIEN) : 인터럽트 활성화 비트
- BIT 3 (WEN) : 대기 활성화 비트
- BIT 1 (AEN) : RGBC 활성화 비트. 2채널 ADC를 활성화 한다
- BIT 0 (PON) : 타이머와 ADC채널이 작동되도록 내부 오실레이터를 활성화하는 비트
<타이밍 레지스터 0x01>
- RGBC 클리어와 IR 채널 ADC들의 내부 통합 시간을 2.4ms 단위로 조절
- 최대 RGBC 카운트 = (256-ATIME)*1024 (최대 65535)
- 카운트가 늘어날 수록 RGBC 검출 시간이 증가하지만 해상도 및 감도도 증가해 더 정확한 색상 검출이 가능해진다
- 설정값은 1~256을 갖는데 실제 레지스터에 입력되는 값은 256-설정값이 되어야 한다
<대기 시간 레지스터 0x03>
- 대기 시간을 12배로 늘리는 WLONG 비트가 활성화되지 않는한 2.4ms 단위로 대기 시간을 설정한다
- WTIME은 2의 보수로 프로그램된다
- 설정값은 1~256을 갖는데 실제 레지스터에 입력되는 값은 256-설정값이 되어야 한다
<RGBC 인터럽트 쓰레스홀드 레지스터 0x04~0x07>
- 측정되는 빛의 세기 (Clear)의 상한 및 하한을 설정하는 레지스터
- 하한 및 상한은 각각 2바이트로 별도의 값 변환없이 10진수 입력 (0~65535까지 설정 가능)
- 레지스터 주소는 하위 8비트, 상위 8비트 순으로 입력할 16비트 데이터를 8비트씩 나누어 하위 8비트 먼저 전송
- 하한 보다 더 낮은 값, 상한 보다 더 높은 값이 측정됐을 때만 인터럽트가 실행된다
(인터럽트 발생 시기는 지속 레지스터(0x0C)에 설정된 값에 영향을 받는다)
- 인터럽트 핀은 기본적으로 HIGH 상태를 유지하다 인터럽트가 발생하며 LOW로 전환된다
- 인터럽트 핀은 LOW상태를 유지하므로 인터럽트 클리어를 실행하기 전까진 더 이상의 인터럽트가 발생하지 않는다
<지속 레지스터 0x0C>
- 디바이스의 필터링 인터럽트 기능을 제어하는데 사용되는 레지스터
- 각 통합 주기마다 인터럽트를 발생시킬지 아니면 쓰레스홀드 레지스터에 지정된 값을 벗어나는 결과를 지정한 횟수만큼 측정한 이후에 인터럽트를 발생시킬지를 결정
- 예) 쓰레스홀드 Low=10,000 / High=50,000 이고 APERS=1111일 때,
60번 연속으로 0~9,999 / 50,001~65535 범위의 Clear 값이 측정된다면 인터럽트가 발생된다
APERS=0000 이라면 매 RGBC 측정 때마다 인터럽트가 발생한다
<환경설정 레지스터 0x0D>
- Wait long 타임 설정 레지스터
- 2번 비트인 WLONG을 활성화할 경우, 대기 시간이 프로그램된 WTIME 시간의 12배가 된다
<컨트롤 레지스터 0x0F>
- RGBC 측정값 증폭에 사용되는 레지스터
- 측정값은 게인값에 정비례하지 않는다 (1x gain*4 != 4x gain)
<ID 레지스터 0x12>
- 사용하는 센서가 TCS34725인지 아니면 TCS34727인지를 알려주는 레지스터
- 읽기 전용
<스테이터스 레지스터 0x13>
- 디바이스 내부 상태를 제공해주는 레지스터
- BIT 5 (AINT) : 인터럽트 활성화 여부 (1-인터럽트 활성화)
- BIT 0 (AVALID) : RGBC 활성화 여부
<RGBC 채널 데이터 레지스터 0x14~0x1B>
- 측정된 RGBC 데이터를 저장하는 레지스터
- 각 색상은 16비트 데이터로 상하 각 8비트로 나누어 저장된다
- 값을 읽어올 때 추가로 10진수 변환할 필요는 없다
- 연속으로 데이터를 읽어와야 하므로 커맨드 레지스터의 값이 0xA0 (CMD : 1, TYPE : 01 (자동 주소 증가))여야 한다
- 예) 0x14 번지부터 0x1B까지 8바이트를 읽어와야 한다면 슬레이드 주소를 제외한 첫 전송 바이트는 (0x14 | 0xA0)가 되어야 한다
- 하위 데이터 레지스터를 읽을 때, 상위 8비트는 섀도우 레지스터에 저장되어 연속으로 읽어진다
- 따라서 하위 바이트와 상위 바이트를 읽는 사이에 추가적인 ADC 통합 주기가 끝나더라도 옳바른 값을 읽어 온다
[코드]
<매크로>
/*
main.c
*/
//TCS34725 센서 주소
#define TCS34725_ADDR 0x29
//TWI PIN
#define TCS34725_SDA_PIN 28
#define TCS34725_SCL_PIN 29
//TSC34725와의 통신에 사용할 TWI 인스턴스 ID
#define TWI_INSTANCE_ID 0
//TWI trasaction manager 인스턴스 Queue 최대 크기 (대기 전송 갯수)
#define MAX_PENDING_TRANSACTIONS 20
/*
- TWI transaction manager 인스턴스 정의 매크로
생성할 TWI transaction manager 인스턴스 이름, 트랜잭션 Queue 크기, 사용할 TWI 인스턴스
*/
NRF_TWI_MNGR_DEF(m_nrf_twi_mngr, MAX_PENDING_TRANSACTIONS, TWI_INSTANCE_ID);
/*
- 공통 TWI 센서 인스턴스 생성 매크로
공통 센서 인스턴스 이름, 사용할 TWI transaction manager 인스턴스, 버퍼 크기
(버퍼 크기의 경우 TWI manager Queue 크기보다 작거나 같아야 한다)
*/
NRF_TWI_SENSOR_DEF(sensor_instance, &m_nrf_twi_mngr, MAX_PENDING_TRANSACTIONS);
/*
- 센서 인스턴스 생성 매크로
센서 인스턴스 이름, 사용할 공통 TWI 센서 인스턴스, 센서 주소
*/
TCS34725_INSTANCE_DEF(tcs34725_instance, &sensor_instance, TCS34725_ADDR);
<TWI 설정>
static void twi_config(void)
{
uint32_t err_code;
/*
-TCS34725와의 TWI 통신 설정
-SDA, SCL핀, 통신속도, 인터럽트 우선순위 설정 뒤 twi 초기화
*/
nrf_drv_twi_config_t const config={
.scl=TCS34725_SCL_PIN,
.sda=TCS34725_SDA_PIN,
.frequency=NRF_DRV_TWI_FREQ_400K,
.interrupt_priority=APP_IRQ_PRIORITY_MID,
};
err_code=nrf_twi_mngr_init(&m_nrf_twi_mngr,&config);
APP_ERROR_CHECK(err_code);
}
<TCS34725 레지스터 구조체 및 Read 함수>
/*
-TCS34725 레지스터로부터 데이터를 읽어오거나 데이터를 쓸 때 사용할 구조체
reg_data : TCS34752 레지스터로부터 읽어 온 데이터 저장 or 레지스터에 쓸 데이터 저장
reg_addr : 액세스 할 레지스터 주소
*/
typedef struct
{
uint8_t reg_data;
uint8_t reg_addr;
} tcs34725_reg_data_t;
/*
-TCS34725의 특정 레지스터로부터 데이터를 읽어 오는 함수
-TCS34725_INSTANCE_DEF을 통해 생성한 TCS34725 센서 인스턴스
-p_reg_data : 액세스 할 레지스터 주소와 읽어 온 데이터를 저장하는데 사용할 구조체 변수
-user_cb : TCS34725로부터 데이터를 수신한 직후 호출된 콜백 함수
(TWI Manager에 큐를 등록한 직후가 아님)
*/
ret_code_t tcs34725_read_reg(tcs34725_instance_t const * p_instance,
tcs34725_reg_data_t * p_reg_data,
tcs34725_data_callback_t user_cb
)
{
/*
p_instance->p_sensor_data : NRF_TWI_SENSOR_DEF을 통해 설정된 공통 센서 인스턴스
p_instance->sensor_addr : TCS34725 센서 인스턴스에 설정된 TCS34725 센서 주소
p_reg_data->reg_addr|0x80 : 전달받은 구조체에 저장된 레지스터 주소+커맨드 레지스터
해당 레지스터 주소에 접근해 데이터를 읽어온다
user_cb : 데이터 수신 완료 후 호출 할 콜백 함수
p_reg_data : 읽어 온 레지스터 데이터를 저장할 포인터 변수
TCS34725_REGISTER_SIZE : 읽어 올 데이터의 바이트 크기 (레지스터 하나의 크기는 8비트=1바이트)
*/
return nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
p_instance->sensor_addr,
p_reg_data->reg_addr|0x80,
(nrf_twi_sensor_reg_cb_t) user_cb,
(uint8_t *) p_reg_data,
TCS34725_REGISTER_SIZE);
}
- 이 함수를 통해 TCS34725 레지스터로부터 데이터를 읽어 온다
- p_reg_data->reg_addr | 0x80 : 레지스터 주소 + 커맨드 레지스터 (CMD : 1, TYPE : 00 (Repeated byte)=0x80)
- 읽어 올 데이터 크기를 1바이트로 설정했으므로 TYPE : 01 (Auto-increment) 로 설정해도 상관없다
<TCS34725 Write 함수>
ret_code_t tcs34725_write_reg(tcs34725_instance_t const * p_instance,
tcs34725_reg_data_t * p_reg_data)
{
//&p_reg_data->reg_data : 레지스터에 입력할 데이터가 저장된 구조체 변수 주소
return nrf_twi_sensor_reg_write(p_instance->p_sensor_data,
p_instance->sensor_addr,
p_reg_data->reg_addr|0x80,
(uint8_t *)&p_reg_data->reg_data,
TCS34725_REGISTER_SIZE);
}
- 아래에 이어질 레지스터 설정 함수들이 이 함수를 통해 TCS34725 레지스터에 설정값을 전송한다
<TCS34725 초기화 함수>
/*
-TCS34752 초기화 함수
-인에이블 레지스터(0x00)에 설정 전송
-인터럽트 활성화, 대기 시간 활성화, RGBC 활성화, POWER ON/OFF 설정
*/
ret_code_t tcs34725_init(tcs34725_instance_t const * p_instance)
{
ret_code_t err_code;
//레지스터 데이터 구조체 선언 (레지스터 주소 및 인에이블 레지스터에 입력할 데이터 저장)
tcs34725_reg_data_t enable_reg;
enable_reg.reg_addr=TCS34725_REG_ENABLE;
//인에이블 설정 저장할 구조체 선언 및 각종 기능 활성화 설정
tcs34725_config_enable_t init_config;
init_config.rgbc_interrupt=false; //인터럽트 활성화
init_config.wait_enable=true; //대기 시간 활성화
init_config.rgbc_enable=true; //RGBC 측정 활성화
init_config.power_on=true; //Power ON/OFF
//설정값들 해당 위치로 비트 시프트
enable_reg.reg_data=(init_config.rgbc_interrupt << TCS34725_INT_POS)|
(init_config.wait_enable << TCS34725_WAIT_POS)|
(init_config.rgbc_enable << TCS34725_RGBC_ENABLE_POS)|
(init_config.power_on << TCS34725_POWER_ON_POS);
//TCS34725 인에이블 레지스터(0x00)로 데이터 전송
err_code=tcs34725_write_reg(&tcs34725_instance, &enable_reg);
return err_code;
}
<TCS34725 레지스터 Read 콜백 함수>
/*
- 레지스터 Read 콜백 함수
- 구조체 포인터 변수에 저장된 레지스터 주소를 스위치 문을 통해 확인 후 로그 출력
*/
void tcs34725_read_reg_cb(ret_code_t result, tcs34725_reg_data_t * p_raw_data)
{
if(result!=NRF_SUCCESS)
{
NRF_LOG_INFO("TCS34725 register read fail");
return;
}
p_raw_data->reg_addr&=0x1F;
switch(p_raw_data->reg_addr)
{
case TCS34725_REG_ENABLE :
NRF_LOG_INFO("Enable register : %X",p_raw_data->reg_data);
break;
case TCS34725_REG_TIMING :
NRF_LOG_INFO("Timing register : %X",p_raw_data->reg_data);
break;
case TCS34725_REG_WAIT_TIME :
NRF_LOG_INFO("Wait time register : %X",p_raw_data->reg_data);
break;
case TCS34725_REG_PERSISTENCE :
NRF_LOG_INFO("Persistence register : %X",p_raw_data->reg_data);
break;
case TCS34725_REG_CONFIG :
NRF_LOG_INFO("Configuration register : %X",p_raw_data->reg_data);
break;
case TCS34725_REG_CONTROL :
NRF_LOG_INFO("Control register : %X",p_raw_data->reg_data);
break;
case TCS34725_REG_ID :
NRF_LOG_INFO("ID register : %X",p_raw_data->reg_data);
break;
case TCS34725_REG_STATUS :
NRF_LOG_INFO("Status register : %X",p_raw_data->reg_data);
break;
default :
break;
}
//레지스터 데이터 수신에 사용된 구조체 포인터 변수 메모리 할당 해제
free(p_raw_data);
}
- Timing, Wait Time의 경우, 1~256 사이의 설정값을 갖는데 실제 레지스터에 설정값을 입력할 땐 [256-사용자 설정값]이 입력되어야 한다
- 예) Timing값을 1로 설정할 경우, 256-1=255의 값이 실제 레지스터에 입력되는 값이 되어야 한다
- 따라서 콜백 함수에서 Timing, Wait Time 설정값을 확인하려면 [256 - p_raw_data->reg_data = 설정값] 이 되어야 한다
<TCS34725 타이밍 설정 함수>
//타이밍 설정 함수 (2.4ms 단위)
//atime : RGBC 통합 주기
ret_code_t tcs34725_set_timing(tcs34725_instance_t const * p_instance,
uint16_t atime)
{
ret_code_t err_code;
//통합 시간이 최소 1*2.4ms 를 갖는다는 것을 명확히 하기 위해 1~256의 값을 입력해야하도록 설정
if((atime==0)||(256 < atime))
{
err_code=NRF_ERROR_INVALID_DATA;
return err_code;
}
//타이밍 레지스터에 전달될 타이밍 레지스터 데이터 구조체 선언 후 레지스터 주소 및 통합 시간 설정
tcs34725_reg_data_t timing_str;
timing_str.reg_addr=TCS34725_REG_TIMING;
//통합 시간은 2.4ms*(256-ATIME) 으로 결정된다. 즉, 레지스터에 입력되는 실제값이 낮을 수록 통합 시간 ↑
timing_str.reg_data=(256-atime);
//설정한 레지스터 구조체 변수 타이밍 레지스터로 전송
err_code=tcs34725_write_reg(p_instance,&timing_str);
return err_code;
}
- Timing 설정값은 1~256 사이의 값을 갖고 레지스터엔 [256-설정값]이 입력되어야 한다
<TCS34725 대기 시간 설정 함수>
//대기 시간 설정 함수 (2.4ms 단위)
//wait_val : 설정할 대기 시간 (wait_val * 2.4ms 가 대기 시간이 된다)
ret_code_t tcs34725_set_wait_time(tcs34725_instance_t const * p_instance,
uint8_t wait_val)
{
ret_code_t err_code;
//타이밍 설정 함수와 마찬가지로 최소 대기시간이 1*2.4ms임을 명확히 하기 위해 사용
if((wait_val==0)||(256 < wait_val))
{
err_code=NRF_ERROR_INVALID_DATA;
return err_code;
}
//대기 시간 레지스터에 입력될 구조체 변수 선언 후 대기 시간 레지스터 주소 및 데이터 입력
tcs34725_reg_data_t wait_time_str;
wait_time_str.reg_addr=TCS34725_REG_WAIT_TIME
//wait_val*2.4ms가 대기 시간이 된다 (WLONG=1일 경우, *12 추가)
//실제 레지스터 입력값은 (256 - WAIT TIME)이 되어야한다
wait_time_str.reg_data=(256-wait_val);
//대기 시간 레지스터(0x03)에 설정한 대기 시간 전송
err_code=tcs34725_write_reg(p_instance,&wait_time_str);
return err_code;
}
- Wait Time 설정값은 1~256 사이의 값을 갖고 레지스터엔 [256-설정값]이 입력되어야 한다.
<TCS34725 지속 레지스터 설정 함수>
//지속 설정 레지스터 설정 함수
//out_of_range_val : 인터럽트를 발생시키기 위해 필요한 쓰레스홀드 범위를 벗어나는 클리어 값 연속 측정 횟수
ret_code_t tcs34725_set_persistence(tcs34725_instance_t const * p_instance,
tcs34725_persistence_t out_of_range_val)
{
ret_code_t err_code;
//지속 레지스터에 전송할 구조체 변수 선언 후 지속 레지스터 주소 및 데이터 설정
tcs34725_reg_data_t persistence;
persistence.reg_addr=TCS34725_REG_PERSISTENCE;
persistence.reg_data=out_of_range_val;
err_code=tcs34725_write_reg(p_instance, &persistence);
return err_code;
}
<TCS34725 환경설정(Wait long) 레지스터 설정 함수>
//환경 설정 레지스터 설정 함수 (Wait long 외에 설정 가능한 데이터가 없다)
//wait_long_val : WLONG 비트 활성화 여부 결정
ret_code_t tcs34725_set_wait_long(tcs34725_instance_t const * p_instance,
tcs34725_wait_long_t wait_long_val)
{
ret_code_t err_code;
tcs34725_reg_data_t wait_long;
wait_long.reg_addr=TCS34725_REG_CONFIG;
//WLONG 비트는 1번 비트에 위치하므로 비트 시프트 사용
wait_long.reg_data=wait_long_val << TCS34725_WAIT_LONG_POS;
err_code=tcs34725_write_reg(p_instance, &wait_long);
return err_code;
}
<TCS34725 컨트롤(게인) 레지스터 설정 함수>
//컨트롤 레지스터 설정 함수 (게인값 외에 설정 가능한 데이터 없음)
//gain_val : 설정할 게인값. 0~1번 비트를 사용해 게인값을 1,4,16,60x 설정 가능
ret_code_t tcs34725_set_gain(tcs34725_instance_t const * p_instance,
tcs34725_gain_t gain_val)
{
ret_code_t err_code;
tcs34725_reg_data_t gain;
gain.reg_addr=TCS34725_REG_CONTROL;
gain.reg_data=gain_val;
err_code=tcs34725_write_reg(p_instance, &gain);
return err_code;
}
<TCS34725 쓰레스홀드 설정 함수>
/*
- 쓰레스홀드 데이터 구조체
- threshold_data : 전송 or 수신할 16비트 쓰레스홀드 데이터 (하위 8비트 먼저 전송 or 수신됨)
- reg_addr : 쓰레스홀드 레지스터 주소 (하한 : 0x04~0x05, 상한 0x06~0x07)
*/
typedef struct
{
uint16_t threshold_data;
uint8_t reg_addr;
} tcs34725_threshold_data_t;
/*
- 쓰레스홀드 설정 함수
- tcs34725_threshold_lh_t : 설정할 쓰레스홀드값이 쓰레스홀드 하한인지 상한인지 판단하는데 사용
- threshold_val : 설정할 쓰레스홀드 값
*/
ret_code_t tcs34725_set_threshold(tcs34725_instance_t const * p_instance,
tcs34725_threshold_lh_t threshold_low_high,
uint16_t threshold_val)
{
ret_code_t err_code;
//전송할 데이터를 저장할 쓰레스홀드 구조체 변수 선언 및 설정할 쓰레스홀드값 입력
tcs34725_threshold_data_t threshold_str;
threshold_str.threshold_data=threshold_val;
/*
- 설정할 쓰레스홀드값이 상한인지 하한인지에 따라 앞서 선언한 쓰레스홀드 구조체 변수의
레지스터 주소에 해당 레지스터 주소 입력
*/
if(threshold_low_high==TCS34725_THRESHOLD_LOW)
{
threshold_str.reg_addr=TCS34725_REG_THRESHOLD_LOW_L; //쓰레스홀드 Low - Low byte
}
else if(threshold_low_high==TCS34725_THRESHOLD_HIGH)
{
threshold_str.reg_addr=TCS34725_REG_THRESHOLD_HIGH_L; //쓰레스홀드 High - Low byte
}
else
{
err_code=NRF_ERROR_INVALID_ADDR;
return err_code;
}
/*
- threshold_str.reg_addr|0xA0 : 각 쓰레스홀드값은 하위 8비트+상위 8비트=16비트이므로
커맨드 레지스터 0xA0을 사용해 연속된 레지스터 주소에 데이터를 입력해야 한다
- TCS34725_THRESHOLD_BYTES : 각 쓰레스홀드 데이터는 2바이트로 구성되므로 2
*/
err_code=nrf_twi_sensor_reg_write(p_instance->p_sensor_data,
p_instance->sensor_addr,
threshold_str.reg_addr|0xA0,
(uint8_t *)&threshold_str,
TCS34725_THRESHOLD_BYTES);
}
<TCS34725 쓰레스홀드 Read 함수>
/*
- 쓰레스홀드 Read 함수
- thr_data_str : 읽어 온 쓰레스홀드 데이터가 저장될 구조체 포인터 변수
- user_cb : 쓰레스홀드 데이터 수신 완료 후 호출할 콜백 함수
*/
ret_code_t tcs34725_read_threshold(tcs34725_instance_t const * p_instance,
tcs34725_threshold_data_t * thr_data_str,
tcs34725_threshold_callback_t user_cb)
{
ret_code_t err_code;
//thr_data_str.reg_addr|0xA0 : 연이은 레지스터 주소에 접근해 데이터를 읽어야 하므로
//레지스터 주소에 0xA0 OR 연산 추가
err_code=nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
p_instance->sensor_addr,
thr_data_str->reg_addr|0xA0,
(nrf_twi_sensor_reg_cb_t) user_cb,
(uint8_t *) thr_data_str,
TCS34725_THRESHOLD_BYTES);
return err_code;
}
<TCS34725 쓰레스홀드 콜백 함수>
void tcs34725_read_thr_cb(ret_code_t result,
tcs34725_threshold_data_t * p_reg_data)
{
if(result!=NRF_SUCCESS)
{
NRF_LOG_INFO("Reading threshold regiseter is failed");
return;
}
//쓰레스홀드 Low 데이터인지 High 데이터인지 구조체 포인터 변수에 저장된 레지스터 주소로 판단 후 출력
if(p_reg_data->reg_addr==TCS34725_REG_THRESHOLD_LOW_L)
{
NRF_LOG_INFO("Threshold Low value : %d",p_reg_data->threshold_data);
}
else
{
NRF_LOG_INFO("Threshold High value : %d",p_reg_data->threshold_data);
}
//쓰레스홀드 레지스터 데이터 수신에 사용된 구조체 포인터 변수 메모리 할당 해제
free(p_reg_data);
}
<TCS34725 RGBC Read 함수>
/*
- TCS34725 RGBC 측정값을 저장할 구조체
- 각 색상은 하위 8비트 + 상위 8비트 = 총 16비트 (0~65535)
- TWI 통신을 통해 데이터를 읽어 올 때, 각 색상 변수의 하위 8비트->상위 8비트 순으로 데이터가 채워짐
*/
typedef struct
{
uint16_t clear;
uint16_t red;
uint16_t green;
uint16_t blue;
} tcs34725_color_data_t;
/*
- TCS34725 RGBC Read 함수
- tcs34725_color_data_t : TCS34725 RGBC 구조체
- tcs34725_rgbc_callback_t : TCS34725 RGBC 콜백 함수, RGBC 값 수신 완료 후 호출
*/
ret_code_t tcs34725_read_rgbc(tcs34725_instance_t const * p_instance,
tcs34725_color_data_t * rgbc_str,
tcs34725_rgbc_callback_t user_cb)
{
ret_code_t err_code;
/*
- TCS34725_REG_CLEAR | 0xA0 : Clear data low byte(0x14)부터 Blue data high byte(0x1B)까지
연속으로 8바이트를 읽어와야 하므로 0xA0 (CMD :1, TYPE : 01 (Auto-increment)) 사용
- rgbc_str : 읽어온 RGBC 데이터를 저장할 구조체 포인터 변수
- TCS34725_RGBC_BYTES : 읽어 올 데이터는 총 8바이트이므로 8
*/
err_code=nrf_twi_sensor_reg_read(p_instance->p_sensor_data,
p_instance->sensor_addr,
(TCS34725_REG_CLEAR|0xA0),
(nrf_twi_sensor_reg_cb_t) user_cb,
(uint8_t *) rgbc_str,
TCS34725_RGBC_BYTES);
return err_code;
}
<TCS34725 RGBC Read 콜백 및 RGBC 수신 데이터 출력 함수>
//RGBC 콜백 함수, TCS34725로부터 RGBC 데이터 수신 완료 직후 호출됨
void tcs34725_rgbc_callback(ret_code_t result, tcs34725_color_data_t * p_raw_data)
{
if(result!=NRF_SUCCESS)
{
NRF_LOG_INFO("Reading RGBC registers is failed");
return;
}
//RGBC 출력 함수 호출
tcs34725_rgbc_print(p_raw_data);
//RGBC 데이터 수신에 사용된 구조체 포인터 변수 메모리 할당 해제
free(p_raw_data);
}
/*
- RGBC 측정값 출력 함수
- 구조체 포인터 변수에 저장된 RGB 데이터를 Clear 값으로 나누고 255를 곱한 뒤 출력
(RGB 값을 0~255 사이로 맞추기 위함)
*/
void tcs34725_rgbc_print(tcs34725_color_data_t * color_str)
{
uint16_t c_red,c_green,c_blue;
c_red=(int)((double)color_str->red/color_str->clear*255);
c_green=(int)((double)color_str->green/color_str->clear*255);
c_blue=(int)((double)color_str->blue/color_str->clear*255);
NRF_LOG_INFO("Clear : %d",color_str->clear);
NRF_LOG_INFO("Red : %d",c_red);
NRF_LOG_INFO("Green : %d",c_green);
NRF_LOG_INFO("Blue : %d",c_blue);
}
- RGB 변환은 https://forums.adafruit.com/viewtopic.php?f=8&t=67795&start=15 참조해서 작성
<TCS34725 인터럽트 설정 함수>
/*
- 인터럽트 설정(활성화) 함수
- 먼저 현재 TCS34725 Enable 레지스터에 저장된 데이터를 읽어 온다
그리고 그 데이터에 int_enable 값에 따라 BIT4 AIEN (인터럽트 인에이블)을 0 or 1로 설정
- 인터럽트 활성화 여부를 추가한 설정 데이터를 TCS34725 인에이블 레지스터에 다시 전송
- int_enable : 인터럽트 활성화 여부
*/
ret_code_t tcs34725_set_interrupt(tcs34725_instance_t const * p_instance,
tcs34725_int_enable_t int_enable)
{
ret_code_t err_code;
//TCS34725 인에이블 레지스터로부터 읽어 온 데이터를 저장하고 재전송할 구조체 변수 선언
tcs34725_reg_data_t enable_reg_str;
enable_reg_str.reg_addr=TCS34725_REG_ENABLE;
//인에이블 레지스터에 저장된 데이터를 읽어 와 구조체 변수에 저장
tcs34725_read_reg(p_instance,&enable_reg_str,NULL);
//TWI 매니저 큐에 대기된 전송이 없을 때까지(=인에이블 레지스터로부터 데이터 수신이 완료될 때까지)
//do~while문 반복
do
{
nrf_delay_us(10);
}while(nrf_twi_mngr_is_idle(&m_nrf_twi_mngr)!=true);
//읽어 온 인에이블 레지스터 데이터에 int_enable 값에 따라 BIT4 AIEN에 0 or 1 입력
if(int_enable==TCS34725_INTERRUPT_ENABLE)
{
enable_reg_str.reg_data=(enable_reg_str.reg_data|(TCS34725_INT_MASK));
}
else if(int_enable==TCS34725_INTERRUPT_DISABLE)
{
enable_reg_str.reg_data=(enable_reg_str.reg_data&~(TCS34725_INT_MASK));
}
else
{
err_code=NRF_ERROR_INVALID_PARAM;
return err_code;
}
//수정한 레지스터 설정값 전송
tcs34725_write_reg(p_instance, &enable_reg_str);
}
<TCS34725 인터럽트 클리어 함수>
/*
- 인터럽트 발생시 호출되는 핸들러
- Clear 쓰레스홀드값을 벗어나는 측정값들로 인해 인터럽트가 발생했음을 알리고
TCS34725에 인터럽트 클리어 명령을 전송해 인터럽트가 계속해서 발생할 수 있게 함
*/
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
if(pin==TCS34725_INT_PIN)
{
ret_code_t err_code;
NRF_LOG_INFO("TCS34725 RGBC Interrupt occured");
//인터럽트 클리어 명령 전송
err_code=tcs34725_int_clear(&tcs34725_instance);
APP_ERROR_CHECK(err_code);
//성공적으로 전송 완료시 출력
if(err_code==NRF_SUCCESS)
{
NRF_LOG_INFO("TCS34725 Clear channel interrupt clear");
}
}
}
/*
- 인터럽트 클리어 함수
- 인터럽트 핀이 High->Low로 전환되며 인터럽트가 발생하는데 TCS34725에 인터럽트 클리어 명령어를
보내지 않는다면 인터럽트 핀이 Low 상태를 유지하므로 더 이상의 인터럽트가 발생하지 않는다
*/
ret_code_t tcs34725_int_clear(tcs34725_instance_t const * p_instance)
{
ret_code_t err_code;
/*
- 커맨드 레지스터 사용
- 0x66 : TYPE=11 (Special Function), ADDR/SF : 00110 (Clear channel interrupt clear)
- 0x80 : CMD=1
*/
uint8_t interrupt_cmd=0x66|0x80;
err_code=nrf_twi_sensor_write(p_instance->p_sensor_data, p_instance->sensor_addr, &interrupt_cmd,
TCS34725_REGISTER_SIZE, true);
return err_code;
}
https://github.com/lee35210/nRF52840_TWI_TCS34725
- nRF52840-DK 1번 버튼 : LED 토글, 2번 버튼 : 쓰레스홀드 인터럽트 활성화, 3번 버튼 : 쓰레스홀드 인터럽트 비활성화
- 핀 설정 : TWI SDA 28, SCL 29 / TCS34725 모듈 LED 제어 30 / TCS34725 인터럽트 감지 31
- APP TIMER에 설정한 시간마다 TCS34725 RGBC 레지스터에 액세스해 측정된 RGBC값 읽어 옴
'nRF52' 카테고리의 다른 글
TCS34725 이용 원두 색상 측정 테스트 (0) | 2020.05.12 |
---|---|
nRF52 - FreeRTOS, BLE 사용 TCS34725 제어 (2) | 2020.03.27 |
nRF52 - FreeRTOS, BLE 사용 MLX90614 제어 (0) | 2020.02.26 |
nRF52 - TWI 이용 MLX90614 적외선 비접촉 온도 측정 센서 (0) | 2020.02.03 |
nRF52 - TWI(I2C) 이용 SSD1306 OLED 제어 (1) | 2020.01.13 |