6808|6

1377

帖子

2

TA的资源

五彩晶圆(初级)

楼主
 

[任性DIY]简易音频信号源 [复制链接]

 
玩模拟电路的都需要个音频信号源吧?(只晚射频电路的除外,避免抬杠) 就算没有专门的信号源,电脑声卡、MP3甚至手机都可以拿来顶替一下。我也经常是用连在电脑上的DAC来获得音频信号的,我也DIY了模拟的低失真信号源,不过那只有单个频点。好,先看我今天做出来的信号源效果图:
呃,这是什么鬼玩意儿? 信号源?

别着急,先看这个:

妥妥的正弦波,这次对了吧。没错,这就是我的信号源出来的。
下面这个呢,看起来好象波形失真了……

其实不是,这只是个巧合,示波器现在是当X-Y显示器用的,再来几个就明白了:






如果X,Y输入同频正弦,相位差90度,示波器X,Y增益相当的话,画出来就是一个圆了。


好,效果图看完了,信号源长啥样?

这东西其实并不大,因为用了单片机,而且可以3.2V电池直供电使用。输出两路音频正弦信号,频率1Hz~23999Hz, 1Hz步进可调。频率很准的,因为是晶振决定。
MCU就是ST的Cortex-m0: STM32F051

不过我这次把MCU给“超频”用了,晶振是49.152MHz, 超过了规格最大48MHz一点。原因是为了采样频率在48000Hz上。那么我用什么来产生模拟信号呢?单片机自带了DAC,这个可以用,但是精度差了点。我另外用了一片音频的Sigma-Delta DAC: Cirrus Logic的CS4344:

就是直插电容旁边的小芯片了,音频专用哦,用在信号源上正合适。这个DAC也很便宜,最高支持192kHz的采样频率,不过我这里只用到48kHz. 通过I2S接口把PCM编码送过去,就出来模拟信号了,因为STM32F051已经有I2S接口了,所以用起来很方便。


线路挺简单的,我多引了些I/O到插针位上,方便以后做其它应用。CS4344的I2S信号MCLK, BCK, LRCK, DATA连到MCU上,作为slave设备。CS4344的模拟输出就按照手册上的简单接法输出了,没有加滤波电路,实际上高频噪声影响还是有的。PCB版图:我惯用的Eagle软件出图。




原来想用一块1602 LCD做显示,几个按钮做控制。后来觉得1602模块本身体积更大,弄出来又不小巧了,一时也没选定盒子。于是我就决定用串口控制吧,设置好了以后可以拔下来,整个更小巧。所以软件上就是更新波形数据,和接收串口命令的干活…… 一个在DMA的中断里面,一个在串口接收中断里面。
  1. #include "stm32f0xx.h"
  2. #include "uart.h"

  3. #include "sinetab.c"
  4. // sinetable, 12001 entries for 1st quarter of a sine wave

  5. int16_t dma_wavebuf[2048];  // can hold 512 samples
  6. int phase1=0, phase2=12000;
  7. int freq1=1000, freq2=1000;

  8. int main(void)
  9. {
  10.     int i;

  11.     RCC->CR |= RCC_CR_HSEON;    // enable external crystal osc (16.9344MHz)
  12.     RCC->CFGR = RCC_CFGR_PLLSRC_1|RCC_CFGR_PLLMUL2;     // PLL (HSE div) mul 2
  13.     FLASH->ACR = FLASH_ACR_LATENCY|FLASH_ACR_PRFTBE;    // one wait state
  14.     while(!(RCC->CR & RCC_CR_HSERDY));  // wait until HSE stable
  15.     RCC->CR |= RCC_CR_PLLON;    // turn on PLL
  16.     while(!(RCC->CR & RCC_CR_PLLRDY));  // wait until PLL ready
  17.     RCC->CFGR = RCC_CFGR_SW_PLL;    // switch sysclk to PLL (49.152MHz)

  18.     RCC->AHBENR |= RCC_AHBENR_GPIOAEN|RCC_AHBENR_GPIOBEN;   // enable GPIO port A & B clock

  19.     GPIOA->MODER = GPIO_MODER_MODER14_1|GPIO_MODER_MODER13_1   // PA14, PA13 AF (SWD pins)
  20.                 |GPIO_MODER_MODER10_1|GPIO_MODER_MODER9_1   // PA10, PA9 AF (UART)
  21.                 |GPIO_MODER_MODER15_1;  // PA15 AF (I2S1_WS)
  22.     GPIOA->AFR[1] = 0x00000110; // AF1 for PA9,PA10; AF0 for others


  23.     RCC->AHBENR |= RCC_AHBENR_DMAEN;
  24.     uart_setup();   // setup USART1, (115200, 8-N-1), use DMA1 ch4
  25.     uart_wstr("\r\nUART OK.\r\n");


  26.     RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // enable SPI1 (I2S) clock
  27.     GPIOB->MODER = GPIO_MODER_MODER3_1|GPIO_MODER_MODER4_1|GPIO_MODER_MODER5_1; // I2S1
  28.     SPI1->I2SCFGR = SPI_I2SCFGR_I2SMOD|SPI_I2SCFGR_I2SCFG_1|SPI_I2SCFGR_DATLEN_0; // I2S 24-bit master transmit
  29.     SPI1->I2SPR = SPI_I2SPR_MCKOE|2;    // I2S prescaler, set for 48kHz fs, MCK enabled (256fs)
  30.     SPI1->CR2 = SPI_CR2_TXDMAEN;    // DMA enabled on transfer

  31.     DMA1_Channel3->CPAR=(uint32_t)&(SPI1->DR);
  32.     DMA1_Channel3->CCR=DMA_CCR_PSIZE_0|DMA_CCR_MSIZE_0|DMA_CCR_CIRC // 16-bit, auto reload, as SPIDR is only 16-bit
  33.                         |DMA_CCR_MINC|DMA_CCR_DIR // 16bit, memory-incremental, memory-to-peripheral
  34.                         |DMA_CCR_TCIE|DMA_CCR_HTIE; // complete and half interrput
  35.     DMA1_Channel3->CMAR=(uint32_t)dma_wavebuf; //wtable;
  36.     DMA1_Channel3->CNDTR = 2048;
  37.     DMA1_Channel3->CCR |= DMA_CCR_EN;

  38.     NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
  39.     NVIC_SetPriority(DMA1_Channel2_3_IRQn,0);
  40.     NVIC_EnableIRQ(USART1_IRQn);

  41.     SPI1->I2SCFGR |= SPI_I2SCFGR_I2SE;  // enable I2S
  42.     uart_wstr("Keys: o/u/i/d/h/t\r\n");

  43.     while (1)
  44.     {
  45.     }
  46. }

  47. void USART1_IRQHandler(void)
  48. {
  49.     char key;
  50.     if(USART1->ISR & USART_ISR_ORE)  // Overrun
  51.     {
  52.         USART1->ICR = USART_ICR_ORECF;
  53.         return;
  54.     }
  55.     key=USART1->RDR;
  56.     switch(key)
  57.     {
  58.         case 'i': freq1--; break;
  59.         case 'u': freq1-=10; break;
  60.         case 'e': freq1-=100; break;
  61.         case 'd': freq1++; break;
  62.         case 'h': freq1+=10; break;
  63.         case 't': freq1+=100; break;
  64.         case 'I': freq2--; break;
  65.         case 'U': freq2-=10; break;
  66.         case 'E': freq2-=100; break;
  67.         case 'D': freq2++; break;
  68.         case 'H': freq2+=10; break;
  69.         case 'T': freq2+=100; break;
  70.         case '\t': freq2=freq1; break;
  71.         case '0': phase2=phase1; break;
  72.         case '1': phase2=phase1+12000; break;
  73.         case '2': phase2=phase1+24000; break;
  74.         case '3': phase2=phase1+36000; break;
  75.         default: return;
  76.     }
  77.     if(freq1<1) freq1=1;
  78.     if(freq1>23999) freq1=23999;
  79.     if(freq2<1) freq2=1;
  80.     if(freq2>23999) freq2=23999;
  81.     uart_wstr("SET freq 1: ");
  82.     uart_wdec(freq1);
  83.     uart_wstr("Hz 2: ");
  84.     uart_wdec(freq2);
  85.     uart_wstr("Hz\r\n");
  86. }

  87. void DMA1_Channel2_3_IRQHandler(void)
  88. {
  89.     int16_t *buf_base;
  90.     int i;
  91.     if(DMA1->ISR & DMA_ISR_HTIF3)
  92.     {
  93.         DMA1->IFCR = DMA_IFCR_CHTIF3;
  94.         buf_base=dma_wavebuf;
  95.     }
  96.     if(DMA1->ISR & DMA_ISR_TCIF3)
  97.     {
  98.         DMA1->IFCR = DMA_IFCR_CTCIF3;
  99.         buf_base=dma_wavebuf+1024;
  100.     }
  101.     for(i=0;i<256;i++)
  102.     {
  103.         int x;
  104.         phase1+=freq1;
  105.         phase1%=48000;
  106.         if(phase1>24000)
  107.         {
  108.             if(phase1>36000)
  109.                 x=-sinetable[48000-phase1];
  110.             else
  111.                 x=-sinetable[phase1-24000];
  112.         }
  113.         else
  114.         {
  115.             if(phase1>12000)
  116.                 x=sinetable[24000-phase1];
  117.             else
  118.                 x=sinetable[phase1];
  119.         }
  120.         buf_base[4*i]=x>>16;
  121.         buf_base[4*i+1]=x&0xffff;
  122.         phase2+=freq2;
  123.         phase2%=48000;
  124.         if(phase2>24000)
  125.         {
  126.             if(phase2>36000)
  127.                 x=-sinetable[48000-phase2];
  128.             else
  129.                 x=-sinetable[phase2-24000];
  130.         }
  131.         else
  132.         {
  133.             if(phase2>12000)
  134.                 x=sinetable[24000-phase2];
  135.             else
  136.                 x=sinetable[phase2];
  137.         }
  138.         buf_base[4*i+2]=x>>16;
  139.         buf_base[4*i+3]=x&0xffff;
  140.     }
  141. }
复制代码
正弦函数我并没有在程序中计算,而是存了一个表,用查表法。一方面是ROM刚好够用,另一方面是对Cortex-M0的浮点库不熟悉就没有此时尝试。采样频率是48000Hz, 如果要输出1Hz的信号,那么一个周期需要48000个样本,根据对称性,只需要存储1/4的正弦就够了。一个样本用24-bit足够,16-bit也差不多(截尾会有一些谐波),我任性地直接用了32-bit整型。计算1Hz的整数倍就很简单,相当于查表时每次跳N个就行了。

最后再强调一下使用CS4344等Sigma-Delta DAC相对于MCU片上DAC的优点:一个是量化精度比较高,谐波失真也低不少。二是独立于MCU,减少了干扰。三是降低了数据处理量,因为DAC带有8倍的上采样,硬件给你完成了插值。(不然,在48k采样率下,直接用零阶保持出来一个20k的信号是什么样子?)

sine_gen.zip

61.66 KB, 下载次数: 28

源程序

最新回复

楼主,下面准备 DIY 什么呀  详情 回复 发表于 2016-7-28 21:51

赞赏

3

查看全部赞赏

点赞 关注(3)
 

回复
举报

1950

帖子

4

TA的资源

版主

沙发
 
DIY成这样,我也是佩服了,能开design house 了

48KHz品质可以了,CD也就44.1Khz

记得以前搞得时候,看到过Cirrus Logic的DAC,
初期话的时候输出可能会有西瓜状的调整波形,下游不把他cut会有一个小的嚣叫
这个在DS都没写,问FAE才说 是的,有
个人签名MicroPython中文社区https://micropython.org.cn/forum/  
 
 

回复

2万

帖子

0

TA的资源

超级版主

板凳
 
楼主留出了几个并行口,猜测是扩展小键盘和数码管显示用。扩展小键盘和显示后才好用,否则使用起来太费劲。
 
 
 

回复

1万

帖子

25

TA的资源

裸片初长成(高级)

4
 
做点实用的东西,不错。

 
 
 

回复

2万

帖子

0

TA的资源

超级版主

5
 
和模拟信号源(例如文氏电桥振荡器)比较,这种电路受时钟频率限制,无法输出更高频率的信号,而文氏电桥之类很容易达到例如1MHz。
当然,作为音频信号,已经足够了。
 
 
 

回复

1950

帖子

4

TA的资源

版主

6
 
楼主,下面准备 DIY 什么呀

点评

晚上正在弄这个。拖了很久了,这周末得交差了  详情 回复 发表于 2016-7-28 22:50
个人签名MicroPython中文社区https://micropython.org.cn/forum/  
 
 
 

回复

1377

帖子

2

TA的资源

五彩晶圆(初级)

7
 
5525 发表于 2016-7-28 21:51
楼主,下面准备 DIY 什么呀

晚上正在弄这个。拖了很久了,这周末得交差了

replace.jpg (126.19 KB, 下载次数: 0)

replace.jpg
 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表