FreeRTOS Code Reading (Arduino編, #1)
興味があるのでFreeRTOSのソースコードを読んでみる。 あとで修正するかもしれませんが、しばらくは体裁が整っていない自分用のメモです。
なぜ読むか?
RTOSの実装に興味があり、Arduino版ならコード量の少なく読みやすそうだったので。
ソースコードはここ。github.com MPU(atmega328p)のdatasheetはここ。https://avr.jp/user/DS/PDF/mega328P.pdf
もしくはここ。http://akizukidenshi.com/download/ds/microchip/atmega328.pdf
読み方、ソースコード引用の方針
どこから読み始めるか
161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf に variantHook.cpp から読めと書いてあるのでそこから始める。 (↑は昔書いたメモだけど、見当たらないorz)
読む
- Arduino_FreeRTOS_Library\src\variantHooks.cpp
extern void setup(void); extern void loop(void); /*-----------------------------------------------------------*/ void initVariant(void) __attribute__ ((OS_main)); void initVariant(void) { #if defined(USBCON) USBDevice.attach(); #endif setup(); // the normal Arduino setup() function is run here. vTaskStartScheduler(); // initialise and run the freeRTOS scheduler. Execution should never return here. }
setup(), loop()はexternされていて、実体は Arduinoのsketchが参照される。
- task.c
void vTaskStartScheduler( void ) { BaseType_t xReturn; /* Add the idle task at the lowest priority. */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) { ... } #else { /* The Idle task is being created using dynamically allocated RAM. */ xReturn = xTaskCreate( prvIdleTask, configIDLE_TASK_NAME, configMINIMAL_STACK_SIZE, ( void * ) NULL, portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ } #endif /* configSUPPORT_STATIC_ALLOCATION */
configSUPPORT_STATIC_ALLOCATION は, config次第だが多分0(要確認)。
- Arduino_FreeRTOS.h
#ifndef configSUPPORT_STATIC_ALLOCATION /* Defaults to 0 for backward compatibility. */ #define configSUPPORT_STATIC_ALLOCATION 0 #endi
スケジューラをタスクの1つとして登録する。
/* The Idle task is being created using dynamically allocated RAM. */ xReturn = xTaskCreate( prvIdleTask, configIDLE_TASK_NAME, configMINIMAL_STACK_SIZE, ( void * ) NULL, portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
if( xReturn == pdPASS ) { ... /* Interrupts are turned off here, to ensure a tick does not occur before or during the call to xPortStartScheduler(). The stacks of the created tasks contain a status word with interrupts switched on so interrupts will automatically get re-enabled when the first task starts to run. */ portDISABLE_INTERRUPTS(); #if ( configUSE_NEWLIB_REENTRANT == 1 ) { /* Switch Newlib's _impure_ptr variable to point to the _reent structure specific to the task that will run first. */ _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); } #endif /* configUSE_NEWLIB_REENTRANT */ xNextTaskUnblockTime = portMAX_DELAY; xSchedulerRunning = pdTRUE; xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; /* If configGENERATE_RUN_TIME_STATS is defined then the following macro must be defined to configure the timer/counter used to generate the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS is set to 0 and the following line fails to build then ensure you do not have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your FreeRTOSConfig.h file. */ portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); traceTASK_SWITCHED_IN(); /* Setting up the timer tick is hardware specific and thus in the portable interface. */ if( xPortStartScheduler() != pdFALSE ) { /* Should not reach here as if the scheduler is running the function will not return. */ } else { /* Should only reach here if a task calls xTaskEndScheduler(). */ } } else { ... }
- スケジューラのタスクの生成に成功したか(xReturn == pdPASS)を確認する
- xPortStartScheduler() を呼ぶまで割り込み禁止にする
xNextTaskUnblockTime = portMAX_DELAY; xSchedulerRunning = pdTRUE; xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;
xNextTaskUnblockTime
は、...xSchedulerRunning
は、スケジューラが動作中はpdTRUE
、void vTaskEndScheduler( void )
が呼ばれるとpdFALSE
に設定されるxTickCount
は、次のスケジュールのタスクの割り込みまでの時間。
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); traceTASK_SWITCHED_IN();
この2つのマクロはdefaultはカラ。
if( xPortStartScheduler() != pdFALSE )
- port.c
BaseType_t xPortStartScheduler( void ) { /* Setup the relevant timer hardware to generate the tick. */ prvSetupTimerInterrupt(); /* Restore the context of the first task that is going to run. */ portRESTORE_CONTEXT(); /* Simulate a function call end as generated by the compiler. We will now jump to the start of the task the context of which we have just restored. */ __asm__ __volatile__ ( "ret" ); /* Should not get here. */ return pdTRUE; }
//initialize watchdog void prvSetupTimerInterrupt( void ) { //reset watchdog wdt_reset(); //set up WDT Interrupt (rather than the WDT Reset). wdt_interrupt_enable( portUSE_WDTO ); }
prvSetupTimerInterrupt()
は、WDT(Watch Dog Timer)をリセットし、WDT割り込みを有効にしている。 ** `wdt_rest(), wdt_interrupt_enable()' は要確認portRESTORE_CONTEXT()
は、CPUレジスタを戻して(pop)、xPortStartScheduler()
が呼ばれる前の状態(context)に戻している。ret
を発行して関数から戻る。
ここまででスケジューラタスクの登録し、実行した状態になった。
次回は、少し戻ってスケジューラタスクの生成処理を読み解いてみる。
/* The Idle task is being created using dynamically allocated RAM. */ xReturn = xTaskCreate( prvIdleTask, configIDLE_TASK_NAME, configMINIMAL_STACK_SIZE, ( void * ) NULL, portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */