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.
#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);
}
;.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:
Problems with this include:
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.