본문 바로가기
  • SpokeHouse
STM32/STM32F

STM32F103 - I2C로 센서 데이터 읽어오기-조도센서 BH1750

by SpokeHouse 2023. 12. 16.
728x90
728x90

안녕하세요 오토입니다.

 

이번 메모 포스팅은 STM32F103 과 I2C를 사용하여 조도센서 BH1750 의 데이터를 확보하는 메모입니다.

 

일단 항상 마찬가지로 IDE 세팅 해줍니다.

 

인터럽트 켜주구요

 

GPIO까지 세팅해주고 나면 끝납니다.

 

다음으로는 관련 헤더파일과 소스파일 생성해주고 코드 넣어줍니다.

 

헤더파일.

#ifndef INC_BH1750_H_
#define INC_BH1750_H_


//Device Address
//Please note that arduino uses 7 bit addresses, STM32 uses 8
#define BH1750_NO_GROUND_ADDR_WRITE     (0xB9 + 0)
#define BH1750_NO_GROUND_ADDR_READ      (0xB9 + 1)
#define BH1750_GROUND_ADDR_WRITE        (0x46 + 0)
#define BH1750_GROUND_ADDR_READ     (0x46 + 1)

//instructions
//datasheet ref http://cpre.kmutnb.ac.th/esl/learning/bh1750-light-sensor/bh1750fvi-e_datasheet.pdf
#define CMD_POWER_DOWN          0x00
#define CMD_POWER_ON            0x01
#define CMD_RESET               0x03
#define CMD_H_RES_MODE          0x10
#define CMD_H_RES_MODE2         0x11
#define CMD_L_RES_MODE          0x13
#define CMD_ONE_H_RES_MODE      0x20
#define CMD_ONE_H_RES_MODE2     0x21
#define CMD_ONE_L_RES_MODE      0x23
#define CMD_CNG_TIME_HIGH       0x30    // 3 LSB set time
#define CMD_CNG_TIME_LOW        0x60    // 5 LSB set time

#ifndef bool
#define bool    uint8_t
#endif

#ifndef true
#define true    1
#endif

#ifndef false
#define false   0
#endif

void BH1750_Init();
//void BH1750_ReadData(uint8_t *data);
void BH1750_ReadData(uint8_t *data);
void BH1750_run(uint8_t *data);

extern uint8_t data[2];
extern uint8_t cmd;
extern uint16_t val;
extern HAL_StatusTypeDef ret;
extern float lighting_c;

typedef struct BH1750_device BH1750_device_t;
struct BH1750_device{
    char* name;

    I2C_HandleTypeDef* i2c_handle;
    uint8_t address_r;
    uint8_t address_w;

    uint16_t value;

    uint8_t buffer[2];

    uint8_t mode;

    void (* poll)(BH1750_device_t*);
} ;

HAL_StatusTypeDef BH1750_read_dev(BH1750_device_t* dev);
HAL_StatusTypeDef BH1750_init_i2c(I2C_HandleTypeDef* i2c_handle);
BH1750_device_t* BH1750_init_dev_struct(I2C_HandleTypeDef* i2c_handle,
        char* name, bool addr_grounded);
HAL_StatusTypeDef BH1750_init_dev(BH1750_device_t* dev);
HAL_StatusTypeDef BH1750_get_lumen(BH1750_device_t* dev);


#endif /* INC_BH1750_H_ */

소스파일.

#define BH1750_ADDR_LOW      0x23 <<1  // BH1750?�� LOW 주소
#define BH1750Address 0x23
#define BH1750_CMD_POWER_ON  0x01
#define BH1750_CMD_RESET  0x07
#define BH1750_CMD_CONT_HRES 0x10  // ?��?�� 모드?? H-Resolution 모드 ?��?��
uint8_t data[2];
uint8_t buff[3];
uint8_t cmd;
uint16_t val =0;
HAL_StatusTypeDef ret;
float lighting_c;


void BH1750_run(uint8_t *data)
{

	if(bh1750_time_flag == 1)
	{

		BH1750_ReadData(data);
		bh1750_time_flag = 0;
	}

}

//i2c_BH1750
void BH1750_Init()
{
    HAL_GPIO_WritePin(LI_ADDR_GPIO_Port, LI_ADDR_Pin, GPIO_PIN_RESET);//Lighting sensor I2C Address Low
    HAL_GPIO_WritePin(LI_DVI_EN_GPIO_Port, LI_DVI_EN_Pin, GPIO_PIN_SET);//Lighting sensor I2C DVI ENABLE
    BH1750_device_t* test_dev = BH1750_init_dev_struct(&hi2c1, "test device", true);
    BH1750_init_dev(test_dev);
    printf("BH1750_init_OK\r\n");

}


void BH1750_ReadData(uint8_t *data)
{

	uint8_t buff[3];
	uint16_t val = 0;
	HAL_I2C_Master_Receive(&hi2c1, (BH1750Address << 1) | 0x01, buff, 2, 500);
	val = (uint16_t)(((buff[0] << 8) | buff[1]) / 1.2);
	printf("i2c val : %d\r\n",val);



}


void _Error_Handler(char * file, int line)
{
  while(1)
  {
  }
}

HAL_StatusTypeDef BH1750_init_i2c(I2C_HandleTypeDef* i2c_handle)
{
	  __HAL_RCC_GPIOB_CLK_ENABLE();

	i2c_handle->Instance = I2C1;
	i2c_handle->Init.ClockSpeed = 100000;
	i2c_handle->Init.DutyCycle = I2C_DUTYCYCLE_2;
	i2c_handle->Init.OwnAddress1 = 0;
	i2c_handle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
	i2c_handle->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
	i2c_handle->Init.OwnAddress2 = 0;
	i2c_handle->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
	i2c_handle->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
	if (HAL_I2C_Init(i2c_handle) != HAL_OK)
	{
		_Error_Handler(__FILE__, __LINE__);
	}

	return HAL_OK;
}

HAL_StatusTypeDef BH1750_send_command(BH1750_device_t* dev, uint8_t cmd)
{
	//TODO hal checks
	if(HAL_I2C_Master_Transmit(
			dev->i2c_handle,	//I2C Handle
			dev->address_w,		//I2C addr of dev
			&cmd,				//CMD to be executed
			1,					//8bit addr
			10					//Wait time
		) != HAL_OK) return HAL_ERROR;

	return HAL_OK;
}

void BH1750_poll_self(BH1750_device_t* self)
{
	BH1750_get_lumen(self);
}

BH1750_device_t* BH1750_init_dev_struct(I2C_HandleTypeDef* i2c_handle,char* name, bool addr_grounded)
{
	BH1750_device_t* init =
			(BH1750_device_t*)calloc(1, sizeof(BH1750_device_t));

	if(init == NULL) return NULL;

	if(addr_grounded){
		init->address_r = BH1750_GROUND_ADDR_READ;
		init->address_w = BH1750_GROUND_ADDR_WRITE;
	}else{
		init->address_r = BH1750_NO_GROUND_ADDR_READ;
		init->address_w = BH1750_NO_GROUND_ADDR_WRITE;
	}

	init->name = (char*)malloc(sizeof(char) * strlen(name));

	if(init->name == NULL) return NULL;

	init->i2c_handle = i2c_handle;

	strcpy(init->name, name);

	init->poll = &BH1750_poll_self;

	return init;
}

HAL_StatusTypeDef BH1750_init_dev(BH1750_device_t* dev)
{
	BH1750_send_command(dev, CMD_POWER_ON);
	BH1750_send_command(dev, CMD_RESET);
	BH1750_send_command(dev, CMD_H_RES_MODE);

	return HAL_OK;
}

HAL_StatusTypeDef BH1750_read_dev(BH1750_device_t* dev)
{
	if(HAL_I2C_Master_Receive(dev->i2c_handle,
			dev->address_r,
			dev->buffer,
			2,
			10
	) != HAL_OK) return HAL_ERROR;

	return HAL_OK;
}

HAL_StatusTypeDef BH1750_convert(BH1750_device_t* dev)
{
	dev->value = dev->buffer[0];
	dev->value = (dev->value << 8) | dev->buffer[1];

	//TODO check float stuff
	dev->value/=1.2;

	return HAL_OK;
}

HAL_StatusTypeDef BH1750_get_lumen(BH1750_device_t* dev)
{
	BH1750_read_dev(dev);
	BH1750_convert(dev);
	return HAL_OK;
}

1. 먼저 INIT 진행해줍니다.

- i2c는 어드레스가 필요하니까 sensor에서 어드레스 세팅될 수 있도록 gpio 먼저 low or high 세팅해주고

-센서 enable 시키는 핀 high 해줍니다.

-다음은 헤더파일에 define 된 struct fmf rename 하여 선언해주고 주고, 모드, 등등을

BH1750_init_dev_struct(&hi2c1, "test device", true);

뒤에 함수로 떼려 박아 넣을 수 있게 해주고 

 

이걸 바탕으로 최종 init 을 해줍니다.

BH1750_init_dev(test_dev);

이렇게 되면 init은 마무리됩니다. struct 사용되고 하니 따라 들어가서 보고 하나씩  이해하시면 간결하게 앞에서는 두줄이면 끝나구요

 

void BH1750_ReadData(uint8_t *data)

{

 

uint8_t buff[3];

uint16_t val = 0;

HAL_I2C_Master_Receive(&hi2c1, (BH1750Address << 1) | 0x01, buff, 2, 500);

val = (uint16_t)(((buff[0] << 8) | buff[1]) / 1.2);

printf("i2c val : %d\r\n",val);

 

 

 

}

다음은 그냥 read 함수로 받아서 버퍼에 떼려넣고 비트 쉬프트 시키는 등등 계산해줍니다.

 

센서 데이터시트보시면 나와있으니 참고하시면 되구요.

 

void BH1750_run(uint8_t *data)

{

 

if(bh1750_time_flag == 1)

{

 

BH1750_ReadData(data);

bh1750_time_flag = 0;

}

 

}

메인 run 함수 하나 만들어서 타이머 플래그 뜰때마다 돌립니다.

 

잘돌아가네요 단위가 룩스인데 사무실이 이렇게 밝은지 ... 시정수 넣어서 좀 계산해주시거나 하면 될거같구요

 

#define CMD_POWER_DOWN 0x00

#define CMD_POWER_ON 0x01

#define CMD_RESET 0x03

#define CMD_H_RES_MODE 0x10

#define CMD_H_RES_MODE2 0x11

#define CMD_L_RES_MODE 0x13

#define CMD_ONE_H_RES_MODE 0x20

#define CMD_ONE_H_RES_MODE2 0x21

#define CMD_ONE_L_RES_MODE 0x23

#define CMD_CNG_TIME_HIGH 0x30 // 3 LSB set time

#define CMD_CNG_TIME_LOW 0x60

헤더파일에 다양한 모드하고 power on/off 레지스터 주소들 있으니까 활용하시면될것같구요

 

그럼이만 오늘도 대단히 맛있는거 많이드시고 이만.

728x90
728x90

댓글