嵌入式系统基础与实践-基于 ARM Cortex-M3 内核的 STM32 微控制器(第2版) 课件 第11章 嵌入式实时操作系统FreeRTOS_第1页
嵌入式系统基础与实践-基于 ARM Cortex-M3 内核的 STM32 微控制器(第2版) 课件 第11章 嵌入式实时操作系统FreeRTOS_第2页
嵌入式系统基础与实践-基于 ARM Cortex-M3 内核的 STM32 微控制器(第2版) 课件 第11章 嵌入式实时操作系统FreeRTOS_第3页
嵌入式系统基础与实践-基于 ARM Cortex-M3 内核的 STM32 微控制器(第2版) 课件 第11章 嵌入式实时操作系统FreeRTOS_第4页
嵌入式系统基础与实践-基于 ARM Cortex-M3 内核的 STM32 微控制器(第2版) 课件 第11章 嵌入式实时操作系统FreeRTOS_第5页
已阅读5页,还剩150页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第13章嵌入式实时操作系统FreeRTOSSTM32本章知识与能力要求了解FreeRTOS功能特点、应用场合;理解和掌握FreeRTOS任务调度的基本原理、实现机制以及任务优先级;理解和掌握FreeRTOS的任务管理、信号量、事件组、消息队列等同步通信机制。第13章嵌入式实时操作系统FreeRTOS13.313.413.213.1FreeRTOS概述FreeRTOS的任务信号量事件组消息队列13.513.1FreeRTOS概述13.1.1FreeRTOS概述13.1.2FreeRTOS的数据类型和编程规范FreeRTOS是一个开源的嵌入式实时操作系统(RTOS:RealTimeOperatingSystem),实时意味着能够满足系统对任务响应时间的要求,强调的是实时性,这里的实时性并不意味着快,而是具有严苛的截止时限(Deadline)。FreeRTOS提供轻量级的资源管理、任务调度和管理机制,通过任务划分和模块化设计,能够实现多任务的并发处理,提供实时操作系统所需的基本功能。第9章定时器13.1.1FreeRTOS概述FreeRTOS是一个多任务、抢占式、可裁剪的实时内核,以系统函数形式提供时间、内存、任务调度、任务同步与通信等各类管理功能,以任务为单位进行应用程序的开发,任务之间相对独立,各任务通过信号量、消息队列等机制进行同步与通信。第9章定时器13.1.1FreeRTOS概述FreeRTOS

内核支持抢占式、合作式和时间片调度,且可裁剪,大小可扩展,可用程序内存占用低至9KB免费开源,根据MIT许可用于任何目的灵活的任务优先级分配,可创建任务的数量没有限制,任务的优先级也没有限制

具有低功耗模式,支持中断嵌套

支持40多种MCU架构和15多种工具链,包括RISC-V和ARMv8-M(ARMCortex-M33)微控制器。支持多种扩展库和组件,提供网络、文件系统、安全、OTA更新等功能13.1.1FreeRTOS概述FreeRTOS具有丰富的生态系统,比如STM32微控制器的开发平台STM32CubeMX集成了FreeRTOS内核软件包,使得开发人员能够快速构建基于STM32+FreeRTOS的应用系统,利用各种现有的软件生态资源,加速应用程序的开发,FreeRTOS作为微控制器和小型微处理器的实时操作系统,被广泛应用于嵌入式系统和物联网设备中。第9章定时器13.1.1FreeRTOS概述1.FreeRTOS的数据类型STM32的HAL库有一个头文件stdint.h,里面定义了一些基础的数据类型,比如uint8_t、uint32_t等。第9章定时器13.1.2FreeRTOS的数据类型和编程规范stdint.h/*exact-widthsignedintegertypes*/typedefsignedcharint8_t;typedefsignedshort

intint16_t;typedefsignedintint32_t;typedefsigned__INT64int64_t;/*exact-widthunsignedintegertypes*/typedefunsignedcharuint8_t;typedefunsignedshortintuint16_t;typedefunsignedintuint32_t;typedefunsigned__INT64uint64_t;portmacro.h头文件中定义的基础数据类型源码如下:第9章定时器13.1.2FreeRTOS的数据类型和编程规范portmacro.h/*Typedefinitions.*/#define portCHAR char//int8_t#define portFLOAT float //4字节浮点数#define portDOUBLEdouble //8字节浮点数#define portLONG long//int32_t#define portSHORT short//int16_t#define portSTACK_TYPEuint32_t//栈数据类型#define portBASE_TYPElong//int32_ttypedefportSTACK_TYPEStackType_t;//栈数据类型StackType_t,32位微处理器中是uint32_ttypedeflong BaseType_t; //基础数据类型BaseType_t,32位微处理器中是int32_ttypedefunsignedlongUBaseType_t; //基础数据类型UBaseType,32位微处理器中是uint32_ttypedefuint32_tTickType_t;//节拍数据类型TickType_t,32位微处理器为uint32_tBaseType_t

作为FreeRTOS自定义的基础数据类型,通常用作简单的返回值的类型以及通用宏定义的逻辑值,比如pdTRUE为1,pdFALSE值为0,pdPASS为1,pdFAIL为0。定义在projdefs.h头文件中,源码如下:第9章定时器13.1.2FreeRTOS的数据类型和编程规范projdefs.h#definepdFALSE ((BaseType_t)0)#definepdTRUE ((BaseType_t)1)#definepdPASS (pdTRUE)#definepdFAIL (pdFALSE)#defineerrQUEUE_EMPTY ((BaseType_t)0)#defineerrQUEUE_FULL ((BaseType_t)0)StackType_t栈变量数据类型定义,这个数量类型由系统架构决定,对于16位系统架构,StackType_t定义的是16位变量,对于32位系统架构,StackType_t定义的是32位变量。TickType_tFreeRTOS的任务调度是基于一个周期性的时钟中断TickInterrupt,Tick中断次数累加称为TickCount,这个变量的类型就是TickType_t。如果用户在FreeRTOSConfig.h头文件中使能了configUSE_16_BIT_TICKS这个宏定义,那么TickType_t定义的就是16位无符号数uint16_t,否则TickType_t定义的就是32位无符号数uint32_t。对于32位架构的处理器,建议把TickType_t配置为uint32_t,即设置此宏定义数值为0即可。UBaseType_t该数据类型是BaseType_t类型的有符号版本。第9章定时器13.1.2FreeRTOS的数据类型和编程规范2.FreeRTOS的编程规范FreeRTOS和uC/OS-III都遵循MISRA-C编码标准(MISRA-C是由汽车工业软件可靠性协会(MISRA)提出的C语言开发标准),支持各种编译器,了解FreeRTOS的变量、函数以及相关宏定义的命名规律有助于理解其意义。第9章定时器13.1.2FreeRTOS的数据类型和编程规范·变量名FreeRTOS中,定义变量时通常将变量的类型作为前缀,通过变量名的前缀就可以判断变量的类型第9章定时器13.1.2FreeRTOS的数据类型和编程规范变量名前缀含义cchar类型sint16_t(short)lint32_t(long)x复杂的结构体、句柄等定义的变量名的前缀为x;BaseType_t类型以及其他非标准的类型,比如结构体变量、任务句柄TaskHandle、队列句柄QueueHandle等,使用前缀x。u表示无符号整数unsigned,比如uc表示uint8_t(unsignedchar)类型,us表示uint16_t类型,ul表示uint32_t类型。p指针类型变量使用前缀ppcchar*类型uxUBaseType_t类型,比如uxCurrentPriority,uxPriority表13-1FreeRTOS常见的变量名类型前缀·函数名FreeRTOS中函数名是由返回值类型和函数功能组成,包含了函数返回值、函数所在的文件名和函数的功能,如果是私有的函数则加prv(private)前缀。

若返回值为void类型,则函数命名规则为:“v+函数功能”,以FreeRTOS中队列删除函数为例,该函数定义在task.c文件中,FreeRTOS将其命名为vQueueDelete(),其中v表示该函数的返回值为void类型。第9章定时器13.1.2FreeRTOS的数据类型和编程规范表13-2FreeRTOS常见的函数名前缀及其所在位置第9章定时器13.1.2FreeRTOS的数据类型和编程规范函数名前缀含义v返回值类型:void比如vTaskPrioritySet定义在task.c文件中x返回值类型:BaseType_t比如xQueueReceive定义在queue.c文件中pv返回值类型:pointertovoid比如pvTimerGetTimerID定义在timer.c文件中pc返回值为char*比如pcTimeGetName定义在timer.h文件中CMSIS-RTOS接口CMSIS-RTOS是ARM公司为运行于Cortex-M系列微控制器上的操作系统专门设计的一种接口标准,它提供了一种标准化的API接口让开发者可以调用嵌入式操作系统的功能而不必理会底层到底采用的是哪种操作系统,这就意味着凡是采用CMSIS-RTOS编写的程序可以无缝移植到其它操作系统上运行,因为CMSIS-RTOS提供一个通用的操作系统API接口会调用其它操作系统的相关功能。STM32CubeMX软件集成的CMSIS_V1是对FreeRTOS进行了上层封装,适用于基于Cortex-M内核的微控制器,比如Cortex-M0/M0+/M3/M4/M7,最新的CMSIS_V2是在CMSIS_V1的基础上进行了扩展,增加了对Armv8-M内核和多核芯片的操作系统的支持,比如Cortex-M,Cortex-A5/A7/A9。这里推荐选择CMSIS_V2,方便以后移植切换到性能更强大的芯片上。第9章定时器13.1.2FreeRTOS的数据类型和编程规范CMSIS-RTOS接口相关的宏定义、数据类型及封装后的功能函数定义在cmsis_os.h、cmsis_os2.h文件中,前缀为“os”。比如#defineosWaitForever0xFFFFFFFFU//WaitforevertimeoutvalueosStatus_tosDelay(uint32_tticks);//FreeRTOS的相对延时函数osStatus_tosDelayUntil(uint32_tticks);//FreeRTOS的绝对延时函数第9章定时器13.1.2FreeRTOS的数据类型和编程规范表13-3

FreeRTOS的API函数及CMSIS-RTOS封装后的API函数对比第9章定时器13.1.2FreeRTOS的数据类型和编程规范API类别FreeRTOS的API函数CMSIS-RTOSV2封装后的API函数说明内核控制vTaskStartSchedulerosKernelStart启动RTOS内核调度程序任务管理xTaskCreateosThreadNew创建一个任务信号量xSemaphoreCreateBinaryxSemaphoreCreateCountingosSemaphoreNew创建并初始化信号量对象互斥量xSemaphoreCreateMutexStaticxSemaphoreCreateMutexosMutexNew创建并初始化一个互斥量对象消息队列xQueueCreateosMessageQueueNew创建并初始化消息队列对象软件定时器xTimerCreateosTimerNew创建一个软件定时器内存管理pvPortMallocosMemoryPoolNewosMemoryPoolAlloc在任务堆上动态申请内存延时函数vTaskDelayvTaskDelayUntilosDelayosDelayUntil相对延时函数绝对延时函数13.2FreeRTOS的任务13.2.1任务13.2.2任务调度13.2.3任务的实现机制13.2.4任务相关API函数13.2.5任务创建步骤13.2.6FreeRTOS创建任务案例1.任务的概念嵌入式实时操作系统将整个系统功能分解为一个个相对独立且具有完整功能的程序模块,称为任务(Task)。任务是一个简单的程序或具有完整逻辑含义的程序段。Linux操作系统以及CMSIS中将任务又称为线程(Thread)。任务本质上就是一个无限循环且无返回值的函数,每个函数内部都是和裸机程序中的main函数一样是一个无限循环,所以任务不能有返回值。第9章定时器13.2.1任务任务的一般结构如下:VoidvTaskFunction(void*pvParameters){for(;;)//任务主体,无限循环且不能返回

{......//任务实现代码

}}2.任务的特性(1)独立性嵌入式操作系统中各任务之间互相独立,不存在互相调用的关系,所有任务在逻辑上都是平等的。操作系统环境下,任务在某一时刻是独占CPU资源的,任务之间互不交叉没有交集,各任务之间无法像裸机的功能模块之间进行信息传输,任务间的通信就需要用到各种通信机制如信号量、事件组、队列等来实现。第9章定时器13.2.1任务2.任务的特性(2)并发性多任务系统中,任务是并发处理的,这里的“并发”并不是说同一时刻一起执行多个任务,而是由于每个任务执行的时间很短,从宏观上看起来像是同一时刻执行了很多任务。将微处理器运行时间划分为固定长度的时间段,按一定的规则将这些小的时间段分配给各个任务进行使用,从宏观上看,有多个任务在微处理器上同时在运行,这就是多任务机制的原理。第9章定时器13.2.1任务任务所具有的独立性和并发特性是与裸机编程中的“功能模块”的本质区别。实时多任务操作系统中,任务(或线程)是构成多任务系统的基本单元,实时操作系统内核(Kernel)负责管理线程,用于管理和分配CPU资源,以便有效地执行各种任务和进程,这一过程称为调度(Scheduling)。一般来说,一个系统会同时处理多个请求,由于计算机资源有限,调度就是用来协调每个请求对资源的使用的方法。多任务系统中,根据一定的策略和算法,为任务分配CPU资源,以实现任务的按时完成和系统资源的最优利用,这一过程称为任务调度(Taskscheduling)。第9章定时器13.2.2任务调度1.任务的状态(State)反映任务当前在系统中所处的情形,由内核维护;基本状态有五种:休眠态就绪态运行态挂起态中断服务态第9章定时器13.2.2任务调度运行态(Running):一个运行态的任务是一个正在使用CPU资源的任务。任何时刻有且只有一个运行着的任务。就绪态(Ready):随时可以运行但因为当前有同优先级或更高优先级的任务正在运行,需要等待任务调度,等占有CPU资源的任务释放CPU就可以由就绪状态转到运行状态。阻塞态(Blocked):任务由于某种原因无法被执行而被阻塞,可能的原因如等待某个事件的触发或等待资源的释放。挂起态(Suspended):某些条件不满足而挂起不能运行的状态,挂起后任务将不被运行。第9章定时器13.2.2任务调度2.任务优先级任务优先级(TaskPriority)用于安排系统中各个任务的执行次序,任务优先级是任务的重要参数,每个任务都有其优先级,可以作为任务的标识。任务的功能在应用程序中越重要,赋予的优先级应越高。根据任务的优先级不同,高优先级的任务可以打断低优先级任务的运行而取得CPU的使用权,高优先级的任务执行完后重新把CPU的使用权归还给低优先级的任务,这样就保证了那些重要的紧急任务的运行。FreeRTOS中,数值越大优先级越高。第9章定时器13.2.2任务调度这里要注意的是configMAX_PRIORITIES参数配置越大,所需要的内存空间越大,实际应用中根据实际情况酌情配置此参数。第9章定时器13.2.2任务调度CMSIS-RTOS封装后的优先级定义在cmsis_os2.h文件中,CubeMX中Normal对应的优先级值为24,优先级数字越低,优先级别越低,0表示没有优先级,最高优先级是56,为osPriorityISR,即中断的优先级最高,创建任务时必须为任务设置初始的优先级。FreeRTOSConfig.h#defineconfigUSE_PREEMPTION1#defineconfigSUPPORT_STATIC_ALLOCATION1#defineconfigSUPPORT_DYNAMIC_ALLOCATION1#defineconfigUSE_IDLE_HOOK0#defineconfigUSE_TICK_HOOK0#defineconfigCPU_CLOCK_HZ(SystemCoreClock)#defineconfigTICK_RATE_HZ((TickType_t)1000)#defineconfigMAX_PRIORITIES(56)3.任务调度任务调度(TaskScheduling)就是操作系统根据调度规则按一定的算法从就绪队列中选择一个任务并分配CPU资源,以实现任务的并发执行。任务调度作为操作系统的核心功能之一。第9章定时器13.2.2任务调度任务调度方式基于优先级的抢占式调度内核总是将CPU分配给处于就绪态的优先级最高的任务运行,保证重要的突发事件及时得到处理。优点是可以满足实时系统的响应时间要求,可以实现任务的优先级管理,缺点是低优先级任务可能会被长时间阻塞。时间片轮转调度将CPU的执行时间划分为固定长度的时间片,每个任务分配一个时间片,在一个时间片内执行一定的指令,当时间片用完后,任务就会被挂起,等待下一次调度。优点是公平地分配CPU时间片,确保每个任务都能获得一定的执行时间,可以避免低优先级任务被长时间阻塞,缺点是无法满足实时系统的响应时间要求。FreeRTOS使用SysTick定时器产生固定间隔的中断,两次中断的间隔时间称为时间片(TimeSlice)。时钟节拍是一个周期性的定时中断,其时间间隔一般在1ms~10ms时间节拍可以为操作系统提供延时功能,将任务延时若干整数倍的时钟节拍,以及当任务等待事件发生时,提供超时判断的依据。不同的系统其任务调度的实现方法不同,FreeRTOS默认使用固定优先级的抢占式调度策略,对同等优先级的任务执行时间片轮转调度。抢占式调度算法,任务优先级越高,被调度的机会就越大,任务优先级相同时,采用时间片轮转调度。第9章定时器13.2.2任务调度4.任务的上下文实时操作系统中,当一个任务执行时,CPU需要知道该任务从哪里加载、又从哪里开始运行,这时就会用到CPU的寄存器和程序计数器(ProgramCounter,PC),并像其他程序一样能够访问RAM和ROM,这些资源(CPU中的寄存器数据、堆栈等)一起组成了任务的上下文(Context)。任务上下文(Context)是任务运行所依赖的环境,主要包括CPU中的寄存器数据、程序计数器、堆栈空间等。它是任务调度中的一个重要概念,定义了执行一个任务时需要具备的条件,操作系统根据调度规则从众多任务中找到符合执行条件的任务进行执行,当内核将执行其它任务时,需将正在运行任务的上下文保存在任务自身的任务栈中。第9章定时器13.2.2任务调度5.任务栈RTOS调度器的职责是确保当一个任务开始执行时其上下文的内容和上一次任务退出时的上下文内容一致,为做到这一点,需要将与当前任务的上下文(任务相关CPU寄存器中的内容)按“先进后出”的规则保存在内存区域中(Task'sContextStorageArea),这个内存保存区称为栈(Stack)。每个任务都会在任务创建时分配属于自己的栈空间。通过上下文,操作系统可以随时打断任务的运行,当优先级更高的任务打断低优先级任务运行时,将当前任务的上下文入栈,即保存当前正在运行任务的状态信息(寄存器的内容)到当前任务的栈中,并从新任务的任务栈中取出上下文重新装入CPU的寄存器,加载并运行新的任务,从而实现不同任务的切换运行,因此任务切换又称作任务上下文切换。第9章定时器13.2.2任务调度任务主要由任务控制块、任务栈和任务函数三个核心要素组成第9章定时器13.2.3任务的实现机制任务函数任务函数是任务要实现的具体功能,任务栈是RAM中的一块连续区域,用于保存任务在调度时的上下文信息及任务内部定义的局部变量,任务控制块是内核定义的一种数据结构,用于记录任务的各个属性,内核通过任务控制块实现对任务的管理和调度。第9章定时器13.2.3任务的实现机制voidvTaskFunction(void*pvParameters){for(;;)//任务函数的主体{......//任务的代码}

vTaskDelete(NULL);}1.任务函数任务函数的主体通常为一个无限循环,并且不允许以返回等任何方式从实现函数中退出程序循环,即任务函数不能有return语句;任务不能自行结束,如果任务需要退出,则需通过任务调度的方式进行删除,即必须调用函数vTaskDelete(NULL)删除该任务。任务函数的一般写法如下:任务是嵌入式实时操作系统最基本的执行单元,用于执行特定的功能。任务函数是任务的实际执行代码,也称作任务入口函数(EntryFunction)2.任务栈任务栈是任务的私有内存空间,用于保存任务的局部变量、上下文信息等,任务栈是在创建任务时进行设置的。FreeRTOS中使用STM32CubeMX创建任务时,需设置任务栈的大小(StackSize),单位为Words(4个字节),如图13-1所示。第9章定时器13.2.3任务的实现机制图13-1FreeRTOS任务栈大小任务栈其实就是一个预先定义好的全局数据,数据类型为StackType_t,定义在protmacro.h文件中,实际为uint32_t类型,默认为128,单位为字,即512字节,这也是FreeRTOS推荐的最小的任务栈。第9章定时器13.2.3任务的实现机制#defineportSTACK_TYPE uint32_ttypedefportSTACK_TYPEStackType_t;其源码如下:3.任务控制块FreeRTOS通过任务控制块(TaskControlBlock,TCB)来定义和管理任务,任务控制块是FreeRTOS中用于描述和管理任务的一种数据结构,包含了任务的名称、任务的状态、优先级、堆栈等信息,用结构体表示。创建任务时,系统会为任务分配相应的内存空间,用于保存任务的所有信息,比如栈指针、任务名称、任务状态等。FreeRTOS中任务控制块定义在task.c文件中,由结构体structtskTaskControlBlock表示,指向任务控制块的指针称为任务句柄,用TaskHandle_t表示。旧版本的结构体类型名为tskTCB,新版本的类型名为TCB_t。第9章定时器13.2.3任务的实现机制FreeRTOS中在tasks.c文件中定义了任务控制块的数据结构,结构如下:typedefstructtskTaskControlBlock {volatileStackType_t*pxTopOfStack; /*任务栈栈顶指针*/ListItem_txStateListItem;/*任务状态列表(就绪,阻塞,挂起)*/ListItem_txEventListItem;/*事件列表*/UBaseType_tuxPriority; /*任务优先级*/StackType_t*pxStack; /*任务栈起始地址*/charpcTaskName[configMAX_TASK_NAME_LEN];/*任务名称*/}tskTCB;typedeftskTCBTCB_t;

typedefstructtskTaskControlBlock*TaskHandle_t;//任务句柄第9章定时器13.2.3任务的实现机制任务句柄(TaskHandle)是RTOS中用于标识和引用任务的数据类型。多任务系统中,每个任务在创建时都会分配一个唯一的任务句柄,任务句柄是一个指向任务控制块的指针,用于指向一个任务,通过任务句柄可以对任务进行操作和管理,比如挂起任务、删除任务、恢复任务或查询任务的状态等,此外,任务句柄还可以用于任务的同步和通信机制,比如向任务发送信号量或消息。【小贴士】句柄(Handle),英文有操作、处理、控制等含义,作为一个名词时,是指某个中间媒介,通过这个中间媒介可以控制、操作某样东西。比如门把手(doorhandle),通过门把手可以去控制门;刀柄(knifehandle),通过刀柄可以使用刀。第9章定时器13.2.3任务的实现机制

任务的创建有两种方法,一种是使用动态创建,一种是使用静态创建。动态创建时,任务控制块和栈的内存是创建任务时动态分配的,任务删除时,内存可以释放;静态创建任务时,任务控制块和栈的内存需要事先定义好,是静态的内存,任务删除时,内存不能释放。第9章定时器13.2.4任务相关API函数FreeRTOS中关于任务和任务管理的API函数及经CMSIS-RTOSV2封装后的常用接口函数如表13-4所示。CMSIS-RTOSV2封装后的API函数位于CMSIS_RTOS_V2/cmsis_os2.c文件中。第9章定时器13.2.4任务相关API函数API类别FreeRTOS原生API函数CMSIS-RTOSV2封装后的API函数函数功能任务管理xTaskCreate()xTaskCreateStatic()osThreadNew()创建任务vTaskSuspend()osThreadSuspend()挂起任务vTaskDelete()osThreadExit()osThreadTerminate()删除任务pcTaskGetName()osThreadGetName()获取当前任务名称xTaskGetHandle()osThreadGetId()获取当前任务句柄eTaskGetState()osThreadGetState()获取任务的状态vTaskPrioritySet()osThreadSetPriority()设置任务的优先级uxTaskPriorityGet()osThreadGetPriority()获取任务的优先级

任务调度vTaskStartScheduler()osKernelStart()开启任务调度器vTaskDelay()osDelay()任务延时taskYIKLD()osThreadYield()任务进行上下文切换(1)任务创建函数osThreadNewosThreadId_tosThreadNew(osThreadFunc_tfunc,void*argument,constosThreadAttr_t*attr);函数功能:创建一个任务第1个参数:func,是一个函数指针,指向执行任务的函数。第2个参数:argument,是传递给任务的参数,不用时设为NULL。第3个参数:attr,任务的属性,声明了一个名为osThreadAttr_t的线程属性结构体类型,该结构体定义在cmsis_os2.h文件中,包含线程名称、栈的大小、线程优先级等成员变量。第9章定时器13.2.4任务相关API函数第9章定时器13.2.4任务相关API函数

返回值:创建成功,返回任务的句柄。osThreadId_t的类型定义在cmsis_os2.h文件中,定义如下所示。typedefvoid*osThreadId_t;//使用typedef定义了一个新的类型(变量)示例:osThreadId_tTaskCOMHandle;//定义任务句柄constosThreadAttr_tTaskCOM_attributes={//任务的属性.name="TaskCOM",//任务名称.priority=(osPriority_t)osPriorityLow,//设置任务优先级.stack_size=128*4,//任务栈的大小};//该属性由STM32CubeMX自动生成TaskCOMHandle=osThreadNew(StartTaskCOM,NULL,&TaskCOM_attributes);cmsis_os2.htypedefstruct{ constchar*name;//线程名称

uint32_tattr_bits;//属性位

void*cb_mem;//线程控制块内存空间指针

uint32_tcb_size;//提供给线程控制块内存的大小void*stack_mem;//栈内存空间指针

uint32_tstack_size;//栈内存大小

osPriority_tpriority;//初始化线程优先级(默认值:osPriorityNormal) TZ_ModuleId_ttz_module;//信任模块标识

uint32_treserved;//保留(必须为0)}osThreadAttr_t;注意:主线程的创建就是通过设置一个线程属性结构体来传递相关参数,若线程属性结构体为空,则试图创建一个默认的线程,从系统内存池中为其分配内存空间。任务删除函数osThreadTerminate函数原型:osStatus_tosThreadTerminate(osThreadId_tthread_id)函数功能:删除一个任务入口参数:thread_id,是一个函数指针,指向执行任务的函数。返回值: 删除成功,返回osOK,否则返回osError。osStatus_t为枚举类型,在cmsis_os2.h头文件中,定义了函数返回值的状态和错误代码,用于说明函数的执行情况第9章定时器13.2.4任务相关API函数第9章定时器13.2.4任务相关API函数typedefenum{ osOK =0,//成功 osError =-1,//失败 osErrorTimeout =-2,//等待超时失败 osErrorResource =-3,//资源错误 osErrorParameter =-4,//参数错误 osErrorNoMemory =-5,//没有足够的内存空间错误 osErrorISR=-6,//在中断调用时设置的超时时间非零 osStatusReserved=0x7FFFFFFF,//状态保留}osStatus_t;源码如下:示例:osThreadId_tTaskCOMHandle;//定义任务句柄constosThreadAttr_tTaskCOM_attributes={//任务的属性

.name="TaskCOM", //任务名称

.priority=(osPriority_t)osPriorityLow,//设置任务优先级

.stack_size=128*4,//任务栈的大小};//该属性由STM32CubeMX自动生成

osThreadTerminate(TaskCOMHandle);注意,CMSIS-RTOS提供了两个删除任务的函数osThreadTerminate和osThreadExit,根据传递的参数不同,osThreadTerminate用于删除另外一个任务,osThreadExit用于删除当前任务,其函数原型如下:voidosThreadExit(void);第9章定时器13.2.4任务相关API函数FreeRTOS任务的创建有相对固定的操作步骤,以SMT32CubeMX创建任务为例,包括以下4个步骤:(1)定义任务(或线程)的句柄

osThreadId_tTaskCOMHandle;//定义任务句柄constosThreadAttr_tTaskCOM_attributes={//任务的属性

.name="TaskCOM", //任务名称

.priority=(osPriority_t)osPriorityLow,//设置任务优先级

.stack_size=128*4,//任务栈的大小};//任务属性,该属性由STM32CubeMX自动生成第9章定时器13.2.5任务创建步骤(2)创建任务(或线程)TaskCOMHandle=osThreadNew(StartTaskCOM,NULL,&TaskCOM_attributes);(3)编写任务函数voidStartCOMTask(void*argument){/*USERCODEBEGINStartCOMTask*//*Infiniteloop*/ for(;;) { ......osDelay(1); }/*USERCODEENDStartCOMTask*/}第9章定时器13.2.5任务创建步骤(4)启动内核任务调度osKernelStart();实践目标:掌握FreeRTOS的任务创建方法。实践内容:基于STM32CubeMX+FreeRTOS创建两个任务:LED指示灯闪烁任务,每隔100ms进行状态转换;蜂鸣器任务,每隔100ms鸣叫。硬件设计引脚分配:LED指示灯连接在PE5引脚上,蜂鸣器连接在PB8引脚上,低电平有效。第9章定时器13.2.6FreeRTOS创建任务案例实践步骤:1.打开STM32CubeMX,新建一个工程,选择MCU。这里采用STM32F103ZET6。2.STM32CubeMX功能参数设置(1)RCC和时钟配置。在RCC选项中,设置HSE作为系统的外部时钟源,在HSE下列选项中选择“Crystal/CeramicResonator”选项。时钟系统配置。在“ClockConfiguration”选项卡进行时钟系统配置,这里采用HSE外部晶振,频率为8MHz,通过PLL的倍频使系统时钟SYSCLK的频率为72MHz。如图13-2所示。第9章定时器13.2.6FreeRTOS创建任务案例图13-2STM32时钟配置第9章定时器13.2.6FreeRTOS创建任务案例(2)设置HAL库的时间基准。在主界面点击SYS,在SYSModeandConfiguration配置页中,设置Debug为SerialWire;在TimebaseSource中选择TIM8作为HAL库的时间基准。这是因为FreeRTOS使用SysTick(嘀嗒定时器)作为基础时钟,而HAL库也使用SysTick作为基础时钟,两者产生冲突,这时就需要为HAL库的时间基准更换和配置另一个定时器供HAL库中的一些函数使用,比如HAL_Delay()延时函数等,如图13-3所示。第9章定时器13.2.6FreeRTOS创建任务案例图13-3HAL库时间基准设置(3)MCU引脚分配在GPIO选项卡中,设置PE5、PB8为GPIO_Output,UserLabel分别为LED、BEEP。(4)FreeRTOS任务创建。在主界面的Middleware中选中FREERTOS选项卡,在配置窗口上方的Mode栏中选择Interface为CMSIS_V2,这是ARM公司为运行于Cortex-M系列微控制器上的操作系统定义的CMSIS-RTOS接口,它提供了一种标准化的API接口。如图13-4所示。第9章定时器13.2.6FreeRTOS创建任务案例图13-4FreeRTOS创建任务第9章定时器13.2.6FreeRTOS创建任务案例CMSIS_V1适用于Cortex-M0/M0+/M3/M4/M7等系列微控制器,CMSIS_V2在CMSIS_V1的基础上进行了扩展,不仅适用于所有的Cortex-M系列微控制器,还支持Cortex-A5/A7/A9系列微处理器,这里建议选择最新的版本CMSIS_V2。FreeRTOS的Configuration页面中,有以下选项卡用于参数配置。详细如表13-5所示。第9章定时器13.2.6FreeRTOS创建任务案例表13-5FreeRTOS的配置选项FreeRTOS选项卡功能Configureparameters用于FreeRTOS参数配置,定义一些相关的宏定义在FreeRTOSConfig.h文件中。Includeparameters包含参数,用于配置相关函数的条件编译,定义在FreeRTOSConfig.h文件中。Advancedsettings高级设置TasksandQueues任务和队列TimersandSemaphores软件定时器和信号量Mutexes互斥量FreeRTOSHeapUsageFreeRTOS堆内存空间使用情况统计UserContants用户常数在配置窗口下方的Configuration栏中,选择TasksandQueues选项卡,双击defaultTask,在弹出的defaultTask任务窗口中,将其修改为LEDTask任务,设置参数如下:

·任务名称TaskName:LEDTask·优先级Priority:osPriorityNormal·任务函数EntryFunction:StartLEDTask

其余参数使用默认值。如图13-5所示。第9章定时器13.2.6FreeRTOS创建任务案例图13-5LEDTask任务设置FreeRTOS的Task选项卡中,相关参数的作用如下表13-6所示。第9章定时器13.2.6FreeRTOS创建任务案例Task选项卡参数功能TaskName任务名称,用户自定义Priority优先级,每个任务都需要设置一个优先级,FreeRTOS默认有56个等级,定义在FreeRTOSConfig.h文件中的宏定义configMAX_PRIORITIES,数字越小,优先级越低。默认优先级为osPriorityBelowNormal。StackSize任务栈空间大小。每个任务都需要分配一个栈空间,单位为字(Words,32位微控制器的1个Words=4个字节)。EntryFunction入口函数,也即任务函数。一个任务就是一个无限循环函数,这个参数为实现此任务的函数名称。CodeGenerationOption生成代码选项。有Default和Asweak两个选项,Asweak为__weak,即将任务函数定义为一个弱函数;Default为生成正常的任务函数。Parameter参数。为任务函数传递的参数,设置为NULL意味着所定义的任务函数没有参数。Allocation内存分配方式。有动态Dynamic和静态Static两种。BufferName缓冲器名称ControlBlockName任务控制块名称点击Add,创建BEEPTask任务,在BEEPTask任务窗口中,设置参数如下:

·任务名称TaskName:BEEPTask·优先级Priority:osPriorityLow·任务函数EntryFunction:StartBEEPTask其余参数使用默认值。如图13-6所示。第9章定时器13.2.6FreeRTOS创建任务案例图13-6BEEPTask任务设置(5)生成工程代码。在主界面中的ProjectManager选项卡中,单击Project选项页,设置工程名称、工程存放的位置,以及工程所使用的编译器,这里选择“MDK-ARM”。第9章定时器13.2.6FreeRTOS创建任务案例3.编写任务程序。利用STM32CubeMX自动生成工程代码后,在MDK-ARM中打开工程,先进行编译,确保程序代码无错误后,编写应用程序。(1)在freertos.c文件中,找到voidStartLEDTask(void*argument)函数,在for循环中输入以下程序代码:第9章定时器13.2.6FreeRTOS创建任务案例voidStartLEDTask(void*argument){/*USERCODEBEGINStartLEDTask*//*Infiniteloop*/for(;;){

HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin); osDelay(100);}/*USERCODEENDStartLEDTask*/}(2)在voidStartBEEPTask(void*argument)函数中输入以下程序代码。voidStartBEEPTask(void*argument){/*USERCODEBEGINStartBEEPTask*//*Infiniteloop*/for(;;){

HAL_GPIO_TogglePin(BEEP_GPIO_Port,BEEP_Pin);osDelay(100);}/*USERCODEENDStartBEEPTask*/}第9章定时器13.2.6FreeRTOS创建任务案例4.下载及验证实验现象:LED灯以每隔100ms进行闪烁,蜂鸣器以每隔100ms进行鸣叫。第9章定时器13.2.6FreeRTOS创建任务案例5.小结基于嵌入式操作系统进行应用程序开发,关键在于如何将系统功能进行模块化分解,将每个功能模块转化为可并发执行的任务,由操作系统执行多任务的调度,从而实现应用功能。13.3信号量信号量概念13.3.1信号量实现机制13.3.2信号量应用步骤13.3.3信号量应用案例13.3.4

信号量(Semaphore)是实现任务与任务之间、任务与中断之间同步问题的机制,用于对共享资源的有序访问,与裸机编程中的标志(flag)类似。信号,现实中常用旗或灯发出的识别信号,如海上求救的SOS灯光信号,铁路上使用的臂板信号机就是利用不同颜色的灯光来显示指挥行车命令的,因此使用信号的目的是实现通知的作用。量,数量,表示资源可以利用和使用的数量。第9章定时器13.3.1信号量概念FreeRTOS中信号量主要有二值信号量和计数信号量。1.二值信号量(BinarySemphores)当“量”只有0和1两种取值时,就是二值信号量,取值为0时表示没有资源可用,取值为1时表示有可使用的资源。创建时其初始值一般设置为0,即没有资源可供使用,多任务系统中一个任务释放信号量,另一个任务通过获取信号量达到任务之间的同步或互斥的目的。第9章定时器13.3.1信号量概念2.计数信号量(CountingSemphores)当“量”的数量没有限制时,用于计数的,就是计数信号量,其初值一般设定为可用资源的数量。计数信号量用于实现资源管理,比如停车场的停车位、图书馆的座位等都是计数信号量的典型应用场景。多任务系统中,一个任务想要获取资源的使用权,需要先获取计数信号量,获取成功后,计数信号量的值减一,当其值减到0时表示没有资源了,想要获取该资源的任务就由运行态转到阻塞状态,等待资源可用,此时就需要一个任务在使用完资源时释放信号量,释放成功,计数信号量的值加一。第9章定时器13.3.1信号量概念FreeRTOS中信号量实现机制是基于消息队列实现的,信号量是消息队列中的消息只有0和1两种数据的特殊形式,二值信号量是长度为1的队列,计数信号量就是长度大于1的队列。FreeRTOS通过消息队列控制块管理信号量,消息队列控制块是一种数据结构,用结构体表示,定义在event_groups.c文件中,详细介绍请见本书13.5一节。FreeRTOS中用于创建、释放、删除信号量的函数定义在semphr.c文件中,经CMSIS-V2封装后的信号量函数在cmsis_os2.c文件中实现。第9章定时器13.3.2信号量实现机制第9章定时器13.3.2信号量实现机制表13-7FreeRTOS的API函数与CMSIS-RTOS封装后的API函数对比功能分类FreeRTOS原生API函数CMSIS-RTOSV2封装后的API函数说明创建xSemaphoreCreateBinary(二值信号量)osSemaphoreNew动态创建并初始化信号量对象xSemaphoreCreateCounting(计数信号量)获取uxSemaphoreGetCountosSemaphoreGetCount获取当前信号量的计数值xSemaphoreTakeosSemaphoreAcquire获取信号量xSemaphoreTakeFromISR在中断服务程序(ISR)中获取信号量释放xSemaphoreGiveosSemaphoreRelease释放信号量xSemaphoreGiveFromISR在中断服务程序(ISR)中释放信号量删除vSemaphoreDeleteosSemaphoreDelete删除一个信号量第9章定时器13.3.2信号量实现机制(1)信号量创建函数osSemaphoreNewosSemaphoreId_tosSemaphoreNew(uint32_tmax_count,uint32_tinitial_count,constosSemaphoreAttr_t*attr);函数功能:创建一个信号量。第1个参数:max_count,信号量的最大计数值。其值为1时,表示二值信号量。第2个参数:initial_count,信号量初值。第3个参数:attr,指向信号量属性的指针,默认值为NULL。声明了一个名为osSemaphoreAttr_t的结构体,该结构体包括信号量的名称、大小等成员变量,定义在cmsis_os2.h文件中,如下所示。第9章定时器13.3.2信号量实现机制typedefstruct{constchar*name;//信号量的名称uint32_tattr_bits;//信号量的属性位void*cb_mem;//为信号量控制块分配的内存首地址uint32_tcb_size;//为信号量控制块分配的内存大小}osSemaphoreAttr_t;返回值:创建成功,返回信号量的句柄,用于标识信号量。 创建失败,返回NULL。示例:osSemaphoreId_t BinarySem01Handle;//定义二值信号量的句柄ConstosSemaphoreAttr_tBinarySem01_attributes={.name="BinarySem01",};//该属性由STM32CubeMX自动生成BinarySem01Handle=osSemaphoreNew(1,1,&BinarySem01_attributes);第9章定时器13.3.2信号量实现机制(2)信号量获取函数osSemaphoreAcquireosStatus_tosSemaphoreAcquire(osSemaphoreId_tsemaphore_id,uint32_ttimeout);函数功能:获取一个信号量。第1个参数:semaphore_id,信号量的句柄。第2个参数:timeout,等待信号量可用资源时的超时等待时间,以时钟节拍为单位。该参数为32位,类型为uint32_t,取值范围为0~osWaitForever,设置为0时,该函数立刻返回;设置为osWaitForever,则会一直等待到信号量有可用资源。返回值:获取成功,返回osOK;否则返回osError。第9章定时器13.3.2信号量实现机制在cmsis_os2.h头文件中,定义了函数返回值的状态和错误代码,用于说明函数的执行情况。typedefenum{osOK=0,//成功osError=-1,//失败osErrorTimeout=-2,//等待超时失败,在给定的超时时间内无法获取消息osErrorResource=-3,//无法从信号量中获取信息错误osErrorParameter=-4,//mq_id参数错误osErrorNoMemory=-5,//没有足够的内存空间错误osErrorISR=-6,//在中断调用时设置的超时时间非零osStatusReserved=0x7FFFFFFF,//状态保留}osStatus_t;示例:osSemaphoreAcquire(BinarySem01Handle,osWaitForever);第9章定时器13.3.2信号量实现机制(3)信号量释放函数osStatus_tosSemaphoreRelease(osSemaphoreId_tsemaphore_id);函数功能:释放一个信号量。入口参数:semaphore_id,信号量的句柄。返回值:释放成功,返回osOK;否则返回错误状态代码。示例:osSemaphoreRelease(BinarySem01Handle);第9章定时器13.3.3信号量应用步骤FreeRTOS信号量的应用有相对固定的操作步骤,以SMT32CubeMX创建信号量为例,包括以下4个步骤:(1)定义信号量句柄

osSemaphoreId_tBinarySem01Handle;//定义信号量句柄constosSemaphoreAttr_tBinarySem01_attributes={.name="BinarySem01"};//信号量属性,由SMT32CubeMX自动生成(2)创建信号量

BinarySem01Handle=osSemaphoreNew(1,0,&BinarySem01_attributes);(3)释放信号量

osSemaphoreAcquire(BinarySem01Handle,osWaitForever);(4)获取信号量

osSemaphoreRelease(BinarySem01Handle);第9章定时器13.3.4信号量应用案例一、功能描述本案例通过二值信号量实现任务与任务之间的同步。通过按键任务利用两个二值信号量分别触发蜂鸣器任务和串口打印任务。按键按一次,蜂鸣器响一次,然后通过串口打印按键按下的次数。二、硬件设计按键KEY1连接在STM32的PE3引脚上,低电平有效;蜂鸣器BEEP连接在STM32的PB8引脚上,低电平有效,通过PC机上的串口调试助手显示信息。第9章定时器13.3.4信号量应用案例三、软件设计本实例需要设计三个任务,分别是:任务1:按键任务,命名为KEYTask,优先级为2,用于释放任务与任务之间同步的二值信号量BinarySem_a和BinarySem_b。任务2:蜂鸣器任务,命名为BEEPTask,优先级为1,通过二值信号量BinarySem_a实现按键任务与蜂鸣器任务之间的同步。任务3:串口打印任务,命名为COMTask,优先级为3,通过二值信号量BinarySem_b实现按键任务与串口打印任务之间的同步。本实例还需要定义两个二值信号量,分别命名为BinarySem_a和BinarySem_b。第9章定时器13.3.4信号量应用案例实施步骤如下。1.STM32CubeMX参数配置。打开STM32CubeMX,新建一个工程,这里采用STM32F103ZET6,读者可根据自己的开发板进行设置。(1)配置系统时钟和FreeRTOS的时间基准在RCC选项中,设置HSE作为系统的外部时钟源,在HSE下列选项中选择“Crystal/CeramicResonator”选项。时钟系统配置。在“ClockConfiguration”选项卡进行时钟系统配置,这里采用HSE外部晶振,频率为8MHz,通过PLL的倍频使系统时钟SYSCLK的频率为72MHz。第9章定时器13.3.4信号量应用案例修改HAL库的时间基准,由于FreeRTOS使用SysTick系统嘀嗒定时器产生所需的时钟节拍,而HAL库默认也使用SysTick产生时间基准,如HAL的延时函数HAL_Delay(),所以为避免产生冲突,需要修改HAL库的时间基准,将其时间基准改为其他定时器产生。这里选择TIM8作为HAL库的时间基准。在SYS配置页面中,选择TimebaseSource(时基源)为TIM8,如图13-6所示。图13-6修改HAL库的时间基准第9章定时器13.3.4信号量应用案例(2)MCU引脚设置在GPIO选项卡中,设置PE3为GPIO_Input模式,UserLabel为KEY1;PB8为GPIO_Output模式,UserLabel设置为BEEP,设置USART1的Mode为Asynchronous,基本参数设置保持默认即可。(3)FreeRTOS任务创建。在主界面的Middleware中选中FREERTOS选项卡,在配置窗口上方的Mode栏中选择Interface为CMSIS_V2。在“TasksandQueues”中将系统默认的任务defaultTask中的参数进行修改,参数如下:·任务名称TaskName:KEYTask·优先级Priority:osPriorityNormal·任务函数EntryFunction:StartKEYTask图13-7KEYTask任务创建第9章定时器13.3.4信号量应用案例然后在配置窗口下方的Configuration栏中,选择TasksandQueues选项卡,点击Add,新建两个任务:BEEPTask和COMTask。在LED1Task任务窗口中,设置参数如下:·任务名称TaskName:BEEPTask·优先级Priority:osPriorityLow·任务函数EntryFunction:StartBEEPTask图13-8BEEPTask任务创建第9章定时器13.3.4信号量应用案例在COMTask任务窗口中,设置参数如下:·任务名称TaskName:COMTask·优先级Priority:osPriorityLow2·任务函数EntryFunction:StartCOMTask其余参数使用默认值。如图13-9所示。图13-9COMTask任务创建第9章定时器13.3.4信号量应用案例(4)创建二值信号量在FREERTOS配置窗口下方的Configuration栏中,选择TimersandSemaphores选项卡,在BinarySemaphores选项页中点击Add,新建一

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论