【嵌入式开源库】timeslice的使用,完全解耦的时间片轮询框架构

完全解耦的时间片轮询框架构

  • 简介
  • 项目代码
    • timeslice.h
    • timeslice.c
    • list.h
    • list.c
  • 创建工程
  • 移植代码
  • 实验
  • 函数说明
    • timeslice_task_init
    • timeslice_task_add
    • timeslice_tak_del
    • timeslice_get_task_num
  • 结尾

简介

timeslice是一个时间片轮询框架,他是一个完全解耦的时间片轮询框架,他的使用非常方便,该项目一共有四个文件分别是tieslice的头文件和源文件以及list的头文件和源文件,tieslice是负责轮询任务,list是一个双向链表负责任务的管理,在Linux内核中使用非常广泛也很经典,该框架是参考rtt实时操作系统的侵入式链表实现的,本章文章是将该框架移植到stm32单片机上实验,使用也非常容易,单片机只需要启用一个定时器作为时钟即可;

本章使用环境:

stm32f407vet6
代码工程使用cubemx创建

项目代码

该项目的代码是我在微信公众号上看到的一个文章,代码并没有上传在github上,这里直接贴上源代码;

timeslice.h

#ifndef _TIMESLICE_H
#define _TIMESLICE_H#include "list.h"typedef enum {TASK_STOP,TASK_RUN
} IsTaskRun;typedef struct timesilce
{unsigned int id;void (*task_hdl)(void);IsTaskRun is_run;unsigned int timer;unsigned int timeslice_len;ListObj timeslice_task_list;
} TimesilceTaskObj;void timeslice_exec(void);
void timeslice_tick(void);
void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len);
void timeslice_task_add(TimesilceTaskObj* obj);
void timeslice_task_del(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_num(void);
unsigned char timeslice_task_isexist(TimesilceTaskObj* obj);#endif

timeslice.c


#include "timeslice.h"static LIST_HEAD(timeslice_task_list);void timeslice_exec()
{ListObj* node;TimesilceTaskObj* task;list_for_each(node, &timeslice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (task->is_run == TASK_RUN){task->task_hdl();task->is_run = TASK_STOP;}}
}void timeslice_tick()
{ListObj* node;TimesilceTaskObj* task;list_for_each(node, &timeslice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (task->timer != 0){task->timer--;if (task->timer == 0){task->is_run = TASK_RUN;task->timer = task->timeslice_len;}}}
}unsigned int timeslice_get_task_num()
{return list_len(&timeslice_task_list);
}void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
{obj->id = id;obj->is_run = TASK_STOP;obj->task_hdl = task_hdl;obj->timer = timeslice_len;obj->timeslice_len = timeslice_len;
}void timeslice_task_add(TimesilceTaskObj* obj)
{list_insert_before(&timeslice_task_list, &obj->timeslice_task_list);
}void timeslice_task_del(TimesilceTaskObj* obj)
{if (timeslice_task_isexist(obj))list_remove(&obj->timeslice_task_list);elsereturn;
}unsigned char timeslice_task_isexist(TimesilceTaskObj* obj)
{unsigned char isexist = 0;ListObj* node;TimesilceTaskObj* task;list_for_each(node, &timeslice_task_list){task = list_entry(node, TimesilceTaskObj, timeslice_task_list);if (obj->id == task->id)isexist = 1;}return isexist;
}unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj)
{return obj->timeslice_len;
}

list.h


#ifndef _LIST_H
#define _LIST_H#define offset_of(type, member)             (unsigned long) &((type*)0)->member
#define container_of(ptr, type, member)     ((type *)((char *)(ptr) - offset_of(type, member)))typedef struct list_structure
{struct list_structure* next;struct list_structure* prev;
} ListObj;#define LIST_HEAD_INIT(name)    {&(name), &(name)}
#define LIST_HEAD(name)         ListObj name = LIST_HEAD_INIT(name)void list_init(ListObj* list);
void list_insert_after(ListObj* list, ListObj* node);
void list_insert_before(ListObj* list, ListObj* node);
void list_remove(ListObj* node);
int list_isempty(const ListObj* list);
unsigned int list_len(const ListObj* list);#define list_entry(node, type, member) \container_of(node, type, member)#define list_for_each(pos, head) \for (pos = (head)->next; pos != (head); pos = pos->next)#define list_for_each_safe(pos, n, head) \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = pos->next)#endif

list.c

#include "list.h"void list_init(ListObj* list)
{list->next = list->prev = list;
}void list_insert_after(ListObj* list, ListObj* node)
{list->next->prev = node;node->next = list->next;list->next = node;node->prev = list;
}void list_insert_before(ListObj* list, ListObj* node)
{list->prev->next = node;node->prev = list->prev;list->prev = node;node->next = list;
}void list_remove(ListObj* node)
{node->next->prev = node->prev;node->prev->next = node->next;node->next = node->prev = node;
}int list_isempty(const ListObj* list)
{return list->next == list;
}unsigned int list_len(const ListObj* list)
{unsigned int len = 0;const ListObj* p = list;while (p->next != list){p = p->next;len++;}return len;
}

创建工程

配置高速时钟和低速时钟为外部晶振提供
在这里插入图片描述
配置调试模式为sw调试模式

在这里插入图片描述配置时钟频率
在这里插入图片描述配置led,这里我的板子是这三个io接入的是led
在这里插入图片描述
配置一个10ms的定时器(1000000hz / 1000ms == 1ms = 1khz 就得到10ms需要计数10000重载)记得打开中断
在这里插入图片描述
在这里插入图片描述

配置工程并生成,工程名设置,单独生成.c.h文件拷贝库文件
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
下面开始代码的移植工作;

移植代码

首先我们需要将该开源项目的代码添加到工程中
在这里插入图片描述在定时器中断服务函数中添加timeslice轮询函数
在这里插入图片描述在这里插入图片描述

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){timeslice_tick();}
}

然后我们需要在main函数中启动定时器并在while1中调用exec函数调度时间片

 HAL_TIM_Base_Start_IT(&htim3);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */timeslice_exec();}

到这里我们的移植工作就做完了,该项目的解耦效果真的非常好,移植相当简单,然后我们创建几个任务实验一下效果;

实验

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "timeslice.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* 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 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){timeslice_tick();}
}// 创建3个任务对象
TimesilceTaskObj task_1, task_2, task_3;// 具体的任务函数
void task1_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_13);
}void task2_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_14 );
}void task3_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_15);
}// 初始化任务对象,并且将任务添加到时间片轮询调度中
void task_init()
{timeslice_task_init(&task_1, task1_hdl, 1, 1);timeslice_task_init(&task_2, task2_hdl, 2, 1);timeslice_task_init(&task_3, task3_hdl, 3, 1);timeslice_task_add(&task_1);timeslice_task_add(&task_2);timeslice_task_add(&task_3);
}
/* 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_TIM3_Init();/* USER CODE BEGIN 2 */HAL_TIM_Base_Start_IT(&htim3);task_init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */timeslice_exec();}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 25;RCC_OscInitStruct.PLL.PLLN = 336;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

核心部分

// 创建3个任务对象
TimesilceTaskObj task_1, task_2, task_3;// 具体的任务函数
void task1_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_13);
}void task2_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_14 );
}void task3_hdl()
{HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_15);
}// 初始化任务对象,并且将任务添加到时间片轮询调度中
void task_init()
{timeslice_task_init(&task_1, task1_hdl, 1, 1);timeslice_task_init(&task_2, task2_hdl, 2, 2);timeslice_task_init(&task_3, task3_hdl, 3, 3);timeslice_task_add(&task_1);timeslice_task_add(&task_2);timeslice_task_add(&task_3);
}

需要注意的是必须要有一个任务是需要在exec前创建,这样才能保证运行,其他的任务可以在这个任务中再创建,上面的实验是实现三个任务,三个任务分别为一个中断触发一次,第二个任务是每隔两个中断触发一次,第三个任务是每隔三个中断触发一次任务;

函数说明

timeslice_task_init

初始化任务函数

void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
{obj->id = id;obj->is_run = TASK_STOP;obj->task_hdl = task_hdl;obj->timer = timeslice_len;obj->timeslice_len = timeslice_len;
}

在这个函数中将任务结构体参数初始化,id类似于任务名称用于区分任务,is_run是一个标志位用于判断该任务在该次中断是否需要执行,task_hd1表示函数指针也就是我们的任务函数,timer表示每多少次中断触发一次计数,timeslice_len 表示没多少次中断触发一次计数初始值,在timeslice_tick中当timer的值减到0任务将触发并会重新复位timer的值为 timeslice_len ;
在这里插入图片描述

timeslice_task_add

添加任务到双向链表中

void timeslice_task_add(TimesilceTaskObj* obj)
{list_insert_before(&timeslice_task_list, &obj->timeslice_task_list);
}void list_insert_before(ListObj* list, ListObj* node)
{list->prev->next = node;node->prev = list->prev;list->prev = node;node->next = list;
}

该链表(timeslice_task_list)在timeslice_tick中会轮询进行遍历

timeslice_tak_del

删除正在运行的任务链表

void timeslice_task_del(TimesilceTaskObj* obj)
{if (timeslice_task_isexist(obj))list_remove(&obj->timeslice_task_list);elsereturn;
}

在该函数中会通过timeslice_task_isexist函数去判断链表中是否存在该任务的id,如果存在将返回退出允许,这里涉及到一个Linux中的函数list_entry->container_of,该函数是通过结构体的某个变量获取整个结构体的指针位置,有兴趣可以去学习一下该项目代码的实现;

timeslice_get_task_num

获取当前任务数量,也就是链表的长度

unsigned int timeslice_get_task_num()
{return list_len(&timeslice_task_list);
}unsigned int list_len(const ListObj* list)
{unsigned int len = 0;const ListObj* p = list;while (p->next != list){p = p->next;len++;}return len;
}

结尾

整体的代码不算复杂但是是值得学习的一个项目,我是凉开水白菜祝各位程序员们节日快乐~ 咱们下文见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/121260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三十七、【进阶】验证索引的效率

1、准备工作: 创建一张表,该表中有一千万条数据,名为tb_sku; 2、使用主键查询: select * from tb_stu where id1\G; 3、使用非索引查询: 4、给sn字段创建索引: 在创建过程中,发现…

如何使用 nvm-windows 这个工具来管理你电脑上的Node.js版本

nvm-windows 是一个用于管理在 Windows 上安装的多个 Node.js 版本的工具。以下是安装和使用 nvm-windows 的步骤: 第1步:下载 nvm-windows 访问 nvm-windows 的 GitHub发布页面.下载最新版本的 nvm-setup.zip 文件。 第2步:安装 nvm-wind…

conda虚拟环境笔记收录

1、安装conda 增加执行权限: chmod x Anaconda3-2023.03-1-Linux-x86_64.sh 开始执行:./Anaconda3-2023.03-1-Linux-x86_64.sh2、查看版本 conda --version3、查看当前虚拟环境 虚拟环境和全局环境有前缀可见 如果不进行设置,重新启动就变成…

MySQL的基础(一)

MySQL的基础(一) SQLSQL的语法特点主要包括以下几点:一、 SQL - DDL -- 数据定义语言1.1 数据库操作1.1 显示现有的数据库1.2 创建数据库1.3 删除数据库1.4 使用 1.2 数据表操作1.2.1 表查询1.2.2 表创建1.2.3 修改表 1.2.4 小结 二、SQL - D…

calcite 校验层总结

1、校验的作用 1)完善语义信息 例如在SQL语句中,如果碰到select * 这样的指令,在SQL的语义当中,“*” 指的是取出对应数据源中所有字段的信息,因此就需要根据元数据信息来展开。 2)结合元数据信息来纠偏…

特殊类设计[下] --- 单例模式

文章目录 5.只能创建一个对象的类5.1设计模式[2.5 万字详解:23 种设计模式](https://zhuanlan.zhihu.com/p/433152245)5.2单例模式1.饿汉模式1.懒汉模式 6.饿汉模式7.懒汉模式7.1饿汉模式优缺点:7.2懒汉模式1.线程安全问题2.单例对象的析构问题 8.整体代码9.C11后可…

UVa129 Krypton Factor(困难的串)

1、题目 2、题意 如果一个字符串包含两个相邻的重复子串,则称它是“容易的串”,其他串称为“困难的串”。例如,BB、ABCDACABCAB、ABCDABCD都是容易的的串,而D、DC、ABDAB、CBABCBA 都是困难的串。 输入正整数 k k k 和 L L L&a…

Linux虚拟机部署与发布项目(Windows版本)

目录 前言 一、虚拟机部署项目的流程 二、单机项目 1. 本机测试 2.虚拟机部署项目 三、前后端分离项目 前言 在软件开发过程中,部署和发布项目是非常重要的一环。使用虚拟机技术可以方便、灵活且可重复使用地部署和发布项目。本篇博客将介绍如何在 Windows 环…

【密评】商用密码应用安全性评估从业人员考核题库(十四)

商用密码应用安全性评估从业人员考核题库(十四) 国密局给的参考题库5000道只是基础题,后续更新完5000还会继续更其他高质量题库,持续学习,共同进步。 3251 单项选择题 根据GB/T 39786《信息安全技术 信息系统密码应用…

Go基础——基础语法

1、简介 Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。语法类似于C,专为高性能和并发程序而设计。通常用于网络编程、云服务器、游戏服务器、DevOps、Web应用、分布式系统、容器虚…

mysql-linux归档版安装

什么是归档版安装?简单来说就是编译好的软件压缩打包版。 说明:我这里服务器之前已经装过一个不同版本的mysql,已经占用了3306端口,所以这里我用3307端口来演示,命令和官方的稍有不同,不过步骤都是差不多的…

搜索引擎搜索技巧总结

晚上在B站上刷到一个关于搜索技巧的干货视频,这个视频真的不错,结尾还提到了AI时代的搜索思路之前自己也零碎的探索出了一些搜索技巧,但是没有总结,就没法稳定的加入自己的工作流,持续提高效率受到这个视频的启发&…

计算机视觉 激光雷达结合无监督学习进行物体检测的工作原理

一、简述 激光雷达是目前正在改变世界的传感器。它集成在自动驾驶汽车、自主无人机、机器人、卫星、火箭等中。该传感器使用激光束了解世界,并测量激光击中目标返回所需的时间,输出是点云信息,利用这些信息,我们可以从3D点云中查找障碍物。 从自动驾驶汽车的角度看激光雷达…

专业135总分400+西安交通大学信息与通信工程学院909/815考研经验分享

今年初试发挥不错,400,专业课135,将近一年复习一路走来,感慨很多,希望以下经历可以给后来的同学提供一些参考。 初试备考经验 公共课:三门公共课,政治,英语,数学。在备考…

轮转数组(Java)

大家好我是苏麟 , 这篇文章是凑数的 ... 轮转数组 描述 : 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 题目 : 牛客 NC110 旋转数组: 这里牛客给出了数组长度我们直接用就可以了 . LeetCode 189.轮转数组 : 189. 轮…

Nginx 的配置文件(负载均衡,反向代理)

Nginx可以配置代理多台服务器,当一台服务器宕机之后,仍能保持系统可用。 cmd查找端口是否使用:netstat -ano Nginx出现403 forbidden #解决办法:修改web目录的读写权限,或者是把nginx的启动用户改成目录的所属用户&…

田忌赛马(贪心算法)

分为两种情况: 1.田最快 快于 王最快(田最快面对任何都赢) ->故和王最快比,消耗王最快 2.田最快 小于等于 王最快(王最快面对任何都可赢) 则比最慢(意在找一匹无法赢任何马的马&#…

20.1 OpenSSL 字符BASE64压缩算法

OpenSSL 是一种开源的加密库,提供了一组用于加密和解密数据、验证数字证书以及实现各种安全协议的函数和工具。它可以用于创建和管理公钥和私钥、数字证书和其他安全凭据,还支持SSL/TLS、SSH、S/MIME、PKCS等常见的加密协议和标准。 OpenSSL 的功能非常…

shell实验

1.编写脚本for1.sh,使用for循环创建20账户,账户名前缀由用户从键盘输入,账户初始密码由用户输入,例如:test1、test2、test3、....、test10 编写脚本,使用read -p提醒用户从键盘输入账户名前缀以…

[论文笔记]GTE

引言 今天带来今年的一篇文本嵌入论文GTE, 中文题目是 多阶段对比学习的通用文本嵌入。 作者提出了GTE,一个使用对阶段对比学习的通用文本嵌入。使用对比学习在多个来源的混合数据集上训练了一个统一的文本嵌入模型,通过在无监督预训练阶段和有监督微调阶段显著增加训练数…