关于时间触发器的多任务思路

多个LED以不同频率运行程序变形

打开网易新闻 查看精彩图片

本文教你一步一步走进多任务,用非常接地气的方式带你了解多任务,无论是嵌入式新手小白还是入行多年嵌入式大佬都有一定的借鉴作用,接下来我们多种方法来实现三个LED灯以不同的频次闪烁

实验内容:黄色灯1Hz(500ms),蓝色灯2Hz(250ms),红色灯4Hz(125ms)

引脚关系:黄色灯->P0.0、蓝色灯->P0.1、红色灯->P0.2

方案一:在大循环中计数的方式

实验效果:

打开网易新闻 查看精彩图片

程序代码:main.c文件

** 文件名称:main.c

** 描 述:LED灯闪烁

** 实验平台:STC12C5A60S2最小系统板 + 面包板 + 分立元器件

** 日 期:2020/01/25

** 作 者:yigo

** 历史记录: 硬件连接: YELLOW_LED->P0.0 BLUE_LED ->P0.1 RED_LED ->P0.2**************************************/

#include"main.h"#include"LED.h"/*********** 变量定义 *************/

#define SYSTEM_DELAY 5 // 系统延时5ms#define YELLOW_LED_TASK_TIME 100 // SYSTEM_DELAY*100 = 500ms任务#define BLUE_LED_TASK_TIME 50 // SYSTEM_DELAY*50 = 250ms任务#define RED_LED_TASK_TIME 25 // SYSTEM_DELAY*25 = 125ms任务uint8_tYELLOW_LED_Task_Count=YELLOW_LED_TASK_TIME;uint8_tBLUE_LED_Task_Count=BLUE_LED_TASK_TIME;uint8_tRED_LED_Task_Count=RED_LED_TASK_TIME;

/*************************************** 函 数 名:main** 输入参数:none** 返 回 值:none** 说 明:主函数**************************************/voidmain(void){

while(1)

// 1Hz黄色LED灯闪烁任务if(YELLOW_LED_Task_Count0)

YELLOW_LED_Task_Count=YELLOW_LED_TASK_TIME;

LED_Togle(YELLOW_LED);

// 2Hz蓝色LED灯闪烁任务if(BLUE_LED_Task_Count0)

BLUE_LED_Task_Count=BLUE_LED_TASK_TIME;

LED_Togle(BLUE_LED);

// 4Hz绿色LED灯闪烁任务if(RED_LED_Task_Count0)

RED_LED_Task_Count=RED_LED_TASK_TIME;

LED_Togle(RED_LED);

YELLOW_LED_Task_Count--;

BLUE_LED_Task_Count--;

RED_LED_Task_Count--;

// 5ms 系统延时delay(SYSTEM_DELAY);

/*************************************** 函 数 名:delay** 输入参数:需要延时ms数,16位无符号整数** 返 回 值:none** 说 明:延时函数**************************************/voiddelay(uint16_ttime){

while(--time){Delay1ms();}}/*************************************** 函 数 名:Delay1ms** 输入参数:none** 返 回 值:none** 说 明:1ms延时函数**************************************/voidDelay1ms(void)//@11.0592MHz{

uint8_ti,j;

_nop_();

_nop_();

_nop_();

i=11;

j=190;

do

while(--j);

}while(--i);}

/************* end of file **********/

main.h文件

/*************************************** 文件名称:main.h** 描 述:公共头文件** 实验平台:STC12C5A60S2最小系统板 + 面包板 + 分立元器件** 日 期:2020/01/24** 作 者:yigo** 历史记录: **************************************/

#ifndef __MAIN_H#define __MAIN_H#include#include/****** 变量类型定义 defines ******/

typedefunsignedcharuint8_t;typedefunsignedintuint16_t;

voiddelay(uint16_ttime);voidDelay1ms(void);

#endif/************* end of file **********/

LED.c文件

/*************************************** 文件名称:LED.c** 描 述:LED控制源文件** 实验平台:STC12C5A60S2最小系统板 + 面包板 + 分立元器件** 日 期:2020/01/25** 作 者:yigo** 历史记录: 硬件连接: YELLOW_LED->P0.0 BLUE_LED ->P0.1 RED_LED ->P0.2**************************************/#include"LED.h"

/*************************************** 函 数 名:LED_Togle** 输入参数:需要翻转的LED** 返 回 值:none** 说 明:LED翻转函数**************************************/voidLED_Togle(uint8_tLED){

switch(LED)

caseYELLOW_LED:

YELLOW_LED_PIN=~YELLOW_LED_PIN;

break;

caseBLUE_LED:

BLUE_LED_PIN=~BLUE_LED_PIN;

break;

caseRED_LED:

RED_LED_PIN=~RED_LED_PIN;

break;

default:

break;

/************* end of file **********/

LED.h文件

/*************************************** 文件名称:LED.h** 描 述:LED控制头文件** 实验平台:STC12C5A60S2最小系统板 + 面包板 + 分立元器件** 日 期:2020/01/25** 作 者:wllis** 历史记录: 硬件连接: YELLOW_LED->P0.0 BLUE_LED ->P0.1 RED_LED ->P0.2**************************************/

#ifndef __LED_H#define __LED_H#include"main.h"

#define YELLOW_LED 1#define BLUE_LED 2#define RED_LED 3/*********** 引脚定义 *************/sbitYELLOW_LED_PIN=P0^0;sbitBLUE_LED_PIN=P0^1;sbitRED_LED_PIN=P0^2;

/*********** 函数定义 *************/voidLED_Togle(uint8_tLED);

#endif/************* end of file **********/

实验依照我们预想的那样,实现了三个LED灯以不同频次闪烁;这个例子根本实现了多任务执行,任务执行的最小周期取决于我们的系统周期,像该例子中,系统周期就是5ms(200Hz);那么我们想要实现高于200Hz执行的任务能够修改系统延时SYSTEM_DELAY,参照LED闪烁的任务实现方式尝试添加其他任务 ,并验证实验效果。

思考:我们注意到while循环中用到了等待延时函数delay( SYSTEM_DELAY );,我们觉得这样还不够,有没有方法能够在main函数大循环中不用任何延时来实现不同频次LED灯的闪烁,大家能够短暂的思考下,带着对问题的思考我们一起来看下方案二的实现方式,或许会给你一些启发。

打开网易新闻 查看精彩图片

方案二:采用定时器

我们知道无论哪种单片机都有定时器,像高级一点的单片机还有专门的系统定时器(systick),比如说: STM32,那么同样是减法运算,我们把它放在在定时器中来进行操作

为了区别于方案1,我们把每个任务的周期改一下:

实验内容:黄色灯4Hz(125ms),蓝色灯2Hz(250ms),红色灯1Hz(500ms)

引脚关系:黄色灯->P0.0、蓝色灯->P0.1、红色灯->P0.2

定时器:我们这里选用T0,定时器周期计算参考宏晶公司提供的软件里面的示例代码,我们这里应为没有让定时器工作在1T状态,所以跟传统单片机是一样的操作方式。

打开网易新闻 查看精彩图片

T0中断时间计算

实验效果

打开网易新闻 查看精彩图片

程序代码:main.c源文件

/*************************************** 文件名称:main.c** 描 述:三个LED灯以不同频率闪烁** 实验平台:STC12C5A60S2最小系统板 + 面包板 + 分立元器件** 日 期:2020/01/25** 作 者:yigo** 历史记录: 硬件连接: YELLOW_LED->P0.0 BLUE_LED ->P0.1 RED_LED ->P0.2**************************************/

#include"main.h"#include"LED.h"/*********** 变量定义 *************/

#define SYSTEM_DELAY 1000 // 系统周期1ms#define YELLOW_LED_TASK_TIME 125 // SYSTEM_DELAY*0.125 = 125ms任务#define BLUE_LED_TASK_TIME 250 // SYSTEM_DELAY*0.25 = 250ms任务#define RED_LED_TASK_TIME 500 // SYSTEM_DELAY*0.5 = 500ms任务uint8_tYELLOW_LED_Task_Count=YELLOW_LED_TASK_TIME;uint8_tBLUE_LED_Task_Count=BLUE_LED_TASK_TIME;uint8_tRED_LED_Task_Count=RED_LED_TASK_TIME;

/*************************************** 函 数 名:main** 输入参数:none** 返 回 值:none** 说 明:主函数**************************************/voidmain(void){

// 系统定时器0初始化SYSTEM_T0_Init();

// 使能总中断,这样定时器0才会启动EA=1;

while(1)

// 1Hz黄色LED灯闪烁任务if(YELLOW_LED_Task_Count0)

YELLOW_LED_Task_Count=YELLOW_LED_TASK_TIME;

LED_Togle(YELLOW_LED);

// 2Hz蓝色LED灯闪烁任务if(BLUE_LED_Task_Count0)

BLUE_LED_Task_Count=BLUE_LED_TASK_TIME;

LED_Togle(BLUE_LED);

// 4Hz绿色LED灯闪烁任务if(RED_LED_Task_Count0)

RED_LED_Task_Count=RED_LED_TASK_TIME;

LED_Togle(RED_LED);

/*************************************** 函 数 名:SYSTEM_T0_Init** 输入参数:none** 返 回 值:none** 说 明:T0初始化函数**************************************/voidSYSTEM_T0_Init(void){

// 定时器0配置为16位定时器,当溢出时手工重装TMOD&=0xF0;// 清除所有有关T0的位 (T1不变)TMOD|=0x01;// 设置所需的T0相关位 (T1 不变)

// 设置定时器重装值TR0=0;// 停止定时器0

// STC12C5A60S2是1T的单片机,跟传统51单片机的指令周期12T有差别// 我们这里设置1ms产生一次中断 TL0=65536-(11059200/12/SYSTEM_DELAY);// 定时器低8位赋值TH0=(65536-(11059200/12/SYSTEM_DELAY))>>8;// 定时器高8位赋值// 启动T0TR0=1;

// 使能定时器T0中断ET0=1;}

/*************************************** 函 数 名:SYSTEM_Tick_Update() interrupt 1** 输入参数:none** 返 回 值:none** 说 明:定时器T0中断入口函数**************************************/voidSYSTEM_Tick_Update(void)interrupt1{

// 设置定时器重装值TR0=0;// 停止定时器0

// STC12C5A60S2跟传统T0定时器设置一样// 我们这里设置1ms产生一次中断 TL0=65536-(11059200/12/1000);// 定时器低8位赋值TH0=(65536-(11059200/12/1000))>>8;// 定时器高8位赋值// 启动T0TR0=1;

YELLOW_LED_Task_Count--;

BLUE_LED_Task_Count--;

RED_LED_Task_Count--;}

/************* end of file **********/

main.h头文件

/*************************************** 文件名称:main.h** 描 述:公共头文件** 实验平台:STC12C5A60S2最小系统板 + 面包板 + 分立元器件** 日 期:2020/01/24** 作 者:yigo** 历史记录: **************************************/

#ifndef __MAIN_H#define __MAIN_H#include#include/****** 变量类型定义 defines ******/

typedefunsignedcharuint8_t;typedefunsignedintuint16_t;

voidSYSTEM_T0_Init(void);

#endif/************* end of file **********/

LED灯部分的头文件和源文件没有改动,大家能够参考方案一的代码。

方案二多任务原理:采用定时器T0的1ms周期性中断操作来对每个任务要计时的量来进行减法运算,当减到零时,运行任务,然后再重新进行任务延时赋值。

打开网易新闻 查看精彩图片

总结:

1、两种多任务方案都能满足小规模嵌入式系统的要求,并且添加多个任务也非常方便

2、采用任务方式编写程序,我们更多的时候不是在写代码,而是在考虑如何让更多的代码可反复利用以及修改的方便性

3、方案二已经接近时间片轮询法系统雏形,可以移植到其他单片机上同样能使用

预告:接下来我们将对方案二中的多任务程序进行进一步改造,使之能适应不同功能需求,并且能方便的添加任务,从而到达我们对一个简单的多任务系统的要求,同时在接下来的讲解中还会牵涉到数据结构,我们也大可不必担心,因为只使用到了其中一部分数据结构,大家能够提前去预习下。

可能很多人在大学里面把数据结构学完,考试完都不清楚数据结构有些什么用途。

想要学习单片机的朋友 ,关注我们,回复一哥,与导师一起学习成长,共同进步,还有更多资料领取。

说了这么多,大家记得留意下方评论第一条(或者私信我)有干货~

部分资源源自于网络,如有侵权请联系删除。