PIC16F Errores de escritura de EEPROM

Estoy desarrollando en un PIC16F1936 usando MPLAB X IDE con el compilador XC8. Me dieron este proyecto en el que necesito "actualizar" el software de una PCB para que pueda interactuar con una herramienta externa a través de un enlace RS232.

Tengo el enlace funcionando y también he comprobado que la fecha que se transmite es correcta. El problema que tengo es con una función específica que calibra los puntos de ajuste para el PCB. No puedo decir mucho sobre cuál es la función exacta de la PCB, así que lo siento por no publicar todo. Además, he heredado mucho del código de este proyecto, como el código que estoy a punto de mostrarles.

Entonces, la función de calibración funciona así: se llama una vez que la herramienta envía el comando relevante a la PCB. Después de esto (dependiendo del comando enviado), la función leerá el valor de la suma de 40 valores del ADC y lo guardará en la EEPROM. ¡Aquí es donde realmente se estropea! La calibración funciona perfectamente bien cuando tengo que poner a CERO la PCB, el problema surge cuando tengo que configurar MAX. ¡Oh! Y el código es idéntico, aparte de la dirección EEPROM...

Aquí está el código para la calibración:

if (zero == 1)
{
    WR = 1;                                         // Set EEPROM_WRITE flag
    EEPROM_WRITE(0x05,(AN1_sum >> 0x08));           // load MSbyte of AN1_sum into NV memory location 1
    EEPROM_WRITE(0x06,(AN1_sum & 0xff));            // load LSbyte of AN1_sum into NV memory location 2
    EEPROM_WRITE(0x07,(AN4_sum >> 0x08));           // load MSbyte of AN4_sum into NV memory location 3
    EEPROM_WRITE(0x08,(AN4_sum & 0xff));            // load LSbyte of AN4_sum into NV memory location 4

    zero1 = AN1_sum;                                // update volatile memory for zero1
    zero4 = AN4_sum;                                // update volatile memory for zero4
    WR = 1;                                         //Set EEPROM_WRITE flag
    EEPROM_WRITE(0x09, 2);                          //Write 2 to the EEPORM

    while(WR){                                      //Loop till WR flag is reset by EEPROM CONTROLLER
        MAL = 1;                                        //Light MAL until WR is reset
    }
    for(KILLTIME = 0; KILLTIME <= 200000; KILLTIME++);
    RESET();
    for(KILLTIME = 0; KILLTIME <= 200000; KILLTIME++);
}
else if (MAX == 1)
{
    WR = 1;                                         //Set EEPROM_WRITE flag
    EEPROM_WRITE(0x01,(AN1_sum >> 0x08));           // load MSbyte of AN1_sum into NV memory location 5
    EEPROM_WRITE(0x02,(AN1_sum & 0xff));            // load LSbyte of AN1_sum into NV memory location 6
    WR = 1;                                         //Set EEPROM_WRITE flag
    EEPROM_WRITE(0x03,(AN4_sum >> 0x08));           // load MSbyte of AN4_sum into NV memory location 7
    EEPROM_WRITE(0x04,(AN4_sum & 0xff));            // load LSbyte of AN4_sum into NV memory location 8

    MAX1 = AN1_sum;                                 // update volatile memory for MAX1
    MAX4 = AN4_sum;                                 // update volatile memory for MAX4

    WR = 1;                                         //Set EEPROM_WRITE flag
    EEPROM_WRITE(0x9, 3);                           //Write 3 to the EEPROM

    while(WR){                                      //Loop till WR flag is reset by EEPROM CONTROLLER
        MAL = 1;                                        //Light MAL until WR is reset
    }
    for(KILLTIME = 0; KILLTIME <= 200000; KILLTIME++);
    RESET();
    for(KILLTIME = 0; KILLTIME <= 200000; KILLTIME++);
}

Después del reinicio, la PCB se reinicia y pasa por este proceso de inicialización:

MAX1 = EEPROM_READ(0x01);                                               
MAX1 = (MAX1 << 8) + EEPROM_READ(0x02);
MAX4 = EEPROM_READ(0x03);                                 
MAX4 = (MAX4 << 8) + EEPROM_READ(0x04); 
zero1 = EEPROM_READ(0x05);                                           
zero1 = (zero1 << 8) + EEPROM_READ(0x06);                          
zero4 = EEPROM_READ(0x07);                                 
zero4 = (zero4 << 8) + EEPROM_READ(0x08); 

Bastante simple, ¿verdad? Este es solo el tiempo en el código donde se configuran MAX y cero.

No tengo idea de por qué esto no funciona. MAX debería ser cualquier valor que AN1 y AN4 tengan en el momento de la ejecución. Cambié las direcciones de EEPROM y cambié el nombre de las variables para asegurarme de que MAX y ZERO no se sobrescriban en ninguna otra parte del código. Sin resultado. MAX solo quiere ser el valor correcto cuando digo MAX1 = x y MAX 4 = y. No puede ser algo con los valores de AN1 y AN4 ya que funcionan bien para TARE sin importar cuál sea su valor.

¿Algunas ideas?

Solo para agregar, he probado las siguientes soluciones:

  1. Cambié las direcciones EEPROM de modo que MAX escribe en las direcciones. Quiero decir que MAX ahora se escribe en 0x05-0x08 y ZERO se escribe en 0x01-0x05. Todavía tengo el mismo problema. El valor del sensor se escribe correctamente en CERO pero no en MAX.

  2. He jugado un poco con la bandera WR colocando más y quitándolas por completo. NINGÚN CAMBIO.

  3. Intenté almacenar los valores de AN1_sum y AN4_sum en una variable temporal tan pronto como se inicia la función de calibración y usé esas variables en su lugar.

  4. He intentado invertir los roles de ZERO y MAX. Con esto quiero decir que configuro CERO en el punto de ajuste máximo y MAX en el mínimo. Descubrí que ZERO se establece con precisión y el valor que tiene puede afectar el valor de MAX. Por ejemplo, primero configuré 45 aplicados a ambos canales y luego configuré el MAX. Después de la calibración, MAX tenía un valor de 26. Luego apliqué 198 a ambos canales y puse CERO. ¡ZERO tenía un valor final de 198, mientras que MAX cambió a 48!

Hace tiempo que sé que MAX es el culpable, pero no sé por qué ni cómo sucede esto. Revisé el resto del código y verifiqué si MAX se ve afectado de alguna manera, pero no es así.

Como dijo "Descubrí que ZERO se establece con precisión y el valor que tiene puede afectar el valor de MAX", le sugiero que elimine cualquier operación de escritura en ZERO, solo realice operaciones en MAX y verifique.

Respuestas (2)

Intente convertir el segundo argumento de EEPROM_WRITE() al tipo correcto. La EEPROM está organizada en ubicaciones de memoria de 8 bits y, por lo que parece, al hacer (AN4_sum y 0xff) está tratando de enmascarar los 8 bits inferiores de un número entero (o un tipo más grande), pero aún así el tipo pasado sigue siendo el mismo y el comportamiento de esta función puede ser impredecible. Siempre me ha resultado útil aplicar máscaras para escribirlas en toda la longitud de la variable que estoy tratando de enmascarar, es decir, (AN4_sum y 0x00ff) para que siempre quede claro lo que sucede, especialmente si está revisando el código mucho después. fue escrito. Espero que esto ayude un poco...

Parece que tiene un soporte de depuración inadecuado e inadecuado, lo que dificulta innecesariamente su trabajo.

Además, mencionas varias veces "El problema", pero nunca nos dices específicamente qué síntomas observas, lo que nos dificulta poder ayudarte. ¿Un LED permaneció oscuro cuando esperaba que parpadeara (o viceversa)? ¿Una pantalla de dígitos de 7 segmentos no muestra el número que esperaba?

Si yo fuera tú,

  1. Leería la hoja de datos del procesador que estoy usando, en particular la sección "Uso de la EEPROM de datos" en las páginas 116-117. Parece decir establecer el bit WR después de establecer la dirección EEPROM y los registros de datos, por lo que la línea "WR = 1;" al comienzo de una sección de código parece fuera de lugar.

  2. Leería las preguntas frecuentes: "¿Cómo escribo y leo datos de EEPROM usando el compilador XC8?"

  3. Leería la Guía del usuario del compilador -- "Guía del usuario del compilador MPLAB XC8 C" , en particular la sección "Funciones de acceso a la EEPROM" en la p. 108.

  4. Cuando tengo 2 fragmentos de código que hacen casi lo mismo, trato de refactorizarlos para tener 1 función que hace todas las cosas comunes y llamo a esa función desde 2 lugares. En este caso, tiene (al menos) 4 ubicaciones que escriben un valor de 16 bits en EEPROM, por lo que podría refactorizar las 4 para llamar a algo como:

    void EEPROM_WRITE_16( char address, int value ){
        char MSByte = (value >> 0x08) & 0xff;
        char LSByte = value & 0xff;
        if(eeprom_read(address) != MSByte){
            eeprom_write(address, MSByte); // load MSbyte into given EEPROM address
        };
        address++;
        if(eeprom_read(address) != LSByte){
            eeprom_write(address, LSByte); // load LSbyte into following EEPROM address
        };
    }
    
  5. Cada vez que hago cosas con EEPROM, agrego algunas rutinas de "depuración" en el firmware para leer el contenido de la EEPROM y enviarlo a algún lugar donde un humano pueda leerlo, tal vez a un puerto de depuración UART o, si hay uno disponible, una pantalla LCD o LED; tal vez incondicionalmente, brevemente en el encendido.

  6. A veces, cuando depuro problemas difíciles, agrego comandos adicionales de "depuración" para escribir nuevos valores en la EEPROM. Si esas funciones llaman a la(s) misma(s) rutina(s) que uso en otro lugar para escribir en EEPROM, entonces puedo probar esas rutinas más o menos de forma aislada.

  7. A veces hago una copia de seguridad y dejo de lado el programa principal, y creo un nuevo programa de prueba que comienza como una copia exacta del programa principal. Siempre que el programa de prueba muestre los síntomas inesperados del problema, sigo eliminando funciones que creo que no están relacionadas con el problema, hasta que termino con un SSCCE: corto, autónomo, compilable, ejemplo . Luego publico ese breve programa de prueba cuando pido ayuda. Dicho programa generalmente obtiene una respuesta mucho más rápida que (a) publicar el programa original extremadamente largo o (b) publicar solo fragmentos del programa completo que creo que están relacionados con el problema.

Los últimos 3 pasos (andamiaje de depuración adicional y SSCCE) facilitan probar rápidamente varias soluciones propuestas y ver si realmente funcionarán para mi código. (Sugerencias como "bucle hasta que se complete la escritura anterior antes de comenzar una nueva lectura" (a) y la página 108 de la "Guía del usuario del compilador MPLAB XC8 C" ; "asegúrese de que el enlazador esté configurado para "Enlazar la biblioteca periférica"" (b ) ; '[reemplace] las llamadas C con las instrucciones de ensamblaje en línea exactas descritas en ... la hoja de datos' (c) ; etc.).

¡Buena suerte!