تنفيذ PPPOS على stm32f4-discovery

ذات مرة ، كانت هناك مهمة لتوفير الوصول إلى الإنترنت على STM32 بعد أن كان لهذا منفذ COM فقط. لحل هذه المشكلة ، كنت بحاجة إلى PPP ، أو ، على وجه الدقة ، PPPoS (بروتوكول نقطة إلى نقطة عبر المسلسل - إحدى طرق تنفيذ PPP ، يتم استخدامه عند الاتصال عبر منفذ COM).

في عملية حل المهمة التي تم تعيينها قبلي ، واجهت بعض الصعوبات ، أحدها ، في رأيي ، هو عدم كفاية تغطية القضايا المتعلقة بـ PPPoS على الإنترنت. مع هذا المنشور ، سأحاول إغلاق الفجوة المحددة ، بقدر ما تسمح معرفتي المتواضعة.

توضح هذه المقالة كيفية إنشاء مشروع لـ System Workbench لـ STM32 من البداية. يعرض مثالا للعمل مع UART. هناك أمثلة برمجية لتطبيق PPP. وبالطبع ، مثال على إرسال رسالة إلى كمبيوتر مجاور.

مقدمة


PPP (بروتوكول نقطة إلى نقطة) هو بروتوكول ربط بيانات من نقطتين لنموذج شبكة OSI. يتم استخدامه عادة لإنشاء اتصال مباشر بين عقدتين للشبكة ، ويمكن أن يوفر مصادقة الاتصال والتشفير وضغط البيانات. تستخدم على أنواع كثيرة من الشبكات المادية: كبل مودم ، خط هاتف ، شبكة خلوية ، إلخ.

غالبًا ما توجد أنواع فرعية من بروتوكول PPP ، مثل بروتوكول نقطة إلى نقطة عبر إيثرنت (PPPoE) ، تستخدم للاتصال عبر إيثرنت ، وأحيانًا من خلال DSL ؛ وبروتوكول نقطة إلى نقطة عبر ATM (PPPoA) ، والذي يستخدم للاتصال عبر ATM Adaptation Layer 5 (AAL5) ، وهو البديل الرئيسي لـ PPPoE لـ DSL.

PPP هي مجموعة من البروتوكولات: بروتوكول التحكم في الارتباط (LCP) وبروتوكول التحكم في الشبكة (NCP) وبروتوكولات المصادقة (PAP و CHAP) و PPP متعدد القنوات (MLPPP).

من ويكيبيديا .

تحضير


لحل المشكلة نحتاج إلى:

الحديد:


  1. لوحة التصحيح stm32f4_discovery:

  2. محول USB إلى miniUSB لتوصيل اللوحة بجهاز كمبيوتر.
  3. اثنان من محولات USBtoUART FT232:

  4. كما أن كبلي تمديد USB مفيدان أيضًا ، وليس بالضرورة ، ولكنهما مناسبان.

ناعم:


  1. الآلة الافتراضية VirtualBox. يمكنك تنزيله من هنا . نقوم أيضًا بتنزيل وتثبيت حزمة الملحقات لـ VirtualBox.
  2. قرصان للتثبيت مع أنظمة التشغيل Windows و Linux. نأخذ Windows هنا ، لينكس هنا .

    بعد تثبيت نظام التشغيل ، ستحتاج إلى تثبيت الوظائف الإضافية لنظام التشغيل الضيف. للمهمة لدينا ما يكفي من أنظمة 32x ، لا يمكنك أن تخدع بتضمين المحاكاة الافتراضية.
  3. بالنسبة لنظام التشغيل Windows ، نحتاج إلى برنامج يمكنه قبول الطلبات والاستجابة لها عبر TCP / IP ، وبرنامج طرفي للعمل مع منفذ COM. قم بتنزيل PacketSender هنا (انقر فوق "لا شكرًا ، فقط دعني أحمل") ، المحطة هنا . بالإضافة إلى ذلك ، نحتاج إلى STM32CubeMX للإعداد الأولي للمشروع. التنزيل من st.com (بعد التسجيل ، سيأتي الرابط عن طريق البريد الإلكتروني).
  4. نضع System Workbench لـ STM32 على نظام التشغيل الرئيسي. التنزيل من هنا (التسجيل مطلوب).

المرحلة 1. إنشاء مشروع


بادئ ذي بدء ، افتح STM32CubeMX وقم بإنشاء مشروع جديد هناك للوحة الاكتشاف stm32f4. قم بتشغيل RCC و Ethernet (ETH) و SYS و USART2 و USART3 ، ثم قم بتشغيل FREERTOS و LWIP.




للتشخيص ، نحتاج إلى مصابيح LED على اللوحة. لهذا ، قم بتكوين أرجل PD12-PD15 كـ GPIO_Output.



في علامة التبويب تكوين الساعة ، قم بتعيين التردد ، كما في الصورة أدناه.



بعد ذلك ، في علامة التبويب "تكوين" ، قم بتكوين منافذ USART. سنعمل معهم في وضع DMA. لدينا منفذين USART ، أحدهما سوف نستخدمه لإرسال واستقبال البيانات عبر PPP ، والثاني لتسجيل الدخول. لجعلها تعمل ، نحتاج إلى تكوين DMA على RX و TX لكلا المنفذين. لجميع أرجل توليف DMA ، اضبط متوسط ​​على الأولوية. ل USART2 leg RX اضبط الوضع على "دائري". يتم ترك بقية الإعدادات بشكل افتراضي.



ستحتاج أيضًا إلى تمكين المقاطعة الشاملة لكلا المنفذين في علامة التبويب "إعدادات NVIC".

هذا يكمل الإعداد الأولي للمشروع في STM32CubeMX. نقوم بحفظ ملف المشروع ونقوم بإنشاء التعليمات البرمجية لـ System Workbench لـ STM32.



التنفيذ


الآن دعونا نتحقق من أن الكود الذي تم تنزيله يعمل ويعمل. للقيام بذلك ، في ملف main.c في وظيفة "StartDefaultTask" ، نستبدل نص الحلقة اللامحدودة (؛؛) بمصابيح LED للتشغيل وإيقاف التشغيل.

يجب أن يكون مثل هذا:

/* StartDefaultTask function */ void StartDefaultTask(void const * argument) { /* init code for LWIP */ MX_LWIP_Init(); /* USER CODE BEGIN 5 */ /* Infinite loop */ for(;;) { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_SET); osDelay(1000); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET); osDelay(1000); } /* USER CODE END 5 */ } 

نحن تجميع البرامج الثابتة والنظر. يجب أن تومض جميع المصابيح الأربعة على اللوحة.

المرحلة 2. العمل مع USART


مهمتنا التالية هي التحقق من صحة تشغيل USART.

أول شيء يتعين علينا القيام به هو ربط FT232 بالكشف. للقيام بذلك ، انظر إلى الأرجل المطلقة لواجهة USART. لدي PD6 و PD5 لـ USART2_RX و USART2_TX على التوالي.



بالإضافة إلى PD9 و PD8 لـ USART3_RX و USART3_TX ، على التوالي.



بالإضافة إلى ذلك ، نحتاج إلى قدم GND.

نجد هذه الدبابيس على اللوحة ونوصلها بدبابيس FT232 ، في حين أن دبوس GND على اللوحة يمكن أن يكون أيًا ، ويجب توصيل دبوس RX على اللوحة بدبوس TX على FT232 ، ويجب توصيل دبوس TX على اللوحة بدبوس RX على FT232. لا يتم استخدام الاستنتاجات المتبقية.

يبقى توصيل FT232 الخاص بنا بمنافذ USB للكمبيوتر ، وكذلك توصيل لوحة الاكتشاف نفسها عبر موصل miniUSB بالكمبيوتر (يجب عدم الخلط بينه وبين microUSB).

بعد توصيل FT232 ، سيقوم نظام التشغيل الرئيسي بتثبيت برامج التشغيل الخاصة بهم ، وبعد ذلك ستحتاج هذه الأجهزة إلى إعادة توجيهها إلى ضيف Windows على الجهاز الظاهري.

الآن نضيف رمز البرنامج المطلوب لتشغيل USART الخاص بنا. للقيام بذلك ، سنضيف أربعة ملفات: usart.h ، usart.c ، logger.h ، logger.c.

محتويات الملف:

ملف usart.h
 #ifndef _USART_ #define _USART_ #include "stm32f4xx_hal.h" void usart_Open(void); bool usart_Send(char* bArray, int size_bArray); uint16_t usart_Recv(char* bArray, uint16_t maxLength); #endif /* _USART_ */ 

ملف usart.c
 #include "usart.h" #include "logger.h" #include "cmsis_os.h" #define Q_USART2_SIZE 200 xQueueHandle g_qUsart; osThreadId g_usart_rxTaskHandle; extern UART_HandleTypeDef huart2; void usart_rxTask(void); uint8_t bGet[Q_USART2_SIZE] = {0}; uint16_t g_tail = 0; void usart_Open(void) { g_qUsart = xQueueCreate( Q_USART2_SIZE, sizeof( unsigned char ) ); osThreadDef(usart_rxTask_NAME, usart_rxTask, osPriorityNormal, 0, Q_USART2_SIZE/4+128); g_usart_rxTaskHandle = osThreadCreate(osThread(usart_rxTask_NAME), NULL); HAL_UART_Receive_DMA(&huart2, bGet, Q_USART2_SIZE); } void usart_rxTask(void) { for(;;) { uint16_t length = Q_USART2_SIZE - huart2.hdmarx->Instance->NDTR; while(length - g_tail) { uint8_t tmp = bGet[g_tail]; xQueueSendToBack( g_qUsart, &tmp, 100 ); g_tail++; if (g_tail == Q_USART2_SIZE) g_tail = 0; } } } bool usart_Send(char* bArray, int size_bArray) { HAL_StatusTypeDef status; status = HAL_UART_Transmit_DMA(&huart2, bArray, size_bArray); while (HAL_UART_GetState(&huart2) != HAL_UART_STATE_READY) { if (HAL_UART_GetState(&huart2) == HAL_UART_STATE_BUSY_RX) break; osDelay(1); } if (status == HAL_OK) return true; return false; } uint16_t usart_Recv(char* bArray, uint16_t maxLength) { uint8_t tmp = 0; uint16_t length = 0; while(uxQueueMessagesWaiting(g_qUsart)) { xQueueReceive( g_qUsart, &tmp, 100 ); bArray[length] = tmp; length++; if (length >= maxLength) break; } return length; } 

ملف logger.h
 #ifndef _LOGGER_ #define _LOGGER_ void logger(const char *format, ...); #endif /* _LOGGER_ */ 

ملف logger.c
 #include "logger.h" #include "stm32f4xx_hal.h" #include <stdarg.h> extern UART_HandleTypeDef huart3; #define MAX_STRING_SIZE 1024 HAL_StatusTypeDef logger_Send(char* bArray, uint32_t size_bArray) { HAL_StatusTypeDef status; for(int i=0;i<5;i++) { status = HAL_UART_Transmit_DMA(&huart3, bArray, size_bArray); if (status == HAL_OK) break; osDelay(2); } while (HAL_UART_GetState(&huart3) != HAL_UART_STATE_READY) { osDelay(1); } return status; } void logger(const char *format, ...) { char buffer[MAX_STRING_SIZE]; va_list args; va_start (args, format); vsprintf(buffer, format, args); va_end(args); buffer[MAX_STRING_SIZE-1]=0; logger_Send(buffer, strlen(buffer)); } 

نحن بحاجة إلى usart لإرسال واستقبال البيانات على usart2. ستكون الواجهة الرئيسية للتواصل مع خادم PPP.

نحتاج إلى Logger لتنفيذ التسجيل عن طريق إرسال رسائل إلى الجهاز الطرفي. تشكل الدالة usart_Open (void) الفارغة قائمة انتظار وتبدأ مهمة خدمة قائمة الانتظار هذه. يجب إكمال هذه الوظيفة قبل استخدام USART. ثم كل شيء بسيط ، تقوم وظيفة usart_Send المنطقية (char * bArray ، int size_bArray) بإرسال البيانات إلى المنفذ ، و
uint16_t usart_Recv (char * bArray، uint16_t maxLength) تحصل عليها من قائمة الانتظار التي أضافتها إليها دالة usart_rxTask (void).

بالنسبة للمسجّل ، لا يزال الأمر أبسط ؛ ليست هناك حاجة للحصول على البيانات ، وبالتالي ، لا حاجة إلى قائمة الانتظار أو مهمة صيانة قائمة الانتظار.

في بداية الملف main.h ، تحتاج إلى إضافة عدة تعريفات تصف نوع منطقي ، وهو غير متوفر في C.

 /* USER CODE BEGIN Includes */ typedef unsigned char bool; #define true 1 #define false 0 /* USER CODE END Includes */ 

حان الوقت الآن للتحقق من وظائف الشفرة الناتجة. للقيام بذلك ، في ملف main.c ، قم بتغيير رمز المهمة المعروفة بالفعل "StartDefaultTask"

 /* USER CODE BEGIN 4 */ #include "usart.h" #include "logger.h" #define MAX_MESSAGE_LENGTH 100 /* USER CODE END 4 */ /* StartDefaultTask function */ void StartDefaultTask(void const * argument) { /* init code for LWIP */ MX_LWIP_Init(); /* USER CODE BEGIN 5 */ usart_Open(); /* Infinite loop */ uint8_t send[] = "Send message\r\n"; uint8_t recv[MAX_MESSAGE_LENGTH] = {0}; uint16_t recvLength = 0; for(;;) { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_SET); osDelay(1000); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET); osDelay(1000); if (usart_Send(send, sizeof(send)-1)) logger("SEND - %s", send); recvLength = usart_Recv(recv, MAX_MESSAGE_LENGTH-1); if (recvLength) { recv[recvLength] = 0; logger("RECV - %s\r\n", recv); } } /* USER CODE END 5 */ } 

بالإضافة إلى ذلك ، نحتاج إلى إعطاء المزيد من الذاكرة لمجموعة المهام الخاصة بنا. للقيام بذلك ، في استدعاء وظيفة osThreadDef () ، الملف main.c ، تحتاج إلى إصلاح 128 × 128 * 10 للحصول على هذا:

 osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, <b>128*10</b>); 

نحن نجمع ونومض. تومض مؤشرات LED بنفس الطريقة كما في المهمة السابقة.

لمعرفة نتيجة عملنا ، تحتاج إلى تشغيل برنامج Terminal في أجهزتنا الافتراضية. مثيل واحد من البرنامج لمنفذ التسجيل ، والثاني للمفتاح الرئيسي. ابحث في إدارة الأجهزة عن أرقام المنافذ التي تم تعيينها لـ FT232. إذا تم تعيين أرقام أكثر من 10 ، إعادة تعيين.

عند بدء تشغيل المثيل الثاني من البرنامج ، قد يحدث خطأ ، أغلق النافذة التي بها الخطأ واستمر في العمل مع البرنامج.

بالنسبة لكلا المنفذين ، نقوم بإنشاء اتصال على 115200 بود ، بتات البيانات - 8 ، التماثل - لا شيء ، بتات التوقف - 1 ، المصافحة - لا شيء.

إذا قمت بكل شيء بشكل صحيح ، فعندئذٍ في النافذة الطرفية لـ usart2 ، سيتم إرسال الرسالة "إرسال رسالة". سيتم تكرار نفس الرسالة في نافذة الوحدة الطرفية للمسجل فقط بالبادئة "إرسال -"

إذا قمت في نافذة المحطة الطرفية لـ usart2 بكتابة بعض النص في حقل "إرسال" والنقر فوق الزر المقابل على يمين هذا الحقل ، فحينئذٍ سترى نفس الرسالة في نافذة المسجل بالبادئة "RECV -"

في الصورة أدناه: على اليسار هو المسجل ، على اليمين هو usart2.



المرحلة 3. البدء باستخدام PPP


كجزء من هذه المهمة ، سنرفع اتصال PPP. بادئ ذي بدء ، قم بتمكين استخدام PPP ، وقم بتغيير قيمة PPP_SUPPORT المحددة في ملف ppp_opts.h إلى 1. ثم نعيد تعريف التعريفات الضرورية في ملف lwipopts.h ،

 /* USER CODE BEGIN 1 */ #define MEMP_NUM_SYS_TIMEOUT 8 #define CHECKSUM_GEN_IP 1 #define CHECKSUM_GEN_TCP 1 /* USER CODE END 1 */ 

في الوقت نفسه ، يجب التعليق على التعريفات القديمة.

الآن نقوم بتعديل ملف lwip.c ، قم بإدراج الكود التالي في كتلة “/ * USER CODE BEGIN 0 * /”:

 /* USER CODE BEGIN 0 */ #include "usart.h" #include "pppos.h" #include "sio.h" #include "dns.h" #include "ppp.h" static ppp_pcb *ppp; struct netif pppos_netif; void PppGetTask(void const * argument) { uint8_t recv[2048]; uint16_t length = 0; for(;;) { length=usart_Recv(recv, 2048); if (length) { pppos_input(ppp, recv, length); logger("read - PppGetTask() len = %d\n", length); } osDelay(10); } } #include "ip4_addr.h" #include "dns.h" static void ppp_link_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { struct netif *pppif = ppp_netif(pcb); LWIP_UNUSED_ARG(ctx); switch(err_code) { case PPPERR_NONE: /* No error. */ { logger("ppp_link_status_cb: PPPERR_NONE\n\r"); logger(" our_ip4addr = %s\n\r", ip4addr_ntoa(netif_ip4_addr(pppif))); logger(" his_ipaddr = %s\n\r", ip4addr_ntoa(netif_ip4_gw(pppif))); logger(" netmask = %s\n\r", ip4addr_ntoa(netif_ip4_netmask(pppif))); } break; case PPPERR_PARAM: /* Invalid parameter. */ logger("ppp_link_status_cb: PPPERR_PARAM\n"); break; case PPPERR_OPEN: /* Unable to open PPP session. */ logger("ppp_link_status_cb: PPPERR_OPEN\n"); break; case PPPERR_DEVICE: /* Invalid I/O device for PPP. */ logger("ppp_link_status_cb: PPPERR_DEVICE\n"); break; case PPPERR_ALLOC: /* Unable to allocate resources. */ logger("ppp_link_status_cb: PPPERR_ALLOC\n"); break; case PPPERR_USER: /* User interrupt. */ logger("ppp_link_status_cb: PPPERR_USER\n"); break; case PPPERR_CONNECT: /* Connection lost. */ logger("ppp_link_status_cb: PPPERR_CONNECT\n"); break; case PPPERR_AUTHFAIL: /* Failed authentication challenge. */ logger("ppp_link_status_cb: PPPERR_AUTHFAIL\n"); break; case PPPERR_PROTOCOL: /* Failed to meet protocol. */ logger("ppp_link_status_cb: PPPERR_PROTOCOL\n"); break; case PPPERR_PEERDEAD: /* Connection timeout. */ logger("ppp_link_status_cb: PPPERR_PEERDEAD\n"); break; case PPPERR_IDLETIMEOUT: /* Idle Timeout. */ logger("ppp_link_status_cb: PPPERR_IDLETIMEOUT\n"); break; case PPPERR_CONNECTTIME: /* PPPERR_CONNECTTIME. */ logger("ppp_link_status_cb: PPPERR_CONNECTTIME\n"); break; case PPPERR_LOOPBACK: /* Connection timeout. */ logger("ppp_link_status_cb: PPPERR_LOOPBACK\n"); break; default: logger("ppp_link_status_cb: unknown errCode %d\n", err_code); break; } } // Callback used by ppp connection static u32_t ppp_output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) { LWIP_UNUSED_ARG(pcb); LWIP_UNUSED_ARG(ctx); if (len > 0) { if (!usart_Send(data, len)) return 0x05; } logger("write - ppp_output_cb() len = %d\n", len); return len; } void pppConnect(void) { ppp = pppos_create(&pppos_netif, ppp_output_cb, ppp_link_status_cb, NULL); ppp_set_default(ppp); osThreadId PppGetTaskHandle; osThreadDef(PPP_GET_TASK_NAME, PppGetTask, osPriorityNormal, 0, 128*10); PppGetTaskHandle = osThreadCreate(osThread(PPP_GET_TASK_NAME), NULL); err_t err = ppp_connect(ppp,0); if (err == ERR_ALREADY) { logger("Connected successfully"); } for(int i=0;i<40;i++) { osDelay(500); if (ppp->phase >= PPP_PHASE_RUNNING) break; } } /* USER CODE END 0 */ 

بعد ذلك ، في الوظيفة MX_LWIP_Init () ، في الكتلة "/ * رمز المستخدم BEGIN 3 * /" ، نضيف مكالمة إلى وظيفة pppConnect ().

بالإضافة إلى ذلك ، تحتاج إلى زيادة حجم الكومة ، لذلك ، في الملف FreeRTOSConfig.h تحتاج إلى التعليق على تعريف configTOTAL_HEAP_SIZE ، وفي نهاية الملف ، في / * رمز المستخدم BEGIN يحدد * / كتلة ، أعلن عنه بقيمة جديدة.

 /* USER CODE BEGIN Defines */ /* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */ #define configTOTAL_HEAP_SIZE ((size_t)1024*30) /* USER CODE END Defines */ 

أيضًا ، في ملف usart.c ، قم بتغيير قيمة تعريف Q_USART2_SIZE إلى 2048.

يبدأ إعداد الاتصال بوظيفة MX_LWIP_Init () ؛ تم إنشاؤه تلقائيًا ؛ لقد أضفنا للتو وظيفة pppConnect () إليها. في هذه الوظيفة ، يتم تشغيل المهام التي تخدم اتصال PPPOS. يجب تمرير دالات pppos_create () عناوين الوظائف التي ستخدم إرسال الرسائل وإخراج المعلومات حول تغيير حالة الاتصال. بالنسبة لنا ، هذه هي الدالتان ppp_output_cb () و ppp_link_status_cb () ، على التوالي. بالإضافة إلى ذلك ، ستبدأ وظيفة pppConnect () مهمة خدمة الرسائل المستلمة. في نهاية عملها ، ستنتظر وظيفة pppConnect () حتى يتم تأسيس الاتصال بالخادم ، ثم تُكمل عملها.

سيتم تنفيذ العمل مع الشبكة على مستوى أعلى ، بمجرد أن يقرر LWIP أنه من الضروري إرسال رسالة إلى الشبكة ، سيتم استدعاء وظيفة ppp_output_cb () تلقائيًا. سيتم تلقي استجابة من الشبكة بواسطة وظيفة PppGetTask () ، كجزء من مهمة خدمة الرسائل الواردة ، وسيتم نقلها إلى أحشاء LWIP. إذا تغيرت حالة الاتصال ، فسيتم استدعاء وظيفة ppp_link_status_cb () تلقائيًا.

أخيرًا ، سنقوم بتعديل مهمة StartDefaultTask. الآن يجب أن يبدو مثل هذا:

 void StartDefaultTask(void const * argument) { /* init code for LWIP */ // MX_LWIP_Init(); /* USER CODE BEGIN 5 */ usart_Open(); MX_LWIP_Init(); /* Infinite loop */ for(;;) { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_SET); osDelay(1000); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET); osDelay(1000); } /* USER CODE END 5 */ } 

تم ، يمكنك تجميع وفلاش.

عند هذه النقطة ، تحتاج إلى بدء خادم PPP. للقيام بذلك ، يجب عليك أولاً نشر جهاز افتراضي مع Linux. استخدمت Ubuntu 16.04 x32. بعد تثبيت نظام التشغيل ، تحتاج إلى تكوين استخدام منفذ COM.

في هذا الجزء ، لا نحتاج إلى جهاز افتراضي يعمل بنظام Windows ، يمكننا إيقاف تشغيله بأمان. نربط كلاً من FT232 في Linux.

في Linux ، قبل البدء في العمل مع منفذ COM ، تحتاج إلى السماح للمستخدم باستخدامه. للقيام بذلك ، قم بتنفيذ الأمر التالي:

 sudo addgroup USERNAME dialout 

حيث USERNAME هو اسم المستخدم الحالي.

لرؤية المنافذ المتوفرة في نظام COM ، تحتاج إلى تشغيل الأمر:

 dmesg | grep tty 



نرى أن هناك منفذين ttyUSB في النظام. لا يمكننا أن نقول على الفور أي واحد هو المسجل وأي usart2. تحتاج فقط إلى التحقق منها بدورها.

أولاً ، نفّذ الأوامر لقراءتها من منفذ واحد:

 stty -F /dev/ttyUSB0 115200 cat /dev/ttyUSB0 

ثم من جهة أخرى:

 stty -F /dev/ttyUSB1 115200 cat /dev/ttyUSB1 

حيث نرى مثل هذه الصورة ، هذا هو المسجل.



يمكنك ترك هذه النافذة ، لن يزعجنا.

بعد ذلك ، تحتاج إلى السماح للحزم المرسلة من مجلسنا بترك حدود الشبكة الفرعية الخاصة بهم. للقيام بذلك ، قم بتكوين iptables. نقوم بتنفيذ الإجراءات التالية:

1. افتح نافذة وحدة تحكم جديدة
2. تحتاج إلى معرفة عنوان IP الخاص بك واسم واجهة الشبكة (قم بتشغيل الأمر ifconfig )



3. قم بتشغيل أوامر التكوين nat

 sudo echo 1 | sudo tee -a /proc/sys/net/ipv4/ip_forward > /dev/null sudo echo 1 | sudo tee -a /proc/sys/net/ipv4/ip_dynaddr > /dev/null sudo iptables -F FORWARD sudo iptables -F -t nat sudo iptables -t nat -A POSTROUTING -o enp0s3 -j SNAT --to-source 192.168.10.196 sudo iptables -t nat -L 

حيث enp0s3 هو اسم واجهة الشبكة
192.168.10.196 - عنوان IP الخاص بك
/ proc / sys / net / ipv4 / - المسار إلى الملف المقابل.

يمكن إعادة كتابة هذه الأوامر في ملف دفعي وتشغيلها في كل مرة قبل بدء تشغيل خادم PPP. يمكنك إضافته إلى التشغيل التلقائي ، لكني لم أفعل ذلك.

الآن نحن جاهزون لبدء الخادم ، يبقى فقط لإنشاء ملف إعدادات. أسميته " pppd.conf " ، أقترح استخدام الإعدادات التالية:

 nodetach noauth passive local debug lock 192.168.250.1:192.168.250.2 /dev/ttyUSB1 115200 lcp-echo-interval 10 lcp-echo-failure 1 cdtrcts 

نعيد كتابة الإعدادات في ملف ثم يمكنك بدء تشغيل الخادم. يتم ذلك باستخدام الأمر sudo pppd ./pppd.conf

يجب أن يبدأ خادم PPPD قبل بدء الاكتشاف ، لذلك بعد بدء PPPD تحتاج إلى النقر على زر "إعادة الضبط" الموجود على اللوحة.

إذا قمت بكل شيء بشكل صحيح ، سترى الصورة التالية:



تشغيل pppd على اليسار ، مسجل على اليمين.

المرحلة 4. نرسل حقيبة


في هذه المرحلة ، نحتاج إلى كلا الجهازين الافتراضيين. لينكس ل pppd وويندوز لاستلام الحزمة. لتبسيط المهمة ، من الضروري أن يكون كلا الجهازين على نفس الشبكة الفرعية ، والحل المثالي هو تحديد اتصال جسر الشبكة لكلا الجهازين في إعدادات شبكة VirtualBox ، وتعطيل جدار الحماية في Windows.

نبدأ في تشغيل الأجهزة الافتراضية وتكوين اتصال ppp للوحة الاكتشاف باستخدام pppd. في نظام التشغيل Windows ، اكتشفنا عنوان IP الخاص بالجهاز (الأمر ipconfig) ، وقد حصلت عليه 192.168.10.97.

قم بتشغيل Packet Sender وقم بتكوينه كما يلي:



الآن مرة أخرى ، قم بتعديل مهمة StartDefaultTask في ملف main.c.

 /* USER CODE BEGIN 4 */ #include "logger.h" #include "sockets.h" typedef uint32_t SOCKET; /* USER CODE END 4 */ /* StartDefaultTask function */ void StartDefaultTask(void const * argument) { /* init code for LWIP */ // MX_LWIP_Init(); /* USER CODE BEGIN 5 */ usart_Open(); MX_LWIP_Init(); /* Infinite loop */ uint8_t sendStr[]="Test message TCP/IP."; uint8_t resvStr[100]={0}; int resvLength = 0; struct sockaddr_in sockAddr; sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons( 6565 ); uint32_t addr = inet_addr("192.168.10.97"); sockAddr.sin_addr.s_addr = addr; SOCKET socket = NULL; int nError = 0; /* Infinite loop */ for(;;) { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_SET); osDelay(1000); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET); osDelay(1000); socket = socket( AF_INET, SOCK_STREAM, 0 ); nError = connect( socket, (struct sockaddr*)&sockAddr, sizeof(sockAddr) ); if ( nError == 0 ) { nError = send( socket, sendStr, sizeof(sendStr)-1, 0 ); if ( nError < 0 ) logger("SEND ERROR %d\n", nError); else { logger("SEND - %s\n", sendStr); resvLength = 0; while(resvLength < 1) resvLength = lwip_recv( socket, resvStr, sizeof(resvStr), MSG_WAITALL); resvStr[resvLength]=0; logger("GET - %s\n", resvStr); } lwip_close(socket); } else logger("CONNECT ERROR %d\n", nError); } /* USER CODE END 5 */ } 

كقيمة لمتغير addr ، نستخدم عنوان جهاز Windows ، منفذ رقم 6565.
الرسالة المرسلة "اختبار الرسالة TCP / IP" ، رد "تم استلام الرسالة".

هنا يمكنك أن ترى أن وظائف PPP لا تستخدم مباشرة لإرسال واستقبال الرسائل. تتم جميع الأعمال على مستوى أعلى ، ويتم استدعاء وظائفنا تلقائيًا.

نحن نجمع ونومض.

تظهر نتيجة الاتصال بـ pppd على جهاز Linux:



يمكن رؤية الطلبات المستلمة والردود المرسلة في برنامج Packet Sender على جهاز Windows:



حسنًا ، هذا كل شيء ، الحزمة التي أرسلناها من لوحة الاكتشاف ذهبت إلى منفذ COM ، وصلت إلى خادم pppd ، تم إرسالها إلى منفذ Windows 6565 للجهاز ، هناك تم استلامها بنجاح ، ردًا على ذلك تم إرسال حزمة أخرى اجتازت هذا الطريق في الاتجاه المعاكس وتم اعتماده بنجاح على السبورة. قد تتمكن أيضًا من إرسال رسائل إلى أي جهاز على الإنترنت.

→ يمكن تحميل كود المشروع الكامل هنا

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


All Articles