我的PIC16多任务RTOS内核不工作的原因是什么?


11

我正在尝试为PIC x16微控制器创建一个半抢占式(协作式)RTOS。在我之前的问题中,我了解到在这些内核中无法访问硬件堆栈指针。我在PIClist中查看了页面,这就是我正在尝试使用C实现的页面。

我的编译器是Microchip XC8,当前正在使用在配置位中选择了4MHz内部RC振荡器的PIC16F616。

我了解到,可以通过查看编译器的头文件来使用C访问PCLATH和PCL寄存器。因此,我尝试实现一个简单的任务切换器。

如果我在重新启动,重置并在光标不在第一行(TRISA=0;)而不是另一行(例如ANSEL=0;)上时将调试器设置为光标后暂停调试器,则它可以在调试器中正常工作。在调试器的第一次启动中,我在以下位置获得这些消息Debugger Console

Launching
Programming target
User program running
No source code lines were found at current PC 0x204

编辑:我不知道是什么使它起作用,但是调试器现在可以完美地工作了。因此,省略上面的输出和段落。

编辑:更改这样的主要定义使下面的代码工作。这将在程序地址处启动主要功能0x0099。我不知道是什么原因造成的。这不是一个真正的解决方案。我现在猜测存在编译器特定的错误。

void main(void) @ 0x0099
{

这是我的C代码:

/* 
 * File:   main.c
 * Author: abdullah
 *
 * Created on 10 Haziran 2012 Pazar, 14:43
 */
#include <xc.h> // Include the header file needed by the compiler
__CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF & IOSCFS_4MHZ & BOREN_ON);
/*
 * INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN
 * WDT disabled and can be enabled by SWDTEN bit of the WDTCON register
 * PWRT enabled
 * MCLR pin function is digital input, MCLR internally tied to VDD
 * Program memory code protection is disabled
 * Internal Oscillator Frequency Select bit : 4MHz
 * Brown-out Reset Selection bits : BOR enabled
 */

/*
 * OS_initializeTask(); definition will copy the PCLATH register to the task's PCLATH holder, which is held in taskx.pch
 * This will help us hold the PCLATH at the point we yield.
 * After that, it will copy the (PCL register + 8) to current task's PCL holder which is held in taskx.pcl.
 * 8 is added to PCL because this line plus the "return" takes 8 instructions.
 * We will set the PCL after these instructions, because
 * we want to be in the point after OS_initializeTask when we come back to this task.
 * After all, the function returns without doing anything more. This will initialize the task's PCLATH and PCL.
 */
#define OS_initializeTask(); currentTask->pch = PCLATH;\
                             currentTask->pcl = PCL + 8;\
                             asm("return");

/*
 * OS_yield(); definition will do the same stuff that OS_initializeTask(); definition do, however
 * it will return to "taskswitcher" label, which is the start of OS_runTasks(); definition.
 */

#define OS_yield();          currentTask->pch = PCLATH;\
                             currentTask->pcl = PCL + 8;\
                             asm("goto _taskswitcher");

/*
 * OS_runTasks(); definition will set the "taskswitcher" label. After that it will change the
 * current task to the next task, by pointing the next item in the linked list of "TCB"s.
 * After that, it will change the PCLATH and PCL registers with the current task's. That will
 * make the program continue the next task from the place it left last time.
 */

#define OS_runTasks();       asm("_taskswitcher");\
                             currentTask = currentTask -> next;\
                             PCLATH = currentTask->pch;\
                             PCL = currentTask->pcl;

typedef struct _TCB // Create task control block and type define it as "TCB"
{
    unsigned char pch; // pch register will hold the PCLATH value of the task after the last yield.
    unsigned char pcl; // pcl register will hold the PCL value of the task after the last yield.
    struct _TCB* next; // This pointer points to the next task. We are creating a linked list.
} TCB;

TCB* currentTask; // This TCB pointer will point to the current task's TCB.

TCB task1; // Define the TCB for task1.
TCB task2; // Define the TCB for task2.

void fTask1(void); // Prototype the function for task1.
void fTask2(void); // Prototype the function for task2.

void main(void)
{
    TRISA = 0; // Set all of the PORTA pins as outputs.
    ANSEL = 0; // Set all of the analog input pins as digital i/o.
    PORTA = 0; // Clear PORTA bits.

    currentTask = &task1; // We will point the currentTask pointer to point the first task.

    task1.next = &task2; // We will create a ringed linked list as follows:
    task2.next = &task1; // task1 -> task2 -> task1 -> task2 ....

    /*
     * Before running the tasks, we should initialize the PCL and PCLATH registers for the tasks.
     * In order to do this, we could have looked up the absolute address with a function pointer.
     * However, it seems like this is not possible with this compiler (or all the x16 PICs?)
     * What this compiler creates is a table of the addresses of the functions and a bunch of GOTOs.
     * This will not let us get the absolute address of the function by doing something like:
     * "currentTask->pcl=low(functionpointer);"
     */
    fTask1(); // Run task1 so that we get the address of it and initialize pch and pcl registers.
    currentTask = currentTask -> next; // Point the currentTask pointer to the next pointer which
    fTask2(); // is task2. And run task2 so that we get the correct pch and pcl.

    OS_runTasks(); // Task switcher. See the comments in the definitions above.
}

void fTask1(void)
{
    OS_initializeTask(); // Initialize the task
    while (1)
    {
        RA0 = ~RA0; // Toggle PORTA.0
        OS_yield(); // Yield
        RA0 = ~RA0; // Toggle PORTA.0
    }
}

void fTask2(void)
{
    OS_initializeTask(); // Initialize the task
    while (1)
    {
        RA1 = ~RA1; // Toggle PORTA.1
        OS_yield(); // Yield
        RA1 = ~RA1; // Toggle PORTA.1
    }
}

这里是我的编译器创建的反汇编列表文件。开始于line 74

我已经对实际芯片进行了编程,而PORTA却没有任何变化。它不起作用。

我的程序无法正常工作的原因是什么?

Answers:


10

您尝试做的事情很棘手,但是很有教育意义(如果您准备花费很多精力)。

首先,您必须意识到,这种仅PC(与PC + SP相对)的任务切换(这是您可以在普通的12位或14位PIC内核上执行的唯一操作)仅在所有yield( )任务中的语句具有相同的功能:它们不能位于被调用的函数中,并且编译器一定不能弄乱函数的结构(就像优化可能那样)。

下一个:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • 您似乎假设PCLATH是程序计数器的高位,因为PCL是低位。不是这种情况。当您写入PCL时,PCLATH位将被写入PC,但是高位PC位将不会(隐式)写入PCLATH。重新阅读数据表的相关部分。
  • 即使PCLATH是PC的高位,当goto之后的指令与第一条指令不在同一256条指令“页”上时,这也会使您陷入麻烦。
  • 当_taskswitcher不在当前PCLATH页面中时,普通的goto将不起作用,您将需要LGOTO或同等功能。

解决PCLATH问题的一种方法是在goto之后声明一个标签,并将该标签的低位和高位写入您的pch和pcl位置。但是我不确定您是否可以在内联汇编中声明“本地”标签。您肯定可以使用简单的MPASM(Olin会微笑)。

最后,对于这种上下文切换,您必须保存并恢复编译器可能依赖的所有上下文,其中可能包括

  • 间接寄存器
  • 状态标志
  • 暂存位置
  • 可能在内存中重叠的局部变量,因为编译器没有意识到您的任务必须独立
  • 我现在无法想象的其他事情,但编译器作者可能会在下一版编译器中使用(它们往往非常有想象力)

PIC架构在这方面存在更大的问题,因为在整个内存映射中分配了很多资源,在内存映射中,更多传统架构将这些资源放在寄存器或堆栈中。结果,PIC编译器通常不生成可重入代码,这是您绝对需要做的事情(再次,Olin可能会微笑并组装在一起)。

如果您出于编写任务切换器的乐趣而热衷于此,建议您选择使用具有更传统组织结构的CPU,例如ARM或Cortex。如果您的脚陷在PIC的混凝土板上,请研究现有的PIC切换器(例如salvo / pumkin?)。


感谢您提供的信息!我决心创建一个协作式任务切换器。XC8和PIC不支持这一点,我知道:)是的,如您所见,可以像我对这个问题的回答之一那样创建标签。
Abdullah kahraman 2012年

而且,令我幸运的是,我正在开发的PIC16F616没有对程序存储器的页面调度,这在当时是一个很大的优势,对吗?
abdullah kahraman 2012年

您能否进一步解释局部变量在内存中如何重叠,以及“刮擦内存位置”?
Abdullah kahraman 2012年

如果您将代码限制在2K或更少的芯片上,那么您确实可以忘记lgoto,而不是256条指令的“页面”。从头开始:编译器可以假设它在内存中所做的任何事情都保持不变,除非它是“易失性的”。因此,它可能会将部分计算放在可以由不同功能共享的某个位置。Ovelap:如果main()同时调用f()和g()(并且没有其他调用),则f()和g()的局部变量可以映射到相同的内存位置。
Wouter van Ooijen 2012年

好吧,由于它们在内存中的随机位置,似乎几乎不可能到达并存储这些变量,对吗?
Abdullah kahraman 2012年

7

我浏览了您提供的程序集列表,但没有发现任何明显的损坏。

如果我是你,我的下一步将是:

(1)我会选择其他方法来使LED闪烁。臭名昭著的“读-修改-写问题”可能(也可能不会)由组装清单中的“ XORWF PORTA,F”触发。

也许像这样:

// Partial translation of code from abdullah kahraman
// untested code
// feel free to use however you see fit
void fTask2(void)
{
    OS_initializeTask(2); // Initialize task 2
    while (1)
    {
        PORTC = 0xAA;
        OS_yield(2); // Yield from task 2
        PORTC = 0x55;
        OS_yield(2); // Yield from task 2
    }
}

(如果你真的想看到关于为什么“XORWF PORTA,F”往往会导致问题的详细说明,请参见“ 是什么原因导致接通了解Microchip PIC16F690单输出引脚,以自发关掉另一个销同一端口上? ‘’ 会发生什么何时将数据写入LATCH? “;” 读取-修改-写入问题 “;” 写入之前先读取 “)

(2)我将单步执行代码,确保将变量设置为预期值并按预期​​顺序进行设置。我不确定PIC16F616是否存在单步硬件调试器,但是有许多出色的PIC微控制器仿真器(例如PICsim) 可以仿真PIC16系列芯片。

单步代码(在模拟器中或在单步硬件调试器中)是了解实际情况的详细信息,确认事情正在按预期方式进行的一种好方法,并且它使您可以查看正在发生的事情。全速运行程序时几乎看不到。

(3)如果仍然感到困惑,我将尝试翻译代码以使用数组而不是指针。有些人发现使用指针有些棘手且难以调试。我经常发现,在过程中翻译技巧的指针代码为面向阵列码的,我找出的错误是什么。即使最终返回原始指针代码并丢弃了数组版本,该练习也很有用,因为它有助于我发现并修复错误。(有时编译器可以从面向数组的代码生成更短,更快的代码,因此有时我会抛出原始指针代码并保留数组版本)。

也许像

// Partial translation of code from abdullah kahraman
// untested code
// feel free to use however you see fit
struct TCB_t // Create task control block and type define it as "TCB_t"
{
    unsigned char pch; // PCLATH value
    unsigned char pcl; // PCL value
    int next; // This array index points to the next task. We are creating a linked list.
};

int currentTask = 1; // This TCB index will point to the current task's TCB.

struct TCB_t tasks[3]; // Define the TCB for task1 and task2.

#define OS_initializeTask(x); tasks[x].pch = PCLATH;\
                             tasks[x].pcl = PCL + 8;\
                             asm("return");

#define OS_runTasks();       asm("_taskswitcher");\
                             currentTask = tasks[currentTask].next;\
                             PCLATH = tasks[currentTask].pch;\
                             PCL = tasks[currentTask].pcl;

#define OS_yield(x);         tasks[x].pch = PCLATH;\
                             tasks[x].pcl = PCL + 8;\
                             asm("goto _taskswitcher");

我现在正在实现数组。感谢您的推荐。
abdullah kahraman 2012年

3

我基本上同意大卫。看起来可以正常工作。

我不知道是什么使它起作用,但是调试器现在可以完美地工作了。

我猜测您的意思是它可以在模拟器中完美运行。

1)在真正的非RTOS环境中,检查您的任务是否可以独立工作。

2)进行在线调试。在真实的芯片上逐步执行程序,并观察所有相关变量,以确保一切按计划进行。


是的,我的意思是调试器,即MPLABX的模拟器。在非RTOS环境中,任务可以独立工作。我没有ICD。我只有带ICD的mikroElektronika easyPIC5,但是,它仅适用于mikroC编译器。现在,更改编译器不会让我发现问题,或者会吗?
abdullah kahraman 2012年

1

我只是简要地看了一下您的代码,但这没有任何意义。在许多地方,您都在写PCL,然后期望它会执行其他指令。

就像我之前说过的那样,C不适合这种基本硬件寄存器的低级访问。您确实需要为此使用汇编。试图弄清楚为什么C代码不起作用只是浪费时间。


我无法将汇编和C结合起来。我不得不做很多工作。在我看来,反汇编和C代码都符合逻辑。您在哪里指的是我希望在写PCL之后执行指令?我已经看过调试器的汇编语言和C语言,并且按预期运行。
Abdullah kahraman 2012年

对不起-1。我应该不小心按了,现在我已经注意到了。
Abdullah kahraman 2012年

@abdullah:在我现在的机器上,我看不到源代码。它在浏览器中永久折叠。我记得您曾将东西分配给PCLATH,然后分配给PCL,然后我认为在一种情况下尝试执行RETURN。一旦写入PCL,执行将跳转到您填充到PCLATH:PCL中的地址,因此以下任何说明均无关紧要。在C中这样做真的不好,因为您弄乱了编译器管理的资源,从而可能使编译器假设无效。已经使用真实的程序集。我已经厌倦了不得不重复这个过程。
奥林·拉斯洛普

1
查看代码,在另一条语句之前没有地方修改PCL。它似乎唯一可以修改的地方是main()的最后。但这是一个很好的一点,您必须非常确定自己没有为编译器争取资源。你们两个都会输。
Rocketmagnet 2012年

3
C对于这种工作是完全可以接受的,实际上,最好使用像C这样的中级语言来编写,而不是使用汇编语言,因为它更易于阅读和维护。一个不错的编译器所生成的代码与普通人编写的代码相差不远。我通常只为非常基本的启动代码,在某些方面我可以比编译器更好地优化,对于快速中断,或者在代码大小限制要求的情况下编写汇编器。这些天对纯组装的需求不大。
akohlsmith 2012年

1

下面是一个行内汇编使用XC8编译器来做到这一点,和它的作品吧!但是,我需要添加更多的代码来保存和恢复STATUS寄存器,这似乎比普通寄存器要复杂一些。

编辑:代码已更改。请参考本文的旧版本以获取先前的代码。

/*
 * File:   main.c
 * Author: abdullah
 *
 * Created on 10 Haziran 2012 Pazar, 14:43
 */
#include <xc.h> // Include the header file needed by the compiler
#include "RTOS.h" // Include the header for co-operative RTOS.
__CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF & IOSCFS_4MHZ & BOREN_ON);

unsigned char OS_currentTask; // This register holds the current task's place in the array OS_tasks
unsigned char OS_tasks[4]; // This array holds PCL and PCLATH for tasks. This array will have..
//                            .. (number of tasks)*2 elements, since every task occupies 2 places.

void fTask1(void); // Prototype the function for task1.
void fTask2(void); // Prototype the function for task2.

void main(void)
{
    TRISA = 0; // Set all of the PORTA pins as outputs.
    TRISC = 0; // Set all of the PORTC pins as outputs.
    ANSEL = 0; // Set all of the analog input pins as digital i/o.
    PORTA = 0; // Clear PORTA bits.
    PORTC = 0; // Clear PORTC bits.

    OS_currentTask = 0; // Current task is first task.
    fTask1(); // Call task to initialize it.
    OS_currentTask += 2; // Increment task pointer by two since every task occupies 2 places in the array.
    fTask2(); // Call task to initialize it.
    OS_runTasks(4); // Run the tasks in order. The argument of this macro takes is: (Number of tasks) * 2
}

void fTask1(void)
{
    OS_initializeTask(); // Initialize the task so that task runner can get its ingredients.
    while (1)
    {
        PORTC = 0xAA;
        OS_yield(); // Yield CPU to other tasks.
        PORTC = 0x55;
        OS_yield(); // Yield CPU to other tasks.
    }
}

void fTask2(void)
{
    OS_initializeTask(); // Initialize the task so that task runner can get its ingredients.
    while (1)
    {
        PORTC = 0xFF;
        OS_yield(); // Yield CPU to other tasks.
        PORTC = 0x00;
        OS_yield(); // Yield CPU to other tasks.
    }
}

这是头文件RTOS.h

/* 
 * File:   RTOS.h
 * Author: abdullah
 *
 * Created on 21 Haziran 2012 Perşembe, 10:51
 */

#ifndef RTOS_H
#define RTOS_H

asm("OS_yield MACRO");
asm("local OS_tmp");
asm("movlw   _OS_tasks            ; Store the address of tasks, which is the start address of our task 'array'."); 
asm("addwf   _OS_currentTask, w   ; Add current task's index to the start address."); 
asm("movwf   fsr                  ; We have the index of current task in W. Copy it to FSR"); 
asm("movlw   high(OS_tmp)         ; Copy PCLATH register's contents for the label, to W register.");
asm("movwf   indf                 ; Copy W to current task's first item. We now store PCLATH of the current state of the task."); 
asm("incf    fsr, f               ; Increment index, so that we will point to the next item of current task."); 
asm("movlw   low(OS_tmp)          ; Copy PCL of the label to W register. This will let us save the PCL of the current state of the task.");
asm("movwf   indf                 ; Copy W to task's next item. With that, we will initialize the current task.");
asm("goto    OS_taskswitcher");
asm("OS_tmp:                      ; We will use this label to gather the PC of the return point.");
asm("ENDM"); 

#define OS_yield(); asm("OS_yield");

asm("OS_initializeTask MACRO");
asm("local   OS_tmp");
asm("movlw   _OS_tasks            ; Store the address of tasks, which is the start address of our task 'array'."); 
asm("addwf   _OS_currentTask, w   ; Add current task's index to the start address."); 
asm("movwf   fsr                  ; We have the index of current task in W. Copy it to FSR"); 
asm("movlw   high(OS_tmp)        ; Copy PCLATH register's contents for the label, to W register."); 
asm("movwf   indf                 ; Copy W to current task's first item. We now store PCLATH."); 
asm("incf    fsr,f                ; Increment index, so that we will point to the next item of current task."); 
asm("movlw   low(OS_tmp)         ; Copy PCL of the label to W register. This will let us save the PCL of the current state of the task."); 
asm("movwf   indf                 ; Copy W to task's next item. With that, we will initialize the current task."); 
asm("return                       ; We have gathered our initialazation information. Return back to main."); 
asm("OS_tmp                      ; We will use this label to gather the PC of the return point.");
asm("ENDM"); 

#define OS_initializeTask(); asm("OS_initializeTask");

asm("OS_runTasks MACRO numberOfTasks");
asm("global OS_taskswitcher");
asm("OS_taskswitcher:");
asm("CLRWDT"); 
asm("movlw   0x02                 ; W = 2"); 
asm("addwf   _OS_currentTask, f   ; Add 2 to currentTask, store it in currentTask."); 
asm("movlw   numberOfTasks        ; W = numOfTasks");
asm("subwf   _OS_currentTask, w   ; w= f - w"); 
asm("btfsc   status, 0            ; If currentTask >= numOfTasks"); 
asm("clrf    _OS_currentTask      ; Clear currentTask"); 
asm("movlw   _OS_tasks            ; Store the address of tasks, which is the start address of our task 'array'."); 
asm("addwf   _OS_currentTask, w   ; Add current task's index to the start address."); 
asm("movwf   fsr                  ; We have the index of current task in W. Copy it to FSR"); 
asm("movf    indf, w              ; Copy the contents of current task's first item to W"); 
asm("movwf   pclath               ; Copy W to PCLATH. As a result, current task's PCLATH will be in PCLATH register."); 
asm("incf    fsr, f               ; Increment index, so that we will point to the next item of current task."); 
asm("movf    indf, w              ; Copy the contents of current task's second item to W."); 
asm("movwf   pcl                  ; Copy W to PCL. Finally, current task's PCL will be in PCL register.");
asm("ENDM");

#define OS_runTasks(numberOfTasks); asm("OS_runTasks "#numberOfTasks);

#endif  /* RTOS_H */

看来您将赢得自己的赏金。恭喜你!:-)
stevenvh 2012年

@stevenvh啊,那会发生,我不知道吗?谢谢:)
阿卜杜拉·卡拉曼

恭喜!
davidcary

谢谢@davidcary!我非常感谢你们的祝贺。
Abdullah kahraman 2012年

1
您真的需要还原状态吗?如果是这样,你需要使用“SWAPF”指令,为其他地方记录的理由是:“ P.安德森 ”,“ Microchip的中档系列手册:部分8.5现场保护 ”,“ PIC节约W和STATUS
davidcary

0

下面是如何使用汇编来实现这一点。使用格式访问相同的代码(链接到Pastebin)。如何改善?这是我在PIC汇编中的第一个程序,任何评论都值得赞赏。

list p=16f616
#include p16f616.inc

;*** Configuration Bits ***
__CONFIG _FOSC_INTOSCIO & _WDTE_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _IOSCFS_8MHZ & _BOREN_ON
;**************************

;*** Variable Definitions ***
VARS        UDATA                   ; Define undefined data(s).
numOfTasks  res     1               ; This variable holds the number of tasks multiplied by 2.
currentTask res     1               ; Index variable that points to the current task's index in "tasks"
tasks       res     4               ; This is task "array". Every task occupies 2 bytes.
;****************************

;*** Reset Vector ***
RESET   CODE    0x0000              ; Define a code block starting at 0x0000, which is reset vector, labeled "RESET"
        goto    start               ; Start the program.
;********************

;*** Main Code ***
MAIN    CODE
start                               ; Label the start of the program as "start".
        banksel TRISA               ; Select appropriate bank for TRISA.
        clrf    TRISA               ; Clear TRISA register. Configure all of the PORTA pins as digital outputs.
        clrf    TRISC               ; Clear TRISC register. TRISC and TRISA are at the same bank, no need for "banksel".
        clrf    ANSEL               ; Clear ANSEL register and configure all the analog pins as digital i/o.
        banksel PORTA               ; Select appropriate bank for PORTA.
        clrf    PORTA               ; Clear PORTA register.
        clrf    PORTC               ; Clear PORTC register. PORTC and PORTA are at the same bank, no need for "banksel".


        movlw   0x04                ; W = Number of tasks * 2.
        movwf   numOfTasks          ; Since every task has two datas in it, we will multiply by 2.
        clrf    currentTask         ; Set the task#0 as current task.

        CALL    task0               ; Call task#0 since we need to initialize it. We are going to get..
                                    ; ..its PCL and PCLATH values at the start address.
        movlw   0x02                ; W = 2
        addwf   currentTask, f      ; Increment currentTask by 2, since every task occupies 2 places.

        CALL    task1               ; Call task#1, for initialazation.

taskswitcher
        movlw   0x02                ; W = 2
        addwf   currentTask, f      ; Add 2 to currentTask, store it in currentTask.
        movf    numOfTasks, w       ; W = numOfTasks
        subwf   currentTask, w      ; w= f - w
        btfsc   STATUS, 0           ; If currentTask >= numOfTasks
        clrf    currentTask         ; Clear currentTask

        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.
                                    ; For example; task1's index is 2:  [task0_1][task0_2][task1_1][task1_2]....
                                    ;                                       0        1        2        3
        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    INDF, w             ; Copy the contents of current task's first item to W
        movwf   PCLATH              ; Copy W to PCLATH. As a result, current task's PCLATH will be in PCLATH register.

        incf    FSR, f              ; Increment index, so that we will point to the next item of current task.
        movf    INDF, w             ; Copy the contents of current task's second item to W.
        movwf   PCL                 ; Copy W to PCL. Finally, current task's PCL will be in PCL register.

        goto    $                   ; This instruction is not effective. But, enter the endless loop.

;*** TASK 0 ***
TASK0   CODE
;**************
task0
        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.

        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    PCLATH, w           ; Copy PCLATH register's contents to W register.
        movwf   INDF                ; Copy W to current task's first item. We now store PCLATH.

        incf    FSR,f               ; Increment index, so that we will point to the next item of current task.
        movlw   low($+3)            ; Copy PCL+3 to W register. This will let us save the PCL of the start of the task.
        movwf   INDF                ; Copy W to task's next item. With that, we will initialize the current task.
        return                      ; We have gathered our initialazation information. Return back to main.

task0main
        banksel PORTA               ; Select the appropriate bank for PORTA
        movlw   0xAA                ; Move literal to W so that W = 0xAA
        movwf   PORTA               ; PORTA = 0xAA. Use a LATA register to create more robust code.

        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.

        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    PCLATH, w           ; Copy PCLATH register's contents to W register.
        movwf   INDF                ; Copy W to current task's first item. We now store PCLATH of the current state of the task.

        incf    FSR,f               ; Increment index, so that we will point to the next item of current task.
        movlw   low($+3)            ; Copy PCL+3 to W register. This will let us save the PCL of the current state of the task.
        movwf   INDF                ; Copy W to task's next item. With that, we will initialize the current task.

        goto    taskswitcher        ; Yield the CPU to the awaiting task by going to task switcher.

        banksel PORTA               ; Select the appropriate bank for PORTA
        movlw   0x55                ; Move literal to W so that W = 0x55
        movwf   PORTA               ; PORTA = 0xAA. Use a LATA register to create more robust code.

        goto    task0main           ; Loop by going back to "task0main". We will continuously toggle PORTA.

;*** TASK 1 ***
TASK1   CODE
;**************
task1
        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.

        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    PCLATH, w           ; Copy PCLATH register's contents to W register.
        movwf   INDF                ; Copy W to current task's first item. We now store PCLATH.

        incf    FSR,f               ; Increment index, so that we will point to the next item of current task.
        movlw   low($+3)            ; Copy PCL+3 to W register. This will let us save the PCL of the start of the task.
        movwf   INDF                ; Copy W to task's next item. With that, we will initialize the current task.
        return                      ; We have gathered our initialazation information. Return back to main.

task1main
        banksel PORTA               ; Select the appropriate bank for PORTA
        movlw   0xAA                ; Move literal to W so that W = 0xAA
        movwf   PORTA               ; PORTA = 0xAA. Use a LATA register to create more robust code.

        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.

        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    PCLATH, w           ; Copy PCLATH register's contents to W register.
        movwf   INDF                ; Copy W to current task's first item. We now store PCLATH of the current state of the task.

        incf    FSR,f               ; Increment index, so that we will point to the next item of current task.
        movlw   low($+3)            ; Copy PCL+3 to W register. This will let us save the PCL of the current state of the task.
        movwf   INDF                ; Copy W to task's next item. With that, we will initialize the current task.

        goto    taskswitcher        ; Yield the CPU to the awaiting task by going to task switcher.

        banksel PORTA               ; Select the appropriate bank for PORTA
        movlw   0x55                ; Move literal to W so that W = 0x55
        movwf   PORTA               ; PORTA = 0xAA. Use a LATA register to create more robust code.

        goto    task1main           ; Loop by going back to "task1main". We will continuously toggle PORTA.

        END                         ; END of the program.

您组装的第一个程序是多任务RTOS?哇。如果大多数人可以让LED指示灯闪烁,则它们的确做得很好。:-)。
davidcary

好吧,实际上这是我在PIC体系结构中的第一个汇编程序。在此之前,在大学里,我已经采取了8086班,但他们并不实用,我只好由我自己来学习,因为讲师是一个替代品,什么都不知道,但要求在考试中..难以回答的问题
阿卜杜拉kahraman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.