Я пытаюсь создать полу-упреждающую (кооперативную) ОСРВ для микроконтроллеров PIC x16. В моем предыдущем вопросе я узнал, что доступ к указателю аппаратного стека невозможен в этих ядрах. Я посмотрел на эту страницу в PIClist, и это то, что я пытаюсь реализовать с помощью C.
Мой компилятор - Microchip XC8, и в настоящее время я работаю над PIC16F616 с внутренним RC-генератором 4 МГц, выбранным в битах конфигурации.
Я узнал, что могу получить доступ к регистрам PCLATH и PCL с помощью C, просматривая заголовочный файл моего компилятора. Итак, я попытался реализовать простой переключатель задач.
В отладчике он работает как требуется, если я приостанавливаю отладчик после перезапуска, сбрасываю и устанавливаю ПК на курсор, когда курсор находится не на первой строке ( TRISA=0;
), а на другой строке (например ANSEL=0;
). При первом запуске отладчика я получаю эти сообщения в Debugger Console
:
Launching
Programming target
User program running
No source code lines were found at current PC 0x204
Редактировать: я не знаю, что заставило его работать, но отладчик теперь работает отлично. Итак, пропустите приведенный выше вывод и абзац.
Изменить: Изменение основного определения, как это заставляет код ниже работать. Это запускает основную функцию по адресу программы 0x0099
. Я не знаю, что вызывает это. Это не настоящее решение. Теперь я предполагаю, что есть ошибка, специфичная для компилятора.
void main(void) @ 0x0099
{
Вот мой код C:
/*
* File: main.c
* Author: abdullah
*
* Created on 10 Haziran 2012 Pazar, 14:43
*/
#include <xc.h> // Include the header file needed by the compiler
__CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF & IOSCFS_4MHZ & BOREN_ON);
/*
* INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN
* WDT disabled and can be enabled by SWDTEN bit of the WDTCON register
* PWRT enabled
* MCLR pin function is digital input, MCLR internally tied to VDD
* Program memory code protection is disabled
* Internal Oscillator Frequency Select bit : 4MHz
* Brown-out Reset Selection bits : BOR enabled
*/
/*
* OS_initializeTask(); definition will copy the PCLATH register to the task's PCLATH holder, which is held in taskx.pch
* This will help us hold the PCLATH at the point we yield.
* After that, it will copy the (PCL register + 8) to current task's PCL holder which is held in taskx.pcl.
* 8 is added to PCL because this line plus the "return" takes 8 instructions.
* We will set the PCL after these instructions, because
* we want to be in the point after OS_initializeTask when we come back to this task.
* After all, the function returns without doing anything more. This will initialize the task's PCLATH and PCL.
*/
#define OS_initializeTask(); currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("return");
/*
* OS_yield(); definition will do the same stuff that OS_initializeTask(); definition do, however
* it will return to "taskswitcher" label, which is the start of OS_runTasks(); definition.
*/
#define OS_yield(); currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
/*
* OS_runTasks(); definition will set the "taskswitcher" label. After that it will change the
* current task to the next task, by pointing the next item in the linked list of "TCB"s.
* After that, it will change the PCLATH and PCL registers with the current task's. That will
* make the program continue the next task from the place it left last time.
*/
#define OS_runTasks(); asm("_taskswitcher");\
currentTask = currentTask -> next;\
PCLATH = currentTask->pch;\
PCL = currentTask->pcl;
typedef struct _TCB // Create task control block and type define it as "TCB"
{
unsigned char pch; // pch register will hold the PCLATH value of the task after the last yield.
unsigned char pcl; // pcl register will hold the PCL value of the task after the last yield.
struct _TCB* next; // This pointer points to the next task. We are creating a linked list.
} TCB;
TCB* currentTask; // This TCB pointer will point to the current task's TCB.
TCB task1; // Define the TCB for task1.
TCB task2; // Define the TCB for task2.
void fTask1(void); // Prototype the function for task1.
void fTask2(void); // Prototype the function for task2.
void main(void)
{
TRISA = 0; // Set all of the PORTA pins as outputs.
ANSEL = 0; // Set all of the analog input pins as digital i/o.
PORTA = 0; // Clear PORTA bits.
currentTask = &task1; // We will point the currentTask pointer to point the first task.
task1.next = &task2; // We will create a ringed linked list as follows:
task2.next = &task1; // task1 -> task2 -> task1 -> task2 ....
/*
* Before running the tasks, we should initialize the PCL and PCLATH registers for the tasks.
* In order to do this, we could have looked up the absolute address with a function pointer.
* However, it seems like this is not possible with this compiler (or all the x16 PICs?)
* What this compiler creates is a table of the addresses of the functions and a bunch of GOTOs.
* This will not let us get the absolute address of the function by doing something like:
* "currentTask->pcl=low(functionpointer);"
*/
fTask1(); // Run task1 so that we get the address of it and initialize pch and pcl registers.
currentTask = currentTask -> next; // Point the currentTask pointer to the next pointer which
fTask2(); // is task2. And run task2 so that we get the correct pch and pcl.
OS_runTasks(); // Task switcher. See the comments in the definitions above.
}
void fTask1(void)
{
OS_initializeTask(); // Initialize the task
while (1)
{
RA0 = ~RA0; // Toggle PORTA.0
OS_yield(); // Yield
RA0 = ~RA0; // Toggle PORTA.0
}
}
void fTask2(void)
{
OS_initializeTask(); // Initialize the task
while (1)
{
RA1 = ~RA1; // Toggle PORTA.1
OS_yield(); // Yield
RA1 = ~RA1; // Toggle PORTA.1
}
}
И вот файл распаковки, который создал мой компилятор. Начинается в line 74
.
Я запрограммировал реальный чип, и никаких изменений в PORTA вообще нет; это не работает
По какой причине моя программа не работает?
Я просмотрел список сборок, который вы предоставили, и ничего не выпрыгивает как явно сломанный.
Если бы я был тобой, мои следующие шаги были бы:
(1) Я бы выбрал другой способ мигания светодиодов. Пресловутая «проблема чтения-изменения-записи» может (или не может) быть вызвана «XORWF PORTA, F» в листе сборки.
Возможно что-то вроде:
(Если вы действительно хотите увидеть подробные объяснения того, почему «XORWF PORTA, F» часто вызывает проблемы, см. « Что вызывает включение одного выходного контакта на Microchip PIC16F690, чтобы самопроизвольно отключить другой контакт на том же порту? »; « Что происходит когда данные записываются в LATCH? ";" Проблема чтения-изменения-записи ";" Чтение перед записью ")
(2) Я бы пошагово прошел по коду, убедившись, что переменные установлены в ожидаемые значения и в ожидаемой последовательности. Я не уверен, существует ли одношаговый аппаратный отладчик для PIC16F616, но есть много отличных симуляторов микроконтроллеров PIC, таких как PICsim которые могут симулировать микросхемы серии PIC16.
Одноступенчатый код (в симуляторе или с помощью одношагового аппаратного отладчика) - это хороший способ понять детали того, что на самом деле происходит, подтвердить, что все происходит так, как вы хотели, и он позволяет вам видеть вещи, которые на самом деле практически невозможно увидеть при запуске программы на полной скорости.
(3) Если бы я все еще был в тупике, я бы попытался перевести код для использования массивов, а не указателей. Некоторые люди находят использование указателей немного сложным и трудным для отладки. Я часто нахожу, что в процессе преобразования хитрого кода указателя в ориентированный на массив код я выясняю, в чем заключается ошибка. Даже если я вернусь к исходному коду указателя и выброшу версию массива, это упражнение будет полезно, потому что оно помогло мне найти и исправить ошибку. (Иногда компилятор может генерировать более короткий и быстрый код из массива-ориентированного кода, поэтому иногда я выкидываю исходный код указателя и сохраняю версию массива).
Возможно что-то вроде
источник
Я бы в основном согласился с Дэвидкари. Похоже, это может сработать.
Полагаю, под этим вы подразумеваете, что он отлично работает в симуляторе .
1) Убедитесь, что ваши задачи работают самостоятельно, в среде, отличной от RTOS, в реальном чипе.
2) Выполнить внутрисхемную отладку. Пройдите программу на реальном чипе и посмотрите все соответствующие переменные, чтобы убедиться, что все идет по плану.
источник
Я только кратко посмотрел на ваш код, но это не имеет смысла. В некоторых местах вы пишете в PCL, а затем ожидаете, что он извинит другие инструкции после этого.
Как я уже говорил ранее, C не подходит для такого низкоуровневого доступа к основным аппаратным регистрам. Вам действительно нужно использовать сборку для этого. Попытка выяснить, почему код C не работает, является просто бессмысленной тратой времени.
источник
Ниже описан способ сделать это с помощью встроенной сборки с использованием компилятора XC8, и теперь он работает ! Однако мне нужно добавить больше кода для сохранения и восстановления
STATUS
реестра, что кажется немного сложнее, чем для обычного регистра.Изменить: код изменен. Пожалуйста, обратитесь к старым версиям этого поста для предыдущего кода.
А вот и заголовочный файл
RTOS.h
:источник
Ниже описано, как реализовать это с помощью сборки. Доступ к тому же коду с форматированием (ссылки на Pastebin) . как это может быть улучшено? Это моя первая программа в сборке PIC, любые комментарии приветствуются.
источник