J'ai eu ce genre de problème: j'ai besoin de transférer des données entre deux microcontrôleurs STM32F407 au moins à une vitesse de 100 Mbps. Il serait possible d'utiliser Ethernet (MAC-à-MAC), mais le problème est qu'il est occupé, c'est à partir de ces données qu'elles sont prises ...
De la périphérie inactive, il n'y a peut-être que SPI - mais ce n'est que 42 Mbps.
Curieusement, rien de prêt n'a été trouvé sur le réseau. Et j'ai décidé d'implémenter un registre d'horloge 8 bits parallèle. Et quoi - la fréquence peut être réglée sur 10 MHz (c'est-à-dire, bien sûr, l'horloge elle-même est deux fois plus rapide, mais 20 MHz n'est pas quelque chose de compliqué) - donc avec une fréquence aussi basse, vous n'aurez pas à vous soucier du câblage de la carte. Et la vitesse sera de 100 Mbps.
Aussitôt dit, aussitôt fait. En général, le système ressemble à ceci. Nous utilisons une minuterie du côté émission, l'un des signaux de comparaison est émis vers une broche - ce sera un signal d'horloge et le second sera utilisé pour démarrer une rafale pour DMA.
J'ai un bus à 82 MHz (en raison de la consommation de courant à une fréquence plus élevée :), une minuterie à la même fréquence: de sorte qu'avec une période d'ARR = 8, il s'avère environ 10 MHz (ce sera donc environ 80 Mbps, eh bien, d'accord).
Le DMA transférera un octet de la mémoire (avec incrémentation automatique, bien sûr) directement vers le port de sortie du registre - dans mon cas, PORTE est apparu - ses 8 premiers bits correspondent à l'adresse du récepteur DMA.
Du côté de la réception, nous utiliserons un signal d'horloge sur les deux fronts pour cadencer le temporisateur, avec une période de 1, et nous utiliserons le signal de mise à jour pour commencer le transfert pour le DMA, qui lit les données du port (le port PORTE à nouveau approché) et écrit dans la mémoire avec incrémentation automatique.
Reste maintenant à tout configurer correctement (code ci-dessous) et à exécuter. La terminaison des deux côtés est déterminée par l'interruption du DMA.
Cependant, pour être complet, vous devez bien sûr inclure des vérifications des retards de transmission et de la gestion des erreurs dans le code, mais je l'omet.
Dans le code ci-dessous, la minuterie TIM8 utilise le canal CC2 pour émettre le signal - pour voir ce qui se passe.
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); }
Pour les tests, la même carte a été utilisée, seule la sortie d'horloge PE9 a été connectée à l'entrée PC6. La boucle principale ressemblait à ceci:
ii_receive(rdata, 256); ii_transmit(tdata, 256); while (!transmit_done); while (!receive_done);
Selon les résultats: les données ont été parfaitement envoyées pendant 30 à 31 microsecondes sans perte. Les signaux ressemblent à ceci:
ici, le blanc est la sortie du temporisateur TIM8, le rouge est le signal d'horloge (TIM1) et l'orange est le bit de données le moins significatif (0-1-0-1 -...).
Ce que je n'aime pas, c'est que vous ne pouvez pas démarrer DMA à partir d'une interruption à partir de l'entrée GPIO, vous devez travailler avec des minuteries. Peut-être que quelqu'un vous dira une autre façon?
PS À la suite de nouvelles expériences, il s'est avéré que l'augmentation de la fréquence à 168 MHz a naturellement augmenté la vitesse de 2 fois et les données ont été transmises en 14 microsecondes (soit 150 Mbps), mais lorsque la minuterie principale a été réduite en dessous de 7, le côté récepteur a commencé à briller - la minuterie n'a pas le temps TIM8. À 7 ça marche toujours, mais à 6 c'est déjà parti, et après tout ce serait 200 Mbps ...