main.c
0.01MB
stm32f4xx_it.c
0.01MB
I2C.c
0.01MB
I2C.h
0.00MB
MS5611.c
0.00MB
MS5611.h
0.00MB

MS5611 datasheet

ENG_DS_MS5611-01BA03_B3.pdf
0.57MB

 

 

드론의 고도를 자동으로 제어하기 위한 방법 중 기압을 측정하여 일정한 고도로 유지하는 방법이 있다. 이때 기압 센서를 사용하는데 처음에는 BMP180을 사용하여 제어하려 했다. 하지만 BMP180은 기압이 일정하게 나오지 않아 MS5611을 사용하게 되었다.

 

MS5611은 BMP180에 비해 계산과정이 간단하고 상수값들의 개수가 적어 더 간단하게 기압 값을 얻을 수 있다.

 

MS5611을 사용할 때 주의해야 하는 점은 빛에 의해 기압 값이 튈 수 있다는 점이다. 따라서 빛에 영향을 받지 않게 케이스에 넣어 빛을 차단해줘야 한다.

 

 

 

https://www.thingiverse.com/thing:3729517

 

MS5611 case by solisqq

Case for MS5611 barometer. This case protects sensor from light and rapid pressure changes caused by air flow what makes readings more noisy. It works perfectly in my quadcopter project.

www.thingiverse.com

위 사이트에서 3D 프린터 파일을 받고 후배에게 부탁해 출력한 후 기압 센서를 넣어서 사용했다.

 

MS5611은 I2C를 사용하여 제어했고 Interrupt, DMA를 사용하지 않았다.

 

SPI로도 제어가 가능하다.

 

 

 

I2C 코드 생성 - (0)

 

I2C는 Fast Mode를 사용하고 Interrupt, DMA 설정은 하지 않는다.

 

 

 

TIM1 코드 생성 - (1)

 

TIM1을 설정하는 이유는 MS5611에서 데이터를 읽기 전에 명령을 내린 후에 데이터를 읽을 수 있다. 이때 센서가 측정하면서 걸리는 시간이 있기 때문에 Timer를 설정하여 UPDATE Interrupt가 발생할 때 데이터를 읽기 위해 설정한다.

 

이 글에서는 OSR을 1024로 설정했기 때문에 넉넉하게 2.28ms보다 큰 3ms를 설정했다.

 

 

 

코드 - main.c (2)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  /* USER CODE BEGIN 2 */
  uint16_t counter=0;
 
  setvbuf(stdout,NULL,_IONBF,0);
  printf("start\r\n");
 
  MS_init(&ms5611,I2C1);
  for(uint8_t i=0;i<6;i++){
      printf("coefficient%d: %d\r\n",i,ms5611.coefficient[i]);
  }
  LL_TIM_EnableIT_UPDATE(TIM1);
  LL_TIM_EnableCounter(TIM1);
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      if(BIT_VAL(ms5611.ms_condition,0)){
          Calculate_Temp_Pressure(&ms5611);
          if(ms5611.pressure_ready_flag){
              counter++;
              if(counter>50){
                  printf("temperature:%d\r\npressure:%d\n\n\r",ms5611.temperature,ms5611.pressure);
                  counter=0;
              }
              ms5611.pressure_ready_flag=0;
          }
      }
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
 

 

MS5611을 초기화하고 TIM1의 UPDATE Interrupt를 활성화한다.

 

stm32f4xx_it.c 파일에서 3ms마다 TIM1 UPDATE Interrupt가 발생해 ms5611.ms_condition의 0번째 bit를 1로 set 하여 3ms마다 값을 읽고 계산을 할 수 있게 했다.

 

 

 

코드 - MS5611.c (3)

1
2
3
4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
void MS_init(MS5611*ms5611,I2C_TypeDef* i2c){
    LL_mDelay(100);
 
    ms5611->i2c.I2C=i2c;
    ms5611->i2c_address=MS5611_ADDRESS;
    ms5611->ms_condition=0;
    ms5611->pressure_ready_flag=0;
    MS_ReadCoefficient(ms5611);
}
 
 
void Calculate(MS5611*ms5611){
    int32_t dT,T2,TEMP;
    int64_t OFF,OFF2,SENS,SENS2;
 
    dT=(ms5611->D2) - (ms5611->coefficient[4* (0x01<<8));
    TEMP=2000+(dT*ms5611->coefficient[5])/(0x01<<23);
 
    if(TEMP<2000){
        T2=(dT*dT)/(0x01<<31);
        OFF2=(5*(TEMP-2000)*(TEMP-2000))/(0x01<<1);
        SENS2=(5*(TEMP-2000)*(TEMP-2000))/(0x01<<2);
        if(TEMP<-1500){
            OFF2=OFF2+(7*(TEMP+1500)*(TEMP+1500));
            SENS2=SENS2+((11*(TEMP+1500)*(TEMP+1500))/(0x01<<1));
        }
    }
    else{
        T2=0;
        OFF2=0;
        SENS2=0;
    }
 
    OFF=((int64_t)ms5611->coefficient[1*(0x01<<16)) +(((int64_t)ms5611->coefficient[3]*(int64_t)dT)/(0x01<<7));
    SENS=((int64_t)ms5611->coefficient[0]*(0x01<<15))+(((int64_t)ms5611->coefficient[2]*dT)/(0x01<<8));
 
    OFF=OFF-OFF2;
    SENS=SENS-SENS2;
 
    ms5611->temperature=TEMP-T2;
    ms5611->pressure=(((ms5611->D1*SENS)/(0X01<<21))-OFF)/(0x01<<15);
}
 
 
void Calculate_Temp_Pressure(MS5611 * ms5611){
    uint8_t temp[3];
 
    if(BIT_VAL(ms5611->ms_condition,0)){
        CBI(ms5611->ms_condition,0);
        if(!BIT_VAL(ms5611->ms_condition,1)){
            MS_Command(ms5611,CONVERTD1_1024);
            SBI(ms5611->ms_condition,1);
        }
        else if(!BIT_VAL(ms5611->ms_condition,2)){
            MS_Command(ms5611,ADC_READ);
            I2C_Receive(&ms5611->i2c,ms5611->i2c_address,temp,3);
            ms5611->D1=(uint32_t)(temp[0]<<16 | temp[1]<<8 | temp[2] );
            MS_Command(ms5611,CONVERTD2_1024);
            SBI(ms5611->ms_condition,2);
        }
        else if(!BIT_VAL(ms5611->ms_condition,3)){
            MS_Command(ms5611,ADC_READ);
            I2C_Receive(&ms5611->i2c,ms5611->i2c_address,temp,3);
            ms5611->D2=(uint32_t)(temp[0]<<16 | temp[1]<<8 | temp[2] );
            Calculate(ms5611);
            ms5611->pressure_ready_flag=1;
 
            ms5611->ms_condition=0;
        }
    }
}
 

 

MS_init() 함수는 MS5611의 상수값들과 MS5611 구조체를 초기화하는 과정이다.

 

상수값은 3번 사진의 명령어와 4번 사진의 주소 값을 통해 읽을 수 있고 읽는 과정은 MS_ReadCoefficient()에 나와있다.

 

Caculate() 함수는 1번 사진의 계산과정과 2번 사진에서의 온도에 따라 변하는 계산과정을 코드로 작성한 것이다. 계산을 위해서는 D1과 D2값이 필요하다.

 

Calculate_Temp_Pressure() 함수는 3ms마다 D1, D2를 구하고 온도 값과 기압 값을 구하는 순서를 나타낸다. D1, D2는 명령을 보낸 후 일정 시간이 흐른 뒤에 읽을 수 있기 때문에 순서에 따라서 값을 읽을 수 있도록 했다.

 

Delay 함수를 사용하지 않은 이유는 Delay를 하게 되면 그 시간 동안 다른 행동을 할 수가 없게 된다. 만약 드론에 사용하게 된다면 3ms동안 아무 계산도 수행할 수 없게된다. 그렇게 되면 제대로 된 제어를 할 수 없으므로 Timer를 통해 3ms가 지날 때마다 순차적으로 값을 읽게 했다.

 

 

 

결과 - (4)

 

실제 실행하면 읽어 들인 상수값과 일정 시간마다 온도 값과 기압 값을 출력한다.

 

 

 

 

-

쿼드콥터에 사용했고 이제 헥사콥터에 적용할 기압 센서 MS5611의 코드를 작성했다.

 

실제 사용하려면 데이터를 약간 가공해서 사용해야 한다.

 

쿼드콥터에 적용했을 때 완벽하게 고도를 유지하지 않고 조금씩 위아래로 흔들리는 모습을 보인다. 이는 계속 개선해야 할 문제이다.

'임베디드 > STM32' 카테고리의 다른 글

[STM32] STM32cubeMX linux  (0) 2021.02.23
[STM32] 간단한 ICM20948 쿼터니언  (0) 2020.09.18
[STM32] LL 드라이버 - SPI DMA, NRF24L01  (1) 2020.08.20
[STM32] LL 드라이버 - ICM20948  (0) 2020.08.14
[STM32] LL 드라이버 - M8N GPS  (0) 2020.08.04
Posted by DDTXRX
,