STM32CubeMX+FreeRTOS学习[7] 软定时器(Lu)
- 格式:pdf
- 大小:762.47 KB
- 文档页数:7
STM32Cube官方例程学习指南STM32CubeMX是ST官方提供的一个代码生成工具。
使用该工具,通过图形化的配置方法,就能快速生成STM32的各种片上外设的初始化代码。
CubeMX生成的软件工程使用HAL库,HAL库是ST 以后主推的外设驱动库。
另外CubeMX还提供了FATFS、FreeRTOS、LWIP、USB库等中间件的支持,配置之后生成软件工程,工程文件就包含了相应代码。
本文档以STM32F4系列为例,简要地分析官方提供的Cube例程。
希望能够帮助CubeMX初学者快速掌握STM32的常用外设使用方法。
文档不求全面,只讲常用的外设,对不常用的只进行概况性地描述。
同时,文档只对例程进行直接分析,不对其他文件进行详述。
第一部分准备工作首先是下载STM32CubeF4支持包,可以到与非网ST社区搜索STM32CubeF4,然后下载当前版本已经更新到V1.13.0。
点击附件中的STM32CubeF4,转到下载链接地址。
附件大小300M左右。
本人当前使用的是V1.9.0版本的,例程相差不大,后面就用V1.9.0版本的例程进行分析。
下载后解压,得到如下图的文件,其中例程放在Projects文件夹中:打开Projects文件夹,可以看到前12个文件夹分别官方提供的12款评估板,后面我们仅以STM324xG_EVAL评估板的例程为讲解内容。
STM324xG_EVAL文件夹中,Examples文件夹存放的就是片上外设的使用例程。
(Applications文件夹是STM324xG_EVAL相关的一些高级应用例程,如FreeRTOS、FatFs、LwIP、USB等,有一定基础之后可以学习这里面的内容。
本文不作分析。
)Examples文件夹提供了27个外设对应文件夹,每个文件夹包含若干个例程,后面将对常用的外设例程(不是全部)进行简要分析。
第二部分例程分析下面将挑选常用外设的例程进行分析,顺序是从简单的到复杂的。
stm32定时器工作原理STM32定时器工作原理简介•STM32定时器是一种用于生成定时中断和脉冲输出的功能强大的设备。
•本文将从基本概念开始,逐步深入,解释STM32定时器的工作原理。
基本概念•定时器:STM32芯片中的一个硬件模块,用于计时和触发中断。
•定时器时基:定时器根据时钟频率进行计数,可以由内部或外部时钟源提供。
•定时器计数器:用于存储当前定时器计数值的寄存器。
•定时器自动重载寄存器(ARR):设定定时器计数器自动重新装载的值,决定定时器溢出周期。
•定时器预分频器:用于设置定时器计数器时钟频率的系数。
STM32定时器工作模式基本工作模式•定时器工作模式可通过控制寄存器设置,常见的模式有定时器模式、输入捕获模式和输出比较模式。
•定时器模式:定时器按照一定的时间间隔生成中断请求。
•输入捕获模式:用于测量外部信号的脉冲宽度、周期等参数。
•输出比较模式:根据比较值产生不同的输出信号。
高级工作模式•高级工作模式是指在基本工作模式的基础上,添加了更多功能。
•单脉冲模式:定时器只工作一次,用于产生单个定时脉冲。
•PWM模式:通过控制输出比较寄存器和定时器计数器的值,实现脉宽调制。
•编码器模式:用于读取和解码外部编码器信号,实现旋转方向和计数功能。
定时器配置计时器时基配置•配置定时器的时钟源和预分频系数,确定计时器的时基。
•定时器的时钟源可以选择内部时钟源(通常为系统时钟)或外部时钟源。
•根据需要选择合适的预分频系数,以满足计时器的计数精度和计数范围要求。
定时器模式配置•配置定时器的工作模式和自动重载寄存器的值,决定定时器的溢出周期。
•根据需要选择定时器模式和设定自动重载寄存器的值,以实现所需的定时功能。
中断配置•配置定时器的中断使能和中断优先级,使得定时器能够触发中断请求。
•根据需要选择定时器相关的中断使能位,并设置相应的中断优先级。
定时器工作流程1.配置定时器的时钟源和预分频系数。
2.配置定时器的工作模式和自动重载寄存器的值。
freertos 定时器用法【最新版】目录1.FreeRTOS 简介2.FreeRTOS 定时器概述3.FreeRTOS 定时器的使用方法4.FreeRTOS 定时器的应用实例5.总结正文1.FreeRTOS 简介FreeRTOS 是一款开源实时操作系统,适用于各种嵌入式系统。
它的设计目标是为微控制器提供可靠的任务调度、时间管理、内存管理和通信功能。
FreeRTOS 具有可扩展性强、稳定性高以及实时性能优越等特点,被广泛应用于各种实时控制系统中。
2.FreeRTOS 定时器概述FreeRTOS 定时器是 FreeRTOS 内核提供的一种时间管理功能,它可以帮助开发者在实时系统中实现精确的时间控制。
FreeRTOS 定时器可以按照设定的时间间隔产生中断,也可以在特定的时间点产生中断,从而实现对任务的精确调度。
3.FreeRTOS 定时器的使用方法要使用 FreeRTOS 定时器,需要先初始化定时器。
初始化定时器时,需要配置定时器的工作模式、时间间隔和优先级等参数。
初始化完成后,可以通过调用 FreeRTOS 的定时器 API 来启动或停止定时器。
以下是一个简单的使用示例:```cvoid init_timer() {// 配置定时器参数xTimerCreate( "my_timer", "0", 1000 ); // 创建一个名为"my_timer"的定时器,时间间隔为 1000ms,优先级为 0 xTimerStart( "my_timer" ); // 启动定时器}void timer_isr() {// 定时器中断服务例程// 在这里添加你的定时器中断处理代码}void app_main() {init_timer(); // 初始化定时器while ( 1 ) {// 循环等待定时器中断}}```4.FreeRTOS 定时器的应用实例FreeRTOS 定时器在实际应用中可以用于实现各种定时任务,例如:系统滴答定时器、任务周期性执行、实时数据采集等。
freertos 定时器用法摘要:1.FreeRTOS 定时器简介2.定时器操作的基本步骤3.定时器应用实例正文:FreeRTOS 定时器用法FreeRTOS 是一款开源实时操作系统,提供了丰富的功能以支持各种实时应用。
在FreeRTOS 中,定时器是一个重要的组件,用于实现延时、调度等功能。
本文将详细介绍FreeRTOS 定时器的用法。
1.FreeRTOS 定时器简介FreeRTOS 定时器主要包括系统定时器、软件定时器和周期性定时器。
系统定时器由操作系统内核管理,用于操作系统任务调度等;软件定时器由用户进程创建和管理,用户可以自定义定时器行为;周期性定时器则是定时器的一种特殊形式,按照指定的时间间隔产生事件。
2.定时器操作的基本步骤(1) 配置定时器:在使用定时器前,需要对其进行初始化配置,包括设置定时器超时值、选择定时器类型等。
(2) 启动定时器:配置完成后,需要调用相应函数启动定时器。
对于系统定时器和软件定时器,可以使用`xTimerStart()`函数启动;对于周期性定时器,在创建时设置` periodic`标志,系统将自动启动定时器。
(3) 处理定时器事件:定时器超时后,会触发相应的事件。
对于系统定时器,会触发系统任务调度;对于软件定时器,会触发用户进程中的回调函数;对于周期性定时器,会按照指定的时间间隔触发事件。
(4) 停止定时器:在定时器不再需要时,需要调用相应函数停止定时器。
对于系统定时器和软件定时器,可以使用`xTimerStop()`函数停止;对于周期性定时器,只需删除定时器即可。
3.定时器应用实例以下是一个简单的FreeRTOS 定时器应用实例,实现一个任务每秒打印一次“Hello, FreeRTOS!”。
```c#include "FreeRTOS.h"#include "task.h"#include "timers.h"#define TIMER_INTERVAL (1000 / portTICK_PERIOD_MS)void vPrintTask(void *pvParameters){for (;;){printf("Hello, FreeRTOS!");vTaskDelay(TIMER_INTERVAL);}}int main(void){// 创建并启动定时器xTimerCreate("PrintTaskTimer",pdMS_TO_TICKS(TIMER_INTERVAL), pdTRUE, NULL, vPrintTask);xTimerStart(xTimerGetHandleFromName("PrintTaskTimer"), NULL);// 创建并启动一个持续运行的任务xTaskCreate(vPrintTask, "PrintTask", 256, NULL, 1, NULL);// 开始调度任务vTaskStartScheduler();// 程序不会执行到这里return 0;}```在这个例子中,我们创建了一个软件定时器,每隔1 秒触发一次vPrintTask 任务。
freertos 定时器用法Freertos 是一款开源的实时操作系统(Real-Time Operating System,简称RTOS),广泛应用于嵌入式系统开发。
在Freertos 中,定时器(Timer)是一个重要的组件,可以用于实现任务调度、延时、周期性事件等功能。
本文将详细介绍Freertos 定时器的使用方法,以及实战应用案例。
一、Freertos 定时器简介Freertos 定时器基于硬件定时器实现,支持多种定时器模式,包括简单定时、计数器、脉冲宽度调制(PWM)等。
定时器可以与硬件定时器接口,也可以与软件定时器接口。
在Freertos 中,定时器分为全局定时器和任务定时器两类。
二、Freertos 定时器使用方法1.创建定时器任务在使用定时器之前,首先需要创建一个定时器任务。
可以通过以下步骤创建:- 调用`xTimerCreate()` 函数创建一个定时器。
- 设置定时器名称,便于调试。
- 配置定时器,包括定时器周期、触发方式等。
- 将定时器任务添加到任务列表中。
2.启动定时器创建好定时器任务后,可以通过以下步骤启动定时器:- 调用`xTimerStart()` 函数启动定时器。
- 调用`xTimerStop()` 函数停止定时器。
3.配置定时器在创建定时器时,可以设置以下参数:- 定时器周期:定时器触发的时间间隔。
- 定时器溢出处理:当定时器计数器溢出时,触发何种事件。
- 定时器精度:定时器计数器的精度,影响定时器准确性。
4.处理定时器事件当定时器触发事件时,可以编写相应的事件处理函数。
在事件处理函数中,可以执行以下操作:- 更新任务状态:如更改任务优先级、切换任务等。
- 执行其他操作:如发送消息、更新显示等。
三、实战应用案例以下是一个使用Freertos 定时器实现LED 闪烁的案例:1.创建一个LED 闪烁任务。
2.配置定时器,设置定时器周期为1000ms。
3.启动定时器。
freertos 定时器用法一、简介Freertos是一种免费的嵌入式操作系统,用于开发实时和多线程的应用程序。
它提供了一组常用的硬件抽象层和任务管理工具,使得开发者能够更高效地开发嵌入式系统。
在Freertos中,定时器是一种常用的工具,用于在特定的时间间隔或延迟后执行某些任务。
二、定时器的类型Freertos提供了两种类型的定时器:1. 滴答定时器(Ticking Timer):它会按照一定的时间间隔自动增加计时值,当计时值达到预设值时,会触发一个中断或回调函数。
这种定时器适用于简单的定时需求,不需要手动重置。
2. 节拍定时器(也叫计数定时器):它按照预设的计数值计数,当计数达到预设值时,会触发一个中断或回调函数。
这种定时器适用于需要精确计时和更复杂的定时需求的情况。
三、定时器的配置和使用配置定时器需要以下步骤:1. 包含所需的头文件:包括freertos.h和timers.h。
2. 定义定时器的属性:包括计时周期、中断优先级、中断处理函数等。
3. 创建定时器实例:使用Timers_Init()函数初始化定时器,并使用Timers_Create()函数创建定时器实例。
4. 配置中断优先级:根据需要配置中断优先级,以确保定时器中断能够正常触发。
使用定时器时,需要设置相应的中断处理函数或回调函数,以在定时器触发时执行相应的操作。
可以在中断处理函数中执行需要定时执行的任务,例如更新UI、发送数据等。
四、定时器的注意事项在使用定时器时,需要注意以下几点:1. 定时器的精度和延迟时间可能受到硬件和操作系统的限制,需要根据实际情况进行调整。
2. 定时器的中断处理函数或回调函数中不要执行过于耗时的操作,以免影响系统的响应速度。
3. 定时器的使用需要与其他任务和中断协调,避免相互干扰。
4. 定时器的配置和使用需要仔细测试和调试,确保其符合实际需求。
五、示例代码以下是一个简单的Freertos定时器示例代码,用于在每两秒触发一次中断,并在中断处理函数中输出当前时间:```c#include <freertos/FreeRTOS.h>#include <freertos/timers.h>#include <stdio.h>void app_main() {// 定义定时器属性TimerHandle_t timer;TimerSettings_t timerSettings;memset(&timerSettings, 0, sizeof(TimerSettings_t));timerSettings.period = configTICK_RATE_HZ / 2; // 设置周期为两秒pare = configTICK_COUNT; // 比较值为当前时间戳减去上次计数值,用于实现滴答计数器效果timerSettings.flags = TIMER_PERIODIC |TIMER_INTERRUPTIBLE; // 设置为滴答定时器并设置中断属性 // 其他配置项...// 初始化定时器并创建实例Timers_Init();timer = Timers_Create(timerSettings); // 创建滴答定时器实例// 其他初始化工作...// 进入循环等待中断触发,不占用主线程资源while (1) { }}void timer_handler(TimerHandle_t timer) {// 在这里执行需要定时执行的任务,例如输出当前时间等操作。
STM32定时器的使用流程1. 简介STM32定时器是STM32系列微控制器中重要的外设之一。
定时器可以用于生成特定的定时器事件,实现计时、测量时间间隔、产生PWM信号等功能。
本文将介绍STM32定时器的使用流程。
2. STM32定时器的基本工作原理STM32定时器通常由一个或多个计数器和若干个通道组成。
计数器用于计算时间的流逝,而通道用于控制输出。
计数器的计数范围和分辨率可以根据需求进行配置。
通常情况下,定时器通过外部时钟源进行计数,也可以使用内部时钟源。
3. STM32定时器的使用流程使用STM32定时器通常需要以下步骤:3.1 初始化定时器在使用定时器之前,需要初始化定时器的相关参数,包括计数器的计数范围、分频系数等。
通常可以通过寄存器的设置来完成初始化工作。
使用HAL库的话,可以使用HAL_TIM_Base_Init()函数进行初始化。
3.2 配置定时器的工作模式定时器可以根据需求配置为不同的工作模式,常见的模式包括单脉冲模式、连续模式、PWM输出模式等。
可以使用TIM_CR1、TIM_CR2等寄存器进行配置。
使用HAL库的话,可以使用相应的函数进行配置。
3.3 配置定时器的中断和DMA定时器可以配置中断和DMA功能,在特定的条件下触发相应的中断或DMA请求。
可以使用TIM_DIER寄存器进行配置。
使用HAL库的话,可以使用相应的函数进行配置。
3.4 启动定时器在配置完成后,需要启动定时器开始计数。
可以使用TIM_CR1寄存器进行配置。
使用HAL库的话,可以使用相应的函数进行配置。
3.5 处理定时器中断如果配置了定时器中断,当定时器达到设定的计数值时,会触发中断。
在中断服务函数中可以根据需求进行相应的处理。
3.6 设置定时器输出如果配置了定时器的通道输出模式,可以在定时器计数到一定值时,通过通道输出相应的信号。
可以使用TIM_CCR1、TIM_CCR2等寄存器进行配置。
3.7 停止定时器如果需要停止定时器的计数,可以使用TIM_CR1寄存器进行配置。
STM32定时器的基础知识:STM32定时器是STM32系列微控制器中的一个重要的硬件模块,它主要用于产生定时中断和PWM信号。
以下是关于STM32定时器的基础知识:1.定时器分类 STM32定时器可以分为通用定时器和高级定时器两类。
其中,通用定时器包括TIM2、TIM3、TIM4和TIM5,它们都具有定时器/计数器、PWM输出和输入捕获等功能;高级定时器包括TIM1、TIM8、TIM9、TIM10、TIM11,它们除了拥有通用定时器的功能外,还具有编码器接口、高级PWM输出、同步功能等。
2.定时器模式 STM32定时器有四种模式:向上计数模式、向下计数模式、向上/向下计数模式和中央对齐模式。
其中,向上计数模式是最常用的模式,它从0计数到设定值后产生中断或触发事件。
3.定时器时钟 STM32定时器时钟可以从内部时钟源(如HSI、HSI14、HSI48、LSI、LSE等)或外部时钟源(如HSE、PLL等)中选择。
时钟频率的选择会影响定时器的分辨率和计数速度。
4.定时器中断 STM32定时器可以通过产生中断来实现定时器功能。
可以设置定时器的计数值和预分频值来控制中断的触发时间。
在中断服务程序中可以执行需要定时的任务,如更新LCD显示、采集传感器数据等。
5.定时器PWM输出 STM32定时器可以产生PWM信号,通过调节占空比和周期可以实现不同的输出波形。
PWM输出可以应用于电机控制、LED灯控制、音频合成等场合。
6.定时器输入捕获 STM32定时器还可以用于输入捕获,即通过输入引脚捕获外部信号的边沿,并将捕获的时间戳保存在定时器的寄存器中。
输入捕获常用于测量脉冲宽度、频率等应用。
7.定时器输出比较 STM32定时器还可以进行输出比较,即将定时器的计数值与设定的比较值进行比较,当计数值等于比较值时触发中断或输出事件。
输出比较常用于控制LED、蜂鸣器、继电器等应用。
8.定时器互联 STM32定时器可以通过互联功能进行互联,即将多个定时器连接在一起形成一个大的定时器,以提高计数范围和精度。
[FreeRTOS⼊门]1.CubeMX中FreeRTOS配置参数及理解1.有关优先级 1.1 Configuration --> FreeRTOSMAX_PRIORITIES 设置任务优先级的数量:配置应⽤程序有效的优先级数⽬。
任何数量的任务都可以共享⼀个优先级,使⽤协程可以单独的给与它们优先权。
见MAX_CO_ROUTINE_PRIORITIES。
在RTOS内核中,每个有效优先级都会消耗⼀定量的RAM,因此这个值不要超过你的应⽤实际需要的优先级数⽬。
每⼀个任务都会被分配⼀个优先级,优先级值从0~(MAX_PRIORITIES - 1)之间。
低优先级数表⽰低优先级任务。
空闲任务的优先级为0(PriorityIdle),因此它是最低优先级任务。
FreeRTOS调度器将确保处于就绪状态(Ready)或运⾏状态(Running)的⾼优先级任务⽐同样处于就绪状态的低优先级任务优先获取处理器时间。
换句话说,处于运⾏状态的任务永远是⾼优先级任务。
处于就绪状态的相同优先级任务使⽤时间⽚调度机制共享处理器时间。
Interrupt nesting behaviour configuration 断⾔配置LIBRARY_LOWEST_INTERRUPT_PRIORITY此宏定义是⽤来配置 FreeRTOS中⽤到的SysTick中断和PendSV中断的优先级reeRTOSreeRTOSreeRTOSreeRTOS ⽤到的 SysTSysT ick 中断和 PendSV中断的优先级。
在 NVIC分组设置为4的情况下,此宏定义的范围就是 0-15 ,即专门配置抢占优先级。
这⾥即专门配置抢占优先级。
这⾥配置为了 15,即 SysTick和 PendSV 都配配置为了最低优先级,实际项⽬中也建议配置为最低优先级。
LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY定义了受FreeRTOS管理的最⾼优先级中断。
STM32定时器定时时间配置总结STM32系列微控制器内置了多个定时器模块,它们可以用于各种定时功能,如延时、周期性触发、脉冲计数等。
在使用STM32定时器之前,我们需要进行定时时间配置,本文将总结一下STM32定时器定时时间配置的相关知识,包括定时器工作模式、定时器时钟源选择、定时器时钟分频、定时器计数器重载值以及定时器中断配置等内容。
首先,我们需要选择定时器的工作模式。
STM32定时器支持多种工作模式,包括基本定时器模式、高级定时器模式、输入捕获模式和输出比较模式等。
基本定时器模式适用于简单的定时和延时操作,输入捕获模式适用于捕获外部事件的时间参数,输出比较模式适用于产生精确的PWM波形。
根据具体的应用需求,选择合适的工作模式。
其次,我们需要选择定时器的时钟源。
STM32定时器的时钟源可以选择内部时钟源(如系统时钟、HCLK等)或外部时钟源(如外部晶体)。
内部时钟源的稳定性较差,适用于简单的定时操作,而外部时钟源的稳定性较好,适用于要求较高的定时操作。
然后,我们需要选择定时器的时钟分频系数。
定时器的时钟分频系数决定了定时器的时钟频率,从而影响了定时器的计数速度。
我们可以通过改变时钟分频系数来调整定时器的计数速度,从而实现不同的定时时间。
时钟分频系数的选择需要考虑定时器的最大计数周期和所需的定时精度。
接着,我们需要配置定时器的计数器重载值。
定时器的计数器从0开始计数,当计数器达到重载值时,定时器将重新开始计数。
通过改变计数器重载值,可以实现不同的定时时间。
计数器重载值的选择需要考虑定时器的时钟频率和所需的定时时间。
最后,我们需要配置定时器的中断。
定时器中断可以在定时器计数达到重载值时触发,用于通知CPU定时器已经计数完成。
在定时器中断中,我们可以执行相应的中断服务程序,比如改变一些IO口的状态,实现定时操作。
通过配置定时器的中断使能和中断优先级,可以实现不同的中断操作。
需要注意的是,不同型号的STM32微控制器的定时器模块可能略有不同,具体的配置方法和寄存器设置也可能不同,请参考相应的数据手册和参考手册进行具体操作。
stm32 timer 用法(最新版)目录1.STM32 定时器概述2.STM32 定时器的工作原理3.STM32 定时器的使用步骤4.STM32 定时器的应用实例5.总结正文【1.STM32 定时器概述】STM32 定时器,全称为 STM32 定时器库,是一款基于 ARM Cortex-M 内核的嵌入式系统开发平台。
定时器库提供了多种定时器模式,可以满足不同应用场景的需求,例如:PWM 输出、输入捕捉、输出比较等。
【2.STM32 定时器的工作原理】STM32 定时器主要由三个部分组成:时钟源、计数器和比较器。
时钟源提供定时器运行所需的时钟信号,计数器用于记录时钟信号的脉冲数,比较器则根据计数器的值来触发定时器中断或输出。
【3.STM32 定时器的使用步骤】(1)配置定时器:根据应用需求,配置定时器的工作模式、时钟源、计数器范围等参数。
(2)初始化定时器:初始化定时器,使定时器处于待用状态。
(3)启动定时器:使能定时器,开始计时。
(4)配置中断:根据需要,配置定时器中断,以便在中断触发时执行相应操作。
(5)使用定时器:在定时器运行过程中,根据实际需求使用定时器提供的功能,如 PWM 输出、输入捕捉等。
(6)停止定时器:在定时器不再需要时,停止定时器。
【4.STM32 定时器的应用实例】以 PWM 输出为例,首先配置定时器相关参数,然后初始化定时器,接着启动定时器。
在定时器运行过程中,可以通过设置 PWM 波形、占空比等参数,实现对 PWM 输出的控制。
当定时器中断触发时,可以执行相应的中断处理函数,以完成实际控制任务。
【5.总结】STM32 定时器库为嵌入式系统开发提供了强大的定时器功能,通过简单易用的 API 接口,可以方便地实现各种定时器应用。
STM32CubeIDE+FreeRTOS计数信号量实验使⽤计数信号量写个模拟停车场的实验,使⽤开发板板载的两个按键,KEY1按下表⽰停车,KEY2按下表⽰取车,初始有10个停车位。
新建⼯程RTOS_CountSem,配置HCLK,使⽤内部晶振,频率为180MHZ(根据板⼦设置)配置两个按键KEY1和KEY2将SYS中时基源(Timebase Source)改为除SysTick之外的任意定时器即可,如:配置FreeRTOS,使⽤CMSIS_V1,先使能USE_COUNTING_SEMAPHORES定义两个任务,⼀个是myTask_StopCar,负责检测KEY1是否按下,如果按下,且当前有剩余车位,将车位减1。
另⼀个是myTask_GetCar,负责检测KEY2是否按下,如果按下,且当前车库有车,将车位加1.添加⼀个Counting SemaphoresCtrl + S⽣成代码修改代码,1,在main.h中添加/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#include "stdio.h"/* USER CODE END Includes */2,在mian.c中添加/* USER CODE BEGIN PFP */int _write(int file , char *ptr,int len){int i = 0;for(i = 0;i<len;i++)ITM_SendChar((*ptr++));return len;}/* USER CODE END PFP */........./* USER CODE BEGIN 2 */printf("starting...\n");/* USER CODE END 2 */3,在main.c中修改3个任务⼊⼝函数的内容/* USER CODE BEGIN Header_StartDefaultTask *//*** @brief Function implementing the defaultTask thread.* @param argument: Not used* @retval None*//* USER CODE END Header_StartDefaultTask */void StartDefaultTask(void const * argument){/* USER CODE BEGIN 5 */int timeCount = 1;/* Infinite loop */for(;;){printf("DefaultTask----time %d\n",timeCount++);osDelay(1000);}/* USER CODE END 5 */}/* USER CODE BEGIN Header_StartTaskStopCar *//*** @brief Function implementing the myTask_StopCar thread.* @param argument: Not used* @retval None*//* USER CODE END Header_StartTaskStopCar */void StartTaskStopCar(void const * argument){/* USER CODE BEGIN StartTaskStopCar *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1){/*按键消抖*/osDelay(10);if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1){while(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1);//等待按键被按下/*如果按键1被按下*/if(osSemaphoreGetCount(myCountingSem01Handle) > 0){/*如果还剩余车位*/osSemaphoreWait(myCountingSem01Handle, osWaitForever);printf("Parking successful, now the remaining parking space is %ld\n",osSemaphoreGetCount(myCountingSem01Handle)); }else{/*如果车位已全部⽤完*/printf("Parking failed. The parking lot is full.\n");}}}osDelay(10);}/* USER CODE END StartTaskStopCar */}/* USER CODE BEGIN Header_StartTaskGetCar *//*** @brief Function implementing the myTask_GetCar thread.* @param argument: Not used* @retval None*//* USER CODE END Header_StartTaskGetCar */void StartTaskGetCar(void const * argument){/* USER CODE BEGIN StartTaskGetCar *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1){osDelay(10);if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1){while(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1);/*如果按键1被按下*/if(!osSemaphoreRelease(myCountingSem01Handle)){/*取车成功*/printf("The car was taken successfully, now the remaining parking space is %ld\n",osSemaphoreGetCount(myCountingSem01Handle)); }else{/*取车失败*/printf("Failed to fetch the car, now the remaining parking space is %ld\n",osSemaphoreGetCount(myCountingSem01Handle));}}}osDelay(10);}/* USER CODE END StartTaskGetCar */}修改完毕后点击⼩锤⼦构建⼯程,然后点击Debug,按如下步骤全速运⾏之前⼀定要先点击SWV ITM data Console 页⾯中的红⾊圆圈现象:分析:DefaultTask 负责每秒输出⼀个当前的时间信息,表⽰此时时间为第⼏秒。
STM32Cubemx配置定时器定时1mS 最近才发现原来我把定时器⾥的配置参数代表的意义给搞混了,这⾥记录⼀下,防⽌以后⾃⼰忘记。
以建⽴⼀个定时1mS定时器为例: 1、先打开定时器 2、配置好时钟 3、配置定时器设置 重点来了,以前在这⾥我⼀直以为这⾥配置的就是时间,然后在调频率的时候,⼀直不对劲,知道查阅了硬⽯的资料才发现,这⾥配置的是进⼊定时器中断的频率,然后要定的时间要跟据这个频率来定时的。
由这个图可见,这⾥配置的是定时器产⽣中断的频率,然后再跟据频率与时间的关系推出定时的时间。
所以定时器频率为 f = 72M / Prescaler / Period = 72000 000 / 72 /1000 = 1000Hz; 定时时间T = 1 / f 则: 1s / 1000Hz = 1000 000us / 1000Hz = 1000us =1ms。
这样就可以定时1ms了啦,如果要做PWM频率调频,就直接改 pre 与 per 算出 f 就可以了。
具体公式如下图(上⾯的计算我是为了⽅便理解把公式逆运算了⼀次): 最后使⽤定时器中断跟关闭定时器中断以及回调函数即可, 使⽤中断的时候注意要先开启中断HAL_TIM_Base_Start_IT(&htim1); //使⽤定时器的时候调⽤这个函数启动HAL_TIM_Base_Stop_IT(&htim1); //停⽌定时器的时候调⽤这个函数关闭void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ if(htim->Instance == TIM1) { //编写回调逻辑,即定时器1定时1MS后的逻辑 }}补充:最后记得⽣成STM32Cubemx⽂件时打开定时器中断,不然进不了回调函数,⼀般的话习惯⽤TIM3做定时器。
STM32定时器程序设计STM32定时器程序设计是一种用于倒计时、计时、周期性触发等应用的重要技术。
这种技术在嵌入式系统中得到广泛应用,尤其是在需要实时控制和定时任务的领域。
本文将介绍STM32定时器的基本原理、使用方法和常见应用场景。
一、STM32定时器简介STM32系列微控制器内置了多个定时器,其中最常用的定时器是通用定时器(TIM)。
STM32的通用定时器具有多个计数器,可以实现多种不同的定时功能。
通用定时器还具有自动更新功能和PWM功能,能够实现精确的定时控制和产生各种电信号。
二、STM32定时器的基本原理三、STM32定时器的使用方法1、初始化定时器:使用 HAL_TIM_Base_Init(函数初始化定时器,并设置定时器的时钟分频系数和计数器的自动重载值。
2、启动定时器:使用 HAL_TIM_Base_Start_IT(函数启动定时器,并使能对应的中断。
3、编写定时器中断处理函数:在定时器中断处理函数中编写需要定时执行的任务,如IO口操作、数据采集等。
4、停止定时器:在需要停止定时器时,使用HAL_TIM_Base_Stop_IT(函数停止定时器。
四、STM32定时器的应用场景1、延时函数:通过定时器的计数功能可以实现精确的延时功能,例如实现毫秒级的延时函数。
2、定时中断:通过定时器的中断功能可以定时地执行一些任务,例如每隔一段时间进行数据采集、ADC转换等。
3、PWM产生:通过定时器的PWM功能可以实现精确的脉冲宽度调制(Pulse Width Modulation),用于驱动舵机、电机等设备。
4、计时功能:通过定时器的计数功能可以实现计时功能,例如实现秒表、计步器等功能。
5、周期性触发:通过定时器的计数功能和自动重载值,可以实现周期性触发事件,例如周期性发送数据、周期性采集传感器数据等。
综上所述,STM32定时器程序设计是一种重要的嵌入式系统技术。
通过合理地使用定时器,可以实现精确的定时控制和周期性触发任务。
stm32定时器原理STM32定时器是一种用于计时和计数的重要功能模块,广泛应用于各种嵌入式系统中。
本文将介绍STM32定时器的原理及其应用。
一、STM32定时器的基本原理STM32定时器是基于计数器的工作原理,通过内部时钟源的驱动,实现对计数器的计数和定时功能。
STM32定时器主要有以下几个核心组件:1.1 时钟源:STM32定时器可以选择多种时钟源,如内部时钟、外部时钟或外部时钟源经过分频后的时钟。
时钟源的选择取决于应用的需要和系统的设计。
1.2 预分频器:预分频器用于将时钟源的频率进行分频,以获得更低的计数频率。
预分频器的分频系数可以通过配置来设置,从而满足不同的计数需求。
1.3 自动重装载寄存器(ARR):ARR用于设置定时器的计数周期,即定时器从0开始计数到ARR的值时就会触发中断或产生某种事件。
通过设置ARR的值,可以实现不同的定时功能。
1.4 计数器:计数器是STM32定时器的核心部件,用于进行实际的计数操作。
计数器的位数根据不同型号的STM32芯片而有所不同,常见的有16位和32位两种。
1.5 输出比较单元(OCU):OCU用于产生定时器的输出信号。
可以通过配置OCU的工作模式、比较值和输出极性等参数,实现各种不同的输出功能。
二、STM32定时器的应用STM32定时器广泛应用于各种嵌入式系统中,常见的应用场景包括:2.1 定时中断:通过设置定时器的ARR值和使能中断,可以实现定时中断功能,用于周期性地执行某些任务或操作。
例如,可以利用定时中断来定时采样、定时发送数据或定时更新显示等。
2.2 脉冲计数:通过配置STM32定时器的输入捕获单元(ICU),可以实现对外部脉冲信号的计数。
这在一些需要测量脉冲频率或脉冲宽度的应用中非常有用,如测速仪、计时器等。
2.3 PWM输出:通过配置STM32定时器的输出比较单元,可以实现PWM信号的输出。
PWM信号广泛应用于电机控制、LED调光、音量控制等场景,具有调节精度高、功耗低的特点。
STM32CubeMX之定时器控制微秒延时
1.在HAL固件库中只有使用Systick作为延时计数器,毫秒级延时HAL_Delay()。
为了增加精确的微秒级延时,一般都是更改Systick配置参数,但HAL固件库许多地方都使用了HAL_Delay()函数,因此建议不要修改系统自动配置的Systick参数;
2.加入操作系统时要占用Systick,而MCU系统自身的时基还要选择其他的定时器
因此采用定时器控制微妙延时的方法,是比较灵活的。
需要用户增加的代码很少,经济实用
步骤1.配置时钟
定时器工作频率=**经过内部时钟分频的**APBx Timer Clock/PSC寄存器的值+1;
举个栗子,如下:
即定时器的时钟频率为84MHz,不用用内部时钟分频,要使定时器的工作频率为1MHz(1us),如下:1MHz=84MHz/(83+1);
编写代码
现象LED1闪烁。
FreeRTOS_软件定时器FreeRTOS 软件定时器实验创建2个任务,start_task、timercontrol_task。
start_stask:创建timercontrol_task任务;创建周期定时器AutoReloadTimer 和单次定时器OneShotTimer;创建⼆值信号量BinarySemaphore。
BinarySemaphore:接收串⼝命名,在中断中释放信号,在timercontrol_task中等待信号量,解析命名,通过不同的命令控制周期定时器AutoReloadTimer和单次定时器OneShotTimer的开启和关闭。
AutoReloadTimer 的回调函数会输出运⾏的次数OneShotTimer的回电函数会输出运⾏的次数任务分配://任务优先级#define START_TASK_PRIO 1//任务堆栈⼤⼩#define START_STK_SIZE 128//任务句柄TaskHandle_t StartTask_Handler;//任务函数void start_task(void *pvParameters);//任务优先级#define TIMERCONTROL_TASK_PRIO 2//任务堆栈⼤⼩#define TIMERCONTROL_STK_SIZE 50//任务句柄TaskHandle_t TimerControlTask_Handler;//任务函数void timercontrol_task(void *pvParameters);SemaphoreHandle_t BinarySemaphore_Handle; // ⼆值信号量句柄TimerHandle_t AutoReloadTimer_Handle ; // 周期定时器句柄TimerHandle_t OneShotTimer_Handle; // 单次定时器句柄void AutoReloadTimerCallback(void); // 周期定时器回调函数void OneShotTimerCallback(void); // 周期定时器回调函数main() 函数int main(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4delay_init(); //延时函数初始化uart_init(115200); //初始化串⼝LED_Init(); //初始化LED//创建开始任务xTaskCreate((TaskFunction_t )start_task, //任务函数(const char* )"start_task", //任务名称(uint16_t )START_STK_SIZE, //任务堆栈⼤⼩(void* )NULL, //传递给任务函数的参数(UBaseType_t )START_TASK_PRIO, //任务优先级(TaskHandle_t* )&StartTask_Handler); //任务句柄vTaskStartScheduler(); //开启任务调度}命令解析相关函数:// 将字符串中的⼩写字母转换为⼤写// str:要转换的字符串// len:字符串长度void LowerToCap(u8 *str,u8 len){u8 i;for(i=0;i<len;i++){if((96<str[i])&&(str[i]<123)) // ⼩写字母str[i] = str[i]-32; // 转换为⼤写}}// 命令处理函数,将字符串命令转换成命令值// str:命令// 返回值:0xFF-命令错误其他值-命令值u8 CommandProcess(u8 *str){u8 CommandValue = 0xFF;if(strcmp((char*)str,"KEY1")==0) CommandValue = 1;else if(strcmp((char*)str,"KEY2")==0) CommandValue = 2;else if(strcmp((char*)str,"KEY3")==0) CommandValue = 3;else if(strcmp((char*)str,"KEY4")==0) CommandValue = 4;return CommandValue;}任务函数://开始任务任务函数void start_task(void *pvParameters){taskENTER_CRITICAL(); //进⼊临界区// 创建⼆值信号量BinarySemaphore_Handle = xSemaphoreCreateBinary(); // 创建⼆值信号量if(BinarySemaphore_Handle ==NULL){printf("BinarySemaphore Create Failed!\r\n");}else{xSemaphoreGive(BinarySemaphore_Handle); // 释放信号量}// 创建周期定时器AutoReloadTimer_Handle = xTimerCreate( (const char *)"AutoReloadTimer", (TickType_t) 1000,(UBaseType_t) pdTRUE,(void *) 1,(TimerCallbackFunction_t)AutoReloadTimerCallback );if(AutoReloadTimer_Handle == NULL){printf("AutoReloadTimer Created Failed \r\n");}else{printf("AutoReloadTimer Created Success \r\n");}// 创建单次定时器OneShotTimer_Handle = xTimerCreate( (const char *)"OneShotTimer",(TickType_t) 2000,(UBaseType_t) pdFALSE,(void *) 2,(TimerCallbackFunction_t)OneShotTimerCallback );if(OneShotTimer_Handle == NULL){printf("OneShotTimer Created Failed \r\n");}else{printf("OneShotTimer Created Success \r\n");}//创建TIMECONTRFOL任务xTaskCreate((TaskFunction_t )timercontrol_task,(const char* )"timercontrol_task",(uint16_t )TIMERCONTROL_STK_SIZE,(void* )NULL,(UBaseType_t )TIMERCONTROL_TASK_PRIO,(TaskHandle_t* )&TimerControlTask_Handler);vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL(); //退出临界区}//TIMERCONTROL任务函数void timercontrol_task(void *pvParameters){u8 len = 0;u8 CommandValue = 0xFF;u8 CommandStr[USART_REC_LEN];BaseType_t err;while(1){xSemaphoreTake( BinarySemaphore_Handle, portMAX_DELAY ); // 死等len = USART_RX_STA&0x3fff; // 得到此次接收到的数据长度 sprintf((char *)CommandStr,"%s",USART_RX_BUF); // 装载数据CommandStr[len] = '\0'; // 加上字符串结尾符号LowerToCap(CommandStr,len); // 将字符串转换为⼤写CommandValue = CommandProcess(CommandStr); // 命令解析if(CommandValue != 0xFF) // 接收到正确的命令{switch(CommandValue){case1: // 开启周期定时器err = xTimerStart( AutoReloadTimer_Handle, 0 );if(err == pdFAIL){printf("AutoReloadTimer Start Failed! \r\n");}else{printf("AutoReloadTimer Start Succeed! \r\n");}break;case2: // 关闭周期定时器err = xTimerStop( AutoReloadTimer_Handle, 0 );if(err == pdFAIL){printf("AutoReloadTimer Stop Failed! \r\n");}else{printf("AutoReloadTimer Stop Succeed! \r\n");}break;case3: // 开启单次定时器err = xTimerStart( OneShotTimer_Handle, 0 );if(err == pdFAIL){printf("OneShotTimer Start Failed! \r\n");}else{printf("OneShotTimer Start Succeed! \r\n");}break;case4: // 关闭周期定时器err = xTimerStop( OneShotTimer_Handle, 0 );if(err == pdFAIL){printf("OneShotTimer Stop Failed! \r\n");}else{printf("OneShotTimer Stop Succeed! \r\n");}break;}}else{printf("Cmd error!\r\n");}USART_RX_STA = 0;}}定时器回调函数:// 周期定时器回调函数void AutoReloadTimerCallback(void){static u8 count = 0;count ++;printf("AutoReloadTimerCallback running %d timers\r\n",count);}// 周期定时器回调函数void OneShotTimerCallback(void){static u8 count = 0;count ++;printf("OneShotTimerCallback running %d timers\r\n",count);}串⼝中断初始化和中断处理函数u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最⼤USART_REC_LEN个字节.//接收状态//bit15,接收完成标志//bit14,接收到0x0d//bit13~0,接收到的有效字节数⽬u16 USART_RX_STA=0; //接收状态标记void uart_init(u32 bound){//GPIO端⼝设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟 //USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复⽤推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输⼊GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=8 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //⼦优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器//USART 初始化设置USART_ART_BaudRate = bound;//串⼝波特率USART_ART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_ART_StopBits = USART_StopBits_1;//⼀个停⽌位USART_ART_Parity = USART_Parity_No;//⽆奇偶校验位USART_ART_HardwareFlowControl = USART_HardwareFlowControl_None;//⽆硬件数据流控制USART_ART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART1, &USART_InitStructure); //初始化串⼝1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串⼝接受中断USART_Cmd(USART1, ENABLE); //使能串⼝1}extern SemaphoreHandle_t BinarySemaphore_Handle; // ⼆值信号量句柄void USART1_IRQHandler(void) //串⼝1中断服务程序{u8 Res;BaseType_t xHigherPriorityTaskWoken = pdFALSE;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) {Res =USART_ReceiveData(USART1); //读取接收到的数据if((USART_RX_STA&0x8000)==0)//接收未完成{if(USART_RX_STA&0x4000)//接收到了0x0d{if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始else USART_RX_STA|=0x8000; //接收完成了}else//还没收到0X0D{if(Res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收}}}}if((BinarySemaphore_Handle != NULL) && (USART_RX_STA&0x8000)){xSemaphoreGiveFromISR( BinarySemaphore_Handle, &xHigherPriorityTaskWoken ); // 释放互斥信号量 portYIELD_FROM_ISR(xHigherPriorityTaskWoken); // 如果需要进⾏⼀次任务切换}}#define USART_REC_LEN 20 //定义最⼤接收字节数 200#define EN_USART1_RX 1 //使能(1)/禁⽌(0)串⼝1接收extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最⼤USART_REC_LEN个字节.末字节为换⾏符注意:串⼝中断的优先级在FreeRTOS的优先级管理范围内。
FreeRTOS学习之七:软定时器
前提:默认已经装好MDK V5和STM32CubeMX,并安装了STM32F1xx系列的支持包。
硬件平台:STM32F1xx系列。
目的:学习软定时器的使用。
有时候我们需要定时进行一些例行处理,FreeRTOS提供了软定时器来满足这种需求。
软定时器不是FreeRTOS内核的组成部分,它本质上是一种任务,周期性地调用其回调函数。
本文例子使用STM32CubeMX配置创建一个软定时器和一个任务,软定时器在回调函数控制LED0的状态,任务控制LED1的状态。
Step1.打开STM32CubeMX,点击“New Project”,选择芯片型号,STM32F103RBTx。
Step2.配置时钟引脚。
Step3.配置PA8和PD2为Output,并把用户标签分别改为LED0,LED1。
Step4.将系统时基源改为TIM4。
Step5.使能FreeRTOS。
Step6.配置时钟树。
8M输入时,通过PLL得到72M内部时钟。
Step7.配置FreeRTOS。
在Tasks and Queues选项卡中,默认配置了一个名为defaultTask的任务,其优先级为普通,任务堆栈大小为128字,任务函数名为StartDefaultTask。
双击蓝色的地方,弹出对话框,将任务名修改为LED1,将任务函数名修改为LED1_Task。
在Configure parameters选项卡,使能软定时器,定时器任务的优先级默认为2,定时器队列深度默认为10。
(后两个参数在后文有详细说明。
)
在Timers and Semaphores选项卡,点击Timers项右边的“Add”按钮,添加一个软定时器,函数改为Timer01_Callback,类型为周期定时器。
注:其他的都使用默认参数。
Step8.生成代码。
等完成后直接打开工程。
工程基本组织结构如下图,其中Application/User组中的文件是用户可以修改的,而其他组中的文件一般不进行修改。
Step9.分析程序结构。
在进入main函数之前,先定义了几个变量,声明了几个函数。
再看main函数。
将main函数整理,删除很多注释之后,得到下图所示内容。
其中第①部分,是硬件配置;第②部分,创建一个软定时器和一个任务;第③部分,启动调度器。
Step10.添加代码。
在main.c文件中,在任务函数LED1_Task添加代码如下。
在软定时器回调函数中添加代码如下。
在main函数的/* USER CODE BEGIN RTOS_TIMERS */和/* USER CODE END RTOS_TIMERS */注释行之间添加启动软定时器的代码。
Step11.编译下载运行。
LED0和LED1分别闪烁,LED0每秒闪2次,LED1每秒闪1次。
特别说明:
软定时器不是FreeRTOS内核的组成部分,它本质上是一种任务,周期性地调用其回调函数。
要注意的是,在回调函数中,不应该调用造成阻塞的API函数,如vTaskDelay(), vTaskDelayUntil()等。
前面Step7配置FreeRTOS时,有两个参数TIMER_TASK_PRIORITY和TIMER_QUEUE_LENGTH,下面详细说明一下。
因为软定时器是一种任务,所以它就应该有优先级。
和一般的FreeRTOS任务一样,它的优先级也是从0到(configMAX_PRIORITIES-1)之间。
TIMER_TASK_PRIORITY的作用就是指定软定时器任务的优先级。
下图是官网上对该参数的描述:
另外,软定时器不是一般的任务,在创建它时,同时还创建了一个消息队列,成为“定时器命令队列”。
软定时器服务任务(或称守护任务)通过这个队列来接收用户发送的命令。
当用户的程序调用xTimerReset() 等定时器API函数时,就是向该队列发送命令。
TIMER_QUEUE_LENGTH的作用就是指定软定时器的命令队列的深度。
下图是官网上对该参数的描述:
需要特别注意红框中描述的信息,可能造成定时器命令队列满载的原因包括:
·在RTOS调度器启动前,多次调用定时器API函数;
·在(硬件)中断服务程序(ISR)中多次调用定时器API函数;
·在优先级高于定时器服务任务的任务中,多次调用定时器API函数。
更多关于定时器的描述,请访问FreeRTOS官网。
S.D.Lu 于深圳
2016年8月。