همانطور که اطلاع دارید قیمت میکروکنترلرهایی مثل stm32 که توانایی اجرای یک گرافیک مناسب رودارند مدت نسبتاً زیادی هست که افزایش پیداکرده و بعضاً نایاب شده، یک روش دیگر هم برای اجرای گرافیک استفاده از تراشههایی مثل ft800 بوده که آن ها هم متاسفانه با مشکل افزایش قیمت و نایاب شدن مواجه شده. اما توی این آموزش میخواهیم در مورد یک تراشه باقیمت خیلی مناسب و امکانات کافی برای جایگزینی صحبت کنیم، البته قبلاً هم در سیسوگ در مورد تراشه f1c100s صحبت کرده بودیم و حتی یک برد اوپن سورس هم برای اون معرفی کرده بودیم اما آموزشهای قبلی بر پایه سیستمعامل لینوکس بود و این بار طی چند قسمت میخواهیم اجرای گرافیک بدون سیستمعامل (baremetal) رو آموزش بدیم، پس با سیسوگ همراه باشید.
معرفی کتابخانه baremetal
اول از همه ما نیاز به یک کتابخانه داریم که بخشهای مختلف چیپ f1c100s رو راهاندازی کرده باشه، این کتابخانه اینجا در گیت هاب در دسترس هست.
کتابخانه شامل این بخشها هست:
- _arm926_: هدر فایلهای لازم برای پردازنده
- _drivers_: درایورهای بخشهای مختلف سختافزار
- _lib_: کتابخانههای لازم
- _tools_: ابزارهای موردنیاز
همچنین شامل چند نمونه کد مختلف برای استفاده از کتابخانه ها هست که لازمه یک توضیح کوتاه در مورد هر کدام بدیم:
- hello_led: سادهترین نمونه کد برای راهاندازی حداقلی (همون blink خودمون)
- lcd_test: رسم متن و شکل روی lcd
- lvgl_demo: راهاندازی کتابخانه گرافیکی lvgl
- tv_out_test: راهاندازی بخش خروجی تصویر چیپ (مثل lcd و خروجی pal)
- tv_in_test: راهاندازی ورودی تصویر pal و نمایش تصویر دریافتی روی lcd
- sd_card_test: خواندن تصویر از روی sd و نمایش روی lcd
- simple_loader: بوت لودر برای اجرای کد از روی spi flash
بررسی کد blink
ما برای اینکه تازهکاریم از hello led شروع میکنیم. در ادامه کد فایل main.c این پروژه رو میتونید بررسی کنید:
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 | #include "main.h" #include <stdio.h> #include <string.h> #include <math.h> #include "system.h" #include "f1c100s_de.h" #include "f1c100s_timer.h" #include "f1c100s_intc.h" #include "f1c100s_gpio.h" #include "f1c100s_pwm.h" #include "arm32.h" /* * Simple demonstaration project. Controls 2 LEDs: * LED1(GPIOA1): Classical, arduino-style LED blinking with delay function. * LED2(GPIOA2): "breathing" effect, created by sinewave-modulated PWM. Uses timer interrupt to update PWM duty * */ void delay(uint32_t t); void timer_init(void); void timer_irq_handler(void); #define LED_FADE_STEPS 1000 #define LED_PWM_PERIOD 200 volatile uint8_t tick_flag = 0; uint16_t sin_values[LED_FADE_STEPS/2]; int main(void) { system_init(); // Initialize clocks, mmu, cache, uart, ... arm32_interrupt_enable(); // Enable interrupts printf("Hello, world!\r\n"); // LED1 GPIO initialization gpio_pin_init(GPIOE, 5, GPIO_MODE_OUTPUT, GPIO_PULL_NONE, GPIO_DRV_3); // LED2 GPIO + PWM initialization gpio_pin_init(GPIOA, 2, GPIO_MODE_AF3, GPIO_PULL_NONE, GPIO_DRV_3); pwm_init(PWM0, PWM_MODE_CONTINUOUS, 1, PWM_PSC_120); // 24M / 120 = 200kHz pwm_set_period(PWM0, LED_PWM_PERIOD); // 200k / 200 = 1kHz pwm_set_pulse_len(PWM0, 0); pwm_enable(PWM0); // Create a table with half-period of sin. Just to make sure math functions are working for (uint16_t i = 0; i < LED_FADE_STEPS/2; i++) { float arg = M_PI/(float)(LED_FADE_STEPS/2)*(float)i - (M_PI/2.f); // -pi/2 .. pi/2 float val = sinf(arg); // -1 .. 1 sin_values[i] = (uint16_t)((val/2.f+0.5f)*(float)LED_PWM_PERIOD); } timer_init(); while (1) { gpio_pin_set(GPIOE, 5); delay(200); gpio_pin_clear(GPIOE, 5); delay(200); } return 0; } void timer_init(void) { // Configure timer to generate update event every 1ms tim_init(TIM0, TIM_MODE_CONT, TIM_SRC_HOSC, TIM_PSC_1); tim_set_period(TIM0, 24000000UL / 1000UL); tim_int_enable(TIM0); // IRQ configuration intc_set_irq_handler(IRQ_TIMER0, timer_irq_handler); intc_enable_irq(IRQ_TIMER0); tim_start(TIM0); } void timer_irq_handler(void) { static uint16_t step = 0; static uint8_t dir = 0; tick_flag = 1; //printf("tim_irq!\r\n"); if (dir == 0) // First half of sinewave { step++; if (step == LED_FADE_STEPS/2-1) dir = 1; } else // Second half of sinewave { step--; if (step == 0) dir = 0; } // Update PWM pulse length pwm_set_pulse_len(PWM0, sin_values[step]); tim_clear_irq(TIM0); } void delay(uint32_t t) { while (1) { if (t == 0) return; if (tick_flag == 1) { tick_flag = 0; t--; } } } |
هدر system.h کد system.c را فراخوانی میکند که وظیفه راهاندازی بخشهای اصلی سختافزاری را ازجمله واحد کلاک cpu، mmu، راهاندازی uart، کلاک نرمافزاری، تأخیر و… را بر عهده دارد.
هدرهایی که با f1c100 شروع میشوند همگی شامل راهاندازی توابع سطح پایین (low level) سختافزاری نظیر تایمر، وقفه، gpio، pwm و… هستند.
به دلیل اینکه ما برای تست از برد sinux f1 استفاده میکنیم و این برد یک led روی پین E5 داره، پینهای A1 رو به E5 تغییر دادیم و در ادامه داخل یک حلقه نامتناهی با دستور gpio_pin_set(GPIOE, 5) پایه شماره پنج از پورت f1c100، E رو بهعنوان خروجی و مقدار ولتاژ یک دیجیتال تعریف میکنیم. دستور gpio_pin_clear(GPIOE, 5) همان پایه پورت را بهعنوان خروجی و با مقدار دیجیتال صفر تعریف میکند. که درنهایت ما یک led چشمکزن با تأخیر 200 میلیثانیه داریم.
1 2 3 4 5 6 7 | while (1) { gpio_pin_set(GPIOA, 1); delay(200); gpio_pin_clear(GPIOA, 1); delay(200); } |
چون ما خیلی سطح پایین کار میکنیم تابع آمادهای برای تأخیر نداریم و خودمان باید delay رو بسازیم، برای این کار یک تایمر 1 میلیثانیه راهاندازی میشه:
1 2 3 4 5 6 7 8 9 10 11 12 | void timer_init(void) { // Configure timer to generate update event every 1ms tim_init(TIM0, TIM_MODE_CONT, TIM_SRC_HOSC, TIM_PSC_1); tim_set_period(TIM0, 24000000UL / 1000UL); tim_int_enable(TIM0); // IRQ configuration intc_set_irq_handler(IRQ_TIMER0, timer_irq_handler); intc_enable_irq(IRQ_TIMER0); tim_start(TIM0); } |
و در وقفه تایمر هر بار متغیر tick_flag رو برابر 1 میکنیم:
1 2 3 4 5 6 7 8 9 | void timer_irq_handler(void) { static uint16_t step = 0; static uint8_t dir = 0; tick_flag = 1; //printf("tim_irq!\r\n"); if (dir == 0) // First half of sinewave { ... |
بعد از اون توی تابع delay در یک حلقه دیگه منتظر ۱ شدن tick_flag میمونیم و بعد از اون بلافاصله صفرش میکنیم و به این صورت یک تابع delay داریم:
1 2 3 4 5 6 7 8 9 10 11 12 13 | void delay(uint32_t t) { while (1) { if (t == 0) return; if (tick_flag == 1) { tick_flag = 0; t--; } } } |
کامپایل کد
داخل توضیحات کتابخانه ابزار لازم برای کامپایل کد در سیستمعامل ویندوز و نرمافزار eclipse رو گفته، اما ما کدمون رو توی لینوکس میخواهیم کامپایل کنیم و از ubuntu استفاده میکنیم، بدون ide. ابتدا لازم هست کامپایلر gcc مناسب arm و برنامه make رو نصب کنید:
1 | sudo apt install gcc-arm-none-eabi make |
حالا به پوشه پروژه رفته و با دستور make برنامه را کامپایل میکنیم:
1 2 3 | git clone https://github.com/nminaylov/F1C100s_projects cd F1C100s_projects-master/hello_led make -j4 |
با اجرای دستورات بالا به صورت خودکار یک پوشه به نام build درست شده که در آن تمام فایل های لینک شده و فایل نهایی hello_led.bin موجود است.
اجرای کد
ما میتونیم برناممون رو از روی sd card و یا spi flash اجرا کنیم که البته در هر دو حالت نیاز به بوت لودر داریم، هرچند درنهایت بهتره که کدتون از روی spi flash اجرا بشه، اما با استفاده از sd card میتونید چند برنامه داشته باشید که هر کدوم رو خواستید توسط بوت لودر اجرا کنید. در ادامه هر دو روش رو توضیح دادیم.
اجرای کد از روی sd card
برای این روش لازم هست شما یک ایمیج که uboot داره رو روی sdcard داشته باشید، میتونید از این ایمیجی که آماده کردیم استفاده کنید و روی sd رایت کنید، بعد از اون هم فایل خروجی hello_led.bin
رو که در پوشه F1C100s_projects/hello_led/build
هست داخل تنها پارتیشن sd کپی میکنیم.
حالا باید sd رو داخل برد sinux f1 یا لیچی پای نانو قرار بدیم و پینهای سریال رو توسط مبدل سریال به سیستم متصل کنیم. بااتصال تغذیه باید چنین خروجی داشته باشید
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | U-Boot 2020.07 (Nov 04 2021 - 06:48:19 -0400) Allwinner Technology CPU: Allwinner F Series (SUNIV) Model: Allwinner F1C100s Generic Device DRAM: 64 MiB MMC: mmc@1c0f000: 0, mmc@1c10000: 1 Setting up a 480x272 lcd console (overscan 0x0) In: serial ... ... switch to partitions #0, OK mmc0 is current device Booting from MMC0... Wrong Image Format for bootm command ERROR: can't get kernel image! => |
خب حالا وارد uboot شدیم و با این دستورات میتونید برنامه رو اجرا کنید :
1 2 | fatload mmc 0:2 80000000 hello_led.bin go 80000000 |
دستور اول فایلمون رو از پارتیشن دوم کارت حافظه به آدرس 80000000
رم انتقال میده و دستور بعدی باعث میشه تا cpu به این آدرس در رم پرش کنه و از اونجا ادامه بده.
در ادامه میتونید خروجی سریال و وضعیت led رو ببینید:
1 2 3 4 5 6 7 8 9 10 | Booting from MMC0... Wrong Image Format for bootm command ERROR: can't get kernel image! => => => fatload mmc 0:2 80000000 hello_led.bin 26376 bytes read in 7 ms (3.6 MiB/s) => go 80000000 ## Starting application at 0x80000000 ... Hello, world! |
اجرای کد از روی spi nor flash
برای این روش نیاز هست که ابتدا یک بوت لودر رو روی حافظه بریزیم و بعد از اون کدمون رو، تا بتونیم اجراش کنیم. برای پروگرام فلش هم میتونیم از طریق درگاه usb خود برد این کار رو انجام بدیم.
انجام این کار در ویندوز قبلاً در سیسوگ توضیح دادهشده، برای استفاده در لینوکس ابتدا باید یکسری پکیج رو نصب کنیم:
1 | sudo apt install pkg-config zlib1g-dev libusb-dev libusb-1.0-0-dev |
حالا سورس کد ابزار sunxi-tools رو دانلود، کامپایل و نصب میکنیم:
1 2 3 4 | git clone https://github.com/Icenowy/sunxi-tools.git -b f1c100s-spiflash cd sunxi-tools make sudo make install |
برای اینکه در زمان انتقال فایل به برد با مشکل دسترسی مواجه نشیم لازمه که یک فایل با محتویات زیر در این آدرس ایجاد کنیم/etc/udev/rules.d/99-allwinnerdl.rules
1 2 3 4 5 6 | ACTION!="add", SUBSYSTEM!="usb_device", GOTO="rules_end" ATTR{idProduct}=="efe8", ATTR{idVendor}=="1f3a", MODE="666" ATTR{idProduct}=="1010", ATTR{idVendor}=="1f3a", MODE="666" LABEL="rules_end" |
مرحله بعدی بررسی روال بوت هست، همان طور که در تصویر پایین مشاهده میکنید اول cpu تلاش میکنه تا از روی sd card بوت بشه، اگر نتونست از روی spi flash و اگه نشد در آخر usb boot.
ما برای اینکه به مرحله آخر برسیم، میتونیم sd card رو دربیاریم، اما نیاز هست که spi flash رو هم رد کنیم، برای این کار در برد sinux f1 میتونید دکمه boot رو نگهدارید تا از spi flash لود نشه، اما برای برد licheepi nano باید یکم لحیمکاری کنید روی برد! (اینجا دقیقتر توضیح دادهشده).
حالا باید برنامه simpleloader رو کامپایل کنیم:
1 2 | cd F1C100s_projects/simple_loader make |
قبل از استفاده از simpleloader باید با استفاده از mksunxi هدر eGON.BT0 رو در این فایل اصلاح کنیم:
1 2 3 | cd F1C100s_projects/_tools_/mksunxi/ gcc -o mksunxi mksunxi.c ./mksunxi ../../simple_loader/build/simple_loader.bin |
درنهایت هم باید با ابزار sunxi-fel که بالاتر نصب کردیم اول لودر و بعدش هم برناممون رو روی spiflash به ترتیب در آدرسهای 0x00000 و 0x10000 بریزیم:
1 2 | sunxi-fel -p spiflash-write 0x00000 mysimple-loader.bin sunxi-fel -p spiflash-write 0x10000 myproject-baremetal.bin |
در قسمت بعد به سراغ اجرای LVGL به کمک این کتابخونه میرویم.
سلام و تشکر دوباره بخاطر مطلب بسیار مفید و ارزشمندی که منتشر فرمودید.
یک خواهشی داشتم اگر ممکن باشه.
لطفا SDK کامل این پردازشگر رو که شامل هدر فایل های audio_decoder.h هم هست
فراهم بفرمایید.
چون بنده خیلی سعی کردم . متاسفانه نتونستم جایی پیدا کنم حتی خود تولید کننده هم دسترسی نمیده.
اگر به روشی توانستید از طریقی دسترسی پیدا کنید لطفا برای ما هم به اشتراک بگذارید
سپاس سپاس سپاس.
سلام
sdk های متفاوتی توسط افراد مختلفی برای این پردازنده توسعه داده شده و معمولا توی هر کدوم یه چیزایی هست و یه چیزایی نیست ، باید همه رو دقیق بررسی کنید برای چیزی که میخواید ، در مورد فایلی که نیاز دارید ما اطلاعاتی نداریم.
اقا سلام تشکر تشکر..
ممکنه خواهش کنم براب فلش کردن لینوکس هم مطلب بزارید من
لینوکس رو کامپایل کردم مخصوص این چیپ و از روی مموری میخونمش اگر بتونم از فلش داریو خود برد اجراش کنم عالی میشه. خیلی خیلی ممنون.
برای بحث لینوکس کار یکم پیچیده تر از این حرفا هست ، چون لازمه هم توی بوت لودر ، هم کرنل و هم سیستم فایل تغییراتی اعمال بشه تا بتونیم از روی فلش بوتش کنیم
میتونید سوالتون رو دقیقتر و با توضیح اینکه از چه روشی برای ساخت ایمیج لینوکسی استفاده میکنید توی انجمن سیسوگ بپرسید تا همکاران در صورت امکان جوابتون رو بدند
ask.sisoog.com
سلام خدا قوت
ببخشید من فایل u-boot شما رو که روی sd card میریزم٬ لینوکس پارتیشنی بعدش روی اون تشخیص نمیده که من بتونم فایل bin رو بهش کپی کنم!
سلام
یه پارتیشن 8.3 مگابایتی میسازه که روی اون میتونید فایلو کپی کنید
اگر این پارتیشن رو نشون نمیده یه جای کار رو اشتباه رفتید
الان هم تست کردم فایل مشکلی نداره
سلام
من هم همین مشکل را دارم
از طریق ویندوز فایل ایمیج را روی اس دی کارت میریزم (با نرم افزار RUFUS یا Win32Disk imager)
بعد که عملیات رایت انجام شد، ویندوز دیگه SD کارت را باز نمیکنه و اگه روی درایو SD کلیک کنی میخواد فرمتش کنه
چطور از طریق ویندوز فایل باینری را بعد از رایت کردن فایل بوت کپی کنم؟
خب این که کاملا درست هست.
چون فرمت پارتیشنی که روی sd ریخته میشه اصلا توسط ویندوز قابل شناسایی نیست و شما فقط توی لینوکس میتونید اون رو باز کنید
چراکه ویندوز سیستم فایل های لینوکسی رو نمیتونه بخونه
با درود.
سپاس بابت مطلب خوبتون.
من مدتی هست درگیر راه اندازی freeRtos با درایورهای این کتابخانه روی f1c100 هستم ولی هنوز موفق نشدم .
یک پورت مناسب از FreeRtos رو برای این چیپ سراغ دارید؟
سلام
میتونید از
https://github.com/RT-Thread/rt-thread
یا
https://github.com/xboot/xboot
استفاده کنید
من پورتی برای کتابخانه FreeRtos برای این چیپ ندیدم
سلام.
اول خیلی ممنون بابت آموزش عالیتون…
دوم اینکه… راهی نیست که هر دفه به وجود کامپیوتر برای شروع برنامهی کامپایل شده نیازی نباشه؟ (هنوز spiflash رو امتحان نکردم البته)
spiflash رو که تست کنید همین کار رو انجام میده 🙂
سلام عالی بود خیلی زحمت کشیدید فقط من قبلا در گیت هاب بخصوص دیده بودم از کامپایلر keil برای این ای سی استفاده کرده بودن فکر میکنم شما هم همینکار را بکنید آموزشهاتون کاربردی تر بشه
سلام خیلی ممنون.
من به شخصه این در این رابطه اطلاعی نداشتم ولی حتما نگاه میکنم و نظرم را عرض میکنم در هر صورت خیلی ممنون بابت اطلاعاتتون.
سلام مهندس عزیز- ممنون از آموزش عالی شما . سوال اول اینکه در روش بدون ide و MAKE فایل چطور میتونید با ویندوز BIN رو ایجاد کنیم ؟ (بدون IDE) آیا نرم افزار و یا ابزار خاصی داره ؟ ویندوز 8.1
و اینکه چطور میتونم برای مطرح کردن چند سوال تخصصی با شما ارتباط بگیرم ؟ تلگرام ، واتس اپ و یا حتی ایمیل
سلام تشکر از باز خوردتون
فایل make دارای مشخصات کامپایلر لینکر خروجی فایل کامپایلی (.bin) و… است. برای کامپایل و اجرای پروژه حتما باید کامپایلر در جایی مشخص شده بشه (که در اینجا در حقیقت کراس کامپایلر) اگر از ide استفاده میشود حتما در ان مشخص شده. اما روش دیگر ایجاد فایل make است یا اجرای عملیات های داخل make به صورت کامندی که اصلا توصیه نمیشه
سلام و عرض ادب
اولا تشکر بابت سایت خیلی خوب تون
به شخصه هر قطعه الکترونیکی بخوام خرید کنم، حتما تو سایت شما جستجو می کنم
مورد دومی که همیشه ازش بهره برم آموزش های خیلی خوب تون هست که تو سایت منتشر می کنید. از این بابت هم به شخصه خیلی بهره بردم. از جمله همین آموزش که خیلی کاربردی و مفید بود برای من.
مشتاقانه منتظر ادامه این آموزش هستم. ممنون از آقای محمد کلاهی، نویسنده این متن
خیلی عالیه سایت تون ?
سلام و وقت بخیر
ممنون بابت بازخوردتون
تاچند روز دیگر انشالله قسمت بعدی آموزش در اختیارتان قرار میگیرد
ایا با کورتکس سری A چیپی هست مثل این که خروجی تصویر LCD و انالوگ و ورودی تصویر انالوگ و دیجیتال داشته باشد؟ با فرکانس بالاتر که بشود bare metal راه اندازی کرد؟
شرکت allwinner ، خیلی از soc های داخل بازار جهانی را تولید میکند.اگر به ویکی خود allwinner مراجعه کنید انواع ان را میبینید.
من به شخصه چیزی کامل تر و بهتر از لینکی که برای f1c100 پیدا کردم پیدا نکردم ولی برای فرکانس بالاتر و قابلیت هایی که گفتید برای پردازنده h3 این لینک را پیدا کردم ولی https://github.com/mprymek/allwinner-baremetal ولی انرا تست نکردم
خیلی عالی و مفید بود
ممنونم امیدوارم مفید واقع شده باشه
عالی عالی عالی خداقوت
تشکر باعث قوت قلب هستید
بسیار عالی بود
خیلی ممنون
مهندس دمت گرم ، لذت بردیم
خوشحالم که مورد پسند بود ?