我学单片机初期的时候是没有接触过什么操作系统的,后续接触过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的时候清除,去打开继电器就好了,当然这里提到的这种处理方式在项目中肯定有弊端,延时需求多标志位设立太多,不过后面我还会整理其他笔记。上面就是很常用的时间片轮训的一种简单架构写法。其他写法的原理也都差不多,都是通过定时器计时。有更高深,或者其他方式希望指导。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/GQli2296523792/article/details/144116686
|