PIC16 멀티 태스킹 RTOS 커널이 작동하지 않는 이유는 무엇입니까?


11

PIC x16 마이크로 컨트롤러를위한 반 선점 (협동) RTOS를 만들려고합니다. 내에서 이전의 질문에 나는 하드웨어 스택 포인터를 액세스하는 이러한 코어에 불가능하다는 것을 배웠다. 나는 살펴 보았다 PIClist 페이지, 이것은 내가 C를 사용하여 구현하기 위해 노력하고 무엇인가

내 컴파일러는 Microchip XC8이며 현재 구성 비트에서 4MHz 내부 RC 발진기가 선택된 PIC16F616을 작업 중입니다.

컴파일러의 헤더 파일을 보면서 C로 PCLATH 및 PCL 레지스터에 액세스 할 수 있다는 것을 배웠습니다. 그래서 간단한 작업 전환기를 구현하려고했습니다.

커서가 첫 번째 줄 ( TRISA=0;)이 아닌 다른 줄 (예 :)에있을 때 커서를 다시 시작하고 재설정하고 커서로 PC를 설정 한 후 디버거를 일시 중지하면 디버거에서 원하는대로 작동합니다 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와 반대) 작업 전환 (일반 12 또는 14 비트 PIC 코어에서 수행 할 수있는 유일한 작업)은 모든 수율 ( ) 작업의 명령문은 동일한 기능을 수행합니다. 호출 된 함수에있을 수 없으며 컴파일러가 함수 구조를 엉망으로 만들면 안됩니다 (최적화와 마찬가지로).

다음:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • PCL은 하위 비트이므로 PCLATH는 프로그램 카운터의 상위 비트라고 가정합니다. 그렇지 않다. PCL에 쓸 때 PCLATH 비트는 PC에 기록되지만 상위 PC 비트는 (암시 적으로) PCLATH에 기록되지 않습니다. 데이터 시트의 관련 섹션을 다시 읽으십시오.
  • PCLATH가 PC의 상위 비트 인 경우에도 이동 후 명령이 첫 번째 명령과 동일한 256 명령 '페이지'에 있지 않으면 문제가 발생할 수 있습니다.
  • _taskswitcher가 현재 PCLATH 페이지에 없으면 일반 goto가 작동하지 않습니다. LGOTO 또는 이와 동등한 제품이 필요합니다.

PCLATH 문제에 대한 해결책은 이동 후 레이블을 선언하고 해당 레이블의 하위 및 상위 비트를 pch 및 pcl 위치에 쓰는 것입니다. 그러나 인라인 어셈블리에서 '로컬'라벨을 선언 할 수 있는지 확실하지 않습니다. 평범한 MPASM으로 할 수 있습니다 (Olin은 웃을 것입니다).

마지막으로 이러한 종류의 컨텍스트 전환을 위해서는 컴파일러가 의존 할 수있는 모든 컨텍스트를 저장하고 복원해야합니다.

  • 간접 레지스터
  • 상태 플래그
  • 스크래치 메모리 위치
  • 컴파일러가 작업이 독립적이어야한다는 것을 인식하지 못하므로 메모리에서 겹칠 수있는 로컬 변수
  • 내가 지금 상상할 수없는 다른 것들이지만 컴파일러 작성자는 다음 버전의 컴파일러에서 사용할 수 있습니다 (매우 상상력이있는 경향이 있습니다)

PIC 아키텍처는 메모리 맵 전체에 많은 리소스가 배치되어 있기 때문에 더 많은 문제가 발생합니다. 더 전통적인 아키텍처는 레지스터 나 스택에 있습니다. 결과적으로 PIC 컴파일러는 종종 재진입 코드를 생성하지 않으므로 원하는 작업을 수행해야합니다 (Olin은 아마도 미소 짓고 모을 것입니다).

작업 스위처를 작성하는 즐거움을 위해이 작업을 수행하는 경우 ARM 또는 Cortex와 같이보다 전통적인 조직이있는 CPU에 집중하는 것이 좋습니다. 콘크리트 PIC 판에 발이 꽂혀 있으면 기존 PIC 스위처 (예 : salvo / pumkin?)를 연구하십시오.


좋은 정보 주셔서 감사합니다! 협동 작업 전환기를 만들겠다고 결심했습니다. XC8과 PIC는 이것에 대해 내 편이 아닙니다. 나는 알고 있습니다 :) 그렇습니다. 보시다시피, 이 질문에 대한 답변 중 하나 에서했던 것처럼 레이블을 만들 수 있습니다 .
abdullah kahraman

또한 운 좋게도 현재 작업중 인 PIC16F616의 프로그램 메모리 페이징이 없습니다.이 시점에서 큰 이점입니다.
abdullah kahraman

로컬 변수가 메모리에서 어떻게 겹치며 "스크래치 메모리 위치"에 대해 더 자세히 설명해 주시겠습니까?
abdullah kahraman

2K 코드 이하의 칩으로 자신을 제한하면 실제로 256 명령어 '페이지'가 아닌 lgoto를 잊을 수 있습니다. 스크래치 : 컴파일러는 '휘발성'이 아닌 한 메모리에서 수행하는 모든 작업이 그대로 유지된다고 가정 할 수 있습니다. 따라서 다른 기능에서 공유 할 수있는 일부 위치에 부분 계산을 배치 할 수 있습니다 . 참고 : main ()이 f ()와 g ()를 모두 호출하고 (다른 호출이없는 경우) f ()와 g ()의 지역 변수는 동일한 메모리 위치에 매핑 될 수 있습니다.
Wouter van Ooijen 2016 년

글쎄, 메모리에 임의의 장소가 있기 때문에 변수에 도달하고 저장하는 것이 거의 불가능한 것 같습니다.
abdullah kahraman 2016 년

7

귀하가 제공 한 어셈블리 목록을 살펴 보았으며 분명히 깨진 부분이 없습니다.

내가 당신이라면 다음 단계는 다음과 같습니다.

(1) LED를 깜박이는 다른 방법을 선택합니다. 악의적 인 "읽기-수정-쓰기 문제"는 어셈블리 목록의 "XORWF PORTA, F"에 의해 트리거 될 수 있습니다.

아마도 다음과 같습니다.

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

당신이 정말로 "XORWF PORTA는, F는"종종 문제가 발생하는 이유에 대한 자세한 설명을 보려면 (의 " 무엇 자발적으로 동일한 포트에서 다른 핀을 해제하려면 마이크로 칩의 PIC16F690의 단일 출력 핀을 ON의 원인은 무엇입니까? ", " 어떻게됩니까 " LATCH에 데이터를 쓸 때? "; " 읽기-수정-쓰기 문제 "; " 읽기 전에 읽기 "

(2) 코드를 한 단계 씩 수행하여 변수가 예상 값과 예상 순서로 설정되어 있는지 확인합니다. PIC16F616 용 단일 단계 하드웨어 디버거가 있는지 확실하지 않지만 PIC16 시리즈 칩을 시뮬레이션 할 수있는 PICsim 과 같은 우수한 PIC 마이크로 컨트롤러 시뮬레이터 가 많이 있습니다.

단일 스테핑 코드 (시뮬레이터 또는 단일 단계 하드웨어 디버거 포함)는 실제로 진행중인 작업의 세부 사항을 이해하고 원하는 방식으로 상황이 발생하고 있음을 확인하고 프로그램을 최고 속도로 실행할 때 실제로 볼 수 없습니다.

(3) 여전히 혼란 스러우면 포인터 대신 배열을 사용하도록 코드를 변환하려고합니다. 어떤 사람들은 포인터를 사용하는 것이 약간 까다 롭고 디버깅하기가 어렵다는 것을 알고 있습니다. 까다로운 포인터 코드를 배열 지향 코드로 변환 하는 과정 에서 종종 버그가 무엇인지 알게됩니다. 원래 포인터 코드로 돌아가서 배열 버전을 버려도 버그를 찾아 수정하는 데 도움이되므로 연습이 유용합니다. (때로는 컴파일러가 배열 지향 코드에서 더 짧고 빠른 코드를 생성 할 수 있으므로 원래 포인터 코드를 버리고 배열 버전을 유지하는 경우가 있습니다).

아마도 같은

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

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

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

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

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

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

지금 배열을 구현하고 있습니다. 추천 해 주셔서 감사합니다.
abdullah kahraman

3

나는 기본적으로 davidcary에 동의합니다. 작동 할 것 같습니다.

나는 그것이 무엇이 작동했는지 알지 못하지만 디버거는 이제 완벽하게 작동합니다.

나는 이것이 시뮬레이터 에서 완벽하게 작동한다는 것을 의미한다고 추측합니다 .

1) 실제 칩의 비 RTOS 환경에서 작업이 자체적으로 작동하는지 확인하십시오.

2) 회로 내 디버깅을 수행하십시오. 실제 칩에서 프로그램을 살펴보고 모든 관련 변수를 관찰하여 모든 것이 계획대로 진행되는지 확인하십시오.


예, 디버거, 즉 MPLABX의 시뮬레이터를 의미했습니다. 작업은 비 RTOS 환경에서 자체적으로 작동합니다. ICD가 없습니다. ICD가있는 mikroElektronika easyPIC5 만 있지만 mikroC 컴파일러에서만 작동합니다. 이제 컴파일러를 변경해도 문제를 찾을 수 없습니까?
abdullah kahraman

1

나는 당신의 코드를 간단히 보았지만 말이되지 않습니다. 여러 곳에서 PCL에 쓰고 다음에 나오는 다른 지시 사항을 표현할 것으로 기대합니다.

앞에서도 언급했듯이 C는 기본적인 하드웨어 레지스터에 대한 이러한 낮은 수준의 액세스에는 적합하지 않습니다. 이를 위해서는 어셈블리를 사용해야합니다. C 코드가 작동하지 않는 이유를 알아 내려고하는 것은 무의미한 시간 낭비입니다.


어셈블리와 C를 결합 할 수 없었습니다. 많은 작업을해야했습니다. 디스 어셈블리와 C 코드 모두 논리적으로 보입니다. PCL에 대한 쓰기를 따르는 명령을 실행할 것으로 예상되는 곳은 어디입니까? 어셈블리와 C에 대한 디버거를 보았으며 원하는대로 작동합니다.
abdullah kahraman

-1 죄송합니다. 나는 우연히 눌렀어야했고 지금 그것을 알아 차렸다.
abdullah kahraman

@abdullah : 현재 컴퓨터에서 소스 코드를 볼 수 없습니다. 브라우저에서 영구적으로 축소됩니다. PCLATH에 PCL을 할당 한 다음 PCL에 할당 한 것을 기억합니다. 어떤 경우에는 RETURN을 시도했다고 생각합니다. PCL에 쓰 자마자 실행은 PCLATH : PCL에 채워진 주소로 이동하므로 다음 지침은 관련이 없습니다. 컴파일러 관리 리소스를 엉망으로 만들고 컴파일러 가정을 무효화 할 수 있기 때문에 C에서는이 작업을 수행하는 것이 좋지 않습니다 . 이미 실제 어셈블리를 사용하십시오. 이 작업을 반복해야하는 데 지쳤습니다.
Olin Lathrop

1
코드를 보면 PCL이 다른 명령문 바로 전에 수정되지 않습니다. 수정 된 것으로 보이는 유일한 곳은 main ()의 맨 끝에 있습니다. 그러나 컴파일러가 리소스를 위해 싸우지 않도록 확신해야한다는 것이 좋은 지적입니다. 둘 다 잃을 것이다.
Rocketmagnet 2016 년

3
C는 이런 종류의 작업에 완벽하게 허용되며 실제로는 읽기 및 유지 관리가 더 쉬우므로 어셈블리 언어가 아닌 C와 같은 중간 수준의 언어로 작성하는 것이 좋습니다. 괜찮은 컴파일러는 평범한 사람이 작성하는 것에서 멀지 않은 코드를 생성합니다. 필자는 일반적으로 매우 기본적인 시작 코드, 컴파일러보다 빠르게 최적화 할 수있는 특정 영역 또는 빠른 인터럽트 또는 코드 크기 제약 조건에 따라 어셈블러를 작성합니다. 요즘에는 순결한 조립이 필요하지 않습니다.
akohlsmith 2016 년

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 2016 년

@stevenvh 아, 그런 일이 있었나요, 몰랐어요? 감사합니다 :)
abdullah kahraman 2016 년

작동하게 된 것을 축하합니다!
davidcary

감사합니다 @davidcary! 축하해 줘서 고마워
압둘라 카흐 라만

1
실제로 상태를 복원해야합니까? 그렇다면 "swapf"명령을 사용해야합니다. " P. Anderson ", " Microchip 중급 제품군 설명서 : 섹션 8.5 상황 저장 ", " PIC 저장 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 개의 수업을 들었지만 실용적이지 않았고 강사는 대체물이었고 아무 것도
몰랐지만
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.