واجهت هذا النوع من المشاكل: أحتاج إلى نقل البيانات بين متحكمين دقيقين STM32F407 على الأقل بسرعة 100 ميجابت في الثانية. قد يكون من الممكن استخدام Ethernet (من MAC إلى MAC) ، لكن المشكلة هي أنه مشغول ، ومن هذه البيانات يتم نقلهم ...
من المحيط الخمول ربما لا يوجد سوى SPI - لكنه فقط 42 ميغابت في الثانية.
الغريب أنه لم يتم العثور على أي شيء جاهز على الشبكة. وقررت تنفيذ سجل ساعة 8 بت مواز. وأيضًا - يمكن ضبط التردد على 10 ميجاهرتز (أي ، بالطبع ، الساعة نفسها أسرع مرتين ، ولكن 20 ميغاهيرتز ليست شيئًا معقدًا) - لذلك مع التردد المنخفض الذي لا داعي للقلق بشأن توصيل اللوحة. وستكون السرعة 100 ميغابت في الثانية.
لم يقل قال من القيام به. بشكل عام ، فإن النظام يشبه هذا. نستخدم مؤقتًا على جانب الإرسال ، وإحدى إشارات المقارنة يتم إخراجها إلى دبوس - ستكون هذه إشارة ساعة ، وسيتم استخدام الثانية لبدء رشقة واحدة لـ DMA.
لديّ حافلة بسرعة 82 ميجاهرتز (بسبب الاستهلاك الحالي بتردد أعلى :) ، جهاز ضبط مؤقت على نفس التردد: بحيث تتحول فترة ARR = 8 إلى حوالي 10 ميجاهرتز (لذلك سيكون حوالي 80 ميجابت في الثانية ، حسنًا ، حسنًا).
ستنقل DMA بايت واحد من الذاكرة (مع زيادة تلقائية ، بالطبع) مباشرة إلى منفذ إخراج التسجيل - في حالتي ، جاء PORTE - أول 8 بتات تناسب فقط كعنوان جهاز استقبال DMA.
على جانب المتلقي ، سوف نستخدم إشارة الساعة على كلا الحواف لساعة الموقت ، مع فترة من 1 ، وسوف نستخدم إشارة التحديث لبدء إعادة التوجيه لـ DMA ، الذي يقرأ البيانات من المنفذ (منفذ PORTE اقترب مرة أخرى) والكتابة إلى الذاكرة مع زيادة تلقائية.
الآن يبقى تكوين كل شيء بشكل صحيح (الكود أدناه) وتشغيله. يتم تحديد الإنهاء على كلا الجانبين عن طريق المقاطعة من DMA.
ومع ذلك ، للتأكد من اكتمالها ، بالطبع ، يجب عليك تضمين اختبارات تأخر الإرسال ومعالجة الأخطاء في التعليمات البرمجية ، لكني حذفت ذلك.
في الكود أدناه ، يستخدم مؤقت TIM8 قناة CC2 لإخراج الإشارة - لمعرفة ما يحدث.
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); }
للاختبارات ، تم استخدام نفس اللوحة ، تم توصيل خرج ساعة PE9 فقط بإدخال PC6. بدا الحلقة الرئيسية مثل هذا:
ii_receive(rdata, 256); ii_transmit(tdata, 256); while (!transmit_done); while (!receive_done);
وفقا للنتائج: تم إرسال البيانات تماما لمدة 30-31 ميكروثانية دون خسارة. الإشارات تبدو شيء مثل هذا:
هنا ، الأبيض هو خرج الموقت TIM8 ، والأحمر هو إشارة الساعة (TIM1) ، والبرتقالي هو أقل جزء من البيانات أهمية (0-1-0-1 -...).
ما لا يعجبني في هذا هو أنه لا يمكنك بدء DMA من مقاطعة GPIO الإدخال ، يجب عليك العمل مع أجهزة ضبط الوقت. ربما شخص ما سوف اقول لك بطريقة أخرى؟
ملاحظة: نتيجة للتجارب الجديدة ، اتضح أن رفع التردد إلى 168 ميجاهرتز زاد بشكل طبيعي من السرعة بمقدار مرتين وتم نقل البيانات في 14 ميكروثانية (أي 150 ميجابت في الثانية) ، ولكن عندما تم تقليل المؤقت المؤقت دون 7 ، بدأ الجانب المتلقي في خلل TIM8. في الساعة السابعة ، لا تزال تعمل ، ولكن في الساعة السادسة ، انتهى الأمر بالفعل ، وبعد كل شيء سيكون 200 ميغابت في الثانية ...