이번 시간부터 중급 강의로 넘어갑니다.

이번 시간부터는 B-L475E-IOT01A 보드를 사용합니다.

STM32L4 기반 보드이며 각종 센서, 무선 모듈을 탑재한 Discovery Kit입니다.

$53의 저렴한 가격으로 여러 가지 센서부터 무선 통신까지 사용해볼 수 있습니다.

이번 시간에는 HTS221이라는 온습도 센서를 사용해 보도록 하겠습니다.

STM32Cube

STM32L475BGTx로 프로젝트를 만들어 줍니다.

PA7번을 USART1_RX, PA6번을 USART1_TX로, USART1번을 Asynchronous로 설정해줍니다.

PB10번을 I2C2_SCL, PB11번을 I2C2_SDA로, I2C2를 I2C로 설정해 줍니다.

Configuration - USART1에서 Baud Rate를 9600으로, Word Length를 8bits로 설정해 줍니다.

이는 Serial 통신 설정에 맞춰주면 됩니다.

NVIC Settings로 이동하여 USART1 global interrupt의 Enabled에 체크해 줍니다.

I2C쪽은 따로 설정해줄 것이 없으므로 넘어갑니다.

톱니바퀴를 눌러 코드를 생성해 줍니다.

여전히 Toolchain / IDE는 MDK-ARM V5를 사용합니다.

uVision5

printf 함수와 직접 만든 함수 2개를 사용합니다.

/* USER CODE BEGIN 0 */

int fputc(int ch, FILE *f)
{
	uint8_t temp[1] = {ch} ;
	
	HAL_UART_Transmit(&huart1, temp, 1, 50) ;
	
	return(ch) ;
}

void reset_buffer(unsigned char *buffer, int buffer_size) {
	for (int i = 0; i < buffer_size; i++) {
		buffer[i] = 0 ;
	}
}

unsigned char read_register(unsigned char slave_address, unsigned char read_register) {
	unsigned char buffer[1] = {read_register} ;
	HAL_I2C_Master_Transmit(&hi2c2, slave_address, buffer, 1, 100) ;
	HAL_Delay(20) ;
	HAL_I2C_Master_Receive(&hi2c2, slave_address, buffer, 1, 200) ;
	return buffer[0] ;
}

/* USER CODE END 0 */

reset_buffer는 배열을 0으로 초기화할 때 사용합니다.

read_register는 i2c 통신에서 메모리의 값(1byte)을 읽을 때 사용합니다.

/* USER CODE BEGIN 1 */
	
	unsigned char buffer[5] ;
	double O2C, datum_temp ;
	int16_t T0c, T1c, T0o, T1o ;
	reset_buffer(buffer, 5) ;

/* USER CODE END 1 */

각종 변수들을 선언해 주고 reset_buffer 함수를 이용하여 초기화 해 줍니다.

O2C는 읽은 온도센서 값(아날로그 값)을 섭씨로 바꾸는 데 사용됩니다.

datum_temp는 기준이 되는 온도값 입니다.

T0c ~ T1o는 추후 서술할 calibration에 사용되는 변수입니다.

선언 후 reset_buffer 함수를 이용하여 buffer 배열을 초기화합니다.

/* USER CODE BEGIN 2 */
	
//check i2c work
buffer[0] = read_register(0x5F << 1, 0x0F) ;

if (buffer[0] == 0xBC) {  // Who am I(0x0F) return 0xBC
	//write sensor on
	printf("find HTS221\n") ;
	reset_buffer(buffer, 5) ;
	buffer[0] = 0x20;
	buffer[1] = 0x80 | 0x01 ;  // power on and 1Hz data configure

	HAL_I2C_Master_Transmit(&hi2c2, 0x5F << 1, buffer, 2, 100) ;	
}

0x5F는 hts221의 I2C 주소값입니다.

HAL_I2C_Master 계열 함수를 사용할 때 주소값을 7비트 값을 사용하기 때문에 왼쪽으로 1번 시프트합니다.

stm32l4xx_hal_i2c.c 파일에 나와있습니다.

0x0F는 WHO_AM_I register입니다.

Read-Only register이며 0xBC 값을 가지고 있습니다.

0xBC로 판단되면(HTS221이 확인되면) 0x20 register에 0x80, 0x01을 써줍니다.

0x20은 CTRL_REG1 register로 전원, data update에 관하여 설정할 수 있습니다.

기본값은 0으로 0x80은 전원을 active mode로, 0x01은 1 Hz의 속도로 데이터를 업데이트합니다.

//32 T0_degC_x8
//33 T1_degC_x8
//35 T1/T0 msb
//3C 3D T0_OUT
//3E 3F T1_OUT

unsigned char start_reg = 0x32 ;
unsigned char end_reg = 0x3F ;
unsigned char now_reg = start_reg ;

//for (int now_reg = start_reg; now_reg <= end_reg; now_reg++) {
while(now_reg <= end_reg) {
	unsigned char reg_value = read_register(0x5F << 1, now_reg) ;
	switch (now_reg) {
		case 0x32 :
			T0c = reg_value ;
			break ;
		case 0x33 :
			T1c = reg_value ;
			now_reg += 1 ;
			break ;
		case 0x35 :
			T0c |= (reg_value & 0x03) << 8 ;
			T1c |= ((reg_value >> 2) & 0x03) << 8 ;
			now_reg += 6 ;
			break ;
		case 0x3C :
			T0o = reg_value ;
			break ;
		case 0x3D :
			T0o |= reg_value << 8 ;
			break ;
		case 0x3E :
			T1o = reg_value ;
			break ;
		case 0x3F :
			T1o |= reg_value << 8 ;
			break ;
	} // end switch
	now_reg += 1 ;
} // end while

O2C = (double)(T1c - T0c) / (double)(8.0 * (T1o - T0o)) ;
datum_temp = (double)(T0c / 8.0) ;

CTRL_REG1 설정이 끝났으면 바로 밑에(if문 안에) calibration 과정을 넣어줍니다.

calibration에 필요한 변수 값들을 먼저 받아옵니다.

T0_degC(T0c), T1_degC(T1c), T0_out(T0o), T1_out(T1o) 값이 있습니다.

0x35번 register에는 T0_degC의 9, 10번째 값, T1_degC의 9, 10번째 값이 들어있습니다.

위 그래프를 참조하여 O2C 값을 찾습니다. O2C는 output 값을 섭씨로 바꾸는데 사용됩니다.

\[\frac{TOc}{8} = aT0o + b\] \[\frac{T1c}{8} = aT1o + b\] \[\frac{(T1c - T0c)}{8} = a(T1o - T0o)\] \[Tc = \frac{T0c}{8} + a(To-T0o)\] \[Tc = \frac{T0c}{8} + \frac{(To-T0o)(T1c-T0c)}{8(T1o-T0o)}\]

여기서 \(\frac{T0c}{8}\)는 datum_temp에

\(\frac{T1c-T0c}{8(T1o-T0o)}\)는 O2C에 저장합니다.

while (1) {
	reset_buffer(buffer, 5) ;
	buffer[0] = 0x27 ;
	buffer[0] = read_register(0x5F << 1, buffer[0]) ;
	HAL_Delay(50) ;
	
	if (buffer[0] == 0x03) {
		buffer[0] = 0x2B ;
		buffer[1] = 0x2A ;
		buffer[0] = read_register(0x5F << 1, buffer[0]) ;
		buffer[1] = read_register(0x5F << 1, buffer[1]) ;
		int16_t To = buffer[0] << 8 | buffer[1] ;
		
		double Tc = datum_temp + O2C * (double)(To - T0o) ;
		printf("Tc = %5.2f\n", Tc) ;
	}
}

while문을 이용하여 무한반복합니다.

통신 프로그램을 이용하여 확인해보면 아래와 같이 나옵니다.

손으로 눌러 온도를 조금 올려보았습니다.

이상입니다.