Ir ao conteúdo
  • Cadastre-se
Otavio Pessini Bini

PIC interrupção por mudança de estado - pic16f877a

Recommended Posts

Boa noite a todos.

 

Há uns dias atrás, postei esse post em outro lugar, fiz diversos testes, e infelizmente ainda não consegui chegar no resultado que preciso.

 

Vamos lá:

 

Estou meio que desesperado com meu projeto, e já quebrei a cabeça inúmeras vezes para chegar num código final, porém sem exito.

 

Estou desenvolvendo uma solenoide, que precisa ser atuada através de uma mudança de estado de uma porta RB do PIC16F877A.

 

Ou seja, se a porta de ENTRADA em questão (RB5) passar de alimentada para não alimentada, e vice versa (de não alimentada para alimentada), a porta RC4 de saída precisa ser acionada, por 2 segundos, e ser desligada em seguinte.

 

Parece ser algo simples diante do mundo de programação, mas mesmo asssim estou quebrando a cabeça de uma forma pela falta de entendimento na área.

 

Se for de ajuda, segue o código feito no mikroC:

void main()
{
       CMCON        =   0x07;   // Desabilita Comparadores;
       RBIE_bit     =   0x01;   // Habilita a interrupção por mudança do PORTB <7:4>
       RBIF_bit     =   0x00;   // Força a flag de sinalização a zero
       GIE_bit      =   0x01;   // Habilita a interrupção global
       
       TRISB        =   0xFF;   // PORTB configurado como entrada digital
       TRISC        =   0xEF;   // Apenas usaremos RC4 como saída digital
       
       RC4_bit      =   0x00;   // Inicia a saída em nível baixo

}

void interrupt ()
{
   if(RBIF_bit){                                 // Caso haja mudança de 1 para 0, ou de 0 para 1 (especificamente na porta RB5 de entrada)

       RC4_bit      =   0x01;               // Saida a ser acionada
       delay_ms(2000);
       RC4_bit      =   0x00;               // Saida a ser desligada

}
}

 

>

 

Preciso que esse ciclo de acionamento, se repita infinitamente...

 

 

Conto com a ajuda urgentemente de vocês, e agradeço desde ja.

 

OBS: Deixo em anexo, o projeto esquematizado no Proteus...

 

Att, Otavio.

SOLENOIDE_PIC_16F877A.rar

Compartilhar este post


Link para o post
Compartilhar em outros sites

Amigo, o que eu sempre digo, não depende exclusivamente da programação (sw). Depende também de por a mão na massa (hw). Rapadura é doce mas num é mole não.  Depois deste desabafo...

 

Experimente setar o bit PEIE

Página 6, 8.2.2:

"The PEIE bit must be set to enable any of these peripheral interrupts"

Também acho interessante desabilitar a interrupt durante a própria pra que algum outro evento faça retornar nela.

void interrupt () 
{
if(RBIF_bit)
{     RBIE_bit=0;

E depois precisa de reabilitação (=eu kk)

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz  Bom dia pra nós,

 

Não entendi muito bem o que você tentou testar, mas o RBIE já não está setado na configuração que eu passei acima? :

 

 void main() {        

CMCON        =   0x07;   // Desabilita Comparadores;        

RBIE_bit     =   0x01;   // Habilita a interrupção por mudança do PORTB <7:4>        

RBIF_bit     =   0x00;   // Força a flag de sinalização a zero

 

Abraço, e aguardo uma força!! haha

Compartilhar este post


Link para o post
Compartilhar em outros sites

Paciência é meu sobrenome. Tentemos...

Aquilo que você fez no main() é feito na inicialização...ok. Agora tipo assim... Quando interrompe (na interrupt!*!) digamos logo que entrou dentro do seu delay(2000) algo do além muda o estado do port. Ele vai tentar voltar a ser interrompido e recomeçar o delay(2000) ou acontecer algo muito lôco como o colapso do universo. Por isso que recomendo inibir a interrupt no começo dela própria. RBIE_bit=0x00 e antes de sair dela, reabilite RBIE_bit=0x01. E vou mais além: desligue o bit RBIF. RBIF_bit=0; pois não sei se ele desliga sozinho. Se não entendeu, você não está sozinho kk.

 

Agora a questão inicial do post, o lance de habilitar o PEIE (outro bit de controle), diga que você entendeu por favor. E o principal, diga que deu certo.

  • Haha 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz  Pois bem,

 

Se bem entendi, ficaria algo do tipo certo ?! :

 

 

void interrupt ()
{
while(1){
       if(RBIF_bit)
{      
       RBIE_bit     =   0x00;
       RBIF_bit     =   0x00;
       RC4_bit      =   0x01;
       delay_ms(5000);
       RC4_bit      =   0x00;
       RBIE_bit     =   0x01;
       RBIF_bit     =   0x00;
 }
}
}
void main()
{
       CMCON        =   0x07;   // Desabilita Comparadores;
       RBIE_bit     =   0x01;   // Habilita a interrupção por mudança do PORTB <7:4>
       RBIF_bit     =   0x00;
       PEIE_bit     =   0x01;   // Desabilita Periféricos
       GIE_bit      =   0x01;   // Habilita a interrupção global
       
       TRISB        =   0xFF;   // PORTB configurado como entrada digital
       TRISC        =   0xEF;   // Apenas usaremos RC4 como saída digital
       
       RC4_bit      =   0x00;   // Inicia a saída em nível baixo

}

 

Caso positivo, não deu certo. Quando aciono de 0 para 1 na porta de entrada (RB5), a porta RC4 vai para 1, porém fica eternamente, sem mudança mesmo ao voltar para 0 a porta de entrada....

 

 

Mais alguma coisa que podemos tentar?!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Claro amigo. Uma troca de fralda a+ não me faz diferença kk brinc claro.. você fez mais uma cagadinha:

42 minutos atrás, Otavio Pessini Bini disse:

while(1){
       if(RBIF_bit)

este while(1) daí tá sobrando. E tem mais cocozinho:

-Seu main() tá ôco: não faz nada além de setar alguns registros.

-rotina delay() dentro da interrupt não é muito interessante

 

Tô quase fazendo tudo pra você. Vai tentando kk. Mas é melhor ir dormir um pouco. Já tá acordo há muito tempo.

  • Haha 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz Fala amiga, seu conselho foi uma ordem: descansei.. hahaha

 

To tentando, mas ainda não entendo algumas coisas que você diz... rs, mas pode ter certeza que com esse seu "fazer", estou aprendendo e absorvendo..

 

Vamos lá, o while não e faz necessário em nenhum momento?! Mas não precisa repetir sempre a condição IF para verificar a porta de entrada?

 

Outra coisa, como assim "main oco"? hahaha

 

E delay não é sempre positivo, principalmente para melhor processamento!? nesse caso é necessário porque estou fazendo uma solenoide, em que precisa apenas ter um pico de atuação (talvez até menos que 2 segundos)....

 

No mais, agradeço, você é demais! 

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Otavio Pessini Bini ,

 

Aprenda isto : nunca se utiliza delay dentro de rotina de interrupção!

 

Em uma situação normal, quando estamos dentro de uma rotina de interrupção não pode acontecer outra interrupção , enquanto não terminar essa rotina de interrupção.

 

Claro que existem técnicas que permitam que outras interrupções possam ocorrer mesmo dentro da rotina de interrupção, mas para isso é necessário um bom entendimento do microcontrolador, do que queremos controlar, e principalmente do que que pode acontecer em qualquer etapa do programa, ou seja, é algo avançado demais para o seu atual estágio.

 

Paulo

Compartilhar este post


Link para o post
Compartilhar em outros sites
14 horas atrás, Otavio Pessini Bini disse:

o while não e faz necessário em nenhum momento

é sim mas não dentro lá da interrupt. O que você fez foi while(1) {buraco negro} o que pode ser traduzido toscamente pra "enquanto(eu respirar) {você está preso}". Ou seja você está preso pra sempre dentro das chaves {}.você só sai de lá quando eu morrer (toc toc toc na madeira!) ou melhor: resetar o sistema.   Além do + nem mesmo outra interrupt te livrará de lá pois você as desligou.

 

14 horas atrás, Otavio Pessini Bini disse:

não precisa repetir sempre a condição IF para verificar a porta de entrada

Aquele bit não é da porta diretamente. Ele mostra que "alguém bateu na porta" toc toc toc na porta de madeira de novo pra garantir!). Mas sim é uma maneira de ler portas. Porta pode ser considerada uma variável qualquer na ram...

if (PORTB==0xaa) ...

 

14 horas atrás, Otavio Pessini Bini disse:

como assim "main oco"

void main()
{
       CMCON        =   0x07;   // Desabilita Comparadores;
       RBIE_bit     =   0x01;   // Habilita a interrupção por mudança do PORTB <7:4>
       RBIF_bit     =   0x00;
       PEIE_bit     =   0x01;   // HABILITA... Desabilita Periféricos
       GIE_bit      =   0x01;   // Habilita a interrupção global
       
       TRISB        =   0xFF;   // PORTB configurado como entrada digital
       TRISC        =   0xEF;   // Apenas usaremos RC4 como saída digital
       RC4_bit      =   0x00;   // Inicia a saída em nível baixo

for(;;) //ou while(1) //eternamente... é ter na mente... éter na mente
{
  //aqui seu programa
}

}

Dentro do main() é que tem que ter o while(1)  - eu prefiro for( ; ; ) mas é a lesma lerda. Dentro dele é  que seria seu núcleo. Sem isso fica oco.... é o que tenho na cabeça agora pra te passar
 

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom dia a todos,

 

Realmente, faço várias tentativas de diferentes formas mas definitivamente NÃO SAIO DO LUGAR.

 

O mais próximo que consigo chegar, é habilitar a saída RC4 apenas em caso da porta de entrada estar em 0 ou 1, porém não monitorando a sua mudança.

 

Galera, por favor, me deem uma luz.. Sei que sou bem leigo, e vocês estão abrindo bastante meus horizontes, mas não estou conseguindo.

 

 

Grato mais uma vez!

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz  Bom dia!!! Pelo que eu compreendi, e através das minhas tentativas, não conseguiria fazer o que desejo (monitorar a alteração na Porta RB5) com esse interrupt correto ?

 

Consegue me aconselhar que caminho devo seguir?

 

Estou desesperado... Grato!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá ... Consegue sim! Yes you can!

Dei um clique no google pic portb interrupt example e vi este detalhe curioso neste sítio e neste pdf

void interrupt(void){
    if (INTCON.RBIF == 1)
       {
        PORTD = ~PORTD;         // invert PORTD
        temp = PORTB;           //Read PORTB to clear mismatch
        INTCON.RBIF = 0;        // Clear interrupt flag
       }//end intcon.RBIF if
 }//end ISR

Ou seja você deve ler o portb pra corrigir alguma cagadinha da microchip. Não esqueça de habilitar o pullup ou colocar por fora senão a porta fica aberta e o trem endoida.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz  Boa tarde!! Ainda bem que não me abandonaste.. rs

 

Continuando meu desespero tentei o seguinte código:

 

void interrupt (void)
{
while(1){
       if(INTCON.RBIF == 1)
{
       RBIE_bit     =   0x00;
       RBIF_bit     =   0x00;
       RC4_bit      =   0x01;
       temp         =   RB5;
       INTCON.RBIF  =   0x00;
 }
}
}
void main()
{
       CMCON        =   0x07;   // Desabilita Comparadores;
       RBIE_bit     =   0x01;   // Habilita a interrupção por mudança do PORTB <7:4>
       RBIF_bit     =   0x00;
       PEIE_bit     =   0x01;   // Desabilita Periféricos
       GIE_bit      =   0x01;   // Habilita a interrupção global

       TRISB        =   0xFF;   // PORTB configurado como entrada digital
       TRISC        =   0xEF;   // Apenas usaremos RC4 como saída digital

       RC4_bit      =   0x00;   // Inicia a saída em nível baixo

}

 

 

Porém agora o que acontece é o seguinte:

 

1. expressão temp dá erro;

2. Apenas quando a RB5 (entrada) está em 1 que ativa a saída.. Quando passa para 0, desativa..

3. Como vou fazer para acionar a RC4, deixar 2 segundos e desativa-la, já que não consigo colocar delay?

 

 

Deus no céu, Isadora na terra..

Compartilhar este post


Link para o post
Compartilhar em outros sites
22 minutos atrás, Otavio Pessini Bini disse:

1. expressão temp dá erro;

Amigo ctrl-c ctrl-v = ndv né.

Vejamos...

1 minuto atrás, Otavio Pessini Bini disse:

void interrupt (void)
{
while(1){
       if(INTCON.RBIF == 1)
{
       RBIE_bit     =   0x00;
       RBIF_bit     =   0x00;
       RC4_bit      =   0x01;
       temp         =   RB5;
       INTCON.RBIF  =   0x00;
 }
}
}

o while(1) lá ta errado. Já te disse! Tira ele.

temp não existe ainda. você tem que definir - alocar espaço de memória pra - ele antes. Antes do interrupt

unsigned char temp;

void interrupt (void)
{
if(INTCON.RBIF == 1)
{
       RBIE_bit     =   0x00;
       RBIF_bit     =   0x00;
       RC4_bit      =   0x01;
       temp         =   PORTB; //leia o port todo
       //INTCON.RBIF  =   0x00; //nem precisa pois já zerou ele ...

       RBIE_bit=1;//reabilite se não não volta
 }
}

e no loop do main()

void main()
{
       CMCON        =   0x07;   // Desabilita Comparadores;
       RBIE_bit     =   0x01;   // Habilita a interrupção por mudança do PORTB <7:4>
       RBIF_bit     =   0x00;
       PEIE_bit     =   0x01;   // HAbilita Periféricos
       GIE_bit      =   0x01;   // Habilita a interrupção global

       TRISB        =   0xFF;   // PORTB configurado como entrada digital
       TRISC        =   0xEF;   // Apenas usaremos RC4 como saída digital

       RC4_bit      =   0x00;   // Inicia a saída em nível baixo

while(1) //agora sim while(1) ou for(;;) este é o loop do main():nunca mais sai daqui...for ever.
{
if (RC4_bit) delay(2000); //vai ficar 2 segundos a toa. mas por hora é só pra você entender
RC4_bit=0;//depois volta a zero
}

}

Não sei se RC4 é open drain ou só entrada. Verifique d.s.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz ,

 

Só de ver essa estrutura de interrupt me deu dor de cabeça .... fazer isso em Basic é tão simples .... ( claro se fosse um Avr em vez de um Pic kkkk ) !

 

@Otavio Pessini Bini ,

 

Olha, a Isa tá te ajudando demais, acho que ela foi com a tua cara viu .... mas você

tem de se ajudar também... sem saber o básico de estruturas de programacão em C você vai ficar chutando um monte de código que não vai funcionar.

 

Estude de novo as estruturas !

 

Paulo

Compartilhar este post


Link para o post
Compartilhar em outros sites
10 horas atrás, aphawk disse:

Só de ver essa estrutura de interrupt me deu dor de cabeça .... fazer isso em Basic é tão simples .... ( claro se fosse um Avr em vez de um Pic

Paulão de fato lá tem quase zero de c. O que tá lá 99% são os nomes dos registros. O lance é entender os registros bit a bit pra que servem o que fazem. P.ex. como aquele pic só tem um (01) vetor de interrupção  - qualquer evento pula pra ele - , temos que verificar qual ocorreu. Algo como:

 

if(INTCON.RBIF == 1) ... se o Bit Flag do Registro de Interrupção do portB foi pra 1... já sabe

 

Ou seja, "the book is on the table?" => Tem que passar os olhos pelo d.s.

 

Algo do gênero aconteceria no seu precioso avr p.ex. atmega48 com os bits p.ex.:

 

Bit 0 – PCIE0: Pin Change Interrupt Enable 0
When the PCIE0 bit is set and the I-bit in the Status Register (SREG) is set, pin change interrupt 0 is
enabled. Any change on any enabled PCINT[7:0] pin will cause an interrupt. The corresponding interrupt
of Pin Change Interrupt Request is executed from the PCI0 Interrupt Vector. PCINT[7:0] pins are enabled
individually by the PCMSK0 Register.

 

Então ilustre amigo, ilustre amigo alguma rotina em basic pra esta missão. Só por curiosidade mesmo. Ok pode ser simples mas só não vale usar rotinas prontas tipo gosub setinterrupt sem saber o que ela tem dentro. Aí é sw né. Também não vale criar o controle bit a bits em assembly. Queria ver como seu basic trata dos registros baseando-se no datasheet do mc.

 

p.ex. o c dum atmega pra interrupt timer0

 

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0=0xa0;
// Place your code here. Apaguei as linhas pra você não se assustar kk
leds0^=0xff;
leds1^=0xaa;
a++;if (a>9999) a=0;

}

 

TIM0_OVF é o vetor de interrupt do timer0. Está nos #includes, TCNT0 é o contator do timer propriamente dito . Está no d.s. Perceba que neste caso nem precisa resetar o flag. avr++! Portanto, claro, não conta como fator simplificador pro basic ok?

 

eis uma original... micro-aula meia boca kk

abç

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz  Muito boa tarde!

 

Sei que entendo muito pouco, e não é só uma questão de ctrl c + ctrl V.. Mas pode ter certeza que dessa forma, estou absorvendo e aprendendo com certeza (no meu ritmo, lógico rs)...

 

Porém Isadora, acontece que só está ativando a saída RC4 quando muda a porta RB de 0 para 1... de 1 para 0 nada acontece!!!

 

Outra coisa, mesmo deixando o tempo de 2 segundos no main para quando a porta RC for ativada, desligar em seguida, isso não está acontecendo! A porta RC fica em 1 eternamente...

 

 

Sabe o que pode estar ocorrendo ?

 

 

Mais uma vez, só tenho a agradecer...

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz ,

 

Sim, eu percebi que eram os nomes doa registradores do Pic.

 

Nos “preciosos” Avrs, existe também a interrupção por mudança de estado em quase todos os pinos, e tem um registrador para cada port cuidando disso,

e nesse caso também tem de escovar uns bits diretamente nos registradores para tratar a interrupção.

 

Mas para o caso da Int0 e Int1, nao preciso me preocupar com nada de baixo nível, o comando apropriado já permite definir o tipo de interrupção ( subida, descida ou mudança de estado ) e qual a sub-rotina que irá tratar, tudo em alto nível.

 

Mais tarde posto um trecho legal de código.

 

Paulo

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites
Em 13/11/2018 às 15:17, Isadora Ferraz disse:

void interrupt (void)
{
while(1){

Alguma coisa me diz que você não apagou o while(1) da interrupt ou

 

Em 13/11/2018 às 15:17, Isadora Ferraz disse:

RBIE_bit=1;//reabilite se não não volta

não reabilitou o bit. É a 3ª vez que te falo! Se for isso receba um cocão!!! kk

 

agora, aphawk disse:

o comando apropriado já permite definir o tipo de interrupção ( subida, descida ou mudança de estado ) e qual a sub-rotina que irá tratar, tudo em alto nível.

ok tem seu valor mas esse era meu medo o tal "comando apropriado" sem precisar saber ou ver a função dos registradores em termos de bit. Fica meio que parecendo a engessadora linguagem arduína. O argumento que pode agilizar o projeto e blablabla pra mim não cola mais. Já ouviu Almir Sater? "Ando devagar porquê já tive pressa...levo este sorriso porq... ". Tem sido minha derradeira filosofia de vida kk. Já estou em contagem regressiva pra aposentadoria!!! UHUL!

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz  ,

 

Sim, apaguei...

 

E reabilitei o bit sim..

 

Código atual :

 

 

unsigned char temp;

void interrupt (void)
{
       if(INTCON.RBIF == 1)
{
       RBIE_bit     =   0x00;
       RBIF_bit     =   0x00;
       RC4_bit      =   0x01;
       temp         =   PORTB;
       RBIE_bit     =   0x01;
 }
}

void main()
{
       CMCON        =   0x07;   // Desabilita Comparadores;
       RBIE_bit     =   0x01;   // Habilita a interrupção por mudança do PORTB <7:4>
       RBIF_bit     =   0x00;
       PEIE_bit     =   0x01;   // Desabilita Periféricos
       GIE_bit      =   0x01;   // Habilita a interrupção global

       TRISB        =   0xFF;   // PORTB configurado como entrada digital
       TRISC        =   0xEF;   // Apenas usaremos RC4 como saída digital

       RC4_bit      =   0x00;   // Inicia a saída em nível baixo

 while(1){
       if(RC4_bit)
{
       delay_ms(2000);
       RC4_bit      =   0x01;

 }
}
}

 

 

Estou compreendendo muito melhor depois que você expos as problemáticas, principalmente quanto a estrutura... Manja pouco rs

 

Pode ser algum problema no circuito em si ? (Acredito que não...)

 

 

Otavio.

Compartilhar este post


Link para o post
Compartilhar em outros sites
agora, Otavio Pessini Bini disse:

RC4_bit      =   0x01;

 

3 horas atrás, Otavio Pessini Bini disse:

A porta RC fica em 1 eternamente... 

 

 

Em 13/11/2018 às 15:17, Isadora Ferraz disse:

RC4_bit=0;//depois volta a zero }

 

afff... Seu ctrl-c ctrl-v tá com defeito!!! Ou a peça que fica entre a cadeira e o monitor! kk

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz Realmente, é a peça atras do monitor...

 

 

hahahahaha nao me atentei do bit = 0.. cabacisse!

 

 

Mais uma vez obrigado! aleluia VOCÊ me fez chegar onde queria! Só agradeço...

 

E adianto que, aprendi muito aqui, e vou continuar estudando a linguagem.. to curtindo afinal!

 

Forte abraço!

  • Haha 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Isadora Ferraz ,

 

Poxa deixa de denegrir meu velho Basic ....:atirador:

 

Olha que simplicidade :

Config Int0 = Falling   	´Queremos interrupção na descida 
On Int0 Int0_isr        	´quando interromper vai para o label Int0_isr
Enable Int0 			´Habilitar a Int0
Enable Interrupts 		´Permitir que ocorram as interrupções 
....
....
´Rotina de interrupção

Int0_isr:
toggle saida1 			´ Inverte o estado do pino saida1 
return 					´ sai da rotina de interrupção e volta pro programa principal
...
...

Nada dessas complicações doidas de C, de Arduíno ..... mais direto e didático do que isso só em Assembly puro !

 

Paulo

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora





Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas publicações sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×