Cuando el PIC no está "co-operando" ...
Usar un PIC es simple: escribir algo de código, quemarlo en el Flash-ROM, poner el PIC en el circuito de aplicación, suministrar voltaje, y trabaja — bueno, en general...
Para todos los amantes de la Ley de Murphy, acá siguen algunas ideas de todo lo que puede andar mal.
El PIC no funciona en absoluto
Causas posibles:
- El PIC no está bien insertado en el zócalo.
Suena ridículo, pero no es imposible poner un PIC al revés en un zócalo, torcer un pin o pescar un zócalo que no da buen contacto.
- Falta el voltaje operativo.
- Falla del oscilador.
Mi favorito.
- Un error típico de principiante consiste en olvidar de ajustar la palabra de configuración. Así en el PIC está seleccionado el oscilador RC, y esto obviamente no funciona sin el circuito externo correspondiente. Para evitar este error es recomendable siempre fijar la palabra de configuración en el código fuente.
- Un oscilador RC puede volverse inestable si R y C están fuera de los límites recomendados (3kΩ < R < 100kΩ, C > 20pF).
- Si se usa un cristal: para frecuencias arriba de 4 MHz se debería elegir el modo HS.
- La mayoría de los PIC 12Fxxx/16Fxxx tiene un rango limitado de frecuencia del oscilador si el voltaje operativo Vdd está por debajo de 4,5 voltios.
Si la velocidad y la exactitud no importan, recomendaría el uso del oscilador interno. Esto simplifica el diseño de la placa y da dos pines extra.
- El PIC permanece en estado RESET.
Además de la causa simple – que el pin /MCLR permanece "accidentalmente" en "low" – hay otras situaciones, no tan fácilmente visibles, que pueden detener a un PIC:
- Si el "Brown-out-Reset" está activado, el PIC permanece en RESET mientras Vdd está por debajo de cierto nivel de voltaje. Este voltaje "Brown-out" es específico para cada controlador. Se puede usar la función "PIC Info" de 'PicProm' para averiguar este valor.
- El circuito interno de "Power-on-Reset" de algunos PIC requiere que Vdd empiece a aumentar desde un valor cerca de Vss, en caso contrario bloquea.
Esto puede ser crítico si se apaga y prende un circuito de bajo consumo de energía. Un capacitor de desacople de 100µF, por ejemplo, puede necesitar incluso unos minutos para descargarse por completo, ya que a voltajes muy bajos el PIC casi no causa un flujo de corriente.
- Impulsos de voltaje negativo en el pin /MCLR pueden causar un "latch-up". Si el pin está operado con baja impedancia, p.ej. por una clave, se debería poner un resistor en serie de 100Ω.
- Con voltajes significativamente mayores que Vdd en el pin /MCLR el PIC entra en el modo de programación.
- El circuito del depurador está activado.
Si el depurador está activado, el PIC ejecuta la primera instrucción y luego para. Por eso, después de depurar el software se debe re-programar el PIC en el modo normal, antes de usarlo en el circuito de aplicación.
- El PIC parece muerto.
En mi experiencia los PIC no se rompen tan fácilmente. Incluso después de aplicar el voltaje operativo al revés hay esperanza.
Antes de transformar al PIC en una linda escultura, se puede intentar recuperarlo borrándolo varias veces.
El PIC no funciona como esperado
Causas posibles:
- Está seleccionado el banco de registros equivocado.
El código operativo de los PIC 12Fxxx/16Fxxx sólo puede contener los 7 bits inferiores de la dirección de un registro. Para permitir más de 128 registros, éstos están organizados en dos o cuatro bancos. Al escribir código ensamblador, antes de acceder a un registro se debe asegurar que en el registro STATUS esté seleccionado el banco correspondiente.
Cuando MASM genera un archivo "List file", el editor de 'PicProm' puede mostrar ambos: el archivo HEX y las líneas de fuente correspondientes, lo cual permite la verificación visual del código generado.
![[Editor]](pe2.png)
(Si bien con MASM se puede usar la pseudo-instrucción "banksel" antes de cada acesso a un registro, la cual genera las instrucciones necesarias para configurar correctamente los bits de banco, no puedo recomendar este método por dos razones: primero, aparentemente no es posible poner un símbolo en la misma línea, y segundo, esta pseudo-instrucción siempre genera código — también cuando los bits de banco ya están configurados correctamente — inflando de este manera el tamaño del código y aumentando el tiempo de ejecución.)
- Está seleccionada la página de memoria equivocada.
El código operativo de los PIC 12Fxxx/16Fxxx sólo puede contener los 11 bits inferiores de la dirección de un blanco de transferencia, lo cual da un rango de direcciones directas de 2 KWords. Si el código supera estos 2 KWords, debe asegurar que la página correspondiente está seleccionada en el registro PCLATH antes de ejecutar un "goto" o "call".
- El temporizador Watchdog ("perro guardián") resetea al PIC una y otra vez.
Con el temporizador Watchdog activado — que es el estado por defecto — se deben incluir instrucciones "clrwdt" en el código fuente, de tal manera que en caso de ejecución normal del programa una de estas sea ejecutada por lo menos cada 10 ms.
Antes de desactivar el temporizador Watchdog en la palabra de configuración, hay que tener en cuenta que este temporizador es la única manera en que un PIC puede "recuperarse" a sí mismo cuando el software queda atrapado en un bucle sin fin por algún desperfecto.
- Los puertos no están inicializados correctamente.
Después del "Power-on-Reset", algunos pines pueden estar configurados como entradas analógicas, y al leerlos siempre da '0'. Esto depende del controlador. Acá ayuda acudir a las especificaciones para saber cómo inicializar los puertos.
- Instrucción con puerto I/O como destino.
Al modificar datos de un puerto, hay que tener en cuenta que la instrucción lee el estado actual – externo – de todos los pines y escribe el resultado a todos los búferes de salida del puerto. Esto puede dar resultados inesperados. Algunos ejemplos:
- Es una buena práctica pre-cargar los búferes de salida antes de cambiar pines de un puerto a salida — ¡pero ojo al cambiar varios pines!
Supongamos que se quieren poner RB0 y RB1 como salida y en "low", y que los pines son llevados en este momento a "high" por un resistor externo o interno.
| bcf | PORTB,0 | ; búfer de salida RB0='0' |
| bcf | PORTB,1 | ; búfer de salida RB1='0', pero RB0='1' |
| bsf | STATUS,RP0 | ; banco 1 |
| bcf | TRISB,0 | ; configurar RB0 como salida |
| bcf | TRISB,1 | ; configurar RB1 como salida |
La segunda instrucción, "bcf PORTB,1", lee el PORTB entero, borra el bit 1 y escribe el resultado en los búferes de salida. Como RB0 todavía es una entrada, va a reflejar el estado "high" (= '1').
Para evitar esto se deben manipular los búferes de salida de RB0 y RB1 en la misma instrucción:
| movlw | 0FCh | |
| andwf | PORTB | ; búfer de salida RB0='0' y RB1='0' |
| bsf | STATUS,RP0 | ; banco 1 |
| andwf | TRISB | ; configurar RB0 y RB1 como salida |
A propósito, si la rutina de interrupción afectara al PORTB, se deben desactivar las interrupciones durante la secuencia de arriba.
- Cuando hay una carga capacitiva en un pin de salida – p.ej. un conductor largo –, conmutar el pin puede demorar un cierto tiempo.
Supongamos que RB2 y RB3 están configurados como salida y en estado "low", y que queremos ponerlos en "high":
| bsf | PORTB,2 | ; búfer de salida RB2='1' |
| bsf | PORTB,3 | ; búfer de salida RB3='1', pero eventualmente RB2='0' |
La primera instrucción escribe los búferes de salida de PORTB al fin del ciclo de reloj Q4 y la segunda instrucción lee el estado real de los pines del puerto al principio del ciclo de reloj Q2 siguiente. Si RB2 no puede re-cargar el conductor en menos de un período de oscilador, la segunda instrucción todavía puede leer "low" y va a devolverlo al búfer de salida RB2, sobrescribiendo el estado deseado.
Por eso, se deberían manipular los búferes de salida de RB2 y RB3 en la misma instrucción:
| movlw | 00Ch | |
| iorwf | PORTB | ; búfer de salida RB2='1' y RB3='1' |
- En modelos más antiguos de PIC, el pin RA4 es una salida "open drain". Si el búfer de salida de RA4 es '1', pero el pin es llevado externamente a "low", cualquier instrucción que modifica otro pin de PORTA va a tener el efecto colateral de cambiar el búfer de salida de RA4 a '0'. De este manera, el conductor ahora es mantenido en "low" por el PIC.
- Si un pin está configurado como entrada analógica, cualquier instrucción que modifique otro pin del puerto afectado va a poner el búfer de salida de este pin en '0', ya que cuando se lee la entrada siempre arroja '0'.
La 'trampa' de los pines analógicos es que al resetear el bit en el registro TRISx se activa (solamente) la etapa de salida, mientras que la entrada queda en modo analógico, o sea, se obtiene una combinación de salida digital y entrada analógica. Para que la salida funcione fiablemente se debería configurar el pin como entrada digital o usar un registro sombra (ver el próximo punto).
La impedancia de los excitadores de salida varía con la temperatura, Vdd y la carga externa. Debido a la saturación de la etapa de salida, una inyección de corriente de apenas unos miliamperios puede, en el peor de los casos, revertir el estado de un pin desde "high" a "low".
(Desafortunadamente, hojas de datos recientes no muestran el rango entero de operación de los excitadores de salida. Basta comparar los grafos "VOH vs. IOH" para el 16F88x, figura 18-28, con aquellos para el 16F87xA, figura 18-17.)
Si el circuito trabaja en un ambiente ruidoso, impulsos parásitos pueden encontrar un camino hacia los pines.
Y un pico atacando un pin justo en el momento de una operación de salida puede ser un buen ejemplo de la Ley de Murphy...
Para prevenir un comportamiento "raro" de la aplicación en el "mundo real", se recomienda el uso de registros sombra. Todas las manipulaciones de los bits de un puerto se hacen con el registro sombra primero y luego se transfiere el resultado al registro del puerto, p.ej.:
| bsf | Port_B,2 | ; registro sombra RB2='1' |
| bsf | Port_B,3 | ; registro sombra RB3='1' |
| movf | Port_B,w | ; leer registro sombra y ... |
| movwf | PORTB | ; ... escribir los búferes de salida |
De esta manera, el estado externo de los pines no tiene ninguna importancia.
- Falta el bucle principal.
En principio, una vez que el PIC se ha puesto en marcha, va a ejecutar instrucciones de la memoria del programa hasta que se lo apague. No hay una instrucción "halt", y la declaración "END" del bloque del código de ensamblador no tiene significado con respecto a la ejecución del código.
Por eso, el programa debe contener un bucle principal (sin fin) o — si el programa se ejecuta sólo una vez después del arranque — por lo menos terminar con una instrucción "goto $" para evitar la ejecución de memoria de programa vacío.
- Desbordamiento de pila.
Los PIC 12Fxxx/16Fxxx sólo tienen 8 niveles de pila para instrucciones "call" y para la rutina de interrupción. Si el programa usa subrutinas anidadas en exceso puede ocurrir un desbordamiento de pila — y para esto no hay previsto ningún tratamiento especial. El programa simplemente se cuelga.
Por lo tanto, al cargar un archivo HEX, 'PicProm' lo analiza y da una advertencia en caso de que se pudiera producir un desbordamiento de pila durante la ejecución del programa.
- Falta el capacitor de desacoplo.
Un capacitor de desacoplo de 100nF debería estar ubicado lo más cerca posible de los pines Vdd y Vss del PIC. Mi método preferido con los dispositivos PDIP es soldar un capacitor SMD pequeño directamente entre los pines.
volver a la página principal