Electronic – Problem with the explorer 16 dev board

assemblycmplabxpic

I have the explorer 16 dev board equipped with the PIC24FJ128GA010. I wrote a C code to write something on the LCD and a LED blinking after that. I wrote the same code in assembly and both codes work. I noticed something after I programmed the board with the assembly code is that when the power is disconnected and reconnected the LCD does not display anything, I must reprogram the device again from MPLAB X, but on the C code the board works correctly.

And some other weird thing, in assembly I wrote a simple delay function that depends on decrementing a value and a branch if not zero instruction, it appears that the code goes into infinite loop because the value is never incremented somehow! I found this out while I was debugging.

C Code:

#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);
}

ASM Code:

;.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

UPDATE – The device does not read any programmed LCD driver (C or assembly) on power reset, therefore not displaying anything on the LCD but everything else works :\

Best Answer

Frankly, your code sucks. You need to stop and learn some coding techniques before continuing. The longer you persist doing things this way, the more painful it will be.

First and foremost, there is not a single comment in sight. Not only is that blatantly irresponsible, deprives you of a first-pass sanity check, will cause work for you in the future, but is also downright rude to ask others to look at it.

Doing timing by busy-wait is another bad programming technique:

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

Problems with this include:

  1. This is dependent on the clock speed and the architecture. For example, the delay value will be different between a 24 F and 24 EP series, even at the same instruction frequency (which is not the same as the same clock frequency). Also, if any interrupts occur, which will be the case in anything but a toy project, the delay time will get stretched out.

  2. There is no indication G is forced to be in near RAM, yet the DEC instruction is used on G directly. This is a bug waiting to happen as the project grows and G happens to be placed past near RAM.

  3. It makes no sense to insert the REPEAT loop between the decrement of G and the test for 0. Did you look up the details of REPEAT to make sure it doesn't clobber the Z status bit?

A much better way to do a 1 second delay is to use a timer. The timer period value would then be computed at build time from the desired value and the instruction clock rate. The instruction clock rate would be defined with other constants in one central place that someone can easily check over when the clock rate is changed or the project ported to a different PIC. A delay as long as 1 s may require counting multiple shorter ticks derived from a timer.

Timing can be required in various points in a project such that it can be useful to set up a periodic interrupt from a timer, then have the interrupt routine derive various clocks or set flags used by the rest of the system. 1 ms (1 kHz rate) is often a useful period for this. To wait for 1 second, for example, you'd count 1000 ticks of the global 1 ms clock. The timer setup in this one interrupt routine may need to be adjusted as hardware changes, but the rest of the system relies on the regular 1 ms clock ticks and is completely independent of instruction cycle time, interrupt delays, etc.