Estou tentando criar um RTOS semi-preventivo (cooperativo) para microcontroladores PIC x16. Na minha pergunta anterior , aprendi que acessar o ponteiro da pilha de hardware não é possível nesses núcleos. Eu olhei para esta página no PIClist, e é isso que estou tentando implementar usando C.
Meu compilador é o Microchip XC8 e atualmente estou trabalhando em um PIC16F616 com oscilador RC interno de 4MHz selecionado nos bits de configuração.
Aprendi que posso acessar os registros PCLATH e PCL com C, olhando o arquivo de cabeçalho do meu compilador. Então, tentei implementar um alternador de tarefas simples.
Funciona como desejado no depurador se eu pausar o depurador após reiniciar, redefinir e definir o PC no cursor quando o cursor não estiver na primeira linha ( TRISA=0;
), mas em outra linha (por exemplo ANSEL=0;
). No primeiro início do depurador, recebo essas mensagens no Debugger Console
:
Launching
Programming target
User program running
No source code lines were found at current PC 0x204
Edit: Eu não sei o que fez funcionar, mas o depurador agora funciona perfeitamente. Portanto, omita a saída e o parágrafo acima.
Edit: Alterar a definição principal como esta faz o código abaixo funcionar. Isso inicia a função principal no endereço do programa 0x0099
. Não sei o que causa isso. Esta não é uma solução real. Agora, estou supondo que haja um erro específico do compilador.
void main(void) @ 0x0099
{
Aqui está o meu código 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
}
}
E aqui está o arquivo de listagem de desmontagem que meu compilador criou. Começa às line 74
.
Programei o chip atual e nenhuma alteração no PORTA; isso não funciona.
Por que meu programa não funciona?
Pesquisei a lista de montagem que você forneceu e nada salta como obviamente quebrado.
Se eu fosse você, meus próximos passos seriam:
(1) Eu escolheria outro método para piscar os LEDs. O notório "problema de leitura-modificação-gravação" pode (ou não) ser acionado pelo "XORWF PORTA, F" na lista de montagem.
Talvez algo como:
(Se você realmente deseja obter explicações detalhadas sobre por que o "XORWF PORTA, F" geralmente causa problemas, consulte " O que faz com que LIGAR um único pino de saída no Microchip PIC16F690 desligue espontaneamente outro pino na mesma porta? "; " O que acontece quando os dados são gravados em LATCH? ";" O problema de leitura, modificação e gravação ";" Ler antes de gravar ")
(2) Eu passo um passo pelo código, certificando-me de que as variáveis estejam sendo definidas com os valores esperados e na sequência esperada. Não tenho certeza se existe um depurador de hardware de etapa única para o PIC16F616, mas existem muitos excelentes simuladores de microcontroladores PIC , como o PICsim, que podem simular os chips da série PIC16.
O código de etapa única (em um simulador ou com um depurador de etapa única) é uma boa maneira de entender os detalhes do que realmente está acontecendo, confirmar se as coisas estão acontecendo do jeito que você pretendia e permitir que você veja as coisas que estão acontecendo. praticamente impossível de ver ao executar o programa em velocidade máxima.
(3) Se ainda estou perplexo, tentaria traduzir o código para usar matrizes em vez de ponteiros. Algumas pessoas acham o uso de ponteiros um pouco complicado e difícil de depurar. Costumo descobrir que, no processo de traduzir código de ponteiro complicado em código orientado a matriz, descubro qual é o erro. Mesmo se eu voltar ao código original do ponteiro e jogar fora a versão do array, o exercício é útil porque me ajudou a encontrar e corrigir o bug. (Às vezes, o compilador pode gerar código mais curto e mais rápido a partir do código orientado a array, então há momentos em que ligo o código do ponteiro original e mantenho a versão do array).
Talvez algo como
fonte
Eu basicamente concordaria com davidcary. Parece que poderia funcionar.
Acho que com isso você quer dizer que ele funciona perfeitamente no simulador .
1) Verifique se suas tarefas funcionam por conta própria, em um ambiente não RTOS no chip real.
2) Faça a depuração no circuito. Percorra o programa no chip real e observe todas as variáveis relevantes para garantir que tudo esteja indo conforme o planejado.
fonte
Eu olhei apenas seu código brevemente, mas não faz sentido. Em vários lugares, você está escrevendo para o PCL e esperando que ele execute outras instruções a seguir.
Como eu também disse antes, C é inadequado para esse tipo de acesso de baixo nível de registros de hardware fundamentais. Você realmente precisa usar a montagem para isso. Tentar descobrir por que o código C não está funcionando é apenas uma perda de tempo inútil.
fonte
Abaixo está a maneira de fazê-lo com montagem em linha usando o compilador XC8, e ele funciona agora! No entanto, preciso adicionar desenvolver mais código para salvar e restaurar o
STATUS
registro, o que parece um pouco mais complicado do que para um registro normal.Editar: o código foi alterado. Consulte as versões mais antigas desta postagem para obter o código anterior.
E aqui está o arquivo de cabeçalho
RTOS.h
:fonte
Abaixo está como implementar isso usando assembly. Acesse o mesmo código com formatação (links para Pastebin) . Como pode ser melhorado? Este é o meu primeiro programa na montagem PIC, qualquer comentário é apreciado.
fonte