FreeRTOS笔记
任务的创建和删除(静态方法)
任务创建后要开启调度器。
FreeRTOSConfig.h
1. 改宏 使能静态创建函数。
会出现,有两个函数未定义。
Cortex-M中断管理(上)
NVIC:嵌套向量中断控制器。
与中断有关的寄存器都在NVIC和SCB中
Cortex-M中断管理(下)
中断优先级设置
IP[240U]
中断向量表中定义具体对应关系。
IP[0]
IP[1]
IP[2]
port.c 中设置PendSV和SysTick优先级
中断屏蔽寄存器有三个:
PRIMASK
FAULTMASK
BASEPRI
11.FreeRTOS中断检测试验
FreeRTOS中优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断会被屏蔽掉,高于的就不会。
使用两个定时器,一个优先级为4,一个优先级为5,两个定时器每间隔1s通过串口输出一串字符串。然后在某个任务中关闭中断一段时间,查看两个定时器的输出情况。
设计两个任务start_task()和interrupt_task(),这两个任务的任务功能如下:
start_task():创建另一个任务。
interrupt_task():中断测试任务,任务中会调用FreeRTOS中的关中断函数portDISABLE_INTERRUPTS()来将中断关闭一段时间。
FreeRTOS开关中断函数
portENABLE_INTERRUPTS() 开中断函数
portDISABLE_INTERRUPTS() 关中断函数
1. 中断优先级分组,组4:全部是抢占优先级。
2. 中断驱动移植,使用定时器中断
12. FreeRTOS列表与列表项简介
列表和列表项是FreeRTOS的一个数据结构,FreeRTOS大量使用到了列表和列表项。
它是FreeRTOS的基石。
列表:是FreeRTOS中的一个数据结构,概念上和链表有点类似,列表被用来跟踪FreeRTOS中的任务。列表结构为List_t,在文件list.h中定义。
1 typedef struct xLIST 2 { 3 listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ 4 configLIST_VOLATILE UBaseType_t uxNumberOfItems; 5 ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */ 6 MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ 7 listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ 8 } List_t;
3行和7行,这两个都是用来检查列表完整性的。需要将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为1,开启以后会想这两地方分别添加一个变量xListIntegrityValue1和xListIntegrityVlue2,在初始化列表的时候会把这两个变量中写入一个特殊的值,默认不开启这个功能。
uxNumerOfItems 用来记录列表中列表项的数量。
pxIndex 用来记录当前列表项索引号,用于遍历列表。
列表的最后一个列表项,用来表示列表结束,此变量类型为MiniListItem_t,这是一个mini列表项。
列表结构示意图:
上图中并未列出用于列表完整性检查的成员变量。
列表项,mini列表项。
列表项就是存放在列表中的项目,FreeRTOS提供了两种列表项:列表项和迷你列表项。这两个都在文件list.h中有定义,先来看一下列表项,定义如下:
1 struct xLIST_ITEM 2 { 3 listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ 4 configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ 5 struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */ 6 struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */ 7 void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ 8 void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */ 9 listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ 10 }; 11 typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
3行和9行,用法和列表一样,用来检查列表项完整性的。
xItemValue 为列表项值。
pxNext 指向下一个列表项。
pxPrevious 指向前一个列表项,和pxNext配合起来实现类似双向链表的功能。
pvOwner 记录此链表项归谁拥有通常是任务控制块。
pvContainer 用来记录此列表项归哪个列表。注意和pvOwner的区别,在前面讲解任务控制块TCB_t的时候说了在TCB_t中有两个变量xStateListItem和xEventListItem,这两个变量的类型就是ListItem_t,也就是说,这两个成员都是列表项。以xStateListItem为例,当创建一个任务以后xStateListItem的pvOwner变量指向这个任务的任务控制块,表示xStateListItem属于此任务。当任务就绪态以后,xStateListItem的pvContainer就执行就绪列表,表明此列表项在就绪列表中。
列表项结构示意图:
上图中并未列出用于列表项完整性检查的成员变量
迷你列表项
迷你列表项在文件list.h中有定义:
1 struct xMINI_LIST_ITEM 2 { 3 listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ 4 configLIST_VOLATILE TickType_t xItemValue; 5 struct xLIST_ITEM * configLIST_VOLATILE pxNext; 6 struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; 7 }; 8 typedef struct xMINI_LIST_ITEM MiniListItem_t;
3行用于检查迷你列表项的完整性。
xItemValue 记录列表列表项值。
pxNext 指向下一个列表项
pxPrevious 指向上一个列表项
可以看出迷你列表项只是比列表项少了几个成员变量,迷你列表项有的成员变量列表项都有,没有感觉有什么本质区别。那为什么弄个迷你列表项出来呢?那是因为有些情况下我们不需要列表项这么全的功能,可能之炫耀其中的某几个成员变量,如果此时用列表项的话会造成内存浪费!比如上上列表结构体List_t中表示最后一个列表项的成员变量xListEnd就是MiniListIten_t类型的。
迷你列表项结构示意图:
上图中并未列出用于迷你列表项完整性检查的成员变量!
列表和列表项初始化
列表初始化
新创建或者定义的列表需要对其进行初始化处理,列表的初始化其实就是初始化列表结构体List_t中各个成员变量,列表的初始化通过使用函数vListInitialise()来完成,此函数在list.c中有定义:
1 void vListInitialise( List_t * const pxList ) 2 { 3 /* The list structure contains a list item which is used to mark the 4 end of the list. To initialise the list the list end is inserted 5 as the only list entry. */ 6 pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ 7 8 /* The list end value is the highest possible value in the list to 9 ensure it remains at the end of the list. */ 10 pxList->xListEnd.xItemValue = portMAX_DELAY; 11 12 /* The list end next and previous pointers point to itself so we know 13 when the list is empty. */ 14 pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ 15 pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ 16 17 pxList->uxNumberOfItems = ( UBaseType_t ) 0U; 18 19 /* Write known values into the list if 20 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ 21 listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ); 22 listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ); 23 }
xListEnd用来表示列表的末尾,而pxIndex表示列表项的索引号,此时列表只有一个列表项,那就是xListEnd,所以pxIndex指向xListEnd。
xListEnd的列表项值初始化为portMAX_DELAY,portMAX_DELAY是个宏,在文件portmacro.h中有定义。根据所使用的MCU的不同,portMAX_DELAY值也不相同,可以为0xffff或者oxffffffffUL,这里使用oxffffffffUL。
初始化列表项xLIstEnd的pxNext变量,因为此时列表只有一个列表项xListEnd,因此pcNext只能指向自身。
初始化xListEnd的pxPrevious变量,指向xListEnd自身。
由于此时没有其他列表项,一次uxNumberOfItems为0,注意,这里没有算xListEnd。
21、22 初始化列表项中用于完整性检查字段,只有宏
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 为1的时候才有效。
同样的,根据所选的MCU不同,其写入的值也不同,可以为ox5a5a或者0x5a5a5a5aUL。STM32是32位系统,写入0x5a5a5a5aUL,
列表初始化完成后如图:
列表项初始化
同列表一样,列表项在使用的时候也需要初始化,列表项初始化由函数vListInitialiseItem()来完成:
1 void vListInitialiseItem( ListItem_t * const pxItem ) 2 { 3 /* Make sure the list item is not recorded as being on a list. */ 4 pxItem->pvContainer = NULL; 5 6 /* Write known values into the list item if 7 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ 8 listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); 9 listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); 10 }
列表项的初始化很简单,只是将列表项成员变量pvContainer初始化为NULL,并且给用于完整性检查的变量赋值。列表项的成员变量比列表要要多,怎么初始化函数就这么短?其他的成员变量什么时候初始化?这是因为列表项要根据实际使用情况来初始化,比如任务创建函数xTaskCreate()就会对任务堆栈中的xStateListItem和xEventListItem这两个列表项中的其他成员变量在做初始化。
列表项插入
列表项插入函数分析
列表项的插入操作通过函数vListInsert()来完成:
1 void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) 2 { 3 ListItem_t *pxIterator; 4 const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; 5 6 /* Only effective when configASSERT() is also defined, these tests may catch 7 the list data structures being overwritten in memory. They will not catch 8 data errors caused by incorrect configuration or use of FreeRTOS. */ 9 listTEST_LIST_INTEGRITY( pxList ); 10 listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); 11 12 /* Insert the new list item into the list, sorted in xItemValue order. 13 14 If the list already contains a list item with the same item value then the 15 new list item should be placed after it. This ensures that TCB's which are 16 stored in ready lists (all of which have the same xItemValue value) get a 17 share of the CPU. However, if the xItemValue is the same as the back marker 18 the iteration loop below will not end. Therefore the value is checked 19 first, and the algorithm slightly modified if necessary. */ 20 if( xValueOfInsertion == portMAX_DELAY ) 21 { 22 pxIterator = pxList->xListEnd.pxPrevious; 23 } 24 else 25 { 26 /* *** NOTE *********************************************************** 27 If you find your application is crashing here then likely causes are 28 listed below. In addition see http://www.freertos.org/FAQHelp.html for 29 more tips, and ensure configASSERT() is defined! 30 http://www.freertos.org/a00110.html#configASSERT 31 32 1) Stack overflow - 33 see http://www.freertos.org/Stacks-and-stack-overflow-checking.html 34 2) Incorrect interrupt priority assignment, especially on Cortex-M 35 parts where numerically high priority values denote low actual 36 interrupt priorities, which can seem counter intuitive. See 37 http://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition 38 of configMAX_SYSCALL_INTERRUPT_PRIORITY on 39 http://www.freertos.org/a00110.html 40 3) Calling an API function from within a critical section or when 41 the scheduler is suspended, or calling an API function that does 42 not end in "FromISR" from an interrupt. 43 4) Using a queue or semaphore before it has been initialised or 44 before the scheduler has been started (are interrupts firing 45 before vTaskStartScheduler() has been called?). 46 **********************************************************************/ 47 48 for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ 49 { 50 /* There is nothing to do here, just iterating to the wanted 51 insertion position. */ 52 } 53 } 54 55 pxNewListItem->pxNext = pxIterator->pxNext; 56 pxNewListItem->pxNext->pxPrevious = pxNewListItem; 57 pxNewListItem->pxPrevious = pxIterator; 58 pxIterator->pxNext = pxNewListItem; 59 60 /* Remember which list the item is in. This allows fast removal of the 61 item later. */ 62 pxNewListItem->pvContainer = ( void * ) pxList; 63 64 ( pxList->uxNumberOfItems )++; 65 }
参数:
pxList:列表项要插入的列表
pxNewListItem:要插入的列表项。
函数vListInsert()的参数pxList决定了列表项要插入到哪个列表中,pxNewListItem决定了要插入的列表项,但是这个列表项具体插入到什么地方呢?要插入的位置由列表项中成员变量xItemValue来决定。列表项的插入根据xItemValue的值按照升序的方式排列!
4行:获取要插入的列表项值,即列表项成员变量xItemValue的值,因为要根据整个值来确定列表项要插入的位置。
9行、10行:用来检查列表和列表项的完整性。其实就是检查列表和列表项中用于完成性检查的变量值是否被改变。这些变量的值在列表和列表项初始化的时候就被写入了,这两行代码需要实现函数configASSERT()!
20行:要掺入列表项,第一步就是要获取该列表项要插入到什么位置!如果要插入的列表项的值等于portMAX_DELAY,也就是说列表项值为最大值,这种情况最好办了,要插入的位置就是
列表项相关API:
vListInitialise() 列表初始化
vListInitialiseItem() 列表初始化
vListInsert() 列表项插入
vListInsertEnd() 列表项末尾插入
uxListRemove() 列表项删除
listGET_OWNER_OF_NEXT_ENTRY() 列表的遍历
列表项的插入和删除实验
本实验设计3个任务,start_task、task1_task和list_task
start_task:用来创建其他2个任务。
task1_task:应用任务1,控制LED0闪烁,用来提示系统正在运行。
list_task: 列表和列表项操作任务,调用列表和列表项相关API函数,并且通过串口输出相应的信息来观察这些API函数的运行过程。
任务的特性
在使用RTOS的时候,一个实时应用可以作为一个独立的任务。每个任务都有自己的运行环境,不依赖于系统中其他任务或者RTOS调度器。任何一个时间点只能有一个任务运行,具体运行哪个任务是由RTOS调度器来决定,RTOS调度器因此就会重复地开启、关闭每个任务。任务不需要了解RTOS调度器的具体行为,RTOS调度器的职责是确保当一个任务开始执行的时候其上下文环境(寄存器值,堆栈内容)和任务上一次退出的时候相同。为了做到这一点,每个任务都必须有个堆栈,当任务切换的时候将上下文环境保存在堆栈中,这样当任务再次执行的时候就可以从堆栈中取出上下文环境,任务恢复运行。
任务状态:
FreeRTOS中的任务永远处于下面几个状态中的某一个:
运行态:当一个任务正在运行时,那么就说这个任务处于运行态,处于运行态的任务就是当前正在使用处理器的任务。如果使用的是单核处理器的话,那么不管在任何时刻永远都只有一个任务处于运行态。
就绪态:处于就绪态的任务就是那些准备就绪(这些任务没有被阻塞或者挂起),可以运行的任务,但是处于就绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行!
阻塞态:如果一个任务当前正在等待某个外部事件的话就说它处于阻塞态,比如说如果某个任务调度使用了函数vTaskDelay()的话就会进入阻塞态,直到延时周期完成。任务在等待队列、信号量。事件组。通知或互斥信号量的时候也会进入阻塞态。任务进入阻塞态会有一个超时时间,当超过这个超市时间,任务就会推出阻塞状态,即时所有等待的事件还没有来临。
挂起态:像阻塞态一样,任务进入挂起态以后,也不能被调度器调用进入运行态,但是进入挂起态的任务没有超时时间。任务进入和退出挂起态通过调用vTaskSuspend()和xTaskResume()。
任务状态之间的
FreeRTOS队列
队列简介
队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之间要交流的数据保存在对了中,叫做队列项目。队列所能保存的最大数据项目的数量叫做队列的长度,创建队列的时候会指定数据项目的大小和队列的长度。由于队列是用来传递消息的,所以也成为消息队列。FreeRTOS中的信号量也是根据队列实现的!所有有必要深入地了解FreeRTOS的队列。
1. 数据存储
通常队列采用先进先出(FIFO)的存储缓冲机制,也就是往队列发送数据的时候永远都是发送到队列的尾部,而从队列提取数据的时候是同队列的头部提取。但是也可以使用LIFO的存储缓冲,也就是后进先出,FreeRTOS中的队列也提供了LIFO的缓存缓冲机制。
数据发送到队列中会导致数据拷贝,也就是将要发送的数据拷贝到队列中,这就意味着在队列中存储的是数据的原始值,而不是原数据的引用(即只传递数据的指针),这个也叫做值传递。UCOS的消息队列采用的是引用传递,传递的是消息指针。采用引用传递的话,消息内容必须一直保持可见性,也就是消息内容必须有效,那么局部变量这种可能会随时被删掉的东西就不能用来传递消息,但是采用引用传递会节省时间,因为不用进行数据拷贝。
采用值传递的话虽然会导致数据拷贝,会浪费一点时间,但是一旦将消息发送到队列中,原始的数据缓冲区就可以删除或者覆写,这样的话这些缓冲区就可以被重复的使用。FreeRTOS中使用队列传递消息的虽然使用的是数据拷贝,但是也可以使用引用来传递消息,直接往队列里发送指向这个消息的地址指针就可以了。这样当我要发送的消息数据太大的时候就可以直接发送消息缓冲区的地址指针,比如在网络应用环境中,网络的数据量往往都很大,才用数据拷贝的话就不现实。
1. 多任务访问
队列不是属于某个特别指定的任务的,任何任务都可以向队列中发送消息,或者从队列中提取消息。