裸机和rots的认识:
裸机:所有的任务放在一个大循环中,轮流调用,实时性差。
rtos:每一个任务都是一个循环。通过时间调度的方式进行, 高优先级的可以打断低优先级的任务,中断可以打断一切任务,并通过任务的栈空间回到打断任务的地方继续执行。
最重要的是: 对于延时函数的处理,裸机是一直在等待,而RTOS的操作是让低优先级的执行,时间到后返回继续执行。
FREERTOS 的简介:免费的 ,移植性强,任务没有限制的操作系统,支持抢占式,协程式, 时间片流转任务的调度。后面细琐。
要求: 学习任务调度,任务启动流程,中断管理。任务切换。列表,队列,信号量,内存管理。
RTOS的简介:
任务调度简介和任务状态:
(1): 抢占式调度: 类似中断的优先级的处理,高优先级的打断低优先级的中断,但是数字使越大优先级越高,针对优先级不同的任务的调度
(2):时间片的调度: 每一个Systick的时钟周期来更新一个任务, 针对优先级相同的任务的调度。
RTOS的四种状态:
(1)运行状态:每次只能有一个任务处于运行态。
(2)就绪状态:已经能够执行,但还未执行。
(3)阻塞状态:因延时和外部中断状态引起。
(4)挂起状态:类似暂停。
当解除挂起状态后回到就绪状态,并不是运行状态。
FreeRTOS的移植: 具体方法可以参考野火,正点原子的资料的“FREERTOS的开发手册”。
正点原子的移植后视频上可以运行,但是实际上有些朋友可能出现编译无错误,但是就是不执行响应的操作。 需将其FreeConfig文件的一些的宏定义做出响应的修改:具体可以参考一下代码(做出相应的修改):
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include "sys.h"
#include "usart.h"
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
/* Ensure stdint is only used by the compiler, and not the assembler. */
#if defined(__ICCARM__)||defined(__CC_ARM)||defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
//断言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
/***************************************************************************************************************/
/* FreeRTOS基础配置配置选项 */
/***************************************************************************************************************/
#define configUSE_PREEMPTION 1 //1使用抢占式内核,0使用协程
#define configUSE_TIME_SLICING 1 //1使能时间片调度(默认式使能的)
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 //1启用特殊方法来选择下一个要运行的任务
//一般是硬件计算前导零指令,如果所使用的
//MCU没有这些硬件指令的话此宏应该设置为0!
#define configUSE_TICKLESS_IDLE 0 //1启用低功耗tickless模式
#define configUSE_QUEUE_SETS 1 //为1时启用队列
#define configCPU_CLOCK_HZ (SystemCoreClock) //CPU频率
#define configTICK_RATE_HZ (1000) //时钟节拍频率,这里设置为1000,周期就是1ms
#define configMAX_PRIORITIES (32) //可使用的最大优先级
#define configMINIMAL_STACK_SIZE ((unsigned short)130) //空闲任务使用的堆栈大小
#define configMAX_TASK_NAME_LEN (16) //任务名字字符串长度
#define configUSE_16_BIT_TICKS 0 //系统节拍计数器变量数据类型,
//1表示为16位无符号整形,0表示为32位无符号整形
#define configIDLE_SHOULD_YIELD 1 //为1时空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configUSE_TASK_NOTIFICATIONS 1 //为1时开启任务通知功能,默认开启
#define configUSE_MUTEXES 1 //为1时使用互斥信号量
#define configQUEUE_REGISTRY_SIZE 8 //不为0时表示启用队列记录,具体的值是可以
//记录的队列和信号量最大数目。
#define configCHECK_FOR_STACK_OVERFLOW 0 //大于0时启用堆栈溢出检测功能,如果使用此功能
//用户必须提供一个栈溢出钩子函数,如果使用的话
//此值可以为1或者2,因为有两种栈溢出检测方法。
#define configUSE_RECURSIVE_MUTEXES 1 //为1时使用递归互斥信号量
#define configUSE_MALLOC_FAILED_HOOK 0 //1使用内存申请失败钩子函数
#define configUSE_APPLICATION_TASK_TAG 0
#define configUSE_COUNTING_SEMAPHORES 1 //为1时使用计数信号量
/***************************************************************************************************************/
/* FreeRTOS与内存申请有关配置选项 */
/***************************************************************************************************************/
#define configSUPPORT_DYNAMIC_ALLOCATION 1 //支持动态内存申请
#define configTOTAL_HEAP_SIZE ((size_t)(20*1024)) //系统所有总的堆大小
/***************************************************************************************************************/
/* FreeRTOS与钩子函数有关的配置选项 */
/***************************************************************************************************************/
#define configUSE_IDLE_HOOK 0 //1,使用空闲钩子;0,不使用
#define configUSE_TICK_HOOK 0 //1,使用时间片钩子;0,不使用
/***************************************************************************************************************/
/* FreeRTOS与运行时间和任务状态收集有关的配置选项 */
/***************************************************************************************************************/
#define configGENERATE_RUN_TIME_STATS 0 //为1时启用运行时间统计功能
#define configUSE_TRACE_FACILITY 1 //为1启用可视化跟踪调试
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 //与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
//prvWriteNameToBuffer(),vTaskList(),
//vTaskGetRunTimeStats()
/***************************************************************************************************************/
/* FreeRTOS与协程有关的配置选项 */
/***************************************************************************************************************/
#define configUSE_CO_ROUTINES 0 //为1时启用协程,启用协程以后必须添加文件croutine.c
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) //协程的有效优先级数目
/***************************************************************************************************************/
/* FreeRTOS与软件定时器有关的配置选项 */
/***************************************************************************************************************/
#define configUSE_TIMERS 1 //为1时启用软件定时器
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1) //软件定时器优先级
#define configTIMER_QUEUE_LENGTH 5 //软件定时器队列长度
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2) //软件定时器任务堆栈大小
/***************************************************************************************************************/
/* FreeRTOS可选函数配置选项 */
/***************************************************************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 1
/***************************************************************************************************************/
/* FreeRTOS与中断有关的配置选项 */
/***************************************************************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 //中断最低优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 //系统可管理的最高中断优先级
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/***************************************************************************************************************/
/* FreeRTOS与中断服务函数有关的配置选项 */
/***************************************************************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#endif /* FREERTOS_CONFIG_H */
当按下F7进行编译的时候可能会报错(类似与中断优先级的问题):
通过CRTL+F搜索“ __NVIC_PRIO_BITS”找到其相应的宏定义位置将4U换为4即可,
FreeRTOS的任务的创建和删除:
1.函数的熟悉
xTaskCreate() /*动态的任务的创建(内存是由FreeRTOS自动管理)*/
xTaskCreateStatic() /*静态的任务的创建(需要用户自己申请内存(麻烦))*/
xTaskDelete() /*任务的删除(通过调用任务的句柄)*/
(1).BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, /*指向任务的函数的指针*/
const char * const pcName, /*任务的名字*/
const configSTACK_DEPTH_TYPE usStackDepth, /*任务的堆栈深度*/
void * const pvParameters, /*任务的参数*/
UBaseType_t uxPriority, /*任务的优先级*/
TaskHandle_t * const pxCreatedTask ) /*任务的句柄(也就是任务控制块)*/
任务控制块(TCB)就是指的是任务的信息(任务的栈顶指针,状态列表信息,事件列表的状态,优先级,任务栈的起始地址,名字) 类似人的身份证
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack;
/*在任务的切换的时候任务的上下的保存,任务的恢复息息相关*/
ListItem_t xStateListItem;
/*任务的状态列表项(就绪,运行,挂起,阻塞)*/
ListItem_t xEventListItem;
UBaseType_t uxPriority;
StackType_t * pxStack; /*< Points to the start of the stack. */
char pcTaskName[ configMAX_TASK_NAME_LEN ];
}
(). TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, /*指任务函数的指针*/
const char * const pcName, /*任务的名字*/
const uint32_t ulStackDepth, /*任务的堆栈大小*/
void * const pvParameters, /*任务的参数*/
UBaseType_t uxPriority, /*任务的优先级*/
StackType_t * const puxStackBuffer, /*任务的堆栈, 一般为数组,由用户分配*/
StaticTask_t * const pxTaskBuffer ) /*任务控制块的指针,由用户分配*/
任务创建完毕后,处于就绪的状态。
(3)void vTaskDelete( TaskHandle_t xTaskToDelete )
当任务句柄为NULL,则删除自身(当前运行的任务)注:通过 xTaskCreate()动态创建的任务,在使用vTaskDelete()删除后,该任务创建时申请的堆栈和内存会在系统的空闲任务中被释放掉。
2.动态任务的创建(实例分析)
主函数还是我们熟悉的main函数,但FreeRTOS里的main函数不需要自己设计成死循环,只需要创建任务并开启任务调度,即可使系统持续运行。任务的创建一般都是先创建一个开始任务,然后开始任务再负责创建其它子任务。
在主函数中
xTaskCreate((TaskFunction_t ) start_task, /*指向任务函数的指针*/
(const char* )"start_task", /*任务名字(默认为16)*/
(uint16_t )START_STK_SIZE, /*任务的堆栈大小:单位为字*/
(void* )NULL, /*传递给任务函数的参数*/
(UBaseType_t )START_TASK_PRIO, /*任务的优先级*/
(TaskHandle_t* )&StartTask_Handler); /*任务句柄(任务的控制块:删除和添加)*/
vTaskStartScheduler(); //开启任务的调度函数
开始任务的函数
//开始任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区(关闭中断:就是那些不想被打断的程序段)
/*该函数的用于先退出中断,任务创建完毕后在开始从高优先级到低优先级的执行*/
xTaskCreate((TaskFunction_t ) led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
xTaskCreate((TaskFunction_t ) led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
xTaskCreate((TaskFunction_t ) float_task,
(const char* )"float_task",
(uint16_t )FLOAT_STK_SIZE,
(void* )NULL,
(UBaseType_t )FLOAT_TASK_PRIO,
(TaskHandle_t* )&FLOATTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
开始创建子任务的函数
void led0_task(void *pvParameters)
{
while(1)
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);
vTaskDelay(500); /* 每个任务函数都是一个死循环,必须加xTaskDelay()函数,用于任务的切换*/
}
}
void led1_task(void *pvParameters)
{
while(1)
{
printf("aaaaaaa\r\n");
vTaskDelay(500);
}
}
void float_task(void *pvParameters)
{
static float float_num = 0.00;
while(1)
{
float_num+=0.01f;
printf("float_num的值为%.4f\r\n",float_num);
vTaskDelay(1000);
}
}
FreeRTOS任务的挂起和删除
(1)vTaskSuspend(LED1Task_Handler); //任务的挂起函数
(2)vTaskResume(LED1Task_Handler); //取消的任务的挂起函数
(3)vTaskResumeFromLSR(LED1Task_Handler); //在中断中恢复挂起函数
(1)vTaskSuspend(LED1Task_Handler); //任务的挂起函数
要先配置#define INCLUDE_vTaskSuspend 1 ,通过传递任务句柄来决定。当传输的句柄为NULL,则挂起当前正在运行的任务。
注意: 挂起并不代表删除,要和删除区分开来
(2)vTaskResume(LED1Task_Handler); //取消的任务的挂起函数
要先配置#define INCLUDE_vTaskSuspend 1 ,通过传递任务句柄来决定。无论任务的句柄被挂起多少次,只需要解挂一次就可以。
(3)vTaskResumeFromLSR(LED1Task_Handler); //在中断中恢复挂起函数
要先配置INCLUDE_xTaskResumeFromLSR和INCLUDE_vTaskSuspend设置为1.中断服务程序中要调用FreeRTOS的API的中断优先级不能高于FreeRTOS所管理的最高优先级。
FreeRTOS的列表和列表项
列表被FreeRTOS调度器使用,用于跟踪任务,处于就绪、挂起、延时的任务,都会被挂接到各自的列表中。用户程序如果有需要,也可以使用列表。
列表类似于c语言的链表。 列表项相当于节点,FreeRTOS中的列表是一个双向列表项。列表和数组的区别好处在于,列表的数目可以在后期进行修改和添加,但是数组不行。
列表的结构体:
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
configLIST_VOLATILE UBaseType_t uxNumberOfItems; /*列表中列表项的个数(不包含xListEnd)*/
ListItem_t * configLIST_VOLATILE pxIndex; /*用于遍历列表*/
MiniListItem_t xListEnd; /*迷你列表项(末尾列表项)*/
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
}List_t;
列表项的结构体:(列表项是列表用于存储数据的地方)
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
configLIST_VOLATILE TickType_t xItemValue; /*列表项值(常按照升序的方式)*/
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*指向列表中下一个列表项*/
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*指向列表中上一个列表项*/
void * pvOwner; /*指向一个任务TCB*/
void * configLIST_VOLATILE pvContainer; /*指向包含该列表项的列表 */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
};
typedef struct xLIST_ITEM ListItem_t;
迷你列表项:(仅仅用于标记列表末尾和挂载其他插入列表中的列表项)
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*用于检测列表项数据是否完整*/
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
列表和列表项相关API:
(1).vListInitialise( List_t * const pxList ) //初始化列表
(2).vListInitialiseItem( ListItem_t * const pxItem ) //初始化列表项
(3).vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) //列表插入列表项
(4).uxListRemove(ListItem_t * const pxNewListItem) //列表删除列表项
(5).vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) //列表末尾插入列表项
void vListInitialise( List_t * const pxList )
{
/*列表索引指向列表项末尾*/
pxList->pxIndex = ( ListItem_t * )&( pxList->xListEnd );
/* 设置为最大可能值 */
pxList->xListEnd.xItemValue =portMAX_DELAY;
/* 列表项xListEnd的pxNext和pxPrevious指针指向了它自己 */
pxList->xListEnd.pxNext = (ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious= ( ListItem_t * ) &( pxList->xListEnd );
/* 初始化时候,列表数目为0*/
pxList->uxNumberOfItems = ( UBaseType_t) 0U;
/* 设置为已知值,用于检测列表数据是否完整*/
listSET_LIST_INTEGRITY_CHECK_1_VALUE(pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE(pxList );
}
列表项的初始比较简单,只要确保列表项不在任何列表中即可。
void vListInitialiseItem( ListItem_t * const pxItem )
{
pxItem->pvContainer = NULL;
/*设置为已知值,用于检测列表项数据是否完整*/
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE(pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE(pxItem );
}
将列表项插入到列表中,列表项所在的位置取决于列表项的列表项值(xItemValue)
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* 检查列表和列表项数据的完整性,仅当configASSERT()定义时有效。*/
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY(pxNewListItem );
/*将新的列表项插入到列表,根据xItemValue的值升序插入列表。*/
if( xValueOfInsertion == portMAX_DELAY)
{
pxIterator =pxList->xListEnd.pxPrevious;
}
else
{
for( pxIterator = (ListItem_t * ) &( pxList->xListEnd );pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator =pxIterator->pxNext )
{
/* 这里为空 */
}
}
pxNewListItem->pxNext =pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious= pxNewListItem;
pxNewListItem->pxPrevious =pxIterator;
pxIterator->pxNext = pxNewListItem;
pxNewListItem->pvContainer = ( void* ) pxList;
( pxList->uxNumberOfItems )++;
}
FreeRTOS的启动任务的调度:
作用: 用于启动任务调度器,任务调度器启动后,FreeRTOS便会开始任务调度。
vTaskStartScheduler()函数主要实现以下功能:
(1),创建空闲任务,
(2),如果使能软件定时器,则创建定时器任务。
(3),关闭中断,防止调度器开启之前或过程中,受到中断的干扰,会在运行第一个任务的时候打开。
(4),初始化全局变量, 并将任务调度器的运行标志设置为已经运行
(5), 初始化任务运行时间统计功能的时基定时器
(6), 调用函数xPortStartScheduler();
函数的分析: 调转进入开启调度器任务函数以后:
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask(); //创建软件定时器的任务并作出相应的返回值
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
如果使能软件定时器,则创建定时器任务。
接着,在前面的任务创建成功后,会接收到一个返回值,当接收到pdPASS后则创建成功
portDISABLE_INTERRUPTS(); //关闭中断,运行第一个任务的时候打开
接着执行第四步和第五步:
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE; //开启调度器
xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; //心跳节拍
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); //统计任务的时间
第六步。调用函数xPortStartScheduler(); //用于完成启动任务调度器中与硬件架构相关的配置部分。
xPortStartScheduler() 函数主要实现以下功能:
(1)检测用户在FreeRTOSConfig.h文件中对中断的相关配置是否有误。
(2)配置PendSV和Systick的中断优先级为最低
(3)调用vPortSetupTimerInterrupt()配置Systick
(4) 初始化临界区嵌套计数为0
(5)调用函数prvEnableVFP()使能FPU()
(6) 调用函数prvStartFirstTask()启动第一个任务.