Como soy novato en esto de los PIC primero haré algunas pruebas para ver que funciona lo hecho hasta ahora…
Hoy toca hacer de Frankenstein. Voy a transplantar el cerebro de otro robot a Reciclator 1. Voy a conectar las 8 señales de control (4 de encoders, 2 de dirección y 2 de velocidad PWM) un prototipo tengo hecho y funcionando para hacer algunas pruebas. El otro robot está basado en Intel (mi especialidad) y la utiliza hardware para los contadores de los encoders. De esta forma sé que el programa es correcto y puedo probar el hardware. Como estoy empezando con PIC y CCS no me atrevo a poner el PIC y programar porque si no funciona me costará mucho determinar si el problema es del programa o del hardware. De esta forma ahorro mucho tiempo en pruebas.
Las pruebas han sido un éxito… He podido controlar los motores y medir con precisión su velocidad. Con el frecuencímetro observo que los encoders proporcionan 4600 Hz a 5V que corresponden a 9200 Rpm del motor. Estos datos ya los había medido anteriormente, pero ahora tiene que medirlos el robot.
Este es el borrador del esquema de conexionado con el PIC:
Ahora hay que programar…
Para la decodificación de los encoders voy a aprovechar la interrupción RB_isr() que se produce cada vez que cambia el estado de los bits 7-4 del puerto B del PIC. De esta forma solo tengo que comprobar como están esos bits e incrementar o decrementar el contador los contadores en consecuencia. Además tengo 4 interrupciones (4 cambios de estado en el encoder) por cada pulso, por lo que multiplico por 4 la resolución original de 30 pulsos por revolución.
Los pulsos de los encoders siguen la secuencia de la foto:
Las flechas de los flancos pertenecen al sentido «contar». En el sentido «descontar» van al revés. Generalmente solo se usa uno de los 2 flancos de una fase para contar y el estado de la ora fase determina la dirección. En este caso podríamos usar el flanco descendente de B para contar si A esta a 0 o para descontar si A está a 1. Si se usan los dos flancos de una fase se multiplica por 2 la resolución del encoder y si se usan los 2 flancos de una fase y los 2 de la otra la resolución se multiplica por 4. El encoder que uso es de 30 pulsos por revolución, pueden medirse hasta 120 pulsos por revolución
Para decodificar la información de los encoder compongo un número de 4 bit con el estado anterior de las fases A y B del encoder y el estado actual:
Hex Anterior AB Actual AB Pulsos XOR XOR AanteriorBactual
00 00 00 NADA 00 NO IMPORTA
01 00 01 - 01 1
02 00 10 + 10 0
03 00 11 PERDIDO 11 NO IMPORTA
04 01 00 + 01 0
05 01 01 NADA 00 NO IMPORTA
06 01 10 PERDIDO 11 NO IMPORTA
07 01 11 - 10 1
08 10 00 - 10 1
09 10 01 PERDIDO 11 NO IMPORTA
0A 10 10 NADA 00 NO IMPORTA
0B 10 11 + 01 0
0C 11 00 PERDIDO 11 NO IMPORTA
0D 11 01 + 10 0
0E 11 10 - 01 1
0F 11 11 NADA 00 NO IMPORTA
NADA significa que el encoder no se ha movido, no hay nada que cambiar. PERDIDO significa que han pasado 2 pulsos respecto del estado anterior. Esto puede ser interesante si se lee el estado de los encoder mediante una interrupción periódica. Como yo lo leo solo CUANDO CAMBIA (con RB_isr()) el caso NADA no puede darse, no genera interrupción y el caso PERDIDO es imposible: cualquier cambio genera interrupción y no pueden perderse pulsos (a no ser que lleguen tan rápido que no le de tiempo a la interrupción a procesarlos, en mi caso tardo 12 us puedo procesar hasta 833 Khz). Utilizando la función XOR entre el estado anterior y el actual puedo determinar cuando he de contar o descontar y cuando no hacer nada. 00 es NADA, 11 es PERDIDO y otro estado contar o descontar según el estado de A y o B del encoder. Para contar o descontar hago un XOR de de la fase A anterior con B actual: si es 0 es CONTAR si es 1 es DESCONTAR.
A la vista de la tabla de verdad hay muchas otras formas de hacerlo, cada una con más o menos código. Puede contarse siempre que haya un 2,4,B o D y descontarse con 1, 7, 8 o E pero las comparaciones y saltos son menos óptimos que las máscaras…
Ahora llega el problema… Para ver la velocidad tengo que medir los pulsos en un intervalo de tiempo. A 5,5 V el motor gira a 10000 RPM que son 166,6 RPs. Si el intervalo es de 1 segundo tendré un máximo de 30 * 4 * 166,6 = 20000 Pulsos en un segundo. No está mal, pero solo tendré una medida ¡cada segundo!. Es muy poco. Si quiero ejecutar el proceso de regulación cada milisegundo el motor solo se ha movido, a la máxima velocidad, ¡20 pulsos!. Es muy poca resolución para hacer una buena regulación. Hay que llegar a una solución de compromiso entre lectura del encoder (cuanto más milisegundos mejor) y la regulación (cuanto menos milisegundos mejor). Voy a hacer la regulación cada 6,55 ms que corresponde a unas 152 veces por segundo. En ese tiempo al encoder le da tiempo a enviar hasta 131 pulsos. Es poco, pero bastante para regular…
La regulación es el clásico PID aunque yo solo uso PD por ahora:
ErrorVI=ConsignaVI-VelocidadI;
SalidaVI+=((ErrorVI)*KVprop) + ((ErrorVI-ErrorAntVI)*KVder);
ErrorAntVI=ErrorVI;
KVprop y KVder son los parámetros del PID. Habrá que ajustarlos para que no oscile y alcance la consigna con rapidez. Uso enteros de 16 bit con signo para los cálculos, pero en punto fijo 11.5 es decir 11 bits de entero y 5 bits de decimal. Así los cálculos son más rápidos.
Estos son los fuentes de lo hecho hasta ahora: Fuentes C CCS. Están bastante comentados, no es muy dificil seguirlos.
Este es Reciclator1 listo para pintar líneas, no para seguirlas. Así veo si oscila el PID y si las 2 ruedas mantienen sus velocidades. Como innovación he aumentado la tensión de la batería a 9,6 V usando otras 2 células para tener más margen de tensión y he incorporado un acelerómetro basado en el ADXL05 (es un componente obsoleto que tenía en una caja, a saber de donde lo he sacado), aunque el programa no lo usa todavía. No he usado el PIC 16F877 sino que pongo un 16F876 con un zócalo adaptador de 28 a 40 patas.