Как настроить SPM STM32f405 для передачи данных с помощью DMA?

У меня есть STM32f405, и задача состоит в том, чтобы передавать данные через SPI и экономить время процессора с помощью DMA. Используемый SPI является SPI1 с выводами PA4-PA7. Я выбрал для DMA 3-й поток из канала 3 DMA2. Идея состоит в том, чтобы активировать сигнал CS и сохранить некоторые данные в памяти, которые затем будут автоматически переданы DMA, и как только это будет сделано, DMA должна вызвать прерывание. обработчик для деактивации CS. Вот код:

static void SPI_Config(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;
    DMA_InitTypeDef DMA_Init_Structure;
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Enable the SPI clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
    /* Enable GPIO clocks */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    /* Enable DMA clock */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    /* SPI GPIO Configuration --------------------------------------------------*/
  /* GPIO Deinitialisation */
    GPIO_DeInit(GPIOA);
    /* Connect SPI pins to AF5 */ 
//  GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1); //SS
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); //SCK   
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); //MISO
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); //MOSI

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;

    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5; //SCK
  GPIO_Init(GPIOA, &GPIO_InitStructure); 

    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6; //MISO
  GPIO_Init(GPIOA, &GPIO_InitStructure); 

    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_7; //MOSI
  GPIO_Init(GPIOA, &GPIO_InitStructure); 

    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4; //SS
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStructure); 

    //DMA Globul Interrupt
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

    //DMA Configuration
    DMA_DeInit(DMA2_Stream3);
    DMA_Cmd(DMA2_Stream3, DISABLE);
    while (DMA1_Stream0->CR & DMA_SxCR_EN);
    DMA_Init_Structure.DMA_BufferSize = 0;
    DMA_Init_Structure.DMA_Channel = DMA_Channel_3; 
    DMA_Init_Structure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_Init_Structure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_Init_Structure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_Init_Structure.DMA_Memory0BaseAddr = (uint32_t)(&spi_tx_val);
    DMA_Init_Structure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_Init_Structure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_Init_Structure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    DMA_Init_Structure.DMA_Mode = DMA_Mode_Circular;
    DMA_Init_Structure.DMA_PeripheralBaseAddr = (uint32_t) (&(SPI1->DR));
    DMA_Init_Structure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init_Structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_Init_Structure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_Init_Structure.DMA_Priority = DMA_Priority_High;
    DMA_Init(DMA2_Stream3,&DMA_Init_Structure);

    //SPI Configuration
    SPI_I2S_DeInit(SPI1);
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //AD5620 doku page 18 falling edge of SCLK
    SPI_InitStructure.SPI_CRCPolynomial = 0; //x_8+x_2+x_1+1 in python hex(2**8+2**2+2+1)
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //AD5620 input register is 16 bit
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_Init(SPI1, &SPI_InitStructure);




}

int8_t Analog_Out_Config(uint32_t target_reg_val) {
    uint16_t power_on_status;
    target_reg_val = target_reg_val;
    SPI_Config();
//  SPI_Cmd(SPI1, ENABLE);
//  power_on_status=PowerOn_AD5750_OutDriver();
//  if(power_on_status) {
        //enable dma interrupt
//      SPI_Cmd(SPI1, DISABLE);
        DMA_ITConfig(DMA2_Stream3,DMA_IT_TC,ENABLE);
        DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_FEIF3|DMA_FLAG_DMEIF3|DMA_FLAG_TEIF3|DMA_FLAG_HTIF3|DMA_FLAG_TCIF3);
        DMA_Cmd(DMA2_Stream3, ENABLE);
      SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx, ENABLE);
        SPI_Cmd(SPI1, ENABLE);

        return power_on_status&0x07;
//  }else {
//      return -1;
//  }
}
void Analog_Output(uint32_t measured_reg_val) {
    val=0x7ff;
    ACTIVATE_CS_DAC();
    spi_tx_val=val; 
}

void DMA2_Stream3_IRQHandler(void) {
    if(DMA_GetITStatus(DMA2_Stream3,DMA_IT_TCIF3)!=RESET) {
        DMA_ClearITPendingBit(DMA2_Stream3,DMA_IT_TCIF0|DMA_IT_HTIF0);
        DEACTIVATE_CS_DAC();

    }
}

int main(void)
{
    target_reg_val=14;
    measured_reg_val=12;
    Analog_Out_Config(target_reg_val);
  while (1)
  {
        for(val=-target_reg_val;val<target_reg_val;val++) {
            Analog_Output(val);
            for(i=0;i<1000;i++);
        }

  }
}

С помощью отладчика я обнаружил, что DMA2_Stream3_IRQHandler никогда не активируется. Согласно справочному руководству DMA должен передавать данные, когда флаг TXE из регистра SPI_DR равен 1, что было так. Также был установлен флаг TXDMAEN от SPI_CR2. Я также проверил регистр DMA S3CR, а также были установлены флаги TCIE и EN. Кроме того, функция DMA2_Stream3_IRQHandler в видимой для основной функции. Тем не менее DMA2_Stream3_IRQHandler никогда не был активирован.

ОБНОВЛЕНИЕ: когда я вручную сбрасываю бит EN регистра DMA2_S3CR, тогда запускается DMA2_Stream3_IRQHandler. Согласно справочному руководству этот бит очищается аппаратно:

  • на конце передачи DMA (поток готов к настройке)
  • если ошибка передачи происходит на главных шинах AHB
  • когда порог FIFO для порта AHB памяти несовместим с размером пакета

Я также изменил SPI_Config и Analog_Out_Config, но все еще без вмешательства отладчика DMA2_Stream3_IRQHandler никогда не запускается. Похоже, что DMA не запускает передачу и по какой-то причине не может ее завершить. Как я могу узнать, имеет ли DMA запуск какой-либо передачи?

1 ответ

У вас есть настройки структуры конфигурации DMA DMA_Mode в DMA_NORMAL, Вероятно, должно быть установлено DMA_PFCTRL который является контролем периферийного потока. Это заставит DMA ждать сигнала от периферийного устройства (которое вы настроили), а не работать непрерывно. Я ожидаю, однако, что без этой настройки вы получите одно или два слова, выходящие из шины SPI, как SPI_DR должен получить всю настройку памяти при передаче DMA, сброшенной на нее последовательно (так как она вытесняет первое слово).

Еще одна вещь, чтобы проверить, что ни одна из ваших периферийных устройств (SPI1, DMA2) находятся в перезагрузке. Я вижу, что вы отключаете часы, но я не помню, выводит ли ST также периферийное устройство из сброса при этом вызове.

Примечание: я использовал периферийное устройство DMA STM32F2xx в качестве эталона, но периферийные устройства STM32F4xx, как правило, являются надмножествами. Также похоже, что вы используете другую версию периферийной библиотеки ST, чем та, которую я использовал в качестве справочной.

Другие вопросы по тегам