Expresate

Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:

Procblema con bucle...
 
Avisos
Vaciar todo

Procblema con bucle while()

12 Respuestas
3 Usuarios
0 Reactions
10.1 K Visitas
sdguez
Respuestas: 6
Topic starter
(@sdguez)
Active Member
Registrado: hace 16 años

Buenas a todos:
Os dejo un pequeño fragmento de código, pues no acabo de entender por qué a la hora de depurar el compilador hace un par de cosas, a mi juicio, un tanto raras.

Dentro de la función main(), he puesto un bucle while(1) para que se ejecute continuamente. Bien, dentro de este bucle hay básicamente dos cosas:
- Otro bucle while con la "chicha" del programa, controlada por una variable boolena
- Tres instrucciones (sei(),cli(),etc) para el control de las interrupciones.
Es decir:

bool inicio = false;

void main ()
{
DDRA = 0xFF; // set PORTA as output
PORTA = 0xff;
while(1)
{
while(inicio)
{
if (contador_s == 20) // Han pasado 20 seg.
{
contador_s=0;
if (regula== 1) salida++; // Incrementamos +1 registro del PuertoA.
if (regula== 2) salida--; // Decrementamos -1.....
PORTA = salida;// escribe registro en Puerto A.
}
if (contador_s2 == 5400) // Han pasado 90 minutos.
{
contador_s2=0;
-------> PORTA = 0xFF;// escribe registro en Puerto A.
}

// más código//

TCNT0 = 0x00; //Poner a 0 resgistro del Timer0
contador_s2=0;
contador_s3=0;
contador_s4=0;
}
sei();
TIMSK |= (1 << TOIE0); // Máscara interrupción timer0
cli();
} //del while(inicio)
sei();
TIMSK |= (1 << TOIE0); // Máscara interrupción timer0
cli();
}// del while (1)
} // del main

Pues bien, resulta que depurando todo paso a paso comienza con normalidad hasta alcanzar el bucle while(inicio). Como dicha variable está inicializada a "false" no se debiera ejecutar. No obstante resulta que se mete dentro del bucle, unas 10 instrucciones más abajo (la instrucción en concreto está señalada en el código con una flecha pr la izquierda) y yo creo que no llega a ejecutar dicha instrucción, le doy otro paso y vuelve al bucle while(inicio), ahora sí lo ignora y salta al final, a la instrucción sei() que está dentro de las llaves del while(1);

Acto seguido, mediante una interrupción:
ISR(IO_PINS_vect)
{
inicio = !inicio;
}

Por cierto, si escribo la instrucción así: "inicio !=;" me da error al compilar, es normal?.

El caso es que el boolean "inicio" pasa a true, se acaba la interrupción, continua con la ejecución de la función cli() y ahora entiendo que debería entrar en el bucle while(inicio), pero pasa olímpicamente del y continua ejecutando las tres últimas instrucciones cíclicamente.

Alguien sabe por qué sucede esto?.

Muchas gracias.

Responder
11 respuestas
bastian
Respuestas: 384
(@bastian)
Ardero
Registrado: hace 17 años

Hola!
Por cierto, si escribo la instrucción así: "inicio !=;" me da error al compilar, es normal?.

Si, es normal, por que estás asignando en ningún sitio (Donde guardas el resultado???)
TCNT0 = 0x00; //Poner a 0 resgistro del Timer0
contador_s2=0;
contador_s3=0;
contador_s4=0;
}//(El bucle while (inicio) lo cierras aquí!!)
sei();
TIMSK |= (1 << TOIE0); // Máscara interrupción timer0
cli();
} //del while(inicio)(En realidad lo cierras con la llave anterior!!)

El bucle while (inicio) no lo cierras donde tu crees....
TIMSK |= (1 << TOIE0); // Máscara interrupción timer0

Ese desplazamiento a izquierdas me chirria... si no recuerdo mal estas desplazando 1, TOIE0 veces a la izquierda... quizá debería ser al revés...
Comprueba eso y luego nos cuentas... 😉

Salu2!

Responder
beamspot
Respuestas: 1132
(@beamspot)
Noble Member
Registrado: hace 18 años

Lo del REGISTRO = (1<<VALOR) es la manera 'estándar' de GNU-GCC para AVR usada para definir posiciones de bits. Es decir, que si quiero activar el bit TCIE del registro TIMSK, lo que hago es poner TIMSK |= (1<<<TCIE). Para eso, en el header de definiciones se definen los valores de TCIE y de TIMSK, de tal manera que luego, cambiando simplemente el tipo de micro, las definiciones actualizan dichos valores y el 'portar' el código a un micro diferente es inmediado, aunque TCIE y TIMSK estén en posiciones diferentes. No olvidemos que al ser TCIE un valor #definido, el preprocesador del compilador sustituye el (1<<TCIE) por el valor en hexadecimal correspondiente. De esta manera queda mucho más legible el código.

A ver si puedo hacer un rato más para leer el código. Por cierto, ¿has probado a definir inicio como volátile? El optimizador del compilador te puede hacer muy malas pasadas si en los ISR sólo asignas valores a variables no definidas como tal, al 'optimizarlas' mediante el sencillo método de borrarlas directamente. También puedes probar a usar la optimización -O0 (o sea, nada de optimizar), malísima pero válida si tu programa es corto y quieres depurarlo en el simulador.

Responder
bastian
Respuestas: 384
(@bastian)
Ardero
Registrado: hace 17 años

Hola!
Lo del REGISTRO = (1<<VALOR) es la manera 'estándar' de GNU-GCC para AVR usada para definir posiciones de bits. Es decir, que si quiero activar el bit TCIE del registro TIMSK, lo que hago es poner TIMSK |= (1<<<TCIE). Para eso, en el header de definiciones se definen los valores de TCIE y de TIMSK, de tal manera que luego, cambiando simplemente el tipo de micro, las definiciones actualizan dichos valores y el 'portar' el código a un micro diferente es inmediado, aunque TCIE y TIMSK estén en posiciones diferentes. No olvidemos que al ser TCIE un valor #definido, el preprocesador del compilador sustituye el (1<<TCIE) por el valor en hexadecimal correspondiente. De esta manera queda mucho más legible el código.

No conocía esa forma de poner a uno o a cero...los AVR los tengo en tareas pendientes 😳 . Entiendo que (1<<TCIE) activa el bit TCIE en el registro y (0<<TCIE) lo desactiva... entonces el valor #definido de TCIE debe ser su posición en el registro ¿no? 🙄

Salu2!

Responder
beamspot
Respuestas: 1132
(@beamspot)
Noble Member
Registrado: hace 18 años

Bastian, si y no. (1<<TCIE) pone a uno dicho bit, pero para ponerlo a cero, lo que se hace es TIMSK &=~(1<<TCIE). TCIE está definido, como muy bien dices tú, como la posición que el bit ocupa en el registro del modelo seleccinado de micro.

La verdad, es que este sistema es muy cómodo y práctico, aunque un poco más largo de escribir. Sin embargo, una línea como

TIMSK = 0x3C;

es difícil de interpretar, mientras que algo así como

TIMS = (1<<TCIE1)|(1<<TOVF1)|(1<<TCA1);

queda mucho más claro aunque el resultado final (en el archivo .hex) sea exactamente el mismo. Y encima, cambiar de un (pongamos por ejemplo) M16 a un M32 es tan sencillo como irse a los 'settings' del AVRStudio y cambiar la referencia. No hace falta ni cambiar el io.h, ya que este tiene un monton de #ifdef que redirigen al m16io.h o al m32.h correspondiente. Vamos, una currada de headers, pero una 'vida más fácil' para los usuarios. Nada mal para ser gratuito todo.

Añado, voviendo al meollo de la cuestión, que todas las variables que se modifican dentro de las interrupciones deben ser definidas siempre y de manera sistemática como volatile. Y mejor si estas (que suelen ser globales), se ponen dentro de un bloque de definición claramente delimitado por sus correspondientes comentarios.

Por cierto, que nadie se olvide que las teclas se deben 'filtrar' (debounce) con un antirebotes. Para eso, las interrupciones en las entradas de tecla son desaconsejables (yo suelo leerlas con la interrupción del timer de sistema).

Responder
Página 1 / 3
Compartir: