SHT3x-DIS 온습도 센서를 포팅할 때 I2C 폴링 방식으로 온습도 raw data를 읽어 처리하였지만, 폴링 방식을 사용할 경우 read 실행 후, 결과를 받는데 일정 시간이 소요된다.
이것은 단일 스레드 환경에서 전체 시스템의 딜레이를 발생할 수 있어 인터럽트 방식으로 하고자 하였으나, 안타깝게도 Read irq Pin은 존재하지 않고, Alert irq Pin밖에 제공하지 않는다. 그래서 기능 검증을 위해 폴링 방식으로 사용하였으나, 약간의 여유가 생겨 이 부분을 dma irq 방식으로 변경하여 read시 발생하는 딜레이를 없앨 수 있다.
SHT3x-DIS 모듈은 초기화 과정에서 Write 명령어를 실행 후, 온습도 데이터는 Read 명령어만 실행하기 때문에 아래와 같이 Rx에 대해서만 DMA와 인터럽트를 등록하도록 한다.
I2C read / write 명령어는 아래와 같은 함수로 구성되며, 폴링 방식인 경우 timeout 값을 설정하여 IC로부터 응답을 받기까지 일정 시간이 소요된다.
uint32_t i2c_read(uint8_t dev_addr, uint16_t mem_addr, uint16_t mem_size, uint8_t* data, uint16_t size, uint32_t timeout)
{
if (HAL_I2C_Mem_Read(&hi2c4, (uint16_t)(dev_addr << 1) | 0x01, mem_addr, mem_size, data, size, timeout) != HAL_OK) {
return -1;
}
return 0;
}
uint32_t i2c_read_dma(uint8_t dev_addr, uint16_t mem_addr, uint16_t mem_size, uint8_t* data, uint16_t size)
{
if (HAL_I2C_Mem_Read_DMA(&hi2c4, (uint16_t)(dev_addr << 1) | 0x01, mem_addr, mem_size, data, size) != HAL_OK) {
return -1;
}
return 0;
}
uint32_t i2c_write(uint8_t dev_addr, uint16_t mem_addr, uint16_t mem_size, uint8_t* data, uint16_t size, uint32_t timeout)
{
if (HAL_I2C_Mem_Write(&hi2c4, (uint16_t)(dev_addr << 1), mem_addr, mem_size, data, size, timeout) != HAL_OK) {
return -1;
}
return 0;
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef* hi2c)
{
if (hi2c->Instance == I2C4) {
sht3x_rx_callback();
}
}
하지만, DMA 방식으로 IC로부터 결과 값을 받을 경우 일정 시간 대기 없이 바로 다른 액션을 취할 수 있다. 그렇다면 결과 값을 읽는 시점은 STM32에서 제공하는 HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) 콜백함수를 이용하여 읽을 수 있다. 이 콜백은 요청한 데이터가 읽기가 완료되면 콜백함수가 실행된다.
read 명령어 실행 시, 읽는 데이터 변수를 지역 변수가 아닌 전역 변수를 사용하고 콜백 함수가 실행될 때 데이터의 무결성 검사와 온습도 데이터로 환산하면 폴링 방식과 다르게 전체 시스템에 딜레이 없이 처리할 수 있다.
static uint8_t buf[SHT3X_TEMPERHUMID_SIZE] = {0, };
static float temperature;
static float humidity;
uint32_t sht3x_rx_callback(void)
{
uint8_t crc_temper = crc8(buf, SHT3X_MEM_SIZE);
uint8_t crc_humid = crc8(&buf[3], SHT3X_MEM_SIZE);
if (crc_temper != buf[2] || crc_humid != buf[5]) {
printf("ERROR :: %s() crc error : temperature 0x%02x, expected(0x%02x), humidity 0x%02x, expected(0x%02x)\r\n", __func__, crc_temper, buf[2], crc_humid, buf[5]);
return 1;
}
/** REF: SHT3x-DIS: Datasheelt 4.13 Conversion of Singed Output */
temperature = -45.0f + (175.0f * ((uint16_t)((buf[0] << 8) | buf[1]) / 65535.0f));
humidity = 100.0f * ((uint16_t)((buf[3] << 8) | buf[4]) / 65535.0f);
return 0;
}
uint32_t sht3x_read_temperature_humidity(void)
{
memset(buf, 0, SHT3X_TEMPERHUMID_SIZE);
if (i2c_read_dma(I2C_ADDR_SHT3X, SHT3x_MEASURE_HIGHREP_STRETCH, SHT3X_MEM_SIZE, buf, SHT3X_TEMPERHUMID_SIZE) != 0) {
return -1;
}
return 0;
}
폴링 타이아웃이 20ms가 큰 시간은 아니지만, 온습도이기 때문에 1초에 한번씩 갱신할 경우 다른 모듈에 영향을 미칠 수 있어 위와 같이 수정하여 전체 시스템에 영향 없이 처리할 수 있다. 500ms 단위로 읽어도 큰 문제 없는 것을 확인하였다.