当前位置: 首页 > news >正文

裸机条件下写一个基于时间片轮转的多任务并发程序

目录

  • 前言
    • A. 使用RTOS
    • B.裸机多任务并发

前言

在学习各种MCU的时候,都是用在main函数里写一个while(1){/* 执行代码 */},这种方式只能一个函数运行完以后再运行另一个函数。
假设需求控制多个模块,如显示屏幕信息的同时控制电机,还要一边接收按键输入。如果用上面的方式每个模块要排队等待CPU运行,就会显的很卡。
那有没有办法每个模块运行固定的时间,时间到了运行下一个模块,这样单个模块即使特别耗时,也不影响其他模块的运行,这个方法叫时间片轮转。
想到这个办法很容易,但要怎么编写代码呢?

A. 使用RTOS

根据不要重复造轮子的理论,能用现有的开源代码当然是最好的了。如类STM32常用的操作系统有uCOS, RT-thread(国产),FreeRTOS。
用现成的去官网等地方搜移植教程,本文不再详述。
用这些开源代码的问题是如果MCU RAM或ROM太小,删减起来就不太方便了,这时候手搓一个多任务并发系统的作用来了。

B.裸机多任务并发

时间片轮转的基本思路就是通过定时器(最好是硬件定时器)将CPU的运行时间切片成一个个时间片,代码里叫tick,然后一个任务每运行一个时间片,tick计数加1,当前任务运行的tick数已经达到分配给任务的tick数,就不再执行当前任务执行下一个任务。

先定义一个最任务结构体,至少包括任务主体函数,分配给任务的时间片tick数和当前运行已经消耗的时间片tick计数。用结构体就是为了方便扩展用的,还可以增加参数比如任务使能,任务偏移量等。这样就使任务执行更加灵活。

typedef void (*Func)(void);
typedef struct task_info_t
{Func func;                /* 任务主体函数 */uint16_t task_tick;        /* 分配给任务的时间片tick数 */uint16_t tast_tick_cnt;    /* 当前运行已经消耗的时间片tick计数 */
} TaskInfo_t;

初学者 typedef void (*Func)(void); 看不懂,这个是定义一种函数类型,这种函数类型是void (*)(void)型的,给他取个别名叫Func. 相当于下面这种写法:
不清楚的看这篇文章typedef void *(Func)(void)用法

typedef struct task_info_t
{void (*func)(void);                /* 任务主体函数 */uint16_t task_tick;        /* 分配给任务的时间片tick数 */uint16_t tast_tick_cnt;    /* 当前运行已经消耗的时间片tick计数 */
} TaskInfo_t;

定义好结构体后创建一个结构体数组,在数组里初始化任务的参数,结构体数组不清楚的看这里结构体数组

TaskInfo_t TaskInfoArray[] = {{IdleTask,  10,    0},{LedTask,   20,    0},{KeyTask,   5,     0},{MotorTask, 50,    0},/*任务名  执行时间 运行计数*/
}

然后要开启一个定时器中断(我是STM32的用这种方法,其他单片机可以用其他方法),比如将时间片定为1ms. 用STM32CubeMX直接配置一个1ms中断的定时器,也可以去问ChatGPT。我这里是开了一个定时器TIM3.

/*** @brief TIM3 Initialization Function* @param None* @retval None*/
static void MX_TIM3_Init(void)
{TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_OC_InitTypeDef sConfigOC = {0};htim3.Instance = TIM3;htim3.Init.Prescaler = 850;htim3.Init.CounterMode = TIM_COUNTERMODE_UP;htim3.Init.Period = 65535;htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_OC_Init(&htim3) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_TOGGLE;sConfigOC.Pulse = 0;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;if (HAL_TIM_OC_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK){Error_Handler();}HAL_TIM_MspPostInit(&htim3);
}

然后设置一个任务运行1ms的标志位,定义成全局变量 task1ms_Flag,定时器里把这个标志位至1.

/*** @brief This function handles TIM3 global interrupt.*/
uint8_t task1ms_Flag = 0;
void TIM3_IRQHandler(void)
{task1ms_Flag = 1;HAL_TIM_IRQHandler(&htim3);
}

然后在while(1){…}里面写整个任务切换的程序,看懂逻辑不复杂

/*** @brief  The application entry point.* @retval int*/
int main(void)
{/*初始化代码*/uint8_t i;TaskInfo_t *p_task;while(1){if (0 == task1ms_Flag)return; //当前运行结束,1ms没到,不需要切换任务elsetask1ms_Flag = 0; //当task1ms_Flag为1时运行,先重置为0for (i = 0; i < sizeof(TaskInfoArray) / sizeof(TaskInfo_t); i++)  //sizeof(TaskInfoArray) / sizeof(TaskInfo_t)为任务数,每1ms时间片轮转一遍{p_task = &TaskInfoArray[i];   //从任务0开始任务轮转if( p_task->task_tick_cnt >= tsk_ptr->tsk_tick )  //当前任务已经消耗tick大于分配的tick{tsk_ptr->func();  //执行当前任务i的函数主体tsk_ptr->tst_tick_cnt = tsk_ptr->tst_tick_cnt % tsk_ptr->tsk_tick; //当前任务已经消耗tick减去分配的tick,相当于清零。}else //如果当前任务已经消耗tick小于分配的tick,则当前任务已经消耗tick加一{tsk_ptr->tst_tick_cnt++;  }}}
}

完成这些配置后就可以写每个任务的执行函数了,和RT-thread不一样,这些任务函数里不要写while(1)

void IdleTask(void)
{
}void LedTask(void)
{
}void KeyTask(void)
{
}void MotorTask(void)
{
}

整个系统的逻辑就是时间片轮转执行Task程序,如IdleTask执行10ms,LedTask执行20ms,KeyTask执行5ms,MotorTask执行50ms。

注意每个任务里如果要写延时函数注意延时的总时间不要大于分配的时间片。

相关文章:

裸机条件下写一个基于时间片轮转的多任务并发程序

目录前言A. 使用RTOSB.裸机多任务并发前言 在学习各种MCU的时候&#xff0c;都是用在main函数里写一个while(1){/* 执行代码 */}&#xff0c;这种方式只能一个函数运行完以后再运行另一个函数。 假设需求控制多个模块&#xff0c;如显示屏幕信息的同时控制电机&#xff0c;还要…...

RK3588 系统定制开关机动画

平台&#xff1a;ITX-3588J, ROC-RK3588S-PC 系统&#xff1a;Android12.0 作者&#xff1a;jpchen & zzz 一. 功能描述 定制自己的开机动画和关机动画 二. 功能实现 1.开启功能 修改device/rockchip/common/BoardConfig.mk文件 BOOT_SHUTDOWN_ANIMATION_RINGINGtrue2.…...

水文-编程命令快查手册

前言 脑子里面记不住一些命令&#xff0c;每次遇到都得查下。我经常在三个实体电脑&#xff0c;windows/uos/ubuntu不同系统上编程。 所以web版本的笔记查看起来方便点。这里报错下。 二级标题 cmake windows在cmake --build的时候&#xff0c;使用–config&#xff0c;指定…...

如何优雅编写测试用例

当你学会了如何设计测试用例之后&#xff0c;接下来便是开始用例的编写。 在设计阶段&#xff0c;更准确的说应该是识别测试点的过程&#xff0c;而编写阶段则是将测试点细化成一条条测试用例的过程&#xff0c;有了比较全的用例场景后&#xff0c;如何让别人更舒服、更方便、…...

[入门必看]数据结构2.3:线性表的链式表示

[入门必看]数据结构2.3&#xff1a;线性表的链式表示第二章 线性表2.3 线性表的链式表示知识总览2.3.1 单链表的定义2.3.2_1 单链表的插入删除2.3.2_2 单链表的查找2.3.2_3 单链表的建立2.3.3 双链表2.3.4 循环链表2.3.5 静态链表2.3.6 顺序表和链表的比较2.3.1 单链表的定义单…...

Golang流媒体实战之二:回源

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 今天的实战是流传输过程中的常见功能&#xff1a;回源如下图&#xff0c;lal(源站)和lal(拉流节点)代表两台电脑&#xff0c;上面都部署了lalVLC在…...

webgl——给场景添加光

文章目录前言光照理论介绍光照效果光源类型反射光颜色向场景中添加光向场景中添加环境光和点光源逐片元光照——更加逼真总结前言 在之前的学习中已经将三维物体添加到了场景中&#xff0c;但是并没有在场景中使用光&#xff0c;照可以使模型更具有立体感&#xff0c;本文主要…...

Vue实战【Vue项目开发时常见的几个错误】

目录&#x1f31f;前言&#x1f31f;安装超时(install timeout)&#x1f31f;can’t not find ‘xxModule’ - 找不到某些依赖或者模块&#x1f31f;data functions should return an object&#x1f31f;给组件内的原生控件添加事件,不生效了&#x1f31f;我在函数内用了this.…...

【多线程】常见的锁策略

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; ✨每日一语&#xff1a;老当益壮&#xff0c;宁移白首之心&#xff1b;穷且益坚&#xff0c;不坠青云之志。 目 录&#x1f3f3;️一. 乐观锁 vs 悲观锁&#x1f3f4;二. 普通的互斥…...

如何让虚拟机里的Ubuntu通过连接手机USB数据线上网

目录 一 前言 二 Windows联网方法 三 Ubuntu联网方法 一 前言 最近遇到了这样一个问题&#xff0c;有一台台式机&#xff0c;地插网口无法访问外网&#xff0c;周边也没有无线路由器&#xff0c;要访问外网&#xff0c;该如何做&#xff1f;进一步的&#xff0c;这台台式机…...

windows渗透(sam、system文件导出)

通过本地PC中渗透测试平台Kali对服务器场景Windows进行系统服务及版本扫描渗透测试,并将该操作显示结果中Telnet服务对应的端口号作为FLAG提交;通过本地PC中渗透测试平台Kali对服务器场景Windows进行系统服...

b01lers(php.galf)

目录 前文 正文 前文 <?phpclass A{public $codeNULL;public $argsNULL;public function __construct($code,$argsNULL){$this->code$code;$this->args$args;print_r("2333") ;} public function __invoke($code,$args){echo $code;print_r("执行inv…...

记一次若依后台管理系统渗透

前言 最近客户开始hw前的风险排查&#xff0c;让我们帮他做个渗透测试&#xff0c;只给一个单位名称。通过前期的信息收集&#xff0c;发现了这个站点&#xff1a; 没有验证码&#xff0c;再加上这个图标&#xff0c;吸引了我注意&#xff1a; 从弱口令开始 若依默认口令为ad…...

Mybatis(四):自定义映射resultMap

自定义映射resultMap前言一、处理字段和属性的映射关系问题&#xff1a;方案一&#xff1a;使用别名方案二&#xff1a;在mybatis-config.xml中设置mapUnderscoreToCamelCase方案三&#xff1a;在映射文件中设置redultMap二、多对一映射处理问题&#xff1a;方案一&#xff1a;…...

机器学习---降维算法

知其然知其所以然【写在前面】主成分分析&#xff08;PCA&#xff09;原理部分代码部分可视化部分线性判别分析&#xff08;LDA&#xff09;原理部分代码部分可视化部分独立成分分析&#xff08;ICA&#xff09;原理部分代码部分可视化部分t-SNE降维算法原理部分代码部分可视化…...

【Vue2从入门到精通】详解Vue.js的15种常用指令及其使用场景

文章目录前言1. v-text / {{ expression }}2.v-html3.v-bind4.v-on5. v-model6.v-for7.v-if / v-else-if / v-else9.v-show10.v-cloak11.v-pre12.组件注册指令13.动态组件指令14.自定义指令15.过滤器指令前言 Vue.js 是一款流行的前端框架&#xff0c;它通过指令&#xff08;Di…...

数据库知识总结

数据库知识点总结个人向。 目录第一章 绪论第二章 关系数据库第三章 关系数据库标准语言SQL第四章 数据库安全性第五章 数据库完整性第六章 关系数据理论第七章 数据库设计第十章 数据库恢复技术第十一章 并发控制第一章 绪论 数据(data): 描述事物的符号记录。 数据库(DataB…...

处理数组循环中删除元素导致索引错位情况

就是很多时候我们对一个数组进行操作的时候&#xff0c;在for遍历的过程中删掉了一个元素&#xff0c;那么在删掉那个元素之后的所有元素的索引值都会减少一位&#xff0c;数组长度缩短一位&#xff0c;删完之后&#xff0c;正在进行的循环会继续循环下去&#xff0c;但是循环的…...

快速排序,分治法实际应用(含码源与解析)

&#x1f38a;【数据结构与算法】专题正在持续更新中&#xff0c;各种数据结构的创建原理与运用✨&#xff0c;经典算法的解析✨都在这儿&#xff0c;欢迎大家前往订阅本专题&#xff0c;获取更多详细信息哦&#x1f38f;&#x1f38f;&#x1f38f; &#x1fa94;本系列专栏 -…...

linux入门---操作体统的概念

什么是操作系统 操作系统是一个对软硬件资源进行管理的软件。计算机由一堆硬件组成&#xff0c;这些硬件遵循着冯诺依曼体系结构 在这个硬件的基础上还有一个软件叫做操作系统 操作系统的任务是对硬件进行管理&#xff0c;既然是管理的话操作系统得访问到底层的硬件&#xf…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...

密码学基础——SM4算法

博客主页&#xff1a;christine-rr-CSDN博客 ​​​​专栏主页&#xff1a;密码学 &#x1f4cc; 【今日更新】&#x1f4cc; 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 ​编辑…...