OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2896|回复: 5

用原子哥代码STM32F4 IIS+DMA发送数据错位

[复制链接]

2

主题

7

帖子

0

精华

新手入门

积分
16
金钱
16
注册时间
2020-3-24
在线时间
7 小时
发表于 2020-4-20 15:21:11 | 显示全部楼层 |阅读模式
6金钱
本帖最后由 吴春盈 于 2020-4-20 16:48 编辑

正点原子探索者F4 资料盘(A盘)\4,程序源码\3,标准例程-HAL库版本\实验44 录音机实验//默认是48000
代码都是照搬原子哥的,只是把双声道改为单声道,I2S2_Init和I2S2ext_RX_DMA_Init只是在上电时初始化,后面如果要播放才调用I2S_Play_Start,尝试过每次播放前也初始化DMA,问题仍在,因为程序有录音调用,所以不可能在播放前初始化I2S2_Init

1. 有点不明白的是:为什么原子哥下面配置为4播放的声音才正常,但原子哥的源码是2
I2S2_TX_DMA_Init(audiodev.i2sbuf1,audiodev.i2sbuf2,I2S_TX_DMA_BUFSIZE/4); //配置TX DMA

2. 根据wav_buffill的内容,应该每次发送的数据是左声道低字节、左声道高字节、0、0,MCLK低电平左声道时才有数据
播放多几次后,发现播放静音,开是我用示波器看
有时候MCLK低电平左声道没有数据,变成MCLK高电平右声道有数据,为什么会这样呢?


下面是代码部分

  1. #define I2S_TX_DMA_BUFSIZE    (1920)        //2*48*2(2个字节,48指采样率,2声道)               
  2. #define I2S_RX_DMA_BUFSIZE            192                //定义RX DMA 数组大小

  3. //音乐播放控制器
  4. typedef  struct
  5. {  
  6.         //2个I2S解码的BUF
  7.         u8 i2sbuf1[I2S_TX_DMA_BUFSIZE];
  8.         u8 i2sbuf2[I2S_TX_DMA_BUFSIZE];
  9.         u8 i2rbuf1[I2S_RX_DMA_BUFSIZE];
  10.         u8 i2rbuf2[I2S_RX_DMA_BUFSIZE];        
  11.         u32 current_read_wav_flash_size;//当前读wav保存在flash里的位置
  12. }__audiodev;
  13. __audiodev audiodev;        //音乐播放控制器
  14. void DMA1_Stream4_IRQHandler(void)
  15. {  
  16.     if(__HAL_DMA_GET_FLAG(&I2S2_TXDMA_Handler,DMA_FLAG_TCIF0_4)!=RESET) //DMA传输完成
  17.     {
  18.         __HAL_DMA_CLEAR_FLAG(&I2S2_TXDMA_Handler,DMA_FLAG_TCIF0_4);     //清除DMA传输完成中断标志位
  19.                 if(i2s_tx_callback!=NULL) i2s_tx_callback();        //执行回调函数,读取数据等操作在这里面处理  
  20.     }
  21. }
  22. u8 buff[I2S_TX_DMA_BUFSIZE/4];
  23. u32 wav_buffill(u8 *buf)
  24. {
  25.         int i, len;
  26.         
  27.         u8 *p;
  28.                 len = I2S_TX_DMA_BUFSIZE/4;
  29.                 sFLASH_ReadBuffer(buff, WAV_Address + audiodev.current_read_wav_flash_size, len);//flash保存的是单声道16位的wav数据
  30.                 p = buff;
  31.                
  32.                 for(i = 0; i < len; i++)
  33.                 {
  34.                         buf[4*i+0] = p[0];//左声道低字节
  35.                         buf[4*i+1] = p[1];//左声道高字节
  36.                         buf[4*i+2] = 0;
  37.                         buf[4*i+3] = 0;
  38.                         p+=2;
  39.                 }
  40.                 audiodev.current_read_wav_flash_size += (len);
  41.                
  42.                 if(audiodev.current_read_wav_flash_size >= wav_param.sizelen)
  43.                 {
  44.                         return 0;//read all wav data finish
  45.                 }
  46.         }
  47.         

  48.         return I2S_TX_DMA_BUFSIZE;
  49. }  

  50. //WAV播放时,I2S DMA传输回调函数
  51. void wav_i2s_dma_tx_callback(void)
  52. {   
  53.         if(DMA1_Stream4->CR&(1<<19))
  54.         {
  55.                 chx_param[0].playfillnum=wav_buffill(audiodev.i2sbuf1);//填充buf1
  56.         }
  57.         else
  58.         {
  59.                 chx_param[0].playfillnum=wav_buffill(audiodev.i2sbuf2);//填充buf2
  60.         }
  61. }


  62. //I2S开始播放
  63. void I2S_Play_Start(void)
  64. {           
  65.         printf("I2S_Play_Start __HAL_DMA_ENABLE\n");
  66.         __HAL_DMA_ENABLE(&I2S2_TXDMA_Handler);//开启DMA TX传输                          
  67. }

  68. u8 I2S2_SampleRate_Set(u32 samplerate)
  69. {   

  70.     u8 i=0;
  71.         u32 tempreg=0;
  72.     RCC_PeriphCLKInitTypeDef RCCI2S2_ClkInitSture;  
  73.         
  74.         for(i=0;i<(sizeof(I2S_PSC_TBL)/10);i++)//看看改采样率是否可以支持
  75.         {
  76.                 if((samplerate/10)==I2S_PSC_TBL[0])break;
  77.         }
  78.     if(i==(sizeof(I2S_PSC_TBL)/10))return 1;//搜遍了也找不到
  79.         
  80.     RCCI2S2_ClkInitSture.PeriphClockSelection=RCC_PERIPHCLK_I2S;        //外设时钟源选择
  81.     RCCI2S2_ClkInitSture.PLLI2S.PLLI2SN=(u32)I2S_PSC_TBL[1];            //设置PLLI2SN
  82.     RCCI2S2_ClkInitSture.PLLI2S.PLLI2SR=(u32)I2S_PSC_TBL[2];            //设置PLLI2SR
  83.         RCCI2S2_ClkInitSture.PLLI2S.PLLI2SM=12;//12m晶振
  84.         HAL_RCCEx_PeriphCLKConfig(&RCCI2S2_ClkInitSture);                     //设置时钟
  85.         
  86.         RCC->CR|=1<<26;                                        //开启I2S时钟
  87.         while((RCC->CR&1<<27)==0);                //等待I2S时钟开启成功.
  88.         tempreg=I2S_PSC_TBL[3]<<0;        //设置I2SDIV
  89.         tempreg|=I2S_PSC_TBL[4]<<8;        //设置ODD位
  90.         tempreg|=1<<9;                                        //使能MCKOE位,输出MCK
  91.         SPI2->I2SPR=tempreg;                        //设置I2SPR寄存器
  92.         return 0;
  93. }  


  94. void I2S2_Init(u32 freq)
  95. { //飞利浦标准,主机发送,时钟低电平有效,16位帧长度
  96.         I2S2_Handler.Instance=SPI2;
  97.         I2S2_Handler.Init.Mode=I2S_MODE_MASTER_TX;                                        //IIS模式
  98.         I2S2_Handler.Init.Standard=I2S_STANDARD_PHILIPS;                        //IIS标准
  99.         I2S2_Handler.Init.DataFormat=I2S_DATAFORMAT_16B;                //IIS数据长度
  100.         I2S2_Handler.Init.MCLKOutput=I2S_MCLKOUTPUT_ENABLE;        //主时钟输出使能
  101.         I2S2_Handler.Init.AudioFreq=I2S_AUDIOFREQ_DEFAULT;        //IIS频率设置必须为I2S_AUDIOFREQ_DEFAULT否则调出来的频率不对
  102.         I2S2_Handler.Init.CPOL=I2S_CPOL_LOW;                        //空闲状态时钟电平
  103.         I2S2_Handler.Init.ClockSource=I2S_CLOCK_PLL;                //IIS时钟源为PLL
  104.         I2S2_Handler.Init.FullDuplexMode=I2S_FULLDUPLEXMODE_ENABLE;        //IIS全双工
  105.         HAL_I2S_Init(&I2S2_Handler);
  106.         
  107.         SPI2->CR2|=1<<1;                                                                        //SPI2 TX DMA请求使能.
  108.         I2S2ext->CR2|=1<<0;                                                                        //I2S2ext RX DMA请求使能.
  109.         __HAL_I2S_ENABLE(&I2S2_Handler);                                        //使能I2S2        
  110.         __HAL_I2SEXT_ENABLE(&I2S2_Handler);                                        //使能I2S2ext

  111.         I2S2_SampleRate_Set(freq);
  112. }


  113. void I2S2_TX_DMA_Init(u8* buf0,u8 *buf1,u16 num)
  114. {  
  115.     __HAL_RCC_DMA1_CLK_ENABLE();                                                    //使能DMA1时钟
  116.     __HAL_LINKDMA(&I2S2_Handler,hdmatx,I2S2_TXDMA_Handler);                         //将DMA与I2S联系起来
  117.         
  118.     I2S2_TXDMA_Handler.Instance=DMA1_Stream4;                                       //DMA1数据流4                     
  119.     I2S2_TXDMA_Handler.Init.Channel=DMA_CHANNEL_0;                                  //通道0
  120.     I2S2_TXDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH;                         //存储器到外设模式
  121.     I2S2_TXDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE;                             //外设非增量模式
  122.     I2S2_TXDMA_Handler.Init.MemInc=DMA_MINC_ENABLE;                                 //存储器增量模式
  123.     I2S2_TXDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;           //外设数据长度:16位
  124.     I2S2_TXDMA_Handler.Init.MemDataAlignment=DMA_PDATAALIGN_HALFWORD;            //存储器数据长度:16位
  125.     I2S2_TXDMA_Handler.Init.Mode=DMA_CIRCULAR;                                      //使用循环模式
  126.     I2S2_TXDMA_Handler.Init.Priority=DMA_PRIORITY_HIGH;                             //高优先级
  127.     I2S2_TXDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;                          //不使用FIFO
  128.     I2S2_TXDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE;                             //存储器单次突发传输
  129.     I2S2_TXDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE;                          //外设突发单次传输
  130.     HAL_DMA_DeInit(&I2S2_TXDMA_Handler);                                            //先清除以前的设置
  131.     HAL_DMA_Init(&I2S2_TXDMA_Handler);                                                    //初始化DMA

  132.     HAL_DMAEx_MultiBufferStart(&I2S2_TXDMA_Handler,(u32)buf0,(u32)&SPI2->DR,(u32)buf1,num);//开启双缓冲
  133.     __HAL_DMA_DISABLE(&I2S2_TXDMA_Handler);                                         //先关闭DMA
  134.     delay_us(1000);                                                                   //10us延时,防止-O2优化出问题         
  135.     __HAL_DMA_ENABLE_IT(&I2S2_TXDMA_Handler,DMA_IT_TC);                             //开启传输完成中断
  136.     __HAL_DMA_CLEAR_FLAG(&I2S2_TXDMA_Handler,DMA_FLAG_TCIF0_4);                     //清除DMA传输完成中断标志位
  137.         
  138.         HAL_NVIC_SetPriority(DMA1_Stream4_IRQn,0,0);                                    //DMA中断优先级
  139.     HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
  140. }
复制代码


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

2

主题

7

帖子

0

精华

新手入门

积分
16
金钱
16
注册时间
2020-3-24
在线时间
7 小时
 楼主| 发表于 2020-4-20 16:49:49 | 显示全部楼层
        初始化这样
I2S2_Init(wav_param.freq);
       
        i2s_tx_callback=wav_i2s_dma_tx_callback;//回调函数指wav_i2s_dma_callback
        I2S2_TX_DMA_Init(audiodev.i2sbuf1,audiodev.i2sbuf2,I2S_TX_DMA_BUFSIZE/4); //配置TX DMA
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165475
金钱
165475
注册时间
2010-12-1
在线时间
2115 小时
发表于 2020-4-21 00:59:26 | 显示全部楼层
帮顶
回复

使用道具 举报

2

主题

7

帖子

0

精华

新手入门

积分
16
金钱
16
注册时间
2020-3-24
在线时间
7 小时
 楼主| 发表于 2020-4-21 09:47:30 | 显示全部楼层

原子哥也看不出问题来吗
回复

使用道具 举报

2

主题

7

帖子

0

精华

新手入门

积分
16
金钱
16
注册时间
2020-3-24
在线时间
7 小时
 楼主| 发表于 2020-4-22 09:33:41 | 显示全部楼层
没有人知道吗?
回复

使用道具 举报

2

主题

7

帖子

0

精华

新手入门

积分
16
金钱
16
注册时间
2020-3-24
在线时间
7 小时
 楼主| 发表于 2020-4-22 09:45:32 | 显示全部楼层
外部用的是12m晶振
Stm32_Clock_Init里,
        plln = 192;
        pllm = 12;
        pllp = 2;
        pllq = 4;
改成
        plln = 336;
        pllm = 12;
        pllp = 4;
        pllq = 7;       
问题仍在
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-2 07:34

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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