freertos(第十課,基於standalone移植freertos)

我們來看看SDK中已經移植好的freertos,具體做了哪些事情。

在BSP文件夾下,除了常規的standaloneOS所提供的模塊,還多了一個freertos901_xilinx_v1_1文件夾。
打開system.mss,可以看到freertos901的相關配置。
這些配置會對應生成freerotscnfig.h。一般情況下,我們不需要手工修改freertosconfig.h,而只需要在system.mss裏配置各種選項即可。

這裏注意幾個重要的選項:

  1. stdin,stdout,設置爲ps7_uart_1.
  2. hook_function->use_malloc_failed_hook,設置爲true。
    3)kernel behavior,
    idle_yield,設置爲true,
    max_api_call_interrupt_priority設置爲18,
    max_priority設置爲8,
    tick_rate,設置爲100,也就是10ms,
    use_preemption,設置爲true,
    use_timeslicing,設置爲true,
    4)kernel feature,
    全部選true。
    5)soft timer
    timer_command_queue_length,設置爲10,
    timer_task_priority,設置爲(configMAX_PRIORITIES - 1)
    timer_task_stack_depth,設置爲(configMINIMAL_STACK_SIZE)
    6)tick_setup,設置爲true。
    psu_ttc0_select,設置爲true。

configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY,這兩個宏是等價的,後者是前者的新名字,在中斷服務例程中僅可以調用以“FromISR”結尾的API函數。
configMAX_SYSCALL_INTERRUPT_PRIORITY用來設置可以在中斷服務程序中安全調用FreeRTOS API函數的最高中斷優先級。優先級小於等於這個宏所代表的優先級時,程序可以在中斷服務程序中安全的調用FreeRTOS API函數;如果優先級大於這個宏所代表的優先級,表示FreeRTOS無法禁止這個中斷,在這個中斷服務程序中絕不可以調用任何API函數。不調用API的中斷可以運行在比configMAX_SYSCALL_INTERRUPT_PRIORITY高的優先級,這些級別的中斷不會被FreeRTOS禁止,因此不會因爲執行RTOS內核而被延時。

SDK移植的文件是portZynq7000.c
這個文件中,只實現了兩個函數,TICK和IRQ。
先來看看IRQ。

.section .vectors
_vector_table:
_freertos_vector_table:
	B	  _boot
	B	  FreeRTOS_Undefined
	ldr   pc, _swi
	B	  FreeRTOS_PrefetchAbortHandler
	B	  FreeRTOS_DataAbortHandler
	NOP	  /* Placeholder for address exception vector*/
	LDR   PC, _irq
	B	  FreeRTOS_FIQHandler

_irq:   .word FreeRTOS_IRQ_Handler
_swi:   .word FreeRTOS_SWI_Handler

.align 4
.type FreeRTOS_IRQ_Handler, %function
FreeRTOS_IRQ_Handler:
	LDR		r1, FreeRTOS_ApplicationIRQHandlerConst

FreeRTOS_ApplicationIRQHandlerConst: .word FreeRTOS_ApplicationIRQHandler

void FreeRTOS_ApplicationIRQHandler( uint32_t ulICCIAR )
{
	extern const XScuGic_Config XScuGic_ConfigTable[];
	static const XScuGic_VectorTableEntry *pxVectorTable = XScuGic_ConfigTable[ XPAR_SCUGIC_SINGLE_DEVICE_ID ].HandlerTable;
	uint32_t ulInterruptID;
	const XScuGic_VectorTableEntry *pxVectorEntry;
	
		/* The ID of the interrupt is obtained by bitwise anding the ICCIAR value
		with 0x3FF. */
		ulInterruptID = ulICCIAR & 0x3FFUL;
		if( ulInterruptID < XSCUGIC_MAX_NUM_INTR_INPUTS )
		{
			/* Call the function installed in the array of installed handler functions. */
			pxVectorEntry = &( pxVectorTable[ ulInterruptID ] );
			pxVectorEntry->Handler( pxVectorEntry->CallBackRef );
		}
}

從中可以看出,IRQ的處理過程,和standaloneOS是一致的,最終都是調用的註冊到GIC中的Callback來處理的。

TICK也是使用了GIC的TIMER中斷,來看看具體實現。
當程序啓動調度器時,xPortStartScheduler會調用setuptickinterrupt函數。

void FreeRTOS_SetupTickInterrupt( void )
{
	BaseType_t xStatus;
	extern void FreeRTOS_Tick_Handler( void );
	XScuTimer_Config *pxTimerConfig;
	XScuGic_Config *pxGICConfig;
	const uint8_t ucRisingEdge = 3;
	
		/* Ensure XScuGic_CfgInitialize() has been called.  In this demo it has
		already been called from prvSetupHardware() in main(). */
		pxGICConfig = XScuGic_LookupConfig( XPAR_SCUGIC_SINGLE_DEVICE_ID );
		xStatus = XScuGic_CfgInitialize( &xInterruptController, pxGICConfig, pxGICConfig->CpuBaseAddress );
		configASSERT( xStatus == XST_SUCCESS );
		( void ) xStatus; /* Remove compiler warning if configASSERT() is not defined. */
	
		/* The priority must be the lowest possible. */
		XScuGic_SetPriorityTriggerType( &xInterruptController, XPAR_SCUTIMER_INTR, portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT, ucRisingEdge );
	
		/* Install the FreeRTOS tick handler. */
		xStatus = XScuGic_Connect( &xInterruptController, XPAR_SCUTIMER_INTR, (Xil_ExceptionHandler) FreeRTOS_Tick_Handler, ( void * ) &xTimer );
		configASSERT( xStatus == XST_SUCCESS );
		( void ) xStatus; /* Remove compiler warning if configASSERT() is not defined. */
	
		/* Initialise the timer. */
		pxTimerConfig = XScuTimer_LookupConfig( XPAR_SCUTIMER_DEVICE_ID );
		xStatus = XScuTimer_CfgInitialize( &xTimer, pxTimerConfig, pxTimerConfig->BaseAddr );
		configASSERT( xStatus == XST_SUCCESS );
		( void ) xStatus; /* Remove compiler warning if configASSERT() is not defined. */
	
		/* Enable Auto reload mode. */
		XScuTimer_EnableAutoReload( &xTimer );
	
		/* Ensure there is no prescale. */
		XScuTimer_SetPrescaler( &xTimer, 0 );
	
		/* Load the timer counter register. */
		XScuTimer_LoadTimer( &xTimer, XSCUTIMER_CLOCK_HZ / configTICK_RATE_HZ );
	
		/* Start the timer counter and then wait for it to timeout a number of
		times. */
		XScuTimer_Start( &xTimer );
	
		/* Enable the interrupt for the xTimer in the interrupt controller. */
		XScuGic_Enable( &xInterruptController, XPAR_SCUTIMER_INTR );
	
		/* Enable the interrupt in the xTimer itself. */
		FreeRTOS_ClearTickInterrupt();
		XScuTimer_EnableInterrupt( &xTimer );
}

從中可以看出,利用standalone提供的GIC和TIMER的API,我們配置了GIC,並配置了TIMER。這樣,SYSTICKTIMER就能夠開始工作,並週期性的發出IRQ。
我們掛接的IRQHandler,是FreeRTOS_Tick_Handler。這是port.c中的函數。
它是ARM體系的通用函數,與zynq無關了。

如果我們需要掛接中斷服務函數Callback,仍然需要standaloneOS中提供的API,將IRQ的Callback連接到GIC的HandlerTable中。
如果希望能夠在ISR中使用API,那麼GIC連接時,setpriorityandtype,就要設置成RTOS能夠屏蔽的優先級。
例如,這裏設置的是18,而SYSTICK使用的是30,30是最低的可用優先級,所以,連接到GIC的IRQ,如果希望使用FromISR的API,就必須設置爲大於30小於18的優先級。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章