Lý do hạt nhân RTOS đa nhiệm PIC16 của tôi không hoạt động là gì?


11

Tôi đang cố gắng tạo ra một RTOS bán sẵn (hợp tác) cho vi điều khiển PIC x16. Trong câu hỏi trước đây của tôi , tôi đã học được rằng việc truy cập con trỏ ngăn xếp phần cứng là không thể trong các lõi này. Tôi đã xem xét này trang trong PIClist, và đây là những gì tôi đang cố gắng thực hiện bằng C.

Trình biên dịch của tôi là Microchip XC8 và hiện tại tôi đang làm việc trên PIC16F616 với bộ dao động RC bên trong 4 MHz được chọn trong các bit cấu hình.

Tôi đã học được rằng tôi có thể truy cập các thanh ghi PCLATH và PCL bằng C, xem tệp tiêu đề của trình biên dịch của tôi. Vì vậy, tôi đã cố gắng thực hiện một trình chuyển đổi tác vụ đơn giản.

Nó hoạt động như mong muốn trong trình gỡ lỗi nếu tôi tạm dừng trình gỡ lỗi sau khi khởi động lại, đặt lại và đặt PC ở con trỏ khi con trỏ không nằm trên dòng đầu tiên ( TRISA=0;) mà trên một dòng khác (ví dụ ANSEL=0;). Trong lần khởi động đầu tiên của trình gỡ lỗi, tôi nhận được các thông báo này trong Debugger Console:

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

Chỉnh sửa: Tôi không biết những gì làm cho nó hoạt động, nhưng trình gỡ lỗi bây giờ hoạt động hoàn hảo. Vì vậy, bỏ qua đầu ra và đoạn văn trên.

Chỉnh sửa: Thay đổi định nghĩa chính như thế này làm cho mã bên dưới hoạt động. Điều này bắt đầu chức năng chính tại địa chỉ chương trình 0x0099. Tôi không biết điều gì gây ra điều này. Đây không phải là một giải pháp thực sự. Bây giờ tôi đoán rằng có một lỗi cụ thể của trình biên dịch.

void main(void) @ 0x0099
{

Đây là mã C của tôi:

/* 
 * 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
    }
}

đây là tập tin liệt kê tháo gỡ mà trình biên dịch của tôi tạo ra. Bắt đầu lúc line 74.

Tôi đã lập trình chip thực tế, và không có thay đổi nào trên PORTA cả; nó không hoạt động.

Lý do chương trình của tôi không hoạt động là gì?

Câu trả lời:


10

Những gì bạn đang cố gắng làm là khó khăn, nhưng rất giáo dục (nếu bạn chuẩn bị dành nhiều nỗ lực).

Trước tiên, bạn phải nhận ra rằng loại chuyển đổi tác vụ chỉ dành cho PC (trái ngược với PC + SP) (đây là điều duy nhất bạn có thể làm trên lõi PIC 12 hoặc 14 bit đơn giản) sẽ chỉ hoạt động khi tất cả năng suất ( ) các câu lệnh trong một tác vụ nằm trong cùng một chức năng: chúng không thể ở trong một hàm được gọi và trình biên dịch không được làm rối với cấu trúc hàm (như tối ưu hóa có thể làm).

Kế tiếp:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • Bạn dường như cho rằng PCLATH là các bit trên của bộ đếm chương trình, vì PCL là các bit thấp hơn. Đây không phải là trường hợp. Khi bạn ghi vào PCL, các bit PCLATH được ghi vào PC, nhưng các bit PC phía trên không bao giờ (hoàn toàn) được ghi vào PCLATH. Đọc lại phần có liên quan của biểu dữ liệu.
  • Ngay cả khi PCLATH là bit trên của PC, điều này sẽ khiến bạn gặp rắc rối khi hướng dẫn sau khi goto không bật trên cùng một trang '256' như hướng dẫn đầu tiên.
  • goto đơn giản sẽ không hoạt động khi _taskswitcher không có trong trang PCLATH hiện tại, bạn sẽ cần một LGOTO hoặc tương đương.

Một giải pháp cho vấn đề PCLATH của bạn là khai báo nhãn sau goto và ghi các bit dưới và trên của nhãn đó vào vị trí pch và pcl của bạn. Nhưng tôi không chắc bạn có thể khai báo nhãn 'cục bộ' trong lắp ráp nội tuyến. Bạn chắc chắn có thể trong MPASM đơn giản (Olin sẽ mỉm cười).

Cuối cùng, để chuyển đổi ngữ cảnh kiểu này, bạn phải lưu và khôi phục TẤT CẢ bối cảnh mà trình biên dịch có thể phụ thuộc, có thể bao gồm

  • đăng ký gián tiếp
  • cờ trạng thái
  • vị trí bộ nhớ
  • các biến cục bộ có thể trùng lặp trong bộ nhớ vì trình biên dịch không nhận ra rằng các tác vụ của bạn phải độc lập
  • những thứ khác tôi không thể tưởng tượng ngay bây giờ nhưng tác giả trình biên dịch có thể sử dụng trong phiên bản tiếp theo của trình biên dịch (chúng có xu hướng rất giàu trí tưởng tượng)

Kiến trúc PIC có vấn đề hơn về mặt này bởi vì rất nhiều tài nguyên được sắp xếp trên khắp bản đồ bộ nhớ, trong đó các kiến ​​trúc truyền thống có chúng trong các thanh ghi hoặc trên ngăn xếp. Do đó, trình biên dịch PIC thường không tạo mã reentrant, đây là điều bạn chắc chắn cần phải làm những điều bạn muốn (một lần nữa, Olin sẽ cười và tập hợp lại.)

Nếu bạn thích điều này vì niềm vui khi viết một trình chuyển đổi tác vụ, tôi khuyên bạn nên chuyển sang CPU có tổ chức truyền thống hơn, như ARM hoặc Cortex. Nếu bạn bị mắc kẹt với đôi chân của mình trong một tấm PIC cụ thể, hãy nghiên cứu các bộ chuyển đổi PIC hiện có (ví dụ salvo / pumkin?).


Cảm ơn về thông tin tuyệt vời! Tôi quyết tâm tạo ra một trình chuyển đổi nhiệm vụ hợp tác. XC8 và PIC không đứng về phía tôi về vấn đề này, tôi biết điều đó :) Vâng, như bạn thấy, có thể tạo nhãn như tôi đã làm trong một trong những câu trả lời của mình cho câu hỏi này.
abdullah kahraman

Ngoài ra, với sự may mắn của tôi, không có phân trang bộ nhớ chương trình cho PIC16F616 mà tôi đang làm việc, đó là một lợi thế lớn vào thời điểm này, phải không?
abdullah kahraman

Bạn có thể giải thích thêm làm thế nào các biến cục bộ sẽ chồng lấp trong bộ nhớ và cũng là "vị trí bộ nhớ cào"?
abdullah kahraman

Nếu bạn giới hạn bản thân với các chip có mã 2K trở xuống, bạn thực sự có thể quên đi lgoto, nhưng không phải về 256 trang 'hướng dẫn'. Scratch: một trình biên dịch có thể giả định bất cứ điều gì nó làm trong bộ nhớ vẫn giữ nguyên vị trí trừ khi nó 'dễ bay hơi'. Vì vậy, nó có thể đặt các tính toán một phần ở một số vị trí có thể được chia sẻ bởi các chức năng khác nhau . Ovelap: nếu main () gọi cả f () và g () (và không có cuộc gọi nào khác), các biến cục bộ của f () và g () có thể được ánh xạ tới cùng một vị trí bộ nhớ.
Wouter van Ooijen

Chà, có vẻ như gần như không thể đạt được các biến đó và lưu trữ, do vị trí ngẫu nhiên của chúng trong bộ nhớ, phải không?
abdullah kahraman

7

Tôi duyệt qua danh sách lắp ráp mà bạn cung cấp, và không có gì nhảy ra như rõ ràng là bị hỏng.

Nếu tôi là bạn, các bước tiếp theo của tôi sẽ là:

(1) Tôi sẽ chọn một số phương pháp nhấp nháy đèn LED khác. "Vấn đề đọc-sửa-ghi" khét tiếng có thể (hoặc không) được kích hoạt bởi "XORWF PORTA, F" trong danh sách lắp ráp.

Có lẽ một cái gì đó như:

// 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
    }
}

(Nếu bạn thực sự muốn xem giải thích chi tiết về lý do tại sao "XORWF PORTA, F" thường gây ra sự cố, hãy xem " Điều gì gây ra việc bật ON pin đầu ra trên Microchip PIC16F690 để tự động TẮT một pin khác trên cùng một cổng? "; " Điều gì xảy ra khi dữ liệu được ghi vào LATCH? ";" Vấn đề Đọc-Sửa đổi-Viết ";" Đọc trước khi ghi ")

(2) Tôi sẽ thực hiện một bước thông qua mã, đảm bảo rằng các biến đang được đặt thành các giá trị dự kiến ​​và trong chuỗi dự kiến. Tôi không chắc có tồn tại trình gỡ lỗi phần cứng một bước cho PIC16F616 hay không, nhưng có nhiều trình giả lập vi điều khiển PIC tuyệt vời như PICsim có thể mô phỏng chip dòng PIC16.

Mã một bước (trong trình giả lập hoặc với trình gỡ lỗi phần cứng một bước) là một cách tốt để hiểu chi tiết về những gì đang diễn ra, xác nhận rằng mọi thứ đang diễn ra theo cách bạn dự định và nó cho phép bạn thấy những điều đang xảy ra Thực tế không thể nhìn thấy khi chạy chương trình đầy đủ tốc độ.

(3) Nếu tôi vẫn còn bối rối, tôi sẽ thử dịch mã để sử dụng mảng thay vì con trỏ. Một số người thấy sử dụng con trỏ hơi khó và khó gỡ lỗi. Tôi thường thấy rằng, trong quá trình dịch mã con trỏ phức tạp thành mã hướng mảng, tôi tìm ra lỗi là gì. Ngay cả khi tôi kết thúc việc quay lại mã con trỏ ban đầu và loại bỏ phiên bản mảng, bài tập vẫn hữu ích vì nó giúp tôi tìm và sửa lỗi. (Đôi khi trình biên dịch có thể tạo mã ngắn hơn, nhanh hơn từ mã hướng mảng, do đó, có lần tôi ném mã con trỏ ban đầu và giữ phiên bản mảng).

Có lẽ một cái gì đó như

// 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");

Tôi đang thực hiện mảng bây giờ. Cảm ơn vì lời giới thiệu.
abdullah kahraman

3

Về cơ bản tôi sẽ đồng ý với davidcary. Có vẻ như nó có thể làm việc.

Tôi không biết những gì làm cho nó hoạt động, nhưng trình gỡ lỗi bây giờ hoạt động hoàn hảo.

Tôi đoán điều này có nghĩa là nó hoạt động hoàn hảo trong trình giả lập .

1) Kiểm tra xem các tác vụ của bạn có tự hoạt động không, trong môi trường không RTOS trong chip thực.

2) Thực hiện sửa lỗi trong mạch. Bước qua chương trình trên chip thực và xem tất cả các biến có liên quan để chắc chắn rằng mọi thứ sẽ diễn ra theo đúng kế hoạch.


Vâng, tôi có nghĩa là trình gỡ lỗi, đó là trình giả lập MPLABX. Nhiệm vụ tự hoạt động, trong môi trường không RTOS. Tôi không có một ICD. Tôi chỉ có mikroElektronika easyPIC5 với ICD, tuy nhiên, nó chỉ hoạt động với trình biên dịch mikroC. Bây giờ, thay đổi trình biên dịch sẽ không cho phép tôi tìm ra vấn đề, hoặc, nó sẽ?
abdullah kahraman

1

Tôi chỉ nhìn vào mã của bạn một cách ngắn gọn, nhưng nó không có ý nghĩa. Ở một số nơi bạn đang viết thư cho PCL, sau đó hy vọng nó sẽ loại bỏ các hướng dẫn khác theo đó.

Như tôi cũng đã nói trước đây, C không phù hợp với loại truy cập cấp thấp của các thanh ghi phần cứng cơ bản. Bạn thực sự cần phải sử dụng lắp ráp cho việc này. Cố gắng tìm hiểu tại sao mã C không hoạt động chỉ là một sự lãng phí thời gian vô nghĩa.


Tôi không thể kết hợp lắp ráp và C. Tôi đã phải làm rất nhiều việc. Cả mã dis-assembly và C có vẻ hợp lý với tôi. Bạn đang đề cập đến điều gì mà tôi đang mong đợi để thực hiện các hướng dẫn tuân theo ghi vào PCL? Tôi đã xem trình gỡ lỗi cho cả lắp ráp và C, và nó hoạt động như mong muốn.
abdullah kahraman

Xin lỗi vì -1. Tôi nên nhấn một cách vô tình và bây giờ tôi đã nhận thấy điều đó.
abdullah kahraman

@abdullah: Trên máy tôi hiện tại, tôi không thể thấy mã nguồn. Nó bị sập vĩnh viễn trong trình duyệt. Tôi nhớ rằng bạn đã gán công cụ cho PCLATH, sau đó là PCL, sau đó tôi nghĩ trong một trường hợp đã cố gắng thực hiện TRẢ LẠI. Ngay khi bạn viết thư cho PCL, việc thực thi sẽ chuyển đến địa chỉ bạn đã nhét vào PCLATH: PCL, vì vậy mọi hướng dẫn sau đây đều không liên quan. Thực sự không tốt khi làm điều này trong C vì bạn đang làm rối tung các tài nguyên do trình biên dịch quản lý và do đó có thể làm mất hiệu lực các giả định của trình biên dịch. Sử dụng lắp ráp thực sự đã. Tôi cảm thấy mệt mỏi vì phải lặp lại điều này.
Olin Lathrop

1
Nhìn vào mã, không có nơi nào PCL được sửa đổi ngay trước một tuyên bố khác. Nơi duy nhất có vẻ như được sửa đổi là ở phần cuối của hàm main (). Nhưng đó là một điểm tốt mà bạn phải chắc chắn rằng bạn không đấu tranh với trình biên dịch để lấy tài nguyên của nó. Cả hai bạn sẽ mất.
Rocketmagnet

3
C hoàn toàn chấp nhận được đối với loại công việc này và trên thực tế, tốt hơn là viết bằng ngôn ngữ cấp trung bình như C trái ngược với ngôn ngữ lắp ráp vì dễ đọc và duy trì hơn. Một trình biên dịch tốt sẽ tạo ra mã không quá xa so với những gì người bình thường sẽ viết. Tôi thường chỉ viết trình biên dịch mã cho mã khởi động rất cơ bản, các khu vực cụ thể mà tôi có thể tối ưu hóa tốt hơn trình biên dịch hoặc cho các ngắt nhanh hoặc nếu các ràng buộc kích thước mã ra lệnh. Ngày nay không có nhiều nhu cầu lắp ráp thuần túy.
akohlsmith

1

Dưới đây là cách thực hiện với lắp ráp nội tuyến bằng trình biên dịch XC8, và nó hoạt động ngay bây giờ! Tuy nhiên, tôi cần thêm phát triển thêm mã để lưu và khôi phục thanh STATUSghi, điều này có vẻ khó hơn một chút so với đăng ký bình thường.

Chỉnh sửa: Mã được thay đổi. Vui lòng tham khảo các phiên bản cũ hơn của bài đăng này cho mã trướ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
#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.
    }
}

Và đây là tập tin tiêu đề 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 */

Có vẻ như bạn sẽ giành được tiền thưởng của riêng bạn. Xin chúc mừng! :-)
stevenvh

@stevenvh Ah, điều đó xảy ra, tôi không biết? Cảm ơn :)
abdullah kahraman

Xin chúc mừng vì đã làm cho nó hoạt động!
davidcary

Cảm ơn @davidcary! Tôi thực sự đánh giá cao lời chúc mừng của bạn
abdullah kahraman

1
Bạn có thực sự cần khôi phục TÌNH TRẠNG? Nếu vậy, bạn sẽ cần sử dụng hướng dẫn "hoán đổi", vì các lý do được ghi lại ở nơi khác: " P. Anderson ", " Hướng dẫn gia đình tầm trung Microchip: phần 8.5 Tiết kiệm bối cảnh ", "Lưu PIC và TÌNH TRẠNG "
davidcary

0

Dưới đây là cách thực hiện điều này bằng cách sử dụng lắp ráp. Truy cập cùng mã với định dạng (liên kết đến Pastebin) . Làm thế nào nó có thể được cải thiện? Đây là chương trình đầu tiên của tôi trong lắp ráp PIC, bất kỳ bình luận nào cũng được đánh giá cao.

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.

Chương trình đầu tiên của bạn trong lắp ráp là một RTOS đa tác vụ? Ồ Hầu hết mọi người đang làm rất tốt nếu họ có thể làm cho đèn LED nhấp nháy. :-).
davidcary

Chà, thực sự đây là chương trình lắp ráp đầu tiên của tôi trong kiến ​​trúc PIC . Trước đó, ở trường đại học, tôi đã học 8086 lớp nhưng chúng không thực tế và tôi phải tự học vì giảng viên là người thay thế và không biết gì, nhưng hỏi những câu hỏi khó trong các kỳ thi ..
abdullah kahraman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.