Problema con la placa de desarrollo explorer 16

Tengo la placa de desarrollo explorer 16 equipada con el PIC24FJ128GA010. Escribí un código C para escribir algo en la pantalla LCD y un LED parpadeando después de eso. Escribí el mismo código en ensamblaje y ambos códigos funcionan. Noté algo después de programar la placa con el código ensamblador, es que cuando se desconecta y se vuelve a conectar la alimentación, la pantalla LCD no muestra nada, debo reprogramar el dispositivo nuevamente desde MPLAB X, pero en el código C la placa funciona correctamente.

Y alguna otra cosa extraña, en ensamblador escribí una función de retraso simple que depende de disminuir un valor y una rama si no es una instrucción cero, ¡parece que el código entra en un ciclo infinito porque el valor nunca se incrementa de alguna manera! Descubrí esto mientras estaba depurando.

Código C:

#define FOSC    (8000000ULL)
#define FCY     (FOSC/2)

#pragma config JTAGEN=OFF, GCP=OFF,GWRP=OFF , COE=OFF , FWDTEN=OFF, ICS=PGx2
#pragma config FCKSM=CSDCMD, OSCIOFNC=OFF,POSCMOD=XT, FNOSC=PRI

#include <libpic30.h>
#include <p24FJ128GA010.h>
#include <xc.h>

#define LCD_RS  PORTBbits.RB15
#define LCD_RW  PORTDbits.RD5
#define LCD_E   PORTDbits.RD4


void LCD_SendChar(char value)
{
    PORTE = value;
    LCD_RS = 1;
    LCD_E = 1;
    Nop();
    LCD_E = 0;
    __delay_ms(5);
}

void LCD_SendCmd(int cmd)
{
    PORTE = cmd;
    LCD_RS = 0;
    LCD_E = 1;
    Nop();
    LCD_E = 0;
    __delay_ms(5);
}

void LCD_SendString(char* value)
{
    while(*value != 0)
        LCD_SendChar(*value++);
}

void LCD_Init()
{
    TRISE = 0x00;
    TRISDbits.TRISD4 = 0;
    TRISDbits.TRISD5 = 0;
    TRISBbits.TRISB15 = 0;

    __delay_ms(50);

    // Function Set
    // 0   0   1   8/4 2/1 10/7    x   x | 0x20 - 0x3F
    LCD_SendCmd(0x38);

    // Display ON/OFF and Cursor
    // 0  0   0   0   1   D   U   B  |  0x08 - 0x0F
    LCD_SendCmd(0x0E);

    // Character Entry Mode
    // 0  0   0   0   0   1   1/D S  |  0x04 - 0x07
    LCD_SendCmd(0x06);

    // Clear Display
    // 0  0   0   0   0   0   0   1
    LCD_SendCmd(0x01);

    // Display and Cursor Home
    // 0  0   0   0   0   0   1   x
    LCD_SendCmd(0x02);
}

int main()
{
    LCD_Init();
    LCD_SendCmd(0x82);
    LCD_SendString("Hello World!");

    TRISA = 0x00;
    LATA = 0xAA;

    while(1) {
        LATA ^= 0xFF;
        __delay_ms(500);
    }

    return (0);
}

Código ASM:

;.include "p24fj128ga010.inc"
.include "xc.inc"
config __CONFIG2, POSCMOD_XT & OSCIOFNC_OFF & FCKSM_CSDCMD & FNOSC_PRI 
config __CONFIG1, JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx2
.global _main

.text

_main:
    CALL    LCD_INIT

    MOV     #0x82, W0
    CALL    LCD_SendCmd

    MOV     #'H', W0
    CALL    LCD_SendChar

    MOV     #'E', W0
    CALL    LCD_SendChar

    MOV     #'L', W0
    CALL    LCD_SendChar

    MOV     #'L', W0
    CALL    LCD_SendChar

    MOV     #'O', W0
    CALL    LCD_SendChar

    MOV     #' ', W0
    CALL    LCD_SendChar

    MOV     #'W', W0
    CALL    LCD_SendChar

    MOV     #'O', W0
    CALL    LCD_SendChar

    MOV     #'R', W0
    CALL    LCD_SendChar

    MOV     #'L', W0
    CALL    LCD_SendChar

    MOV     #'D', W0
    CALL    LCD_SendChar

    MOV     #'!', W0
    CALL    LCD_SendChar

    CLR     TRISA
    CLR     LATA

    COM     LATA

L:  GOTO    L

LCD_INIT:
    CLR     TRISE
    BCLR    TRISD, #4
    BCLR    TRISD, #5
    BCLR    TRISB, #15

    CALL    Delay_1

    ;Function Set
    ;0   0   1   8/4 2/1 10/7    x   x | 0x20 - 0x3F
    MOV     #0x38, W0
    CALL    LCD_SendCmd

    ;Display ON/OFF and Cursor
    ;0  0   0   0   1   D   U   B  |  0x08 - 0x0F
    MOV     #0x0E, W0
    CALL    LCD_SendCmd

    ;Character Entry Mode
    ;0  0   0   0   0   1   1/D S  |  0x04 - 0x07
    MOV     #0x06, W0
    CALL    LCD_SendCmd

    ;Clear Display
    ;0  0   0   0   0   0   0   1
    MOV     #0x01, W0
    CALL    LCD_SendCmd

    ;Display and Cursor Home
    ;0  0   0   0   0   0   1   x
    MOV     #0x02, W0
    CALL    LCD_SendCmd

    RETURN

LCD_SendCmd:
    MOV     W0, PORTE
    BCLR    PORTB, #15
    BSET    PORTD, #4
    NOP
    BCLR    PORTD, #4
    CALL    Delay_1
    RETURN

LCD_SendChar:
    MOV     W0, PORTE
    BSET    PORTB, #15
    BSET    PORTD, #4
    NOP
    BCLR    PORTD, #4
    CALL    Delay_1
    RETURN

Delay_1:
    REPEAT  #0xD00
    NOP
    RETURN

Delay:
    REPEAT  #16383
    NOP
    RETURN

Delay_1s:
    MOV     #0xFFFF, W0
    MOV     W0, G
R1: DEC     G
    REPEAT  #0x3D
    NOP
    BRA     NZ, R1
    RETURN

.end

ACTUALIZACIÓN: el dispositivo no lee ningún controlador LCD programado (C o ensamblaje) en el reinicio de energía, por lo tanto, no muestra nada en la pantalla LCD, pero todo lo demás funciona: \

Déjanos ver tu código.
Esto parece una rutina de inicialización descuidada para el controlador LCD.
venny: Ahí tienes, actualicé la pregunta.
Funciona tanto en C como en ASM, pero al restablecer la energía, la pantalla LCD en el ensamblaje no muestra nada, el dispositivo debe reprogramarse
En el archivo ASM hay un procedimiento llamado Delay_1s, esto está creando el problema y nunca se decrementa, no tengo idea de por qué. Aunque una disminución en C aparece como instrucción DEC normal en el desmontaje.
Dado que ya tiene un código C en funcionamiento, podría ser útil mirar la vista de desensamblado del código C y ver qué genera el compilador.
No puedo desensamblar todo el código, se muestra el desensamblaje para cada función por separado. :\
¿G es una variable? ¿Dónde se almacena?
L: GOTO L en tu .ASM me grita 'bucle infinito', ya que en realidad va a sí mismo...
@AdamLawrence Sí, es un bucle infinito al final del programa.
Chicos, lo que quise decir con bucle infinito es que el código Delay_1s nunca sale, sigue en bucle porque la instrucción BRA siempre es verdadera y nunca se vuelve falsa.
Veo. Pero, ¿qué es G y dónde se encuentra?
Olvidé escribir su definición: D, la G es una ubicación de 4 bytes en la memoria (declarada en la directiva .bss) que debe tener el valor de 0xFFFF y una vez que es cero, debe salir de la rama. Puede omitirlo y usar DEC W0, W1 en su lugar, le dará el mismo resultado.
¿Quiso decir DEC W0,W0?
Sea cual sea el origen y el destino, no importará ya que la operación nunca llega a 0.
Muchos módulos LCD requieren una espera después del inicio, lo que puede llevar mucho tiempo antes de enviar cualquier comando. ¿Puedes poner un enlace a la hoja de datos del módulo LCD?
Gracias por tu interés. Es la Hitachi HD44780 estándar. Mi problema se resolvió, creo que se debió a un valor de retraso corto entre los comandos enviados a la pantalla LCD.

Respuestas (2)

Francamente, tu código apesta. Debe detenerse y aprender algunas técnicas de codificación antes de continuar. Cuanto más persista en hacer las cosas de esta manera, más doloroso será.

En primer lugar, no hay un solo comentario a la vista. Eso no solo es descaradamente irresponsable, lo priva de una verificación de cordura de primer paso, le generará trabajo en el futuro, sino que también es francamente grosero pedirle a otros que lo vean.

Hacer cronometraje por ocupado-esperar es otra mala técnica de programación:

Retraso_1s:
    MOV #0xFFFF, W0
    MOV W0, G
R1: DEC G
    REPETIR #0x3D
    NOP
    SUJETADOR NZ, R1
    DEVOLVER

Los problemas con esto incluyen:

  1. Esto depende de la velocidad del reloj y la arquitectura. Por ejemplo, el valor de retraso será diferente entre una serie 24 F y 24 EP, incluso a la misma frecuencia de instrucción (que no es lo mismo que la misma frecuencia de reloj). Además, si se produce alguna interrupción, que será el caso en cualquier proyecto que no sea un juguete, el tiempo de demora se alargará.

  2. No hay indicación de que G esté obligado a estar cerca de la RAM, pero la instrucción DEC se usa directamente en G. Este es un error que espera ocurrir a medida que el proyecto crece y G se coloca cerca de la RAM.

  3. No tiene sentido insertar el ciclo REPETIR entre la disminución de G y la prueba de 0. ¿Buscó los detalles de REPETIR para asegurarse de que no afecta el bit de estado Z?

Una manera mucho mejor de hacer un retraso de 1 segundo es usar un temporizador. El valor del período del temporizador se calcularía en el momento de la construcción a partir del valor deseado y la velocidad del reloj de la instrucción. La frecuencia de reloj de la instrucción se definiría con otras constantes en un lugar central que alguien puede verificar fácilmente cuando se cambia la frecuencia de reloj o se transfiere el proyecto a un PIC diferente. Un retraso de hasta 1 s puede requerir el conteo de múltiples tics más cortos derivados de un temporizador.

La temporización puede ser necesaria en varios puntos de un proyecto, de modo que puede ser útil configurar una interrupción periódica desde un temporizador y luego hacer que la rutina de interrupción derive varios relojes o establezca indicadores utilizados por el resto del sistema. 1 ms (velocidad de 1 kHz) suele ser un período útil para esto. Para esperar 1 segundo, por ejemplo, contaría 1000 tics del reloj global de 1 ms. Es posible que la configuración del temporizador en esta rutina de interrupción deba ajustarse a medida que cambia el hardware, pero el resto del sistema se basa en los pulsos de reloj regulares de 1 ms y es completamente independiente del tiempo del ciclo de instrucción, los retrasos de interrupción, etc.

¡Su problema es que tiene un "bucle de bloqueo" en su código ASM! En su lugar, debería tener un comando final. Parece que el comando final está fuera de lugar, debería estar al final de main, NO al final de las subrutinas.

Sin bucle de bloqueo, la PC se reiniciará, y este no es mi problema, vea mi actualización de la pregunta