Author Topic: UART communication: PIC18F and PC  (Read 2929 times)

0 Members and 3 Guests are viewing this topic.

Offline Lucky-LukaTopic starter

  • Supporter
  • ****
  • Posts: 240
  • Country: it
UART communication: PIC18F and PC
« on: March 21, 2021, 05:35:59 pm »
Hi all
I am experimenting UART communication between a PIC18F4580 and the PC through USB-TTL Converter Module.
I've written a program that performs a loopback test: I write something on the PC using Realterm and the PIC will send it back.
I've set up the UART in asynchronous 8-bit mode.
It works but if I enable the line "PIE1bits.TXIE = 1; // UART Transmit Interrupt Enable bit" it doesn't work anymore and I can't understand why.
I haven't really understood how and why to set that bit after reading the datasheet:

Quote
Once the TXREG register transfers the data to the TSR
register (occurs in one TCY), the TXREG register is empty
and the TXIF flag bit (PIR1) is set. This interrupt can
be enabled or disabled by setting or clearing the interrupt
enable bit, TXIE (PIE1). TXIF will be set regardless of
the state of TXIE; it cannot be cleared in software. TXIF
is also not cleared immediately upon loading TXREG, but
becomes valid in the second instruction cycle following
the load instruction. Polling TXIF immediately following a
load of TXREG will return invalid results.

Second thing that I can't understand: I thought that I could just send 1 ascii character at the time in this manner. I tried to send  this string "Hello1Hello2Hello3Hello4" and I received it back.
How is it possible that after the first character all the rest is not lost? RCREG is 2 layer FIFO 8 bit register right? Is the string buffered in the PC?
Thanks

Code: [Select]
#include <xc.h>
#include <stdint.h>
#include "stdbool.h"
#include "config.h"
#define _XTAL_FREQ 8000000 // 8MHz


// Function declaration
void UART_RX_Init(void);

// Globals
uint8_t data = 0;
bool got_data_bool = false;

void UART_send(uint8_t *c);
void UART_receiver(uint8_t *c, bool *rx_flag);


void main(void) {
    // 8MHz internal oscillator
    OSCCONbits.IRCF = 0x07;
    // Internal oscillator selected
    OSCCONbits.SCS = 0x03;
    // I wait until the oscillator frequency is stable
    while(OSCCONbits.IOFS != 1);
   
    RCONbits.IPEN = 1; // Enable interrupt priorities
    ei(); // It's like GIE = 1 to enable all interrupts
    INTCONbits.GIEL = 1; // Enable LOW priority interrupt
    //INTCONbits.GIEH = 1; // Enable HIGH priority interrupt
   
    UART_RX_Init(); // Initialize the UART @ 9600bps
   
    while(1){
        if(got_data_bool){
            UART_send(&data);
            got_data_bool = false;
        }
    }
       
    return;
}


// UART initialization
void UART_RX_Init(void){
    // Set the RX-TX pins to be in UART mode, not I/O
    TRISCbits.RC7 = 1;
    TRISCbits.RC6 = 0;
   
    SPBRG = 51; // 9600bps @ 8MHz, 8bit baud rate generator, high speed
   
    RCSTAbits.CREN = 1; // Asyncronous mode continuous receiver
    RCSTAbits.SPEN = 1; // Serial port enabled
    BAUDCONbits.BRG16 = 0; // 8bit baud rate generator
   
    TXSTAbits.SYNC = 0; // Asyncronous mode
    TXSTAbits.BRGH = 1; // High speed
    TXSTAbits.TXEN = 1; // Transmit enable   
   
    IPR1bits.RCIP = 1; // Receive interrupt high priority
    //PIE1bits.TXIE = 1; // UART Transmit Interrupt Enable bit (no software clear needed)
    PIE1bits.RCIE = 1; // UART receive interrupt enabled
}

// High priority ISR
void __interrupt(high_priority) HIGH_ISR_RCIF(void){
    // Check if the ISR is caused by RCIF
    // The EUSART receive buffer, RCREG, is full (clear when RCREG is read)
    // Check interrupt flag bit
    if(PIR1bits.RCIF){
        UART_receiver(&data,&got_data_bool);
        PIR1bits.RCIF = 0; // Clear interrupt flag
    }
}

void UART_send(uint8_t *c){
    // It is more efficient to use TXIF rather than polling TRMT.
    //while(!TXSTAbits.TRMT); // Wait until TSR register is empty (TMRT = 1 --> TSR empty)
    while(!PIR1bits.TXIF); // Wait until TXREG is empty
    TXREG=*c;
}

void UART_receiver(uint8_t *c, bool *rx_flag){
    // Framing error (can be updated by reading RCREG register and
    // receiving next valid byte)
    if(RCSTAbits.FERR){
        uint8_t er = RCREG;
    // Overrun error (can be cleared by clearing bit CREN)
    }else if(RCSTAbits.OERR){
        RCSTAbits.CREN=0;
        RCSTAbits.CREN=1;
    }else{
        *c = RCREG; // Read the received buffer
        *rx_flag = true;
    }
}
Memento audere semper.
 

Offline Lucky-LukaTopic starter

  • Supporter
  • ****
  • Posts: 240
  • Country: it
Re: UART communication: PIC18F and PC
« Reply #1 on: March 21, 2021, 06:05:53 pm »
Reading the datasheet I thought that TXIF hasn't to be cleared by software but it's cleared when TXREG is loaded.
Am i wrong?
Memento audere semper.
 

Online DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6231
  • Country: es
Re: UART communication: PIC18F and PC
« Reply #2 on: March 21, 2021, 06:13:01 pm »
Hmmm...

In main:
- Interrupt priorities enabled
- Low priority interrupt enabled
- High priority interrupt disabled

Code: [Select]
void main(void) {
    RCONbits.IPEN = 1; // Enable interrupt priorities
    ei(); // It's like GIE = 1 to enable all interrupts
    INTCONbits.GIEL = 1; // Enable LOW priority interrupt
    //INTCONbits.GIEH = 1; // Enable HIGH priority interrupt


In UART_RX_Init:
- Receive interrupt high priority

Code: [Select]
// UART initialization
void UART_RX_Init(void){
    IPR1bits.RCIP = 1; // Receive interrupt high priority
    //PIE1bits.TXIE = 1; // UART Transmit Interrupt Enable bit (no software clear needed)
    PIE1bits.RCIE = 1; // UART receive interrupt enabled
}


And you only have the high priority ISR, so it will never be called?
Code: [Select]
// High priority ISR
void __interrupt(high_priority) HIGH_ISR_RCIF(void)



First try only one way.
Set only TX and send a string to computer every second, and check it works.
If it doesn't work in that routine until you find the problem.

Then set only Rx, send a string from the PC and store it in the PIC ram.
Debug to ensure it getting received correctly.
Again, if it doesn't work, keep trying until you solve the issue.

Only when both are working, try to make them work together.


Reading the datasheet I thought that TXIF hasn't to be cleared by software but it's cleared when TXREG is loaded.
Am i wrong?

Yes, PIC datasheets are usually very clear about this (As you quoted from the datasheet).
Anyways, check the errata, there could be some surprises there.

Your Tx routine should work:
Code: [Select]
void UART_send(uint8_t *c){
    // It is more efficient to use TXIF rather than polling TRMT.
    //while(!TXSTAbits.TRMT); // Wait until TSR register is empty (TMRT = 1 --> TSR empty)
    while(!PIR1bits.TXIF); // Wait until TXREG is empty
    TXREG=*c;
}


Just in case the compiler optimizes to much, I would add a nop before checking the TXIF:
Code: [Select]
asm("nop");
while(!PIR1bits.TXIF); // Wait until TXREG is empt
You can remove it later if you get it working. Don't think on optimizing the code yet.


To start, I would disable all interrupts and try something like this:
Code: [Select]
// Send a string to the computer
while(1){
Delay(1000); // 1 second delay
UART_send('T');
UART_send('X');
UART_send(' ');
UART_send('T');
UART_send('E');
UART_send('S');
UART_send('T');
UART_send('\r');
UART_send('\n');
}

That will ensure the TXIF is working as it should and you're not overwriting / overflowing the TXbuffer.


And to test RX:
Code: [Select]
char RxBuffer[32];
unsigned char RxPos=0;

while(rxPos<32){
if(got_data_bool){
got_data_bool = 0;
RxBuffer[RxPos++] = data ;
}
}
while(1); // Set breakpoint here

Then debug it, send a string from the computer, when it fills up check that the 32 stored chars are correct (No missing or corrupted chars).
« Last Edit: March 21, 2021, 06:36:07 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: Lucky-Luka

Offline Lucky-LukaTopic starter

  • Supporter
  • ****
  • Posts: 240
  • Country: it
Re: UART communication: PIC18F and PC
« Reply #3 on: March 21, 2021, 08:03:57 pm »
To address the other question of the original post, do you know how it is possible that using Realterm I can send strings and not only one ascii character at the time?
Memento audere semper.
 

Online DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6231
  • Country: es
Re: UART communication: PIC18F and PC
« Reply #4 on: March 21, 2021, 10:29:21 pm »
I use YAT (https://sourceforge.net/projects/y-a-terminal/), it's great for that.
I always had a lot of trouble with Realterm.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: Lucky-Luka

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4306
  • Country: us
Re: UART communication: PIC18F and PC
« Reply #5 on: March 22, 2021, 01:48:17 am »
Quote
how [can I] send strings and not only one ascii character at the time?
How do you think those are different?
Don't most terminal emulators let you "paste" into them?
 

Offline TomS_

  • Frequent Contributor
  • **
  • Posts: 851
  • Country: gb
Re: UART communication: PIC18F and PC
« Reply #6 on: March 23, 2021, 06:39:36 am »
A string is just an array of characters. You paste it into your terminal, it queues them up to be transmitted one after the other (usually in a continuous stream), your PIC receives them and echoes them back.

The PIC and the terminal software have no particular concept of a string. All they do is send and receive bytes, whether you supply them one at a time or in a chunk doesn't matter.
 
The following users thanked this post: Lucky-Luka

Online DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6231
  • Country: es
Re: UART communication: PIC18F and PC
« Reply #7 on: March 23, 2021, 01:59:15 pm »
I know it's not correct, but given the context, I perfectly understood the meaning of "sending a string".
Sending data trought the serial port without any delay between characters.
This is very important to ensure the receiving device is capable at handling the data at max speed .
A 1MHz MCU could handle 1Mbit UART if you send a char every 100mS.
But when you send 10 together ... buffer overflow for sure.


So the pic receives
{H}{E}{L}{L}{O}

Instead
{H}...{E}............{L}......{L}.........{O}



« Last Edit: March 23, 2021, 02:04:25 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6231
  • Country: es
Re: UART communication: PIC18F and PC
« Reply #8 on: March 31, 2021, 01:06:12 pm »
pic18 family has indirect addressing, they have few instrucciones over the classic pic16 to add better support for high levels languages like C.

I've used them a lot in C, with code optimization it was fairly good
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: Lucky-Luka


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf