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();
}
这是操作系统
宏观上 任务创建完就是“同时运行”的{:4_165:}
当然 cpu只有一个,同一时刻只能运行一个任务
按照优先级和任务建立顺序运行(遇到延时就进入堵塞状态,运行下一个任务)
gentleman 发表于 2024-2-5 13:09
这是操作系统
宏观上 任务创建完就是“同时运行”的
就是没有裸机程序那种明显的逻辑关系,搞的不是太好看, 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同时闪烁的效果,表示三个任务在运行,之后自己改代码,玩一些自己喜欢有花样, 有助于理解
tzz1983 发表于 2024-2-22 10:25
@3800***,RTOS学得怎么样了, 挂起自己这个操作在实际应该用中并不多, 最常用的是OSTimeDly(x); 去了解 ...
感谢老师指点,感谢STC老师们帮助和支持,给老师汇报一下我进度:我已经采用打狗棒硬件,测试了,明了很多,我也在看一些网上资料,注释源码,任务切换也有了一定的理解了,但是不是很透彻,邮箱和消息队列,这些还没有接触到, 本帖最后由 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: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(); //退出临界区
}
380091044 发表于 2024-2-22 13:50
老师:你看一下这个延时函数,如下:
voidOSTimeDly (INT32U ticks) large reentrant
{
你说的次优先级。。。 是不是有些误解了, 如果当前任务调用了 OSTimeDly (),则当前任务进入阻塞,已经不再是最高优先级, 这种情况下,切换的目标绝对不会指向自己 tzz1983 发表于 2024-2-22 15:11
你说的次优先级。。。 是不是有些误解了, 如果当前任务调用了 OSTimeDly (),则当前任务进入阻塞,已 ...
老师:我被误导了,你说的我茅塞顿开,是这样的,
页:
[1]