PIC18F Kernel - 06

Post date: 08/04/2011 14:35:12

Depois de um bom tempo ocupado com outra atividades estarei falando sobre a primeira versão funcional do kernel no PIC18F4550.

Falta ainda padronizar a parte de drivers e chamadas às API's. Comecei a trabalhar bastante nisso e já estou com alguns resultados interessantes, mas isso é para outro post! Vamos então ao que interessa.

Como citei no artigo anterior houve algumas mudanças, principalmente quanto ao modo de reconhecer qual deve ser a próxima função a ser executada. Uma vantagem que esse sistema trouxe e que só percebi agora é que eu posso continuar decrementando o tempo mesmo que ele se torne negativo.

Se quiser um igual é só comprar aqui no ThinkGeek

Como assim? Pode uma função ter um tempo negativo? Poder pode, mas faz sentido?

Nesse caso sim ele indica há quando tempo aquela determinada função deveria ter sido executada mas ainda não foi. Ou seja, para nosso kernel qualquer função com tempo menor ou igual a zero deve ser executada imediatamente.

Essa aboradagem traz mais algumas vantagens: Eu consigo de maneira fácil (inspecionando os tempos) verificar se existe alguma função que está com problemas para ser executada e qual o atraso médio na execução. Isso permite ao desenvolvedor perceber quando a CPU passa a não dar conta de executar tudo com a velocidade projetada. Outra grande vantagem, que eu devo implementar nas próximas versões, é permitir um modo simples de aumentar a prioridade da função.

Agora o projeto está todo voltado para trabalhar com o PIC, desse modo temos que realizar algumas mudanças. A primeira delas é do compilador. Estou trabalhando com o SDCC (versão 2.9.0) como compilador open source (leia-se gratuito) baseado no GCC. Ele, por sua vez, faz uso do GPUtils (versão 0.13.7), uma suíte de linker, assembler e headers para as famílias PIC16 e PIC18 da Microchip.

Deste modo apresentamos algumas "bibliotecas", uma para trabalhar com as interrupções(int.c e int.h), uma para operar com o timer (timer.c e timer.h), uma para configurar os fusíveis (config.h) e uma para funções básicas e ponteiros para registradores (basico.h). Segue abaixo apenas as funções que esses arquivos disponibilizam

//CONFIG.H //configurações do microcontrolador code char at 0x300000 CONFIG1L = 0x01; // No prescaler used code char at 0x300001 CONFIG1H = 0x0C; // HS: High Speed Cristal code char at 0x300003 CONFIG2H = 0x00; // Disabled-Controlled by SWDTEN bit code char at 0x300006 CONFIG4L = 0x00; // Disabled low voltage programming //INT.H void InicializaInterrupt(void);

//TIMER.H char FimTimer(void);

void AguardaTimer(void);

void ResetaTimer(unsigned int tempo);

void InicializaTimer(void);

//BASICO.H (apenas alguns trechos #define FIM_OK 0 #define MIN_INT -30000 //funções de bit #define BitFlp(arg,bit) ((arg) ^= (1<<bit))

//defines para registros especiais #define PORTD (*(volatile __near unsigned char*)0xF83)

#define TRISC (*(volatile __near unsigned char*)0xF94)

Vamos agora ao que interessa.

Para o correto funcionamento do kernel é necessário que algumas operações sejam executadas com tempo definido, numa base de tempo fixa. Todas essas operações foram reunidas numa única função:

void KernelClock(void){

unsigned char i;

i = ini;

while(i!=fim){

if((vetProc[i].start)>(MIN_INT)){

vetProc[i].start--;

}

i = (i+1)%SLOT_SIZE;

}

}

Será nesta função que posteriormente faremos a verificação de tempo negativo e faremos o "upgrade" da prioridade de cada função para evitar que ela morra de fome. Cada arquitetura (e compilador) possui um modo diferente de alocar uma função como responsável pela interrupção. No SDCC essa tarefa é bem simples, basta adicionar a palavra interrupt X, onde X é a interrupção desejada: 1 para as de alta prioridade e 2 para as de baixa prioridade.

//Interrupção void isr1(void) interrupt 1 { ResetaTimer(1000); //reseta com 1ms KernelClock(); }

A função ResetaTimer() faz os cálculos para o registro do timer e rearma a interrupção, enquanto chama a função de clock do kernel.

Já o kernel permanece praticamente inalterado, com a excessão de permitir que o tempo possa ser negativo. Novamente é importante notar que o SDCC não permite a atribuição de ponteiros para structs por isso o código nojento pra trocar os processos.

void ExecutaKernel(void){

unsigned char j;

unsigned char prox;

processo tempProc;

for(;;){

if (ini != fim){

//Procura a próxima função a ser executada com base no tempo j = (ini+1)%SLOT_SIZE;

prox = ini;

while(j!=fim){

if (vetProc[j].start < vetProc[prox].start){

prox = j;

}

j = (j+1)%SLOT_SIZE;//para poder incrementar e ciclar o j }

//troca e coloca o processo com menor tempo como o proxima //**Código nojento tempProc.Func = vetProc[prox].Func; tempProc.start = vetProc[prox].start; tempProc.t_ms = vetProc[prox].t_ms; vetProc[prox].Func = vetProc[ini].Func; vetProc[prox].start = vetProc[ini].start; vetProc[prox].t_ms = vetProc[ini].t_ms; vetProc[ini].Func = tempProc.Func; vetProc[ini].start = tempProc.start; vetProc[ini].t_ms = tempProc.t_ms; while(vetProc[ini].start>0){

//adicionar sleep aqui, acorda na int. } //retorna se precisa repetir novamente ou não if ( (*(vetProc[ini].Func))() == REPETIR ){

AddProc(&(vetProc[ini]));

}

//próxima função ini = (ini+1)%SLOT_SIZE;

}

}

}

Pronto! Segue em anexo também o main.c, com apenas 4 processos que fazem 4 leds ligados à porta D ficarem piscando com tempos diferentes.

Os próximos posts sobre o kernel devem demorar um pouco mais, em compensação no próximo vou apresentar a nova IDE da microchip e como configurar o SDCC.

Até mais!