打印

SPI工作在主模式接收数据可以用DMA吗?如果可以的话该怎么用呢?

[复制链接]
4918|28
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
767598314|  楼主 | 2013-8-15 08:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
SPI工作在主模式(SPI_InitStructure.SPI_Mode = SPI_Mode_Master;)接收数据可以用DMA吗?如果可以的话该怎么用呢?
沙发
feilusia| | 2013-8-15 09:23 | 只看该作者
可以。你设置的接收计数值到0,它就进中断,你想再用它就要再重新设置接收计数值。

使用特权

评论回复
板凳
767598314|  楼主 | 2013-8-15 09:27 | 只看该作者
feilusia 发表于 2013-8-15 09:23
可以。你设置的接收计数值到0,它就进中断,你想再用它就要再重新设置接收计数值。 ...

可是spi工作在主模式下的话,没接收一个数据都是要向从设备发送一个时钟信号的啊,那如果用DMA方式的话,这个时钟信号应该怎么发送出去呢?

使用特权

评论回复
地板
feilusia| | 2013-8-15 09:44 | 只看该作者
DMA自然会帮你发送时钟信号,怎么发怎么收你可以不管。
发送的计数器到0就中断,接收的计数器到0也中断。

使用特权

评论回复
5
767598314|  楼主 | 2013-8-15 10:02 | 只看该作者
DMA会自动帮我发送这个信号的啊,那太好了。你说的这个计数器到0就中断是什么意思,意思是接收缓存满了就会自动进DMA中断吗?我做adc的时候用过DMA.我只用在dma的配置里面给定一个缓存的大小,存满了以后他就自动进DMA的中断。

使用特权

评论回复
6
767598314|  楼主 | 2013-8-15 10:05 | 只看该作者
feilusia 发表于 2013-8-15 09:44
DMA自然会帮你发送时钟信号,怎么发怎么收你可以不管。
发送的计数器到0就中断,接收的计数器到0也中断。 ...

对了,还有一个问题,就是我的数据是否到来是通过外部中断告诉cpu的,可以让外部中断来触发spi让它去接收数据吗?(就像用定时器去触发DAC一样)

使用特权

评论回复
7
feilusia| | 2013-8-15 10:12 | 只看该作者
你去看DMA的CNDTR寄存器,那个就是设置你要发送或接受的数量。比如你设置10,那就接10个数就进中断(如果你开了中断)。
SPI自己有中断,一接收到数据就进入中断。

使用特权

评论回复
8
767598314|  楼主 | 2013-8-15 10:21 | 只看该作者
好的,谢谢你,我先试试。你可以加我为qq好友吗?我没别的意思,我怕等会儿遇到问题时找不到你了!

使用特权

评论回复
9
767598314|  楼主 | 2013-8-15 10:21 | 只看该作者
feilusia 发表于 2013-8-15 10:12
你去看DMA的CNDTR寄存器,那个就是设置你要发送或接受的数量。比如你设置10,那就接10个数就进中断(如果你 ...

我的qq号是767598314     验证答案是:张春

使用特权

评论回复
10
feilusia| | 2013-8-15 10:24 | 只看该作者
767598314 发表于 2013-8-15 10:21
我的qq号是767598314     验证答案是:张春

上班不方便聊QQ,有问题这里问吧。

使用特权

评论回复
11
767598314|  楼主 | 2013-8-15 10:29 | 只看该作者
好吧,那你记得来看我的帖子哦,弄好了我也会告诉你的

使用特权

评论回复
12
song19881218| | 2013-8-15 13:02 | 只看该作者
你确定DMA会帮你发送时钟信号?我记得以前试过貌似不行,要开接收DMA貌似发送DMA也要开了,在收之前必须先使能发送DMA。

使用特权

评论回复
13
feilusia| | 2013-8-15 14:31 | 只看该作者
song19881218 发表于 2013-8-15 13:02
你确定DMA会帮你发送时钟信号?我记得以前试过貌似不行,要开接收DMA貌似发送DMA也要开了,在收之前必须先 ...

没注意到LZ说是主设备,那是要发送和接收都开。我之前做的是双机通信,所以要发数据的自动变为主机,要收数据的变从机。

使用特权

评论回复
14
767598314|  楼主 | 2013-8-15 16:26 | 只看该作者
DMA不能自动帮忙发送时钟信号啊,难怪我试了一早上,死活不行。发送和接收DMA同时开是为啥啊?我应该怎么去配置啊,我现在数据是在外部中断里面接收的,代码如下:
void EXTI9_5_IRQHandler(void)               
{
  if( EXTI_GetITStatus(EXTI_Line6) != RESET)
  {
    INT8U i,temp;
    temp = CCxxx0_RXFIFO | READ_BURST;                //写入要读的配置寄存器地址和读命令
    SPI_FLASH_CS_LOW();                         //将片选信号拉低以后去读取数据               
    while (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_SO));
    SPI_FLASH_SendByte(temp);   
    for (i = 0; i < 64; i++)
    {
      buffer[i] = SPI_FLASH_SendByte(0);              //到SPI_DR里面去将数据取回来
    }
    SPI_FLASH_CS_HIGH();  
    halSpiStrobe(CCxxx0_SFRX);
    halSpiStrobe(CCxxx0_SRX);
    EXTI_ClearITPendingBit(EXTI_Line6);

  }
}

使用特权

评论回复
15
song19881218| | 2013-8-15 17:37 | 只看该作者

你可以看一下,这是我以前弄的,我验证的可以,你挑有用

本帖最后由 song19881218 于 2013-8-15 17:40 编辑
767598314 发表于 2013-8-15 16:26
DMA不能自动帮忙发送时钟信号啊,难怪我试了一早上,死活不行。发送和接收DMA同时开是为啥啊?我应该怎么去 ...

硬件为stm32+m25p40
void SPIx_Init(void)                 //spi2初始化
{  
   SPI_InitTypeDef  SPI_InitStructure;
   GPIO_InitTypeDef GPIO_InitStructure;
   RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
   RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2,  ENABLE );
   
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOB, &GPIO_InitStructure);
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
   GPIO_Init(GPIOB, &GPIO_InitStructure);        //NSS    PB12

   /* Deselect the FLASH: Chip Select high */
   SPI_FLASH_CS_HIGH();
   /* SPI2 configuration */
   SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;       //SPI 设置为双线双向全双工
   SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                                    //设置为主SPI
   SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                                //SPI 发送接收8 位帧结构
   SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                                       //时钟悬空高
   SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                                    //数据捕获于第二个时钟沿
   SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                                             //内部NSS 信号有SSI 位控制
   SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;//波特率预分频值为64
   SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                                    //数据传输从MSB位开始
   SPI_InitStructure.SPI_CRCPolynomial = 7;
   SPI_Init(SPI2, &SPI_InitStructure);
     /* Enable SPI1  */
   SPI_Cmd(SPI2, ENABLE);                //使能SPI 外设
}

u8 DuumyClock[4] = {0xA5,0xA5,0xA5,0xA5};
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
   DMA_InitTypeDef   DMA_InitStructure;
   //配置DMA通道
   DMA_DeInit(DMA1_Channel4);
   DMA_DeInit(DMA1_Channel5);                  
   DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;
   DMA_InitStructure.DMA_MemoryBaseAddr = (u32)pBuffer;
   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
   DMA_InitStructure.DMA_BufferSize = NumByteToRead;
   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
   DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
   DMA_Init(DMA1_Channel4, &DMA_InitStructure);

   DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DuumyClock;  //dummy
   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
   DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
   DMA_Init(DMA1_Channel5, &DMA_InitStructure);

   SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
   SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);  
   SPI_FLASH_StartReadSequence(ReadAddr);      //发送开始读命令与地址
   DMA_Cmd(DMA1_Channel4,ENABLE);
   DMA_Cmd(DMA1_Channel5,ENABLE);
   while(!DMA_GetFlagStatus(DMA1_FLAG_TC4)) ;
   while(!DMA_GetFlagStatus(DMA1_FLAG_TC5)) ;
   DMA_Cmd(DMA1_Channel4, DISABLE);
   DMA_Cmd(DMA1_Channel5, DISABLE);
   SPI_FLASH_CS_HIGH();
   SPI_FLASH_SendByte(Dummy_Byte) ;
}

u8 SPI_FLASH_SendByte(u8 byte)
{
    u16 count = 0;//
  /* Loop while DR register in not emplty */
  while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
  {
      count++;
    if (count > 65533)
    {
        return 0;
    }
  }
  /* Send byte through the SPI1 peripheral */
  SPI_I2S_SendData(SPI2, byte);
  /* Wait to receive a byte */
  count = 0;
  while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
  {
     
    count++;
     if (count > 65533)
     {
        return 0;
     }
  }
  /* Return the byte read from the SPI bus */
  return SPI_I2S_ReceiveData(SPI2);
}
u8 SPI_FLASH_ReadByte(void)
{
  return (SPI_FLASH_SendByte(Dummy_Byte));
}
void SPI_FLASH_StartReadSequence(u32 ReadAddr)
{
  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();
  /* Send "Read from Memory " instruction */
  SPI_FLASH_SendByte(READ);
  /* Send the 24-bit address of the address to read from -----------------------*/
  /* Send ReadAddr high nibble address byte */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /* Send ReadAddr medium nibble address byte */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /* Send ReadAddr low nibble address byte */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);
}
/*******************************************************************************
* Function Name  : SPI_FLASH_SectorErase
* Description    : Erases the specified FLASH sector.
* Input          : SectorAddr: address of the sector to erase.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
  /* Send write enable instruction */
  SPI_FLASH_WriteEnable();
  /* Sector Erase */
  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();
  /* Send Sector Erase instruction */
  SPI_FLASH_SendByte(SE);
  /* Send SectorAddr high nibble address byte */
  SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  /* Send SectorAddr medium nibble address byte */
  SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  /* Send SectorAddr low nibble address byte */
  SPI_FLASH_SendByte(SectorAddr & 0xFF);
  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();
  /* Wait the end of Flash writing */
  SPI_FLASH_WaitForWriteEnd();
}
/*******************************************************************************
* Function Name  : SPI_FLASH_BulkErase
* Description    : Erases the entire FLASH.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_BulkErase(void)
{
  /* Send write enable instruction */
  SPI_FLASH_WriteEnable();
  /* Bulk Erase */
  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();
  /* Send Bulk Erase instruction  */
  SPI_FLASH_SendByte(BE);
  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();
  /* Wait the end of Flash writing */
  SPI_FLASH_WaitForWriteEnd();
}
/*******************************************************************************
* Function Name  : SPI_FLASH_PageWrite
* Description    : Writes more than one byte to the FLASH with a single WRITE
*                  cycle(Page WRITE sequence). The number of byte can't exceed
*                  the FLASH page size.
* Input          : - pBuffer : pointer to the buffer  containing the data to be
*                    written to the FLASH.
*                  - WriteAddr : FLASH's internal address to write to.
*                  - NumByteToWrite : number of bytes to write to the FLASH,
*                    must be equal or less than "SPI_FLASH_PageSize" value.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  /* Enable the write access to the FLASH */
  SPI_FLASH_WriteEnable();
  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();
  /* Send "Write to Memory " instruction */
  SPI_FLASH_SendByte(WRITE);
  /* Send WriteAddr high nibble address byte to write to */
  SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  /* Send WriteAddr medium nibble address byte to write to */
  SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  /* Send WriteAddr low nibble address byte to write to */
  SPI_FLASH_SendByte(WriteAddr & 0xFF);
  /* while there is data to be written on the FLASH */
  while (NumByteToWrite--)
  {
    /* Send the current byte */
    SPI_FLASH_SendByte(*pBuffer);
    /* Point on the next byte to be written */
    pBuffer++;
  }
  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();
  /* Wait the end of Flash writing */
  SPI_FLASH_WaitForWriteEnd();
}
/*******************************************************************************
* Function Name  : SPI_FLASH_BufferWrite
* Description    : Writes block of data to the FLASH. In this function, the
*                  number of WRITE cycles are reduced, using Page WRITE sequence.
* Input          : - pBuffer : pointer to the buffer  containing the data to be
*                    written to the FLASH.
*                  - WriteAddr : FLASH's internal address to write to.
*                  - NumByteToWrite : number of bytes to write to the FLASH.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
  Addr = WriteAddr % SPI_FLASH_PageSize;
  count = SPI_FLASH_PageSize - Addr;
  NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
  NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  if (Addr == 0) /* WriteAddr is SPI_FLASH_PageSize aligned  */
  {
    if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
  }
  else /* WriteAddr is not SPI_FLASH_PageSize aligned  */
  {
    if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
    {
      if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
      {
        temp = NumOfSingle - count;
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
        WriteAddr +=  count;
        pBuffer += count;
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
      }
      else
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
      WriteAddr +=  count;
      pBuffer += count;
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
      if (NumOfSingle != 0)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}
}

使用特权

评论回复
评分
参与人数 1威望 +3 收起 理由
767598314 + 3
16
feilusia| | 2013-8-15 20:25 | 只看该作者
767598314 发表于 2013-8-15 16:26
DMA不能自动帮忙发送时钟信号啊,难怪我试了一早上,死活不行。发送和接收DMA同时开是为啥啊?我应该怎么去 ...

我之前说错了,对不住LZ。
时钟是主机在发送过程中产生的,如果主机想读从机的值,那就要发送数据到从机,从机会返回来值,所以就要同时开发送和接收的DMA啦。
要使用DMA的话有三个步骤:
1)在SPI配置里把发送缓冲区DMA和接收缓冲区DMA使能
2)DMA参数配置
3)DMA使能

使用特权

评论回复
17
myxiaonia| | 2013-8-16 07:38 | 只看该作者
767598314 发表于 2013-8-15 10:02
DMA会自动帮我发送这个信号的啊,那太好了。你说的这个计数器到0就中断是什么意思,意思是接收缓存满了就会 ...

不应该说是dma帮你发时钟,否则dma控制器要干的活就太多了,它只会帮你传递数据,比如写入spi的数据寄存器,是spi控制器在收到数据写入时发的时钟

使用特权

评论回复
18
outstanding| | 2013-8-16 09:01 | 只看该作者

使用特权

评论回复
19
767598314|  楼主 | 2013-8-16 10:18 | 只看该作者
song19881218 发表于 2013-8-15 17:37

谢谢你的分享,还有一点我不太明白:我的硬件是stm32+cc1101无线模块,cc1101向stm32发送数据是通过外部中断通知stm32的,如你所说,要想在stm32工作在主模式下用DMA接收SPI数据的话,得同时使能发送和接收DMA.我的问题就是,我可以在外部中断来临时才使能spi,而其它的时候都关闭吗?你的代码里面有这样几句话:
   DMA_Cmd(DMA1_Channel4,ENABLE);
   DMA_Cmd(DMA1_Channel5,ENABLE);
   while(!DMA_GetFlagStatus(DMA1_FLAG_TC4)) ;
   while(!DMA_GetFlagStatus(DMA1_FLAG_TC5)) ;
   DMA_Cmd(DMA1_Channel4, DISABLE);
   DMA_Cmd(DMA1_Channel5, DISABLE);
就是这个意思吗?需要在用的时候再把两个DMA使能,用完了再关闭。比如我的应用中,就应该在外部中断中写上   
DMA_Cmd(DMA1_Channel4,ENABLE);
DMA_Cmd(DMA1_Channel5,ENABLE);
将两个DMA通道使能,数据接收完以后将其打开,对吧?

使用特权

评论回复
20
767598314|  楼主 | 2013-8-16 10:22 | 只看该作者
feilusia 发表于 2013-8-15 20:25
我之前说错了,对不住LZ。
时钟是主机在发送过程中产生的,如果主机想读从机的值,那就要发送数 ...

没事的,你也给了我启发啊,至少我知道了工作在主模式下也可以用DMA的。还是很感谢你!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

23

主题

83

帖子

0

粉丝