PIC16マルチタスクRTOSカーネルが機能しない理由は何ですか?


11

PIC x16マイクロコントローラー用のセミプリエンプティブ(協調)RTOSを作成しようとしています。私には、前の質問、私は、ハードウェアスタックポインタにアクセスすると、これらのコアは不可能であることを学びました。私はPIClist でこのページを見てきましたが、これはCを使用して実装しようとしているものです。

私のコンパイラはMicrochip XC8で、現在、構成ビットで4MHzの内部RC発振器が選択されたPIC16F616で作業しています。

コンパイラのヘッダーファイルを確認しながら、CでPCLATHおよびPCLレジスタにアクセスできることを学びました。そこで、簡単なタスクスイッチャーを実装してみました。

カーソルが最初の行(TRISA=0;)ではなく別の行(などANSEL=0;)にあるときに、再起動後にデバッガを一時停止してリセットし、カーソルにPCを設定すると、デバッガで必要に応じて機能します。デバッガーの最初の起動時に、これらのメッセージがに表示されます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にはまったく変更がありません。動作しません。

私のプログラムが機能しない理由は何ですか?

回答:


10

あなたがやろうとしていることはトリッキーですが、非常に教育的です(多くの努力を費やす準備ができている場合)。

まず、この種のPCのみ(PC + SPとは対照的に)のタスク切り替え(これは、プレーンな12ビットまたは14ビットPICコアで実行できる唯一のことです)は、すべての歩留まり( )タスク内のステートメントは同じ機能にあります。それらは呼び出された関数内にあってはならず、コンパイラーは関数構造を混乱させてはなりません(最適化のように)。

次:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • PCLは下位ビットであるため、PCLATHはプログラムカウンタの上位ビットであると想定しているようです。これはそうではありません。PCLに書き込むと、PCLATHビットはPCに書き込まれますが、上位のPCビットが(暗黙的に)PCLATHに書き込まれることはありません。データシートの関連セクションをもう一度お読みください。
  • PCLATHがPCの上位ビットであったとしても、gotoの後の命令が最初の命令と同じ256命令の「ページ」上にない場合、これにより問題が発生します。
  • _taskswitcherが現在のPCLATHページにない場合、プレーンなgotoは機能しません。LGOTOまたは同等のものが必要です。

PCLATH問題の解決策は、gotoの後にラベルを宣言し、そのラベルの下位ビットと上位ビットをpchとpclの場所に書き込むことです。しかし、インラインアセンブリで「ローカル」ラベルを宣言できるかどうかはわかりません。あなたは確かにプレーンなMPASMでできます(Olinは笑います)。

最後に、この種のコンテキスト切り替えには、コンパイラーが依存する可能性があるすべてのコンテキストを保存および復元する必要があります。

  • インダイレクションレジスタ
  • ステータスフラグ
  • スクラッチメモリの場所
  • タスクが独立している必要があることをコンパイラが認識しないため、メモリ内でオーバーラップする可能性があるローカル変数
  • 現時点では想像できない他の事柄ですが、コンパイラの作者は次のバージョンのコンパイラで使用するかもしれません(それらは非常に想像力豊かになる傾向があります)

PICアーキテクチャは、この点でより問題が多く、多くのリソースがメモリマップ全体に行き渡っているため、より伝統的なアーキテクチャでは、それらはレジスタまたはスタック上にあります。結果として、PICコンパイラーは再入可能コードを生成しないことがよくあります。これは、確実に必要なことを実行するために必要なものです(ここでも、Olinはおそらく微笑んで組み立てます)。

タスクスイッチャーを書く喜びでこれに夢中になっている場合は、ARMやCortexなどの従来の構成のCPUを使用することをお勧めします。PICのコンクリートプレートで足が動かなくなった場合は、既存のPICスイッチャー(例:salvo / pumkin?)を調べてください。


素晴らしい情報をありがとう!協力的なタスクスイッチャーを作成することにしました。XC8とPICは私の側ではありません、私はそれを認識しています:)はい、ご覧のとおり、この質問に対する私の回答の1つで行ったようにラベルを作成することは可能です。
abdullah kahraman 2012年

また、幸運にも、私が取り組んでいるPIC16F616のプログラムメモリのページングは​​ありません。これは、現時点で大きな利点ですよね。
abdullah kahraman

メモリ内でローカル変数がどのようにオーバーラップするか、および「スクラッチメモリの場所」についてさらに説明できますか?
abdullah kahraman 2012年

2Kコード以下のチップに制限すると、実際にはlgotoを忘れることがありますが、256命令の「ページ」については忘れません。スクラッチ:コンパイラーは、それが「揮発性」でない限り、メモリー内で実行することはすべて、その場に留まると想定できます。したがって、異なる関数で共有できる場所に部分的な計算を配置する可能性があります。Ovelap:main()がf()とg()の両方を呼び出す場合(他に呼び出しがない場合)、f()とg()のローカル変数を同じメモリ位置にマップできます。
Wouter van Ooijen

さて、それらの変数にアクセスして格納することは、メモリ内のランダムな場所のため、ほとんど不可能であるようですよね?
abdullah kahraman

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は」多くの場合、問題が発生する理由について詳細な説明を表示したい場合はどのように自然に同一ポート上で別のピンをオフにするには、マイクロチップPIC16F690上の単一の出力ピンをオンにする原因は?『;』何が起こりましたデータがLATCHに書き込まれるとき? ";" 読み取り-変更-書き込みの問題 ";" 書き込み前の読み取り ")

(2)コードを1ステップ実行して、変数が期待される値に、期待される順序で設定されていることを確認します。PIC16F616のシングルステップハードウェアデバッガーが存在するかどうかはわかりませんが、 PIC16シリーズチップをシミュレートできるPICsimなどの優れたPICマイクロコントローラーシミュレーターはたくさんあります。

シングルステップコード(シミュレーター内またはシングルステップハードウェアデバッガーを使用)は、実際に起こっていることの詳細を理解し、意図したとおりに起こっていることを確認するための優れた方法です。プログラムをフルスピードで実行しているときは、ほとんど見ることができません。

(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

3

私は基本的にデビッドカリーに同意します。うまくいくようです。

何が機能したのかはわかりませんが、デバッガは完全に機能します。

これは、シミュレータで完全に機能することを意味していると思います。

1)実際のチップの非RTOS環境で、タスクが独自に機能することを確認します。

2)インサーキットデバッグを実行します。実際のチップでプログラムをステップ実行し、関連するすべての変数を監視して、すべてが計画どおりに進んでいることを確認します。


はい、私はデバッガ、つまりMPLABXのシミュレータを意味しました。RTOS以外の環境では、タスクは独自に機能します。ICDを持っていません。私はICDを備えたmikroElektronika easyPIC5しか持っていませんが、それはmikroCコンパイラでのみ機能します。さて、コンパイラを変更しても問題を見つけることができませんか?
abdullah kahraman

1

私はコードを簡単に見ただけですが、意味がありません。PCLに書き込んでいるいくつかの場所で、PCLが他の命令を実行することを期待しています。

前にも述べたように、Cは基本的なハードウェアレジスタへのこの種の低レベルアクセスには不適切です。これには本当にアセンブリを使用する必要があります。Cコードが機能しない理由を理解しようとするのは、時間の無駄です。


アセンブリとCを組み合わせることができませんでした。多くの作業を行う必要がありました。逆アセンブリとCコードの両方が私には論理的に思えます。PCLへの書き込みに続く命令を実行することを期待していることをどこで参照していますか?私はデバッガーをアセンブリーとCの両方で監視しており、期待どおりに動作します。
abdullah kahraman

-1でごめんなさい。私は誤って押していたはずでしたが、今はそれに気づきました。
abdullah kahraman

@abdullah:私が今いるマシンでは、ソースコードを見ることができません。ブラウザで完全に折りたたまれています。私はあなたがPCLATH、次にPCLにスタッフを割り当てたことを覚えています。それから、あるケースではRETURNを実行しようとしたと思います。PCLに書き込むとすぐに、実行はPCLATH:PCLに詰め込んだアドレスにジャンプするので、以下の命令は関係ありません。Cでこれを行うのは実際に良いことではありません。コンパイラーが管理するリソースをいじって、コンパイラーの仮定を無効にする可能性があるからです。すでに実際のアセンブリを使用してください。私はこれを繰り返さなければならないことにうんざりしています。
Olin Lathrop

1
コードを見ると、PCLが別のステートメントの直前に変更されている場所はありません。変更されているように見える唯一の場所は、main()の最後です。しかし、コンパイラーとリソースを争っていないことを非常に確信している必要があるのは良い点です。どちらも負けます。
Rocketmagnet

3
Cはこの種の作業に完全に受け入れられます。実際、アセンブリ言語ではなく、Cのような中級言語で書く方が、読みやすく、保守しやすいので望ましいです。まともなコンパイラーは、平均的な人がとにかく書くものからそれほど遠くないコードを生成します。私は通常、非常に基本的なスタートアップコード、コンパイラーより高速に最適化できる特定の領域、または高速割り込みのために、またはコードサイズの制約がそれを要求する場合にのみアセンブラーを記述します。最近、純粋なアセンブリの必要性はあまりありません。
akohlsmith '19年

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

@stevenvhああ、それは起こりますか?ありがとう:)
abdullah kahraman

動作させていただき、ありがとうございます。
davidcary 2012

@davidcaryに感謝!皆さんおめでとうございます。
abdullah kahraman 2012

1
本当にSTATUSを復元する必要がありますか?その場合は、使用する必要があります指示「swapf」、他の場所で文書化の理由のために:「P.アンダーソン」、「手動マイクロチップミッドレンジ・ファミリ:セクション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 2012

まあ、これはPICアーキテクチャでの最初のアセンブリプログラムです。その前に、大学では、..私は8086クラスをとっているが、彼らは実用的ではなかったと講師が代替したと、何も知らなかったので、私は自分で学ばなければならなかった、まだ試験でのハードな質問を尋ねる
アブドラkahraman
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.