Próbuję stworzyć pół-wyprzedzający (współpracujący) RTOS dla mikrokontrolerów PIC x16. W poprzednim pytaniu dowiedziałem się, że dostęp do wskaźnika stosu sprzętu nie jest możliwy w tych rdzeniach. Przejrzałem tę stronę na liście PIClist i właśnie to próbuję wdrożyć za pomocą C.
Mój kompilator to Microchip XC8 i obecnie pracuję na PIC16F616 z wewnętrznym oscylatorem RC 4MHz wybranym w bitach konfiguracyjnych.
Nauczyłem się, że mogę uzyskać dostęp do rejestrów PCLATH i PCL za pomocą C, patrząc na plik nagłówka mojego kompilatora. Próbowałem więc zaimplementować prosty przełącznik zadań.
Działa tak, jak chciałem w debuggerze, jeśli zatrzymam debuger po ponownym uruchomieniu, zresetowaniu i ustawieniu komputera na kursor, gdy kursor nie znajduje się w pierwszej linii ( TRISA=0;
), ale w innej linii (na przykład ANSEL=0;
). Przy pierwszym uruchomieniu debugera otrzymuję te wiadomości w Debugger Console
:
Launching
Programming target
User program running
No source code lines were found at current PC 0x204
Edycja: Nie wiem, co sprawiło, że to działało, ale debugger działa teraz idealnie. Pomiń powyższe wyjście i akapit.
Edycja: Zmiana głównej definicji w ten sposób powoduje, że poniższy kod działa. Uruchamia to główną funkcję pod adresem programu 0x0099
. Nie wiem co to powoduje. To nie jest prawdziwe rozwiązanie. Zgaduję, że wystąpił błąd specyficzny dla kompilatora.
void main(void) @ 0x0099
{
Oto mój kod 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
}
}
A oto plik z listą dezasemblacji, który utworzył mój kompilator. Zaczyna się o line 74
.
Zaprogramowałem rzeczywisty układ i wcale nie zmieniłem PORTA; to nie działa.
Z jakiego powodu mój program nie działa?
Przejrzałem listę zgromadzeń, którą dostarczyłeś, i nic nie wyskakuje jak najwyraźniej zepsute.
Gdybym był tobą, moje następne kroki to:
(1) Wybrałbym inną metodę mrugania diod LED. Notoryczny „problem odczytu / modyfikacji / zapisu” może (ale nie musi) być wywołany przez „XORWF PORTA, F” na liście zespołów.
Być może coś takiego:
(Jeśli naprawdę chcesz zobaczyć szczegółowe wyjaśnienia, dlaczego „XORWF PORTA, F” często powoduje problemy, zobacz „ Co powoduje włączenie pojedynczego styku wyjściowego w Microchip PIC16F690, aby spontanicznie wyłączyć kolejny styk na tym samym porcie? ”; „ Co się stanie kiedy dane są zapisywane w LATCH? ”;„ Problem odczytu-modyfikacji / zapisu ”;„ Przeczytaj przed zapisem ”)
(2) Przejrzałbym kod krok po kroku, upewniając się, że zmienne są ustawione na oczekiwane wartości oraz w oczekiwanej kolejności. Nie jestem pewien, czy istnieje jednoetapowy debugger sprzętowy dla PIC16F616, ale istnieje wiele doskonałych symulatorów mikrokontrolerów PIC, takich jak PICsim, które mogą symulować układy serii PIC16 .
Kod jednostopniowy (w symulatorze lub z jednoetapowym debugerem sprzętowym) to dobry sposób na zrozumienie szczegółów tego, co się naprawdę dzieje, potwierdzenie, że wszystko dzieje się zgodnie z zamierzeniem, i pozwala zobaczyć rzeczy, które są praktycznie niemożliwe do zobaczenia podczas uruchamiania programu z pełną prędkością.
(3) Jeśli nadal mam problemy, spróbuję przetłumaczyć kod, aby używał tablic zamiast wskaźników. Niektórzy uważają, że używanie wskaźników jest nieco trudne i trudne do debugowania. I często, że w procesie tłumaczenia trudne kod wskaźnika do tablicy zorientowanych kodu, dowiem się, na czym polega błąd. Nawet jeśli w końcu powrócę do pierwotnego kodu wskaźnika i wyrzucę wersję tablicy, ćwiczenie jest przydatne, ponieważ pomogło mi znaleźć i naprawić błąd. (Czasami kompilator może generować krótszy, szybszy kod z kodu zorientowanego na tablicę, więc czasami wyrzucam oryginalny kod wskaźnika i zachowuję wersję tablicy).
Może coś takiego
źródło
Zasadniczo zgodziłbym się z Davidcary. Wygląda na to, że może działać.
Zgaduję, że masz na myśli, że działa idealnie w symulatorze .
1) Sprawdź, czy Twoje zadania działają samodzielnie, w środowisku innym niż RTOS w prawdziwym układzie scalonym.
2) Wykonaj debugowanie w obwodzie. Przejdź przez program na prawdziwym chipie i obserwuj wszystkie odpowiednie zmienne, aby mieć pewność, że wszystko idzie zgodnie z planem.
źródło
Patrzyłem tylko na twój kod krótko, ale to nie ma sensu. W kilku miejscach piszesz do PCL, a następnie spodziewasz się, że wyda inne instrukcje.
Jak już powiedziałem wcześniej, C jest nieodpowiedni do tego rodzaju dostępu do podstawowych rejestrów sprzętowych na niskim poziomie. Naprawdę musisz do tego użyć zestawu. Próbowanie wyjaśnienia, dlaczego kod C nie działa, jest po prostu bezcelową stratą czasu.
źródło
Poniżej znajduje się sposób, aby to zrobić z wbudowanym zestawem za pomocą kompilatora XC8 i działa teraz! Muszę jednak dodać więcej kodu do zapisywania i przywracania
STATUS
rejestru, co wydaje się nieco trudniejsze niż w przypadku zwykłego rejestru.Edycja: Kod został zmieniony. Poprzedni kodzawiera starsze wersje tego postu.
A oto plik nagłówka
RTOS.h
:źródło
Poniżej znajduje się sposób zaimplementowania tego za pomocą zestawu. Uzyskaj dostęp do tego samego kodu za pomocą formatowania (linki do Pastebin) . Jak można to poprawić? To mój pierwszy program w montażu PIC, każdy komentarz jest mile widziany.
źródło