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

[复制链接]
5767|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也中断。
 楼主| 767598314 发表于 2013-8-15 10:02 | 显示全部楼层
DMA会自动帮我发送这个信号的啊,那太好了。你说的这个计数器到0就中断是什么意思,意思是接收缓存满了就会自动进DMA中断吗?我做adc的时候用过DMA.我只用在dma的配置里面给定一个缓存的大小,存满了以后他就自动进DMA的中断。
 楼主| 767598314 发表于 2013-8-15 10:05 | 显示全部楼层
feilusia 发表于 2013-8-15 09:44
DMA自然会帮你发送时钟信号,怎么发怎么收你可以不管。
发送的计数器到0就中断,接收的计数器到0也中断。 ...

对了,还有一个问题,就是我的数据是否到来是通过外部中断告诉cpu的,可以让外部中断来触发spi让它去接收数据吗?(就像用定时器去触发DAC一样)
feilusia 发表于 2013-8-15 10:12 | 显示全部楼层
你去看DMA的CNDTR寄存器,那个就是设置你要发送或接受的数量。比如你设置10,那就接10个数就进中断(如果你开了中断)。
SPI自己有中断,一接收到数据就进入中断。
 楼主| 767598314 发表于 2013-8-15 10:21 | 显示全部楼层
好的,谢谢你,我先试试。你可以加我为qq好友吗?我没别的意思,我怕等会儿遇到问题时找不到你了!
 楼主| 767598314 发表于 2013-8-15 10:21 | 显示全部楼层
feilusia 发表于 2013-8-15 10:12
你去看DMA的CNDTR寄存器,那个就是设置你要发送或接受的数量。比如你设置10,那就接10个数就进中断(如果你 ...

我的qq号是767598314     验证答案是:张春
feilusia 发表于 2013-8-15 10:24 | 显示全部楼层
767598314 发表于 2013-8-15 10:21
我的qq号是767598314     验证答案是:张春

上班不方便聊QQ,有问题这里问吧。
 楼主| 767598314 发表于 2013-8-15 10:29 | 显示全部楼层
好吧,那你记得来看我的帖子哦,弄好了我也会告诉你的
song19881218 发表于 2013-8-15 13:02 | 显示全部楼层
你确定DMA会帮你发送时钟信号?我记得以前试过貌似不行,要开接收DMA貌似发送DMA也要开了,在收之前必须先使能发送DMA。
feilusia 发表于 2013-8-15 14:31 | 显示全部楼层
song19881218 发表于 2013-8-15 13:02
你确定DMA会帮你发送时钟信号?我记得以前试过貌似不行,要开接收DMA貌似发送DMA也要开了,在收之前必须先 ...

没注意到LZ说是主设备,那是要发送和接收都开。我之前做的是双机通信,所以要发数据的自动变为主机,要收数据的变从机。
 楼主| 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);

  }
}
song19881218 发表于 2013-8-15 17:37 | 显示全部楼层

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

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

  1. 硬件为stm32+m25p40
  2. void SPIx_Init(void)                 //spi2初始化
  3. {  
  4.    SPI_InitTypeDef  SPI_InitStructure;
  5.    GPIO_InitTypeDef GPIO_InitStructure;
  6.    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
  7.    RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2,  ENABLE );
  8.    
  9.    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  10.    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  11.    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  12.    GPIO_Init(GPIOB, &GPIO_InitStructure);
  13.    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
  14.    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  15.    GPIO_Init(GPIOB, &GPIO_InitStructure);        //NSS    PB12

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

  32. u8 DuumyClock[4] = {0xA5,0xA5,0xA5,0xA5};
  33. void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
  34. {
  35.    DMA_InitTypeDef   DMA_InitStructure;
  36.    //配置DMA通道
  37.    DMA_DeInit(DMA1_Channel4);
  38.    DMA_DeInit(DMA1_Channel5);                  
  39.    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;
  40.    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)pBuffer;
  41.    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  42.    DMA_InitStructure.DMA_BufferSize = NumByteToRead;
  43.    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  44.    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  45.    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  46.    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  47.    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  48.    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  49.    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  50.    DMA_Init(DMA1_Channel4, &DMA_InitStructure);

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

  55.    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
  56.    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);  
  57.    SPI_FLASH_StartReadSequence(ReadAddr);      //发送开始读命令与地址
  58.    DMA_Cmd(DMA1_Channel4,ENABLE);
  59.    DMA_Cmd(DMA1_Channel5,ENABLE);
  60.    while(!DMA_GetFlagStatus(DMA1_FLAG_TC4)) ;
  61.    while(!DMA_GetFlagStatus(DMA1_FLAG_TC5)) ;
  62.    DMA_Cmd(DMA1_Channel4, DISABLE);
  63.    DMA_Cmd(DMA1_Channel5, DISABLE);
  64.    SPI_FLASH_CS_HIGH();
  65.    SPI_FLASH_SendByte(Dummy_Byte) ;
  66. }

  67. u8 SPI_FLASH_SendByte(u8 byte)
  68. {
  69.     u16 count = 0;//
  70.   /* Loop while DR register in not emplty */
  71.   while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
  72.   {
  73.       count++;
  74.     if (count > 65533)
  75.     {
  76.         return 0;
  77.     }
  78.   }
  79.   /* Send byte through the SPI1 peripheral */
  80.   SPI_I2S_SendData(SPI2, byte);
  81.   /* Wait to receive a byte */
  82.   count = 0;
  83.   while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
  84.   {
  85.      
  86.     count++;
  87.      if (count > 65533)
  88.      {
  89.         return 0;
  90.      }
  91.   }
  92.   /* Return the byte read from the SPI bus */
  93.   return SPI_I2S_ReceiveData(SPI2);
  94. }
  95. u8 SPI_FLASH_ReadByte(void)
  96. {
  97.   return (SPI_FLASH_SendByte(Dummy_Byte));
  98. }
  99. void SPI_FLASH_StartReadSequence(u32 ReadAddr)
  100. {
  101.   /* Select the FLASH: Chip Select low */
  102.   SPI_FLASH_CS_LOW();
  103.   /* Send "Read from Memory " instruction */
  104.   SPI_FLASH_SendByte(READ);
  105.   /* Send the 24-bit address of the address to read from -----------------------*/
  106.   /* Send ReadAddr high nibble address byte */
  107.   SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  108.   /* Send ReadAddr medium nibble address byte */
  109.   SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  110.   /* Send ReadAddr low nibble address byte */
  111.   SPI_FLASH_SendByte(ReadAddr & 0xFF);
  112. }
  113. /*******************************************************************************
  114. * Function Name  : SPI_FLASH_SectorErase
  115. * Description    : Erases the specified FLASH sector.
  116. * Input          : SectorAddr: address of the sector to erase.
  117. * Output         : None
  118. * Return         : None
  119. *******************************************************************************/
  120. void SPI_FLASH_SectorErase(u32 SectorAddr)
  121. {
  122.   /* Send write enable instruction */
  123.   SPI_FLASH_WriteEnable();
  124.   /* Sector Erase */
  125.   /* Select the FLASH: Chip Select low */
  126.   SPI_FLASH_CS_LOW();
  127.   /* Send Sector Erase instruction */
  128.   SPI_FLASH_SendByte(SE);
  129.   /* Send SectorAddr high nibble address byte */
  130.   SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  131.   /* Send SectorAddr medium nibble address byte */
  132.   SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  133.   /* Send SectorAddr low nibble address byte */
  134.   SPI_FLASH_SendByte(SectorAddr & 0xFF);
  135.   /* Deselect the FLASH: Chip Select high */
  136.   SPI_FLASH_CS_HIGH();
  137.   /* Wait the end of Flash writing */
  138.   SPI_FLASH_WaitForWriteEnd();
  139. }
  140. /*******************************************************************************
  141. * Function Name  : SPI_FLASH_BulkErase
  142. * Description    : Erases the entire FLASH.
  143. * Input          : None
  144. * Output         : None
  145. * Return         : None
  146. *******************************************************************************/
  147. void SPI_FLASH_BulkErase(void)
  148. {
  149.   /* Send write enable instruction */
  150.   SPI_FLASH_WriteEnable();
  151.   /* Bulk Erase */
  152.   /* Select the FLASH: Chip Select low */
  153.   SPI_FLASH_CS_LOW();
  154.   /* Send Bulk Erase instruction  */
  155.   SPI_FLASH_SendByte(BE);
  156.   /* Deselect the FLASH: Chip Select high */
  157.   SPI_FLASH_CS_HIGH();
  158.   /* Wait the end of Flash writing */
  159.   SPI_FLASH_WaitForWriteEnd();
  160. }
  161. /*******************************************************************************
  162. * Function Name  : SPI_FLASH_PageWrite
  163. * Description    : Writes more than one byte to the FLASH with a single WRITE
  164. *                  cycle(Page WRITE sequence). The number of byte can't exceed
  165. *                  the FLASH page size.
  166. * Input          : - pBuffer : pointer to the buffer  containing the data to be
  167. *                    written to the FLASH.
  168. *                  - WriteAddr : FLASH's internal address to write to.
  169. *                  - NumByteToWrite : number of bytes to write to the FLASH,
  170. *                    must be equal or less than "SPI_FLASH_PageSize" value.
  171. * Output         : None
  172. * Return         : None
  173. *******************************************************************************/
  174. void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
  175. {
  176.   /* Enable the write access to the FLASH */
  177.   SPI_FLASH_WriteEnable();
  178.   /* Select the FLASH: Chip Select low */
  179.   SPI_FLASH_CS_LOW();
  180.   /* Send "Write to Memory " instruction */
  181.   SPI_FLASH_SendByte(WRITE);
  182.   /* Send WriteAddr high nibble address byte to write to */
  183.   SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  184.   /* Send WriteAddr medium nibble address byte to write to */
  185.   SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  186.   /* Send WriteAddr low nibble address byte to write to */
  187.   SPI_FLASH_SendByte(WriteAddr & 0xFF);
  188.   /* while there is data to be written on the FLASH */
  189.   while (NumByteToWrite--)
  190.   {
  191.     /* Send the current byte */
  192.     SPI_FLASH_SendByte(*pBuffer);
  193.     /* Point on the next byte to be written */
  194.     pBuffer++;
  195.   }
  196.   /* Deselect the FLASH: Chip Select high */
  197.   SPI_FLASH_CS_HIGH();
  198.   /* Wait the end of Flash writing */
  199.   SPI_FLASH_WaitForWriteEnd();
  200. }
  201. /*******************************************************************************
  202. * Function Name  : SPI_FLASH_BufferWrite
  203. * Description    : Writes block of data to the FLASH. In this function, the
  204. *                  number of WRITE cycles are reduced, using Page WRITE sequence.
  205. * Input          : - pBuffer : pointer to the buffer  containing the data to be
  206. *                    written to the FLASH.
  207. *                  - WriteAddr : FLASH's internal address to write to.
  208. *                  - NumByteToWrite : number of bytes to write to the FLASH.
  209. * Output         : None
  210. * Return         : None
  211. *******************************************************************************/
  212. void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
  213. {
  214.   u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
  215.   Addr = WriteAddr % SPI_FLASH_PageSize;
  216.   count = SPI_FLASH_PageSize - Addr;
  217.   NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
  218.   NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  219.   if (Addr == 0) /* WriteAddr is SPI_FLASH_PageSize aligned  */
  220.   {
  221.     if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
  222.     {
  223.       SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  224.     }
  225.     else /* NumByteToWrite > SPI_FLASH_PageSize */
  226.     {
  227.       while (NumOfPage--)
  228.       {
  229.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  230.         WriteAddr +=  SPI_FLASH_PageSize;
  231.         pBuffer += SPI_FLASH_PageSize;
  232.       }
  233.       SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  234.     }
  235.   }
  236.   else /* WriteAddr is not SPI_FLASH_PageSize aligned  */
  237.   {
  238.     if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
  239.     {
  240.       if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
  241.       {
  242.         temp = NumOfSingle - count;
  243.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  244.         WriteAddr +=  count;
  245.         pBuffer += count;
  246.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
  247.       }
  248.       else
  249.       {
  250.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
  251.       }
  252.     }
  253.     else /* NumByteToWrite > SPI_FLASH_PageSize */
  254.     {
  255.       NumByteToWrite -= count;
  256.       NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
  257.       NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  258.       SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
  259.       WriteAddr +=  count;
  260.       pBuffer += count;
  261.       while (NumOfPage--)
  262.       {
  263.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
  264.         WriteAddr +=  SPI_FLASH_PageSize;
  265.         pBuffer += SPI_FLASH_PageSize;
  266.       }
  267.       if (NumOfSingle != 0)
  268.       {
  269.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
  270.       }
  271.     }
  272.   }
  273. }
  274. }

评分

参与人数 1威望 +3 收起 理由
767598314 + 3

查看全部评分

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使能
myxiaonia 发表于 2013-8-16 07:38 | 显示全部楼层
767598314 发表于 2013-8-15 10:02
DMA会自动帮我发送这个信号的啊,那太好了。你说的这个计数器到0就中断是什么意思,意思是接收缓存满了就会 ...

不应该说是dma帮你发时钟,否则dma控制器要干的活就太多了,它只会帮你传递数据,比如写入spi的数据寄存器,是spi控制器在收到数据写入时发的时钟
outstanding 发表于 2013-8-16 09:01 | 显示全部楼层
 楼主| 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通道使能,数据接收完以后将其打开,对吧?
 楼主| 767598314 发表于 2013-8-16 10:22 | 显示全部楼层
feilusia 发表于 2013-8-15 20:25
我之前说错了,对不住LZ。
时钟是主机在发送过程中产生的,如果主机想读从机的值,那就要发送数 ...

没事的,你也给了我启发啊,至少我知道了工作在主模式下也可以用DMA的。还是很感谢你!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

23

主题

83

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部