Не скажу что я имею большой опыт конструирования цветомузык. Просто решил немного попрактиковаться и заодно получить опыт программирования STM32. Были у меня мысли написать программу на mikroBasic, но посмотрев справку и примеры так и не разобрался как работать с таймерами (похоже нужно взаимодействовать непосредственно с регистрами МК) и DMA. Поэтому писал на Си, где большую часть кода сгенерировала программа STM32Cube.
Программа работает следующим образом. По таймеру каждые 50 микросекунд запускается АЦП и по окончанию измерения с помощью DMA (по нашему ПДП - прямой доступ к памяти) результат записывается в буфер в памяти. При заполнении буфера происходит прерывание (примерно каждые 50 миллисекунд (20 раз в секунду)) и производится копирование данных в другой буфер (чтобы не спеша обрабатывать данные пока в основной буфер записываются новые данные).
void ADC_CopyAdata(void) // Копирование данных из DMA буфера. Выззывается из DMA прерывания. { if (ADC_FlagData != 1) // Ожидается копирование новых данных. { uint16_t i; for (i=0; i<ADC_BuffSize; i++) { ADC_Buff[i].Real = (short)(ADC_Buff_DMA[i] - 0x8000); } ADC_FlagData = 1; // В буфер скопированы новые данные. } }
Размер буфера (массива) 1024 элемента (2 байта каждый, т. к. АПЦ 12-ти битный) и соответственно БПФ (быстрое преобразование) рассчитывается на основании 1024 точек (часть кода из функции main).
while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if (ADC_FlagData == 1) // В буфер скопированы новые данные с АЦП. { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // Мигаем светодиодом. ClearMemory32((uint32_t*)&FFT_Buff, ADC_BuffSize); // Обнуляем память под результат БПФ. fftR4((short*)&FFT_Buff, (short*)&ADC_Buff, ADC_BuffSize); // Выполняем БПФ. ClearMemory32((uint32_t*)&ADC_Buff, ADC_BuffSize); // Обнуляем память. ADC_FlagData = 0; // Т. к. БПФ провели, разрешаем копирование новых данных. for (i=0;i<16;i++) SpectrChannel[i]=0; // Обнуляем память для данных спектра. GetSpectr((FFT_Data*)&FFT_Buff, ADC_BuffSize, (uint16_t*)&SpectrChannel, 16); SetPWM((uint16_t*)&SpectrChannel); } } /* USER CODE END 3 */
После этого вычисляются амплитуды для 16 точек (1024 делится на 16 путем вычисления среднего арифметического)
// Получение спектра из результата БПФ. void GetSpectr(FFT_Data *Data, uint16_t Size, uint16_t *OutData, uint16_t OutSize) { uint16_t x=0; uint16_t s=Size/OutSize; uint16_t i; double tmp=0; for (i=0;i<Size;i++) { tmp=tmp+sqrt(Data[i].Real*Data[i].Real+Data[i].Imag*Data[i].Imag); if (i % s == s-1) { tmp = tmp / s; if (tmp<10) tmp=0; // Это шум. OutData[x] = (uint16_t)tmp; tmp = 0; x++; } } }
а затем загружается в ШИМ регистры таймеров (4 таймера по 4 канала в каждом, т. е. 16 каналов ШИМ).
void SetPWM_Tim(TIM_TypeDef *htim, uint16_t *Spectr) { htim->CCR1 = Spectr[0]; htim->CCR2 = Spectr[1]; htim->CCR3 = Spectr[2]; htim->CCR4 = Spectr[3]; } void SetPWM(uint16_t *Spectr) // Загрузка данных ШИМ в таймеры. { SetPWM_Tim(TIM1, &Spectr[0]); SetPWM_Tim(TIM2, &Spectr[4]); SetPWM_Tim(TIM3, &Spectr[8]); SetPWM_Tim(TIM4, &Spectr[12]); }
Звуковой сигнал нужно подавать на вывод A0 (амплитуда до трех вольт).
Светодиоды нужно подключить к выводам A8, A9, A10, A11, A15, B3, A2, A3, A6, A7, B0, B1, B6, B7, B8, B9.
Файлы. http://pure-basic.narod.ru/forum_files/ … Lights.zip