野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 15998|回复: 3

【求救】STM32F407通过双缓冲模式DMA向I2S传数据,为何有数据错位?

[复制链接]
发表于 2016-10-23 14:41:48 | 显示全部楼层 |阅读模式
本帖最后由 wz18th 于 2016-10-23 14:44 编辑

想用STM32F407做个播放器,由于从SD卡读取文件时间比较长,如果单缓冲DMA传输到I2S总是断,现在用双缓冲模式做测试,却发现双缓冲数据发送错位了,部分测试代码如下:

这是配置函数:
  1. void I2S_Mode_Config(uint16_t _usStandard, uint16_t _usWordLen, uint32_t _uiAudioFreq, uint16_t _usMode)
  2. {
  3.         I2S_InitTypeDef I2S_InitStructure;

  4.         if ((_usMode == I2S_Mode_SlaveTx) && (_usMode == I2S_Mode_SlaveRx))
  5.         {
  6.                 /*不支持这2种模式 */
  7.                 return;
  8.         }

  9.         /*
  10.         For I2S mode, make sure that either:
  11.         - I2S PLL is configured using the functions RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S),
  12.           RCC_PLLI2SCmd(ENABLE) and RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY).
  13.         */
  14.         {
  15.                                         uint32_t n = 0;
  16.                                         FlagStatus status = RESET;
  17.                                        
  18.                                         RCC_PLLI2SConfig(271,2);//new,手册755页表格

  19.                                         RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S);
  20.                                         RCC_PLLI2SCmd(ENABLE);

  21.                                         for (n = 0; n < 500; n++)
  22.                                         {
  23.                                                 status = RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY);
  24.                                                 if (status == 1)
  25.                                                 {
  26.                                                         break;
  27.                                                 }
  28.           }
  29.         }

  30.         /* 打开 I2S2 APB1 时钟 */
  31.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

  32.         /* 复位 SPI2 外设到缺省状态 */
  33.         SPI_I2S_DeInit(SPI2);
  34.         
  35.         /* I2S2 外设配置 */
  36.         if (_usMode == I2S_Mode_MasterTx)
  37.         {
  38.                                         I2S_StructInit(&I2S_InitStructure);
  39.                                         I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;  /* 配置I2S工作模式 */
  40.                                         I2S_InitStructure.I2S_Standard = _usStandard;    /* 接口标准 */
  41.                                         I2S_InitStructure.I2S_DataFormat = _usWordLen;   /* 数据格式,16bit */
  42.                                         I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;/* 主时钟模式 */
  43.                                         I2S_InitStructure.I2S_AudioFreq = _uiAudioFreq;  /* 音频采样频率 */
  44.                                         I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
  45.                                         I2S_Init(SPI2, &I2S_InitStructure);

  46. //                                        /* Configure the I2Sx_ext (the second instance) in Slave Receiver Mode */
  47. //                                        I2S_FullDuplexConfig(I2S2ext, &I2S_InitStructure);

  48.                                         /* 使能 SPI2/I2S2 外设 */
  49.                                         I2S_Cmd(SPI2, ENABLE);

  50. //                                        /* Enable the I2Sx_ext peripheral for Full Duplex mode */
  51. //                                        I2S_Cmd(I2S2ext, ENABLE);
  52. }
  53. }
复制代码
  1. void I2S_DMA_Config(u32 memory_address_0,u32 memory_address_1,u16 number_to_transfer)
  2. {
  3.         DMA_InitTypeDef  DMA_InitStructure;
  4.        
  5.         SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);//配置I2S TX DMA使能
  6.        
  7.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
  8.        
  9.   DMA_DeInit(DMA1_Stream4);
  10.        
  11.         while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE){}//等待DMA可配置
  12.        
  13.   /* 配置 DMA Stream */
  14.   DMA_InitStructure.DMA_Channel = DMA_Channel_0;                                                  //通道选择
  15.   DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;        //DMA外设地址
  16.   DMA_InitStructure.DMA_Memory0BaseAddr = memory_address_0;                //DMA 存储器0地址
  17.   DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                        //存储器到外设模式
  18.   DMA_InitStructure.DMA_BufferSize = number_to_transfer;                        //数据传输量
  19.   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
  20.   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                        //存储器增量模式
  21.   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据长度:16位
  22.   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//存储器数据长度:16位
  23.   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                                        //使用循环模式
  24.   DMA_InitStructure.DMA_Priority = DMA_Priority_High;                                        //高优先级
  25.   DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
  26.   DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  27.   DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;        //存储器突发单次传输
  28.   DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
  29.   DMA_Init(DMA1_Stream4, &DMA_InitStructure);//初始化DMA Stream
  30.        
  31.         //配置双缓冲的第二个缓冲区,指定第一个缓冲区先发
  32.         DMA_DoubleBufferModeConfig(DMA1_Stream4, memory_address_1,DMA_Memory_0);
  33.         DMA_DoubleBufferModeCmd(DMA1_Stream4,ENABLE);        //使能双缓冲
  34. }
复制代码
  1. void I2S_DMA_Trans_Start(void)
  2. {
  3.                 DMA_Cmd(DMA1_Stream4, ENABLE);

  4. }
复制代码



在主函数中:
  1. u16 test_buf0[8] = {0x0000, 0x1111, 0x2222, 0x3333, 0x4444 ,0x5555, 0x6666, 0x7777};
  2. u16 test_buf1[8] = {0x8888, 0x9999, 0xaaaa, 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff};

  3.         I2S_Mode_Config(I2S_Standard_Phillips,I2S_DataFormat_16b,I2S_AudioFreq_44k,I2S_Mode_MasterTx);
  4.         I2S_DMA_Config((u32)test_buf0, (u32)test_buf1, 8);        
  5.         I2S_DMA_Trans_Start();
复制代码


预想结果是按顺序输出:
0x0000, 0x1111, 0x2222, 0x3333, 0x4444 ,0x5555, 0x6666, 0x7777, 0x8888, 0x9999, 0xaaaa, 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff
然后不断自动重复……


但是结果却不是这样的,逻辑分析仪输出是这样:


逻辑分析仪捕捉结果1

逻辑分析仪捕捉结果1

逻辑分析仪捕捉结果2

逻辑分析仪捕捉结果2


请问是哪里出了问题呢?

回复

使用道具 举报

发表于 2016-10-23 23:51:48 | 显示全部楼层
你这是什么仿真软件啊?波形这么清楚???
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-10-24 15:27:07 | 显示全部楼层
算命先生 发表于 2016-10-23 23:51
你这是什么仿真软件啊?波形这么清楚???

这不是仿真,这是采集到的实际波形
回复 支持 反对

使用道具 举报

发表于 2016-11-4 23:12:11 | 显示全部楼层
我也正在做i2s的双缓冲,不过我的数据不是音频文件解码,而是想利用i2s输出一个1000Hz的正弦波,
这样就必须利用算法一边填充缓冲区,一边计算出sin的值。
发现边计算便填充特别耗cpu,缓冲区播放完了,另一个缓冲还没填充完,
我打算把sin表复位时存放到sdram中,用空间换时间。
回复 支持 反对

使用道具 举报

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

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-5-18 09:33 , Processed in 0.031477 second(s), 27 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表