经过一段时间的努力我的基于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。低噪声麦克风偏置电压发生器可为绝大部分驻极体麦克风提供偏置。
模块实物图:
其原理图如下:
从上原理图上看只要把Output接ADC的任何一个输入通道就可以,这里我选择ADC_CHANNEL_10通道。PC0管脚。
要实现8K采样,就要使用ADC的外部触发,这里采用TIMER1的通道1来触发ADC。
配置代码如下:
- void adc_config(void)
- {
- //配置ADC触发源TIMER1 CH1 event select
- adc_external_trigger_source_config(ADC_REGULAR_CHANNEL,ADC_EXTTRIG_REGULAR_T1_CH1);
- adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
- adc_channel_length_config(ADC_REGULAR_CHANNEL,1);
-
- adc_regular_channel_config(0,ADC_CHANNEL_10,ADC_SAMPLETIME_71POINT5);
- adc_external_trigger_config(ADC_REGULAR_CHANNEL,ENABLE);
-
- adc_enable();
- delay_10us(2);
- adc_calibration_enable();
-
- //开启DMA模式
- adc_dma_mode_enable();
- }
这里使用了DMA,本来打算是用双缓冲来做,但是调试的时候,发现在进行缓冲切换的时候会语音造成影响,使得语音出现卡顿。所以就没有采用双缓冲,只接采集过来就发送,接收端也变的简单些,收到数据就进行DAC转换,不再需要使用定时器了。而且还能保证语音同步,不会出现延时。DMA配置代码如下:
- void dma_config(void)
- {
- dma_parameter_struct dmaParam;
- //DMA_CH0 ADC
- dma_deinit(DMA_CH0);
- dmaParam.periph_addr = (uint32_t)&ADC_RDATA;
- dmaParam.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
- dmaParam.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
- dmaParam.memory_addr = (uint32_t)&wav_value;
- dmaParam.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
- dmaParam.memory_width = DMA_MEMORY_WIDTH_16BIT;
- dmaParam.direction = DMA_PERIPHERAL_TO_MEMORY;
- dmaParam.number = 1;
- dmaParam.priority = DMA_PRIORITY_HIGH;
- dma_init(DMA_CH0,&dmaParam);
-
- dma_circulation_enable(DMA_CH0);
- dma_interrupt_enable(DMA_CH0,DMA_INT_FTF);
- dma_channel_enable(DMA_CH0);
- }
然后在DMA中断里通知前台,数据已经准备就维,可以进行发送。由前台代码负责把数据发送出去。
中断代码如下:
- void DMA_Channel0_IRQHandler(void)
- {
- extern uint8_t StartSend;
- /* ADPCM 编码
- uint8_t d1 = ADPCM_Encode(adc_value);
- uint16_t d2 = ADPCM_Decode(d1);
- */
- //发送音频数据
- StartSend = 1;
-
- dma_interrupt_flag_clear(DMA_CH0,DMA_INT_FLAG_FTF);
- }
这里发现了一个GD32F350固件库DMA驱动的一个反人类的设计
根据以往的经验,这里应该是个指针才对,不知道GD32的固件库工程师是怎么想的,要在这里传一个结构体实体。只有自己动手把它改过来了。
TIIMER1的配置代码如下:
- timer_parameter_struct timParam;
- //Timer1 8KHz for ADC Sample.
- timParam.prescaler = 53;
- timParam.alignedmode = TIMER_COUNTER_EDGE;
- timParam.counterdirection = TIMER_COUNTER_UP;
- timParam.period = 124; //124 8k, 44 22k
- timParam.clockdivision = TIMER_CKDIV_DIV1;
- timParam.repetitioncounter = 0;
- timer_init(TIMER1,&timParam);
-
- timer_parameter_struct timParam;
- //Timer1 8KHz for ADC Sample.
- timParam.prescaler = 53;
- timParam.alignedmode = TIMER_COUNTER_EDGE;
- timParam.counterdirection = TIMER_COUNTER_UP;
- timParam.period = 124; //124 8k, 44 22k
- timParam.clockdivision = TIMER_CKDIV_DIV1;
- timParam.repetitioncounter = 0;
- timer_init(TIMER1,&timParam);
这里主要是配置8K的ADC触发。好了今天就到这里,
下篇分享2.4G的语音传输。
本帖最后由 ketose 于 2018-9-27 23:15 编辑