单片机裸机常用的时间片轮训系统

        我学单片机初期的时候是没有接触过什么操作系统的,后续接触过FreeRTOS,了解过一下时间片轮训和抢占式。但是工作发现许多工控行业的产品用不到移植操作系统。

        那我学习初期遇到一种问题就很棘手,比如我想在ADC采集温度到88度时,打开一个继电器,延时30后,打开第二个继电器。这个延时肯定不能在while{1}里面用delay延时,会导致整个程序都卡住30s。为了处理这种问题可以采取下面这种写法。

        time.h头文件代码:

#include <stdint.h>

#ifndef _TIME_H
#define _TIME_H



typedef enum{
	Task_1MS,
	Task_10MS,
	Task_50MS,
	Task_100MS,
	Task_500MS,
	Task_1000MS,
	Task_Max
}Sys_Task_Type;

void Tim6_Init(void);
void Timer_CallBack_Handler(void);
void Sys_Task_Reload(Sys_Task_Type type);
extern uint16_t Sys_Run_Task[Task_Max];

#endif

time.c源文件代码:

#include "time.h"

uint16_t Sys_Run_Task[Task_Max] = {1,10,50,100,500,1000};


//基本定时器
void Tim6_Init()
{
	timer_parameter_struct timer_initpara;
    rcu_timer_clock_prescaler_config (RCU_TIMER_PSC_MUL4);
    timer_deinit(TIMER6);

    timer_initpara.prescaler         = 168-1;					//分频系数
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 1000-1;				//重装载值
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER6,&timer_initpara);

	timer_update_event_enable(TIMER6);						//更新事件
	timer_interrupt_flag_clear(TIMER6,TIMER_FLAG_UP);		//清除更新中断标志位
    timer_interrupt_enable(TIMER6,TIMER_INT_FLAG_UP);		//更新中断使能

	nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
	nvic_irq_enable(TIMER6_IRQn,2,2);
	
	timer_enable(TIMER6);									//启用计数器
}


void TIMER6_IRQHandler(void)
{
	if(RESET != timer_interrupt_flag_get(TIMER6,TIMER_INT_FLAG_UP))
	{
		Timer_CallBack_Handler();	
		timer_interrupt_flag_clear(TIMER6,TIMER_INT_FLAG_UP);//清除更新中断标志位
	}
}


//遍历 Sys_Run_Task 数组,将每个任务的剩余运行时间减一
void Timer_CallBack_Handler(void)
{
	uint8_t index = 0;
	for(index = 0;index < Task_Max;index++)
	{
		if(Sys_Run_Task[index] > 0)
		{
			Sys_Run_Task[index]--;
		}
	}
}


void Sys_Task_Reload(Sys_Task_Type type)//定义一个枚举类型的变量
{
	switch((uint8_t)type)
	{
		case Task_1MS://0
		{
			Sys_Run_Task[type] = 1;
			break;
		}
		case Task_10MS:
		{
			Sys_Run_Task[type] = 10;
			break;
		}
		case Task_50MS:
		{
			Sys_Run_Task[type] = 50;
			break;
		}
		case Task_100MS:
		{
			Sys_Run_Task[type] = 100;
			break;
		}
		case Task_500MS:
		{
			Sys_Run_Task[type] = 500;
			break;
		}
		case Task_1000MS:
		{
			Sys_Run_Task[type] = 1000;
			break;
		}
	}
}

上面有4个函数

一个是配置1ms进一次中断的基本定时器初始化函数;

 一个中断服务函数,用来调用自减函数;

一个循环遍历的自减函数;

一个填充赋值函数;

main.c文件

include "main.h"


int main(void)
{
	All_Periph_Clock_Enable();							//外设时钟使能
	All_Config();										//外设初始化
	Nvic_Config();										//中断通道配置	

    while(1)
	{	
		if(Sys_Run_Task[Task_1MS] == 0)
		{
			
			Sys_Task_Reload(Task_1MS);
		}
		if(Sys_Run_Task[Task_10MS] == 0)
		{

			Sys_Task_Reload(Task_10MS);
		}
		if(Sys_Run_Task[Task_50MS] == 0)
		{
			
			Sys_Task_Reload(Task_50MS);
		}
		if(Sys_Run_Task[Task_100MS] == 0)
		{
			
			
			Sys_Task_Reload(Task_100MS);
		}
		if(Sys_Run_Task[Task_500MS] == 0)
		{
			
			Sys_Task_Reload(Task_500MS);
		}
		if(Sys_Run_Task[Task_1000MS] == 0)
		{
			

			Sys_Task_Reload(Task_1000MS);
		}
	}
}

        基本逻辑就是每ms执行一次中断,将数组的数据都减1,在main.c里面的while{1}里,循环的判断数组的数据什么时候减到为0,等于0的立马给填充回原先设定的时间。上面设定了1,10,50,100,500,1000ms不同的时间,时间一到就执行对应部分。

        现在再想处理最初的30s延时功能,只需要在1s的条件下,设定一个标志累加30次,到30的时候清除,去打开继电器就好了,当然这里提到的这种处理方式在项目中肯定有弊端,延时需求多标志位设立太多,不过后面我还会整理其他笔记。上面就是很常用的时间片轮训的一种简单架构写法。其他写法的原理也都差不多,都是通过定时器计时。有更高深,或者其他方式希望指导。

### 前端事件轮训的实现与优化 #### 什么是事件轮训? 事件轮训是一种常见的前端性能优化手段,其主要目的是控制高频事件触发时的行为频率。通过减少不必要的重复操作来提高应用性能和用户体验。根据描述的内容[^1],节流(throttling)的核心思想是在固定的周期内仅允许执行一次特定的动作。 #### 如何实现事件轮训? 以下是基于 JavaScript 的一种简单实现方式: ```javascript function throttle(func, delay) { let lastCall = 0; // 记录上次调用时间 return function (...args) { const now = new Date().getTime(); // 获取当前时间戳 if (now - lastCall >= delay) { // 判断是否超过设定的时间间隔 func.apply(this, args); // 执行函数并传递参数 lastCall = now; // 更新最后一次调用时间为当前时间 } }; } // 使用示例 const handleScroll = () => console.log('滚动事件被触发'); window.addEventListener('scroll', throttle(handleScroll, 300)); ``` 此代码片段定义了一个 `throttle` 函数,它接受两个参数:要节流的目标函数以及指定的时间间隔(单位为毫秒)。当目标函数绑定到某个事件监听器时,只有在两次连续触发之间超过了给定的时间间隔才会实际运行该函数。 #### 性能优化建议 为了进一步提升效率,可以从以下几个方面着手改进: 1. **区分前缘与延迟模式**: 根据需求决定采用哪种类型的节流逻辑——如果需要立即响应用户的首次交互,则应选用前缘版本;反之则适合于延迟型设计。这有助于更好地满足具体场景下的业务诉求[^1]。 2. **利用浏览器原生特性**: 对某些特殊类型的高频率输入源(如鼠标移动), 可考虑借助 CSS 或者 HTML 属性本身提供的解决方案代替手动编写复杂的脚本处理流程。例如设置 overflow:hidden 来阻止超出容器范围内的拖拽行为从而间接降低 scroll handler 的负担. 3. **结合 HTTPDNS 技术加速资源加载过程** : 尽管严格意义上不属于直接针对 DOM Event Handler 的范畴之内 ,但如果整个 Web 应用依赖大量外部静态文件或者 API 接口请求的话 ,那么合理运用诸如文中提到的手淘团队开发出来的 httpdns 方案确实可以在一定程度上缓解因 DNS 查询耗时较长而导致的整体卡顿现象 [^3]. 4. **自动化预加载机制引入** : 同样出于全局视角考量而非单纯聚焦局部细节层面的操作而言 ,提前预测即将需要用到的数据并通过科学规划安排好它们下载时机也是不可或缺的一环 。这样即使面对突发状况也能从容应对而不至于因为临时发起过多异步任务造成主线程阻塞等问题发生 . 综上所述,通过对基础算法结构加以调整配合其他辅助措施共同作用便能达到理想中的效果平衡点既兼顾流畅度又不失精确性 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GQli2048

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值