定时事件链表

2023-05-16

本文主要写的是:将需要定时的事件作为一个链表节点添加到链表中。所写代码是从LWIP源码中复制出来的,稍作修改。
当阅读到lwip源码timers.c文件中的sys_timeout函数时,觉得非常适合如下一种情况:

step1();
delay(100);
step2()
delay(100);
***
stepn();

上面这段代码是阻塞的,代码运行的时候这种耗费CPU的延时会导致其它代码得不到执行,一般不能写成这种方式。 对于嵌入式开发来说如果有操作系统支持,可以把这一部分分离成一个任务,如果没有操作系统的支持,需要我们自己自己通过状态机或是其它的方法实现,实现的方法有很多种。本文介绍的就是其中的一种解决方案。个人认为这种方案比较优雅。将每个step写成一个事件(函数)。然后挂载到链表上。时间到后执行回调函数。
time_list.h代码如下:

#ifndef __TIME_LIST_H
#define __TIME_LIST_H

typedef unsigned int uint32_t;

typedef void(*time_callback)(void*arg);

typedef struct node
{
	struct node *next;	//用来构成链表
	uint32_t time;	//要延时的时间
	time_callback callback;	//延时时间到后需要指令的回调函数
	void *arg;	//传递给回调函数的参数
}Sys_timeo;

#endif

time_list.c代码如下:

#include <stdio.h>
#include <stdlib.h>
#include "time_list.h"
#include "time.h"

#define NULL 0

#define ONE			"ONE"
#define TOW			"TOW"
#define THREE		"THREE"
#define FOUR		"FOUR"

void test_function(void *arg)
{
	time_t t;
	time(&t);
	printf("%u\t", t);
	printf("%s\r\n", arg);
}


Sys_timeo *sys_timeo_list;//定义一个超时链表

/*
	功能:向链表中添加一个定时节点
	msecs:需要定时的时间
	callback:定时时间到后触发的回调函数
	arg:传递给回调函数的参数
	opt:延时选项 0表示相对于当前系统时间延时msecs个时间单位 1表示相对于最后一个节点延时msecs个时间单位
*/
void sys_timeout(uint32_t msecs, time_callback callback, void *arg,uint32_t opt)
{
	Sys_timeo *timeout = NULL,*temp_node = NULL;
	timeout = malloc(sizeof(Sys_timeo));
	if (timeout == NULL)
		return;
	timeout->next = NULL;
	timeout->time = msecs;
	timeout->callback = callback;
	timeout->arg = arg;

	if (sys_timeo_list == NULL)//如果链表是空的,把当前节点作为第一个节点
	{
		sys_timeo_list = timeout;
		return;
	}

	if (opt == 1)
	{
		for (temp_node = sys_timeo_list; temp_node->next != NULL; temp_node = temp_node->next)
		{


		}
		temp_node->next = timeout;
		return;
	}

	/*执行到这里说明链表中存在节点,接下来就找一个合适的位置区添加*/
	if (msecs < sys_timeo_list->time)//先和链表上的头节点对比
	{
		sys_timeo_list->time -= msecs;  //调整时间 因为这个节点已经不再是头节点 调整成相对上一个节点的时间
		timeout->next = sys_timeo_list; //这两行是把节点添加到链表首部
		sys_timeo_list = timeout;
	}
	else
	{
		for (temp_node = sys_timeo_list; temp_node != NULL; temp_node = temp_node->next)
		{
			timeout->time -= temp_node->time; //更新插入节点的时间 减去前边已经遍历过节点的时间
			//插入节点的时间小于当前节点的下一个节点的时间 那么 就找到了位置:  当前节点  要插入的节点  当前节点的下一个节点
			if ((temp_node->next == NULL) || (timeout->time < temp_node->next->time))
			{
				//temp_node->next不是NULL(其实就是timeout插入以后不是最后一个节点) 那么就需要调整后边的那个节点的时间
				if (temp_node->next != NULL)
				{
					temp_node->next->time -= timeout->time;
				}

				timeout->next = temp_node->next;
				temp_node->next = timeout;
				break;
			}
		}
	}
}

int main()
{
	Sys_timeo * temp_node = NULL;
	time_t current_time, last_time, difference_time;

	sys_timeout(10, test_function, ONE, 1);
	sys_timeout(15, test_function, TOW, 1);
	sys_timeout(20, test_function, THREE, 1);
	sys_timeout(25, test_function, FOUR, 1);

	time(&current_time);
	last_time = current_time;

	printf("start_time = %u\r\n",current_time);

	while(1)
	{
		if (sys_timeo_list == NULL)
		{
			printf("exti\r\n");
			return 0;
		}
		time(&current_time);
		difference_time = current_time - last_time;
		if (difference_time)
		{
			last_time = current_time;
			if (sys_timeo_list->time >= difference_time)
				sys_timeo_list->time -= difference_time;
			else
				sys_timeo_list->time = 0;
		}


		if (sys_timeo_list->time == 0)
		{
			sys_timeo_list->callback(sys_timeo_list->arg);
			temp_node = sys_timeo_list;
			sys_timeo_list = sys_timeo_list->next;
			free(temp_node);
		}
	}
	return 0;
}

先看一下Sys_timeo结构体:

typedef struct node
{
	struct node *next;	//用来构成链表
	uint32_t time;	//要延时的时间
	time_callback callback;	//延时时间到后需要指令的回调函数
	void *arg;	//传递给回调函数的参数
}Sys_timeo;

要声明的一点是:结构中time字段延时的时间是相对于上一个节点执行过后还需要延时的时间。
搞清楚这一点后就好办了,我们在中断服务函数中只需要对链表当前的首节点时间做减操作即可。当减到0的时候,执行回调函数,并把链表的首节点释放,下一个节点就是节点了,以此类推。这样就会按既定的时间延时。
函数void sys_timeout(uint32_t msecs, time_callback callback, void *arg,uint32_t opt)会根据需要延时的时间和延时的选项在链表中找到一个合适的位置,将链表节点插入到该位置。介绍一下opt选项,如果为1就是相对于当前链表的尾部节点再延时msecs个时间单位,如果为0就是相对于当前系统时间延时msecs个时间单位。
因为我是在win32环境编写和下测试的,所以就是在主循环中比较当前时间和上次时间的差,以此方法对头节点的时间做减法操作如果在嵌入式中可以在中断服务函数中对链表头部的时间做减法。
在实际使用的时候,在任意时刻需要一个延时时间就调用sys_timeout函数,向sys_timeo_list链表中添加一个延时事件即可。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

定时事件链表 的相关文章

  • PX4垂直起降过程研究总结

    这篇博客只要对垂直起降过程和控制逻辑进行分析 版本 xff1a V1 10 2 源码位置 xff1a src modules vtol att control vtol att control main cpp src modules vt
  • PX4 四元数旋翼姿态控制修正

    PX4 四元数旋翼姿态控制修正 1 四元数介绍2 旋翼四元数角度控制逻辑3 最终控制实现逻辑4 仿真验证 版本 xff1a V1 10 2 xff1b 源码位置 xff1a src modules ma att control ma att
  • PX4实践中遇到的问题总结(持续更新中!!!!)

    PX4实践中遇到的问题总结 xff08 持续更新中 xff01 xff01 xff09 1 解锁的时候各种传感器报错2 舵机莫名抖动3 起落架没法转向4 遥控器无法校准5 遥控器校准好之后操纵出现问题 1 解锁的时候各种传感器报错 如果是处
  • PX4 添加自定义参数

    PX4 添加自定义参数 PX4添加参数1 以添加参数 VT TILT MC 2 为例2 验证 PX4添加参数 1 以添加参数 VT TILT MC 2 为例 tiltrotor cpp 在构造函数里面初始化参数映射 params handl
  • Ubuntu系统中文乱码的解决办法

    Ubuntu系统中文乱码的解决办法 文章目录 Ubuntu系统中文乱码的解决办法1 安装中文语言2 安装语言设置的命令locale3 安装中文的相关字体4 修改语言的环境变量4 1 环境变量一4 2 设置二 5 正式配置语言后记 最近在do
  • Laf Assistant:云开发从未如此爽快!

    原文链接 xff1a https forum laf run d 67 工欲善其事 xff0c 必先利其器 在编写代码时 xff0c IDE 也是我们不可或缺的 它可以让我们更高效地完成代码编写 xff0c 提高开发效率 因此 xff0c
  • PX4垂起(Tiltrotor)偏航控制研究

    PX4垂起 xff08 Tiltrotor xff09 偏航控制研究 PX4垂起 xff08 Tiltrotor xff09 偏航控制研究1 问题描述2 过渡过程中为什么没有偏航角度控制问题1 xff1a 为什么在过渡阶段固定翼位置控制没有
  • PX4卡尔曼滤波分析

    PX4卡尔曼滤波分析 本文主要介绍了PX4中EKF相关的代码
  • 飞行器参数辨识-极大似然估计

    飞行器参数辨识 极大似然估计 1 理论1 1 极大似然估计一般理论1 2 极大似然估计应用在飞行器参数估计1 3 拟线性化求解带估计参数 2 程序实现2 1 飞行器运动的状态方程 1 理论 1 1 极大似然估计一般理论 n n n 个随机观
  • 使用opencv进行车牌提取及识别

    https blog csdn net u011808673 article details 78510692
  • ​天天干着打杂的活,你做好突破自我的觉悟了吗?

    天天干着打杂的活 xff0c 你做好突破自我的觉悟了吗 xff1f 本文为草核儿创作 xff0c 经授权在本公众号原创首发 关于作者 xff1a 草核儿 xff0c 互联网行业沉思者 xff01 希望传播的是正能量 xff0c 但偶尔会被误
  • 数据仓库系列:如何优雅地规划数仓体系

    0x00 前言 数仓规划是数仓建设的蓝图 xff0c 涵盖从需求分析开始到最终的数仓评估验收整个环境 xff1b 数仓规划之所以重要 xff0c 是因为它是描述了数据流动的概念性框架 xff0c 为元数据管理奠定了基础 xff0c 对数据加
  • 【实践案例分享】有赞数据仓库实践之路

    作者 xff1a 叶瑞典 团队 xff1a 数据中台 一 大数据环境下的有赞数仓 关于数据仓库 xff0c 在维基百科中将它定义为用于报表和数据分析的系统 xff0c 是商务智能 Business Intelligence 的核心部分 在数
  • 3分钟看懂用户标签体系怎么做

    随着越来越多的企业开始追求对用户的精细化运营 用各种手段延长用户的生命周期 xff0c 促进用户的活跃与转化 xff0c 并尽一切可能产生商业价值 xff0c 已经是运营的核心 而要做好精准化运营的第一步 xff0c 便是更好的认识我们的用
  • 数据运营系列(三):熵权法如何确定指标权重构建评价体系

    1 熵权法 信息论基本原理解释信息是系统有序性的度量单位 xff0c 而熵可以度量系统的无序程度 xff1b 如果某个指标的信息熵越小 xff0c 该指标提供的信息量越大 xff0c 指标变异程度 方差 高 xff0c 因此在综合评价中所起
  • 漫谈数据团队协作之各岗位间的相互尊重

    最近和几位资深的团队管理者聊团队协作 xff0c 受益匪浅 xff0c 其中有一个观点令居士印象深刻 xff1a 想让团队成员之间能够默契地协作 xff0c 有非常多的方法 xff0c 但是 xff0c 有一个很重要且很基本的要求是 xff
  • 【Proteus仿真】【STM32单片机】智能窗帘控制系统设计

    文章目录 一 功能简介二 软件设计三 实验现象联系作者 一 功能简介 本项目使用Proteus8仿真STM32单片机控制器 xff0c 使用LCD1602显示模块 按键模块 HC05蓝牙 DHT11温湿度 PCF8591 ADC模块 光线传
  • 直播PPT分享-如何体现数据同学的业务敏感度

    今天即兴给大家直播分享了一次 xff0c 反馈还比较不错 xff0c 不少童鞋私信我要ppt xff0c 这里就统一发出来给大家参考了 xff0c 其实内容很少 xff0c 6句话搞定 以后 xff0c 居士会不定期地直播分享 xff0c
  • 无题

    今天在思考一个问题 xff0c 关于互联网行业的 xff0c 其他行业还不太清楚不敢妄加评论 互联网行业在当前社会中其实面临着很多社会问题需要解决和探索 xff0c 比如职业发展的不确定性 35岁的职业危机 职业社交关系的难处理 xff0c
  • Markdown学习笔记:如何画流程图

    如何使用markdown画流程图 话说网上关于使用markdown画流程图的相关的教程真是一堆堆的坑 xff0c 我丝毫不怀疑这些博文作者确实是知道如何使用markdown画流程图了 xff0c 但是写出来的文章却丝毫没有明白具体的使用方法

随机推荐

  • Gradle打jar包,包含所有依赖

    前言 最近被gradle折腾的欲仙欲死 gradle想把所有依赖打进jar包主要有两种方式 xff1a 一种是重写jar动作 xff0c 一种是用第三方插件 为了装x xff0c 我一直都是用的第一种方式 xff0c 结果出了问题解决不了
  • Spring Data Rest如何暴露ID字段

    前言 为了懒省事 xff0c 使用Spring Data Rest来直接提供rest接口 xff0c 重点遇到点小坑 xff0c 记录一下 记录 问题 entity xff1a span class hljs annotation 64 E
  • hadoop清空回收站

    直接删除目录 xff08 不放入回收站 xff09 hdfs dfs span class hljs attribute rm span span class hljs attribute skipTrash span path span
  • 漫谈数据仓库之拉链表(原理、设计以及在Hive中的实现)

    0x00 前言 本文将会谈一谈在数据仓库中拉链表相关的内容 xff0c 包括它的原理 设计 以及在我们大数据场景下的实现方式 最新文章已经迁入公众 xff1a 木东居士 全文由下面几个部分组成 xff1a 先分享一下拉链表的用途 什么是拉链
  • 《数据仓库实践》

    序言 2017 年初 xff0c 我开始在简书上写关于数据仓库的系列博客 xff0c 博客主题围绕大数据场景下数据仓库的理论和实践来展开 xff0c 截止现在已有十篇左右 最初写作的时候主要是抱着学习和总结的态度 xff0c 导致很多地方略
  • 一种计算用户留存的方法

    0x00 概述 用户留存分析是互联网时代常用的一种数据分析方法 而很多快速发展的公司并没有相应的方法论沉淀 xff0c 这就导致了在计算用户留存的时候会出现下面的一些问题 xff1a 1 xff09 用户留存的定义不明确 xff0c 不同的
  • MAVLink--结构

    MAVLink源文件结构 MAVLink是为微型飞行器MAV xff08 Micro Air Vehicle xff09 设计的 xff08 LGPL xff09 开源的通讯协议 是无人飞行器和地面站 xff08 Ground Contro
  • 关于函数strtok和strtok_r的使用要点和实现原理(二)

    xff08 一 xff09 中已经介绍了使用strtok函数的一些注意事项 xff0c 本篇将介绍strtok的一个应用并引出strtok r函数 1 一个应用实例 网络上一个比较经典的例子是将字符串切分 xff0c 存入结构体中 如 xf
  • TX2 ubuntu CPU占用率、占用物理内存、占用虚拟内存、进程ID、系统温度

    文件解释 在实际工作中有时需要程序打印出某个进程的内存占用情况以作参考 下面介绍一种通过Linux下的伪文件系统 proc计算某进程内存占用的程序实现方法 首先 为什么会有所谓的 伪文件 呢 Linux系统的文件类型大致可分为三类 普通文件
  • 【Fast RTPS】入门--------Ubuntu系统下

    RTPS协议的简单介绍 在RTPS的顶层 xff0c Domain域定义了不同的通信层 几个域可以同时独立地共存 域包含任意数量的参与者Participants xff0c 即发送和接收数据的元素 参与者使用端Endpoints xff1a
  • Linux 内核配置选项(转)

    Linux 内核配置选项 from http www mitbbs com mitbbs article t php board 61 Linux amp gid 61 10715608 amp ftype 61 0 第一部分 01 Cod
  • Cortex-M3双堆栈MSP和PSP

    什么是栈 xff1f 在谈M3堆栈之前我们先回忆一下数据结构中的栈 栈是一种先进后出的数据结构 类似于枪支的弹夹 xff0c 先放入的子弹最后打出 xff0c 后放入的子弹先打出 M3内核的堆栈也不例外 xff0c 也是先进后出的 栈的作用
  • 烧毁DC/DC电路问题

    使用芯龙半导体的XL7005A DC DC芯片 已经很多年了 xff0c 用的也很稳定 这次在做一个设备的时候 xff0c 系统上电就会烧DC DC芯片以及系统电路中的LDO和MCU等 试了很多次终于发现规律了 xff0c DC DC电路就
  • FreeRTOS内核全局变量

    想要分析FreeRTOS源码 xff0c 想要理解FreeRTOS源码的整个宏观架构 xff0c 有一个前提就是必须知道FreeRTOS内核中那些全局变量的意义 xff0c 每个全局变量都是用来干什么的 只有了解了这些全局变量我们才能从宏观
  • 基于LWIP协议栈RAW API的 UDP传输实验

    什么是UDP xff1f UDP是用户数据报协议 xff0c 是OSI参考模型中的传输层协议 UDP的特点 缺点 xff1a 无连接的 xff0c 不可靠的 xff0c 不能保证数据安全到达目的地 优点 xff1a 消耗资源小 xff0c
  • 初识CANOpen

    什么是CANOpen CANOpen是位于CAN总线之上的一个应用层协议 CAN总线只规定了物理层和数据链路层 xff0c 有了这两层 xff0c 数据就可以在CAN总线上传输了 我们和哪个设备通信就和哪个设备约定好 xff0c 哪个ID代
  • STM32单片机被锁无法烧写程序解决办法

    以前遇到无法烧写程序的问题 在开发中 xff0c 单片机突然无法烧写程序 xff0c 这种情况相信大家应该都遇到过 比如烧写程序引脚被设置为别的功能 这种情况也是最常见的 我们可以把复位电容短路 xff0c 让单片机复位 xff0c 然后点
  • STM32F407以太网DMA描述符和数据链路层收发数据

    本文主要介绍STM32F407单片机MAC内核的DMA描述符 xff0c 以及如何实现以太网二层的数据收发 这一篇先实现数据链路层的正常收发 xff0c 下一篇再去介绍如何把LWIP移植到单片机上 大部分资料都是把LWIP移植和以太网卡驱动
  • linux查看日志常用命令

    线上环境出现问题 xff0c 熟悉常用的日志操作命令 xff0c 对有效的排查出问题至关重要 下面将介绍一些常用的命令 xff0c 一起学习下 1 tail命令 xff08 查询日志文件尾部 xff09 tail f 日志文件 xff1a
  • 定时事件链表

    本文主要写的是 xff1a 将需要定时的事件作为一个链表节点添加到链表中 所写代码是从LWIP源码中复制出来的 xff0c 稍作修改 当阅读到lwip源码timers c文件中的sys timeout函数时 xff0c 觉得非常适合如下一种