[经验分享] 【基于GDF350的无线数字对讲机】 1、ADC+DMA语音信号采集

ketose   2018-9-27 22:59 楼主
经过一段时间的努力我的基于GD32F350无现数字对讲也算是成功了。 首先说下主控:GD32F350系列新品最高主频可达108MHz并支持DSP指令运算。配备了16KB到128KB的内置Flash及4KB到16KB的SRAM,内核访问闪存高速零等待,在最高主频下的工作性能可达135DMIPS,同主频下的代码执行效率相比市场同类Cortex-M3产品提高30%,相比Cortex-M0+产品更提高50%以上。 从今天开始分享自己在制作过程中的一些心得和体会。基本原理就是通过ADC 8K采样,然后在OLED上显示声音频普,再通过2.4G无线传输至别一端,通知DAC把语音信号播放出来。原理就这样了,没什么好说的。 要进行语音传输首先第一步就是采集语音信号,因为GD32F50开发板本身没有集成数字MIC,所以要外接一个MIC模块。这里我选用的是GY-MAX9814这个语音模块。 MAX9814是一款低成本、高性能麦克风放大器,具有自动增益控制(AGC)和低噪声麦克风偏置。器件具有低噪声前端放大器、可变增益放大器(VGA)、输出放大器、麦克风偏置电压发生器和AGC控制电路。低噪声前置放大器具有12dB固定增益;VGA增益根据输出电压和AGC门限在20dB至0dB间自动调节。输出放大器提供可选择的8dB、18dB和28dB增益。在未压缩的情况下,放大器的级联增益为40dB、50dB或60dB。输出放大器增益由一个三态数字输入编程。AGC门限由一个外部电阻分压器控制,动作/释放时间由单个电容编程。动作/释放时间比由一个三态数字输入设置。AGC保持时间固定为30ms。低噪声麦克风偏置电压发生器可为绝大部分驻极体麦克风提供偏置。 模块实物图: TB2saynXLHlJuJjSZFtXXad7FXa_!!161518516.jpg 其原理图如下: 9814_sch.jpg 从上原理图上看只要把Output接ADC的任何一个输入通道就可以,这里我选择ADC_CHANNEL_10通道。PC0管脚。 QQ截图20180927222423.png 要实现8K采样,就要使用ADC的外部触发,这里采用TIMER1的通道1来触发ADC。 配置代码如下:
  1. void adc_config(void)
  2. {
  3. //配置ADC触发源TIMER1 CH1 event select
  4. adc_external_trigger_source_config(ADC_REGULAR_CHANNEL,ADC_EXTTRIG_REGULAR_T1_CH1);
  5. adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
  6. adc_channel_length_config(ADC_REGULAR_CHANNEL,1);
  7. adc_regular_channel_config(0,ADC_CHANNEL_10,ADC_SAMPLETIME_71POINT5);
  8. adc_external_trigger_config(ADC_REGULAR_CHANNEL,ENABLE);
  9. adc_enable();
  10. delay_10us(2);
  11. adc_calibration_enable();
  12. //开启DMA模式
  13. adc_dma_mode_enable();
  14. }
这里使用了DMA,本来打算是用双缓冲来做,但是调试的时候,发现在进行缓冲切换的时候会语音造成影响,使得语音出现卡顿。所以就没有采用双缓冲,只接采集过来就发送,接收端也变的简单些,收到数据就进行DAC转换,不再需要使用定时器了。而且还能保证语音同步,不会出现延时。DMA配置代码如下:
  1. void dma_config(void)
  2. {
  3. dma_parameter_struct dmaParam;
  4. //DMA_CH0 ADC
  5. dma_deinit(DMA_CH0);
  6. dmaParam.periph_addr = (uint32_t)&ADC_RDATA;
  7. dmaParam.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
  8. dmaParam.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
  9. dmaParam.memory_addr = (uint32_t)&wav_value;
  10. dmaParam.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
  11. dmaParam.memory_width = DMA_MEMORY_WIDTH_16BIT;
  12. dmaParam.direction = DMA_PERIPHERAL_TO_MEMORY;
  13. dmaParam.number = 1;
  14. dmaParam.priority = DMA_PRIORITY_HIGH;
  15. dma_init(DMA_CH0,&dmaParam);
  16. dma_circulation_enable(DMA_CH0);
  17. dma_interrupt_enable(DMA_CH0,DMA_INT_FTF);
  18. dma_channel_enable(DMA_CH0);
  19. }
然后在DMA中断里通知前台,数据已经准备就维,可以进行发送。由前台代码负责把数据发送出去。 中断代码如下:
  1. void DMA_Channel0_IRQHandler(void)
  2. {
  3. extern uint8_t StartSend;
  4. /* ADPCM 编码
  5. uint8_t d1 = ADPCM_Encode(adc_value);
  6. uint16_t d2 = ADPCM_Decode(d1);
  7. */
  8. //发送音频数据
  9. StartSend = 1;
  10. dma_interrupt_flag_clear(DMA_CH0,DMA_INT_FLAG_FTF);
  11. }
这里发现了一个GD32F350固件库DMA驱动的一个反人类的设计 QQ截图20180927225131.png 根据以往的经验,这里应该是个指针才对,不知道GD32的固件库工程师是怎么想的,要在这里传一个结构体实体。只有自己动手把它改过来了。 TIIMER1的配置代码如下:
  1. timer_parameter_struct timParam;
  2. //Timer1 8KHz for ADC Sample.
  3. timParam.prescaler = 53;
  4. timParam.alignedmode = TIMER_COUNTER_EDGE;
  5. timParam.counterdirection = TIMER_COUNTER_UP;
  6. timParam.period = 124; //124 8k, 44 22k
  7. timParam.clockdivision = TIMER_CKDIV_DIV1;
  8. timParam.repetitioncounter = 0;
  9. timer_init(TIMER1,&timParam);
  10. timer_parameter_struct timParam;
  11. //Timer1 8KHz for ADC Sample.
  12. timParam.prescaler = 53;
  13. timParam.alignedmode = TIMER_COUNTER_EDGE;
  14. timParam.counterdirection = TIMER_COUNTER_UP;
  15. timParam.period = 124; //124 8k, 44 22k
  16. timParam.clockdivision = TIMER_CKDIV_DIV1;
  17. timParam.repetitioncounter = 0;
  18. timer_init(TIMER1,&timParam);
这里主要是配置8K的ADC触发。好了今天就到这里, 下篇分享2.4G的语音传输。 本帖最后由 ketose 于 2018-9-27 23:15 编辑

回复评论 (2)

多谢分享!
点赞  2018-9-29 12:48
感谢分享,点赞
点赞  2018-10-19 21:33
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复