找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 8788|回复: 7
打印 上一主题 下一主题
收起左侧

STM32+咪头+OLED12864音乐LED频谱制作 附程序

  [复制链接]
跳转到指定楼层
楼主
ID:575440 发表于 2019-7-1 10:29 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
制作过程:
1.准备材料:
stm32f103核心板 1块
OLED12864显示屏 1块(SPI接口)
声音检测传感器 1块 (咪头+放大电路 可以网上买现成的模块,也可根据后文提供的原理图自己做)
2.硬件连接:
(1)OLED连接:
OLED_SCLK   ————  PB7
OLED_SDIN    ————  PB6
OLED_RST     ————  PB5
OLED_RS       ————  PB4
(2)声音检测传感器连接:
直接将模块的输出接到单片机的PA0即可。
OK硬件连接完成!就这么简单!
3.程序下载
接下来将程序下载到单片机即可,音乐频谱就完成了!(别告诉我你连下载程序都不会 滑稽)
程序烧录文件 链接:https://pan.baidu.com/s/1EjKPvBFbTmYzzh6fSn0U5A 密码:o6uu
程序源码:https://download.csdn.net/download/mc_li/10601743
ps:以上就是简单的音乐频谱制作过程,下面是较为详细的制作过程,提供源码和原理图,有兴趣的同志们可以看看。

单片机源程序如下:
  1. /* Includes ------------------------------------------------------------------*/
  2. #include "main.h"
  3. #include "stm32f1xx_hal.h"
  4. #include "adc.h"
  5. #include "dma.h"
  6. #include "tim.h"
  7. #include "usart.h"
  8. #include "gpio.h"

  9. /* USER CODE BEGIN Includes */
  10. #include "stm32_dsp.h"
  11. #include "table_fft.h"
  12. #include "math.h"
  13. #include "oled.h"
  14. #include "config.h"
  15. #include "bg.h"
  16. /* USER CODE END Includes */

  17. /* Private variables ---------------------------------------------------------*/

  18. /* USER CODE BEGIN PV */
  19. /* Private variables ---------------------------------------------------------*/
  20. #define NPT 256
  21. #define PI2 6.28318530717959
  22. //采样率计算
  23. //分辨率:Fs/NPT
  24. //#define Fs        10000
  25. #define Fs        9984
  26. //取9984能出来整数的分辨率 9984/256 = 39Hz

  27. /* USER CODE END PV */

  28. /* Private function prototypes -----------------------------------------------*/
  29. void SystemClock_Config(void);
  30. void Error_Handler(void);

  31. /* USER CODE BEGIN PFP */
  32. /* Private function prototypes -----------------------------------------------*/
  33. void Creat_Single(void);
  34. void GetPowerMag(void);
  35. void Single_Get(void);
  36. void display1(void);
  37. void display2(void);
  38. void Key_Scan(void);
  39. /* USER CODE END PFP */

  40. /* USER CODE BEGIN 0 */
  41. uint32_t adc_buf[NPT]={0};

  42. long lBufInArray[NPT];
  43. long lBufOutArray[NPT/2];
  44. long lBufMagArray[NPT/2];


  45. uint8_t prt = 10;        //量化显示的比例
  46. #define SHOW_NUM 4                                //显示函数的个数
  47. uint8_t display_num = 1;        //控制显示方式的
  48. uint8_t auto_display_flag = 0;        //自动切换显示标志 1:自动切换 0:手动

  49. uint8_t fall_pot[128];        //记录下落点的坐标

  50. /* USER CODE END 0 */

  51. int main(void)
  52. {

  53.   /* USER CODE BEGIN 1 */
  54.         uint16_t i = 0;
  55.   /* USER CODE END 1 */

  56.   /* MCU Configuration----------------------------------------------------------*/

  57.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  58.   HAL_Init();

  59.   /* Configure the system clock */
  60.   SystemClock_Config();

  61.   /* Initialize all configured peripherals */
  62.   MX_GPIO_Init();
  63.   MX_DMA_Init();
  64.   MX_USART1_UART_Init();
  65.   MX_ADC1_Init();
  66.   MX_TIM3_Init();

  67.   /* USER CODE BEGIN 2 */
  68.         printf("uart test! \r\n");
  69.        
  70.         /*初始化显示*/
  71.         GUI_Initialize();
  72.         /*设置前景色和背景色 这里用1和0代替*/
  73.         GUI_SetColor(1,0);
  74.         GUI_LoadPic(0,0,(uint8_t *)&gImage_bg,128,64);
  75.         GUI_Exec();
  76.         HAL_Delay(3000);
  77.        
  78.         //初始化下落点 把下落的点 初始化为最底部显示
  79.         for(i=0;i<128;i++)
  80.                 fall_pot[i] = 63;
  81.        
  82.         /*启动ADC的DMA传输 配合下面定时器来触发ADC转换*/
  83.         HAL_ADC_Start_DMA(&hadc1, adc_buf, NPT);
  84.         /*开启定时器 用溢出事件来触发ADC转换*/
  85.         HAL_TIM_Base_Start(&htim3);

  86.   /* USER CODE END 2 */

  87.   /* Infinite loop */
  88.   /* USER CODE BEGIN WHILE */
  89.   while (1)
  90.   {
  91.   /* USER CODE END WHILE */

  92.   /* USER CODE BEGIN 3 */
  93.                 Key_Scan();
  94.   }
  95.   /* USER CODE END 3 */

  96. }

  97. /** System Clock Configuration
  98. */
  99. void SystemClock_Config(void)
  100. {

  101.   RCC_OscInitTypeDef RCC_OscInitStruct;
  102.   RCC_ClkInitTypeDef RCC_ClkInitStruct;
  103.   RCC_PeriphCLKInitTypeDef PeriphClkInit;

  104.     /**Initializes the CPU, AHB and APB busses clocks
  105.     */
  106.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  107.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  108.   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  109.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  110.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  111.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  112.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  113.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  114.   {
  115.     Error_Handler();
  116.   }

  117.     /**Initializes the CPU, AHB and APB busses clocks
  118.     */
  119.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  120.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  121.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  122.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  123.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  124.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  125.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  126.   {
  127.     Error_Handler();
  128.   }

  129.   PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  130.   PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  131.   if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  132.   {
  133.     Error_Handler();
  134.   }

  135.     /**Configure the Systick interrupt time
  136.     */
  137.   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  138.     /**Configure the Systick
  139.     */
  140.   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  141.   /* SysTick_IRQn interrupt configuration */
  142.   HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
  143. }

  144. /* USER CODE BEGIN 4 */
  145. /************FFT相关*****************/
  146. //测试用 生成一个信号
  147. void Creat_Single(void)
  148. {
  149.         u16 i = 0;
  150.         float fx=0.0;
  151.        
  152.         for(i=0; i<NPT; i++)
  153.         {
  154.                 fx = 2048+2048*sin(PI2 * i * 200.0 / Fs)+
  155.                                  3100*sin(PI2 * i * 502.0 / Fs)+
  156.                                  1300*sin(PI2 * i * 990.0 / Fs);
  157.                 lBufInArray[i] = ((signed short)fx) << 16;               
  158.         }
  159. }
  160. //获取FFT后的直流分量
  161. void GetPowerMag(void)
  162. {
  163.     signed short lX,lY;
  164.     float X,Y,Mag;
  165.     unsigned short i;
  166.     for(i=0; i<NPT/2; i++)
  167.     {
  168.         lX  = (lBufOutArray[i] << 16) >> 16;
  169.         lY  = (lBufOutArray[i] >> 16);
  170.                        
  171.                                 //除以32768再乘65536是为了符合浮点数计算规律
  172.         X = NPT * ((float)lX) / 32768;
  173.         Y = NPT * ((float)lY) / 32768;
  174.         Mag = sqrt(X * X + Y * Y)*1.0/ NPT;
  175.         if(i == 0)       
  176.             lBufMagArray[i] = (unsigned long)(Mag * 32768);
  177.         else
  178.             lBufMagArray[i] = (unsigned long)(Mag * 65536);
  179.     }
  180. }
  181. /*柱状显示*/
  182. void display1(void)
  183. {
  184.         uint16_t i = 0;
  185.         uint8_t x = 0;
  186.         uint8_t y = 0;
  187.        
  188.         /*******************显示*******************/
  189.         GUI_ClearSCR();
  190.         for(i = 0; i < 32; i++)        //间隔的取32个频率出来显示
  191.         {
  192.                 x = (i<<2);        //i*4
  193.                 y = 63-(lBufMagArray[x+1]/prt)-2;        //加1是为了丢掉第一个直流分量
  194.                 if(y>63) y = 63;
  195.                
  196.                 GUI_LineWith(x,y,x,63,3,1);
  197.                
  198.                 //画下落的点
  199.                 if(fall_pot[i]>y) fall_pot[i] = y;
  200.                 else
  201.                 {
  202.                                 if(fall_pot[i]>63) fall_pot[i]=63;
  203.                                 GUI_LineWith(x,fall_pot[i],x,fall_pot[i]+3,3,1);
  204.                                 fall_pot[i] += 2 ;
  205.                 }
  206.         }
  207.         GUI_Exec();
  208. }
  209. /*单柱状显示*/
  210. void display2(void)
  211. {
  212.         uint16_t i = 0;
  213.         uint8_t y = 0;
  214.        
  215.         /*******************显示*******************/
  216.         GUI_ClearSCR();
  217.         for(i = 1; i < 128; i++)       
  218.         {
  219.                 y = 63-(lBufMagArray[i]/prt)-2;
  220.                 if(y>63) y = 63;
  221.                
  222.                 GUI_RLine(i,y,63,1);               
  223.                 //画下落的点
  224.                 if(fall_pot[i]>y) fall_pot[i] = y;
  225.                 else
  226.                 {
  227.                                 if(fall_pot[i]>63) fall_pot[i]=63;
  228.                                 GUI_RLine(i,fall_pot[i],fall_pot[i]+1,1);
  229.                                 fall_pot[i] += 2 ;
  230.                 }
  231.         }
  232.         GUI_Exec();
  233. }
  234. /*柱状显示 中间对称*/
  235. void display3(void)
  236. {
  237.         uint16_t i = 0;
  238.         uint8_t y = 0;
  239.        
  240.         /*******************显示*******************/
  241.         GUI_ClearSCR();
  242.         for(i = 0; i < 127; i++)       
  243.         {
  244.                 y = 31-(lBufMagArray[i+1]/prt)-2;        //加1是为了丢掉第一个直流分量
  245.                 if(y>31) y = 31;
  246.                
  247.                 GUI_RLine(i,32,y,1);
  248.                 GUI_RLine(i,32,63-y,1);
  249.                
  250.                 //画下落的点
  251.                 if(fall_pot[i]>y) fall_pot[i] = y;
  252.                 else
  253.                 {
  254.                                 if(fall_pot[i]>30) fall_pot[i]=30;
  255.                                 GUI_RLine(i,fall_pot[i],fall_pot[i]+1,1);
  256.                                 GUI_RLine(i,63-fall_pot[i],63-(fall_pot[i]+1),1);
  257.                                 fall_pot[i] += 2 ;
  258.                 }
  259.         }
  260.         GUI_Exec();
  261. }
  262. /*单柱状显示 中间对称*/
  263. void display4(void)
  264. {
  265.         uint16_t i = 0;
  266.         uint8_t x = 0;
  267.         uint8_t y = 0;
  268.        
  269.         /*******************显示*******************/
  270.         GUI_ClearSCR();
  271.         for(i = 0; i < 32; i++)        //间隔的取32个频率出来显示
  272.         {
  273.                 x = (i<<2);        //i*4
  274.                 y = 31-(lBufMagArray[x+1]/prt)-2;        //加1是为了丢掉第一个直流分量
  275.                 if(y>31) y = 31;
  276.                
  277.                 GUI_LineWith(x,y,x,32,3,1);
  278.                 GUI_LineWith(x,63-y,x,32,3,1);
  279.                
  280.                 //画下落的点
  281.                 if(fall_pot[i]>y) fall_pot[i] = y;
  282.                 else
  283.                 {
  284.                                 if(fall_pot[i]>31) fall_pot[i]=31;
  285.                                 GUI_LineWith(x,fall_pot[i],x,fall_pot[i]+3,3,1);
  286.                                 GUI_LineWith(x,63 - fall_pot[i],x,63 - fall_pot[i]-3,3,1);
  287.                                 fall_pot[i] += 2 ;
  288.                 }
  289.         }
  290.         GUI_Exec();
  291. }


  292. //ADC DMA传输中断
  293. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
  294. {
  295.         uint16_t i = 0;
  296.         static uint16_t num = 0;       
  297. //        printf("adc dma interrupt \r\n");
  298.         HAL_ADC_Stop_DMA(&hadc1);                                                        //完成一次测量 关闭DMA传输
  299.        
  300.         //填充数组
  301.         for(i=0;i<NPT;i++)
  302.                 lBufInArray[i] = ((signed short)(adc_buf[i]-2048)) << 16;                //这里因为单片机的ADC只能测正的电压 所以需要前级加直流偏执
  303.                                                                                                                                                                                                                                                                         //加入直流偏执后 软件上减去2048即一半 达到负半周期测量的目的
  304.         //cr4_fft_1024_stm32(lBufOutArray, lBufInArray, NPT);                                                        //FFT变换
  305.         cr4_fft_256_stm32(lBufOutArray, lBufInArray, NPT);
  306.         GetPowerMag();                                                                                                                                                                                                        //取直流分量对应的AD值
  307. //        //打印出来测试
  308. //        for(i=0;i<NPT/2;i++)
  309. //                printf("i:%3d, f:%.2f, Power:%10d\r\n", i, (float)i*Fs/NPT, lBufMagArray[i]);
  310.        
  311.         //自动显示
  312.         if(auto_display_flag == 1)
  313.         {
  314.                 if(num>300)
  315.                 {
  316.                         num = 0;
  317.                         display_num ++;
  318.                         if(display_num>SHOW_NUM) display_num = 1;
  319.                 }
  320.         }
  321.         num++;
  322.         //显示
  323.         switch(display_num)
  324.         {
  325.                 case 1:
  326.                         display1();
  327.                         break;
  328.                 case 2:
  329.                         display2();
  330.                         break;
  331.                 case 3:
  332.                         display3();
  333.                         break;
  334.                 case 4:
  335.                         display4();
  336.                         break;
  337.                 default:
  338.                         display3();
  339.                         break;
  340.         }
  341.         HAL_ADC_Start_DMA(&hadc1, adc_buf, NPT);
  342. }

  343. #define K1 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)
  344. #define K2 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)
  345. #define K3 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)

  346. void Key_Scan(void)
  347. {
  348.         static uint8_t mode_num = 0;
  349.         if(K1 == RESET)
  350.         {
  351.                 HAL_Delay(10);
  352.                 if(K1 == RESET)
  353.                 {
  354.                         while(!K1);
  355.                         mode_num=!mode_num;
  356.                         if(mode_num == 1) //自动显示模式
  357.                         {
  358.                                 auto_display_flag = 1;
  359.                                 GUI_PutString(0,0,"Auto");
  360.                                 GUI_Exec();
  361.                         }
  362.                         else                                                        //正常显示模式 手动切换效果
  363.                         {
  364.                                 auto_display_flag = 0;
  365.                                 GUI_PutString(0,0,"Manual");
  366.                                 GUI_Exec();
  367.                         }
  368.                 }
  369.         }
  370.         if(K2 == RESET)
  371.         {
  372.                 HAL_Delay(10);
  373.                 if(K2 == RESET)
  374.                 {
  375.                         while(!K2);
  376.                         if(mode_num == 0)        //手动模式
  377.                         {
  378.                                 display_num ++;
  379.                                 if(display_num > SHOW_NUM) display_num = 1;
  380.                         }
  381.                 }
  382.         }
  383.         if(K3 == RESET)
  384.         {
  385.                 HAL_Delay(10);
  386.                 if(K3 == RESET)
  387.                 {
  388.                         while(!K3);
  389.                         if(mode_num == 0)        //手动模式
  390.                         {
  391.                                 if(display_num == 1) display_num = SHOW_NUM+1;
  392. ……………………

  393. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

程序51hei提供下载:
FFT V2.0.7z (620.44 KB, 下载次数: 265)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏7 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:537401 发表于 2019-7-2 21:26 | 只看该作者
楼主高大。
回复

使用道具 举报

板凳
ID:562991 发表于 2019-7-30 15:52 | 只看该作者
支持谢谢!
回复

使用道具 举报

地板
ID:4572 发表于 2020-4-2 23:24 | 只看该作者
支持谢谢!
回复

使用道具 举报

5#
ID:745514 发表于 2020-6-3 11:06 来自手机 | 只看该作者
原理图楼主能分享一下吗
回复

使用道具 举报

6#
ID:434962 发表于 2021-4-22 15:14 | 只看该作者
效果怎么样,有没有人调试过?
估计失真较大
回复

使用道具 举报

7#
ID:480779 发表于 2021-5-30 22:16 | 只看该作者
这是OLED音乐频谱吧,你的标题对不对啊?
回复

使用道具 举报

8#
ID:796531 发表于 2022-4-19 16:39 | 只看该作者
好东西,网上找了半天都要money下载,总算在这里下到了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表