Saya mendapatkan masalah seperti ini: Saya perlu mentransfer data antara dua mikrokontroler STM32F407 setidaknya dengan kecepatan 100 Mbps. Mungkin saja menggunakan Ethernet (MAC-to-MAC), tetapi masalahnya adalah sibuk, dari data inilah mereka diambil ...
Dari pinggiran kosong mungkin hanya ada SPI - tetapi hanya 42 Mbps.
Anehnya, tidak ada yang siap ditemukan di jaringan. Dan saya memutuskan untuk mengimplementasikan register 8-bit clock paralel. Dan apa - frekuensinya dapat diatur ke 10 MHz (yaitu, tentu saja, jam itu sendiri dua kali lebih cepat, tetapi 20 MHz bukanlah sesuatu yang rumit) - jadi dengan frekuensi yang begitu rendah Anda tidak perlu khawatir tentang perkabelan papan. Dan kecepatannya akan 100 Mbps.
Tidak lebih cepat dikatakan daripada dilakukan. Secara umum, sistemnya terlihat seperti ini. Kami menggunakan penghitung waktu di sisi transmisi, salah satu sinyal pembanding adalah output ke pin - ini akan menjadi sinyal jam, dan yang kedua akan digunakan untuk memulai satu burst untuk DMA.
Saya memiliki bus pada 82 MHz (karena konsumsi saat ini pada frekuensi yang lebih tinggi :), timer pada frekuensi yang sama: sehingga dengan periode ARR = 8 ternyata sekitar 10 MHz (jadi itu akan menjadi sekitar 80 Mbps, well, oke).
DMA akan mentransfer satu byte dari memori (dengan penambahan otomatis, tentu saja) langsung ke port output register - dalam kasus saya, PORTE muncul - 8 bit pertamanya hanya sesuai dengan alamat penerima DMA.
Di sisi penerima, kami akan menggunakan sinyal jam di kedua sisi untuk mencatat timer, dengan periode 1, dan kami akan menggunakan sinyal pembaruan untuk mulai meneruskan untuk DMA, yang membaca data dari port (port PORTE kembali mendekati) dan menulis ke memori dengan peningkatan otomatis.
Sekarang tinggal mengkonfigurasi semuanya dengan benar (kode di bawah) dan menjalankan. Pengakhiran di kedua sisi ditentukan oleh interupsi dari DMA.
Namun, untuk kelengkapan, tentu saja Anda harus menyertakan pemeriksaan untuk keterlambatan transmisi dan penanganan kesalahan dalam kode, tapi saya hilangkan ini.
Dalam kode di bawah ini, timer TIM8 menggunakan saluran CC2 untuk mengeluarkan sinyal - untuk melihat apa yang terjadi.
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); }
Untuk pengujian, papan yang sama digunakan, hanya output clock PE9 terhubung ke input PC6. Loop utama tampak seperti ini:
ii_receive(rdata, 256); ii_transmit(tdata, 256); while (!transmit_done); while (!receive_done);
Menurut hasil: data dikirim dengan sempurna selama 30-31 mikrodetik tanpa kehilangan. Sinyal terlihat seperti ini:
di sini, putih adalah output dari timer TIM8, merah adalah sinyal clock (TIM1), dan oranye adalah bit data yang paling tidak signifikan (0-1-0-1 -...).
Apa yang saya tidak suka tentang ini adalah bahwa Anda tidak dapat memulai DMA dari gangguan dari input GPIO, Anda harus bekerja dengan timer. Mungkin seseorang akan memberi tahu Anda cara lain?
PS Sebagai hasil dari percobaan baru, ternyata menaikkan frekuensi ke 168 MHz secara alami meningkatkan kecepatan sebanyak 2 kali dan data ditransmisikan dalam 14 mikrodetik (mis. 150 Mbps), tetapi ketika master timer berkurang di bawah 7, sisi penerima mulai glitch - timer tidak punya waktu TIM8. Pada 7 masih berfungsi, tetapi pada 6 sudah hilang, dan setelah semua itu akan menjadi 200 Mbps ...