|
制作過程:
1.準(zhǔn)備材料:
stm32f103核心板 1塊
OLED12864顯示屏 1塊(SPI接口)
聲音檢測傳感器 1塊 (咪頭+放大電路 可以網(wǎng)上買現(xiàn)成的模塊,也可根據(jù)后文提供的原理圖自己做)
2.硬件連接:
(1)OLED連接:
OLED_SCLK ———— PB7
OLED_SDIN ———— PB6
OLED_RST ———— PB5
OLED_RS ———— PB4
(2)聲音檢測傳感器連接:
直接將模塊的輸出接到單片機(jī)的PA0即可。
OK硬件連接完成!就這么簡單!
3.程序下載
接下來將程序下載到單片機(jī)即可,音樂頻譜就完成了!(別告訴我你連下載程序都不會 滑稽)
程序燒錄文件 鏈接:https://pan.baidu.com/s/1EjKPvBFbTmYzzh6fSn0U5A 密碼:o6uu
程序源碼:https://download.csdn.net/download/mc_li/10601743
ps:以上就是簡單的音樂頻譜制作過程,下面是較為詳細(xì)的制作過程,提供源碼和原理圖,有興趣的同志們可以看看。
單片機(jī)源程序如下:
- /* Includes ------------------------------------------------------------------*/
- #include "main.h"
- #include "stm32f1xx_hal.h"
- #include "adc.h"
- #include "dma.h"
- #include "tim.h"
- #include "usart.h"
- #include "gpio.h"
- /* USER CODE BEGIN Includes */
- #include "stm32_dsp.h"
- #include "table_fft.h"
- #include "math.h"
- #include "oled.h"
- #include "config.h"
- #include "bg.h"
- /* USER CODE END Includes */
- /* Private variables ---------------------------------------------------------*/
- /* USER CODE BEGIN PV */
- /* Private variables ---------------------------------------------------------*/
- #define NPT 256
- #define PI2 6.28318530717959
- //采樣率計算
- //分辨率:Fs/NPT
- //#define Fs 10000
- #define Fs 9984
- //取9984能出來整數(shù)的分辨率 9984/256 = 39Hz
- /* USER CODE END PV */
- /* Private function prototypes -----------------------------------------------*/
- void SystemClock_Config(void);
- void Error_Handler(void);
- /* USER CODE BEGIN PFP */
- /* Private function prototypes -----------------------------------------------*/
- void Creat_Single(void);
- void GetPowerMag(void);
- void Single_Get(void);
- void display1(void);
- void display2(void);
- void Key_Scan(void);
- /* USER CODE END PFP */
- /* USER CODE BEGIN 0 */
- uint32_t adc_buf[NPT]={0};
- long lBufInArray[NPT];
- long lBufOutArray[NPT/2];
- long lBufMagArray[NPT/2];
- uint8_t prt = 10; //量化顯示的比例
- #define SHOW_NUM 4 //顯示函數(shù)的個數(shù)
- uint8_t display_num = 1; //控制顯示方式的
- uint8_t auto_display_flag = 0; //自動切換顯示標(biāo)志 1:自動切換 0:手動
- uint8_t fall_pot[128]; //記錄下落點(diǎn)的坐標(biāo)
- /* USER CODE END 0 */
- int main(void)
- {
- /* USER CODE BEGIN 1 */
- uint16_t i = 0;
- /* USER CODE END 1 */
- /* MCU Configuration----------------------------------------------------------*/
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* Configure the system clock */
- SystemClock_Config();
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_DMA_Init();
- MX_USART1_UART_Init();
- MX_ADC1_Init();
- MX_TIM3_Init();
- /* USER CODE BEGIN 2 */
- printf("uart test! \r\n");
-
- /*初始化顯示*/
- GUI_Initialize();
- /*設(shè)置前景色和背景色 這里用1和0代替*/
- GUI_SetColor(1,0);
- GUI_LoadPic(0,0,(uint8_t *)&gImage_bg,128,64);
- GUI_Exec();
- HAL_Delay(3000);
-
- //初始化下落點(diǎn) 把下落的點(diǎn) 初始化為最底部顯示
- for(i=0;i<128;i++)
- fall_pot[i] = 63;
-
- /*啟動ADC的DMA傳輸 配合下面定時器來觸發(fā)ADC轉(zhuǎn)換*/
- HAL_ADC_Start_DMA(&hadc1, adc_buf, NPT);
- /*開啟定時器 用溢出事件來觸發(fā)ADC轉(zhuǎn)換*/
- HAL_TIM_Base_Start(&htim3);
- /* USER CODE END 2 */
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- Key_Scan();
- }
- /* USER CODE END 3 */
- }
- /** System Clock Configuration
- */
- void SystemClock_Config(void)
- {
- RCC_OscInitTypeDef RCC_OscInitStruct;
- RCC_ClkInitTypeDef RCC_ClkInitStruct;
- RCC_PeriphCLKInitTypeDef PeriphClkInit;
- /**Initializes the CPU, AHB and APB busses clocks
- */
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
- RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
- RCC_OscInitStruct.HSIState = RCC_HSI_ON;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
- RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
- if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
- {
- Error_Handler();
- }
- /**Initializes the CPU, AHB and APB busses clocks
- */
- RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
- |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
- RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
- RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
- if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
- {
- Error_Handler();
- }
- PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
- PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
- if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
- {
- Error_Handler();
- }
- /**Configure the Systick interrupt time
- */
- HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
- /**Configure the Systick
- */
- HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
- /* SysTick_IRQn interrupt configuration */
- HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
- }
- /* USER CODE BEGIN 4 */
- /************FFT相關(guān)*****************/
- //測試用 生成一個信號
- void Creat_Single(void)
- {
- u16 i = 0;
- float fx=0.0;
-
- for(i=0; i<NPT; i++)
- {
- fx = 2048+2048*sin(PI2 * i * 200.0 / Fs)+
- 3100*sin(PI2 * i * 502.0 / Fs)+
- 1300*sin(PI2 * i * 990.0 / Fs);
- lBufInArray[i] = ((signed short)fx) << 16;
- }
- }
- //獲取FFT后的直流分量
- void GetPowerMag(void)
- {
- signed short lX,lY;
- float X,Y,Mag;
- unsigned short i;
- for(i=0; i<NPT/2; i++)
- {
- lX = (lBufOutArray[i] << 16) >> 16;
- lY = (lBufOutArray[i] >> 16);
-
- //除以32768再乘65536是為了符合浮點(diǎn)數(shù)計算規(guī)律
- X = NPT * ((float)lX) / 32768;
- Y = NPT * ((float)lY) / 32768;
- Mag = sqrt(X * X + Y * Y)*1.0/ NPT;
- if(i == 0)
- lBufMagArray[i] = (unsigned long)(Mag * 32768);
- else
- lBufMagArray[i] = (unsigned long)(Mag * 65536);
- }
- }
- /*柱狀顯示*/
- void display1(void)
- {
- uint16_t i = 0;
- uint8_t x = 0;
- uint8_t y = 0;
-
- /*******************顯示*******************/
- GUI_ClearSCR();
- for(i = 0; i < 32; i++) //間隔的取32個頻率出來顯示
- {
- x = (i<<2); //i*4
- y = 63-(lBufMagArray[x+1]/prt)-2; //加1是為了丟掉第一個直流分量
- if(y>63) y = 63;
-
- GUI_LineWith(x,y,x,63,3,1);
-
- //畫下落的點(diǎn)
- if(fall_pot[i]>y) fall_pot[i] = y;
- else
- {
- if(fall_pot[i]>63) fall_pot[i]=63;
- GUI_LineWith(x,fall_pot[i],x,fall_pot[i]+3,3,1);
- fall_pot[i] += 2 ;
- }
- }
- GUI_Exec();
- }
- /*單柱狀顯示*/
- void display2(void)
- {
- uint16_t i = 0;
- uint8_t y = 0;
-
- /*******************顯示*******************/
- GUI_ClearSCR();
- for(i = 1; i < 128; i++)
- {
- y = 63-(lBufMagArray[i]/prt)-2;
- if(y>63) y = 63;
-
- GUI_RLine(i,y,63,1);
- //畫下落的點(diǎn)
- if(fall_pot[i]>y) fall_pot[i] = y;
- else
- {
- if(fall_pot[i]>63) fall_pot[i]=63;
- GUI_RLine(i,fall_pot[i],fall_pot[i]+1,1);
- fall_pot[i] += 2 ;
- }
- }
- GUI_Exec();
- }
- /*柱狀顯示 中間對稱*/
- void display3(void)
- {
- uint16_t i = 0;
- uint8_t y = 0;
-
- /*******************顯示*******************/
- GUI_ClearSCR();
- for(i = 0; i < 127; i++)
- {
- y = 31-(lBufMagArray[i+1]/prt)-2; //加1是為了丟掉第一個直流分量
- if(y>31) y = 31;
-
- GUI_RLine(i,32,y,1);
- GUI_RLine(i,32,63-y,1);
-
- //畫下落的點(diǎn)
- if(fall_pot[i]>y) fall_pot[i] = y;
- else
- {
- if(fall_pot[i]>30) fall_pot[i]=30;
- GUI_RLine(i,fall_pot[i],fall_pot[i]+1,1);
- GUI_RLine(i,63-fall_pot[i],63-(fall_pot[i]+1),1);
- fall_pot[i] += 2 ;
- }
- }
- GUI_Exec();
- }
- /*單柱狀顯示 中間對稱*/
- void display4(void)
- {
- uint16_t i = 0;
- uint8_t x = 0;
- uint8_t y = 0;
-
- /*******************顯示*******************/
- GUI_ClearSCR();
- for(i = 0; i < 32; i++) //間隔的取32個頻率出來顯示
- {
- x = (i<<2); //i*4
- y = 31-(lBufMagArray[x+1]/prt)-2; //加1是為了丟掉第一個直流分量
- if(y>31) y = 31;
-
- GUI_LineWith(x,y,x,32,3,1);
- GUI_LineWith(x,63-y,x,32,3,1);
-
- //畫下落的點(diǎn)
- if(fall_pot[i]>y) fall_pot[i] = y;
- else
- {
- if(fall_pot[i]>31) fall_pot[i]=31;
- GUI_LineWith(x,fall_pot[i],x,fall_pot[i]+3,3,1);
- GUI_LineWith(x,63 - fall_pot[i],x,63 - fall_pot[i]-3,3,1);
- fall_pot[i] += 2 ;
- }
- }
- GUI_Exec();
- }
- //ADC DMA傳輸中斷
- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
- {
- uint16_t i = 0;
- static uint16_t num = 0;
- // printf("adc dma interrupt \r\n");
- HAL_ADC_Stop_DMA(&hadc1); //完成一次測量 關(guān)閉DMA傳輸
-
- //填充數(shù)組
- for(i=0;i<NPT;i++)
- lBufInArray[i] = ((signed short)(adc_buf[i]-2048)) << 16; //這里因?yàn)閱纹瑱C(jī)的ADC只能測正的電壓 所以需要前級加直流偏執(zhí)
- //加入直流偏執(zhí)后 軟件上減去2048即一半 達(dá)到負(fù)半周期測量的目的
- //cr4_fft_1024_stm32(lBufOutArray, lBufInArray, NPT); //FFT變換
- cr4_fft_256_stm32(lBufOutArray, lBufInArray, NPT);
- GetPowerMag(); //取直流分量對應(yīng)的AD值
- // //打印出來測試
- // for(i=0;i<NPT/2;i++)
- // printf("i:%3d, f:%.2f, Power:%10d\r\n", i, (float)i*Fs/NPT, lBufMagArray[i]);
-
- //自動顯示
- if(auto_display_flag == 1)
- {
- if(num>300)
- {
- num = 0;
- display_num ++;
- if(display_num>SHOW_NUM) display_num = 1;
- }
- }
- num++;
- //顯示
- switch(display_num)
- {
- case 1:
- display1();
- break;
- case 2:
- display2();
- break;
- case 3:
- display3();
- break;
- case 4:
- display4();
- break;
- default:
- display3();
- break;
- }
- HAL_ADC_Start_DMA(&hadc1, adc_buf, NPT);
- }
- #define K1 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)
- #define K2 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)
- #define K3 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)
- void Key_Scan(void)
- {
- static uint8_t mode_num = 0;
- if(K1 == RESET)
- {
- HAL_Delay(10);
- if(K1 == RESET)
- {
- while(!K1);
- mode_num=!mode_num;
- if(mode_num == 1) //自動顯示模式
- {
- auto_display_flag = 1;
- GUI_PutString(0,0,"Auto");
- GUI_Exec();
- }
- else //正常顯示模式 手動切換效果
- {
- auto_display_flag = 0;
- GUI_PutString(0,0,"Manual");
- GUI_Exec();
- }
- }
- }
- if(K2 == RESET)
- {
- HAL_Delay(10);
- if(K2 == RESET)
- {
- while(!K2);
- if(mode_num == 0) //手動模式
- {
- display_num ++;
- if(display_num > SHOW_NUM) display_num = 1;
- }
- }
- }
- if(K3 == RESET)
- {
- HAL_Delay(10);
- if(K3 == RESET)
- {
- while(!K3);
- if(mode_num == 0) //手動模式
- {
- if(display_num == 1) display_num = SHOW_NUM+1;
- ……………………
- …………限于本文篇幅 余下代碼請從51黑下載附件…………
復(fù)制代碼
程序51hei提供下載:
FFT V2.0.7z
(620.44 KB, 下載次數(shù): 273)
2019-7-1 17:55 上傳
點(diǎn)擊文件名下載附件
|
評分
-
查看全部評分
|