IMX6ULL裸机启动,IMX6ULL运行FreeRTOS,源代码分析
裸机启动部分
imx6ull中根据运行代码位置的不同RAM/flash/SD卡,上电会先运行片上ROM中的代码,搬运的数据大小是不同的,代码也不是存储在0地址处的。如下图,比如从sd中运行代码,代码IVToffset大小1k,initloadSize为4k。
1mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80200000 -d led.bin led.imx
2// -e 0x80200000程序会被搬运到的位置
其他的开发板如,原子的imxdownload工具中直接固定了加载地址为0X87800000,链接脚本中也是这个地址,如不地址不相同,就会牵扯到代码重定位的问题。
但也有可能有人设置的就是不一样的地址,照样能跑下去,这就要看start.s中的跳转指令是
1 ldr sp, =main /* 跳转到main函数 */
2 b main /* 跳转到main函数 */
链接地址和加载地址不一样的情况: 如果是第一行的话就无法运行,地址相关代码 如果第二行的话就代码可以部分运行,比如打印定义好的全局变量就不正常了,data区没有搬运到相应的链接地址去。
IMX6ULL运行FreeRTOS
imx6ull有官网的skd,里面已经有一些例子。 第一步: SDK_2.2_MCIM6ULL/boards/evkmcimx6ull/demo_apps/hello_world/armgcc中直接运行 ./build_ddr_debug.sh
1SDK_2.2_MCIM6ULL/boards/evkmcimx6ull/demo_apps/hello_world/armgcc# ./build_ddr_debug.sh
第二步:
1mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80002000 -d led.bin led.imx
注意:-e 地址 为 0x80002000
第三步: 利用100ask_imx6ull_flashing_tool下载到imx6ull上面运行就会看到串口打印的
1hello world.
买到板子之后不久就一直想在imx6ULL上面跑rtos,结果也没找到啥教程,有人说cortexa7的可以参看a9的移植,无奈还是懵,后来想看看FreeRTOS源码之后在动手,结果发现简单看了源码还是菜到没头绪。 中间玩玩裸机程序,突然有两天就想研究一下,imx上程序从编译好到下载到板子上是怎么运行的,为啥我编译的sdk代码不可以用,后面就发现rtos的demo代码可能启动位置不对,根据连接脚本中的信息链接地址为0x80002000 SDK_2.2_MCIM6ULL/boards/evkmcimx6ull/rtos_examples 目录中有些例子,只要编译后,加上imx头部信息,就可以愉快的在imx6ull上面跑FreeRTOS了, 后续看看我的板子怎么能在FreeRTOS基础上跑lwip
代码重定位
关于代码重定位。
API总览
@startmindmap
+ FreeRTOS
++ Task Creation
+++_ xTaskCreate
+++_ xTaskCreateStatic
+++_ vTaskDelete
+++_ xTaskGetStaticBuffers
++ Task Control
+++_ vTaskDelay
+++_ vTaskDelayUntil
+++_ xTaskDelayUntil
+++_ uxTaskPriorityGet
+++_ uxTaskPriorityGetFromISR
+++_ uxTaskBasePriorityGet
+++_ uxTaskBasePriorityGetFromISR
+++_ vTaskPrioritySet
+++_ vTaskSuspend
+++_ vTaskResume
+++_ xTaskResumFromISR
+++_ xTaskAbortDelay
++ Task Utilities
+++_ uxTaskGetSystemState
+++_ vTaskGetInfo
+++_ xTaskGetApplicationTaskTag
+++_ xTaskGetCurrentTaskHandle
+++_ xTaskGetHandle
+++_ xTaskGetIdleTaskHandle
+++_ uxTaskGetStackHighWaterMark
+++_ eTaskGetState
+++_ pcTaskGetName
+++_ xTaskGetTickCount
+++_ xTaskGetTickCountFromISR
+++_ xTaskGetSchedulerState
+++_ uxTaskGetNumberOfTasks
+++_ vTaskList
+++_ vTaskStartTrace
+++_ ulTaskEndTrace
+++_ vTaskGetRunTimeStats
+++_ vTaskSetApplicationTaskTag
+++_ xTaskCallApplicationTaskHook
+++_ vTaskSetThreadLocalStoragePointer
+++_ pvTaskGetThreadLocalStoragePointer
+++_ vTaskSetTimeoutState
+++_ xTaskCheckForTimeOut
++ RTOS Kernel Control
+++_ taskYIELD Colors
+++_ taskENTER_CRITICAL
+++_ taskEXIT_CRITICAL
+++_ taskENTER_CRITICAL_FROM_ISR
+++_ taskEXIT_CRITICAL_FROM_ISR
+++_ taskDISABLE_INTERRUPTS
+++_ taskENABBLE_INTERRUPTS
+++_ vTaskStartScheduler
+++_ vTaskEndScheduler
+++_ vTaskSuspendAll
+++_ vTaskResumeAll
+++_ vTaskStepTick
+++_ xTaskCatchUpTicks
++ Direct To Task Notifications
+++_ xTaskNotifyGive/xTaskNotifyGiveIndexed
+++_ vTaskNotifyGiveFromISR
+++_ ulTaskNotifyTake
+++_ xTaskNotify
+++_ xTaskNotifyAndQuery
+++_ xTaskNotifyAndQueryFromISR
+++_ xTaskNotifyFromISR
+++_ xTaskNotifyWait
+++_ xTaskNotifyStateClear
+++_ ulTaskNotifyValueClear
++ Queues
+++_ xQueueCreate
+++_ xQueueCreateStatic
+++_ xQueueDelete
+++_ xQueueSend
+++_ xQueueSendFromISR
+++_ xQueueSendToBack
+++_ xQueueSendToBackFromISR
+++_ xQueueSendToFront
+++_ xQueueSendToFrontFromISR
+++_ xQueueReceive
+++_ xQueueReceiveFromISR
+++_ uxQueueMessagesWaiting
+++_ uxQueueMessagesWaitingFromISR
+++_ uxQueueSpacesAvailable
+++_ xQueueReset
+++_ xQueueOverwrite
+++_ xQueueOverwriteFromISR
+++_ xQueuePeek
+++_ xQueuePeekFromISR
+++_ vQueueAddToRegistry
+++_ vQueueUnregisterQueue
+++_ pcQueueGetName
+++_ xQueueIsQueueFullFromISR
+++_ xQueueIsQueueEmptyFormISR
+++_ xQueueGetStaticBuffers
+++_ xQueue
+++_ xQueue
+++_ xQueue
+++_ xQueued
+++_ xQueuedd
++ Queue Sets
+++_ xQueueCreateSet
+++_ xQueueAddToSet
+++_ xQueueRemoveFromSet
+++_ xQueueSelectFromSet
+++_ xQueueSelectFromSetFromISR
-- Stream Buffers
---_ xStreamBufferCreate/xStreamBufferCreateWithCallback
---_ xStreamBufferCreateStatic/xStreamBufferCreateStaticWithCallback
---_ xStreamBufferSend
---_ xStreamBufferSendFromISR
---_ xStreamBufferReceive
---_ xStreamBufferReceiveFromISR
---_ xStreamBufferDelete
---_ xStreamBufferBytesAvailable
---_ xStreamBufferSpacesAvailable
---_ xStreamBufferSetTriggerLevel
---_ xStreamBufferReset
---_ xStreamBufferResetFromISR
---_ xStreamBufferIsEmpty
---_ xStreamBufferIsFull
---_ xStreamBufferGetStaticBuffers
---_ uxStreamBufferGetStreamBufferNotificationIndex
---_ xStreamBufferSetStreamBufferNotificationIndex
---_ xStreamBatchingBufferCreate
---_ xStreamBatchingBufferCreateStatic
-- Message Buffers
---_ xMessageBufferCreate
---_ xMessageBufferCreateStatic
---_ xMessageBufferSend
---_ xMessageBufferSendFromISR
---_ xMessageBufferReceive
---_ xMessageBufferReceiveFromISR
---_ xMessageBufferReset
---_ xMessageBufferResetFromISR
---_ xMessageBufferIsEmpty
---_ xMessageBufferIsFull
---_ xMessageBufferGetStaticBuffers
-- Semaphore / Mutexes
---_ xSemaphoreCreateBinary
---_ xSemaphoreCreateBinaryStatic
---_ vSemaphoreCreateBinary
---_ xSemaphoreCreateCounting
---_ xSemaphoreCreateCountingStatic
---_ xSemaphoreCreateMutex
---_ xSemaphoreCreateMutexStatic
---_ xSemaphoreCreateRecursiveMutex
---_ xSemaphoreCreateRecursiveMutexStatic
---_ vSemaphoreDelete
---_ xSemaphoreGetMutexHolder
---_ xSemaphoreGetCount
---_ xSemaphoreTake
---_ xSemaphoreTakeFromISR
---_ xSemaphoreTakeRecursive
---_ xSemaphoreGive
---_ xSemaphoreGiveRecursive
---_ xSemaphoreGiveFromISR
---_ xSemaphoreGetStaticBuffer
-- Software Timers
---_ xTimerCreate
---_ xTimerCreateStatic
---_ xTimerIsTimerActive
---_ xTimerStart
---_ xTimerStop
---_ xTimerChangePeriod
---_ xTimerDelete
---_ xTimerReset
---_ xTimerStartFromISR
---_ xTimerStopFromISR
---_ xTimerChangePeriodFromISR
---_ xTimerResetFromISR
---_ pvTimerGetTimerID
---_ vTimerSetReloadMode
---_ vTimerSetTimerID
---_ xTimerGetTimerDaemonTaskHandle
---_ xTimerPendFunctionCall
---_ xTimerPendFunctionCallFromISR
---_ xTimerGetName
---_ xTimerGetPeriod
---_ xTimerGetExpiryTime
---_ xTimerGetReloadMode
-- Event Groups
---_ xEventGroupCreate
---_ xEventGroupCreatxeStatic
---_ vEventGroupDelte
---_ xEventGroupWaitBits
---_ xEventGroupSetBits
---_ xEventGroupSetBitsFromISR
---_ xEventGroupClearBits
---_ xEventGroupClearBitsFromISR
---_ xEventGroupGetBits
---_ xEventGroupSync
---_ xEventGroupGetStaticBuffer
-- MPU
---_ xTaskCreateRestricted
---_ xTaskCreateRestrictedStatic
---_ vTaskAllocateMPURegions
---_ portSWITCH_TO_USER_MODE
-- Co-routines
---_ xCoRoutineCreate
---_ crDELAY
---_ crQUEUE_SEND
---_ crQUEUE_RECEIVE
---_ crQUEUE_SEND_FROM_ISR
---_ crQUEUE_RECEIVE_FROM_ISR
---_ vCoRoutineSchedule
---_ vCoRoutineResetState
@endmindmap
FreeRTOS源代码分析
任务切换
@startuml
start
fork
:Irq Handler;
split
:other iar func;
split again
:timer tick func;
note right
FreeRTOS_Tick_Handler
xTaskIncrementTick(超时任务,从delay,event list删除,加入ready list)
如果 xTickCount 溢出,累加 xNumOfOverflows
delay到期的任务高于当前优先级,时间片轮转,xYieldPending=true(其他任务抢占需要调度)
ulPortYieldRequired = true
end note
end split
if (ulPortYieldRequired == true) then (false)
:irq return;
else (yes)
:save_context_and_switch;
note right
bl vTaskSwitchContextConst (vTaskSwitchContext)
uxSchedulerSuspended != flase 挂起 os 不允许调度
xYieldPending = 0 一个标志位吧,其他需要调度的时候置1
设置新的 pxCurrentTCB,(软件,clz方式找到最高优先级)
就可以恢复当前task上下文,完成任务切换
end note
endif
fork again
:FreeRTOS_SWI_Handler;
:save_context
bl vTaskSwitchContextConst
restroe_context;
end merge
stop
@enduml
vTaskDelay
@startuml
start
:vTaskDelay;
:vTaskSuspendAll;
note right
++uxSchedulerSuspended
end note
if (xTicksToDelay > 0) then (yes)
:prvAddCurrentTaskToDelayedList;
note right
设置delay到期时间,更新 xNextTaskUnblockTime
end note
:xTaskResumeAll;
note right
--uxSchedulerSuspended, 为0往下继续处理
xPendingReadyList 处理pending list中task,加入ready list
当有任务大于当前优先级,需要任务切换
设置 xYieldPending=true,更新 xNextTaskUnblockTime
补偿系统tick,uxPendedTicks
xAlreadyYielded = xTaskIncrementTick()
如果已经有其他task请求切换了,当前就无需再请求切换了
xYieldPending 会在 vTaskSwitchContext 设置 false
end note
else
:xAlreadyYielded = false;
endif
if (xAlreadyYielded = false) then (yes)
:portYIELD_WITHIN_API;
note right
delay参数为0,强制执行调度
portYIELD
__asm volatile ( "SWI 0" )
end note
else (no)
endif
stop
@enduml
xQueueGenericReceive
@startuml
start
:xQueueGenericReceive;
repeat
:taskENTER_CRITICAL();
if (uxMessagesWaiting >0) then(yes)
note right
队列里的元素数量
end note
:记录读数据的位置,receive数据会出队,peek只是读数据;
:prvCopyDataFromQueue();
note right
读的索引后移一个uxItemSize,如果到tail,再指向head
copy一个uxItemSize的数据到pvBuffer中
end note
if (xJustPeeking == pdFALSE) then(yes)
:当前为receive模式,读了数据之后就更新读指针,相当于出队操作;
:当前queue有互斥锁,指向当前的运行task pxCurrentTCB->uxMutexesHeld++;
:判断当前queue的 xTasksWaitingToSend 不为空的话;
:xTaskRemoveFromEventList();
note left
移除发送队列,os未挂起就加入readylist,挂起加加入pendingreadlist
优先级大的话,设置task调度标志
end note
else
:当前模式可以一直读队首的数据,不会出队;
:恢复上面记录的数据读位置;
:判断当前queue的 xTasksWaitingToReceive 不为空的话;
:xTaskRemoveFromEventList(), task是否加入readlist的处理,是否切换task等;
endif
:taskEXIT_CRITICAL();
:return pdPASS;
stop
else (队列为空)
if (xTicksToWait==0) then (等待时间为0)
:taskEXIT_CRITICAL();
:return errQUEUE_EMPTY;
stop
elseif (xEntryTimeSet == pdFALSE) then(等待时间不为0)
:vTaskSetTimeOutState();
note right
xEntryTimeSet = true
记录 xNumOfOverflows xTickCount 为后面判断delay做准备
end note
endif
endif
:taskEXIT_CRITICAL();
:;
:vTaskSuspendAll();
:prvLockQueue(), rx tx加互斥锁;
if ( xTaskCheckForTimeOut()== pdFALSE) then (yes)
note right
根据xTimeOut pxTicksToWait判断是否延时时间到
延时时间到返回true,否则返回false,并更新vTaskSetTimeOutState( pxTimeOut );
end note
:当前wait时间没有超时;
if (队列是否为空) then (yes)
:互斥锁,进入临界区,修改task优先级,优先级继承机制;
note left
vTaskPriorityInherit()
持有锁的taskA优先级低于当前task优先级,调整taskA的优先级
锁的持有者可能为null,意味中当前在isr上下文
end note
:vTaskPlaceOnEventList;
note right
insert event list
加入delay list中
end note
:prvUnlockQueue();
if (xTaskResumeAll() == false) then (yes)
:调用 svc 切换task;
endif
else
:prvUnlockQueue();
:xTaskResumeAll();
endif
else
:当前时间已经超时,等待结束了,有数据就返回数据,没有就返回空;
:prvUnlockQueue();
:xTaskResumeAll();
if (队列 uxMessagesWaiting 是否为空) then (yes)
:return errQUEUE_EMPTY;
stop
else
:非空的话,继续下一个循环;
endif
endif
repeat while ()
@enduml
xQueueGenericSend
@startuml
start
:xQueueGenericSend;
repeat
:taskENTER_CRITICAL();
if (队列有空闲 || 覆盖写) then(yes)
note right
队列里的元素数量
end note
:记录读数据的位置,receive数据会出队,peek只是读数据;
:xYieldRequired = prvCopyDataToQueue();
note left
1. uxItemSize = 0可能表示互斥锁,需要调整恢复task优先级 xTaskPriorityDisinherit
2. queueSEND_TO_BACK,pcWriteTo+uxItemSize
3. queueSEND_TO_FRONT 先写后调整位置pcReadFrom-uxItemSize pcReadFrom指向的是空
4. queueOVERWRITE,有点坑,只能用在队列长度为1的场景中(无法执行,否则队列中就会存在无效的值)
end note
if (configUSE_QUEUE_SETS) then (yes)
:prvNotifyQueueSetContainer;
note left
1. prvCopyDataToQueue,这里把当前queue放入set中
队列集上有等待接收的task,xTaskRemoveFromEventList
唤醒的task高于当前task优先级,则抢占运行
2. set为null场景,判读是否有等待接收task,逻辑同上
end note
else
:没有配置队列集;
if (xTasksWaitingToReceive 非空) then (yes)
:xTaskRemoveFromEventList;
:如果高优先级task,则抢占运行;
else if (xYieldRequired = true) then
note left
只有互斥锁的时候才有可能到达这里,因为优先级继承
发送数据的时候会恢复优先级
end note
:抢占;
endif
endif
:taskEXIT_CRITICAL();
:return pdPASS;
stop
else (队列已满)
if (xTicksToWait==0) then (等待时间为0)
:taskEXIT_CRITICAL();
:return errQUEUE_EMPTY;
stop
elseif (xEntryTimeSet == pdFALSE) then(等待时间不为0)
:vTaskSetTimeOutState();
note right
xEntryTimeSet = true
记录时间戳 xNumOfOverflows xTickCount 为后面判断delay做准备
end note
endif
endif
:taskEXIT_CRITICAL();
:;
:vTaskSuspendAll();
:prvLockQueue(), rx tx加互斥锁;
if ( xTaskCheckForTimeOut()== pdFALSE) then (yes)
note right
根据xTimeOut pxTicksToWait判断是否延时时间到
延时时间到返回true,否则返回false,并更新vTaskSetTimeOutState( pxTimeOut );
end note
:当前wait时间没有超时;
if (队列是否已满) then (yes)
:互斥锁,进入临界区,修改task优先级,优先级继承机制;
:vTaskPlaceOnEventList;
note left
加入待发送队列 xTasksWaitingToSend,xEventListItem
从当前优先级队列移除,加入 delaylist, xStateListItem
end note
:prvUnlockQueue();
if (xTaskResumeAll() == false) then (yes)
:调用 svc 切换task;
endif
else
:prvUnlockQueue();
:xTaskResumeAll();
endif
else
:当前时间已经超时,等待结束了;
:prvUnlockQueue();
:xTaskResumeAll();
:return errQUEUE_FULL;
note right #yellow
为啥这里直接返回,不判断队列是否已满
有空闲的话,进行下一次循环呢??
end note
stop
endif
repeat while ()
@enduml
评论