学习RT-thread(RT-thread定时器)

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 定时器工作原理

  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 启动定时器函数:

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. 定时器1周期性执行,控制灯的闪烁;

  2. 定时器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闪烁一次

相关推荐
爱学习的大牛1235 分钟前
如何系统学习网络渗透测试:从入门到精通的完整指南
网络·学习
落子摘星1 小时前
suricata学习杂记(一)
学习·modbus·suricata·pdu
charlie1145141911 小时前
深入理解C/C++的编译链接技术6——A2:动态库设计基础之ABI设计接口
c语言·开发语言·c++·学习·动态库·函数
white-persist1 小时前
【攻防世界】reverse | Reversing-x64Elf-100 详细题解 WP
c语言·开发语言·网络·python·学习·安全·php
AI绘画哇哒哒5 小时前
【收藏必看】大模型智能体六大设计模式详解:从ReAct到Agentic RAG,构建可靠AI系统
人工智能·学习·ai·语言模型·程序员·产品经理·转行
小奶包他干奶奶8 小时前
Webpack学习——Loader(文件转换器)
前端·学习·webpack
小奶包他干奶奶9 小时前
Webpack学习——原理理解
学习·webpack·devops
励志成为美貌才华为一体的女子9 小时前
强化学习PPO和GRPO逻辑学习
学习
meichaoWen9 小时前
【Vue3】vue3的全面学习(一)
前端·javascript·学习
FFF团团员9099 小时前
树莓派学习笔记3:LED和Button
笔记·学习