قسمت دوم از مجموعه مقالات «EC25 و QuecOpen»
سلام به همه دوستان سیسوگی!
در قسمت اول با ماژول EC25-EUX، قابلیتهایش و مفهوم QuecOpen آشنا شدیم و دیدیم که داخل این ماژول یک نسل پردازنده ARM با لینوکس نشسته است. حالا وقت آن رسیده که آستینها را بالا بزنیم و اولین برنامه خودمان را روی همین پردازنده اجرا کنیم!
در این قسمت قدمبهقدم پیش میرویم:
اگر تازهکار هستید، کافی است مراحل را به ترتیب دنبال کنید. اگر مهندس باتجربهای هستید، در هر بخش نکتههای فنیای هست که معمولاً در مستندات به آنها اشاره نمیشود.
قبل از هر کاری، ماژول را با کابل USB به کامپیوتر وصل کنید و ببینید لینوکس آن را چطور میشناسد:
$ lsusb
Bus 001 Device 011: ID 2c7c:0125 Quectel Wireless Solutions Co., Ltd. EC25 LTE modem
عدد 2c7c شناسه سازنده (Vendor ID) کوئکتل و 0125 شناسه محصول (Product ID) ماژول EC25 است. حالا ببینیم این دستگاه از چه بخشهایی تشکیل شده است:
$ lsusb -t
|__ Port 002: Dev 011, If 0, Class=Vendor Specific Class, Driver=option, 480M
|__ Port 002: Dev 011, If 1, Class=Vendor Specific Class, Driver=option, 480M
|__ Port 002: Dev 011, If 2, Class=Vendor Specific Class, Driver=option, 480M
|__ Port 002: Dev 011, If 3, Class=Vendor Specific Class, Driver=option, 480M
|__ Port 002: Dev 011, If 4, Class=Vendor Specific Class, Driver=qmi_wwan, 480M
|__ Port 002: Dev 011, If 5, Class=Vendor Specific Class, Driver=usbfs, 480M
ماژول یک دستگاه ترکیبی (Composite Device) است که شش رابط (Interface) را همزمان در اختیار کامپیوتر میگذارد:
| رابط | درایور | فایل در لینوکس | کاربرد |
| If 0 | option | /dev/ttyUSB0 | DM/DIAG: پورت عیبیابی کوالکام؛ برای ثبت لاگ و رایت فریمور |
| If 1 | option | /dev/ttyUSB1 | NMEA: خروجی موقعیت GNSS |
| If 2 | option | /dev/ttyUSB2 | فرمانهای AT |
| If 3 | option | /dev/ttyUSB3 | Modem: فرمان AT و اتصال PPP |
| If 4 | qmi_wwan | wwan0 | اتصال داده از طریق پروتکل QMI |
| If 5 | usbfs | — | ADB |
عدد 480M یعنی ارتباط از نوع USB 2.0 پرسرعت (High-Speed) است.
اگر ماژول شما فریمور QuecOpen ندارد یا میخواهید فریمور را بهروز کنید، دو ابزار اصلی وجود دارد:
sudo QFirehose -f EC25EUXGAR08A19M1G_OCPU_20.300.20.300
دو نکته مهم که بهتر است قبل از رایت بدانید:
ADB همان ابزاری است که برنامهنویسهای اندروید با آن آشنا هستند. در QuecOpen هم از ADB برای سه کار استفاده میکنیم:
با یک برنامه ترمینال سریال (مثل picocom یا minicom) به پورت AT یعنی /dev/ttyUSB2 وصل شوید و این فرمان را بفرستید:
AT+QCFG=”usbcfg”
+QCFG: “usbcfg”,0x2C7C,0x0125,1,1,1,1,1,0,0
هر عدد در این پاسخ معنای مشخصی دارد:
| فیلد | VID | DIAG | NMEA | AT | Modem | NET | ADB | UAC | |
| مقدار | 0x2C7C | 0x0125 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
فیلد هشتم مربوط به ADB است که در اینجا غیرفعال (0) است. فیلد آخر هم مربوط به کلاس صوتی UAC است.
AT+QCFG=”usbcfg”,0x2C7C,0x0125,1,1,1,1,1,1,0
AT+CFUN=1,1
فرمان اول ADB را فعال میکند و فرمان دوم ماژول را ریاستارت میکند تا تنظیمات جدید اعمال شود. این تنظیم در حافظه غیرفرار (NV) ماژول ذخیره میشود، پس با خاموشوروشن شدن از بین نمیرود.
⚠️ دقت کنید: فرمان AT+QADB=1 که در بعضی آموزشهای اینترنتی آمده، روی این ماژول وجود ندارد و خطا برمیگرداند. روش درست همان AT+QCFG=usbcfg است.
$ adb devices
List of devices attached
3e7db67c device
عدد 3e7db67c شماره سریال ماژول است. اگر کنجکاو باشید از کجا میآید، در قسمت فرایند بوت میبینیم که بوتلودر آن را با پارامتر androidboot.serialno به کرنل لینوکس تحویل میدهد.
اگر ماژول در فهرست نیامد، دو راهحل رایج وجود دارد (در جدول خطاهای رایج انتهای مقاله آمدهاند):
~/.android/adb_usb.ini
اضافه کنید. این کار بیشتر برای نسخههای قدیمیتر adb لازم است؛ نسخههای جدید رابط ADB را از روی کلاس آن تشخیص میدهند.
$ adb shell
/ # uname -a
Linux mdm9607 3.18.44 #1 PREEMPT Thu Apr 24 09:18:02 CST 2025 armv7l GNU/Linux
تبریک! حالا داخل لینوکسِ ماژول هستید. یک کرنل لینوکس (Linux Kernel) نسخه 3.18 روی پلتفرمی به نام mdm9607 که معماری armv7l دارد. جزئیات حافظه، پارتیشنها و سیستم فایل را در قسمت بعد با هم بررسی میکنیم.
برنامهای که روی کامپیوتر x86 مینویسیم باید برای پردازنده ARM ماژول ساخته شود. به این کار کامپایل متقابل (Cross-Compile) میگویند و ابزارش در پوشه ql-ol-crosstool قرار دارد. در هر ترمینال جدید، قبل از هر کامپایلی، این دستور را اجرا کنید:
$ cd ql-ol-sdk
$ source ql-ol-crosstool/ql-ol-crosstool-env-init
QUECTEL_PROJECT_NAME =EC25EUX
QUECTEL_PROJECT_REV =EC25EUXGAR08A19M1G_OCPU_20.300
source autogen-env …..
Done.
این اسکریپت متغیرهای محیطی مثل CC و CFLAGS را تنظیم میکند و مسیرهای SDK را برای جای فعلی پوشه اصلاح میکند. بیایید ببینیم کامپایلر چطور تعریف شده است:
$ echo $CC
arm-oe-linux-gnueabi-gcc -march=armv7-a -mfloat-abi=softfp -mfpu=neon
$ arm-oe-linux-gnueabi-gcc –version
arm-oe-linux-gnueabi-gcc (GCC) 4.9.3
SDK پوشهای از مثالهای آماده در ql-ol-extsdk/example دارد. ما از سادهترین آنها شروع میکنیم:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
int num;
char* pStr = “19.7”;
char strTmp[100];
printf(“\n<Hello OpenLinux !>\n”);
num = atoi(pStr);
memset(strTmp, 0x0, sizeof(strTmp));
sprintf(strTmp, “atoi(\”19.7\”)=%d\n”, num);
printf(strTmp);
return 0;
}
برنامه یک پیام چاپ میکند، رشته “19.7” را با تابع atoi به عدد صحیح تبدیل میکند و نتیجه را نمایش میدهد. چون atoi فقط بخش صحیح را میخواند، خروجی 19 خواهد بود. عبارت OpenLinux هم نام قدیمی QuecOpen است و پیشوند ql-ol در نام پوشههای SDK یادگار همین نام است.
حالا برنامه را بسازید :
$ cd ql-ol-extsdk/example/hello_world
$ make
arm-oe-linux-gnueabi-gcc -march=armv7-a -mfloat-abi=softfp -mfpu=neon -O2 … \
-include quectel-features-config.h -I./ -I/mdm9607/usr/include … -c helloworld.c
arm-oe-linux-gnueabi-gcc -march=armv7-a -mfloat-abi=softfp -mfpu=neon … -lrt helloworld.o -o helloworld
قبل از ارسال برنامه به ماژول، بیایید مطمئن شویم چیزی که ساختهایم واقعاً برای ماژول مناسب است. ابزار readelf از همین SDK به ما کمک میکند:
$ arm-oe-linux-gnueabi-readelf -A helloworld
Tag_CPU_name: “7-A”
Tag_CPU_arch: v7
Tag_THUMB_ISA_use: Thumb-2
Tag_FP_arch: VFPv3
Tag_Advanced_SIMD_arch: NEONv1
Tag_ABI_HardFP_use: SP and DP
…
حالا ببینیم برنامه برای اجرا به چه بارگذار پویا (Dynamic Loader) نیاز دارد:
$ arm-oe-linux-gnueabi-readelf -l helloworld | grep interpreter
[Requesting program interpreter: /lib/ld-linux.so.3]
این خط کلید یکی از گیجکنندهترین خطاهاست:
پس اگر یک برنامه armhf را روی ماژول اجرا کنید، با خطای عجیب not found روبهرو میشوید، در حالی که فایل همانجا جلوی چشمتان است!
$ adb push helloworld /tmp/
helloworld: 1 file pushed, 0 skipped. 62.7 MB/s (5992 bytes in 0.000s)
$ adb shell chmod +x /tmp/helloworld
$ adb shell /tmp/helloworld
<Hello OpenLinux !>
atoi(“19.7”)=19
اولین برنامه ما روی پردازنده داخلی ماژول اجرا شد! 🎉
یک نکته مهم درباره محل قرار دادن برنامه:
جمعبندی
در این قسمت:
مهمتر از همه، دیدیم چرا قرارداد softfp و بارگذار ld-linux.so.3 برای این ماژول اهمیت دارند و چطور با readelf میتوانیم قبل از اجرا از سازگاری برنامه مطمئن شویم. در قسمت بعد به درون ماژول سفر میکنیم. اگر سؤالی دارید یا در مراحل به مشکلی خوردید، در بخش نظرات بنویسید. تا قسمت بعد، خدانگهدار!
درباره نویسنده
Mr. Zebel – مهندس تحقیق و توسعه نرمافزار نهفته (R&D Embedded Software Engineer)، فعال در حوزه اینترنت اشیا، شبکههای سلولار و سیستمهای نهفته
لینکدین:
https://www.linkedin.com/in/mohammad-javad-hashemi-17981514b
مهندس تحقیق و توسعه نرمافزار نهفته (R&D Embedded Software Engineer)، فعال در حوزه اینترنت اشیا، شبکههای سلولار و سیستمهای نهفته
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.