본문 바로가기

CMSIS DSP

FFT 원리와 Frequency Bin Example

먼저 DSP의 가장 기본이되는 FFT 이론이 프로그램으로 어떻게 구성되고 사용되는지 알아보기 위해 FFT 공식을 간단히 분석해 보고 ARM 사에서 제공하는 CMSIS DSP 함수중 하나인 FFT(Fast Fourier Transform) 함수 예제를 약간 수정해서 돌려보았습니다.


MCU는 Cortex-M0 기반의 STM32F091RC를 사용했습니다. 처음엔 F가 붙은 Cortex MCU만 CMSIS DSP를 사용할 수 있다고 알고 있었는데, F는  DSP에 사용되는 Float 연산을 1~2 사이클 안에 처리해 주는 하드웨어인 Float Process Unit(FPU)의 탑제 여부만 의미할 뿐, FPU가 없는 Cortex-M0도 CMSIS DSP 라이브러리를 사용할 수 있습니다. 단지 Float 연산이 소프트웨어로 구성되어 속도가 느릴 뿐이죠.


Cortex-M0에서 CMSIS DSP 라이브러리를 사용하기 위해선 ARM_MATH_CM0를 정의하고, 필요한 소스 코드와 헤더 파일을 추가해 줘야 합니다. 전 귀찮아서 라이브러리 파일을 통으로 사용했습니다. 이 라이브러리 파일의 경로를 링커에 알려주면 됩니다. IDE는 TrueStudio를 사용했습니다. TrueStudio에서 printf 함수로 소수점을 출력하기 위해서는 링커 옵션에 -u _printf_float을 추가해 주어야 합니다.


FFT 공식과 의미, 그리고 이것이 프로그램에 어떻게 적용되었는지 그림으로 나타내 봤습니다.


그런데 저도 물음표로 되어 있는 부분은 확실히 모르겠습니다.


CMSIS FFT 함수인 arm_cfft_f32 함수는 2개의 float32_t 타입으로 구성된 bin 단위로 결과를 출력합니다. bin은 사전적 의미로 '통'을 의미합니다. 단순히 실수와 허수로 구성된 '집합'이라고 생각하면 됩니다. 각 bin의 앞 인덱스는 실수를 뒤 인덱스는 허수의 계수를 나타냅니다. arm_cfft_f32 함수는 입력과 출력에 동일한 버퍼를 사용하기 때문에 ADC 값들은 한칸씩 띄워서 패딩 공간을 만들어 놔야 합니다.


또한 arm_cfft_f32 함수는 입력된 ADC 값이 모두 0~π ((N-1)/2) 안에 있다고 가정하고 연산을 하고, 0~2π (N-1) 까지의 결과를 출력하므로, 항상 NZ(Nyquist Zone)#1과 NZ#2를 출력하고, NZ#1과 NZ#2는 방향만 대칭인 동일한 값들을 가지고 있습니다.


따라서 arm_cfft_f32 함수에서 실제 주파수는 의미가 없으며, 대신 bin의 인덱스를 사용하여 주파수를 표시합니다. 그리고 이것은 샘플링 주파수를 알고 있는 개발자에 의해 적절하게 해석되어야 합니다.


그리고 arm_cmplx_mag_f32 함수에서 실수와 허수로 구성된 bin의 절대 크기를 계산합니다. arm_cfft_f32 함수의 결과로 생성된 bin의 실수와 허수가 크기와 위상에 대한 모든 정보를 가지고 있었다면 arm_cmplx_mag_f32 함수의 결과는 크기 정보만 가지고 있습니다. 하지만 위상 정보로 얻을 수 있는 주파수는 bin의 인덱스로 얻을 수 있으므로 분석을 위해서는 절대 크기를 사용하는 것이 더 편리합니다.


arm_cmplx_mag_f32 함수는 arm_cfft_f32 함수와 달리 입력 버퍼와 출력 버퍼를 따로 지정해야 하기 때문에 testInput_f32_10khz_sampled_48khz 배열의 절반 크기인 testOutput 배열을 출력 버퍼로 사용했습니다.


ARM 사에서 제공하는 기본 예제는 10KHz 주파수와 노이즈가 섞인 신호를 48KHz로 샘플링한 데이터를 arm_cfft_f32 함수를 사용하여 FFT를 수행하고, arm_cmplx_mag_f32 함수를 사용하여 이 결과를 절대 크기로 변환합니다. 저는 여기에 printf를 추가하여 결과값을 Tera Term으로 출력하고, 이것들을 로그로 저장해 엑셀로 그래프를 그려봤습니다.


앞에서 bin의 인덱스로 주파수가 계산된다고 했습니다. bin의 마지막 인덱스는 샘플링 주파수를 의미합니다. 그리고 각 bin이 의미하는 주파수는 샘플링 주파수 x (bin의 인덱스) / (bin의 개수-1)가 됩니다. 결과를 확인해보면 213 인덱스에서 최대값이 관찰됩니다. 이 인덱스는 10KHz를 의미하고, 입력된 신호를 예상대로 분석하여 나타내고 있습니다. 그리고 NZ#2에 미러 이미지가 나타납니다.