Programador para los PIC16F88x

En esta entrada te muestro una forma de hacer un programador para los PIC16F882, 883, 884, 886 y 887. Decidí desarrollar y presentarte este material porque:

  • La entrada de la bitácora que ha recibido más visitas es: Programador para los PIC16F627A, 628A y 648A.
  • El PIC16F887 es uno de los microcontroladores más completos de la familia PIC16.
  • Para que mis alumnos hagan prácticas con más libertad, necesitamos más programadores de los que tenemos.

La idea y código son prácticamente los mismos que en Programador para los PIC16F627A, 628A y 648A.

Otra vez usé el convertidor USB-RS232 número 4 de la entrada Comunicación RS232 a través del USB. Lo que cambia es:

  • Probé todo con el PIC16F887.
  • La terminal PGM se conecta al mismo punto que VPP (en lugar de mantenerla a 5 V).
  • El circuito está contemplado para programar y usar el microcontrolador en la misma tarjeta.
  • Trabajo con dos palabras de configuración.
  • El programa evita que accidentalmente se deshabilité el uso de MCLR y el modo de programación a bajo voltaje.

En la figura siguiente te muestro el diagrama del circuito:

Diagrama del programador para los PIC16F88x.
Diagrama del programador para los PIC16F88x. Durante la programación del microcontrolador JP1 debe estar colocado y JP2 debe conectar las terminales 1 y 2. Para ejecutar el código grabado en el PIC hay que retirar JP1, y hacer que JP2 conecte las terminales 2 y 3. Observa que este circuito implica que el microcontrolador funcionará con su oscilador interno.

Aquí está el código fuente. Los cambios con respecto a Prog.c son:

  • Las dos últimas líneas de la función LeeIdConfig permiten leer la segunda palabra de configuración.
void LeeIdConfig(void)
{
 int n;
 
 ModoProg();
 Escribe2(0x0,0x0); // comando cargar configuración
 for (n=0; n<6; n++) Escribe(0x6); // comando incrementar dirección
 printf("identificador: %X\n",Lee(0x4)); // imprime lo que lee de la memoria de programa
 Escribe(0x6);
 printf("bits de configuración: %X\n",Lee(0x4));
 Escribe(0x6);
 printf("                     : %X\n",Lee(0x4));
}
  • Para trabajar con 2 palabras de configuración, inserté la siguiente sentencia if – else en la función ProgMemProg. Observa que apliqué una máscara (línea 225) para evitar que desactives accidentalmente MCLR y PGM, esto causaría que el programador deje de funcionar.
if (renglon[5]=='0') // CONFIG1
{
 for (n=0; n<7; n++) Escribe(0x6);
 dato|=0x01020; // Activa la programación a bajo voltaje y el uso de MCLR
 Escribe2(0x2,dato);
}
else // CONFIG2
{
 for (n=0; n<8; n++) Escribe(0x6);
 Escribe2(0x2,dato);
}

Te recuerdo que:

  • Si requieres instalar GTK+, desde una cuenta de administrador debes teclear:
sudo apt update
sudo apt install libgtk-3-dev
  • En Ubuntu hay que tener permiso para poder usar los puertos serie, aquí los detalles.
  • Al construir un proyecto en el MPLAB obtendrás el archivo hex que usarás para programar el PIC.

Aquí está el archivo hex generado a partir del código TX.asm que puedes usar para probar el diseño. Este programa permitirá al PIC16F887 recibir datos por el puerto serie de forma indefinida, incrementar el código ASCII de cada dato recibido y transmitir el resultado a la PC. Así que si usas GtkTerm para enviar la secuencia 123… observarás en la pantalla 234… (la intención es hacer evidente que el uC recibe y transmite información).

Código de prueba TX.asm:

     #INCLUDE	"p16f887.inc"

     __CONFIG	_CONFIG1,0X33F5
     __CONFIG	_CONFIG2,0X3EFF

     ORG	    0
     ; Configura la USART.
     BSF	    STATUS,RP0
     BSF	    TXSTA,TXEN
     BSF	    TXSTA,BRGH
     MOVLW   .25        ; 9600 bits a partir del osc interno (4 MHz).
     MOVWF   SPBRG
     BCF	    STATUS,RP0
     BSF	    RCSTA,CREN
     BSF	    RCSTA,SPEN
ESPERA	    
     BTFSS   PIR1,RCIF  ; Espera un carácter,
     GOTO    ESPERA
     MOVF    RCREG,W
     ADDLW   .1
TX?	    
     BTFSS   PIR1,TXIF  ; envía el ASCII siguiente.
     GOTO    TX?
     MOVWF   TXREG
     GOTO    ESPERA
     END

Contenido del archivo hex:

:020000040000FA
:100000008316981618151930990083121816981728
:100010008C1E08281A08013E0C1E0C28990008287E
:040020000034003474
:02400E00F53388
:02401000FF3E71
:00000001FF

La secuencia de prueba es:

  • Descargar Prog88x.c y hex.txt o TX.txt.
  • Renombrar hex.txt como TX.hex. O renombrar TX.txt como TX.asm, incorporarlo a un proyecto en el MPLAB X y construirlo para obtener el archivo hex.
  • Compilar Prog88x.c para obtener Prog88x:
gcc Prog88x.c -o Prog88x $(pkg-config --cflags --libs gtk+-3.0)
  • Asegurarte de que en el circuito estén puestos los puentes JP1 y JP2 (entre las posiciones 1 y 2).
  • Ordenar la programación (es posible que tengas que cambiar de puerto y la trayectoria o nombre del archivo hex):
Captura de pantalla al programar el PIC.
Captura de pantalla al programar el PIC. Observa que, en este caso, el archivo hex está en una carpeta diferente a la que contiene a Prog88x.
  • Al terminar la programación, retirar JP1 y cambiar JP2 para que quede entre las posiciones 2 y 3.
  • Ejecutar GtkTerm y configurar el puerto:
Captura de pantalla al configurar GtkTerm.
Captura de pantalla al configurar GtkTerm. En este caso sólo cambie el puerto que usaré.
  • Observar que teclas oprimes y que caracteres se muestran en la pantalla.

Finalmente te comento que NO he tenido tiempo de probar exhaustivamente el diseño, así que si encuentras alguna falla no dudes en comentarlo, gracias de antemano.

ACTUALIZACIÓN (25/09/2017).

Hasta donde he probado, Prog88x.c funciona correctamente si todas las instrucciones están acomodadas de forma consecutiva (como en la mayoría de los programas del libro), pero falla cuando hay segmentos vacíos (situación que se presenta frecuentemente al usar la versión gratuita del compilador XC8).

Escribí código para solucionar el problema en Prog88xV2.c

En seguida te muestro la sección modificada del programa:

void ProgMemProg(FILE *arch)
{
 char renglon[50], cadena[5]="____";
 int n, m, dir, dato, n_datos, direc, direc_llenado, saltar;
 
 ModoProg();
 direc_llenado=0;
 do
 {
  fgets(renglon,50,arch);
  printf("%s",renglon);
  if (renglon[8]=='0')
  {
   cadena[0]=renglon[3]; cadena[1]=renglon[4]; cadena[2]=0;
   dir=(int)strtol(cadena,NULL,16); // para distinguir entre instrucciones y bits de configuración
   if (dir<0x40)
   {
    cadena[0]=renglon[1]; cadena[1]=renglon[2]; cadena[2]=0;
    n_datos=(int)strtol(cadena,NULL,16)/2; // número de instrucciones en el renglón
    cadena[0]=renglon[3]; cadena[1]=renglon[4]; cadena[2]=renglon[5]; cadena[3]=renglon[6]; cadena[4]=0;
    direc=(int)strtol(cadena,NULL,16)/2; // dirección inicial de programación
    if (direc>direc_llenado) saltar=direc-direc_llenado; else saltar=0;
    for (m=0; m<saltar; m++) Escribe(0x06); // En caso de que las direcciones NO sean consecutivas
    direc_llenado=direc+n_datos;
    for (m=0; m<n_datos; m++)
    {
     cadena[0]=renglon[11+m*4]; cadena[1]=renglon[12+m*4];
     cadena[2]=renglon[9+m*4]; cadena[3]=renglon[10+m*4];
     dato=(int)strtol(cadena,NULL,16);
     Escribe2(0x2,dato);
     Escribe(0x8);
     g_usleep(6100);
     Escribe(0x6);
    }
   }

Te reitero la invitación a analizar códigos y circuitos, y comentarme cualquier falla que encuentres.

ACTUALIZACIÓN (24/11/2017).

Si haz llegado a pensar que la versión 2 del programa se queda atorada después de grabar algunas instrucciones, puedes modificar la línea 208 para que quede:

for (m=0; m<saltar; m++) { printf("."); Escribe(0x06); }

Lo que sucede es que se están ejecutando muchas instrucciones de salto. Con esta nueva línea, observarás que aparece un punto en pantalla por cada instrucción que se ejecuta. Por otro lado, la programación tardará un poco más.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *