学习RT-thread(RT-thread定时器)
1. 时钟节拍
对于RT-Thread来说,它和其他系统都一样,都需要一个时钟节拍,以提供系统处理所有和时间有关的事件,如线程的延时,线程的时间片轮转调度以及定时器超时等。
时钟节拍是特定的周期性中断,这个中断被描述为系统的心跳。而中断的事件间隔取决于系统配置,时钟节拍越快,系统的实时响应越快,但会使系统的开销变大。
以STM32为例,通过SysTick提供了这个时钟节拍,SysTick每次中断的到来,都会调用一次 rt_tick_increase函数,通知操作系统已经过去一个系统时钟。
void SysTick_Handler(void){/* 进入中断 */rt_interrupt_enter();……rt_tick_increase();/* 退出中断 */rt_interrupt_leave();}2. 定时器
2.1 什么是定时器
定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,例如定个时间提醒第二天能够按时起床。定时器有硬件定时器和软件定时器之分:
2.1.1 硬件定时器:
硬件定时器:定时器超时函数在中断上下文环境中执行,可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_HARD_TIMER 来指定。注意:在中断上下文环境中执行时,对于超时函数的要求 与中断服务例程的要求相同:执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如 在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等。
2.1.2 软件定时器:
软件定时器:系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER 模式的定时器超时函数在 都会在 timer 线程的上下文环境中执行。可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_SOFT_TIMER 来指定设置 SOFT_TIMER 模式。
注意:RT-Thread 操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时 数值必须是 OS Tick 的整数倍,例如一个 OS Tick 是 10ms,那么上层软件定时器只能是 10ms, 20ms,100ms 等,而不能定时为 15ms。RT-Thread 的定时器也基于系统的节拍,提供了基于节拍整数 倍的定时能力。
2.2 定时器工作原理
1. 当前系统经过的 tick 时间 rt_tick(当硬件定时器中断来临时,它将加 1)
2. 定时器链表 rt_timer_list。系统新创建并激活的定时器都会按照以超时时间排序的方式插入到 rt_timer_list 链表中
如下图所示,系统当前 tick 值为 20,在当前系统中已经创建并启动了三个定时器,分别是定时时间为 50 个 tick 的 Timer1、100 个 tick 的 Timer2 和 500 个 tick 的 Timer3,这三个定时器分别加上系统当 前时间 rt_tick=20,从小到大排序链接在 rt_timer_list 链表中,形成如图所示的定时器链表结构。

而 rt_tick 随着硬件定时器的触发一直在增长(每一次硬件定时器中断来临,rt_tick 变量会加 1),50 个 tick 以后,rt_tick 从 20 增长到 70,与 Timer1 的 timeout 值相等,这时会触发与 Timer1 定时器相关联 的超时函数,同时将 Timer1 从 rt_timer_list 链表上删除。同理,100 个 tick 和 500 个 tick 过去后,与 Timer2 和 Timer3 定时器相关联的超时函数会被触发,接着将 Timer2 和 Timer3 定时器从 rt_timer_list 链表中删除。
如果系统当前定时器状态在 10 个 tick 以后(rt_tick=30)有一个任务新创建了一个 tick 值为 300 的 Timer4 定时器,由于 Timer4 定时器的 timeout=rt_tick+300=330, 因此它将被插入到 Timer2 和 Timer3 定时器中间,形成如下图所示链表结构:

3. 定时器相关函数
3.1 启动和停止函数
3.1.1 启动定时器函数:
rt_err_t rt_timer_start(rt_timer_t timer);
3.1.2 停止定时器函数
rt_err_t rt_timer_stop(rt_timer_t timer);
3.2 创建/删除函数
3.2.1 创建定时器函数:
void rt_timer_init(rt_timer_t timer,const char* name,void (*timeout)(void* parameter),void* parameter,rt_tick_t time, rt_uint8_t flag);
flag定义:
#define RT_TIMER_FLAG_ONE_SHOT 0X0 /*单次定时*/
#define RT_TIMER_FLAG_PERIODIC 0X2 /*周期定时*/
#define RT_TIMER_FLAG_HARD_TIMER 0X0 /*硬件定时器*/
#define RT_TIMER_FLAG_SOFT_TIMER 0X4 /*软件定时器*/3.2.2 删除定时器函数:
rt_err_t rt_timer_detach(rt_timer_t timer);
4. 定时器实操
实验要求:
场景:分别通过创建两个定时器:
1. 定时器1周期性执行,控制灯的闪烁;
2. 定时器2周期性执行,打印一条打印。
CubeMX配置
我们要控制LED灯闪烁根据原理图将PA12设为GPIO_OutPut


代码实现
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
#include <rtthread.h>
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
static struct rt_timer time1_handle;static struct rt_timer time2_handle;/* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);void timer1(void* parameter)
{HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12);
}void timer2(void* parameter)
{rt_kprintf("timer2\n");
}
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */rt_timer_init(&time1_handle,"time1",timer1,NULL,1000,RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);rt_timer_init(&time2_handle,"time2",timer2,NULL,1000,RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);rt_timer_start(&time1_handle);rt_timer_start(&time2_handle);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */rt_thread_delay(100);/* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
实验现象:
1.打开串口终端观察实验现象
timer2 1s 打印一次

2.LED1 1s闪烁一次
