No se actualizan valores del contador TCNT1 del Timer 1 [Solucionado]

Hola,

estoy intentando medir el duty-cycle de un pulso detectando los flancos de subida y bajada mediante accediendo al contador interno del timer 1, TCNT1, pero no se me actualizan los valores y realmente nose porque.

La cuestion es que utilizo el acceso al contador del timer 1 en otros algoritmos y funciona, es decir, se incrementa los valores bien.

He repasado el codigo unas cuantas veces pero no encuentro nada raro, aunq seguramente habra algo que haga que dichos valores no se actualicen, pero no lo veo.

Haber si cuatro ojos ven mas que dos, gracias.

const int Pin_in_ch1 = 2; // Pin entrada ch1 (direccion)

char giro = 0;
char giro_ant = 0;
char Dgiro = 0;
unsigned int Duty_giro = 0;

unsigned int Tini = 0;
unsigned int Tact = 0;
unsigned int RestoT = 0;
float Tclk = 0.0625; // en microsegundos

void setup() {

  Serial.begin(9600);
  pinMode(Pin_in_ch1,INPUT);
  
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  TCCR1B = _BV(CS10); // No pre-escaler
}

void loop() {
  
  
  giro = digitalRead(Pin_in_ch1);
  
  Dgiro = giro - giro_ant; // diferencia de valores leidos
  
  if(Dgiro==1) // flanco de subida
  Tini = TCNT1;
  
  if(Dgiro == -1) // flanco de bajada
  {
  Tact = TCNT1;
  
    if(Tact < Tini) // para los desbordes del TCNT1
      {
        //RestoT = (Tact + 65535) - Tini;
        Duty_giro = ((Tact + 65536) - Tini)*Tclk;
      }
    else
      {
        //RestoT = Tact - Tini;
        Duty_giro = (Tact - Tini)*Tclk;
      }
 }
 
  giro_ant = giro;
  
  Serial.print ("DIFERENCIA CONTADOR : ");
  Serial.println (RestoT);
  
  Serial.print ("Duty CH1 : ");
  Serial.println (Duty_giro);
  Serial.println (" us. ");

}

valores TCNT1.png

y no usas una interrupción para medir?

Aca tienes como hacerlo al Estilo Nick Gammon

Ya las estoy utilizando, concretamente las dos externar (INT0 e INT1, pines 2 y 3) pero para medir el periodo de dos canales.

En tu código no veo que inicialices ninguna interrupción. De modo que CREES que las usas.
Usas los pines 2 y 3, pero no veo que uses

attachInterrupt(digitalPinToInterrupt(pin2), funcion1, RISING);
attachInterrupt(digitalPinToInterrupt(pin3), funcion2, FALLING);

Perdona que puntualice , este codigo, es una funcion para ese fin, es decir, medir el duty cycle, que a su vez es PARTE de un programa. Las interrupciones, evidentemente no aparecen aqui, como te he comentado anteriormente las estoy utilizando para medir el periodo de dos canales de transmision.

Por simplificar, he cogido la funcion y la estoy ejecutando sola, en un archivo nuevo, en vez de estar desactivando partes de codigo del programa entero.

Tambien puedes decir, por que no lo ejecutas dentro del programa y vas desactivando cosas para ver donde falla? o que es lo que hace que falle?, tambien lo he probado y al final opte por sacarlo fuera y probarlo aparte, y claro la sorpresa a sido ver que no funciona :(.

Seguramente sean algun error de estos chorras que cuesta verlo y que te hacen perder mucho tiempo.

Porque repaso el codigo una y otra vez y no veo ninguna anomalia, pero seguro que la tiene que haber, porque no se actualizan los valores del contador TCNT1. He buscado por la red pero no encuentro nada.

Al menos yo, no puedo darte una devolución como corresponde si no veo todo el código.
De modo que pensar en que esta funcionando mal cuando veo las cosas parcializadas, es dificil, al menos para mi.

Ok, te lo adjunto. Esta estructurado en pestañas. Te adjunto una captura de pantalla tambien.

/////////// DEFICIION DE PINES //////////
const int Pin_in_ch1 = 2;
const int Pin_in_ch2 = 3;

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

////////////// VARIABLES TIPO "VOLATILE" INTERRUPCIION CH1 (PIN 2) //////////
volatile boolean primero_ch1;
volatile boolean disparo_ch1;
volatile unsigned long tiempo_inicial_ch1;
volatile unsigned long tiempo_final_ch1;
////////////////////////////////////////////////////////////////////
volatile unsigned long overflowCount; 
////////////// VARIABLES TIPO "VOLATILE" INTERRUPCIION CH2 (PIN 3) //////////
volatile boolean primero_ch2;
volatile boolean disparo_ch2;
volatile unsigned long tiempo_inicial_ch2;
volatile unsigned long tiempo_final_ch2;
////////////////////////////////////////////////////////////////////


///////// VARIABLES PARA LEER CH1 /////////////////

char dato_ch1 = 0;
char dato_ant_ch1 = 0;
char Ddato_ch1 = 0;

unsigned int Tini = 0;
unsigned int Tact = 0;
unsigned int DT = 0;

const float Tclk = 0.0625; // microsegundos



ISR(TIMER1_OVF_vect){
  
  overflowCount++;
  
  }
  


void setup() {
  
  Serial.begin(9600);
  
  pinMode(Pin_in_ch1,INPUT);
  pinMode(Pin_in_ch2,INPUT);
  
  TCCR1A = 0;
  TCCR1B = 0;
  TIMSK1 = _BV(TOIE1); // interrupcion desbordamiento
  TCNT1 = 0;
  TCCR1B = _BV(CS10);  //No pre-escaler
   
  
  PrepararInterrupcionPD2();
  PrepararInterrupcionPD3();

  
}

void loop() {
  
  ///////////// CALCULO DE FRECUENCIA Y PERIODO EN CH1 /////////////
  if(!disparo_ch1)
  return;
  
  unsigned long tiempo_transcurrido_ch1 = tiempo_final_ch1 - tiempo_inicial_ch1;
  float freq_ch1 = F_CPU / float(tiempo_transcurrido_ch1);
  float T_ch1 = float(tiempo_transcurrido_ch1*62.5e-6); // expresado en ms
  
  ///////////// CALCULO DE FRECUENCIA Y PERIODO EN CH1 /////////////
  
  // ###############################################################
  
  ///////////// CALCULO DE FRECUENCIA Y PERIODO EN CH2 /////////////
  if(!disparo_ch2)
  return;
  
  unsigned long tiempo_transcurrido_ch2 = tiempo_final_ch2 - tiempo_inicial_ch2;
  float freq_ch2 = F_CPU / float(tiempo_transcurrido_ch2);
  float T_ch2 = float(tiempo_transcurrido_ch2*62.5e-6); // expresado en ms
  
  ///////////// CALCULO DE FRECUENCIA Y PERIODO EN CH2 /////////////


  
  Leer_Canales();

  PrepararInterrupcionPD2();
  PrepararInterrupcionPD3();

  
  Serial.println("###############################");
  Serial.print(" Frecuencia CH1: ");
  Serial.print("  ");
  Serial.println(freq_ch1);
  Serial.print(" Periodo CH1: ");
  Serial.print("  ");
  Serial.println(T_ch1);
  Serial.println("###############################");
  Serial.print(" Frecuencia CH2: ");
  Serial.print("  ");
  Serial.println(freq_ch2);
  Serial.print(" Periodo CH1: ");
  Serial.print("  ");
  Serial.println(T_ch2);
  Serial.println("------------------------------");

  Serial.print(" Cuentas CH1: ");
  Serial.print("  ");
  Serial.println(DT);
  Serial.println("------------------------------");

}
void PrepararInterrupcionPD2(){
  
  EIFR = _BV(INTF0);
  primero_ch1 = true;
  disparo_ch1 = false;
  attachInterrupt(0,InterrupcionPD2,RISING);
  }
void PrepararInterrupcionPD3(){
  
  EIFR = _BV(INTF1);
  primero_ch2 = true;
  disparo_ch2 = false;
  attachInterrupt(1,InterrupcionPD3,RISING);
  }
void InterrupcionPD2(){
  
  unsigned int contador_ch1 = TCNT1;
  
  if(disparo_ch1)
  return;
  
  if(primero_ch1)
  {
    tiempo_inicial_ch1 = (overflowCount << 16) + contador_ch1;
    primero_ch1 = false;
    return;
  }
  
    tiempo_final_ch1 = (overflowCount << 16) + contador_ch1;
    disparo_ch1 = true;
    detachInterrupt(0);
  }
void InterrupcionPD3(){
  
  unsigned int contador_ch2 = TCNT1; 
  
  if(disparo_ch2)
  return;
  
  if(primero_ch2)
  {
    tiempo_inicial_ch2 = (overflowCount << 16) + contador_ch2;
    primero_ch2 = false;
    return;
  }
  
    tiempo_final_ch2 = (overflowCount << 16) + contador_ch2;
    disparo_ch2 = true;
    detachInterrupt(1);
  }
void Leer_Canales(){
  
  dato_ch1 = digitalRead(Pin_in_ch1);
  
  Ddato_ch1 = dato_ch1 - dato_ant_ch1;
  
  if(Ddato_ch1 == 1)
  {
    Tini = TCNT1;
  }
  if(Ddato_ch1 == -1)
  {
    Tact = TCNT1;
  
  if(Tact < Tini)
  {
    DT = (Tact + 65536) - Tini;
  }
  else
  {
    DT = Tact - Tini;
  }
 }
  dato_ant_ch1 = dato_ch1;
  }

Alguna sugerencia sobre como esta el codigo??, estructura??, llamadas de las funciones??. please!!

Bueno analizo lo que he visto con señales simuladas de 10Hz, 100Hz, 1000Hz y 10Khz.


Los 4 casos fueron presentados correctamente.
Habría que trabajar en la representación del período porque no puedo leer 10khz en frecuencia y 0.10 en período
0.10 que? y porque no ponerlo en useg?

Por lo demás luce bien. Lo noto poco preciso pero voy a compararlo con la version de Nick Gammon.

Sigo sin entender que falla tiene tu código.

A ver, el problema es a la hora de calcular el , " DUTY CYCLE ", lo demas fuinciona bien, periodo y frecuencia, OK.

Lo repito de nuevo, en la funcion Leer Canales() pretendo calcular el ciclo de trabajo, es decir, la duracion del pulso de la señal proveniente de una emisora RC, ok?.

explico el proceso:

1.) Leo pin

2.) Resto valores leidos --> Dif_valor = dato - dato_ant

3.) si Dif_valor == 1 // flanco de subida

tiempo inicial = TCNT1;

4.) si Dif_valor == -1 // flanco de baja

tiempo actual = TCNT1

!!teniendo en cuenta el desborde del contador!!

5.) si tiempo actual es < tiempo inicial

nuevo tiempo actual = tiempo actual + 65536(desborde)

diferencia de tiempo = nuevo tiempo actual - tiempo inicial

si no

diferencia de tiempo = tiempo actual - tiempo inicial

dato anterior = dato

El problema esta en que cuando accedo al TCNT1 no se incrementa ni responde a las acciones del mando. Porque cuando vario la palanca de giro por ejemplo el tiempo actual deberia de variar, no?? y el otro igual, porq no puede ser que siempre pille el flanco de subida en el mismo instante y ademas lo que lee es incorrecto.

vamos a ver, de una forma analitica

si el contador cuentas de 0 a 65535, es decir 65536 cuentas y se desborda, vuelve a empezar desde cero

en hacer un ciclo tarda 4.096 mseg (sin pre-escaler) --> 65536* 62.5e-9

si divido el numero de cuentas por lo que tarda en desbordar el contador, nos da que, para 1 mseg tiene que haber contado 65536/4= 16384 ticks.

Ahora, 0.5 mseg = 16384 / 2 = 8192 ticks

en estado de reposo del mando --> Duty-cyle = 1.5mseg ---> nº de ticks deberia de ser 16383 + 8192 = 24575.

Con todo esto quiero decir que la diferencia de ticks o cuentas del TCNT1 tendria que variar de 16383(1 mseg) a 32786(2 mseg), en funcion de la posicion del mando.

OK??

LA cuestion es tambien que cuando accedo al cantador para calcular periodo y frecuencia funciona bien y no se por que en esta funcion no??

ni siquiera ejecutando la funcion sola. es muy muy raro y por mas que reviso el codigo no encuentro el fallo joder !!.

Yo estuveo practicando con la rutina de Nick Gammon y es extraordinariamente precisa.
Mas tarde comprobaré lo que me dices.

Alguna sugerencia please!?!?!?

Hola. La verdad es que cuando expusiste tu problema no llegué a entender qué intentaba explicar. Ahora lo he releído, y lo mismo digo una burrada, pero como parece que estás desesperado he revisado tu primer código.
¿No te referirás a que el resultado de esta expresión te da siempre cero?

Duty_giro = (Tact - Tini)*Tclk;

Si es eso, ten en cuenta que la variable duty_giro es entera, y que los operandos también, excepto tclk que es float y convertido a int es cero, osea que estás multiplicando por cero.

Hola noter, gracias por contestar, en cuanto a la dessesperacion, si, tienes razon, llevo un monton de tiempo atascado con esto y me esta empezando a minar la moral..jejej.

En cuanto a lo que comentas, soy consciente de lo que dices, pero ese no es el problema. No paro de ejecutar el codigo y la cuestion es, a parte de que los valores del contador no varian de una forma correcta, es decir, cuando acciono las palancas del mando (traccion y giro), deberian de variar las cuentas del contador en funcion de ello, pero parece como que no hace una buena lectura del pulso porq cuando saco por pantalla los valores leidos y su diferencia, desde que se encuentra un flanco de subida que la resta de valores tiene que ser
Ddato = dato_ch1 - dato_ant_ch1 = 1, las siguientes restas tienen que ser 0, y sin embargo pasa de 1 a -1.

como si del flanco de subida se pasara directamente al flanco de bajada sin recorrer todo el estado positivo del pulso.

No se si me he explicado bien??.

En el antepenultimo post creo que esta bien explicado, o a lo mejor no me explico bien.

Mira yo hices este pequeño cambio y ahora siempre me da resultados

unsigned long DT = 0;

porque esto no me gustó

DT = (Tact + 65536) - Tini;

Antes los valores oscilaban y ahora marcan bien.

Creo que ahora sí acabas de decir el problema. Efectivamente, puede ser que no se mantenga la condición que has señalado como flanco de subida, y sospecho que ese sistema de la resta para detectar el cambio de estado no es muy adecuado. Prueba este código a ver si es lo que buscas y funciona correctamente. Si es así, te digo por dónde podría venir el error.

const int Pin_in_ch1 = 2; // Pin entrada ch1 (direccion)

char giro = 0;
char giro_ant = 0;
unsigned int Duty_giro = 0;

unsigned int Tini = 0;
unsigned int Tact = 0;
unsigned int RestoT = 0;

void setup() {

  Serial.begin(9600);
  pinMode(Pin_in_ch1,INPUT);

  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  TCCR1B = _BV(CS10); // No pre-escaler
}

void loop() {
  giro = digitalRead(Pin_in_ch1);
  if (giro != giro_ant) {
    if(giro) {// flanco de subida  Tact = TCNT1;
      Tini = TCNT1;
    }
    else {
      Tact = TCNT1;
      RestoT= Tact - Tini;
    }
    giro_ant = giro;
  }

  Serial.print ("DIFERENCIA CONTADOR : ");
  Serial.println (RestoT);
}

Por cierto...
Como ves, no hago nada especial si ha habido overflow del timer, porque todas las variables a calcular son unsigned int, y la resta nos va a dar resultado correcto aunque el sustraendo sea mayor que el minuendo. Osea que esta operación aparentemente descabellada es cierta si trabajamos con unsigned int.

usigned int a = 0;
unsigned int b = 0xFFFF;
unsigned int c = a - b;
Serial.print (c); // si no me equivoco debería imprimir 1 ;)

A mi en lo personal no me gusta que uses interrupciones para contar Período/Frecuencia y luego simplemente mires dentro del loop el flanco. Te estas perdiendo useg importantes.

No entiendo del todo lo que quieres decir,

lo de cambiar a "long" no creo que sea necesario, porque esa variable no va almacenar valores superiores a los que soporta. La diferencia de cuentas tendria que oscilar de unas 16383 mas o menos a 32768, que es el doble, es decir, las cuentas que van de 1 a 2 mseg, y con "unsigned int" que almacena de 0 a 65535 es suficiente verdad?

Y lo que no te gusto, es para corregir los desbordamientos que puedan ocurrir del contador, lo unico es hacer la suma en otra variable y despues sacar la diferencia, pero funciona igual de mal.

Me puedes explicar un poco mejor los cambios que propones??.

Vale noter, parece que vamos por buen camino!...jejej todavia no varian las cuentas en funcion de las ordenes del mando, pero por lo menos el valor que se obtiene en reposo es correcto, voy a seguir probando a partir de esto....y creo recordar que probe un fragmento de codigo parecido a este, pero no me funciono...seguro que no lo estaba haciendo bien del todo...jejejej.

haber si conseguimos resolverlo del todo!!

Muchas gracias !!