LusoRobótica - Robótica em Português

Sistemas específicos => PIC => Tópico iniciado por: Electropepper em 02 de Novembro de 2014, 18:20

Título: Routina de 1ms com o tmr0
Enviado por: Electropepper em 02 de Novembro de 2014, 18:20
Boa tarde pessoal,

Tenho estado aqui a testar o compilador SDCC (http://sdcc.sourceforge.net/), 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
    }
}
Título: Re: Routina de 1ms com o tmr0
Enviado por: senso 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.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 02 de Novembro de 2014, 18:58
Boa tarde pessoal,

Tenho estado aqui a testar o compilador SDCC (http://sdcc.sourceforge.net/), 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 (http://lusorobotica.com/index.php?topic=1041.msg10064)
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave 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.



 ;)
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper 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;
    }
}
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave 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?
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave 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....
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper 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.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena 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.


Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave em 02 de Novembro de 2014, 20:54
Electropepper, o erro que dizes ter é de quanto?
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper 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.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave 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).
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena 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.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave em 02 de Novembro de 2014, 22:12
Bahh... não ando a ler bem os datasheets xD
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave 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...
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 02 de Novembro de 2014, 22:39
Ok, o que eu quero para já é aprender a fazer uma rotina de 1ms com precisão, dai eu posso tirar 1s, 1m ou seja o que for para poder aplicar de futuro noutros projectos, segundo o meu projecto é alimentar este pic com uma pilha CR2032 de 3v que vou ligar a um daqueles modulos pequenos de RF315Khz, dai eu querer usar o oscilador interno e tambem o mais lento possivel, não demasiado lento porque vou ter que usar a USART tambem.
Mas se de futuro eu quiser usar por exemplo o protocolo OneWire preciso de precisão ao milisegundo.
Portanto para já tenho um led com a respectiva resistencia de 220 no pino 5 do 12f1822 ou RA2, mais nada.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 02 de Novembro de 2014, 22:45
Já agora Asena, quando começei isto tinha as subrotinas em assembly, no final o resultado foi igualzinho a este, por isso decidi usar apenas C.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 02 de Novembro de 2014, 22:48
- Para 1-Wire precisas de precisao ao micro-segundo. O protocolo eh apertado mas permite algum erro.

- Ainda escrevo em ASM qd preciso de precisao em termos de poder controlar os ciclos todos do programa. Posso-te garantir que eh possivel fazer as rotinas q quiseres, a demorar o tempo q quiseres !! :)

Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 02 de Novembro de 2014, 23:01
Vou procurar e já te mostro.  :)
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 02 de Novembro de 2014, 23:15
Asena, não encontrei mas fiz aqui uma rapidamente.
Continua impreciso na mesma.
Usei o Code generator (http://www.piclist.com/cgi-bin/delay.exe?Delay=0.001&Type=seconds&Regs=d1+d2+d3+d4&clock=1&routine=yes&name=Delay&CPU=PIC).

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;
__asm
cblock 0x20
d1
endc
__endasm;

}

void delay_ms(int milis) {

    while (milis--) {
    __asm
        movlw   0x51
        movwf   d1
        Delay_0:
        decfsz  d1, f
        goto    Delay_0
    __endasm;
    }
}
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave em 02 de Novembro de 2014, 23:34
Assim, de forma rápida, no XC8...

Tenta lá o *.hex do anexo.

Não deve dar um erro assim tão grande...
Já usei esse micro e é brutal para aplicações pequenas. Nunca tive grande problemas com coisas destas...

Tem um pequeno desvio... mas deve ser alguma coisa a rondar os ~700uS por cada minuto, segundo o simulador...

O LED vai ficar 1min ligado, 1min desligado, e assim sucessivamente...

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

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#define _XTAL_FREQ 1000000

unsigned char seg = 0;
unsigned int cont = 0;

void interrupt isr (void)
{
    if(INTCONbits.T0IF)
    {
        TMR0 += 6;

        cont++;

        if(cont>=1000)
        {
            seg++;
            cont = 0;
        }

        if(seg>=60)
        {
            seg = 0;
            LATAbits.LATA2 = !LATAbits.LATA2;
        }

        INTCONbits.T0IF = 0; 
    }

}


void init_osc(void)         //1MHz
{
    OSCCONbits.SPLLEN = 0;
    OSCCONbits.IRCF = 0b1011;
    OSCCONbits.SCS = 0;
}

void init_port(void)
{
    TRISA   = 0;
    LATA    = 0;
    ANSELA  = 0;
}

void interrup(void)
{
    INTCONbits.T0IF = 0;
    INTCONbits.T0IE = 1;
    INTCONbits.GIE = 1;
}

void init_timer0(void)
{
    OPTION_REGbits.TMR0CS = 0;
    OPTION_REGbits.T0SE = 0;
    OPTION_REGbits.PSA = 1;
    TMR0 = 0;
}

void main(void)
{
    init_osc();
    init_port();
    init_timer0();
    interrup();

    LATAbits.LATA2 = 1;
    TMR0 = 6;

    while(1);
}

Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 02 de Novembro de 2014, 23:55
Sim está bem perto de um minuto  :).
Mas tenho de esperimentar isto com 1s ai é que se vê claramente com um cronometro ao lado.
O código que postei acima com a rotina em assembly tambem está igualmente perto.
Já agora o XC da microchip não traz rotinas de delay ? ou outro tipo de libs ?
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave em 03 de Novembro de 2014, 00:16
Sim tras... mas no caso usar vários delays para fazer 1 min vai dar num grande erro também...
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave em 03 de Novembro de 2014, 00:20
Mas tenho de esperimentar isto com 1s ai é que se vê claramente com um cronometro ao lado.
Isso é relativo... vais ter imensa dificuldade em cronometrar manualmente o tempo certo.

Tenta o *.hex em anexo para 1segundo.

O LED vai ter um comportamento semelhante ao de 1min, mas agora a cada segundo.

Segundo o simulador dá um erro de ~12uS...
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 03 de Novembro de 2014, 08:20
Asena, não encontrei mas fiz aqui uma rapidamente.
Continua impreciso na mesma.
Usei o Code generator (http://www.piclist.com/cgi-bin/delay.exe?Delay=0.001&Type=seconds&Regs=d1+d2+d3+d4&clock=1&routine=yes&name=Delay&CPU=PIC).

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;
__asm
cblock 0x20
d1
endc
__endasm;

}

void delay_ms(int milis) {

    while (milis--) {
    __asm
        movlw   0x51
        movwf   d1
        Delay_0:
        decfsz  d1, f
        goto    Delay_0
    __endasm;
    }
}



O facto de teres uma parte do codigo em ASM nao significa q as temporizacoes estao certas.

Estahs a ter em conta os saltos para entrar e sair da funcao?
O teu loop While estahs a ser convertido para ASM de q forma?

Trabalhar com tempos precisos tem algumas nuances pois no Arduino as brincadeiras nao teem importancia se atrasarem, mas em trabalhos com plataformas a serio as coisas mudam de figura e tem q se "escrever" tudo como se quer.

Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 03 de Novembro de 2014, 08:21
Mas tenho de esperimentar isto com 1s ai é que se vê claramente com um cronometro ao lado.
Isso é relativo... vais ter imensa dificuldade em cronometrar manualmente o tempo certo.

Tenta o *.hex em anexo para 1segundo.

O LED vai ter um comportamento semelhante ao de 1min, mas agora a cada segundo.

Segundo o simulador dá um erro de ~12uS...


Querias dizer ~12 mili-segundos ?   pela imagem do simulador eh o q lah esta.

Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 03 de Novembro de 2014, 08:37
Assim, de forma rápida, no XC8...

Tenta lá o *.hex do anexo.

Não deve dar um erro assim tão grande...
Já usei esse micro e é brutal para aplicações pequenas. Nunca tive grande problemas com coisas destas...

Tem um pequeno desvio... mas deve ser alguma coisa a rondar os ~700uS por cada minuto, segundo o simulador...

O LED vai ficar 1min ligado, 1min desligado, e assim sucessivamente...

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

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#define _XTAL_FREQ 1000000

unsigned char seg = 0;
unsigned int cont = 0;

void interrupt isr (void)
{
    if(INTCONbits.T0IF)
    {
        TMR0 += 6;

        cont++;

        if(cont>=1000)
        {
            seg++;
            cont = 0;
        }

        if(seg>=60)
        {
            seg = 0;
            LATAbits.LATA2 = !LATAbits.LATA2;
        }

        INTCONbits.T0IF = 0; 
    }

}


void init_osc(void)         //1MHz
{
    OSCCONbits.SPLLEN = 0;
    OSCCONbits.IRCF = 0b1011;
    OSCCONbits.SCS = 0;
}

void init_port(void)
{
    TRISA   = 0;
    LATA    = 0;
    ANSELA  = 0;
}

void interrup(void)
{
    INTCONbits.T0IF = 0;
    INTCONbits.T0IE = 1;
    INTCONbits.GIE = 1;
}

void init_timer0(void)
{
    OPTION_REGbits.TMR0CS = 0;
    OPTION_REGbits.T0SE = 0;
    OPTION_REGbits.PSA = 1;
    TMR0 = 0;
}

void main(void)
{
    init_osc();
    init_port();
    init_timer0();
    interrup();

    LATAbits.LATA2 = 1;
    TMR0 = 6;

    while(1);
}



O facto de teres a rotina de Interrupcao cheia de codigo, eh propositadamente educativo? ou eh naturalmente como programas?

A funcao de interrupcao nao deve ter nada a nao ser Set/Clear de Flags. Qq codigo extra atrasa todo o processo em curso no Main.

Assim estaria mais correcto ::


void interrupt isr (void)
{
    if(INTCONbits.T0IF)
    {
        INTCONbits.T0IF = 0; 

        TMR0 += 6;

        FLAG-TMR0 = 1;
    }
}


Main:
   if (FLAG-TMR0)
   {
        cont++;

        if(cont>=1000)
        {
            seg++;
            cont = 0;
        }

        if(seg>=60)
        {
            seg = 0;
            LATAbits.LATA2 = !LATAbits.LATA2;
        }
    }




Eh mais ou menos isto q estava a falar.
E mesmo aquele TMR0 += 6 ali, da-me arrepios :D

Crias uma estrutura so com registos de Flags e podes usar isso por todo o programa.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave em 03 de Novembro de 2014, 09:02

Querias dizer ~12 mili-segundos ?   pela imagem do simulador eh o q lah esta.

Há dias lixados... xD

Sim ~12milisegundos :D

Quanto ao resto, não é comum. É um programa rápido para mostrar que funciona.
De qualquer forma, para o que o micro está a fazer, não há nada a correr no main e o timer não para, seja onde estiver. Por isso o resultado, neste caso, ia dar os mesmos tempos, quer da minha forma quer da tua, acho eu... mas tu é que és o mestre :D.


PS: Estava aqui a tentar ver melhor, "FLAG-TMR0" deveria ser "FLAG_TMR0"?
Se não for,  não consigo entender o objectivo. Se puderes, depois dá uma explicação ;)


Abraço.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 03 de Novembro de 2014, 09:47

Querias dizer ~12 mili-segundos ?   pela imagem do simulador eh o q lah esta.

Há dias lixados... xD

Sim ~12milisegundos :D

Quanto ao resto, não é comum. É um programa rápido para mostrar que funciona.
De qualquer forma, para o que o micro está a fazer, não há nada a correr no main e o timer não para, seja onde estiver. Por isso o resultado, neste caso, ia dar os mesmos tempos, quer da minha forma quer da tua, acho eu... mas tu é que és o mestre :D.


PS: Estava aqui a tentar ver melhor, "FLAG-TMR0" deveria ser "FLAG_TMR0"?
Se não for,  não consigo entender o objectivo. Se puderes, depois dá uma explicação ;)


Abraço.


Qual mestre LOL.... mestres somos todos uns dos outros ;)

FLAG-TMR0 eh apenas um pseudo nome.

Algo como isto talvez, quick examplenao formatado nem outras coisas:

Código: [Seleccione]
typedef struct
{
    unsigned char   byState;

    unsigned        bTimerUsartRX:1;
    unsigned        bTimerComms:1;
    unsigned        bTimerAlarm:1;

}_TIMERS;

_TIMERS                 g_TIMERS;






void interrupt interrupcao(void)
{
    if ((TMR2IF) && (TMR2IE))
    {
        TMR2IF = 0;

        // Da indicacao q o timer rolou
        g_TIMERS.bTimerComms = 1;
    }

}


void interrupt interrupcao(void)
{

    if ((TMR2IF) && (TMR2IE))
    {
        TMR2IF = 0;

        // Da indicacao ah maquina de estados dos Timers do proximo passo
        g_TIMERS.byState = ST_Timers_TICK;
    }


}





void main (void)
{
...

if (g_TIMERS.bTimerComms)
{
// executar tarefa
}
}
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 03 de Novembro de 2014, 10:04
Ok pessoal, já deu para tirar umas conclusões.
Usar a interrupção com apenas uma flag(state machine) a dar indicação que o timer rolou.
Ter em conta a chamada e saida para a função de delay assim como fazer o load do tmr0 com o respectivo valor.
Mais logo quando chegar a casa já esperimento isto outa vez.
E tenho de ver se arranjo um oscilóscpio para tirar as teimas.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave em 03 de Novembro de 2014, 10:07
E tenho de ver se arranjo um oscilóscpio para tirar as teimas.

Sim... é melhor que cronometrar à mão.... muito melhor  ;D ;D
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 03 de Novembro de 2014, 10:30
Ok pessoal, já deu para tirar umas conclusões.
Usar a interrupção com apenas uma flag(state machine) a dar indicação que o timer rolou.
Ter em conta a chamada e saida para a função de delay assim como fazer o load do tmr0 com o respectivo valor.
Mais logo quando chegar a casa já esperimento isto outa vez.
E tenho de ver se arranjo um oscilóscpio para tirar as teimas.


Olha q possivelmente para trabalho digital, ha algo melhor q o osciloscopio ::  um Logic Analyzer

Tipo estes muito bons: https://www.saleae.com/ (https://www.saleae.com/)  com a vantagem de agora as entradas tb medirem sinais analogicos substituindo completamente o osciloscopio e com  memoria "infinita"

Mais baratuxo tens o Bus Pirate mas c as specs muito muito abaixo.
Tambem o USBEE, mais caro q o Saleae e nao eh melhor.

Ha outros.
O meu conselho eh comecar com um Analyzer q faca analogico tb e depois se precisares de medir RF, entao outro aparelho dedicado.
E podes levar na bolsa do computador, o q nao acontece c o scope.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 03 de Novembro de 2014, 10:51
Boa dica, é que eu até tenho aqui um bus pirate  :D
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 03 de Novembro de 2014, 22:22
Ora já tirei umas amostras com o bus pirate, a unica consistencia é que os valores são completamente inconsistentes, talvez por causa do cristal interno.
Ora vejam :
Para este código ,
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 minute_delay(void);


void main(void) {

    setup();


    while(1) {

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

}

void setup(void) {

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

__asm
cblock 0x20
d1
endc
__endasm;

}

void delay_ms(int milis) {

    while (milis--) {
    __asm
        movlw   0x51
        movwf   d1
        Delay_0:
        decfsz  d1, f
        goto    Delay_0
    __endasm;
    }
}


void minute_delay(void) {

    unsigned char minute = 60;

    while (minute--) {
    delay_ms(1000);
    }
}
O ficheiro logic_sniffer_asm.png

E para este código,
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(1);
        LATA |= (1<<2);
        delay_ms(1);
        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

}

void delay_ms(int milis) {

    while (milis--) {

        TMR0 = 6;  //
        while (!TMR0IF);  //latch till 1ms interrupt happens
        TMR0IF = 0;
    }
}
O ficheiro logic_sniffer_C_no_int.png.
Título: Re: Routina de 1ms com o tmr0
Enviado por: senso em 03 de Novembro de 2014, 22:30
Tanto num como noutro é normal teres essas diferenças, quando apagas o led tens mais código a ser executado para voltares ao inicio do loop while, isso leva-te a que o on/off não tenha um duty cycle de 50%, terás sempre ligeiras diferenças, no outro, isso parece-me jitter devido a caminhos de execução diferentes, tempo de entrada numa ISR e la la la, não sei se num pic é totalmente deterministico mas num AVR um salto para uma interrupção pode demorar 3 ou 4 ciclos de relógio, depende de algumas coisas.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 03 de Novembro de 2014, 22:40
Tambem me cheira a imprecisão do próprio bus pirate, afinal é um PIC e não estou a ver nenhum cristal na placa pelo que tambem deve estar a usar o cristal interno. cómico no minimo.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 03 de Novembro de 2014, 22:43
Bom, eu acho q a aproximacao ao problema estah a ser feita da maneira menos correcta.
Andas preocupado no porque das funcoes nao darem o tempo correcto, qd dever-se-ah pensar de outra maneira.

Se sabes quanto tempo demora cada instrucao a executar, entao contar instrucoes eh a melhor solucao. E ASM eh a melhor solucao p esta situacao. Uma funcao completamente TODA em ASM apenas para fazer o temporizador.

Este exemplo em ASM estah feito par aum cristal de 4MHz tendo entao um Fosc de 1MHz. No teu caso eh aumentar o teu clock soh dividir o numero multiplicado.
Experimenta. Este garanto-te que te dah 1.000ms ou 2*500us

Código: [Seleccione]

processor 16F886 ;configura o tipo de CPU usado
radix hex ;pre-definicao do tipo de valores numericos usados

include "P16F886.inc" ;inclui pre-definicoes do processador

;---------------------------
temp equ 0x20 ;define endereco 0x20 com o nome de TEMP

;---------------------------


inicio

call atraso_500u ;chama rotina de atraso
call atraso_500u ;chama rotina de atraso

goto inicio ;faz temporizacao de novo

;---------------------------

atraso_500u
movlw 0xA5 ;carrega W com o valor 0xA5
movwf temp ;move valor em W para TEMP

decfsz temp, f ;decrementa TEMP em uma unidade, coloca resultado em TEMP
;e salta a instrucao seguinte, se o resultado for zero
goto $-1


nop
nop

return ;sai fora da rotina de ATRASO

;---------------------------
end

Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 03 de Novembro de 2014, 22:46
Tambem me cheira a imprecisão do próprio bus pirate, afinal é um PIC e não estou a ver nenhum cristal na placa pelo que tambem deve estar a usar o cristal interno. cómico no minimo.


Infelizmente equipamente barato tem sempre concessoes. E os cristais bons sao caros. Um cristal barato eh igual a usar o RC interno cortado a laser q vai alterar com a tensao e a temperatura.

Tambem o compilador nao estah provavelmente a optimizar para o melhor servico (soh vendo o ASM final) logo aih tambem tem falhas.

Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 03 de Novembro de 2014, 23:02
Bem acho que definitivamente já não vou confiar no bus pirate para isto.
Esta foi feita com o mikroC.
Mas asena uma coisa eu garanto-te de todos os métodos com o mikroC tenho o melhor resultado se não perfeito com oscilador interno.
Título: Re: Routina de 1ms com o tmr0
Enviado por: senso em 03 de Novembro de 2014, 23:04
Não tens nada mais standalone?
O bus pirate está a correr montes de código enquanto está a olhar para os pinos.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 03 de Novembro de 2014, 23:06
Bem tenho oscilóscopio no trabalho, é o próximo passo.
Mas asena o teu asm está simples e eficaz, não duvido que funcione, estás-me a sugerir que escreva o código todo em asm então ?
De outra maneira não estou a ver como transporto isto para projectos maiores.
Título: Re: Routina de 1ms com o tmr0
Enviado por: jm_araujo em 03 de Novembro de 2014, 23:44
Código: [Seleccione]
void delay_ms(int milis) {

    while (milis--) {

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

Devias ler com mais atenção o que já te disseram pelo menos o Dave (http://lusorobotica.com/index.php?topic=7725.msg86679#msg86679 (http://lusorobotica.com/index.php?topic=7725.msg86679#msg86679)), o asena (http://lusorobotica.com/index.php?topic=7725.msg86685#msg86685 (http://lusorobotica.com/index.php?topic=7725.msg86685#msg86685)) e também ler os vários  exemplos que deram.
Deves adicionar ao TMR0 em vez de fazer uma atribuição para diminuir o erro: usar "TMR0+=6;" em vez de "TMR0=6;". Eu próprio ia fazer essa sugestão quando eles se me adiantaram, é usada desde os tempos em que praticamente só se programava PICs em assembly (ainda não havia compiladores de C de jeito).

Enquanto não eliminares o básico vai-te ser difícil despistar se é imprecisão do oscilador ou outro fator.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 04 de Novembro de 2014, 05:41
Ok peço desculpa não prestei mesmo atenção.
Vou fazer isso.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Dave em 04 de Novembro de 2014, 09:37
Não quero ser chato, mas faz lá teste ao exemplo que te deixei aí de 1s...

Só mesmo para matar a curiosidade...
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 04 de Novembro de 2014, 09:52
Mas asena o teu asm está simples e eficaz, não duvido que funcione, estás-me a sugerir que escreva o código todo em asm então ?
De outra maneira não estou a ver como transporto isto para projectos maiores.

Toda nao, mas a funcao de temporizacao se nao fizeres por Interrupcoes, deverahs fazer em ASM e inxluir no teu codigo.

Nem q seja educativo, merece a pena faze-lo.
Título: Re: Routina de 1ms com o tmr0
Enviado por: Electropepper em 09 de Novembro de 2014, 21:28
Sim Dave já tinha esperimentado, mencionei ai em algum lado que está bem próximo e tambem já esperimentei tudo o que aqui me foi sugerido agradeço a todos, ainda não estou muito satisfeito, mas tenho mais trabalho para fazer.
Voltarei a isto noutra altura a não ser que estas rotinas sejam o suficiente para por exemplo trabalhar com o onewire protocol ou assim.
Título: Re: Routina de 1ms com o tmr0
Enviado por: senso em 09 de Novembro de 2014, 22:30
Eu acho que já respondeste, mas porque não um micro mais capaz?
Não seria mais económico usar algo mais rápido e meter o micro a dormir quando não é preciso que baixar o clock para reduzir o consumo e andar a programar em assembly?
Título: Re: Routina de 1ms com o tmr0
Enviado por: Sérgio_Sena em 10 de Novembro de 2014, 09:00
Voltarei a isto noutra altura a não ser que estas rotinas sejam o suficiente para por exemplo trabalhar com o onewire protocol ou assim.

Mais que suficiente.
O 1-wire eh perfeitamente adaptavel a estas temporizacoes.

Um logic-analyzer vai-te ajudar a fazer o debug.