سیستم عامل Multitasking روی AVR

مثال C برای پیاده سازی کرنل چند وظیفه ای برای AVR

معرفی :

وظیفه یک سیستم عامل Real Time این است که بین Task ها سوئیچ کند و مطمئن شود که هر Task با توجه به اولویتی که دارد پردازش خواهد شد .این که این سوئیچ شدن چطور انجام شود به معماری میکروکنترلر بستگی دارد . این مقاله از کد های FreeRTOS( an open source real time scheduler ) و GCC برای نشان دادن سوئیچ شدن Task روی AVR استفاده می کند .

کد ها از پایین به بالا توضیح داده می شوند . موضوع مورد بحث شامل پیکر بندی Tick دوره ای و استفاده از GCC برای نوشتن سرویس وفقه با C و ویژگیهای GCC در FreeRTOS و Execution Context درباره AVR می شود .

قسمت آخر راهنمای گام به گام در مورد سوئیچ شدن کامل یک وظیفه می شود.

دنبال کردن سورس کد میتواند راه خوبی برای یادگیری کامپایلر و سخت افزار باشد – حتی اگر این موضوع مستقیماً مربوط به برنامه شما نباشد . من امیدوارم این مقاله مورد علاقه کسانی باشد که می خواهند یاد بگیرند که چطور در AVR با استفاده از GCC سرویس  وقفه بنویسند ، کسانی که تازه وارد AVR شدند و یا کسانی که فقط به RTOS علاقه دارند .

خوانندگان محترم باید با مباحث پایه ای Real Time Operating System مثل Task ، Multitaskig ، Context Switching آشنا باشند . توضیحات مختصری در مورد این موضوعات را می توانید از www.FreeRTOS.org کسب کنید .

 

The RTOS Tick

ساختار برنامه های کاربردی که از Real Time Oprating System (RTOS) استفاده میکنند به این صورت است که از تعدادی وظایف مستقل از هم تشکیل شده اند که سیستم عامل در هر لحظه تصمیم میگیرد کدام وظیفه باید اجرا شود . Kernel یا هسته RTOS هر جا که نیاز باشد وظایف را به طور موقت کنار میگذارد یا اجرای وظیفه ای را ادامه میدهد تا مطمئن شود وظیفه ی با اولویت بیشتر برای گرفتن زمان CPU آماده اجرا است . علاوه بر معوق کردن وظایف توسط  Kernel یک وظیفه میتواند خودش را نیز معوق کند .این کار  میتواند به این دلیل صورت گیرد که وظیفه بخواهد تا مدت زمانی منتظر بماند یا به حالت Sleep برود تا منابع (Resource) قابل دسترس شوند . برای جزئیات بیشتر به FreeRTOS WEB مراجعه کنید اگر اطلاعات کافی در این ضمینه ندارید .

FreeRTOS با استفاده از متغیر Tick count زمان را انداز گیری می کند. وقفه تایمر (the RTOS tick interrupt) مقدار tick count را افزایش میدهد در نتیجه زمان با دقت خوبی محاسبه میشود– با اندازه گیری فرکانس رخ دادن وقفه ی تایمر .

هنگامی که یک وظیفه خودش را معوق میکند یک دوره ی تاخیر («Sleep»)  مشخص میکند . هر زمانی که tick count افزایش می یابد کرنل باید چک کند که آیا زمان تاخیر به پایان رسیده است . هر وظیفه ای که توسط کرنل تشخیص داده شود که زمان تاخیر آن تمام شده برای اجرا آماده میشود .ممکن است Context Switching در زمانی که یک وظیفه با اولویت بالاتر توسط RTOS tick برای اجرا آماده شده نیاز باشد . با این اتفاق RTOS tick باعث وقفه در یک وظیفه میشود اما اجرای وظیفه با اولویت بالاتر را ادامه میدهد .

در این دیاگرام زمان از چپ به راست در گذر است . خطوط رنگی نشان دهنده وظایف در حال اجرا در هر لحظه ی خاص هستند . با توجه به شماره ها در دیاگرام:

 – در 1 idle tack در حال اجراست .

– در 2 RTOS tick رخ میدهد و کنترول برنامه به tick ISR 3 میرود .

Tick ISR – باعث آماده ی اجرا شدن vControlTask میشود و vControlTask اولویت بیشتری ازidle tack  دارد .

–          با Context switch به vControlTask پس از بازگشت از ISR 4 کنترول برنامه به vControlTask میرود و شروع به اجرا شدن میکند .

 

ایجاد وقفه Tick

وقفه Compare match تایمر 1 برای ایجاد RTOS tick بکار گرفته میشود .

تایمر 1 به صورت افزایشی در فرکانس مشخصی که با تقسیم فرکانس کلاک سیستم به Prescaler تعیین میشود پیکربندی میشود . بخاطر این به Prescaler نیاز داریم که مطمئن شویم شمارنده تایمر خیلی سریع سرریز نشود . مقدار compare match باید به صورتی که تایمر 1 از صفر تا مقدار مورد نیاز برای ایجاد دوره تیک افزایش یابد محاسبه شود.(سخن مترجم:فرمولهای مورد نیاز در Datasheet موجود است). هنگامی که شمارنده تایمر 1 ، برابر با  مقدار compare match شود وقفه اجرا میشود و AVR به طور خودکار تایمر 1 را صفر میکند در نتیجه وفقه بعدی دقیقاً با همان Interval رخ خواهد داد .

 
/* Hardware constants for timer 1 on ATMega323. */
#define portCLEAR_COUNTER_ON_MATCH ( 0x08 )
#define portPRESCALE_256 ( 0x04 )
#define portCLOCK_PRESCALER ( 256 )
#define portCOMPARE_MATCH_A_INTERRUPT_ENABLE ( 0x10 )
/*
Setup timer 1 compare match A to generate a tick interrupt.
*/
static void prvSetupTimerInterrupt( void )
{
unsigned portLONG ulCompareMatch;
unsigned portCHAR ucHighByte, ucLowByte;
/* Generate the compare match value for our required tick
frequency. */
ulCompareMatch = portCPU_CLOCK_HZ / portTICK_RATE_HZ;
/* We only have 16 bits so have to scale to get our
required tick rate. */
ulCompareMatch /= portCLOCK_PRESCALER;
/* Setup compare match value for compare match A.
Interrupts are disabled before calling this function so
we need not bother here. [casting has been removed for
each of reading] */
ucLowByte = ulCompareMatch & 0xff;
ulCompareMatch >>= 8;
ucHighByte = ulCompareMatch & 0xff;
outb( OCR1AH, ucHighByte );
outb( OCR1AL, ucLowByte );
/* Setup clock source and compare match behaviour. */
ucLowByte = portCLEAR_COUNTER_ON_MATCH | portPRESCALE_256;
outb( TCCR1B, ucLowByte );
/* Enable the interrupt - this is okay as interrupt
are currently globally disabled. */
ucLowByte = inb( TIMSK );
ucLowByte |= portCOMPARE_MATCH_A_INTERRUPT_ENABLE;
outb( TIMSK, ucLowByte );
}

 

تعریف Execution Context

هنگامی که یک وظیفه اجرا میشود مانند هر برنامه دیگر از رجیستر ها و RAM و ROM استفاده میکند .این منابع با هم ( رجیسترها و استک و غیره ) execution context یک وظیفه را تشکیل میدهند .

یک وظیفه رشته ای از کد است که نمی داند چه موقع معوق خواهد شد یا دوباره اجرا میشود .تصور کنید مثلاً یک وظیفه درست در زمانی که میخواهد عملیاتی روی محتوای رجیسترها انجام گیرد با ید معوق شود .

 

در این زمان دیگر وظایف ممکن است محتوای رجیستر ها را تغییر دهند و عملیاتی را روی آنها انجام دهند . هنگامی که وظیفه معوق شده میخواهد ادامه یابد نمیداند که محتوای رجیسترها تغییر کرده اند و اگر از آنها برای جمع کردن استفاده کند به نتیجه اشتباه میرسد .

برای اینکه از چنین اشتباهی جلوگیری شود در هنگام از سرگیری وظیفه باید مقادیر رجیسترها برابر همان موقعی باشند که وظیفه معوق شده است .کرنل وظیفه حصول اطمینان از این مورد را برعهده دارد و باید Context را اگر وظیفه ای باید معوق شود ذخیره کند . هنگام از سرگیری نیز محتوای ذخیره شده باید بازیابی شوند . پروسه ذخیره و بازیابی به Context switching موصوم است.

 

Context در AVR

Context در میکرو کنترولر AVR شامل این موارد است :

– 32 رجیستر عمومی .

– Status register . مقدار این رجیستر در اجرای دستورات تاثیر دارد و باید در هنگام سوئیچ شدن حفظ شود .

– Program counter . یک وظیفه باید در زمان از سرگیری ،از جایی که به تعویق افتاده ادامه پیدا کند .

– دو رجیستر Stack pointer .

 

نوشتن ISR – صفت signal در GCC

FreeRTOS وقفه tick را با استفاده از رویداد تطابق مقایسه در تایمر شماره یک ایجاد می کند . برای نوشتن ISR با استفاده از GCC باید از سینتکس زیر پیروی کنید :

 
void SIG_OUTPUT_COMPARE1A( void ) __attribute__ ( ( signal )
);
void SIG_OUTPUT_COMPARE1A( void )
{
/* ISR C code for RTOS tick. */
vPortYieldFromTick();
}

کد Cبرای سرویس روتین وقفه ی تطابق مقایسه

دستور _attribute_(( signal )) به کامپایلر اطلاع می دهد که تابع نوشته شده یک ISR است و در نتیجه باعث دو تغییر مهم در کدهای خروجی کامپایلر می شود :

  1. صفت signal مطمئن می شود که هر رجیستری که در ISR تغییر می کند هنگام خروج از ISR به مقدار قبلی خود باز می گردد . نوشتن آن به این منظور است که کامپایلر برای بهینه سازی کدها و اینکه کدام رجیستر ها در سرویس وقفه مورد استفاده قرار می گیرند و کدامیک استفاده نمی شوند ، محاسبات لازم را انجام دهد .
  2. همچنین صفت signal باعث میشود که بجای دستور بازگشت RET از دستور » بازگشت از وقفه » RETI استفاده شود . AVR تمام وقفه ها را در هنگام ورود به ISR غیر فعال می کند و دستور  RETI برای فعال کردن دوباره وقفه ها مورد نیاز است .

کد خروجی تولید شده توسط کامپایلر :

 
;void SIG_OUTPUT_COMPARE1A( void )
;{
; ---------------------------------------
; CODE GENERATED BY THE COMPILER TO SAVE
; THE REGISTERS THAT GET ALTERED BY THE
; APPLICATION CODE DURING THE ISR.
PUSH R1
PUSH R0
IN R0,0x3F
PUSH R0
CLR R1
PUSH R18
PUSH R19
PUSH R20
PUSH R21
PUSH R22
PUSH R23
PUSH R24
PUSH R25
PUSH R26
PUSH R27
PUSH R30
PUSH R31
; ---------------------------------------

; CODE GENERATED BY THE COMPILER FROM THE
; APPLICATION C CODE.
;vTaskIncrementTick();
CALL 0x0000029B ;Call subroutine
;}
; ---------------------------------------
; CODE GENERATED BY THE COMPILER TO
; RESTORE THE REGISTERS PREVIOUSLY
; SAVED.
POP R31
POP R30
POP R27
POP R26
POP R25
POP R24
POP R23
POP R22
POP R21
POP R20
POP R19
POP R18
POP R0
OUT 0x3F,R0
POP R0
POP R1
RETI
; ---------------------------------------

سازماندهی Context – صفت » عادی » در GCC

قسمت قبلی نشان داد که چطور از صفت signal برای نوشتن ISR در C و ذخیره ی Context ها به طور اتوماتیک استفاده کرد ( البته فقط رجیستر هایی از میکروکنترلر که در ISR تغییر می کردند ذخیره می شوند ) . برای Context switching باید تمام context ذخیره شوند . اگر برنامه کاربردی تمام رجیستر ها را ذخیره کند ممکن است بعضی از آنها دو بار ذخیره شوند ، یکبار توسط کد تولید شده ی کامپایلر و دوباره توسط کدهای برنامه کاربردی . میتوان از این کار با بکار بردن صفت «naked» جلوگیری کرد  .

 

 
void SIG_OUTPUT_COMPARE1A( void ) __attribute__ ( ( signal,
naked ) );
void SIG_OUTPUT_COMPARE1A( void )
{
/* ISR C code for RTOS tick. */
vPortYieldFromTick();
}

کد Cعادی برای سرویس روتین وقفه ی تطابق مقایسه

صفت «naked» باعث جلوگیری از ایجاد کدهای ورود و خروج از هر تابعی  توسط کامپایلر می شود .

کدهای تولید شده توسط کامپایلر زمانی که از هر دو صفت signal و naked استفاده شده باشد :

 

 
;void SIG_OUTPUT_COMPARE1A( void )
;{
; ---------------------------------------
; NO COMPILER GENERATED CODE HERE TO SAVE
; THE REGISTERS THAT GET ALTERED BY THE
; ISR.
; ---------------------------------------
; CODE GENERATED BY THE COMPILER FROM THE
; APPLICATION C CODE.
;vTaskIncrementTick();
CALL 0x0000029B ;Call subroutine
; ---------------------------------------
; NO COMPILER GENERATED CODE HERE TO RESTORE
; THE REGISTERS OR RETURN FROM THE ISR.
; ---------------------------------------
;}

زمانی که از صفت naked استفاده شود کامپایلر هیچ کد ورود و خروجی به تابع ایجاد نمی کند ، پس باید به طور صریح به شکل زیر نوشته شوند :

 
void SIG_OUTPUT_COMPARE1A( void ) __attribute__ ( ( signal,
naked ) );
void SIG_OUTPUT_COMPARE1A( void )
{
/* Macro that explicitly saves the execution
context. */
portSAVE_CONTEXT();
/* ISR C code for RTOS tick. */
vPortYieldFromTick();
/* Macro that explicitly restores the
execution context. */
portRESTORE_CONTEXT();
/* The return from interrupt call must also
be explicitly added. */
asm volatile ( "reti" );
}

ISRبا کد های صریح ورود و خروج :

صفت naked به برنامه کاربردی این امکان را میدهد که کنترول کامل روی چطور و چه موقع ذخیره شدن context های AVR ، داشته باشد . اگر کدهای برنامه کاربردی تمام context ها را زمان ورود به ISR ذخیره کند دیگر نیازی نیست که دوباره موقع context switching ذخیره شوند پس هیچ یک از رجیستر ها دو بار ذخیره نمی شوند .

 

ذخیره و باز یابی Context

رجیستر ها به سادگی با push کردن آنها در stack ذخیره می شوند . هر task پشته مخصوص خودش را دارد که در آن context های خودش را زمانی که معوق می شود ذخیره می کند .

ذخیره سازی contextهای AVR از معدود جاهایی است که نوشتن کدهای اسمبلی اجتناب ناپذیر میشود . portSAVE_CONTEXT() به صورت یک ماکرو پیاده سازی می شود که کدهای آن در زیر آمده است:

 

 
#define portSAVE_CONTEXT() \
asm volatile ( \
"push r0 \n\t" \ (1)
"in r0, __SREG__ \n\t" \ (2)
"cli \n\t" \ (3)
"push r0 \n\t" \ (4)
"push r1 \n\t" \ (5)
"clr r1 \n\t" \ (6)
"push r2 \n\t" \ (7)
"push r3 \n\t" \
"push r4 \n\t" \
"push r5 \n\t" \
:
:
:
"push r30 \n\t" \
"push r31 \n\t" \
"lds r26, pxCurrentTCB \n\t" \ (8)
"lds r27, pxCurrentTCB + 1 \n\t" \ (9)
"in r0, __SP_L__ \n\t" \ (10)
"st x+, r0 \n\t" \ (11)
"in r0, __SP_H__ \n\t" \ (12)
"st x+, r0 \n\t" \ (13)
);

نکاتی در مورد کدهای بالا:

  • رجیستر R0 در ابتدا ذخیره شده (1) زیرا زمانی که می خواهیم از آن برای ذخیره کردن status register استفاده کنیم باید مقدار اصلی آن ذخیره شده باشد .
  • Status register در R0 کپی شده(2) پس می توان آنرا در پشته ذخیره کرد (4) .
  • وقفه ها غیر فعال شده اند (3) . اگر portSAVE_CONTEXT() فقط در ISR فراخوانی شود نیازی نیست که به طور صریح غیر فعال شوند چون قبلا این کار با ورود به سرویس وقفه در AVR انجام میشود . اما از آنجایی که ممکن است ماکروی portSAVE_CONTEXT() در بیرون از سرویس روتین وقفه ( زمانی که یک task خودش را معوق میکند ) نیز بکار گرفته شود ،وقفه ها باید تا حد ممکن به سرعت و به طور صریح غیر فعال شوند .
  • کدهای تولید شده توسط کامپایلر رجیستر R1 را صفر کرده است . مقدار اصلی R1 قبل از اینکه پاک شود (6) ذخیره شده است (5) .
  • بین 7 و 8 تمام رجیسترهای میکروکنترلر بر اساس ترتیب شمارشی ذخیره شده اند .
  • پشته ی task در حال تعویق اکنون شامل یک کپی از محتوای اجرایی آن وظیفه است . کرنل اشاره گر پشته را برای باز یابی context زمانی که دوباره وظیفه ادامه پیدا خواهد کرد ذخیره می کند . رجیستر x   با آدرس محل ذخیره سازی اشاره گر پشته ای که باید ذخیره شود بار گیری می شود .( 8 و 9 )
  • اشاره گر پشته ذخیره میشود ابتدا بایت پایین (10 و 11) ،سپس نیبل بالا (12 و 13)
  • Context ذخیره میشود .
  • portRESTORE_CONTEXR() برعکس portSAVE_CONTEXT() عمل می کند . context وظیفه ای که قبلا اجرا می شده در پشته ی وظیفه ذخیره شده . کرنل اشاره گر پشته  برای task جدید را بازیابی می کند  وسپس با pop کردن ،context را دوباره به رجیستر های صحیح برمی گرداند .
 
#define portRESTORE_CONTEXT() \
asm volatile (
"lds r26, pxCurrentTCB \n\t" \ (1)
"lds r27, pxCurrentTCB + 1 \n\t" \ (2)
"ld r28, x+ \n\t" \
"out __SP_L__, r28 \n\t" \ (3)
"ld r29, x+ \n\t" \
"out __SP_H__, r29 \n\t" \ (4)
"pop r31 \n\t" \
"pop r30 \n\t" \
:
:
:
"pop r1 \n\t" \
"pop r0 \n\t" \ (5)
"out __SREG__, r0 \n\t" \ (6)
"pop r0 \n\t" \ (7)
);

نکاتی در مورد کدهای بالا:

  • pxCurrentTCB آدرس جایی که اشاره گر پشته می تواند بازیابی شود را نگه می دارد و در رجیستر x لود می شود(1 و 2).
  • اشاره گر پشته ی وظیفه ای که باید ادامه پیدا کند در stack pointer کپی می شود ، ابتدا بایت پایین (3) سپس نیبل بالا (4).
  • رجیسترهای میکروکنترلر برعکس ترتیب شمارشی pop میشوند تا R1 .
  • status register که بین R1 و R0 در استک ذخیره شده بود ، قبل از R0 بازیابی می شود .

 

ISR کامل FreeRTOS

کدهای استفاده شده برای FreeRTOS کمی با کدهای مثال نشان داده شده در بالا تفاوت دارد . ذخیره کردن context ها در داخل vPortYieldFromTick() که خودش به صورت یک تابع naked پیاده سازی شده انجام می شود .

 

 
void SIG_OUTPUT_COMPARE1A( void ) __attribute__ ( ( signal,
naked ) );
void vPortYieldFromTick( void ) __attribute__ ( ( naked ) );
/*--------------------------------------------------*/
/* Interrupt service routine for the RTOS tick. */
void SIG_OUTPUT_COMPARE1A( void )
{
/* Call the tick function. */
vPortYieldFromTick();
/* Return from the interrupt. If a context
switch has occurred this will return to a
different task. */
asm volatile ( "reti" );
}
/*--------------------------------------------------*/
void vPortYieldFromTick( void )
{
/* This is a naked function so the context
is saved. */
portSAVE_CONTEXT();
/* Increment the tick count and check to see
if the new tick value has caused a delay
period to expire. This function call can
cause a task to become ready to run. */
vTaskIncrementTick();
/* See if a context switch is required.
Switch to the context of a task made ready
to run by vTaskIncrementTick() if it has a
priority higher than the interrupted task. */
vTaskSwitchContext();
/* Restore the context. If a context switch
has occurred this will restore the context of
the task being resumed. */
portRESTORE_CONTEXT();
/* Return from this naked function. */
asm volatile ( "ret" );
}
/*--------------------------------------------------*/

 

جمع بندی و یک مثال گام به گام

در اینجا عملیات context switching با جزئیات روی میکروکنترلرهای AVR نشان داده می شود . این مثال در هفت قسمت عملیات switch شدن از یک task با اولویت پایین تر به نام TaskA ، به task با اولویت بالا تر به نام TaskB نشان داده خواهد شد .

 

Step 1 : قبل از وقفه ی RTOS tick

این مثال با اجرای TaskA شروع می شود . TaskB قبلا معوق شده و context آن در پشته ی TaskB ذخیره شده .

دیاگرام زیر context برای TaskA را نشان می دهد .

برچسب (A)  برای رجیستر ها نشان دهنده مقدار صحیح context  برای Task A است .

 

Step 2 : وقفه RTOS tickاتفاق می افتد

هنگام وقوع وقفه TaskA در حال اجرای دستور LDI است . با وقوع وقفه AVR به طور اتوماتیک program counter (PC) را قبل از پرش به ISR در پشته قرار می دهد .

 

Step 3 : وقفه ی RTOS tickاجرا می شود

کدهای اجرایی ISR در زیر آورده شده . توضیحات برای راحت تر خواندن حذف شده اند ولی میتوانید آنها را در بالا بخوانید .

 
/* Interrupt service routine for the RTOS tick. */
void SIG_OUTPUT_COMPARE1A( void )
{
vPortYieldFromTick();
asm volatile ( "reti" );
}
/*--------------------------------------------------*/
void vPortYieldFromTick( void )
{
portSAVE_CONTEXT();
vTaskIncrementTick();
vTaskSwitchContext();
portRESTORE_CONTEXT();
asm volatile ( "ret" );
}
/*--------------------------------------------------*/

SIG_OUTPUT_COMPARE1A() یک تابع naked است ،پس اولین دستور فراخوانی vPortYieldFromTick() است . vPortYieldFromTick() نیز یک تابع naked است پس context اجرایی AVR به طور صریح با فراخوانی portSAVE_CONTEXT() ذخیره می شود .

portSAVE_CONTEXT() تمام context اجرایی را در پشته ی TaskA قرار می دهد ، نتیجه در شکل زیر مشخص است . حالا stack pointer به بالای پشته ی TaskA اشاره می کند . portSAVE_CONTEXT() با کپی کردن stack pointer کامل می شود . کرنل از اشاره گر پشته ی  TaskB قبل از معوق شدنش یک کپی گرفته .

 

Step 4 : افزایش شمارنده ی tick

vTaskIncrementTick() بعد از ذخیره کردن context های TaskA اجرا می شود . در این مثال افزایش شمارنده ی tick باعث می شود TaskB آماده ی اجرا شود . چون TaskB دارای اولویت بالا تری نسبت به TaskA است vTaskSwitchContext() تصمیم می گیرد که TaskB باید پس از اتمام ISR ادامه پیدا کند .

 

step 5 : اشاره گر پشته ی TaskBبازیابی می شود

Context های TaskB باید بازیابی شوند . اولین کاری که portRESTORE_CONTEXT() انجام می دهد بازیابی اشاره گر پشته ی TaskB است که قبل از معوق شدن از آن یک کپی گرفته شده . اشاره گر پشته ی TaskB در stack pointer بارگیری میشود و در نهایت stack pointer به بالای context های TaskB اشاره می کند .

 

Step 6 : بازیابی context های TaskB

portRESTORE_CONTEXT() با بازیابی context های TaskB از پشته و قرار دادن آنها در رجیسترهای مناسب کامل می شود .

فقط program counter در پشته باقی می ماند .

 

Step 7 : خروج از RTOS tick

vPortYieldFromTick() پس از اجرا به SIG_OUTPUT_COMPARE1A() باز می گردد و در آخر دستور بازگشت از وقفه RETI اجرا می شود . دستور RETI به این معناست که آخرین مقداری که در پشته موجود است ، آدرس بازگشت به جایی است که وقفه در آنجا اتفاق افتاده .

وقتی وقفه ی RTOS tick شروع می شود AVR به طور اتوماتیک آدرس بازگشت به TaskA را در پشته ذخیره می کند – آدرس دستور اجرایی بعدی در TaskA . اما ISR اشاره گر پشته را تغییر داده و اکنون به پشته ی TaskB اشاره می کند . بنابراین آدرس بازگشتی که با دستور RETI از پشته pop میشود آدرس بازگشت به دستوری از TaskB است که قبل از معوق شدن در حال اجرا بود .

وقفه ی RTOS tick در واقع TaskA را به وقفه انداخت ولی به TaskB بازگشت و context switching کامل شد !

 

منبع :

MultiTasking on an AVR نوشته ی استــــــــــــاد  Richard Barry

دانلود مقاله ی اینگلیسی از اینجا Multitasking on an AVR( حجم در حدود 500KB )

 

ی
و ر
حیـــــــــــد
حیــــــــــــــــد

پایان !

Advertisements

دربارهٔ DeltaCode

Somewhere near the sky Far away from people Far away from noise Somewhere near yourself

Posted on سپتامبر 3, 2011, in AVR. Bookmark the permalink. ۱ دیدگاه.

  1. دوست گرامی
    ضمن تحسین اقدام شما، و برنامه ی کاربردی که تهیه کردید، تنها تبلیغی که ما برای شما می تونیم انجام بدیم، تایید دیدگاه تون روی این مطلب هست.

پاسخی بگذارید

در پایین مشخصات خود را پر کنید یا برای ورود روی شمایل‌ها کلیک نمایید:

نشان‌وارهٔ وردپرس.کام

شما در حال بیان دیدگاه با حساب کاربری WordPress.com خود هستید. بیرون رفتن / تغییر دادن )

تصویر توییتر

شما در حال بیان دیدگاه با حساب کاربری Twitter خود هستید. بیرون رفتن / تغییر دادن )

عکس فیسبوک

شما در حال بیان دیدگاه با حساب کاربری Facebook خود هستید. بیرون رفتن / تغییر دادن )

عکس گوگل+

شما در حال بیان دیدگاه با حساب کاربری Google+ خود هستید. بیرون رفتن / تغییر دادن )

درحال اتصال به %s

%d وب‌نوشت‌نویس این را دوست دارند: