一、时间管理
- 软件定时器
概述
软件定时器提供了一套从软件层次实现的定时器机制,相对应的概念是硬件定时器。用户可以创建一系列的软件定时器,并指定软件定时器到期的条件以及执行回调,当软件定时器到期时会执行注册的回调。
通常来说,用户注册的软件定时器回调中很可能包含延迟动作或同步等待操作,或者回调函数本身逻辑复杂执行耗时较长,因此系统将软件定时器管理逻辑设计成一个任务,在这个任务中扫描定时器是否过期并执行定时器回调。但是如你所知,创建一个任务是需要消耗系统内存资源的(任务的栈、任务句柄本身的内存空间等等),而如果用户注册的软件定时器回调中并不包含延迟动作也不包含同步等待操作,或者回调本身执行耗时很短,这种情况下软件定时器管理逻辑无需被设计成任务,而是可以被设计成时钟中断中被调用的一个函数。当软件定时器管理逻辑被设计成一个函数时,就可以节省创建任务所需的资源。
系统默认采用的实现是将定时器管理逻辑设计为任务,当用户的定时器回调都是耗时极短的操作时,用户可以通过将软件定时器管理逻辑配置为函数来节省内存资源。通过在tos_config.h中打开TOS_CFG_TIMER_AS_PROC开关来讲软件定时器管理逻辑配置为函数:
#define TOS_CFG_TIMER_AS_PROC 1u
API讲解
k_err_t tos_timer_create(k_timer_t *tmr,
k_tick_t delay,
k_tick_t period,
k_timer_callback_t callback,
void *cb_arg,
k_opt_t opt)
这里详细讲解此api参数意义:
-
tmr
软件定时器句柄。
-
delay
该定时器延迟多久后执行。
-
period
一个定时器的执行周期。
-
callback
定时器到期后的执行回调。
-
cb_arg
执行回调的入参。
-
opt
此opt的传入主要是界定tmr的属性,如果传入的是TOS_OPT_TIMER_ONESHOT,表明此tmr是一次性的,当delay时间到期,tmr的执行回调调用完毕后此tmr生命周期就结束了;如果传入的是TOS_OPT_TIMER_PERIODIC,表明此tmr是周期性定时器,当tmr定时时间到期,tmr的执行回调调用完毕后,系统会重新按period参数为到期时间将tmr加入到定时队列,开启下一个周期。
编程实例
1、在tos_config.h中,配置软件定时器组件开关TOS_CFG_TIMER_EN:
#define TOS_CFG_TIMER_EN 1000u
2、编写main.c示例代码:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "cmsis_os.h"
#include "stdio.h"
#include "tos_k.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define STK_SIZE_TASK_DEMO 512
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
k_task_t task_demo;
extern void entry_task_demo(void *arg);
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
void oneshot_timer_cb(void *arg)
{
printf("this is oneshot timer callback, current systick: %ld\n", tos_systick_get());
}
void periodic_timer_cb(void *arg)
{
printf("this is periodic timer callback, current systick: %ld\n", tos_systick_get());
}
void entry_task_demo(void *arg)
{
k_timer_t oneshot_tmr;
k_timer_t periodic_tmr;
// 这是一个一次性的timer,且超时时间是3000个tick之后
tos_timer_create(&oneshot_tmr, 3000, 0, oneshot_timer_cb, K_NULL, TOS_OPT_TIMER_ONESHOT);
// 这是一个周期性的timer,第一次超时时间是2000个tick之后,之后按3000个tick为周期执行回调
tos_timer_create(&periodic_tmr, 2000, 3000, periodic_timer_cb, K_NULL, TOS_OPT_TIMER_PERIODIC);
printf("current systick: %ld\n", tos_systick_get());
tos_timer_start(&oneshot_tmr);
tos_timer_start(&periodic_tmr);
while (K_TRUE) {
tos_task_delay(1000);
}
}
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* 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 */
tos_knl_init();
(void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
tos_knl_start();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
3、运行效果
源码链接