要被STM32的SPI从机DMA玩死了,偶尔会跳字节,大家帮忙看看

2019-12-10 18:19发布

        芯片: SMT32F103RET6。
        问题: 在使用SPI 做从机时,接收发送都是DMA模式。 在发送时, DMA偶尔会跳过发送缓存的第2个字节, 调试了好几天, 没有头绪,望大家帮忙看看,
       
        代码:
                1. SPI 初始化

       

  1. //
  2. //       SPI PART
  3. static void m_spi_init(void) {
  4.     GPIO_InitTypeDef GPIO_InitStructure;
  5.     SPI_InitTypeDef  SPI_InitStructure;
  6.     NVIC_InitTypeDef NVIC_InitStructure;
  7.     DMA_InitTypeDef  DMA_InitStructure;
  8.     EXTI_InitTypeDef EXTI_InitStructure;

  9.     // enable clocks
  10.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
  11.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

  12.     // MISO
  13.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_14;
  14.     GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_AF_PP;
  15.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  16.     GPIO_Init(GPIOB, &GPIO_InitStructure);

  17.     // sck  mosi nss
  18.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_13 | GPIO_Pin_12;
  19.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  20.     GPIO_Init(GPIOB, &GPIO_InitStructure);
  21.    
  22.     // enable cs  interrupt
  23.     GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);

  24.     EXTI_InitStructure.EXTI_Line    = EXTI_Line12;
  25.     EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
  26.     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  27.     EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  28.     EXTI_Init(&EXTI_InitStructure);

  29.    
  30.     NVIC_InitStructure.NVIC_IRQChannel                   = EXTI15_10_IRQn; //使能外部中断所在的通道
  31.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; //抢占优先级 2,
  32.     NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0x01; //子优先级 2
  33.     NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE; //使能外部中断通道
  34.     NVIC_Init(&NVIC_InitStructure);
  35.    
  36. #if 1
  37.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

  38.     DMA_DeInit(DMA1_Channel4); //Set DMA registers to default values
  39.     DMA_StructInit(&DMA_InitStructure);

  40.     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; //Address of peripheral the DMA must map to
  41.     DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)m_spi_dma_receive_buff; //Variable to which received data will be stored
  42.     DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;
  43.     DMA_InitStructure.DMA_BufferSize         = sizeof(m_spi_dma_receive_buff); //Buffer size
  44.     DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
  45.     DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
  46.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  47.     DMA_InitStructure.DMA_MemoryDataSize     = DMA_PeripheralDataSize_Byte;
  48.     DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
  49.     DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
  50.     DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
  51.     DMA_Init(DMA1_Channel4, &DMA_InitStructure); //Initialise the DMAa

  52.     DMA_DeInit(DMA1_Channel5);               
  53.     DMA_StructInit(&DMA_InitStructure);

  54.     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;          // Address of peripheral the DMA must map to
  55.     DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)m_spi_dma_send_buf; // Variable from which data will be transmitted
  56.     DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralDST;
  57.     DMA_InitStructure.DMA_BufferSize         = sizeof(m_spi_dma_send_buf);   //Buffer size
  58.     DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
  59.     DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
  60.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  61.     DMA_InitStructure.DMA_MemoryDataSize     = DMA_PeripheralDataSize_Byte;
  62.     DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;
  63.     DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
  64.     DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;

  65.     DMA_Init(DMA1_Channel5, &DMA_InitStructure);

  66. #endif
  67.    
  68.     // enable transfer complete & error interrupt
  69.     NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
  70.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  71.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  72.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  73.     NVIC_Init(&NVIC_InitStructure);
  74.     DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_TE, ENABLE);

  75.     // enable the dma channel of receiving
  76.     DMA_Cmd(DMA1_Channel4, ENABLE); //Enable the DMA1 - Channel4   


  77.     SPI_Cmd(SPI2, DISABLE);
  78.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  79.     SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
  80.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  81.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  82.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  83.     SPI_InitStructure.SPI_NSS =  SPI_NSS_Hard;
  84.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  85.     SPI_InitStructure.SPI_CRCPolynomial = 7;
  86.     SPI_Init(SPI2, &SPI_InitStructure);

  87.     // enable spi error interrupt
  88.     NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;
  89.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  90.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  91.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  92.     NVIC_Init(&NVIC_InitStructure);
  93.     SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_TXE);
  94.     SPI_I2S_ClearFlag(SPI2, SPI_FLAG_MODF);
  95.     SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_ERR, ENABLE);
  96.    
  97.     // enable dma receive and transmit
  98.     SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
  99.    
  100.     // enable spi2 device
  101.     SPI_Cmd(SPI2, ENABLE);
  102. }
复制代码

        2: 发送代码

  1. /*
  2. * @brief: send data via the spi bus.
  3. * @param ack: pointer to the buffer of ack data.
  4. * @param ack_size: size of the ack buffer.
  5. * @param buf: pointer to the data buffer that real be send.
  6. * @param size: size of real data.
  7. */
  8. void board_spi_send(uint8_t *ack, int ack_size, uint8_t *buf, int size) {
  9.     int i, index = 0;

  10.     // copy ACK data to memory
  11.     if (ack != null) {
  12.         for (i = 0; i < ack_size; i++) {
  13.             m_spi_dma_send_buf[index++] = *ack++;
  14.         }
  15.     }
  16.     // copy real data to memory
  17.     if (buf != null) {
  18.         for (i = 0; i < size; i++) {
  19.             m_spi_dma_send_buf[index++] = *buf++;
  20.         }
  21.     }
  22.     // finally, it has data to send.
  23.     if (index != 0) {
  24. #if 1
  25.         DBG_INFO("Fill data intp DMA => ");
  26.         for (i = 0; i < index; i++) {
  27.             printf("0x%02x ", m_spi_dma_send_buf[i]);
  28.         }
  29.         printf(" ");
  30. #endif
  31.         // wait for the dma finish its work.
  32.         while ((m_spi_is_sending == true || m_spi_dma_waiting_send == true) && (DMA1_Channel5->CCR & DMA_CCR5_EN));
  33.         // disable dma channel
  34.         DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
  35.         m_spi_is_sending = false;
  36.         // use to indicate that we are waiting for sending.
  37.         m_spi_dma_waiting_send = true;
  38.         // re-config the address and size
  39.         DMA1_Channel5->CMAR = (uint32_t)m_spi_dma_send_buf;
  40.         // set the buffer size
  41.         DMA1_Channel5->CNDTR = index;
  42.     } else {
  43.         printf("nothing send to spi ");
  44.     }
  45. }
复制代码

        3: CS 中断
  1. //
  2. // interrupt handler routine for spi cs
  3. void EXTI15_10_IRQHandler(void) {
  4.    
  5.     // the master start to transfer
  6.     if (EXTI_GetITStatus(EXTI_Line12) != RESET) {
  7.         // check whether there are some data waiting for sending.
  8.         if (m_spi_dma_waiting_send == true) {
  9.             DBG_INFO("enable spi send. size = %d ", DMA1_Channel5->CNDTR);
  10.             // set this variable to indicate that we are sending.
  11.             m_spi_is_sending = true;
  12.             // unmark this variable.
  13.             m_spi_dma_waiting_send = false;
  14.             // enable the transmit DMA channel.
  15.             DMA1_Channel5->CCR |= DMA_CCR5_EN;
  16.         }
  17.         EXTI_ClearITPendingBit(EXTI_Line12);
  18.     }  
  19. }
复制代码

        4:DMA 中断
  1. void DMA1_Channel5_IRQHandler(void) {

  2.     if (DMA_GetITStatus(DMA1_IT_TC5) != RESET) {
  3.         DBG_INFO("DMA_INT=> send ack ok! remain size = %d ", DMA1_Channel5->CNDTR);
  4.         // disable the dma channel
  5.         DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
  6.         m_spi_is_sending = false;
  7.         DMA_ClearITPendingBit(DMA1_IT_TC5);
  8.     }
  9.    
  10.     if (DMA_GetITStatus(DMA1_IT_TE5) != RESET) {
  11.         DBG_ERROR("some error occured while sending... ");
  12.         // disable the dma channel
  13.         DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
  14.         m_spi_is_sending = false;
  15.         DMA_ClearITPendingBit(DMA1_IT_TE5);
  16.     }
  17. }
复制代码

        5: SPI 中断
  1. void SPI2_IRQHandler(void) {

  2.     if (SPI_I2S_GetITStatus(SPI2, SPI_I2S_IT_OVR)) {
  3.         DBG_ERROR("spi2 receive overrun! ");
  4.         SPI_I2S_ClearITPendingBit(SPI2, SPI_I2S_IT_OVR);
  5.     }
  6.     if (SPI_I2S_GetITStatus(SPI2, SPI_IT_CRCERR)) {
  7.         DBG_ERROR("spi2 crc error! ");
  8.         SPI_I2S_ClearITPendingBit(SPI2, SPI_IT_CRCERR);
  9.     }

  10.     if (SPI_I2S_GetITStatus(SPI2, SPI_IT_MODF)) {
  11.         DBG_ERROR("spi2 mode error! ");
  12.         SPI_I2S_ClearITPendingBit(SPI2, SPI_IT_MODF);
  13.     }
  14. }
复制代码


错误现象:

        调试图片.png (363.97 KB, 下载次数: 0) 下载附件 2018-5-28 14:37 上传

DMA在发送完第一个字节数据后, 跳过了第二个, 直接发送第三个了。。。。。。

看过这个时候的DMA控制寄存器, 值为 209a   字节单位没有改, 都是 Byte。 而且在整个过程中, SPI 和 DMA的错误中断都没有触发! 希望大家帮忙分析一下。。。。


       
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
19条回答
yddoofrank
1楼-- · 2019-12-10 22:12
没有仔细看你的代码,不过有个经验分享给你,DMA发送时会BURST 几个( 2,4? )字节到DMA BUFFER里,你要注意这个问题,一定要把手册要看懂
浮华一生
2楼-- · 2019-12-11 00:36
yddoofrank 发表于 2018-5-28 15:02
没有仔细看你的代码,不过有个经验分享给你,DMA发送时会BURST 几个( 2,4? )字节到DMA BUFFER里,你要 ...

我看了DMA和SPI部分, 没有提到SPI有FIFO, 也没有提到DMA会突发发送几个字节啊, 呃,或许我看漏了? 朋友, 麻烦指点一下在哪个位置有提到。谢谢了
zchong
3楼-- · 2019-12-11 05:51
本帖最后由 zchong 于 2018-5-28 15:59 编辑
浮华一生 发表于 2018-5-28 15:29
我看了DMA和SPI部分, 没有提到SPI有FIFO, 也没有提到DMA会突发发送几个字节啊, 呃,或许我看漏了? 朋 ...


F1的DMA没有FIFO,F2以上才有,FIFO应该也不会引起你说的现象。

建议还是从自己的代码逻辑上查一下看看,多调试。
浮华一生
4楼-- · 2019-12-11 11:26
顶一顶                 
yddoofrank
5楼-- · 2019-12-11 14:58
 精彩回答 2  元偷偷看……
浮华一生
6楼-- · 2019-12-11 17:39
zchong 发表于 2018-5-28 15:56
F1的DMA没有FIFO,F2以上才有,FIFO应该也不会引起你说的现象。

建议还是从自己的代码逻辑上查一下看看 ...

逻辑的话,我看了好几遍, 也没看出问题。 因为我们一般都是操作使能 或者 结束后禁止等逻辑, 它这个现象是,已经开始发送数据了,然后会跳过第2个字节。就是说第一个字节已经发出去了,然后跳过一个,后面的又继续发送,,,

一周热门 更多>