collapse

* Posts Recentes

Amplificador - Rockboard HA 1 In-Ear por almamater
[Ontem às 19:13]


O que é isto ? por KammutierSpule
[26 de Março de 2024, 19:35]


Bateria - Portátil por almamater
[25 de Março de 2024, 22:14]


Emulador NES em ESP32 por dropes
[13 de Março de 2024, 21:19]


Escolher Osciloscópio por jm_araujo
[06 de Fevereiro de 2024, 23:07]


TP4056 - Dúvida por dropes
[31 de Janeiro de 2024, 14:13]


Leitura de dados por Porta Serie por jm_araujo
[22 de Janeiro de 2024, 14:00]


Distancia Cabo por jm_araujo
[08 de Janeiro de 2024, 16:30]


Meu novo robô por josecarlos
[06 de Janeiro de 2024, 16:46]


Laser Engraver - Alguém tem? por almamater
[16 de Dezembro de 2023, 14:23]

Autor Tópico: Routina de 1ms com o tmr0  (Lida 21071 vezes)

0 Membros e 1 Visitante estão a ver este tópico.

Offline Electropepper

  • Mini Robot
  • *
  • Mensagens: 116
    • Electropepper
Routina de 1ms com o tmr0
« em: 02 de Novembro de 2014, 18:20 »
Boa tarde pessoal,

Tenho estado aqui a testar o compilador SDCC, e tenho aqui um pequeno projecto em que precisáva de usar o clock interno a 1MHZ do pic12f1822.
O problema é que não consigo gerar uma rotina de 1s, já tentei usar o tmr1 e tmr2, neste momento estou a usar o tmr0 e o interrupt para gerar uma pequena rotina de 1ms, depois uso essa para gerar o meu 1s.
Fica sempre ou mais ou menos.
Existe por aqui algum perito em PICs ?


Código: [Seleccione]
#include <pic12f1822.h>


/* Setup chip configuration */
__code int __at(_CONFIG1) __CONFIG = _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF;

void setup(void);
void delay_ms(int milis);
volatile unsigned char done;


static void isr(void) __interrupt 0 {

    if(TMR0IE==1 && TMR0IF==1)//timer 1 interrupt overflow detect
    {
        done = 1; //latch enable as soon as interrupt detected
        TMR0IF = 0;//clear overflow flag
    }

}


void main(void) {

    setup();


    while(1) {

        delay_ms(1000);
        LATA |= (1<<2);
        delay_ms(1000);
        LATA &= ~(1<<2);
}

}

void setup(void) {

    OSCCON = 0b01011000; // 1MHZ internal clock
    LATA = 0;
    TRISA = 0x00;
    PORTA = 0x00;


//  Clock source selection (Fosc/4)
    TMR0CS = 0;
    PSA = 1;    // No prescaler

//  Enable Timer1 interrupt
    TMR0IE = 1;
    GIE = 1;
}

void delay_ms(int milis) {

    while (milis--) {

        done = 0; //disable delay latch
        TMR0 = 250;  //
        while (!done);  //latch till 1ms interrupt happens
    }
}

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: Routina de 1ms com o tmr0
« Responder #1 em: 02 de Novembro de 2014, 18:41 »
Porque é que não metes o timer a contar 1ms e incrementas uma variavel, assim em vez de fazeres polling ao timer funciona por interrupções, mas com oscilador interno terás sempre variações de frequência.
Avr fanboy

Offline Sérgio_Sena

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 1.649
    • Electronic Gear for Musicians
Re: Routina de 1ms com o tmr0
« Responder #2 em: 02 de Novembro de 2014, 18:58 »
Boa tarde pessoal,

Tenho estado aqui a testar o compilador SDCC, e tenho aqui um pequeno projecto em que precisáva de usar o clock interno a 1MHZ do pic12f1822.
O problema é que não consigo gerar uma rotina de 1s, já tentei usar o tmr1 e tmr2, neste momento estou a usar o tmr0 e o interrupt para gerar uma pequena rotina de 1ms, depois uso essa para gerar o meu 1s.
Fica sempre ou mais ou menos.
Existe por aqui algum perito em PICs ?


Código: [Seleccione]
#include <pic12f1822.h>


/* Setup chip configuration */
__code int __at(_CONFIG1) __CONFIG = _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF;

void setup(void);
void delay_ms(int milis);
volatile unsigned char done;


static void isr(void) __interrupt 0 {

    if(TMR0IE==1 && TMR0IF==1)//timer 1 interrupt overflow detect
    {
        done = 1; //latch enable as soon as interrupt detected
        TMR0IF = 0;//clear overflow flag
    }

}


void main(void) {

    setup();


    while(1) {

        delay_ms(1000);
        LATA |= (1<<2);
        delay_ms(1000);
        LATA &= ~(1<<2);
}

}

void setup(void) {

    OSCCON = 0b01011000; // 1MHZ internal clock
    LATA = 0;
    TRISA = 0x00;
    PORTA = 0x00;


//  Clock source selection (Fosc/4)
    TMR0CS = 0;
    PSA = 1;    // No prescaler

//  Enable Timer1 interrupt
    TMR0IE = 1;
    GIE = 1;
}

void delay_ms(int milis) {

    while (milis--) {

        done = 0; //disable delay latch
        TMR0 = 250;  //
        while (!done);  //latch till 1ms interrupt happens
    }
}



Bom exercicio.

Mais depressa consegues resultados precisos se usares Assembler para este PIC em particular, do que com C. Especialmente para um Clock tao lento.

Ha varias maneiras de o fazer, depende exactamente do q pretendes. Eu vou assumir q o q tu queres fazer eh um "pisca-pisca" ::

- se o q queres fazer eh contar tempo com o TMR0, entao nem necessitas de activar interrupcoes. Basta esperares pela flag TMR0IF. Se nao tiveres interrupcoes activas, nao ha salto no contador de programa p outra posicao de memoria.

- podes criar uma variavel INT16 para te contar o tempo necessario, se o teu Fosc=250kHz, e o teu tempo de espera eh de 1khz, entao tens q contar 250ciclos de relogio apenas. Seria TMR0=6 (o overflow de 0xFF->0x00 eh q te dah a Flag de interrupcao q necessitas)

- se nao houver preocupacao de Low Power, eu aumentaria o clock pelo menos para 4MHz, assim o Fosc ja eh de 1MHz e perdes menos tempo nas manobras dos registos do TMR0. Usa o Prescaler p te aumentar o periodo do TMR0 para 1:4, assim so precisas de contar desde o TMR0=6


Enfim... ha muitas maneiras de o fazer.
Ha um bom manual neste forum (acho eu, mas estou biased), provavelmente um pouco desactualizado mas ainda bastante valido ::
http://lusorobotica.com/index.php?topic=1041.msg10064

Offline Dave

  • Mini Robot
  • *
  • Mensagens: 368
    • "O Engenhocas"
Re: Routina de 1ms com o tmr0
« Responder #3 em: 02 de Novembro de 2014, 19:46 »
Tendo em conta que o timer tem o seu overflow de 0xFF para 0x00, de forma a aumentares a precisão entre interrupções podes adicionar ao valor com que carregas o timer, aquilo que ele entretanto já contou.

Por exemplo, imagina que para teres um dado tempo de interrupção tens de carregar o TMR0 com o valor 100. Na passagem de 255 para 0 ele vai para a rotina de interrupção. Essa "viagem", imagina assim, leva algum tempo...
Na rotina de interrupção se carregares novamente TMR0 com 100, já ficaram "perdidas" algumas contagens.
O que faço para obter mais precisão é adicionar ao registo de contagem as contagens que ocorreram entretanto. Ou seja TMR0 += 100 (TMR0 = TMR0 +100).

Com isto consigo uma melhoria de precisão em todas as minhas contagens de tempo...

Obviamente isto só funciona com multiplas interrupções, na mesmo sentido que o asena falou.



 ;)
« Última modificação: 02 de Novembro de 2014, 19:51 por Dave »
David Martins
Eng. Electrotécnica e de Computadores, UBI

Offline Electropepper

  • Mini Robot
  • *
  • Mensagens: 116
    • Electropepper
Re: Routina de 1ms com o tmr0
« Responder #4 em: 02 de Novembro de 2014, 20:16 »
Obrigado pelas dicas,
Acabei de alterar o código e continuo com um segundo muito "torto".
Desta vez já não utilizo nenhuma interrupção pois como já foi dito, isso só vai resultar em saltos e mais tempo desperdiçado.
Quanto á questão do timer interno ser muito impreciso, acredito nisso mas o que me pasma é que entretanto usei o mikroC que já vem com a função delay_ms(), e o meu 1s vem perfeito, por isso agora já é uma questão de honra, hehe.
Sabendo que é possivel realizar um delay preciso com o oscilador interno, quero perceber como se faz até porque tenciono usar este compilador de agora adiante.

Código: [Seleccione]
#include <pic12f1822.h>


/* Setup chip configuration */
__code int __at(_CONFIG1) __CONFIG = _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF;

void setup(void);
void delay_ms(int milis);



void main(void) {

    setup();


    while(1) {

        delay_ms(1000);
        LATA |= (1<<2);
        delay_ms(1000);
        LATA &= ~(1<<2);
    }

}

void setup(void) {

    OSCCON = 0b01011000; // 1MHZ internal clock
    LATA = 0;
    TRISA = 0x00;
    PORTA = 0x00;
//  Clock source selection (Fosc/4)
    TMR0CS = 0;
    PSA = 1;    // No prescaler

//  Enable Timer1 interrupt
//  TMR0IE = 1;
//  GIE = 1;
}

void delay_ms(int milis) {

    while (milis--) {

        TMR0 = 6;  //
        while (!TMR0IF);  //latch till 1ms interrupt happens
        TMR0IF = 0;
    }
}

Offline Dave

  • Mini Robot
  • *
  • Mensagens: 368
    • "O Engenhocas"
Re: Routina de 1ms com o tmr0
« Responder #5 em: 02 de Novembro de 2014, 20:24 »

Desta vez já não utilizo nenhuma interrupção pois como já foi dito, isso só vai resultar em saltos e mais tempo desperdiçado.


Quem disse isso?
David Martins
Eng. Electrotécnica e de Computadores, UBI

Offline Dave

  • Mini Robot
  • *
  • Mensagens: 368
    • "O Engenhocas"
Re: Routina de 1ms com o tmr0
« Responder #6 em: 02 de Novembro de 2014, 20:33 »
Código: [Seleccione]
//  Clock source selection (Fosc/4)
    TMR0CS = 0;
    PSA = 1;    // No prescaler

PSA = 1 ?

Talvez esteja aqui o problema!

Coloca:
PSA = 0;
PS = 0;     //prescaller 1:2

Pelas minhas contas, para 1ms de interrupção TMR0 deve ser carregado com 131 e não com 6...
(0.001=1/((1000000/4)/2)*(256-TMR0))

Em relação ao resto, não leste bem as respostas anteriores...

Experimenta:
Código: [Seleccione]
//  Clock source selection (Fosc/4)
    TMR0CS = 0;
    PSA      = 0;
    PS        = 0;

Código: [Seleccione]
TMR0 = 0;

while(milis--)
{
   TMR0IF = 0;

   TMR0 += 131;

   while(!TMR0IF);
}


Isto deve funcionar melhor do que o que aí tens.... mas continuava a ser melhor com interrupção....
« Última modificação: 02 de Novembro de 2014, 20:55 por Dave »
David Martins
Eng. Electrotécnica e de Computadores, UBI

Offline Electropepper

  • Mini Robot
  • *
  • Mensagens: 116
    • Electropepper
Re: Routina de 1ms com o tmr0
« Responder #7 em: 02 de Novembro de 2014, 20:44 »
Dave é efectivamente PSA = 1 para fazer o bypass to prescaler.
Fica aqui a imagem tirada directamente do datasheet.

Offline Sérgio_Sena

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 1.649
    • Electronic Gear for Musicians
Re: Routina de 1ms com o tmr0
« Responder #8 em: 02 de Novembro de 2014, 20:51 »
O Dave tem razao, se vais trabalhar com Clock tao lento, tens q fazer o q ele sugeriu, adicionar o teu atraso no programa ao teu TMR.

No entanto, com Pre-scaller 1:1, o Fosc eh de 250kHz.
Logo, se o TMR0 contar 250, teremos um overflow a cada 1kHz.

O MikroC usa rotinas por software para fazer a temporizacao, ou seja, nao s epode fazer mais nada enqt ele estah a contar tempo. Isto eh um desperdicio de CPU. Eh bom p aprender, mas nao p fazer trabalhos a serio.

Se nao quiseres subir o Clock, faz a rotina de temporizacao a somar o erro ou entao escreve-a em ASM e junta-a ao programa q estahs a fazer.
O Assembler serve p isso mesmo, fazer tudo rapido e com as instrucoes bem contadas.



Offline Dave

  • Mini Robot
  • *
  • Mensagens: 368
    • "O Engenhocas"
Re: Routina de 1ms com o tmr0
« Responder #9 em: 02 de Novembro de 2014, 20:54 »
Electropepper, o erro que dizes ter é de quanto?
David Martins
Eng. Electrotécnica e de Computadores, UBI

Offline Electropepper

  • Mini Robot
  • *
  • Mensagens: 116
    • Electropepper
Re: Routina de 1ms com o tmr0
« Responder #10 em: 02 de Novembro de 2014, 21:12 »
Em 1 minuto tenho um erro de cerca de 2 ou 3s, isto contado com um cronómetro, que eu sei que é um pouco crú, mas mesmo assim dá para notar.

Offline Dave

  • Mini Robot
  • *
  • Mensagens: 368
    • "O Engenhocas"
Re: Routina de 1ms com o tmr0
« Responder #11 em: 02 de Novembro de 2014, 21:41 »
Então está certo...

Isto não dá para contar grandes tempos com muita precisão...

Ainda por cima, estás a usar o oscilador interno...

Além disso, o método que estás a usar para contar milisegundos não é o melhor... em cada contagem há um pequeno erro que se vai acumulando.

Se utilizares um cristal externo bom esse erro irá diminuir...

Eu já usei um cristal de 2MHz para fazer umas contagens ao longo de um mês. No final apenas tenho um erro de meia dúzia de minutos (já não sei precisar, mas penso que eram cerca de ~3min).
David Martins
Eng. Electrotécnica e de Computadores, UBI

Offline Sérgio_Sena

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 1.649
    • Electronic Gear for Musicians
Re: Routina de 1ms com o tmr0
« Responder #12 em: 02 de Novembro de 2014, 22:04 »
Ele estah a usar um micro com encapsulameno de 8pinos, se usar um cristal externo, perde dois pinos. Se somar ah alimentacao, entao sobram 3 I/O.
Nao sabemos a aplicacao, pode dar como pode nao dar.
Mas claro q usar um cristal de 32k728 para o TMR1 eh o melhor.

O circuito vai estar ligado com transformador ah rede ou vai ter um transofrmador na placa ou var ser alimentado a bateria?
Um tiro no escuro: se o sistema tiver alimentacao na placa, podes aproveitar os ~50Hz da rede para medir os teus segundos, ou 20ms de cada vez.

Offline Dave

  • Mini Robot
  • *
  • Mensagens: 368
    • "O Engenhocas"
Re: Routina de 1ms com o tmr0
« Responder #13 em: 02 de Novembro de 2014, 22:12 »
Bahh... não ando a ler bem os datasheets xD
« Última modificação: 02 de Novembro de 2014, 22:17 por Dave »
David Martins
Eng. Electrotécnica e de Computadores, UBI

Offline Dave

  • Mini Robot
  • *
  • Mensagens: 368
    • "O Engenhocas"
Re: Routina de 1ms com o tmr0
« Responder #14 em: 02 de Novembro de 2014, 22:24 »
Electropepper, estás a meter um LED a piscar a cada minuto?

Fala-me mais disso. Qual é o pino e tudo.
Eu crio aqui um programa e mando-te o *.hex só para testares se o tempo em o mesmo erro ou se há melhorias...
David Martins
Eng. Electrotécnica e de Computadores, UBI