본문 바로가기
임베디드 개발/STM32 (ARM Cortex-M)

STM32 ] GY-61(ADXL335) 3축 가속도센서

by eteo 2024. 6. 18.

 

 

 

보드 : STM32F429ZI

 

STM32보드에서 GY-61(ADXL335) 3축 가속도센서 값을 측정해봤다.

 

 

GY-61(ADXL335)

 

보통 아두이노랑 많이 쓰이는 MPU6050 같은 모듈은 안에 ADC가 내장되어 있어서 디지털 인터페이스로 값을 읽는데 이 모듈 같은 경우 각 축의 가속도 값을 아날로그로 출력한다.

 

데이터시트를 확인해보면 작동 전압 3V~5V이고 아날로그 출력 전압의 센터 값은 1.65V이다. Full scale 센싱 범위가 +/-3g이고 Sensitivity는 300mv/g니까 아날로그 출력은 0.75V에서 2.55V 사이로 스윙할 것이다. Sensitivity of accuracy +/-10%이다.

 

 

핀 아웃은 다음과 같다.

 

 

 

 

 

 

 

ADC

다음은 STM32 보드의 데이터시트와 매뉴얼을 살펴보자.

 

일단 ADC의 레퍼런스 전압은 Vdda로 3.3V이다.

 

그리고 변환시간과 직접적으로 관련있는 ADCCLK는 데이터시트상 Typical value가 30MHz로 나와있다.

 

그런데 ADC는 APB2의 PCLK2로부터 클럭을 공급받는데 APB2는 MAX가 90MHz이고 선택가능한 분주비는 2, 4, 6, 8이다보니까 APB2를 MAX로 운용하면 ADCCLK를 30MHz로 설정할 수 없다. 나는 그냥 APB2를 90MHz로 하고 prescaler 값을 4로 선택해 ADCCLK는 22.5MHz로 운용했다.

 

그리고 Sampling time의 경우 최소 3 ADCCLK cycles이 필요하다고 나와있고, 그 아래에는 Sampling time을 포함한 Total conversion time이 나와있는데 12-bit ADC의 경우 추가로 12 ADCCLK cycles가 필요하다.

 

Datasheet p.158

 

Datasheet p.20

 

Reference manual p.399

 

 

 

 

 

CubeMX 설정

 

클락 설정

 

 

 

ADC 핀

각 축 가속도 값을 측정할 아날로그 입력 핀

 

 

ADC1 설정

 

 

 

Sampling Time은 실험적으로 설정하고 나중에 조정해도 되는데 일단은 28 Cycles로 했다. Min값은 3 cycles이지만 실제 응용에 Min값을 선택하는 경우는 많지 않은듯하다. 

 

여기서 말하는 샘플링은 사실 Sample and Hold (S/H)라는 두 가지 단계로 이루어진다. Sample 단계에서는 아날로그 신호의 값을 특정 순간에 캡쳐하고, Hold 단계에서는 샘플링된 신호 값을 변환이 완료될 때까지 유지하는 과정으로 이루어진다.

 

이 때 Sample and Hold 회로에 있는 커패시터가 아날로그 신호의 전압을 저장하고 유지하는 역할을 하는데, 샘플링 시간이 너무 짧으면 커패시터가 신호의 전압을 춘분히 충전할 시간이 부족하여 변환 결과가 부정확해질 수도 있다.

 

ADCCLK를 22.5MHz로 설정했기 때문에 주기는 44ns인데 총 68(= 56 + 12) cycles이 필요하니까 한 채널당 변환시간이 3.02us 걸리는 셈이다.

 

그런데 나는 여기서 ADC 채널도 3개만 쓸거고 샘플링 레이트도 1kHz로 할거기때문에 저렇게 설정해도 충분하지 싶다.

 

 

 

ADC1 DMA 설정

 

 

CPU 개입없이 ADC 변환값이 레지스터에서 지정된 메모리로 전송될 수 있도록 DMA 설정을 한다. Mode는 Circular로 설정을 하고 Data Width는 ADC 결과값이 12-bit니까 Half Word인 16bit으로 충분하다.

 

 

 

 

 

 

코드

 

1초에 한번씩 각 축의 가속도 값을 UART를 통해 PC로 전송한다.

 

/* USER CODE BEGIN PV */
uint16_t accelXYZ[3];
/* USER CODE END PV */
// ...
int main(void)
{
  // ...
  /* USER CODE BEGIN 2 */
  HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&accelXYZ, 3);
  /* USER CODE END 2 */
  // ...
	while (1)
	{
      // ...
      uart_printf(UART_CH1, "%d, %d, %d\n", accelXYZ[0], accelXYZ[1], accelXYZ[2]);
	  HAL_Delay(1000);
      // ...
	}
}

 

 

 

 

 

 

그럼 올바른 값을 출력하고 있는지 확인해보자. 센서를 지면과 평행하게 놓았을 때 z축 ADC 값은 2450정도가 나오는데 z축 방향의 가속도 값은 중력가속도에 의해 1g(9.81 m/s²)에 가깝게 나와야 한다.

 

ADC 값 2450을 다시 전압값으로 변환하면 1.97V가 나온다.

1.97V = Digital value / ADC resolution * ADC Vref = 2450/4096 * 3.3

 

아까 센서 데이터시트에 0g에 해당하는 중간전압이 1.65V이고 변환공식은 300mv/g라고 했으니까 g값은 다음과 같이 구해보면 1g의 근사치가 나오는 것을 확인할 수 있다.

1.07g = 1.97V - 1.65V / 0.3(V/g)

 

 

 

 

g단위로 출력하기

 

여기서 g는 가속도의 단위이다. 1g는 지구 표면에서 물체가 받는 중력 가속도를 뜻하며 1g = 9.81 m/s²이므로 m/s² 단위로도 변환할 수 있다.

/* USER CODE BEGIN 0 */
float convert_raw_to_g(uint16_t adcValue) {
	float voltage = (adcValue / 4096.0) * 3.3;		// 디지털 값을 전압으로 변환
	float acceleration = (voltage - 1.65) / 0.3;	// 전압을 가속도 값으로 변환
	return acceleration;
}
/* USER CODE END 0 */
// ...

int main(void)
{
    //...
	while (1)
	{
		float accelXYZ_g[3];
		accelXYZ_g[0] = convertRawToG(accelXYZ[0]);
		accelXYZ_g[1] = convertRawToG(accelXYZ[1]);
		accelXYZ_g[2] = convertRawToG(accelXYZ[2]);
		uart_printf(UART_CH1, "%.2f, %.2f, %.2f\n", accelXYZ_g[0], accelXYZ_g[1], accelXYZ_g[2]);
		HAL_Delay(1000);
	}
}

 

 

 

 

 

가속도의 크기 출력하기

 

진동의 세기에 관심이 있을 때는 3차원 벡터의 크기를 구하는 공식으로 x, y, z 축에서의 가속도를 하나의 값으로 종합하여 총 가속도(Total acceleration)를 구해볼 수 있다.

 

float accel_total = sqrt(accelXYZ_g[0] * accelXYZ_g[0] + accelXYZ_g[1] * accelXYZ_g[1] + accelXYZ_g[2] * accelXYZ_g[2]);
uart_printf(UART_CH1, "%.2f\n", accel_total);
HAL_Delay(1000);

 

 

 

참고. 실수형 출력을 위해 다음과 같이 설정한다.