إدخال البيانات في STM32F4xx من ADC الموازي عبر DCMI

من المعروف أن عائلة متحكم STM32F4xx ، التي تحتوي على نوى إنتاجية كافية ومناسبة تمامًا للمهام "التي لا تقطع اللحوم" ، لا تملك DSPs واجهة إدخال بيانات كاملة مع أبسط ناقل متوازي في وضع "خط توجيه البيانات". بعد أن قمت بتدخين dm00037051.pdf ، وجدت خيارًا غير مناسب ، للوهلة الأولى - واجهة DCMI (واجهة الكاميرا الرقمية).

بطبيعة الحال ، فإن استخدام متحكم دقيق STM32 لجهاز DSP الكلاسيكي المحمل (FIR ، IIR ، FFT) ليس خيارًا مثاليًا ، ولكن إذا وضعت البطاقات فجأة وما زالت إمكانيات جهاز التحكم الصغير كافية ، بالإضافة إلى أنك تحتاج إلى عدد كافٍ من واجهات السرعة المنخفضة. حول هذا تحت خفض.





في أحد المشروعات ذات المواعيد النهائية والميزانية "الساخنة" ، كانت هناك حاجة لتطبيق "قطعة حديدية" مع أفضل المواضع: الكتلة ، الأبعاد ، الاستهلاك. كدالة أساسية ، كانت معالجة الإشارات الرقمية (التصفية والتحليل الإحصائي) ، القادمة من ADC في وضع الوقت الحقيقي الناعم ، مطلوبة. للمعالجة ، كنت أرغب في الحصول على نقطة عائمة دقيقة واحدة. تم استلام الإشارة من ADC بتردد متوسط ​​قدره 48 MHz. 1 ميغاهيرتز إشارة الفرقة. لتنفيذ نقل طيف الإشارة من التردد الوسيط إلى الصفر ، يفضل استخدام أخذ عينات فرعية للنطاق العريض ADC. أيضًا ، كان من الضروري تلقي ونقل المعلومات عبر Ethernet و SPI و UART و I2C والعمل مع المقاطعات.


لم تسمح المواعيد النهائية للتنفيذ ووظائف DSP المحددة باستخدام FPGAs لهذه الأغراض. مع معالج الإشارات المعروف لعائلة Blackfin ، لم تكن الأجهزة التناظرية المعروفة لديها أي خبرة في التواصل ولم تكن هناك أدوات لتصحيح الأخطاء ولوحات تجريبية في الوصول القريب. كانت هناك تجربة فقط من الاتصال الوثيق والطويل مع مكلفة ، مرة واحدة المعالج الرئيسي DSP ADSP-TS201 TigerSHARC. بالإضافة إلى ذلك ، تفتقر Blackfin إلى تنفيذ الأجهزة من IEEE-754. أيضًا ، كان من الضروري قبول كتلة مستمرة من البيانات مع ADC من 128 كيلو بايت ، بالإضافة إلى 30 كيلو بايت من معالجة الحمل وبدون ذاكرة خارجية ، كان من الصعب دمج كل شيء في شيء أقل ميزانية.


بشكل عام ، لم يكن هناك سوى لوحات STM32F407 (اكتشاف والعرف من الصين) في متناول اليد. كما أظن ، فإن العديد من الذين يتعاملون مع الموضوعات ذات الصلة لديهم الآن هذه المساعدة العالمية في متناول اليد. كان هناك أيضًا لوحة Terasic ADA-HSMC حيث تم تثبيت ADC ثنائي القناة AD9228 (12 بت ، Fd = 65 msps ، عرض النطاق = 315 MHz).


يعد الخيار DCMI (واجهة الكاميرا الرقمية) أحد الخيارات غير المحددة ، ولكنه مناسب تمامًا ، وهو الجهاز المطبق في STM32F4.


تم وصف تشغيل هذه الواجهة في RM0090 ، DocID018909 ، الصفحة 454/1718. الأرقام الأربعة التالية مقدمة من هذه الوثيقة.


لذلك ، تردد الإدخال المطالب يصل إلى 54 ميغاهيرتز. وهو ما يكفي - معدل أخذ العينات الفرعي لدينا هو 10 ميغاهيرتز. فيما يلي مجموعة من إشارات واجهة DCMI المستخدمة:





ملاحظة: D13 ، D14 متاحة فقط في حزمة 144 دبوس. لدينا 100 دبوس ، لكننا لسنا في حاجة إليها. (على الرغم من أن الأجهزة التناظرية لها 14 بت مماثلة ADC - AD9248).


فيما يلي مخطط توقيت عام للواجهة:





وهذا هو مخطط توقيت الواجهة في وضع تنسيق إطار JPEG:





سوف نستخدم وضع التشغيل هذا انه يناسبنا أفضل.


في هذا الوضع ، يتم سحب إشارة VSYNC إلى السلطة. سوف نستخدم HSYNC كإشارة خارجية لتمكين بدء استقبال البيانات عبر الواجهة.


استخدمنا متحكم STM32F407RGT6 في الحزمة LQFP100.


يحتوي AD9238 ADC على مدخل لوضع إيقاف التشغيل (توفير الطاقة) للقناة المقابلة PDWN_A (B) ، وإذن الإخراج هو OEB_A (B). من المنطقي الحصول عليها من أي دبابيس تحكم. نتيجة لذلك ، سيبدو مخطط اتصال pin كما يلي:



نظرًا لأن ADC لا يحتوي على إشارة ساعة إخراج ، فمن الضروري استخدام الضرب (ساعة عازلة). استخدمنا LMK00101 من Texas Instruments - قيمة جيدة للسعر ، وارتعاش منخفض ، والأهم من ذلك ، مرة أخرى - في متناول اليد).


أثناء المعالجة ، نأخذ في الاعتبار أن البيانات الموجودة على الناقل المتوازي ADC تظهر مع تأخير لمدة 7 دورات على مدار الساعة بالنسبة إلى إشارة ساعة الإدخال.


سوف نتلقى البيانات (بالطبع) من خلال DMA. هنا هو شفرة المصدر لتهيئة DCMI و DMA.


نقوم بتشغيل قطع مسافة المنافذ التي نحتاجها ، DCMI و DMA2


GPIO_InitTypeDef GPIO_InitStructure; /* Enable DCMI GPIOs clocks */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD, ENABLE); /* Enable DCMI clock */ RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); 

سيقوم هذا الدبوس (PA5) بمحاكاة التقسيم إلى إطارات - HSYNC. التهيئة عند الخروج


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_ResetBits(GPIOA, GPIO_Pin_5); //HSYNC_PA4 -PA5 -> GND 

تكوين دبابيس المقابلة في وضع DCMI


  /* PCLK */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_DCMI); /* D0-D7 */ GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_DCMI); //D0* GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_DCMI); //D1* GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_DCMI); //D2* GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_DCMI); //D3* GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_DCMI); //D4* GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_DCMI); //D5* GPIO_PinAFConfig(GPIOE, GPIO_PinSource5, GPIO_AF_DCMI); //D6* GPIO_PinAFConfig(GPIOE, GPIO_PinSource6, GPIO_AF_DCMI); //D7* GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_DCMI); //D8* GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_DCMI); //D9* GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_DCMI); //D10* GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_DCMI); //D11* /* VSYNC */ GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_DCMI); /* HSYNC */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_DCMI); /* DCMI GPIO configuration **************************************************/ /* D0,D1,D2,D3,D4,D8,D9 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ; GPIO_Init(GPIOC, &GPIO_InitStructure); /* D6,D7*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6; GPIO_Init(GPIOE, &GPIO_InitStructure); /* D10, D5, VSYNC(PB7) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_Init(GPIOB, &GPIO_InitStructure); /* D11(PD2) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &GPIO_InitStructure); /* PCLK(PA6) HSYNC(PA4)*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); 

الشيء الأكثر إثارة للاهتمام هنا هو تكوين DCMI في الوضع المقابل لإطارات JPEG.


  DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_Continuous; DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Embedded; DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising; DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_Low; DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_High; DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame; DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_12b; 

إعداد DMA


  DCMI_Init(&DCMI_InitStructure); DMA_InitStructure.DMA_Channel = DMA_Channel_1; DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS; //0x50050028 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DCMI_PendingData; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = MAX_DOWBLE_BUF; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream1, &DMA_InitStructure); 

تحديد المقاطعات في نهاية تلقي البيانات من قناة DMA المقابلة


  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 

رمز معالجنا ، الذي نوقف فيه استقبال بيانات DCMI ونضع إشارة جاهزة للبيانات.


 void EXTI1_IRQHandler() { EXTI_ClearFlag(EXTI_Line1); DMA_ClearITPendingBit(DMA2_Stream1, DMA_IT_TCIF1); DMA_Cmd(DMA2_Stream1, DISABLE); DCMI_Cmd(DISABLE); dma_recv_f = 1; //GPIO_ResetBits(GPIOE, GPIO_Pin_0); //VSYNC reset } 

كل شيء مع الإعدادات. الآن نقوم بتشغيل قناة DMA الخاصة بنا ، كتلة DCMI ، نبدأ في تلقي بيانات DCMI في وضع إطار JPEG.


  /* Enable DMA transfer */ DMA_Cmd(DMA2_Stream1, ENABLE); /* Enable DCMI interface */ DCMI_Cmd(ENABLE); /* Start Image capture */ DCMI_CaptureCmd(ENABLE); DCMI_JPEGCmd(ENABLE); 

دورة البرنامج الرئيسي. هنا ، استقصاء العلم وإعادة تشغيل استقبال البيانات.


 uint8_t dma_recv_f = 0; //,     . uint16_t DCMI_PendingData[8500]; //    DCMI int main(void) { DCMI_Config(); //        __enable_irq(); //  while(1) { while(dma_recv_f!=1){}; //    dma_recv_f = 0; //  /*     */ Re_DMA_Config(DCMI_PendingData, glob_cnt); // DMA } } 

ملاحظة: إذا كنت بحاجة إلى تلقي البيانات ومعالجتها في الوقت الفعلي الصعب مع التخزين المؤقت المزدوج ، فإن stm32f4 لديه آلية مقاطعة عندما يكون نصف المخزن المؤقت ممتلئًا. في إعدادات DMA ، من الضروري ضبط الوضع الدوري المستمر لاستقبال البيانات. على سبيل المثال:


  // DMA2 Stream0 channel0 configuration ************************************** DMA_InitStructure.DMA_Channel = DMA_Channel_1; DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS; //0x50050028 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DCMI_PendingData; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_BufferSize = buf_size; <b>DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;</b> //     DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; <b>DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;</b> //  DMA     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 

في معالج المقاطعة ، من الضروري إعادة تعيين بت نهاية DMA فقط بعد ملء المخزن المؤقت بأكمله ، وبرنامج المستخدم للإشارة إلى عدد المخزن المؤقت الحالي الذي انتهى إليه استقبال البيانات. شيء مثل هذا:


 if(DMA_GetITStatus(DMA2_Stream1,DMA_IT_TCIF1)){ DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF1); num_buf = 1; } else{ num_buf = 0; } 

هذا بشكل أساسي كل ما هو مطلوب لتلقي البيانات من ADC عبر DCMI.


لسوء الحظ ، في الوقت الحالي لا يمكنني إظهار هذه الآلية بالكامل بشكل مباشر. قطعة الحديد هي بالفعل حوالي 3 سنوات في العملية))). يمكنني فقط إحضار بيانات تسجيل الاختبار المحفوظة من تلك الأوقات.


فيما يلي إشارة توافقية من مولد SMB100A بتردد 48.001 ميجاهرتز ، مساوية لـ IF الخاص بنا مع إزاحة 1 KHz:





وهذا هو طيفها:





للتحقق من الحد الأقصى للأداء على أسلاك اللوح القياسية بطول 200 ملم تقريبًا ، حيث تم توصيل ADA-HSMC و STM32F4 Discovery ، تم استلام البيانات الصحيحة بتردد على مدار الساعة قدره 40 ميجاهرتز.
على لوحة "أصلية" تم تصنيعها لهذه المهمة ، من خلال كبل مسطح بطول 100 مم ، عند درجة حرارة الغرفة ، اتضح أن يرفع تردد أخذ العينات إلى 54 ميجا هرتز كحد أقصى.
على تردد أخذ العينات المطلوب من MHz 10 ، تم اختبار الأداء على النطاق الصناعي: من -40 إلى +60.


في الواقع كل شيء. شكرا لاهتمامكم!




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


All Articles