美文网首页C语言&嵌入式每天一点单片机知识
(2)《时间触发嵌入式系统设计模式》-多任务程序设计思路

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

作者: 想啥做啥 | 来源:发表于2021-02-24 22:42 被阅读0次

    多个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<stc12c5a60s2.h>
    #include<intrins.h>
    
    /****** 变量类型定义 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<stc12c5a60s2.h>
    #include<intrins.h>
    
    /****** 变量类型定义 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)《时间触发嵌入式系统设计模式》-多任务程序设计思路

        本文链接:https://www.haomeiwen.com/subject/ytmefltx.html