为什么用SD+DMA+DAC播放WAV文件只有哒哒哒的声音

2019-07-21 06:36发布

我是直接在开发板的PA4引脚和一个GND引脚上接的一个8欧0.5瓦的小喇叭,喇叭上还串联了一个105的电容,播放的WAV文件是8位22KHz单声道PCM格式的文件,DMA代码是这样的
  DMA_DeInit(DMA2_Channel3);
  DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_Address; // 单通道8位右对齐     数据的来源
  DMA_InitStructure.DMA_BufferSize = BUFF_SIZE; // 缓冲大小
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 8位数据宽度
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8位数据宽度
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Wavebuffer; // 内存基址     数据要写到哪里去
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 外设作为数据传输的目的地
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址寄存器不变
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址寄存器递增
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 工作在正常缓存模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA通道x拥有高优先级
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // DMA通道x没有设置为内存到内存传输
  DMA_Init(DMA2_Channel3, &DMA_InitStructure);
  DMA_Cmd(DMA2_Channel3, ENABLE);     //使能DMA2通道3
  DMA_ITConfig(DMA2_Channel3, DMA_IT_TC | DMA_IT_HT, ENABLE);

TIM6的代码是这样的:
  TIM_DeInit(TIM6);
  TIM_SetAutoreload(TIM6, TIM6ARRValue);        //TIM6ARRValue的值是72000000/22050
  TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
  TIM_Cmd(TIM6, ENABLE);

DAC的代码是这样的

  DAC_DeInit();
  DAC_StructInit(&DAC_InitStructure);

  DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; //选择定时器6做外部触发源
  DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; // 无波形产生
  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; // 禁止输出缓冲

  DAC_Init(DAC_Channel_1, &DAC_InitStructure);         //DAC通道1初始化
  DAC_Cmd(DAC_Channel_1, ENABLE);            //使能DAC通道1:一旦使能了DAC通道1,PA.04将自动连接到DAC转换器
  DAC_DMACmd(DAC_Channel_1, ENABLE);       //使能DMA用于DAC通道1

  DAC_Init(DAC_Channel_2, &DAC_InitStructure);   //DAC通道2初始化
  DAC_Cmd(DAC_Channel_2, ENABLE);             //使能DAC通道2:一旦使能了DAC通道2,PA.05将自动连接到DAC转换器   
  DAC_DMACmd(DAC_Channel_2, ENABLE);        //使能DMA用于DAC通道2

然后使用了中断
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Channel3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =3;//NVIC_PriorityGroup_1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;// NVIC_PriorityGroup_4;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

在中断函数中写数据

void PlayReadBlock(u16 offset)
{
    FRESULT res;
    res = f_read(&fp, Wavebuffer + offset, 512, &br);
    if((res!=FR_OK) || (br==0))
    {
      f_close(&fp);
    }
    else if (br < 512)
    {
        f_close(&fp);
        TIM_ITConfig(TIM6, TIM_IT_Update, DISABLE);    //禁止TIM6更新中断
          TIM_Cmd(TIM6, DISABLE);       //禁止TIM6    
    }    
}

void DMA2_Channel3_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA2_IT_HT3) == SET)
    {    
        DMA_ClearITPendingBit(DMA2_IT_HT3);
        DMA_ClearFlag(DMA2_FLAG_HT3);
        //检查过半标志,有效,清除标志,处理前半段数据   
        PlayReadBlock(0);
    }
    LED3 = !LED3;
    if (DMA_GetITStatus(DMA2_IT_TC3) == SET)
    {  
        DMA_ClearITPendingBit(DMA2_IT_TC3);
        DMA_ClearFlag(DMA2_FLAG_TC3);    
        //检查完成标志,有效,清除标志,处理后半段数据
        PlayReadBlock(BUFF_SIZE>>1);
      }
}

基本上就是这些了,代码都能正常运行中断也能正常进入,不知道是不是我的配置有问题,或者是计算有问题,本人是菜鸟才入门STM32请教坛子里的各位朋友,谢谢了!
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
11条回答
ricefat
1楼-- · 2019-07-21 09:29
512字节数据量在8位22k下大概够维持23ms的播放。检测读取时间是不是能跟上播放速度,另外我不是太清楚你这样同时读取和写入同一个数组行不行,印象中音频还是应该用双缓冲。
csf4824521
2楼-- · 2019-07-21 13:21
回复【2楼】ricefat:
---------------------------------
如果您说的是单独的f_read的读取SD卡文件的时间的话,我测试过每秒有200K以上。
您说的双缓冲是这样吗?
    res = f_read(&fp, Wavebuffer2, BUF_SIZE, &br);
    if((res!=FR_OK) || (br==0))
    {
      f_close(&fp);
  //LED1 = !LED1;
      return Invalid_WAVE_File;
    }
    
    if(WaveDataLength)
    {
      WaveDataLength -= BUF_SIZE;
    }
    if(WaveDataLength < BUF_SIZE)
    {
      WaveDataLength = 0;
    }

    while(DMA_GetFlagStatus(DMA2_FLAG_TC3) == RESET)   //转换完成标志
    {
      tmp=(u16)((u32)((WAVE_Format.DataSize-WaveDataLength)*400)/WAVE_Format.DataSize);
      LCD_DrawYLine(tmp, 202, 213);
    }
    
    DMA2->IFCR = DMA2_FLAG_TC3;  // 清除DMA2中断标志
    DMA2_Channel3->CCR = 0x0;  // 清除DMA2通道3设置寄存器配制
    DMA2_Reconfig((u32)&Wavebuffer2);

    res=f_read(&fp, Wavebuffer, BUF_SIZE, &br);
    if((res!=FR_OK) || (br==0))
    {
      f_close(&fp);
      return Invalid_WAVE_File;
    }

    if(WaveDataLength)
    {
      WaveDataLength -= BUF_SIZE;
    }
    if(WaveDataLength < BUF_SIZE)
    {
      WaveDataLength = 0;
    }
    
    while(DMA_GetFlagStatus(DMA2_FLAG_TC3) == RESET)
    {
      tmp=(u16)((u32)((WAVE_Format.DataSize-WaveDataLength)*400)/WAVE_Format.DataSize);
      LCD_DrawYLine(tmp, 202, 213);
    } 
    DMA2->IFCR = DMA2_FLAG_TC3;  // 清除DMA2中断标志
    DMA2_Channel3->CCR = 0x0;  // 清除DMA2通道3设置寄存器配制
    DMA2_Reconfig((u32)&Wavebuffer);
不知道会不会是喇叭的问题我之前用网上找到的一个代码,并且他介绍了直接移植的方法,编译通过后输出到喇叭上的声音也是哒哒哒的声音,我看人家都是接的音响或者耳机,不知道能不能驱动起来这个喇叭
csf4824521
3楼-- · 2019-07-21 16:02
问题还没有解决,顶一下自己
正点原子
4楼-- · 2019-07-21 21:41
 精彩回答 2  元偷偷看……
csf4824521
5楼-- · 2019-07-22 02:34
回复晚了,谢谢ricefat和原子兄,原因找到了,自己都觉得丢人!我的喇叭和开发板的引脚接错了,所以一直没有声音,还需要学习啊,确实很难驱动,声音非常小,用一个C9014的三极管和4K电阻接上喇叭声音大了不少,但还是达不到要求,可能需要上驱动电路,还有ricefat兄我的应该就是双缓冲,因为我是1024的缓冲区,先写前512判断过半再写后512判断结束全部放在DMA中断中,实现后音频播放效果不错,再次感谢二位
xiang90721
6楼-- · 2019-07-22 06:41
回复【5楼】正点原子:
---------------------------------
原子大哥,1:使用DMA+DAC+TIM6这样的传输方式,因为外设地址不变,有木有可能DMA之前传送的一个数据还未数模转换,DMA传送的数据又到了将之前的数据覆盖了???
2:DMA使用正常模式(即不使用循环传送),缓冲区数据传送完成后将停止DMA传送,如果再次启动DMA,是不是又是从缓冲区首地址开始传送?
求解,谢谢原子大哥!

一周热门 更多>