最近在做ADC的音频采样,之前做8位,8K,单声道采样的时候都是没有问题的,现在想改成16位、16K立体声采样,采集直接手机播放的音乐文件,现在的问题是有杂音且速度很快。我的第一个问题是想确定一下8位的音频采样是不是是无符号的,而16位是要考虑符号?
我现在是按有符号的方式来考虑的,下面是我的相关代码首先是ADC初始化代码:
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //AD单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_13Cycles5); //Channel1~2,即PA1、PA2用来采集通过功放输入的音频数据
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_13Cycles5);
然后是控制采样率的TIM2,因为我是系统默认的初始化所以APB1应该是2分频,所以TIM2应该是72M的时钟
TIM_TimeBaseStructure.TIM_Period = 24;
TIM_TimeBaseStructure.TIM_Prescaler = 2880000/16000-1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分频因子
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //定时器初始化函数,初始化TIM2
然后是DMA的代码:
u16 ADCConvertedValue[Adc_Buff_Size];
#define ADC_Channels 2
#define A_SECTOR 512
#define ADC_SamplingRate 16000
#define FileSamplingRate 16000
#define Adc_Buff_Size (2 * ADC_SamplingRate / FileSamplingRate * A_SECTOR * ADC_Channels) //这里是上下半区所以乘了2
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //DMA对应的外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADCConvertedValue; //内存存储基地址,对应的一个数组
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //DMA转换模式为SRC模式,由外设搬移到内存
DMA_InitStructure.DMA_BufferSize = Adc_Buff_Size; //DMA缓存大小(设置DMA在传输时缓冲区的长度)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //接收一次数据后设备地址禁止后移也就是说地址保持不变(设置DMA的外设递增模式)
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //关闭接收一次数据后目标内存地址后移(设置DMA内存递增模式)
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据宽度半字
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据宽度半字
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA的传输模式为循环
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA的通道拥有高优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
然后是DMA的中断函数,我采用的是上下半区的方式:
void DMA1_Channel1_IRQHandler(void)
{
if (DMA_GetITStatus(DMA1_IT_HT1) == SET){
DMA_ClearITPendingBit(DMA1_IT_HT1);
DMA_ClearFlag(DMA1_FLAG_HT1);
//检查过半标志,有效,清除标志,处理前半段数据
TreatData(0);
}
if (DMA_GetITStatus(DMA1_IT_TC1) == SET){
DMA_ClearITPendingBit(DMA1_IT_TC1);
DMA_ClearFlag(DMA1_FLAG_TC1);
//检查完成标志,有效,清除标志,处理后半段数据
TreatData(Adc_Buff_Size>>1);
}
}
然后是 TreatData
#define REC_DATA_LEN 1024
#define CYCLE_COUNT 512
static s16 ChannelData[REC_DATA_LEN];
s32 i, j, Pos;
s32 temp;
for(i=0,j=0; i<CYCLE_COUNT; i++){
Pos = i*2+Offset;
temp = GetADCValue(Pos+0);
temp = temp-2048;
ChannelData[j] = temp*16;
//ChannelData[j] = (GetADCValue(Pos+5)-(s16)2048)*16;
j++;
temp = GetADCValue(Pos+1);
temp = temp-2048;
ChannelData[j] = temp*16;
//ChannelData[j] = (GetADCValue(Pos+6)-(s16)2048)*16;
j++;
}
还有就是GetADCValue
u16 GetADCValue(u16 index)
{
return ADCConvertedValue[index];
}
SaveRecording();
}
最后是SaveRecording
u8 SaveRecording(void)
{
if (Is_Recording == TRUE){
u8 i, res;
u16 offset;
for (i=0; i<2; i++){
offset = i * 512;
//将录音数据分两次写入到SD卡中
res = f_write(&fdst, ChannelData+offset, 512, &bw1);
if(res != FR_OK){
ErrorEvent = WriteRecordingError;
return res;
}
}
SectorCount++; //每写入一块到SD卡中,都需要进行计数
}
return 0;
}
请大家帮我看看问题出在哪里
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
首先可以肯定的是16位的采样是需要考虑符号的,我上面贴出来的整个代码的大框架是没有问题的,主要是在计算采样率和存放采样数据的内存大小上有问题。
作如下修改:
①控制采样率的TIM2,这里TIM2的时钟应该是36M,所以TIM_Prescaler应该是1440000/16000-1
②其实最关键的问题就在这里SaveRecording,之前采集正弦波的时候发现数据老是出现断层,有很大的一个跳变,后来发现是f_write的使用问题,f_write这个函数是按字节写入的,其第三个参数不是写入的数据的个数,而是写入的数据的字节数,之前8位数据写入时之所以没有出现也是因为这个原因,假设我要写入512个u8数据到SD卡的文件中,那么就相当于写512个字节的数据,但是对于s16的数据要写入1024个字节才能写完这512个数据,所以问题就出在这里,当写16位数据时512个数据的实际字节数应该是1024.
小弟现在也要搞搞音频采集,能不能把你之前做的8位,8K,单声道采样程序,给小弟参考参考谢谢了!874675487@qq.com
一周热门 更多>