So übertragen Sie Daten zwischen Mikrocontrollern mit 100 Mbit / s

Ich habe diese Art von Problem: Ich muss Daten zwischen zwei STM32F407-Mikrocontrollern mindestens mit einer Geschwindigkeit von 100 Mbit / s übertragen. Es wäre möglich, Ethernet (MAC-zu-MAC) zu verwenden, aber das Problem ist, dass es beschäftigt ist. Aus diesen Daten werden sie entnommen ...
Von der freien Peripherie gibt es vielleicht nur SPI - aber es sind nur 42 Mbit / s.

Seltsamerweise wurde im Netzwerk nichts Bereites gefunden. Und ich habe beschlossen, ein paralleles 8-Bit-Taktregister zu implementieren. Und was - die Frequenz kann auf 10 MHz eingestellt werden (das heißt natürlich, der Takt selbst ist doppelt so schnell, aber 20 MHz sind nicht kompliziert) - bei einer so niedrigen Frequenz müssen Sie sich also keine Gedanken über die Verkabelung der Platine machen. Und die Geschwindigkeit wird 100 Mbit / s betragen.

Kaum gesagt als getan. Im Allgemeinen sieht das System so aus. Wir verwenden einen Timer auf der Sendeseite, eines der Vergleichssignale wird an einen Pin ausgegeben - dies ist ein Taktsignal und das zweite wird verwendet, um einen Burst für DMA zu starten.

Ich habe einen Bus mit 82 MHz (aufgrund des Stromverbrauchs bei einer höheren Frequenz :), einen Timer mit derselben Frequenz: so dass sich bei einer Periode von ARR = 8 ungefähr 10 MHz herausstellen (also ungefähr 80 Mbit / s, na gut).

DMA überträgt ein Byte aus dem Speicher (natürlich mit automatischer Inkrementierung) direkt an den Registerausgangsport - in meinem Fall kam PORTE - seine ersten 8 Bits passen genau als Adresse des DMA-Empfängers.

Auf der Empfangsseite verwenden wir ein Taktsignal an beiden Flanken, um den Timer mit einer Periode von 1 zu takten, und wir verwenden das Aktualisierungssignal, um die Weiterleitung für den DMA zu starten, der Daten vom Port liest (der PORTE-Port nähert sich erneut) und mit automatischer Inkrementierung in den Speicher schreibt.

Jetzt bleibt alles richtig zu konfigurieren (Code unten) und auszuführen. Die Beendigung auf beiden Seiten wird durch die Unterbrechung durch den DMA bestimmt.

Der Vollständigkeit halber müssen Sie natürlich Überprüfungen auf Übertragungsverzögerungen und Fehlerbehandlung in den Code aufnehmen, aber ich lasse dies weg.

Im folgenden Code verwendet der TIM8-Timer den CC2-Kanal, um das Signal auszugeben - um zu sehen, was passiert.

volatile int transmit_done; volatile int receive_done; void DMA2_Stream1_IRQHandler(void) { TIM8->CR1 &= ~TIM_CR1_CEN; DMA2->LIFCR |= 0b1111 << 8; receive_done = 1; } void DMA2_Stream4_IRQHandler(void) { TIM1->CR1 &= ~TIM_CR1_CEN; TIM1->EGR |= TIM_EGR_BG; DMA2->HIFCR |= 0b1111101; transmit_done = 1; } void ii_receive(uint8_t *data, int len) { GPIOE->MODER = (GPIOE->MODER & 0xFFFF0000) | 0x0000; DMA2_Stream1->PAR = (uint32_t) &(GPIOE->IDR); DMA2_Stream1->M0AR = (uint32_t) data; DMA2_Stream1->NDTR = len; TIM8->CNT = 0; TIM8->BDTR |= TIM_BDTR_MOE; receive_done = 0; DMA2_Stream1->CR |= DMA_SxCR_EN; TIM8->CR1 |= TIM_CR1_CEN; } void ii_transmit(uint8_t *data, int len) { GPIOE->MODER = (GPIOE->MODER & 0xFFFF0000) | 0x5555; DMA2_Stream4->PAR = (uint32_t) &(GPIOE->ODR); DMA2_Stream4->M0AR = (uint32_t) data; DMA2_Stream4->NDTR = len; TIM1->CNT = 6; transmit_done = 0; DMA2_Stream4->CR |= DMA_SxCR_EN; TIM1->SR |= TIM_SR_BIF; TIM1->BDTR |= TIM_BDTR_MOE; TIM1->CR1 |= TIM_CR1_CEN; } // tx: TIM1 CH4 on DMA2/stream4/channel6, CH1 on output clock in PE9 // rx: TIM8 CH2 on DMA2/stream3/channel7, CH1 on input clock in PC6 void ii_init() { __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_TIM1_CLK_ENABLE(); __HAL_RCC_TIM8_CLK_ENABLE(); __HAL_RCC_TIM2_CLK_ENABLE(); __HAL_RCC_DMA2_CLK_ENABLE(); GPIOC->MODER |= (0b10 << GPIO_MODER_MODE6_Pos) | (0b10 << GPIO_MODER_MODE7_Pos); GPIOC->PUPDR |= (0b10 << GPIO_PUPDR_PUPD7_Pos); GPIOC->AFR[0] |= (GPIO_AF3_TIM8 << 24) | (GPIO_AF3_TIM8 << 28); GPIOE->MODER |= (0b10 << GPIO_MODER_MODE9_Pos); GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9 | 0xFFFF; GPIOE->AFR[1] |= GPIO_AF1_TIM1 << 4; GPIOE->PUPDR |= (0b10 << GPIO_PUPDR_PUPD9_Pos); TIM1->ARR = 8; TIM1->CCR1 = 5; TIM1->CCR4 = 1; TIM1->EGR |= TIM_EGR_CC4G; TIM1->DIER |= TIM_DIER_CC4DE; TIM1->CCMR1 |= (0b110 << TIM_CCMR1_OC1M_Pos); TIM1->CCER |= TIM_CCER_CC1E; TIM1->EGR |= TIM_EGR_BG; TIM8->ARR = 1; TIM8->CCR2 = 1; TIM8->EGR |= TIM_EGR_UG; TIM8->DIER |= TIM_DIER_UDE; TIM8->SMCR |= (0b100 << TIM_SMCR_TS_Pos) | (0b111 << TIM_SMCR_SMS_Pos); TIM8->CCMR1 = (0b01 << TIM_CCMR1_CC1S_Pos) | (0b110 << TIM_CCMR1_OC2M_Pos); TIM8->CCER |= (0b11 << TIM_CCER_CC1P_Pos) | TIM_CCER_CC2E; DMA2_Stream1->CR = DMA_CHANNEL_7 | DMA_PRIORITY_VERY_HIGH | DMA_MINC_ENABLE | (0b00 << DMA_SxCR_DIR_Pos) | DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE; DMA2_Stream1->FCR |= DMA_FIFOMODE_ENABLE; DMA2_Stream4->CR = DMA_CHANNEL_6 | DMA_PRIORITY_VERY_HIGH | DMA_MINC_ENABLE | (0b01 << DMA_SxCR_DIR_Pos) | DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE; DMA2_Stream4->FCR |= DMA_FIFOMODE_ENABLE; HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn); HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn); } 

Für die Tests wurde dieselbe Karte verwendet, nur der PE9-Taktausgang wurde mit dem PC6-Eingang verbunden. Die Hauptschleife sah folgendermaßen aus:

  ii_receive(rdata, 256); ii_transmit(tdata, 256); while (!transmit_done); while (!receive_done); 

Nach den Ergebnissen: Die Daten wurden perfekt für 30-31 Mikrosekunden ohne Verlust gesendet. Die Signale sehen ungefähr so ​​aus:


Hier ist Weiß der Ausgang des TIM8-Timers, Rot das Taktsignal (TIM1) und Orange das niedrigstwertige Datenbit (0-1-0-1 -...).

Was mir daran nicht gefällt, ist, dass Sie DMA nicht durch Unterbrechung des GPIO-Eingangs starten können, sondern mit Timern arbeiten müssen. Vielleicht sagt dir jemand einen anderen Weg?

PS Als Ergebnis neuer Experimente stellte sich heraus, dass das Erhöhen der Frequenz auf 168 MHz die Geschwindigkeit natürlich um das Zweifache erhöhte und die Daten in 14 Mikrosekunden (d. H. 150 Mbit / s) übertragen wurden. Als der Master-Timer jedoch unter 7 reduziert wurde, begann die Empfangsseite zu stören - der Timer hat keine Zeit TIM8. Bei 7 funktioniert es immer noch, aber bei 6 ist es bereits weg und schließlich wären es 200 Mbit / s ...

Source: https://habr.com/ru/post/de460805/


All Articles