อะไรเป็นสาเหตุที่ทำให้เคอร์เนล RTOS มัลติทาสกิ้ง PIC16 ของฉันไม่ทำงาน?


11

ฉันพยายามที่จะสร้าง RTOS กึ่งร่วมมือ (pre-emptive) สำหรับไมโครคอนโทรลเลอร์ PIC x16 ในคำถามก่อนหน้านี้ฉันได้เรียนรู้ว่าการเข้าถึงตัวชี้สแต็กของฮาร์ดแวร์ไม่สามารถทำได้ในแกนประมวลผลเหล่านี้ ฉันดูที่หน้านี้ใน PIClist และนี่คือสิ่งที่ฉันพยายามนำไปใช้โดยใช้ C

คอมไพเลอร์ของฉันคือ Microchip XC8 และขณะนี้ฉันกำลังทำงานกับ PIC16F616 ด้วย RC oscillator ภายใน 4MHz ที่เลือกในบิตกำหนดค่า

ฉันได้เรียนรู้ว่าฉันสามารถเข้าถึงการลงทะเบียน 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 เท่านั้น (ตรงข้ามกับ PC + SP) (ซึ่งเป็นสิ่งเดียวที่คุณสามารถทำได้บนคอร์ PIC แบบธรรมดาขนาด 12 หรือ 14 บิต) จะทำงานเมื่อผลผลิตทั้งหมดเท่านั้น ( ) คำสั่งในงานที่อยู่ใน funtion เดียวกัน: พวกเขาไม่สามารถอยู่ในฟังก์ชั่นที่เรียกว่าและคอมไพเลอร์จะต้องไม่ได้ยุ่งกับโครงสร้างฟังก์ชั่น (เช่นการเพิ่มประสิทธิภาพอาจจะทำ)

ต่อไป:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • ดูเหมือนว่าคุณจะคิดว่า PCLATH เป็นบิตบนของตัวนับโปรแกรมเนื่องจาก PCL เป็นบิตที่ต่ำกว่า กรณีนี้ไม่ได้. เมื่อคุณเขียนไปยัง PCL บิต PCLATH จะถูกเขียนไปยังพีซี แต่บิตพีซีด้านบนจะไม่ถูกเขียนลงใน PCLATH (โดยนัย) อ่านส่วนที่เกี่ยวข้องของแผ่นข้อมูลอีกครั้ง
  • แม้ว่า PCLATH จะเป็นบิตบนของพีซีสิ่งนี้จะทำให้คุณเกิดปัญหาเมื่อคำสั่งหลังจาก goto ไม่ได้อยู่ในหน้า 256 คำสั่งเดียวกับคำสั่งแรก
  • goto ธรรมดาจะไม่ทำงานเมื่อ _taskswitcher ไม่ได้อยู่ในหน้า PCLATH ปัจจุบันคุณจะต้องมี LGOTO หรือเทียบเท่า

ทางออกสำหรับปัญหา PCLATH ของคุณคือการประกาศฉลากหลังข้ามไปและเขียนบิตที่ต่ำและสูงของฉลากนั้นไปยังตำแหน่ง pch และ pcl ของคุณ แต่ฉันไม่แน่ใจว่าคุณสามารถประกาศฉลาก 'ท้องถิ่น' ในชุดประกอบแบบอินไลน์ คุณแน่ใจได้ใน MPASM ธรรมดา (แลงจะยิ้ม)

สุดท้ายนี้การสลับบริบทชนิดนี้คุณต้องบันทึกและกู้คืนบริบททั้งหมดที่คอมไพเลอร์อาจขึ้นอยู่กับซึ่งอาจรวมถึง

  • การลงทะเบียนทางอ้อม
  • ธงสถานะ
  • เกาตำแหน่งหน่วยความจำ
  • ตัวแปรโลคัลที่อาจทับซ้อนกันในหน่วยความจำเนื่องจากคอมไพเลอร์ไม่ได้ตระหนักว่างานของคุณต้องเป็นอิสระ
  • สิ่งอื่น ๆ ที่ฉันไม่สามารถจินตนาการได้ในขณะนี้ แต่ผู้เขียนคอมไพเลอร์อาจใช้ในคอมไพเลอร์รุ่นต่อไป (พวกเขามีแนวโน้มที่จะจินตนาการมาก)

สถาปัตยกรรม PIC มีปัญหามากขึ้นในส่วนนี้เนื่องจากมีทรัพยากรจำนวนมากทั่วทั้งแผนที่หน่วยความจำซึ่งมีสถาปัตยกรรมแบบดั้งเดิมจำนวนมากที่มีอยู่ในรีจิสเตอร์หรือบนสแต็ก ดังนั้นคอมไพเลอร์ PIC มักจะไม่สร้างรหัส reentrant ซึ่งเป็นสิ่งที่คุณต้องทำในสิ่งที่คุณต้องการแน่นอน (อีกครั้งแลงจะยิ้มและประกอบไปด้วย)

หากคุณเป็นเช่นนี้เพื่อความสุขในการเขียนตัวสลับงานฉันขอแนะนำให้คุณใช้ซีพียูที่มีองค์กรดั้งเดิมมากกว่าเช่น ARM หรือ Cortex หากคุณติดอยู่กับเท้าของคุณในแผ่นคอนกรีตของ PIC ให้ศึกษาสวิตช์ของ PIC ที่มีอยู่ (เช่น salvo / pumkin?)


ขอบคุณสำหรับข้อมูลที่ดี! ฉันมุ่งมั่นที่จะสร้างตัวสลับงานแบบร่วมมือ XC8 และ PIC ไม่ได้อยู่ข้างฉันฉันรู้เรื่องนั้น :) ใช่อย่างที่คุณเห็นมันเป็นไปได้ที่จะสร้างป้ายกำกับตามที่ฉันทำในหนึ่งในคำตอบของคำถามนี้
abdullah kahraman

นอกจากนี้เพื่อโชคของฉันไม่มีเพจจิ้งหน่วยความจำของโปรแกรมสำหรับ PIC16F616 ที่ฉันทำงานอยู่นั่นเป็นข้อได้เปรียบที่ยิ่งใหญ่ในตอนนี้ใช่ไหม?
abdullah kahraman

คุณช่วยอธิบายเพิ่มเติมได้หรือไม่ว่าตัวแปรโลคัลจะซ้อนทับกันในหน่วยความจำและ "ลบตำแหน่งหน่วยความจำ" ได้อย่างไร
abdullah kahraman

หากคุณ จำกัด ตัวเองด้วยชิปที่มีรหัส 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" มักจะทำให้เกิดปัญหาดู " สิ่งที่ทำให้เกิดการเปิดขาเอาท์พุทเดียวบน Microchip PIC16F690 เพื่อธรรมชาติปิดขาอีกในพอร์ตเดียวกันได้หรือไม่ ';' จะเกิดอะไรขึ้น เมื่อข้อมูลถูกเขียนไปยัง LATCH? ";" ปัญหาอ่าน - แก้ไข - เขียน ";" อ่านก่อนเขียน ")

(2) ฉันต้องการทีละขั้นตอนผ่านโค้ดทำให้แน่ใจว่าตัวแปรถูกตั้งค่าเป็นค่าที่คาดหวังและตามลำดับที่คาดไว้ ฉันไม่แน่ใจว่ามีดีบักเกอร์ฮาร์ดแวร์ขั้นตอนเดียวสำหรับ PIC16F616 หรือไม่ แต่มีตัวจำลองไมโครคอนโทรลเลอร์ PIC ที่ยอดเยี่ยมมากมายเช่นPICsim ที่สามารถจำลองชิปซีรีส์ PIC16 ได้

รหัสก้าวเดียว (ในโปรแกรมจำลองหรือด้วยดีบักเกอร์ฮาร์ดแวร์ขั้นตอนเดียว) เป็นวิธีที่ดีในการทำความเข้าใจรายละเอียดของสิ่งที่เกิดขึ้นจริงยืนยันว่าสิ่งต่าง ๆ เกิดขึ้นตามที่คุณต้องการและช่วยให้คุณเห็นสิ่งที่เป็น แทบเป็นไปไม่ได้ที่จะเห็นเมื่อรันโปรแกรมเต็มความเร็ว

(3) ถ้าฉันยังคงนิ่งงันฉันจะลองแปลรหัสเพื่อใช้อาร์เรย์แทนพอยน์เตอร์ บางคนพบว่าใช้พอยน์เตอร์ยุ่งยากเล็กน้อยและยากที่จะดีบั๊ก ฉันมักจะพบว่าในกระบวนการแปลรหัสตัวชี้หากินเป็นรหัสแบบอาเรย์ฉันพบว่าข้อบกพร่องคืออะไร แม้ว่าฉันจะกลับไปเป็นรหัสตัวชี้ดั้งเดิมและทิ้งเวอร์ชั่นอาเรย์การออกกำลังกายก็มีประโยชน์เพราะมันช่วยให้ฉันค้นหาและแก้ไขข้อบกพร่อง (บางครั้งคอมไพเลอร์สามารถสร้างรหัสที่สั้นกว่าและเร็วกว่าจากรหัสที่มีแถวลำดับดังนั้นจึงมีบางครั้งที่ฉันปล่อยรหัสตัวชี้ดั้งเดิมและเก็บรุ่นของอาร์เรย์)

บางทีสิ่งที่ชอบ

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

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

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

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

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

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

ตอนนี้ฉันกำลังใช้อาร์เรย์ ขอบคุณสำหรับคำแนะนำ
abdullah kahraman

3

โดยทั่วไปฉันจะเห็นด้วยกับดาวิด ดูเหมือนว่ามันจะทำงานได้

ฉันไม่รู้ว่ามันทำงานอย่างไร แต่ดีบักก็ทำงานได้อย่างสมบูรณ์

ฉันคาดเดาจากนี้คุณหมายความว่ามันทำงานได้อย่างสมบูรณ์ในการจำลอง

1) ตรวจสอบว่างานของคุณทำงานด้วยตนเองในสภาพแวดล้อมที่ไม่ใช่ RTOS ในชิปจริง

2) ทำการดีบักในวงจร ผ่านโปรแกรมบนชิปจริงและดูตัวแปรที่เกี่ยวข้องทั้งหมดเพื่อให้แน่ใจว่าทุกอย่างเป็นไปตามแผนที่วางไว้


ใช่ฉันหมายถึงเครื่องมือดีบั๊กนั่นคือโปรแกรมจำลองของ MPLABX งานทำงานด้วยตนเองในสภาพแวดล้อมที่ไม่ใช่ RTOS ฉันไม่มี ICD ฉันมี mikroElektronika easyPIC5 กับ ICD เท่านั้น แต่ใช้ได้กับคอมไพเลอร์ mikroC เท่านั้น ตอนนี้การเปลี่ยนคอมไพเลอร์จะไม่ให้ฉันพบปัญหาหรือจะเป็นอย่างไร
abdullah kahraman

1

ฉันดูโค้ดของคุณสั้น ๆ แต่ก็ไม่สมเหตุสมผล ในหลาย ๆ สถานที่ที่คุณเขียนถึง PCL จากนั้นคาดว่าจะเป็นการเพิ่มคำแนะนำอื่น ๆ

อย่างที่ฉันได้กล่าวไปก่อนหน้านี้ C ไม่เหมาะสมสำหรับการลงทะเบียนฮาร์ดแวร์ระดับต่ำแบบนี้ คุณต้องใช้ชุดประกอบสำหรับสิ่งนี้จริงๆ การพยายามหาสาเหตุที่รหัส C ไม่ทำงานเป็นเพียงการเสียเวลาเปล่า ๆ


ฉันไม่สามารถรวมการชุมนุมกับซีได้ฉันต้องทำงานมากมาย ทั้ง dis-assembly และรหัส 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

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! ฉันขอขอบคุณพวกคุณด้วยความยินดี
abdullah kahraman

1
คุณต้องการเรียกคืนสถานะจริงหรือไม่ ถ้าเป็นเช่นนั้นคุณจะต้องใช้คำสั่ง "swapf" ด้วยเหตุผลที่บันทึกไว้ที่อื่น: " P. Anderson ", " คู่มือสำหรับครอบครัวระดับกลางของ Microchip: ส่วนที่ 8.5 การบันทึกบริบท ", "การบันทึกบริบทW และสถานะ "
davidcary

0

ด้านล่างเป็นวิธีการใช้งานนี้โดยใช้ชุดประกอบ เข้ารหัสเดียวกันกับการจัดรูปแบบ(เชื่อมโยงไปยัง Pastebin) จะปรับปรุงได้อย่างไร? นี่เป็นโปรแกรมแรกของฉันในการประกอบ PIC ความคิดเห็นใด ๆ ที่ชื่นชม

list p=16f616
#include p16f616.inc

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

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

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

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


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

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

        CALL    task1               ; Call task#1, for initialazation.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        END                         ; END of the program.

โปรแกรมแรกของคุณในการชุมนุมคือ RTOS แบบมัลติทาสกิ้ง? ว้าว. คนส่วนใหญ่ทำดีมาก ๆ ถ้าพวกเขาได้รับไฟ LED กะพริบ :-)
davidcary

ดีจริงนี้เป็นโปรแกรมการชุมนุมครั้งแรกของฉันในสถาปัตยกรรม PIC ก่อนหน้านั้นในมหาวิทยาลัยฉันได้เรียน 8086 วิชา แต่พวกเขาไม่ได้ฝึกฝนและฉันต้องเรียนรู้ด้วยตัวเองเพราะอาจารย์เป็นตัวแทนและไม่รู้อะไรเลย แต่ยังถามคำถามยาก ๆ ในการสอบ ..
abdullah kahraman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.