Чому причина ядра RTOS ядра POS16 не працює?


11

Я намагаюся створити напівперешкодний (кооперативний) RTOS для мікроконтролерів PIC x16. У своєму попередньому запитанні я дізнався, що в цих ядрах отримати доступ до вказівника стеку апаратних засобів неможливо. Я переглянув цю сторінку в PIClist, і це те, що я намагаюся реалізувати за допомогою C.

Мій компілятор Microchip XC8, і зараз я працюю над PIC16F616 з внутрішнім RC-генератором 4 МГц, вибраним у бітах конфігурації.

Я дізнався, що я можу отримати доступ до регістрів PCLATH та PCL за допомогою C, дивлячись на файл заголовка мого компілятора. Отже, я спробував реалізувати простий перемикач завдань.

Він працює так, як хотів у відладчику, якщо я призупиняю відладчик після перезавантаження, скидання та встановлення ПК на курсор, коли курсор знаходиться не в першому рядку ( 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 взагалі не змінився; це не працює.

З якої причини моя програма не працює?

Відповіді:


10

Те, що ви намагаєтеся зробити, є складним, але дуже навчальним (якщо ви готові витратити багато зусиль).

По-перше, ви повинні усвідомити, що такий тип переключення завдань лише для ПК (на відміну від PC + SP) (що єдине, що ви можете зробити на простому 12 або 14-бітному ядрі PIC) буде працювати лише тоді, коли весь вихід ( ) заяви у завданні виконуються в одній і тій же функції: вони не можуть бути в викликаній функції, і компілятор не повинен змішуватися зі структурою функції (як це може зробити оптимізація).

Далі:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • Ви, здається, припускаєте, що PCLATH є верхніми бітами програмного лічильника, як PCL - нижніми бітами. Це НЕ так. Коли ви пишете в PCL, біти PCLATH записуються на ПК, але верхні біти ПК ніколи (неявно) не записуються в PCLATH. Перечитайте відповідний розділ даних.
  • Навіть якщо PCLATH був верхнім бітом ПК, це призведе до виникнення неприємностей, коли інструкція після goto знаходиться не на тій самій 256 сторінці інструкцій, що і перша інструкція.
  • звичайний goto не працюватиме, коли _taskswitcher не знаходиться на поточній сторінці PCLATH, вам знадобиться LGOTO або його аналог.

Рішення вашої проблеми PCLATH полягає в тому, щоб оголосити мітку після goto та записати нижній та верхній біти цієї мітки у свої pch та pcl місця. Але я не впевнений, що ви можете оголосити "локальну" мітку в рамках вбудованої збірки. Ви впевнені, що можете в простому MPASM (Олін посміхнеться).

Нарешті, для такого типу перемикання контексту необхідно зберегти та відновити ВСІ контексти, від яких може залежати компілятор, який може включати

  • непрямий регістр (и)
  • прапори статусу
  • подряпини пам'яті
  • локальні змінні, які можуть перекриватися в пам'яті, оскільки компілятор не усвідомлює, що ваші завдання повинні бути незалежними
  • інші речі, які я не уявляю зараз, але автор компілятора може використовувати в наступній версії компілятора (вони, як правило, дуже образні)

В цьому відношенні архітектура PIC є більш проблематичною, оскільки багато ресурсів розміщено по всій карті пам'яті, де більш традиційні архітектури мають їх у регістрах або на стеку. Як наслідок, PIC-компілятори часто не генерують рецензуючий код, саме це вам точно потрібно робити те, що вам потрібно (знову ж таки, Олін буде заборонено посміхатися і збиратися разом.)

Якщо ви з цим радієте написати перемикач завдань, я пропоную вам перейти до центрального процесора, який має більш традиційну організацію, наприклад, ARM або Cortex. Якщо ви застрягли ногами в бетонній плиті ПІК, вивчіть існуючі перемикачі ПІК (наприклад, залп / гарбуз?).


Дякую за чудову інформацію! Я налаштований на створення кооперативного перемикача завдань. XC8 і PIC не на моєму боці в цьому, я це знаю :) Так, як ви бачите, можна створити мітки, як я це робив в одній із своїх відповідей на це питання.
абдулла кахраман

Також, на мою щастя, немає жодної сторінки програмної пам'яті для PIC16F616, над якою я працюю, що є великою перевагою на даний момент, правда?
абдулла кахраман

Чи можете ви пояснити, як локальні змінні перекриватимуться в пам'яті, а також "подряпують місця пам'яті"?
абдулла кахраман

Якщо ви обмежитеся фішками з кодом 2K або менше, ви дійсно можете забути про lgoto, але не про "сторінки" з 256 інструкціями. Scratch: компілятор може припускати, що все, що він робить у пам'яті, залишається на місці, якщо тільки воно не є "мінливим". Таким чином, він може розмістити часткові обчислення в якомусь місці, яке може бути розділене різними функціями . Overlap: якщо main () викликає f () та g () (а інших викликів немає), локальні змінні f () та g () можуть бути відображені в одних і тих же місцях пам'яті.
Wouter van Ooijen

Що ж, здається, що майже неможливо дістатись до цих змінних і зберегти їх через їх випадкове місце в пам'яті, правда?
абдулла кахраман

7

Я переглянув список, який ви надали, і нічого не вискакує як очевидно зламане.

Якби я був ти, наступними моїми кроками були б:

(1) Я вибрав би інший метод блимання світлодіодів. Сумно відома проблема "читання-зміна-запис" може (або не може) бути спровокована "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");

Зараз я реалізую масиви. Дякую за рекомендацію.
абдулла кахраман

3

Я б в основному погодився з давидкарі. Схоже, це могло б спрацювати.

Я не знаю, що змусило його працювати, але відладчик зараз працює чудово.

Я здогадуюсь цим, ви маєте на увазі, що він прекрасно працює в тренажері .

1) Переконайтеся, що ваші завдання працюють самостійно, у не-RTOS-середовищі, у реальному чіпі.

2) Робіть налагодження в ланцюзі. Перегляньте програму на справжньому мікросхемі та перегляньте всі відповідні змінні, щоб переконатися, що все йде за планом.


Так, я мав на увазі відладчик, тобто симулятор MPLABX. Завдання працюють самостійно, в не-RTOS-середовищі. У мене немає МКБ. У мене є лише mikroElektronika easyPIC5 з ICD, однак він працює лише з компілятором mikroC. Тепер, зміна компіляторів не дозволить мені знайти проблему, чи, чи не так?
абдулла кахраман

1

Я лише коротко переглянув ваш код, але це не має сенсу. У кількох місцях ви пишете в PCL, а потім очікуєте, що він виправдає інші інструкції, що випливають після цього.

Як я вже говорив раніше, C не підходить для такого доступу з низьким рівнем доступу до основних апаратних регістрів. Для цього вам дійсно потрібно використовувати збірку. Спроба з'ясувати, чому код C не працює - це лише безглузда марнотрата часу.


Я не міг поєднувати складання і C. Мені довелося багато працювати. І розбір, і код C мені здаються логічними. Куди ви посилаєтесь, що я очікую виконати інструкції, що слідують за записом на PCL? Я спостерігав за налагоджувачем і для збірки, і для C, і він працює як хотілося.
абдулла кахраман

Вибачте за -1. Я мав би натиснути випадково, і зараз я це помітив.
abdullah kahraman

@abdullah: На машині, на якій я зараз перебуваю, я не бачу вихідного коду. Він назавжди згорнувся в браузері. Я пам’ятаю, що ви присвоїли речі PCLATH, потім PCL, і я думаю, що в одному випадку намагалися зробити ПОВЕРНЕННЯ. Як тільки ви пишете в PCL, виконання буде переходити на адресу, яку ви вписали в PCLATH: PCL, тому будь-які наступні інструкції не мають значення. Це дійсно не добре робити це в C, тому що ви возитесь з керованими ресурсами компілятора і тим самим можливо недійсні припущення компілятора. Використовуйте справжню збірку вже. Мені набридло повторювати це.
Олін Латроп

1
Дивлячись на код, PCL ніде не змінюється перед іншим оператором. Єдине місце, яке, здається, модифіковане, знаходиться в самому кінці main (). Але це добре, що ви повинні бути дуже впевнені, що не бороєтесь з компілятором за його ресурси. Ви обоє програєте.
Rocketmagnet

3
C є цілком прийнятним для такої роботи, і насправді бажано писати мовою середнього рівня, як C, а не мовою складання, оскільки її легше читати та підтримувати. Гідний компілятор генерує код не надто далеко від того, що в середньому людина напише. Я, як правило, пишу асемблер для самого базового коду запуску, конкретних областей, де я можу оптимізувати краще, ніж компілятор або для швидких переривань, або якщо обмеження розміру коду диктують це. Чиста потреба в чистому складі в ці дні.
akohlsmith

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

Дякую @davidcary! Я дуже ціную ваші вітання, хлопці.
абдулла кахраман

1
Вам справді потрібно відновити STATUS? Якщо це так, вам потрібно буде скористатись інструкцією "swapf" з причин, зафіксованих в інших місцях: " P. Anderson ", " 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? Ого. Більшість людей роблять дуже добре, якщо вони можуть отримати світлодіодний індикатор. :-).
davidcary

Ну, насправді це моя перша програма складання архітектури PIC . До цього в університеті я брав 8086 занять, але вони не були практичними, і мені довелося вчитися самостійно, оскільки викладач був замісником і нічого не знав, але на екзаменах задавав важкі запитання ..
abdullah kahraman
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.