1. 时钟节拍
对于RT-Thread来说,它和其他系统都一样,都需要一个时钟节拍,以提供系统处理所有和时间有关的事件,如线程的延时,线程的时间片轮转调度以及定时器超时等。
时钟节拍是特定的周期性中断,这个中断被描述为系统的心跳。而中断的事件间隔取决于系统配置,时钟节拍越快,系统的实时响应越快,但会使系统的开销变大。
以STM32为例,通过SysTick提供了这个时钟节拍,SysTick每次中断的到来,都会调用一次 rt_tick_increase函数,通知操作系统已经过去一个系统时钟。
cpp
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 定时器工作原理
-
当前系统经过的 tick 时间 rt_tick(当硬件定时器中断来临时,它将加 1)
-
定时器链表 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 启动定时器函数:
cpp
rt_err_t rt_timer_start(rt_timer_t timer);

3.1.2 停止定时器函数
cpp
rt_err_t rt_timer_stop(rt_timer_t timer);

3.2 创建/删除函数
3.2.1 创建定时器函数:
cpp
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定义:
cpp
#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 删除定时器函数:
cpp
rt_err_t rt_timer_detach(rt_timer_t timer);

4. 定时器实操
实验要求:
场景:分别通过创建两个定时器:
-
定时器1周期性执行,控制灯的闪烁;
-
定时器2周期性执行,打印一条打印。
CubeMX配置
我们要控制LED灯闪烁根据原理图将PA12设为GPIO_OutPut


代码实现
cpp
/* 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闪烁一次