3

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.

Daymov
  • 45
  • 9

3 Answers3

2

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

embedded_guy
  • 1,939
  • 3
  • 24
  • 39
  • I tried the two suggestions you made. `WriteByte_UART()` works perfectly as always. If I try the predefined function `putsUSART("Hello World !")` I have the text on the terminal. Now if I declare a variable `unsigned char MyString = "Hello World\r\n";` and then I do `WriteByte_UART(MyString)` it works ! Why `WriteByte_UART("Hello World")` won't, that's the 1 million $ question. – Daymov Aug 04 '16 at 12:16
  • 1
    @Tealyf, Your issue may be because the separate address spaces for program memory and data memory utilize different pointer types. 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)`. – embedded_guy Aug 04 '16 at 17:33
  • @Tealyf, just out of curiosity, did you have an issue with the pointer types? – embedded_guy Aug 15 '16 at 20:42
  • Yes. I changed as you suggested in your comment of Aug. 4 and that's how I got it to work – Daymov Aug 22 '16 at 10:21
  • Thank you, will update to ensure that the answer is properly captured. – embedded_guy Aug 22 '16 at 18:26
1
  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 !".

  • I'm afraid it doesn't. No matter the address `*data` is pointing to, its value remains 0... eg : `data 0x025E` --> `*data 0x025E '\0'` (address, value) – Daymov Aug 04 '16 at 14:00
1

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.

Asct20
  • 84
  • 4
  • Thanks @A.Saucet. As I am working only on one part of a bigger project using C18, I can't change the compiler. I will try what you have kindly suggested. Just to synthesize my thoughts, I also wanted to find a way to use `putsUASRT` with a variable (print the different value of a counter for instance). – Daymov Aug 29 '16 at 09:04
  • So I have tried `putsUSART`and `putrsUSART`. Here are the result : `char datstring[] = "Hello PIC\r\n";` `char datstring2[] = "Test log\r\n";` `putsUSART(datstring);` --> I get "Hello PIC" `putrsUSART(datstring2);`--> I get "͎͚͆̈́͒̀͘ÏjÎj" – Daymov Aug 29 '16 at 15:09
  • Unfortunately I can't test on real hardware right now as I just moved and am waiting for components to be delivered, but I will as soon as I can. – Asct20 Aug 29 '16 at 16:07
  • Have you tried the other method I suggested? It consists of copying the ROM string into the RAM, and sending the RAM string over UART with `putsUSART(ramstr)`. For your other question, you can print a variable using `putsUSART`by doing the `sprintf()` function: `int value = 2;` `char string[16];` `sprintf(string, "Value=%i", value);` `putsUSART(string);` `sprintf()` works like printf but outputs the result in the first argument passed. – Asct20 Aug 29 '16 at 16:19
  • Eventually I succeeded last night to use `putrsUSART` and `putsUSART` by converting my value with `itoa (datvalue,printdatvalue); `. I haven't tried yet to method using the copy. Have you got an idea of the number of instructions cycle for copying the ROM string into the RAM ? – Daymov Aug 30 '16 at 07:27
  • Glad to hear you succeeded! I'll try to see how much instruction cycles it takes as soon as I receive my components, which hopefully should be in the next couple days. – Asct20 Aug 30 '16 at 16:20
  • 1
    @Tealyf: So I tested in MPLABX simulator how much instruction cycles the sprintf() function takes. On the following example, I get 982 cycles: `int value = 2; char string[16]; sprintf(string, "Value=%i", value);` – Asct20 Sep 16 '16 at 03:08