Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:
Buenas a todos.
Tengo una duda, por llamarlo de alguna forma, o necesito mas bien consejo sobre como he de programar :p , el tema es que tengo una plaquita que mueve un motor que arrastra una carga, encima de esa carga va montado un detector de inducción de estos que ven chapas metálicas, el caso es que el trasto se mueve a 1 m/s, el tamaño de las chapas es de aproximadamente 5 cm.
El detector está conectada a una entrada de interrupción, cuando veo las chapas según las condiciones del programa hago unas operaciones, o bien sumo un contador, o resto, o decelero el motor, o acelero, paro...
El tema es que por un lado para saber que tengo que hacer tengo bastantes bucles IF metidos al hacer la interrupción, lo programo con CCS (Lenguaje C) y el tema es que cuando salta la interrupción leo el Puerto B, compruebo que entrada a provocado la interrupción, deshabilito las interrupción globales, hago todas las comprobaciones mediante if, si voy en un sentido y una variable toma tal valor acelero o si toma este otro decelero... y así bastantes condiciones, actúo según estas y por ultimo habilito interrupciones y salgo de la interrupción.
Claro el tema es que si pillo una chapa bastante alejada de las otras parece que trabaja bien (cada chapa esta a una distancia distinta a las otras, no guardan simetría ni nada por el estilo), ahora si hay dos chapas bastante cercanas la cosa cambia porque se salta detecciones.
He comprobado que el detector lee porque he metido un led a la entrada de la interrupción y se le ve lucir, por descarte imagino que el problema esta en la velocidad, he metido un micro 16f877 al que le he subido a un cristal de 20MHZ, pero aun así falla, yo pensé que con estas velocidades el tamaño del código de dentro de la interrupción no influiría demasiado, pero al parecer debe de ver una chapa, entra en la interrupción y antes de acabar el código y volver a habilitar las interrupciones globales ve la siguiente, con lo cual se la salta.
La distancia entre chapas es de aproximadamente 1 m en el peor de los casos es decir que hay como 1 seg. entre una y otra detección, pensé que a 20MHz era tiempo mas que suficiente, puesto que he trabajado con controladores de Motion y servos industriales y a 1500 Rpm eran capaces de detectar la fase Z del encoder y resetear el contador en cada vuelta, usando interrupciones, es decir capaz de detectar y gestionar 25 interrupciones (y reseteo de contadores) en cada segundo.
Bueno, al tajo, el tema es que he estado pensando la forma de reducir el tiempo de ejecución del código y no termina de mejorar, por un lado se me plantea la duda si quizá el tema de habilitar y deshabilitar las interrupciones provoca un retraso grande, no debería porque no se que hará el compilador pero en principio en lenguaje del PIC solo es cambiar un bit, por otro lado no termino de ver la forma de hacer saltos desde la interrupción, es decir si fuera en lenguaje del PIC, bastaría con ver cual es el bit que hace saltar la interrupción y mandar el puntero a una línea de programa y listo, ahora bien, en C eso se supone que no se debe de hacer, que no es bueno lanzar GOTO a tal linea, además no veo la forma de saltar a una línea de programa con C y luego devuolver el return de la interrupción, total que estoy liado, ¿se os ocurre alguna forma de sacar las comparaciones de la interrupción para hacer el código más rapido? es fundamental que nada más ver la interrupción ejecute las comparaciones porque en ocasiones esto manda que pare el motor, y claro, lo suyo es tener repetitibilida, es decir veo chapa e inmediatamente paro el motor, lo de veo chapa, lo registro, hago lo que estaba haciendo y cuando toque paro el motor no es valido, porque la diferencia en las paradas va a ser muy grande
¿suponer que el problema es por la longitud del código de la interrupción no es erróneo verdad?
Gracias a todos
Mira el número de ciclos que te tarda en ejecutar el camino más largo y haz cálculos... (lo hace el propio compilador). Así sales de dudas.
El tema es que por un lado para saber que tengo que hacer tengo bastantes bucles IF metidos al hacer la interrupción, lo programo con CCS (Lenguaje C) y el tema es que cuando salta la interrupción leo el Puerto B, compruebo que entrada a provocado la interrupción, deshabilito las interrupción globales, hago todas las comprobaciones mediante if, si voy en un sentido y una variable toma tal valor acelero o si toma este otro decelero... y así bastantes condiciones, actúo según estas y por ultimo habilito interrupciones y salgo de la interrupción.
No es necesario que deshabilites las interrupciones, ya que estas se deshabilitan automáticamente al servir una interrupción, y se habilitan al finalizar.
Claro el tema es que si pillo una chapa bastante alejada de las otras parece que trabaja bien (cada chapa esta a una distancia distinta a las otras, no guardan simetría ni nada por el estilo), ahora si hay dos chapas bastante cercanas la cosa cambia porque se salta detecciones.
He comprobado que el detector lee porque he metido un led a la entrada de la interrupción y se le ve lucir, por descarte imagino que el problema esta en la velocidad, he metido un micro 16f877 al que le he subido a un cristal de 20MHZ, pero aun así falla, yo pensé que con estas velocidades el tamaño del código de dentro de la interrupción no influiría demasiado, pero al parecer debe de ver una chapa, entra en la interrupción y antes de acabar el código y volver a habilitar las interrupciones globales ve la siguiente, con lo cual se la salta.
Mi impresión es que no sucede lo que te imaginas, ya que con 20 MHz el ciclo de reloj es de 50 nS (ciclo de máquina 200 nS). En un milisegundo (mS) se ejecutan algo así como 5000 instrucciones (hay instrucciones que consumen dos ciclos de máquina). No creo que tu interrupcción consuma más de 20 ó 40 mS en ejecutarse. Así que yo no buscaría por aquí el problema.
Para visualizar cuando entra en la interrupción programa lo siguiente (PINA conectado a una resitencia y un led):
SI (PINA ){
PINA(nivel_bajo);
} else {
PINA(nivel_alto)
}
La distancia entre chapas es de aproximadamente 1 m en el peor de los casos es decir que hay como 1 seg. entre una y otra detección, pensé que a 20MHz era tiempo mas que suficiente, puesto que he trabajado con controladores de Motion y servos industriales y a 1500 Rpm eran capaces de detectar la fase Z del encoder y resetear el contador en cada vuelta, usando interrupciones, es decir capaz de detectar y gestionar 25 interrupciones (y reseteo de contadores) en cada segundo.
Bueno, al tajo, el tema es que he estado pensando la forma de reducir el tiempo de ejecución del código y no termina de mejorar, por un lado se me plantea la duda si quizá el tema de habilitar y deshabilitar las interrupciones provoca un retraso grande, no debería porque no se que hará el compilador pero en principio en lenguaje del PIC solo es cambiar un bit, por otro lado no termino de ver la forma de hacer saltos desde la interrupción, es decir si fuera en lenguaje del PIC, bastaría con ver cual es el bit que hace saltar la interrupción y mandar el puntero a una línea de programa y listo, ahora bien, en C eso se supone que no se debe de hacer, que no es bueno lanzar GOTO a tal linea, además no veo la forma de saltar a una línea de programa con C y luego devuolver el return de la interrupción, total que estoy liado, ¿se os ocurre alguna forma de sacar las comparaciones de la interrupción para hacer el código más rapido? es fundamental que nada más ver la interrupción ejecute las comparaciones porque en ocasiones esto manda que pare el motor, y claro, lo suyo es tener repetitibilida, es decir veo chapa e inmediatamente paro el motor, lo de veo chapa, lo registro, hago lo que estaba haciendo y cuando toque paro el motor no es valido, porque la diferencia en las paradas va a ser muy grande
¿suponer que el problema es por la longitud del código de la interrupción no es erróneo verdad?
Gracias a todos
En C también puedes colocar etiquetas y ejecutar la instrucción GOTO. No creo que tu problema sea el tiempo de ejecución de la interrupción, pues esta podría ejecutarse 100 veces sin interferir el programa. .....
Probablemente el problema sea debido al tiempo de respuesta del detector, su histeresis etc.
Si es un detector inductivo comenrcial convencional no está preparado para enviar pulsos a mucha frecuencia...
En vez de probar si funciona bien con un LED conectalo a un osciloscopio, para ver si las señales suben y bajan bien.
Este fragmento de código lo uso para leer un encoder y calcular la posición y la velocidad, es lo más óptimo que he podido conseguir://=============================================================================
// interrupcion de cambio de estado del puerto B, cuenta pulsos de encoder * 4
#int_RB
RB_isr()
{
static int8 EncoderAnt;
int8 Encoder,tmp, tmp2;
// Esto es para direccionar los bits individuales
#bit tmp_6 = tmp.6
#bit tmp_4 = tmp.4
Encoder=input_b (); // Lee todas las entradas de encoder a la vez
// Procesa el encoder Derecho, de esta forma rara usa 4 instrucciones menos
tmp=(EncoderAnt>>1)^Encoder;
tmp2=(Encoder^EncoderAnt)&0xC0;
if (tmp2==0xC0) goto NOEncoderD; // imposible, son dos pulsos
if (tmp2==0x00) goto NOEncoderD; // no se ha movido
if (tmp_6) PulsosD--;
else PulsosD++;
NoEncoderD:
// Procesa el encoder izquierdo de la misma forma
tmp2=(Encoder^EncoderAnt)&0x30;
if (tmp2==0x30) goto NOEncoderI; // imposible, son dos pulsos
if (tmp2==0x00) goto NOEncoderI; // no se ha movido
if (tmp_4) PulsosI--; // he correjido el cableado
else PulsosI++;
NoEncoderI:
EncoderAnt=Encoder;
}
//=============================================================================
// interrupcion cada 6.55 ms
#int_TIMER0
TIMER0_isr()
{
PosicionI+=PulsosI; // Calcula la posición absoluta
VelocidadI=PulsosI*2; // Escalado: Para encoder hasta 5khz * 4 cuenta 131 pulsos en 6.55 ms
PulsosI=0;
PosicionD+=PulsosD; // Calcula la posición absoluta
VelocidadD=PulsosD*2; //Escalado
PulsosD=0;
ProcesaPID=1; // Ya hay datos recientes para procesar en el bucle main()
} // Esto tardaba 12 us en completarse
main()
{
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_128); // genera una interrupcion cada 6,55 ms
// Time = ((256 * 4) / 20.000.000) * 128 = 6.55 ms
enable_interrupts(INT_RB);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
set_tris_b(TRISB); //trisB para leer el encoder, 4 entradas
while (TRUE) // Bucle principal
{
if ( ProcesaPID) // Hay datos frescos, procesarlos
{
ProcesaPID=0;
// procesar aqui
}
// resto del programa
}
}
Buenos dias a ambos
En primer lugar gracias por responder, He intentado ver el camino mas largo y que el compilador me de el tiempo maximo en recorrerlo pero o mi compilador es viejo o soy muy torpe (que lo soy) y no he sabido encontrar el dato, uso el CCS C COMPILER 4.038.
Lo de deshabilitar las interrupciones lo hago porque en algun sitio habia leido que era necesario hacerlo, pensé que en C tambien habria que hacerlo, si no es necesario lo quitaré y lineas que me ahorro.
Hombre lo de que no suceda lo que me imagino fué mi primer pensamiento por lo que comentaba antes de controladores industriales que habia usado, pero no sé, quizá pense que estos correrian con CPUs mas potentes y optimizadas. El problema es que aunque pueda hacer 5000 ticks o pasos, no puedo evaluar el tiempo porque al hacerlo en C no se realmente cuanto código nativo mete el compilador por cada intruccion de C.
La verdad que la idea del led es muy buena, meteré al inicar la interrupcion una activacion del led y al salir la apago a ver si veo algo, aunque si luce 20 ms no creo que vea siquiera el parpadeo. pero claro, el hecho de que tarde mucho en salir de interrupcion en caso de que el led quede mucho tiempo activo tampoco me garantiza que sea lento sino que a lo mejor he programado algo mál dentro de la interrupcion... Voy a probar.
Gracias.