سیستم ماهوارهای ناوبری جهانی (GNSS) یک فناوری پیشرفته و حیاتی است که در زمینه ناوبری و تعیین موقعیت مکانی در سراسر جهان مورد استفاده قرار میگیرد. این سیستم با استفاده از شبکهای از ماهوارهها، امکان تعیین موقعیت دقیق و زمانبندی هماهنگ را برای کاربران در هر نقطه از کره زمین فراهم میکند.
GNSS شامل چندین سیستم ماهوارهای است که توسط کشورهای مختلف راهاندازی شدهاند، از جمله سیستم موقعیتیاب جهانی (GPS) ایالات متحده، سیستم گلوناس (GLONASS) روسیه، سیستم بیدو (BeiDou) چین و سیستم گالیله (Galileo) اتحادیه اروپا. هر یک از این سیستمها دارای ماهوارههای متعددی هستند که در مدار زمین قرار دارند و سیگنالهایی را ارسال میکنند که توسط گیرندههای GNSS روی زمین دریافت میشوند.
این فناوری در طیف وسیعی از کاربردها از جمله ناوبری هوایی و دریایی، نقشهبرداری، کشاورزی دقیق، مدیریت ناوگان، و حتی در گوشیهای هوشمند برای تعیین موقعیت مکانی کاربران مورد استفاده قرار میگیرد. با ترکیب سیگنالها از چندین سیستم GNSS، دقت و قابلیت اطمینان تعیین موقعیت به طور قابل توجهی افزایش مییابد.
در ایران نیز، استفاده از GNSS در بخشهای مختلف از جمله حمل و نقل، نقشهبرداری و تحقیقات علمی رو به گسترش است. با توجه به اهمیت این فناوری در توسعه زیرساختها و بهبود خدمات مکانی، کشورمان نیز در تلاش است تا از مزایای GNSS به بهترین نحو بهرهبرداری کند.
در این بخش، به معرفی اجمالی GNSS و کاربردهای آن پرداختیم و در ادامه، به بررسی بیشتر این فناوری و تاثیر آن بر زندگی روزمره و صنایع مختلف بپردازیم. به برخی از کاربردهای GNSS در صنایع مختلف به شکل زیر است:
درکنار GNSS مفهوم دیگری به نام AGPS وجود دارد که مخفف Assisted GPS یا “جیپیاس کمکدار” است. این فناوری یک روش بهبود یافته از سیستم موقعیتیاب جهانی (GPS) است که برای افزایش سرعت و دقت تعیین موقعیت در شرایط دشوار، مانند مناطق شهری با ساختمانهای بلند یا محیطهای داخلی، طراحی شده است.
– در GPS معمولی، گیرنده باید سیگنالها را از ماهوارهها دریافت کند و این فرایند ممکن است در شرایط نامساعد زمانبر باشد. AGPS با استفاده از دادههای کمکی از منابع دیگر، مانند شبکههای موبایل یا Wi-Fi، زمان لازم برای قفل شدن روی ماهوارهها (Time to First Fix) را کاهش میدهد.
– در مناطق شهری با ساختمانهای بلند یا محیطهای داخلی، سیگنالهای GPS ممکن است ضعیف یا مسدود شوند. AGPS با ترکیب دادههای ماهوارهای و اطلاعات از منابع دیگر، دقت تعیین موقعیت را در این شرایط بهبود میبخشد.
– AGPS به طور گستردهای در گوشیهای هوشمند و دستگاههای موبایل استفاده میشود. این فناوری به برنامههای ناوبری، نقشهبرداری و برنامههای مبتنی بر موقعیت مکانی کمک میکند تا سریعتر و دقیقتر کار کنند.
– با تسریع در فرایند تعیین موقعیت، AGPS مصرف انرژی دستگاه را کاهش میدهد، که برای دستگاههای موبایل با باتری محدود بسیار مهم است.
– در دستگاههای IoT که نیاز به تعیین موقعیت دارند، AGPS میتواند کارایی را بهبود بخشد. به عنوان مثال، در ردیابی داراییها، مدیریت ناوگان یا حتی در دستگاههای پوشیدنی.
– AGPS به خدمات مبتنی بر موقعیت مکانی، مانند تبلیغات هدفمند، خدمات امدادی و برنامههای اجتماعی، کمک میکند تا دقیقتر و کارآمدتر عمل کنند.
در ایران نیز، با توجه به گسترش استفاده از گوشیهای هوشمند و خدمات مبتنی بر موقعیت مکانی، AGPS نقش مهمی در بهبود تجربه کاربران و ارائه خدمات دقیقتر ایفا میکند اما متاسفانه پس از تحریم ها استفاده از این فناوری دچار مشکل شده است هرچند که خوشبختانه ماژول کویکتل از این قابلیت پشتیبانی میکند. در ادامه به بررسی کاربردهای GNSS در اینترنت اشیا میپردازیم.
GNSS به عنوان یک فناوری کلیدی، نه تنها در صنایع سنتی بلکه در حوزههای نوین مانند IoT نیز نقش حیاتی ایفا میکند. با ادغام GNSS با سایر فناوریها مانند حسگرها، ارتباطات بیسیم و هوش مصنوعی، امکان ایجاد سیستمهای هوشمند و کارآمدتر فراهم میشود. در ایران نیز، با توجه به نیازهای توسعهای و اهمیت دقت در موقعیتیابی، استفاده از GNSS در حال گسترش است و میتواند به بهبود زیرساختها و خدمات در بخشهای مختلف کمک کند. برخی از کاربردهای آن عبارتند از:
همانطور که مشاهده کردید GNSS در تمام صنایع و مخصوصا در پروژه های IOT اهمیت بالایی دارد. خوشبختانه ماژولی که استفاده میکنیم از این قابلیت و قابلیتهای بیشتری که در پستهای قبل مشاهده کردید پشتیبانی میکند که این ماژول را به گزینه ی بسیار خوبی برای پروژه های مرتبط تبدیل میکند. در این پست قصد داریم که راه اندازی GNSS را باهم بررسی کنیم.
قبل از اینکه به بررسی برنامه بپردازیم لازم است این نکته را خاطرنشان کنم که باید ماژول شما از نوع (AA) باشد تا قابلیت GNSS را پشتیبانی کند اما چنانچه این ماژول را در دسترس ندارید میتوانید همچنان از ادامه این آموزش استفاده کنید چراکه با بعضی از قابلیتهایی که در ماژول فعلیتان وجود دارد آشنا میشوید.
در اینجا میخواهم ابتدا شما را با ابزاری که برای دیدن داده (لاگ) برنامه استفاده میکنیم آشنا کنم. برای اینکار از برنامه coolwatcher استفاده میکنیم به این صورت که برنامه coolwatcher را اجرا کرده و پورت مناسب (AP LOG) را انتخاب میکنیم و داده ها را دریافت میکنیم. اگر از ویندوز استفاده میکنید بجز انتخاب صحیح پورت چالش دیگری ندارید اما چنانچه از لینوکس استفاده میکنید لازم است تا تنظیمات را به صورت زیر انجام دهید.
1 2 3 4 5 6 7 | cd ~/.wine/dosdevices/ ln -s /dev/ttyUSB4 COM4 wine regedit Got to HKEY_LOCAL_MACHINE,Software,Wine,Ports select add string and then add COM4 as name and /dev/ttyUSB4 as value finally close it. wineserver -k wine coolwatcher_usb.exe |
با استفاده از کد بالا پورت چهارم که مربوط به داده های مورد انتظار برنامه است را به عنوان COM معرفی میکنیم، زیرا برنامه به دنبال COM است. (ممکن است شماره پورت در سیستم شما متفاوت باشد که در این صورت باید شماره ی دیگری را برای آن درنظر بگیرید و کد بالا را به طور مناسب برای سیستم خود ویرایش کنید.) برای انتخاب پورت صحیح مانند عکس شماره پورت مورد نظر را در قسمت آبی رنگ وارد کنید و روی OK کلیک نمایید.
پنجره ای مانند تصور زیر برای شما باز میشود که در آن لازم است مانند تصویر گزینه ی Trace را انتخاب کنید.
سپس از منوی Trace قسمت Play را انتخاب کنید. حالا باید داده ها را دریافت کنید.
برای استفاده از GNSS ابتدا نیاز است که از فایل ql_init.c تابع ql_gnss_app_init از حالت کامنت خارج کنید و سپس مانند آنچه در آموزش قبل داشتیم برنامه را کامپایل کنید. قسمتی که باید از کامنت خارج کنید:
1 2 3 | #ifdef QL_APP_FEATURE_GNSS ql_gnss_app_init(); #endif |
مانند آنچه در آموزش قبل دیدیم کد ما سه قسمت دارد که تکرار نمیکنم و در عوض میخواهم شما را با استفاده از UART برای دریافت پیامهای GPS آشنا کنم. برای اینکه استفاده از UART را به طور جامع یادبگیرید میتوانید به مثال مربوط در فایل ql_init.c یعنی تابع ql_uart_app_init مراجعه کنید هرچند که اینجا چیزی که به آن نیاز دارید را خواهید آموخت. برای استفاده از UART ابتدا لازم است که کتابخانه مربوط را اضافه کنیم:
1 2 | #include "ql_pin_cfg.h" #include "ql_uart.h" |
نیاز است که کتابخانه ql_pin_cfg را از فایلهای پروژه (با استفاده از مثالها) پیدا کنید و در دایرکتوری inc مثال gnss کپی کنید. سپس به سراغ کد میرویم. کد ما دو تابع دارد که یکی به عنوان تابع فراخوان (callback) و دیگری به عنوان تابع اصلی مورد استفاده قرار میگیرد. تابع اصلی به شکل زیر است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | void gnss_uart_log(const char *format, ...) { char buffer[UART_LOG_BUFF_SIZE]; // Local buffer va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer) - 3, format, args); // Reserve space for "\r\n" va_end(args); strcat(buffer, "\r\n"); // Automatically add "\r\n" int ret = 0; ql_uart_config_s uart_cfg = {0}; uart_cfg.baudrate = QL_UART_BAUD_115200; uart_cfg.flow_ctrl = QL_FC_NONE; uart_cfg.data_bit = QL_UART_DATABIT_8; uart_cfg.stop_bit = QL_UART_STOP_1; uart_cfg.parity_bit = QL_UART_PARITY_NONE; ret = ql_uart_set_dcbconfig(QL_UART_PORT_1, &uart_cfg); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); ret = ql_pin_set_func(QL_UART2_RX_PIN, QL_UART2_RX_FUNC); ret = ql_uart_open(QL_UART_PORT_1); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); if (QL_UART_SUCCESS == ret) { ret = ql_uart_register_cb(QL_UART_PORT_1, gnss_ql_uart_notify_cb_main); ql_uart_get_dcbconfig(QL_UART_PORT_1, &uart_cfg); ql_uart_write(QL_UART_PORT_1, (unsigned char*)buffer, strlen(buffer)); } } |
در این تابع از ellipsis استفاده کردیم که برای ما یک تابع Variadic میسازه که میتونیم ورودیهای متفاوتی بهش بدیم. اگر در این مورد اطلاعات کافی ندارید به آموزشهای C مراجعه کنید. بجز چیزی که گفته شد تابع ما نکته خاصی نداره.
به ترتیب ابتدا متغییرها رو تعریف میکنیم و بعد از اون تنظیمات UART را روی چیزی که لازم داریم قرار میدیم، در آخر هم با بررسی شرط اعمال شدن موفق تنظیماتی که انجام دادیم، پیام را ارسال میکنیم یا توسط تابع فراخوان آن را دریافت میکنیم. تابع فراخوان به شکل زیر است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #include "ql_pin_cfg.h" #include "ql_uart.h" #define UART_LOG_BUFF_SIZE 512 // Buffer size for formatted output #define QL_UART_RX_BUFF_SIZE 2048 #define QL_UART_TX_BUFF_SIZE 2048 #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define QL_VIRT_AT_RX_BUFF_SIZE 1024 void gnss_ql_uart_notify_cb_main(unsigned int ind_type, ql_uart_port_number_e port, unsigned int size) { unsigned char *recv_buff = calloc(1, QL_UART_RX_BUFF_SIZE+1); unsigned int real_size = 0; int read_len = 0; switch(ind_type) { case QUEC_UART_RX_OVERFLOW_IND: //rx buffer overflow case QUEC_UART_RX_RECV_DATA_IND: { while(size > 0) { memset(recv_buff, 0, QL_UART_RX_BUFF_SIZE+1); real_size= MIN(size, QL_UART_RX_BUFF_SIZE); read_len = ql_uart_read(port, recv_buff, real_size); if((read_len > 0) && (size >= read_len)) { size -= read_len; } else { break; } } break; } case QUEC_UART_TX_FIFO_COMPLETE_IND: { break; } } free(recv_buff); recv_buff = NULL; } |
در این مثال از تابع فراخوان استفاده نمیکنیم و فقط برای اینکه درمورد روش کارکردن توابع UART بیشتر بدانید این تابع را در اینجا آوردم. به طور خلاصه این تابع بررسی میکند که اگر داده ای وجود داشته باشد آن را ذخیره کند.
ساختار کلی کدی که شرکت برای GNSS ساخته را تغییر نمیدهیم و فقط نیاز است تابع nmea_dbg_log و QL_GNSSDEMO_LOG را با تابع gnss_uart_log که نوشتیم جایگزین کنیم و سپس برنامه را کامپایل و نصب کنید. به این صورت پیامهای GNSS روی UART قابل مشاهده است.
همانطور که مشاهده میکنید دستگاه هنوز نتوانسته پیامی از ماهواره بگیرد. معمولا چنانچه آنتن مناسبی استفاده کنید و نزدیک پنجره باشد پس از مدت کوتاهی پیام ماهواره را دریافت میکنید. کد کامل در ادامه ی این متن برای استفاده شما عزیزان قرار داده شده است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | #include <stdio.h> #include <string.h> #include <stdlib.h> #include "ql_api_osi.h" #include "ql_log.h" #include "gnss_demo.h" #include "ql_pin_cfg.h" #include "ql_uart.h" #define MAX_UINT32 4294967295U #define UART_LOG_BUFF_SIZE 512 // Buffer size for formatted output ql_task_t gnss_task = NULL; ql_gnss_data_t g_gps_data = {0}; nmeasrc_sentences nmea_handle={0}; char gnss_device_info[100]={0}; static uint32 prev_ms_gsv =0; static bool tick_overflow = FALSE; void gnss_uart_log(const char *format, ...) { char buffer[UART_LOG_BUFF_SIZE]; // Local buffer va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer) - 3, format, args); // Reserve space for "\r\n" va_end(args); strcat(buffer, "\r\n"); // Automatically add "\r\n" int ret = 0; ql_uart_config_s uart_cfg = {0}; uart_cfg.baudrate = QL_UART_BAUD_115200; uart_cfg.flow_ctrl = QL_FC_NONE; uart_cfg.data_bit = QL_UART_DATABIT_8; uart_cfg.stop_bit = QL_UART_STOP_1; uart_cfg.parity_bit = QL_UART_PARITY_NONE; ret = ql_uart_set_dcbconfig(QL_UART_PORT_1, &uart_cfg); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); ret = ql_pin_set_func(QL_UART2_RX_PIN, QL_UART2_RX_FUNC); ret = ql_uart_open(QL_UART_PORT_1); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); if (QL_UART_SUCCESS == ret) { ql_uart_get_dcbconfig(QL_UART_PORT_1, &uart_cfg); ql_uart_write(QL_UART_PORT_1, (unsigned char*)buffer, strlen(buffer)); } } void ql_gnss_notify_cb(uint32 ind_type, ql_uart_port_number_e port, uint32 size) { if( ind_type == QUEC_UART_RX_RECV_DATA_IND ) { ql_event_t event; event.id = ind_type; event.param1 = port; event.param2 = size; ql_rtos_event_send(gnss_task, &event); gnss_uart_log("gnss demo port is %d, size is %d", event.param1, event.param2); } else { gnss_uart_log("gnss demo recv overflow error!"); } } static void ql_gnss_demo_thread(void *param) { gnss_uart_log("gnss demo thread enter, param 0x%x", param); ql_event_t event; int ret=0; static int total_bytes = 0; static char buffer[2048]={0}; unsigned char nmea_buff[256]; char *start, *end; struct nmea_s *nmea = NULL; unsigned char *recbuff=NULL; ql_gnss_apflashdatarecv_e datarecv_status=0; /* open GNSS */ ret = ql_gnss_switch(GNSS_ENABLE); if(ret == QL_GNSS_ALREADY_OPEN) { gnss_uart_log("GNSS demo already open"); } if( ret == QL_GNSS_NOT_SUPPORT_ERR) { goto exit; } while(ql_gnss_state_info_get()==GNSS_FIRMWARE_UPDATE) { ql_rtos_task_sleep_ms(1000); } ql_gnss_callback_register(ql_gnss_notify_cb); if(ret ==QL_GNSS_CB_NULL_ERR) { goto exit; } ql_gnss_device_info_request(); ql_gnss_apflash_getpvdata(&g_gps_data); ql_gnss_cfg_s gnss_cfg; ret = ql_gnss_get_cfg(&gnss_cfg); if (ret == QL_GNSS_SUCCESS) { gnss_uart_log("get sys_type=%d", gnss_cfg.nmea_cfg.gnss_nmea_sys_type); } while(1) { if( ql_event_try_wait(&event) != 0 ) { continue; } if( event.id == QUEC_UART_RX_RECV_DATA_IND ) { recbuff=calloc(1,QL_UART_RX_BUF_SIZE); //recbuff is the buffer for receiving the underlying uart. Increase the recbuff size to keep it consistent with the underlying UART RX buffer to prevent memory out of bounds when getting nmea. if(NULL==recbuff) { gnss_uart_log("malloc err\r\n"); break; } if(ql_gnss_nmea_get( event.param1,recbuff, event.param2)<0) { if(NULL != recbuff) { free(recbuff); recbuff=NULL; } break; } // get apflashdata ql_gnss_apflash_get_recv_status(&datarecv_status); if(datarecv_status == APFLASH_DATA_SET_RECV) { uint32 u32flag=0; u32flag=ql_gnss_apflash_data_parse(recbuff, event.param2); //gnss_dbg_log("u32flag %d\r\n", u32flag); if(NULL != recbuff) { free(recbuff); recbuff=NULL; } switch (u32flag) { case QL_GNSS_APFLASH_NO_ERR: continue; case QL_GNSS_APFLASH_MALLOC_ERR: ql_gnss_apflash_retry_enable(APFLASH_RETRY_ENABLE); ql_gnss_apflash_set_recv_status(APFLASH_DATA_NOT_RECV); gnss_uart_log("malloc err\r\n"); continue; case QL_GNSS_APFLASH_OVERFLOW_ERR: case QL_GNSS_APFLASH_HEAD_ERR: case QL_GNSS_APFLASH_RX_ERR: ql_gnss_apflash_retry_enable(APFLASH_RETRY_ENABLE); ql_gnss_apflash_set_recv_status(APFLASH_DATA_NOT_RECV); continue; case QL_GNSS_APFLASH_RX_GOING: continue; case QL_GNSS_APFLASH_RX_PASS: ql_gnss_apflash_retry_enable(APFLASH_RETRY_DISABLE); ql_gnss_apflash_set_recv_status(APFLASH_DATA_NOT_RECV); continue; default: continue; } } total_bytes += event.param2; if (total_bytes > sizeof(buffer)-150) { total_bytes = 0; gnss_uart_log("nmea data Overflow\r\n"); if(NULL != recbuff) { free(recbuff); recbuff=NULL; } continue; } memcpy(buffer+total_bytes-event.param2, recbuff, event.param2); if(NULL != recbuff) { free(recbuff); recbuff=NULL; } if(event.param2>0) { end=buffer; while(1) { start = memchr(end, '$', total_bytes-(end-buffer)); if (NULL == start) { total_bytes = 0; break; } end = memchr(start, NMEA_END_CHAR_1, total_bytes-(start-buffer)); if (NULL==end || NMEA_END_CHAR_2 != *(++end)) { if (buffer != memmove(buffer, start, total_bytes-(start-buffer))) { total_bytes = 0; } total_bytes = total_bytes-(start-buffer); break; } memset(nmea_buff, 0, sizeof(nmea_buff)); memcpy(nmea_buff, start, jmin(sizeof(nmea_buff)-1, end-start-1)); gnss_uart_log("%s\r\n", nmea_buff); /* nmea string parse */ nmea = nmea_parse(start, end-start+1, 1); if (nmea) { ret=nmea_value_update(nmea, &g_gps_data); if(ret) { gnss_uart_log("nmea_value_update error. \r\n"); gnss_uart_log("GSV: %s", nmea_buff); } if (nmea->data) { free(nmea->data); nmea->data = NULL; } free(nmea); nmea = NULL; } if(end==buffer+total_bytes) { total_bytes=0; break; } end=end+1; } } } } exit: if(NULL != recbuff) { free(recbuff); recbuff=NULL; } ql_gnss_switch(GNSS_DISABLE); gnss_uart_log("gnss demo thread exit, param 0x%x", param); ql_rtos_task_delete(NULL); } |
زمانی که شما در فایل ql_init.c تابع را از حالت کامنت خارج میکنید تابع ql_gnss_app_init صدازده میشود که یک تسک به نام ql_gnss_demo_thread برای ما میسازد. در تابع ql_gnss_demo_thread ابتدا متغییرها تعریف میشوند و بعد با تابع ql_gnss_switch سراغ راه اندازی (Enable) کردن GNSS میرویم.
چنانچه از قبل روشن باشد پیامی بازمیگرداند که این قابلیت از قبل فعال است و اگر این قابلیت را پشتیبانی نکند یا خطا داشته باشیم از برنامه خارج میشود و در غیر این صورت به درستی فعال شده است. تابع مهم بعدی ql_gnss_callback_register است که تابع بازگشتی (callback) برنامه نویس را تعریف میکند. بعد از آن تابع ql_gnss_apflash_getpvdata است که استراکچری که برای داده های GNSS میخواهیم استفاده کنیم را به آن میدهیم که شامل داده هایی مثل طول و عرض جغرافیایی و زمان میشود. تابع بعدی ql_gnss_get_cfg است که به آن استراکچری که برای اطلاعات کانفیگ GNSS هست را به آن میدهیم. در لوپ بینهایتی که در ادامه داریم پیامهای GNSS مدیریت میشود. این داده ها توسط تابع ql_gnss_nmea_get دریافت و وضیت آن توسط ql_gnss_apflash_get_recv_status مشخص میشود و بوسیله ی توابع بعدی خطاهای احتمالی بررسی و رفع میشود و در ادامه چنانچه خطایی نباشد خروجی برای ما چاپ میشود.
امیدوارم از این آموزش استفاده کرده باشید. منتظر آموزشهای بعدی باشید. 🙂
وبسایت: http://www.arvidtek.com
www.arvidtek.com | گروه مهندسی آرویدتک | فعال حوزه الکترونیک و مخابرات | فروشگاه تخصصی قطعات الکترونیک
مقالات بیشتر
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.