روشهای برنامهنویسی تأثیر بسزایی در سرعت اجرای برنامه خواهندداشت. فرضکنید قصددارید ۶۴ کانال مجزای PWM با فرکانس ۱۰ کیلوهرتز ایجاد کنید، با دقت 8 بیتی هر PWM؛ در نگاهاول پیادهسازی بااستفادهاز میکروکنترلر حتی میکروکنترلرهای قدرتمندی چون ARM غیرممکن بهنظرمیرسد و ممکناست برای پیادهسازی آن به سراغ FPGA بروید یا کلاً از پیادهسازی چشمپوشی کنید؛ اما با روشهای صحیح برنامهنویسی و تسلط به میکروکنترلر بهراحتی میتوان این بحث را بااستفادهاز یک میکروکنترلر STM32F103 انجامداد. شناخت محدودیتهای موجود و داشتن مهارت برنامهنویسی به ما کمک میکند که قادربه طراحی بهینه باشیم و ازقبل بدانیم چهکاری ممکن و چهکاری غیرممکن است. در این مقاله دو مبحث را موردبررسی قرار میدهیم: 1. تأثیر روشهای مختلف برنامهنویسی 2. حالتهای مختلف اپتیمایز (Optimize) بر عملکرد و سرعت اجرای برنامه.
در این مقاله بااستفادهاز میکروکنترلر STM32F103VET6 از مجموعه سریهای ARM Cortex-M3 ساخت شرکت ST سعیداریم نشاندهیم که روشهای مختلف برنامهنویسی و اپتیمایزهای مختلف چه تأثیری در روند اجرای برنامه دارند. برای این اندازهگیری، از یک روش ساده و همیشگی استفادهخواهیمکرد: تغییر وضعیت یک پایه؛ هرچه فرکانس ایجادشده بیشتر باشد، یعنی عملکرد بهینهتر و سریعتر برنامه. همچنین از کامپایلر GCC که یک کامپایلر قدرتمند و متنباز است استفادهخواهیمکرد. این آزمایش را میتوان توسط دو روش مختلف انجام داد:
- استفاده مستقیم از CPU برای تغییر وضعیت پایه
- استفادهاز انتقالدهنده DMA
که در ادامه نتایج هر دو روش را بررسی میکنیم. استفادهاز آیسی های ARM این اجازه را به ما میدهد که برنامه به زبان C را بدون استفادهاز هیچ کتابخانهای و بهصورت ساده و مبتدی روی آن پیاده کنیم.
تنظیمات کلاک سیستم: کلاک سیستم میتواند فرکانسهای مختلفی بین ۴۸ و ۷۲MHz داشته باشد که تنظیمات فلش روی کلاک سیستم تأثیرگذار است:
These bits represent the ratio of the SYSCLK (system clock) period to the Flash access time. 000 Zero wait state, if 0 < SYSCLK ≤ 24 MHz 001 One wait state, if 24 MHz < SYSCLK ≤ 48 MHz 010 Two wait states, if 48 MHz < SYSCLK ≤ 72 MHz
سرعت تغییر وضعیت STM32F1 یکی از روشهای ساده برای کپی کردن اطلاعات از مموری بهروی GPIO استفادهاز حلقهی for است:
{ u8 buffer[8] = {0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}; while(1) { for(u32 i=0; i<8; i++) { GPIOD->ODR = buffer[i]; } } }
در فرکانس ۴۸MHz بدون تنظیمات flash wait states، درحالت بهینهسازی نشده (O0)، به فرکانس بیش از ۱MHz دست پیدا میکند. در زمان اجرای برنامه در زمانهای ریاستارتِ حلقهی while، شکافی در سرعت اجرا ایجاد میشود که بهدلیل پاکشدن PipeLine درهنگام اجرای دستورات شرطی و پرشی است:
مجدداً برای فرکانس ۴۸MHz بدون تنظیمات flash wait states آزمایش را تکرار میکنیم ولی اینبار بهینهسازی O2 را اعمال میکنیم. به فرکانس ۱.۸MHz دست پیدا میکند. درشکل نیز دیده میشود که شکافهای ایجادشده توسط حلقهی while حذف شدهاند:
وقتیکه تنظیمات فلش را روی ۲wait states قرار دهیم، بدون اعمال بهینهسازی عملکرد CPU کاهش مییابد: (here -O0)
با تنظیم فلش روی ۲wait states و اعمال بهینهسازی O2 عملکرد CPU نسبتبه بدون تنظیمات فلش کاهش مییابد: (here -O2)
اینبار فرکانس را روی ۷۲MHz تنظیم میکنیم و آزمایشها را تکرار میکنیم. در حالت بدون تنظیمات flash wait states و بدون بهینهسازی عملکرد CPU بهبود مییابد: (here -O0)
با تنظیم فلش روی ۲wait states و اعمال بهینهسازی O2، عملکرد CPU در فرکانس ۷۲MHz نسبت به فرکانس ۴۲MHz بهبود مییابد: (here -O2)
در حالت بهینهسازی نشده، حلقهی for در اجرای برنامه تأخیر ایجاد میکند. برای حل این مسئله دستورات برنامه را بدوناستفادهاز حلقهی for مینویسیم:
{ u8 buffer[8] = {0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}; while(1) { GPIOD->ODR = buffer[0]; GPIOD->ODR = buffer[1]; GPIOD->ODR = buffer[2]; GPIOD->ODR = buffer[3]; GPIOD->ODR = buffer[4]; GPIOD->ODR = buffer[5]; GPIOD->ODR = buffer[6]; GPIOD->ODR = buffer[7]; } }
در این برنامه با تنظیمفرکانس سیستم، برروی ۷۲MHz میتوانیم به فرکانس ۳.۶MHz دستیابیم. البته تأثیر حلقهی while همچنان باقیاست:
در فرکانس ۷۲MHz مشابه حالت قبل اما با بهینهسازی O2 عملکرد CPU بهطرز چشمگیری به ۱۸MHz بهبود مییابد. قابلتوجهاست که ۱۸MHz بالاترین سرعت GPIO بیانشده در دیتاشیت آن است.
ارسال اطلاعات توسط DMA به GPIO: روش دیگری برای کپی اطلاعات از SRAM روی GPIO استفادهاز (DMA (Direct Memory Access است. DMA بدون استفادهاز CPU اطلاعات را از A روی B کپی میکند.
u8 buffer[8] = {0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}; /* DMA setup */ RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* channel 1: mem:8bit -> peri:8bit */ DMA1_Channel1->CNDTR = 8; DMA1_Channel1->CMAR = (uint32_t)buffer; DMA1_Channel1->CPAR = (uint32_t)&(GPIOD->ODR); DMA1_Channel1->CCR = 0; DMA1_Channel1->CCR = DMA_CCR1_MEM2MEM | DMA_CCR1_PL | DMA_CCR1_MINC | DMA_CCR1_CIRC | DMA_CCR1_DIR | DMA_CCR1_EN; while(DMA1_Channel1->CNDTR);
درواقع، بهدلیل استفادهاز حلقهی while در انتهای برنامه، روش DMA بدون استفادهاز بهینهسازی بهاندازهی CPU سریع عمل نمیکند. در زمان استفاده DMA برای انتقال اطلاعات به GPIO ،اطلاعات خواستهشده از روی دیتاباس خوانده میشوند.
اینبار حلقه while را با یک حلقه for بدون شرط جایگزین میکنیم تا تفاوت را مشاهدهکنیم:
//while(DMA1_Channel1->CNDTR); for(;;) {}
در این حالت نتیجه با حالت استفادهاز CPU مشابه میشود (۳.۶MHz):
قابلذکر است که تمامی آزمایشهای DMA بدون اعمال بهینهسازی انجام میشود؛ زیرا با اعمال بهینهسازی O2 نتایج مطلوبی حاصل نمیشود:
جمعبندی: با توجهبه آزمایشهای انجامشده ملاحظهشد که STM32F1 با اعمال بهینهسازی O2 و بدون استفادهاز دستور حلقهی for میتواند به فرکانس ۱۸MHz در GPIO دست پیدا کند (البته اعمال بهینهسازی O3 نتایج بهتری را تولید نمیکند). برای حالت انتقال اطلاعات توسط DMA اگر از ۳۲ بیت DMA برای خواندن اطلاعات از SRAM استفاده شود ممکناست بتوان به نتایجبهتری دستیافت.
منبع:سیسوگ