学习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闪烁一次

相关推荐
QT 小鲜肉2 小时前
【个人成长笔记】在 Linux 系统下撰写老化测试脚本以实现自动压测效果(亲测有效)
linux·开发语言·笔记·单片机·压力测试
申克Lab2 小时前
STM32 串口概念 UART协议
stm32·单片机·嵌入式硬件
小莞尔2 小时前
【51单片机】【protues仿真】基于51单片机自动浇花系统
单片机·嵌入式硬件
2301_796512523 小时前
Rust编程学习 - 为什么说Cow 代表的是Copy-On-Write, 即“写时复制技术”,它是一种高效的 资源管理手段
java·学习·rust
故里21303 小时前
学习前端记录(二)21-40
学习
ThreeYear_s3 小时前
电力电子技术学习路径与FPGA/DSP技术结合方向(gemini生成)
学习·fpga开发
沐欣工作室_lvyiyi3 小时前
基于51单片机的宠物喂食器的设计与实现(论文+源码)
单片机·嵌入式硬件·毕业设计·51单片机·宠物
好奇龙猫4 小时前
【生活相关-日语-日本-入国&出国-海关&市役所(4)-办理手续】
学习·生活
sendnews4 小时前
红松小课如何成为激活老年人生活的新引擎?从兴趣学习到价值重塑!
学习·生活