3
votes

I need help with the uart communication I am trying to implement on my Proteus simulation. I use a PIC18f4520 and I want to display on the virtual terminal the values that have been calculated by the microcontroller.

Here a snap of my design on Proteus

Right now, this is how my UART code looks like :

#define _XTAL_FREQ  20000000
#define _BAUDRATE   9600

void Configuration_ISR(void) {
    IPR1bits.TMR1IP = 1;        // TMR1 Overflow Interrupt Priority - High
    PIE1bits.TMR1IE = 1;        // TMR1 Overflow Interrupt Enable
    PIR1bits.TMR1IF = 0;        // TMR1 Overflow Interrupt Flag
                            //   0 = TMR1 register did not overflow
                            //   1 = TMR1 register overflowed (must be cleared in software)
    RCONbits.IPEN   = 1;        // Interrupt Priority High level
    INTCONbits.PEIE = 1;        // Enables all low-priority peripheral interrupts
    //INTCONbits.GIE  = 1;          // Enables all high-priority interrupts
}

void Configuration_UART(void) {
    TRISCbits.TRISC6 = 0;
    TRISCbits.TRISC7 = 1;

    SPBRG = ((_XTAL_FREQ/16)/_BAUDRATE)-1;

    //RCSTA REG
    RCSTAbits.SPEN = 1;     // enable serial port pins    
    RCSTAbits.RX9 = 0;

    //TXSTA REG
    TXSTAbits.BRGH = 1;     // fast baudrate
    TXSTAbits.SYNC = 0;     // asynchronous
    TXSTAbits.TX9 = 0;      // 8-bit transmission
    TXSTAbits.TXEN = 1;     // enble transmitter
}

void WriteByte_UART(unsigned char ch) {
    while(!PIR1bits.TXIF);  // Wait for TXIF flag Set which indicates
                            // TXREG register is empty
    TXREG = ch;             // Transmitt data to UART
}

void WriteString_UART(char *data) { 
       while(*data){ 
          WriteByte_UART(*data++); 
       }
}

unsigned char ReceiveByte_UART(void) {
    if(RCSTAbits.OERR) {
        RCSTAbits.CREN = 0;
        RCSTAbits.CREN = 1;
    }
    while(!PIR1bits.RCIF); //Wait for a byte
    return RCREG;
}

And in the main loop :

while(1) {
    WriteByte_UART('a'); // This works. I can see the As in the terminal
    WriteString_UART("Hello World !"); //Nothing displayed :(
}//end while(1)

I have tried different solution for WriteString_UART but none has worked so far.

I don't want to use printf cause it impacts other operations I'm doing with the PIC by adding delay. So I really want to make it work with WriteString_UART. In the end I would like to have someting like "Error rate is : [a value]%" on the terminal.

Thanks for your help, and please tell me if something isn't clear.

3
Where is PIR1bits declared? - Daniel Margosian
It's in another function. I'll edit the post now. - Daymov
In my program, I use one high ISR. In the routine I do a comparison between two values that two PICs are exchanging (using SPI). What I would like to do, is to display the result of the comparison on the terminal :) - Daymov
What is the type of PIR1bits? Show me the declaration, not the definition. - Daniel Margosian
Does theTXREG empty itself automatically after transmission to UART? - Daniel Margosian

3 Answers

2
votes

In your WriteByte_UART() function, try polling the TRMT bit. In particular, change:

while(!PIR1bits.TXIF);

to

while(!TXSTA1bits.TRMT);

I don't know if this is your particular issue, but there exists a race-condition due to the fact that TXIF is not immediately cleared upon loading TXREG. Another option would be to try:

...
Nop();
while(!PIR1bits.TXIF);
...

EDIT BASED ON COMMENTS

The issue is due to the fact that the PIC18 utilizes two different pointer types based on data memory and program memory. Try changing your declaration to void WriteString_UART(const rom char * data) and see what happens. You will need to change your WriteByte_UART() declaration as well, to void WriteByte_UART(const unsigned char ch).

1
votes
  1. Add delay of few miliseconds after line TXREG = ch;

  2. verify that pointer *data of WriteString_UART(char *data) actually point to
    string "Hello World !".

1
votes

It seems you found a solution, but the reason why it wasn't working in the first place is still not clear. What compiler are you using?

I learned the hard way that C18 and XC8 are used differently regarding memory spaces. With both compilers, a string declared literally like char string[]="Hello!", will be stored in ROM (program memory). They differ in the way functions use strings.

C18 string functions will have variants to access strings either in RAM or ROM (for example strcpypgm2ram, strcpyram2pgm, etc.). XC8 on the other hand, does the job for you and you will not need to use specific functions to choose which memory you want to access.

If you are using C18, I would highly recommend you switch to XC8, which is more recent and easier to work with. If you still want to use C18 or another compiler which requires you to deal with program/data memory spaces, then here below are two solutions you may want to try. The C18 datasheet says that putsUSART prints a string from data memory to USART. The function putrsUSART will print a string from program memory. So you can simply use putrsUSART to print your string.

You may also want to try the following, which consists in copying your string from program memory to data memory (it may be a waste of memory if your application is tight on memory though) :

char pgmstring[] = "Hello";
char datstring[16];
strcpypgm2ram(datstring, pgmstring);
putsUSART(datstring);

In this example, the pointers pgmstring and datstring will be stored in data memory. The string "Hello" will be stored in program memory. So even if the pointer pgmstring itself is in data memory, it initially points to a memory address (the address of "Hello"). The only way to point to this same string in data memory is to create a copy of it in data memory. This is because a function accepting a string stored in data memory (such as putsUSART) can NOT be used directly with a string stored in program memory.

I hope this could help you understand a bit better how to work with Harvard microprocessors, where program and data memories are separated.