(2)《时间触发嵌入式系统设计模式》-多任务程序设计思路

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



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

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

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

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

实验效果:


程序代码:main.c文件

/*************************************
**  文件名称:main.c
**  描    述:LED灯闪烁
**  实验平台:STC12C5A60S2最小系统板 + 面包板 + 分立元器件
**  日    期:2020/01/25
**  作    者:wllis
**  历史记录:

    硬件连接:
    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_t YELLOW_LED_Task_Count = YELLOW_LED_TASK_TIME;  
uint8_t BLUE_LED_Task_Count   = BLUE_LED_TASK_TIME;    
uint8_t RED_LED_Task_Count    = RED_LED_TASK_TIME;     

/*************************************
** 函 数 名:main
** 输入参数:none
** 返 回 值:none
** 说    明:主函数
**************************************/
void main ( void )
{    
    while(1)
    {
       
        // 1Hz黄色LED灯闪烁任务
       if(YELLOW_LED_Task_Count<=0)
       {
            YELLOW_LED_Task_Count = YELLOW_LED_TASK_TIME;
            LED_Togle( YELLOW_LED );
       }
       
       // 2Hz蓝色LED灯闪烁任务
        if(BLUE_LED_Task_Count<=0)
       {
            BLUE_LED_Task_Count = BLUE_LED_TASK_TIME;
            LED_Togle( BLUE_LED );
       }
       
        // 4Hz绿色LED灯闪烁任务
        if(RED_LED_Task_Count<=0)
       {
            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
** 说    明:延时函数
**************************************/
void delay( uint16_t time )
{
    while(--time){ Delay1ms(); }
}
/*************************************
** 函 数 名:Delay1ms
** 输入参数:none
** 返 回 值:none
** 说    明:1ms延时函数
**************************************/
void Delay1ms( void )       //@11.0592MHz
{
    uint8_t i, j;

    _nop_();
    _nop_();
    _nop_();
    i = 11;
    j = 190;
    do
    {
        while (--j);
    } while (--i);
}

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

main.h文件

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

**************************************/

#ifndef __MAIN_H
#define __MAIN_H

#include
#include

/****** 变量类型定义 defines ******/

typedef  unsigned char uint8_t;
typedef  unsigned int  uint16_t;

 
void delay( uint16_t time );
void Delay1ms( void );

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

LED.c文件

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

**************************************/
#include "LED.h"


/*************************************
** 函 数 名:LED_Togle
** 输入参数:需要翻转的LED
** 返 回 值:none
** 说    明:LED翻转函数
**************************************/
void LED_Togle( uint8_t LED )
{
    switch(LED)
    {
        case YELLOW_LED:
            YELLOW_LED_PIN = ~YELLOW_LED_PIN;
            break;
        case BLUE_LED:
            BLUE_LED_PIN = ~BLUE_LED_PIN;
            break;
        case RED_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

/*********** 引脚定义 *************/ 
sbit YELLOW_LED_PIN = P0^0;
sbit BLUE_LED_PIN   = P0^1;
sbit RED_LED_PIN    = P0^2;

/*********** 函数定义 *************/ 
void LED_Togle( uint8_t LED );

#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状态,所以跟传统单片机是一样的操作方式。


实验效果

程序代码:main.c源文件

/*************************************
**  文件名称:main.c
**  描    述:三个LED灯以不同频率闪烁
**  实验平台:STC12C5A60S2最小系统板 + 面包板 + 分立元器件
**  日    期:2020/01/25
**  作    者:wllis
**  历史记录:

    硬件连接:
    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_t YELLOW_LED_Task_Count = YELLOW_LED_TASK_TIME;  
uint8_t BLUE_LED_Task_Count   = BLUE_LED_TASK_TIME;    
uint8_t RED_LED_Task_Count    = RED_LED_TASK_TIME;     

/*************************************
** 函 数 名:main
** 输入参数:none
** 返 回 值:none
** 说    明:主函数
**************************************/
void main ( void )
{    
    // 系统定时器0初始化
    SYSTEM_T0_Init( );
    
    // 使能总中断,这样定时器0才会启动
    EA = 1;
    
    while(1)
    {      
        // 1Hz黄色LED灯闪烁任务
       if(YELLOW_LED_Task_Count<=0)
       {
            YELLOW_LED_Task_Count = YELLOW_LED_TASK_TIME;
            LED_Togle( YELLOW_LED );
       }
       
       // 2Hz蓝色LED灯闪烁任务
        if(BLUE_LED_Task_Count<=0)
       {
            BLUE_LED_Task_Count = BLUE_LED_TASK_TIME;
            LED_Togle( BLUE_LED );
       }
       
        // 4Hz绿色LED灯闪烁任务
        if(RED_LED_Task_Count<=0)
       {
            RED_LED_Task_Count = RED_LED_TASK_TIME;
            LED_Togle( RED_LED );
       }        
    }
}

/*************************************
** 函 数 名:SYSTEM_T0_Init
** 输入参数:none
** 返 回 值:none
** 说    明:T0初始化函数
**************************************/
void SYSTEM_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位赋值
   //  启动T0
   TR0  = 1;

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

/*************************************
** 函 数 名:SYSTEM_Tick_Update() interrupt 1
** 输入参数:none
** 返 回 值:none
** 说    明:定时器T0中断入口函数
**************************************/
void SYSTEM_Tick_Update( void ) interrupt 1
{
    // 设置定时器重装值
    TR0 = 0;        // 停止定时器0

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

    YELLOW_LED_Task_Count--;
    BLUE_LED_Task_Count--;
    RED_LED_Task_Count--;
}

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

main.h头文件

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

**************************************/

#ifndef __MAIN_H
#define __MAIN_H

#include
#include

/****** 变量类型定义 defines ******/

typedef  unsigned char uint8_t;
typedef  unsigned int  uint16_t;


void SYSTEM_T0_Init( void );

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

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

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

总结:

1、两种多任务方案都能满足小规模嵌入式系统的要求,并且添加多个任务也非常方便
2、采用任务方式编写程序,我们更多的时候不是在写代码,而是在考虑如何让更多的代码可重复利用以及修改的方便性
3、方案二已经接近时间片轮询法系统雏形,可以移植到其他单片机上同样能使用
预告:接下来我们将对方案二中的多任务程序进行进一步改造,使之能适应不同功能需求,并且能方便的添加任务,从而达到我们对一个简单的多任务系统的要求,同时在接下来的讲解中还会牵涉到数据结构,我们也大可不必担心,因为只使用到了其中一部分数据结构,大家可以提前去预习下。
可能很多人在大学里面把数据结构学完,考试完都不清楚数据结构有些什么用途。
给大家推荐一本书,大家可以参考下:


https://pan.baidu.com/link/zhihu/7lhnzbuchxiVUtVE1GbLhK1GZXU1E1TQQZhU==
同样的,还是希望大家多多支持正版书籍,每一本好书都值得你多次去阅读!!

你可能感兴趣的:((2)《时间触发嵌入式系统设计模式》-多任务程序设计思路)