380091044 发表于 2024-2-5 09:07:21

uCOSII-STC8-V1.06系统的任务A与任务BC之间是如何切换的?谁能看出来,指点一下?

uCOSII-STC8-V1.06系统的任务A与任务BC之间是如何切换的?谁能看出来,指点一下?代码如下:


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

uC/OS-II 移植到STC8H单片机
uC/OS-II Version: V2.93.01
修改版本: V1.03
修改时间: 2023-08-27
作者:熊仔
QQ:   616421684

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

#include "config.h"
#include "os.h"



#define ET2 0x04
#define ET3 0x20
#define ET4 0x40

// ==== 实时任务函数定义 ============
void Task_A(void *p_arg) reentrant;
void Task_B(void *p_arg) reentrant;
void Task_C(void *p_arg) reentrant;


#define TASKA_STK_SIZE 64
#define TASKB_STK_SIZE 64
#define TASKC_STK_SIZE 64

// ==== 实时任务堆栈定义 ============
OS_STK Task_STK_A;
OS_STK Task_STK_B;
OS_STK Task_STK_C;

longaa = 123456789 ;   //测试任务传参

#defineUART1_BAUD115200
void UartInit(void)
{
    SCON = 0x50;      //8位数据,可变波特率
    AUXR |= 0x01;       //串口1选择定时器2为波特率发生器
    AUXR |= 0x04;       //定时器时钟1T模式
    T2L = -((MAIN_Fosc / UART1_BAUD+2) / 4);
    T2H = -((MAIN_Fosc / UART1_BAUD+2) / 4) >> 8;
    AUXR |= 0x10;       //定时器2开始计时
}



char putchar(char c)
{
    SBUF = c;
    while(TI == 0);
    TI = 0;
    return c;
}



// ==== 端口初始化 准双向模式 ========
void PortX_Init(void)
{
    P0M1 = 0x00;
    P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;
    P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;
    P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;
    P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;
    P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;
    P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;
    P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;
    P7M0 = 0x00;   //设置为准双向口

    P0 = 0xFF;
    P1 = 0xFF;
    P2 = 0xFF;
    P3 = 0xFF;
    P4 = 0xFF;
    P5 = 0xFF;
    P6 = 0xFF;
    P7 = 0xFF;
}

void Delay_MS(unsigned int ms) //reentrant//没必要弄重入函数
{
    unsigned int i;
    do
    {
      i = MAIN_Fosc / 10000;
      while(--i);
    }
    while(--ms);
}

// ---- 定时器1初始化,用于测试 ----------
#define Timer1_HZ10000UL        // 10KHz

void Timer1_Init(void)
{
    AUXR |= 0x40;   //定时器时钟1T模式
    TMOD &= 0x0F;   //设置定时器模式
    TL1 = -(MAIN_Fosc / Timer1_HZ);
    TH1 = -(MAIN_Fosc / Timer1_HZ) >> 8;
    TF1 = 0;      //清除TF1标志
    TR1 = 1;      //定时器1开始计时
}

// ---- 定时器3初始化,用于测试 ----------
#define Timer3_HZ10000UL        // 10KHz

void Timer3_Init(void)
{
    T4T3M &= 0xF0;   //停止计数, 定时模式, 1T模式, 不输出时钟
    T4T3M |= 0x02;      //定时器时钟1T模式
    T3L = -(MAIN_Fosc / Timer3_HZ);
    T3H = -(MAIN_Fosc / Timer3_HZ) >> 8;
    T4T3M |=0x08;    //开始运行
}



// **** 实时任务 ***************
// ==== 任务 A ====================
void Task_A(void *p_arg) reentrant
{
//    longv;
//    v = *( long*)p_arg;
//    printf("p_arg=%ld\n",v);
    p_arg = p_arg;

    // ==== 无限循环 ============
    while (1)
    {
      P22 = ~P22;
      OSTimeDly(OS_TICKS_PER_SEC / 2);        // 500毫秒
    }
}

// ==== 任务 B ====================
void Task_B(void *p_arg) reentrant
{
    int n;
    p_arg = p_arg;

    // ==== 无限循环 ============
    while(1)
    {
      P24 = 1;
      for(n = 0; n < 3; n++)
      {
            //                                P23=1;
            P1 = 0x0F;
            Delay_MS(50);            // 50毫秒
            P1 = 0xF0;
            //                                P23=0;
            Delay_MS(100);            // 100毫秒
      }
      OSTaskResume(4);
      P24 = 0;
//      OSTimeDly(OS_TICKS_PER_SEC / 100);
      OSTaskSuspend(OS_PRIO_SELF);
    }
}

// ==== 任务 C ====================
void Task_C(void *p_arg) reentrant
{
    char i, m;
    p_arg = p_arg;

    // ==== 无限循环 ============
    while(1)
    {
      m = 0x04;
      P25 = 1;
      for(i = 0; i < 6; i++)
      {
            P3 = ~m;
            m <<= 1;
            Delay_MS(100);    // 100毫秒
      }
      P25 = 0;
//      OSTimeDly(OS_TICKS_PER_SEC / 100);
      OSTaskResume(3);
    }
}


// **** 单片机程序入口 *********
void main(void)
{
    char i;
    EAXSFR();//使能XSFR访问
    // ==== 端口初始化 准双向模式 ========
    PortX_Init();
//    UartInit();

    // ==== LOGO ============
    for(i = 0; i < 3; i++)        // LOGO
    {
      P2 = 0x00;
      Delay_MS(50);
      P2 = 0xFF;
      Delay_MS(500);
    }

//    printf("start\r\n");
    // ==== 定时器1 初始化 ===========
//    Timer1_Init();
//    ET1 = 1;

//    // ==== 定时器3 初始化 ===========
//    Timer3_Init();
//    IE2|=ET3;         //允许中断



    // ==== uCOS-II RTOS 初始化 ===============
    OSInit();
    //系统滴答定时器初始化
    Timer0_Init();

    // ==== uCOS-II 建立任务 ===============
    OSTaskCreate(Task_A, ( void * )&aa, &Task_STK_A, TASKA_STK_SIZE, 2);
    OSTaskCreate(Task_B, (void *)0, &Task_STK_B, TASKB_STK_SIZE, 3);
    OSTaskCreate(Task_C, (void *)0, &Task_STK_C, TASKC_STK_SIZE, 4);
    // ==== uCOS-II 开始任务调度 不会返回 ==========
    OSStart();

}

gentleman 发表于 2024-2-5 13:09:38


这是操作系统

宏观上 任务创建完就是“同时运行”的{:4_165:}

当然 cpu只有一个,同一时刻只能运行一个任务


按照优先级和任务建立顺序运行(遇到延时就进入堵塞状态,运行下一个任务)








380091044 发表于 2024-2-5 15:35:17

gentleman 发表于 2024-2-5 13:09
这是操作系统

宏观上 任务创建完就是“同时运行”的


就是没有裸机程序那种明显的逻辑关系,搞的不是太好看,

tzz1983 发表于 2024-2-22 10:25:10

380091044 发表于 2024-2-5 15:35
就是没有裸机程序那种明显的逻辑关系,搞的不是太好看,

@3800***,RTOS学得怎么样了, 挂起自己这个操作在实际应该用中并不多, 最常用的是OSTimeDly(x); 去了解一下

建议你删除掉例程中三个任务while(1){}中的所有代码. 分别改成这样:
while(1)
{
      P02=~P02;
      OSTimeDly(200); //阻塞延时200个时钟滴答,并立即切换至下一个任务, 200个时钟滴答后重新运行本任务, 理解一下
}

while(1)
{
      P03=~P03;
      OSTimeDly(200);
}

while(1)
{
      P04=~P04;
      OSTimeDly(200);
}


这样就可以看到3个LED同时闪烁的效果,表示三个任务在运行,之后自己改代码,玩一些自己喜欢有花样, 有助于理解

380091044 发表于 2024-2-22 13:13:11

tzz1983 发表于 2024-2-22 10:25
@3800***,RTOS学得怎么样了, 挂起自己这个操作在实际应该用中并不多, 最常用的是OSTimeDly(x); 去了解 ...

感谢老师指点,感谢STC老师们帮助和支持,给老师汇报一下我进度:我已经采用打狗棒硬件,测试了,明了很多,我也在看一些网上资料,注释源码,任务切换也有了一定的理解了,但是不是很透彻,邮箱和消息队列,这些还没有接触到,

380091044 发表于 2024-2-22 13:50:06

本帖最后由 380091044 于 2024-2-22 13:51 编辑

tzz1983 发表于 2024-2-22 10:25
@3800***,RTOS学得怎么样了, 挂起自己这个操作在实际应该用中并不多, 最常用的是OSTimeDly(x); 去了解 ...
老师:你看一下这个延时函数,如下:
voidOSTimeDly (INT32U ticks) large reentrant
{
    INT8U      y;
#if OS_CRITICAL_METHOD == 3u                     /* Allocate storage for CPU status register         */
    OS_CPU_SRcpu_sr = 0u;
#endif



    if (OSIntNesting > 0u) {                     /* See if trying to call from an ISR                  */
      return;
    }
    if (OSLockNesting > 0u) {                  /* See if called with scheduler locked                */
      return;
    }
    if (ticks > 0u) {                            /* 0 means no delay!                                  */
      OS_ENTER_CRITICAL();
      y            =OSTCBCur->OSTCBY;      /* Delay current task                                 */
      OSRdyTbl &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
      OS_TRACE_TASK_SUSPENDED(OSTCBCur);
      if (OSRdyTbl == 0u) {
            OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
      }
      OSTCBCur->OSTCBDly = ticks;            /* Load ticks in TCB                                  */
      OS_TRACE_TASK_DLY(ticks);
      OS_EXIT_CRITICAL();
      OS_Sched();                              /* Find next task to run!                           */
    }
}

这其中的 OS_Sched();    是切换下一个任务,我看网上资料说,如果当前任务已经是最高级,则退出,那是不是就接着找已就绪的次有优先级任务切换执行?

OS_Sched();源码如下:
voidOS_Sched (void) large reentrant
{
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register   */
    OS_CPU_SRcpu_sr = 0u;
#endif



    OS_ENTER_CRITICAL();
    if (OSIntNesting == 0u) {                        /* Schedule only if all ISRs done and ...       */
      if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */
            OS_SchedNew();
            OSTCBHighRdy = OSTCBPrioTbl;
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy   */
#if OS_TASK_PROFILE_EN > 0u
                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endif
                OSCtxSwCtr++;                        /* Increment context switch counter             */

#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
                OS_TLS_TaskSw();
#endif
#endif
//                OS_EXIT_CRITICAL();    //需要提前退出临界区再切换任务
                OS_EXIT_CRITICAL_NOT_INT();
                OS_TASK_SW();                        /* Perform a context switch                     */
                return;               //前面已经退出临界区,这里直接返回
            }
      }
    }
    OS_EXIT_CRITICAL();
}

tzz1983 发表于 2024-2-22 14:21:06

本帖最后由 tzz1983 于 2024-2-22 14:46 编辑

380091044 发表于 2024-2-22 13:50
老师:你看一下这个延时函数,如下:
voidOSTimeDly (INT32U ticks) large reentrant
{

if (OSPrioHighRdy != OSPrioCur)这一句就是你说的那个判断了, 如果当前任务的优先级不是最高优先级,才做任务切换, 否则不需要做切换,(自己切换到自己,没必要,所以不切换了, 直接退出)

自己是最高优先级的, 那就退出切换函数,接着运行自己, 没有必要找次优先级, UCOS里没有这种说法, 只关心最高优先级, 等高优先级阻塞了,次优先级就自动成为最高优先级了

下面是OS_Sched 函数我添加的注释

voidOS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register   */
    OS_CPU_SRcpu_sr = 0u;
#endif

    OS_ENTER_CRITICAL();            //进临界区
    if (OSIntNesting == 0u) {                        /* 确保当前不是中断例程       */
      if (OSLockNesting == 0u) {                     /* 如果调度器没有被锁定               */
            OS_SchedNew();          //查找可运行的最高优先级任务
            OSTCBHighRdy = OSTCBPrioTbl; //可运行的最高任务优先级
            if (OSPrioHighRdy != OSPrioCur) {          /* 如果当前任务不是最高优先级任务   */
#if OS_TASK_PROFILE_EN > 0u
                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endif
                OSCtxSwCtr++;                        /* 切换计数加1             */

#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
                OS_TLS_TaskSw();
#endif
#endif

                OS_TASK_SW();                        /* 任务切换宏                     */
            }
      }
    }
    OS_EXIT_CRITICAL(); //退出临界区
}





tzz1983 发表于 2024-2-22 15:11:22

380091044 发表于 2024-2-22 13:50
老师:你看一下这个延时函数,如下:
voidOSTimeDly (INT32U ticks) large reentrant
{


你说的次优先级。。。 是不是有些误解了, 如果当前任务调用了 OSTimeDly (),则当前任务进入阻塞,已经不再是最高优先级, 这种情况下,切换的目标绝对不会指向自己

380091044 发表于 2024-2-22 17:14:49

tzz1983 发表于 2024-2-22 15:11
你说的次优先级。。。 是不是有些误解了, 如果当前任务调用了 OSTimeDly (),则当前任务进入阻塞,已 ...

老师:我被误导了,你说的我茅塞顿开,是这样的,
页: [1]
查看完整版本: uCOSII-STC8-V1.06系统的任务A与任务BC之间是如何切换的?谁能看出来,指点一下?