Programando PIC18F2550 con un Arduino

Estoy tratando de implementar un programador de modo de bajo voltaje para el PIC18F2550 en un Arduino Uno de acuerdo con estas especificaciones de programación.

Las secuencias de bits se escriben en el PIC desde el bit menos significativo hasta el bit más significativo. También he tenido en cuenta los retrasos mínimos especificados en la hoja de datos, no parece haber retrasos máximos, excepto el tiempo de subida de MCLR/Vpp.

Para validar mi código, quiero leer el DeviceID en la dirección 0x3ffffe y 0x3fffff. Aquí está mi código:

    /* 
 * Trying to build an arduino pic programmer for the PIC18F2550
 * Programming specifications: http://ww1.microchip.com/downloads/en/DeviceDoc/39622L.pdf
 * Author: Ingmar Jager
 */

//Arduino Pinout
int pgc = 9;  // program clock
int pgd = 8;  // program data
int pgm = 10;  // program mode
int MCLR = 11; //Master Clear Reset / Vpp

void setup()
{
  Serial.begin(9600);

  pinMode(pgc, OUTPUT);
  pinMode(pgd, OUTPUT);
  pinMode(pgm, OUTPUT);
  pinMode(MCLR, OUTPUT);
  digitalWrite(pgc, LOW);
  digitalWrite(pgd, LOW);
  digitalWrite(pgm, LOW);
  digitalWrite(MCLR, LOW);
  delay(500);

  enterProgramming();
  delay(500);
  tableRead(0x3F, 0xFF, 0xFF); //read DeviceID2 at 0x3FFFFF. Should be 0x12 = 00010010

  //delay(500);
  tableRead(0x3F, 0xFF, 0xFF);
 // delay(500);
  tableRead(0x3F, 0xFF, 0xFF);
  exitingProgramming();
}

/*
 * @param: three bytes which form the tablepointer address
 */
void setTablePTR(int addr_upper_byte,int addr_half_byte,int addr_lower_byte)
{

 Serial.print("Set TablePTR to: 0x");
 Serial.print(addr_upper_byte, HEX);
 Serial.print(addr_half_byte, HEX);
 Serial.println(addr_lower_byte, HEX);


 writeBits(4, 0x0); // 4 bit command

 writeBits(16, (0x0E << 8) + addr_upper_byte); // 16 bit data payload: load address

 writeBits(4, 0x0); 
 writeBits(16, 0x6EF8);  // set TBLPTR_U to addr_upper_byte

 writeBits(4, 0x0);
 writeBits(16, (0x0E << 8) + addr_half_byte);

 writeBits(4, 0x0);
 writeBits(16, 0x6EF7); //TBLPTR_H

 writeBits(4, 0x0);
 writeBits(16, (0x0E << 8) + addr_lower_byte);

 writeBits(4, 0x0);
 writeBits(16, 0x6EF6); //TBLPTR_L

}

/*
 * Read byte from the given address
 */
void tableRead(int high_byte, int mid_byte, int low_byte)
{

  setTablePTR(high_byte,mid_byte,low_byte);

  writeBits(4, 0x9); //1000 = Read and no increment
  writeBits(8, 0x00);
  pinMode(pgd, INPUT);
  delayMicroseconds(5); //P6 

  byte data = 0;
  //actual read
  Serial.print("Read bits from LSB to MSB: ");
  for (int i = 0; i < 8; i++)
  {
    digitalWrite(pgc, HIGH);
    delayMicroseconds(3); //P14 

    if (digitalRead(pgd)==HIGH)
    {
      Serial.print("1");
      data += (1 << i);
    }
    else
    {
      Serial.print("0");
    }

    digitalWrite(pgc, LOW);
        delayMicroseconds(3); 
  }  

  delayMicroseconds(5); //P5A
  pinMode(pgd, OUTPUT);
  Serial.println();
  Serial.print("Reading result: ");
  Serial.println(data);

}

/* Write LSB to MSB
 * @param n = number of bits to write
 * @param value = value to write in n bits
 */
void writeBits(int n, int value)
{

  for (int i = 0; i < n; i++) {
   if(boolean ((value >> i) & 1))
     {   
       digitalWrite(pgd,HIGH);
     //   Serial.println("Write High");
     } // else Serial.println("Write Low");
  }   
  digitalWrite(pgc, HIGH);
  delayMicroseconds(3);
  digitalWrite(pgc, LOW);
  delayMicroseconds(3);
  digitalWrite(pgd,LOW);
  delayMicroseconds(5); //P5A
} 

/*
 * Entering low voltage programming signals
 */
void enterProgramming()
{
   digitalWrite(pgc, LOW);
   digitalWrite(pgd, LOW);
   digitalWrite(pgm, LOW);
   digitalWrite(MCLR, LOW);

   delayMicroseconds(20);

   digitalWrite(pgm, HIGH);
   delayMicroseconds(3);//P15
   digitalWrite(MCLR, HIGH);
   delayMicroseconds(3);//P12
   Serial.println("Entered Low Voltage Programming");
}

/*
 * Exiting low voltage programming signals
 */
void exitingProgramming()
{
   digitalWrite(pgc, LOW);
   digitalWrite(pgd, LOW);
   delayMicroseconds(1); //P16
   digitalWrite(MCLR, LOW);
   delayMicroseconds(1); //P18
   digitalWrite(pgm, LOW);
   Serial.println("Exiting Low Voltage Programming");
}


void loop()
{

}

Este código ingresa al modo de programación, luego intenta leer desde 0x3fffff tres veces seguidas, finalmente sale del modo de programación. Lástima que estoy obteniendo tres resultados diferentes, aparentemente no estoy leyendo el DeviceID... El resultado impreso es:

Entered Low Voltage Programming
Set TablePTR to: 0x3FFFFE
Read bits from LSB to MSB: 00000111
Reading result: 224
Set TablePTR to: 0x3FFFFF
Read bits from LSB to MSB: 00011111
Reading result: 248
Set TablePTR to: 0x3FFFFF
Read bits from LSB to MSB: 00000000
Reading result: 0
Exiting Low Voltage Programming

Cuando conecto otro PIC18F2550, los resultados difieren. Ahora no sé cómo continuar para depurar este proyecto. ¿Alguien tiene experiencia con este asunto y / o algunas sugerencias?

p.d., sé que es más fácil usar un programador, pero esto es más divertido ;)

¿Ha mirado alguna de las líneas con un osciloscopio para verificar su tiempo? No confiaría en que las funciones de retardo sean muy precisas para valores bajos.
@AngryEE, tiene razón al no confiar en las funciones de retraso, sin embargo, no se especifican máximos para los retrasos. Así que creo que eso no debería ser un problema. Sin embargo, si puedo poner mis manos en un visor, lo revisaré.

Respuestas (1)

Lo primero que debe hacer para depurar algo como esto es verificar que realmente ingresó al modo de programación. La forma más confiable es usar la entrada del modo de programa de alto voltaje. Asegúrese de que el pin PGM se mantenga bajo durante este tiempo. También asegúrese de que todos los pines de alimentación y tierra estén conectados, ya sea que crea que los está usando o no, con una tapa de derivación en cada pin de alimentación.

Para verificar que ingresó al modo de programación, cambie algo que debería impulsar PGD, como la lectura de la instrucción TABLAT (0010). Siga esto con 8 bits cero, luego, en el siguiente flanco ascendente de PGC, el chip de destino debería cambiar a conducir PGD desde que anteriormente era de alta impedancia.

Una vez que esté funcionando, lo siguiente es leer el ID del dispositivo, ya que siempre es un valor fijo conocido. Esto es más complicado ya que debe ejecutar instrucciones básicas para cargar los tres bytes de TBLPTR y luego realizar una lectura.

Dado que todas estas interacciones son síncronas y el reloj es tuyo, puedes ir tan lento como quieras y observar todo en el osciloscopio pieza por pieza.

Sería útil si describiera exactamente cuál cree que es su proceso para intentar realizar estas operaciones. No, no voy a buscar código arduino. Hay demasiado entre eso y las líneas PGC y PGD.

Gracias @Olin! Lo que estaba tratando de hacer: Primero ingrese al modo de programa. En segundo lugar, establezca el TBLPTR en 0x3FFFFF. Luego envíe la instrucción de lectura sin incremento (1000) seguida de 8 bits cero. Posteriormente leyendo de PGD los próximos 8 tics de reloj. Pero voy a tratar de leer el registro TABLAT ahora.
Intentar leer el byte alto del DEV ID también debería funcionar como prueba. En tu caso, deberías tener 12h. Verifique que el PIC esté impulsando PGD después del flanco ascendente de PGC después de los 8 bits cero. Si es así, probablemente no cargó TBLPTR correctamente.