2016-08-04 40 views
0

Ich habe ein STM32f405 und die Aufgabe besteht darin, Daten über den SPI zu übertragen und Prozessorzeit mit dem DMA zu sparen. Der verwendete SPI ist SPI1 mit Pins PA4 bis PA7. Ich habe für DMA den 3-rd-Stream von DMA2-Kanal 3 gewählt. Die Idee ist, das CS-Signal zu aktivieren und einige Daten im Speicher zu speichern, die dann automatisch vom DMA übertragen werden, und dann sollte DMA einen Interrupt auslösen Handler, um den CS zu deaktivieren. Hier ist der Code:Wie konfiguriert man STM32f405 SPI, um Daten mit DMA zu übertragen?

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++); 
     } 

    } 
} 

Mit dem Debugger die ich gefunden habe, dass die DMA2_Stream3_IRQHandler nie aktiviert. Gemäß dem Referenzhandbuch sollte DMA die Daten übertragen, wenn das TXE-Flag von dem SPI_DR-Register 1 ist, was so war. Auch das Flag TXDMAEN vom SPI_CR2 wurde gesetzt. Ich überprüfte auch das DMA S3CR-Register und die Flags TCIE und EN wurden ebenfalls gesetzt. Zusätzlich ist die Funktion DMA2_Stream3_IRQHandler für die Hauptfunktion sichtbar. Still DMA2_Stream3_IRQHandler wurde nie aktiviert.

UPDATE: wenn ich manuell zurückgesetzt registriert die EN-Bit von DMA2_S3CR dann wird der DMA2_Stream3_IRQHandler ausgelöst. Gemäß dem Referenzhandbuch wird dieses Bit durch die Hardware gelöscht:

  • auf einem DMA Ende der Übertragung (Strom bereit konfiguriert werden)
  • wenn ein Übertragungsfehler auf den Master-Bussen AHB tritt
  • wenn die FIFO-Schwelle auf Speicher AHB-Port ist nicht kompatibel mit der Größe des Bursts

ich habe auch die SPI_Config und die Analog_Out_Config geändert, aber noch ohne Eingriff mit dem Debugger DMA2_Stream3_IRQHandler wird nie ausgelöst. Es sieht so aus, als ob der DMA die Übertragung nicht auslöst und aus irgendeinem Grund nicht beenden kann. Wie kann ich herausfinden, ob DMA eine Übertragung ausgelöst hat?

+0

TL; DR, aber als Hinweis muss der DMA im Peripheriegerät aktiviert sein, bevor die eigentliche Übertragung gestartet wird. Ein Aspekt ist, dass Sie CPU- und DMA-Übertragungen nicht (einfach) mischen können. (Dies gilt beispielsweise auch für die USART). – Olaf

+0

Ich nehme an, Sie haben nicht den ganzen Code hier, aber in der Frage, rufen Sie nicht 'SPI_Config', wo Sie Ihre DMA einrichten. Wenn dies tatsächlich der Fall ist, ist das wahrscheinlich der Grund, warum es nicht funktioniert. – rjp

+0

@rjp Ich benutze SPI_Config, um den SPI einschließlich der DMA für die SPI-Übertragung einzurichten. Aber ich aktiviere den DMA mit der Funktion Analog_Out_Config. Ich habe das schon vorher für andere Teile des Systems wie ADC getan und es war nie ein Problem. –

Antwort

0

Sie haben Ihre DMA-Konfigurationsstruktur DMA_Mode auf DMA_NORMAL eingestellt. Es sollte wahrscheinlich auf DMA_PFCTRL gesetzt werden, was eine periphere Flusssteuerung ist. Dies führt dazu, dass der DMA auf das Signal vom Peripheriegerät (das Sie konfiguriert haben) wartet, anstatt kontinuierlich zu laufen. Ich würde jedoch erwarten, dass Sie ohne diese Einstellung ein oder zwei Wörter aus dem SPI-Bus bekommen würden, da SPI_DR die gesamte Speichereinrichtung in der DMA-Übertragung nacheinander erhalten sollte (da es das erste Wort ausschaltet) .

Eine andere Sache zu überprüfen ist, dass keine Ihrer Peripheriegeräte (SPI1, DMA2) zurückgesetzt sind. Ich sehe, dass Sie die Uhr deaktivieren, aber ich erinnere mich nicht, wenn ST auch das Peripheriegerät aus dem Zurücksetzen auf diesem Anruf nimmt.

Hinweis: Ich habe die DMA-Peripherie STM32F2xx als Referenz verwendet, aber die Peripheriegeräte STM32F4xx neigen dazu, Supersätze zu sein. Es sieht auch so aus, als ob Sie eine andere Version der ST Peripheral Library verwenden als die, die ich als Referenz verwendet habe.

+0

Die F4-Peripheriegeräte sind praktisch identisch mit denen von F2 (ich wünschte, einige wären nicht ...). Als zusätzliche Anmerkung: Ich würde es ablehnen, den Müll STlib zu benutzen. Es ist nur Bloatware und kompliziert die Dinge unnötig. Es "HAL" zu nennen ist ein Scherz, man muss sowieso die Hardware kennen. Verwenden Sie einfach den Header der peripheren Definitionen und schreiben Sie eigene Treiber, um Hardware-Zugriffe einzukapseln. – Olaf

+0

Das haben wir getan, obwohl es schon ein paar Jahre her ist. Ich bin zu der Kinetis-Familie übergegangen, die mit KSDK 2.0 tatsächlich einige brauchbare Treiber hat (obwohl ich immer noch ein paar mit einem portablen Framework habe, das ich an die Kinetis-Peripheriegeräte portiert habe). – rjp

+0

@Programmed beide Familien tatsächlich (und einige andere). Trotzdem muss mir eine Bibliothek angezeigt werden, die für Treiber mit direktem Hardwarezugriff geeignet ist. Ein Hauptproblem ist, dass diese Bibliotheken oft eine Systemarchitektur durchsetzen, die nicht dem entspricht, was ich bevorzuge (und sich in verschiedenen Projekten als hervorragend erwiesen hat). Diese Bibliotheken halten sich an einen veralteten Programmierstil, insbesondere auf dem Cortex-M3/4/7, der Interrupt-Prioritäten, eine sehr clevere Multithreading-Unterstützung usw. bietet. - Harte Worte? Könnte sein. Aber ich habe gute Gründe dafür. – Olaf