Usar a função delay() dentro de uma interrupção do timer2

Boas,

Tenho o seguinte código

unsigned int toggle = 0;  //used to keep the state of the LED
unsigned int count = 0;   //used to keep count of how many interrupts were fired
boolean flag = false;

//Timer2 Overflow Interrupt Vector, called every 1ms
ISR(TIMER2_OVF_vect) 
{
  count++;               //Increments the interrupt counter
        
       // if(count > 999) //1 seg
       if(count > 499) // 500ms
        {
          toggle = !toggle;    //toggles the LED state
                   
          tx_trame(trame); 
          //Serial.println("\n\rDEBUG timer"); 
          //Serial.println("\n\r");
          
          count = 0;    //Resets the interrupt counter
       
         }
         
  digitalWrite(13,toggle); //LED
 
  TCNT2 = 130;           //Reset Timer to 130 out of 255
  TIFR2 = 0x00;          //Timer2 INT Flag Reg: Clear Timer Overflow Flag
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup(void)
{

  Serial.begin(19200);
  pinMode(etx, OUTPUT);  
  pinMode(13,OUTPUT);
  Serial.println("DEBUG ok ok ");

  //Setup Timer2 to fire every 1ms
  TCCR2B = 0x00;        //Disbale Timer2 while we set it up
  TCNT2  = 130;         //Reset Timer Count to 130 out of 255
  TIFR2  = 0x00;        //Timer2 INT Flag Reg: Clear Timer Overflow Flag
  TIMSK2 = 0x01;        //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
  TCCR2A = 0x00;        //Timer2 Control Reg A: Wave Gen Mode normal
  TCCR2B = 0x05;        //Timer2 Control Reg B: Timer Prescaler set to 128

}



/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop()
{
  
// tx_trame(trame);
// delay(500);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void tx_trame (byte *data)
{
 
 byte i =0;
 byte checksum =0;
static unsigned long ii=0;

  
  digitalWrite(etx, HIGH);  // escreve MAX485
  delay(10);                                                                 //****** problema ******
  
//__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); //delay

  Serial.write(0xF3); 
  Serial.write(0xF7);   

      //for (int i=0; i<length; i++) 
      for (i=0; i<13; i++) 
      {
        Serial.write(data[i]); 
        checksum ^= trame[i];
      }

    Serial.write(checksum);

  delay(10);                                             //****** problema ******
 
//delayMicroseconds(100);
 
 /* Serial.println("\n\rDebug 8");
      for (ii=0; ii<10000; ii++)
     {
      __asm__("nop\n\t"); //delay
      // __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); //delay
     }
   Serial.println("\n\rDebug 9");*/
   
 digitalWrite(etx, LOW);
 
}

Se chamar a função tx_trame() pela função loop() ... tudo funciona as mil maravilhas ....

Mas desde que a chamo pelo timer2 ... chapéu ... nada funciona

Estou a usar a interrupção do timer2 todos os 500ms porque estou a controlar uma carta de E/S que tem que receber todos os 500ms o que fazer se não ao fim de 800ms reseta

A questão é como posso substituir a função delay(10); da função tx_trame(); para que funcione dentro do timer2

PS: Não sei porque é que o post original foi apagado.

Cumprimentos

Zé tretas

Chegaste a ver a resposta que lá meti?

Boas,

bubulindo não.

Cpts

Zé tretas

Nao tenho tempo para pesquisar na net a confirmacão (apesar de estar algures pelo forum), mas basicamente, sempre que entras numa interrupcão, as interrupcões são desactivadas e activadas na saida para evitar "nested interrupts" e parar o codigo inexplicavelmente. Como o delay do Arduino não e feito com busy waiting (na forma original), mas sim contanto os micros vindo do timer0, se parares o programa dentro duma interrupt, o timer não conta e o teu codigo bloqueia.

Não pesquisei nada dentro do manual do AVR ou no avr-libc, mas não perdes nada em experimentar isto:

ISR(TIMER2...) {
sei();
...
}

Duvido que funcione, mas não tenho disponibilidade para testar eu mesmo. Logo não perdes nada em testar.
Ou então...

Experimenta a implementacão do delay vinda da avr-libc.

http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html

#include <util/delay.h>
...
 _delay_ms(10);
...

Ou altera o codigo para fazeres o minimo possivel na interrupcao.

Boas,

acabo de testar a função _delay() e também não funciona se chamar a função tx_trame pelo timer2 se for pelo loop() tudo ok

e também usei a função sei() tb sem resultado positivo

ISR(TIMER2_OVF_vect) 
{
  count++;               //Increments the interrupt counter
        
       // if(count > 999) //1 seg
       if(count > 499) // 500ms
        {
          cli(); 
          toggle = !toggle;    //toggles the LED state
                   
         tx_trame(trame); 
          //Serial.println("\n\rDEBUG timer"); 
          //Serial.println("\n\r");
          sei();
          count = 0;    //Resets the interrupt counter
       
         }
         
  digitalWrite(13,toggle); //LED
 
  TCNT2 = 130;           //Reset Timer to 130 out of 255
  TIFR2 = 0x00;          //Timer2 INT Flag Reg: Clear Timer Overflow Flag
}

Tem que haver bem uma maneira de eu executar uma funçao todos os 500ms e usar o loop com o resto do meu codigo ... tou velho para estas coisas :frowning:

Cpts

Zé tretas

Existe... não podes é usar interrupções dentro dessa interrupção.

Já experimentaste flags?

Boas,

bandeiras só conheço mesmo aquelas do milho :stuck_out_tongue: ...

Desculpa o meu humor do interior ... Mas se deres ai umas dicas estou disposto a tentar tudo.

Ja agora tira aqui uma duvida que eu pensava ter percebido mas cada vez tenho menos a certeza que tenha bem compreendido

Quando existe uma interrupção como é no meu caso

e entro dentro do if só existe uma nova interrupção quando ele executar todas as funções que estão dentro do if ... se demorar muito tempo e houver outra interrupção ele volta ao inicio sem acabar de executar as funções todas ?

Cpts

Zé tretas

LOL

Essa do milho tem piada. :slight_smile:

Uma flag é algo assim:

volatile unsigned char Executa_funcao = 0; 

setup(); 

ISR(timer2) {
Executa_funcao = 1; 

}

loop() {
if (Executa_funcao == 1) {
    Executa_funcao = 0; 
    send_trama(); // ou algo parecido...
}

Isto é uma solução.

Quanto às interrupções, o que acontece é

Sempre que entras numa interrupção, as outras são desactivadas, quando essa interrupção terminar, as interrupções que estiveram por ser atendidas, serão atendidas consoante a prioridade que têm (isto está no manual do ATmega). Se houver interrupções que acontecem várias vezes enquanto as interrupções estão desactivadas, apenas a última será atendida.

Por exemplo, imagina que enquanto estás dentro da interrupção do timer, recebes a string "abc". Quando a ISR do timer terminar e a interrupção da porta série ser atendida, ela apenas vai receber o c da string.

Agora, o que também pode estar a acontecer é que seja o compilador que não permite isto das interrupções, mas seja possível activar ou desactivar isso. Mas diria que os trabalhos extra que isso acarreta não vale a pena.

Outra coisa que podes usar é algo assim:

unsigned long tempo=0;

setup();

loop() {

if (millis() - tempo >= 500) { //meio segundo = a 500 milisegundos
    tempo = millis(); 
    send_trama();
}

Dependendo do código que vais estar a correr, acredito que consigas enviar a trama de 500 em 500 milisegundos, ou muito próximo.
Ainda outra solução (que não aconselho), será tentares fazer um loop de espera

volatile unsigned long espera = 0; 

... 

for (espera = 0; espera < 65000; espera++); //isto prende o programa aqui X tempo. Podes calcular quanto fica ou ver num osciloscopio, por exemplo. 
//Mas o compilador também pode optimizar o código e cortar esta parte. Depende dos settings no compilador... e isso não é simples de alterar no Arduino. 
//Normalmente o volatile faz com que isso não aconteça... mas só experimentando.

Boas,

em relação as interrupções era bem o que eu tinha percebido ... desde já obrigada.

em relação a usar "for" para espera também já tinha tentado com "nop" e sem resultados práticos ...

Timing

The Arduino delayMicroseconds() function creates the shortest delay possible from within the Arduino language. The shortest delay possible is about 2 us (microseconds).
For shorter delays use assembly language call 'nop' (no operation). Each 'nop' statement executes in one machine cycle (at 16 MHz) yielding a 62.5 ns (nanosecond) delay.
asm("nop\n\t");

asm("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); \ gang them up like this

o meu problema é que se usar as flags é que vai dar barraca ou então tenho que usar uma array onde vou guardar vários tempos porque eu tenho que ter vários delay na loop :frowning:

tipo
loop()
{

todos os 10 minutos enviar as entradas para a web para preencher uma bd em mysql
todos 60s executar uma treta qualquer
todos 15s executar uma treta qualquer
todos 25s executar uma treta qualquer
todos 120s executar uma treta qualquer

e mesmo assim no max todos os 500ms tenho que enviar ordens a minha placa de E/S para ela não resetar ... se conheceres alguma lib
que possa gerir assim o tempo eu aceito

Boas,

parece que me vou safar com esta lib.

http://playground.arduino.cc//Code/Timer

cpts

Zé tretas

Ok...

vais ter um RTC?
Com esses intervalos, quase que vale a pena. :slight_smile:

Boas,

Não estava previsto usar um RTC, mas tenho ali 2 ou 3 ... só não consigo perceber o que ganharia com ele ... também da para ele gerar interrupções ?

O RTC (DS1307) tem a possibilidade de criar um alarme (creio) e também de gerar uma onda quadrada de 1Hz. Se ligares essa onda quadrada a um pino e configurares uma interrupção por mudança de estado, tens uma interrupção de 500 em 500 ms.

Essa será uma hipótese, creio. Também podes... criar um oscilador com um op-amp e ajustar a frequência para criares isso sem duty cycle. Isto é para um projecto que depois vais passar para uma placa própria (ou seja, não no Arduino), ou é algo que depois vais deixar a correr no Arduino? Assim podes talvez ligar um outro cristal e usar esse em vez do de 16MHz do Arduino para criar temporizações. :\

Queres explicar um pouco mais a aplicação para ver se saem mais sugestões?