playingaman 发表于 2018-5-28 15:19:28

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

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

               
//
//       SPI PART
static void m_spi_init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDefSPI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    DMA_InitTypeDefDMA_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;

    // enable clocks
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

    // MISO
    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // sckmosi nss
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_13 | GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // enable csinterrupt
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);

    EXTI_InitStructure.EXTI_Line    = EXTI_Line12;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);


    NVIC_InitStructure.NVIC_IRQChannel                   = EXTI15_10_IRQn; //使能外部中断所在的通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; //抢占优先级 2,
    NVIC_InitStructure.NVIC_IRQChannelSubPriority      = 0x01; //子优先级 2
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE; //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);

#if 1
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

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

    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; //Address of peripheral the DMA must map to
    DMA_InitStructure.DMA_MemoryBaseAddr   = (uint32_t)m_spi_dma_receive_buff; //Variable to which received data will be stored
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize         = sizeof(m_spi_dma_receive_buff); //Buffer size
    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_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority         = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure); //Initialise the DMAa

    DMA_DeInit(DMA1_Channel5);               
    DMA_StructInit(&DMA_InitStructure);

    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;          // Address of peripheral the DMA must map to
    DMA_InitStructure.DMA_MemoryBaseAddr   = (uint32_t)m_spi_dma_send_buf; // Variable from which data will be transmitted
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize         = sizeof(m_spi_dma_send_buf);   //Buffer size
    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_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority         = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;

    DMA_Init(DMA1_Channel5, &DMA_InitStructure);

#endif

    // enable transfer complete & error interrupt
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_TE, ENABLE);

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


    SPI_Cmd(SPI2, DISABLE);
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS =SPI_NSS_Hard;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI2, &SPI_InitStructure);

    // enable spi error interrupt
    NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_TXE);
    SPI_I2S_ClearFlag(SPI2, SPI_FLAG_MODF);
    SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_ERR, ENABLE);

    // enable dma receive and transmit
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);

    // enable spi2 device
    SPI_Cmd(SPI2, ENABLE);
}

        2: 发送代码
       
/*
* @brief: send data via the spi bus.
* @param ack: pointer to the buffer of ack data.
* @param ack_size: size of the ack buffer.
* @param buf: pointer to the data buffer that real be send.
* @param size: size of real data.
*/
void board_spi_send(uint8_t *ack, int ack_size, uint8_t *buf, int size) {
    int i, index = 0;

    // copy ACK data to memory
    if (ack != null) {
      for (i = 0; i < ack_size; i++) {
            m_spi_dma_send_buf = *ack++;
      }
    }
    // copy real data to memory
    if (buf != null) {
      for (i = 0; i < size; i++) {
            m_spi_dma_send_buf = *buf++;
      }
    }
    // finally, it has data to send.
    if (index != 0) {
#if 1
      DBG_INFO("Fill data intp DMA => ");
      for (i = 0; i < index; i++) {
            printf("0x%02x ", m_spi_dma_send_buf);
      }
      printf("\r\n");
#endif
      // wait for the dma finish its work.
      while ((m_spi_is_sending == true || m_spi_dma_waiting_send == true) && (DMA1_Channel5->CCR & DMA_CCR5_EN));
      // disable dma channel
      DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
      m_spi_is_sending = false;
      // use to indicate that we are waiting for sending.
      m_spi_dma_waiting_send = true;
      // re-config the address and size
      DMA1_Channel5->CMAR = (uint32_t)m_spi_dma_send_buf;
      // set the buffer size
      DMA1_Channel5->CNDTR = index;
    } else {
      printf("nothing send to spi\r\n");
    }
}

        3: CS 中断
        //
// interrupt handler routine for spi cs
void EXTI15_10_IRQHandler(void) {

    // the master start to transfer
    if (EXTI_GetITStatus(EXTI_Line12) != RESET) {
      // check whether there are some data waiting for sending.
      if (m_spi_dma_waiting_send == true) {
            DBG_INFO("enable spi send. size = %d\r\n", DMA1_Channel5->CNDTR);
            // set this variable to indicate that we are sending.
            m_spi_is_sending = true;
            // unmark this variable.
            m_spi_dma_waiting_send = false;
            // enable the transmit DMA channel.
            DMA1_Channel5->CCR |= DMA_CCR5_EN;
      }
      EXTI_ClearITPendingBit(EXTI_Line12);
    }
}

        4:DMA 中断
void DMA1_Channel5_IRQHandler(void) {

    if (DMA_GetITStatus(DMA1_IT_TC5) != RESET) {
      DBG_INFO("DMA_INT=> send ack ok! remain size = %d\r\n", DMA1_Channel5->CNDTR);
      // disable the dma channel
      DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
      m_spi_is_sending = false;
      DMA_ClearITPendingBit(DMA1_IT_TC5);
    }

    if (DMA_GetITStatus(DMA1_IT_TE5) != RESET) {
      DBG_ERROR("some error occured while sending...\r\n");
      // disable the dma channel
      DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
      m_spi_is_sending = false;
      DMA_ClearITPendingBit(DMA1_IT_TE5);
    }
}

        5: SPI 中断
        void SPI2_IRQHandler(void) {

    if (SPI_I2S_GetITStatus(SPI2, SPI_I2S_IT_OVR)) {
      DBG_ERROR("spi2 receive overrun!\r\n");
      SPI_I2S_ClearITPendingBit(SPI2, SPI_I2S_IT_OVR);
    }
    if (SPI_I2S_GetITStatus(SPI2, SPI_IT_CRCERR)) {
      DBG_ERROR("spi2 crc error!\r\n");
      SPI_I2S_ClearITPendingBit(SPI2, SPI_IT_CRCERR);
    }

    if (SPI_I2S_GetITStatus(SPI2, SPI_IT_MODF)) {
      DBG_ERROR("spi2 mode error!\r\n");
      SPI_I2S_ClearITPendingBit(SPI2, SPI_IT_MODF);
    }
}


错误现象:

       

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

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


       

feixiang20 发表于 2018-5-29 00:27:28

本帖最后由 feixiang20 于 2018-5-29 00:28 编辑

查看DMA配置里的缓存数据大小BufferSize是你要发送的数据个数

playingaman 发表于 2018-5-29 17:19:45

feixiang20 发表于 2018-5-29 00:27
查看DMA配置里的缓存数据大小BufferSize是你要发送的数据个数

在发送之前CS中断里打印出来 是预期的现在把程序用 STM32F103VCT6 100PIN测试, 已经跑了一下午没出问题了

toofree 发表于 2018-5-30 01:49:10

有原理图吗?你是搭的电路,还是画的PCB板上的电路。
从你的现象来看的话(首先我猜想你的 m_spi_dma_receive_buff为2),SPI片选有没有可能,受干扰,而产生一个上升沿脉冲呢?

黑皮男 发表于 2018-5-30 09:09:32

我的spi 从机的时候也是偶尔会出错,有点郁闷
页: [1]
查看完整版本: 要被STM32的SPI从机DMA玩死了,偶尔会跳字节,大家帮忙看看