5
\$\begingroup\$

I want to do a simple project with my PIC24FJ1024GB610, where I use the UART1 module and transmit character 'a', the Transmitter pin is connected to the Receiver pin of the PIC24F, if the receiver buffer received an 'a' the microcontroller will set the LED, else clear the LED.

There are two main parts of the project, initializing the UART1 module, and the PPS (Peripheral Pin Select) module. I don't know which part did I get wrong the UART section or the PPS section.


Code

This is the code FYI

#pragma config FWDTEN = OFF
#pragma config ICS = PGD2

#pragma config FNOSC = FRC//8MHz Fcy = 4MHz

#include "xc.h" 

#define DELAY_105uS asm volatile ("REPEAT, #4201"); Nop();//105us delay

int main(void) 
{
    /*LED*/
    TRISAbits.TRISA0 = 0;


    /*UART1 Setup*/
    U1BRG = 25; //((4000000)/(16*9600)) - 1

    U1MODEbits.UEN = 0;//only U1RX and U1TX
    U1MODEbits.BRGH = 0;//normal baud rate speed
    U1MODEbits.PDSEL = 0;//8 bits, No Parity
    U1MODEbits.STSEL = 0;//One Stop Bit

    U1STAbits.UTXISEL1 = 0;
    U1STAbits.UTXISEL0 = 0;//TX Int Flag set when a char is transferred from TXBUF -> TSR
    U1STAbits.URXISEL = 0;//RX Int Flag set when char is trans from RSR -> RX BUF

    /*PPS Setup*/

    //U1RX -> RD10 -> RP3
    TRISDbits.TRISD10 = 1;//input

    //U1TX -> RD9 -> RP4
    TRISDbits.TRISD9 = 0;//output

    RPINR18bits.U1RXR = 3;//RP3 -> U1RX
    RPOR2bits.RP4R = 3;//RP4 -> U1TX

    asm volatile    ("MOV #OSCCON, w1   \n"
                    "MOV #0x46, w2      \n"
                    "MOV #0x57, w3      \n"
                    "MOV.b w2, [w1]     \n"
                    "MOV.b w3, [w1]     \n"
                    "BSET OSCCON, #6");

    /*Enable UART, TX, RX and the Delay*/
    U1MODEbits.UARTEN = 1;
    U1STAbits.UTXEN = 1;
    U1STAbits.URXEN = 1;

    DELAY_105uS

    U1TXREG = 'a';



    while(1)
    {
        if(U1RXREG == 'a')
            LATAbits.LATA0 = 1;
        else
            LATAbits.LATA0 = 0;
    }

    return 0;
}

UART section

UART is on section 19.0 of the datasheet

  • I am using UART1, and the data format will be 8 bits, No Parity, and one Stop bit.
  • Desired Baud Rate is 9600, Fcy (Fosc/2) is 4MHz, using equation the Baud Rate equation at section 19.1 of the datasheet, we get U1BRG = ((4000000)/(16*9600)) - 1 = 25
  • Enable the UART, Transmitter, and Reciever, in order for the UART to transmit properly a delay of at least 1/Baud Rate (105us) (section 5.3, Example 5-1 of the dsPIC33/PIC24 Family Reference Manual - UART
  • Write 'a' into the Transmit Buffer (U1TXREG), and if the Recieve Buffer received an 'a', the LED will be lit.

PPS section

Before enabling the UART, and the Transmitter, we need to assign the Receive pin (U1RX) and the Transmit pin (U1TX) to remappable input and output pins using the PPS module.

This is what I understood so far from mapping the peripheral pins to the remappable pins section 11.4 of the datasheet:

  • If the peripheral was a digital input (U1RX), we need to make the pin a digital and an input pin using the ANSx (if applicable) and TRISx registers.
  • Assign input using the RPINRx register, in our case RPINR18bits.U1RXR = 3;//U1RX -> RP3
  • Map peripheral output using RPORx register in my case RPOR2bits.RP4R = 3;//RP4 -> U1TX
  • In order to write to the PPS registers, we need to clear the IOLOCK bit in the OSCCON register
  • In order to clear or set the IOLOCK bit a Lock/Unlock sequence must be performed in assembly (as it is time-critical), where you move 0x46 to OSCCONL then 0x57 to OSCCONL.
  • After a reset, the IOLOCK is cleared by default and we only need to set it after assigning the PPS pins the peripherals, so we only need to perform a lock sequence.

    asm volatile    ("MOV #OSCCON, w1   \n"
                    "MOV #0x46, w2      \n"
                    "MOV #0x57, w3      \n"
                    "MOV.b w2, [w1]     \n"
                    "MOV.b w3, [w1]     \n"
                    "BSET OSCCON, #6");
    

section 11.4.5, Example 11-4 of the datasheet


Debugging

However, when I debug the code, and move to the U1REXREG there isn't any char stored in it?

enter image description here

Where did I go wrong? In the PPS section, or the UART section?

Kindly assist me with my problem.


EDIT1

This is the modified section of the code

//Everything above this section remained the same

/*Enable UART, TX, RX and the Delay*/
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1;
U1STAbits.URXEN = 1;

IFS0bits.U1TXIF = 0;//clear TX flag

DELAY_105uS

while(U1STAbits.UTXBF);//move when not full

while(U1STAbits.TRMT == 0);

U1TXREG = 'a';



while(1)
{
    if(U1STAbits.FERR)
        continue;

    if(U1STAbits.OERR)
    {
        U1STAbits.OERR = 0;
        continue;
    }

    if(U1STAbits.URXDA)
    {
        if(U1RXREG == 'a')
            LATAbits.LATA0 = 1;
        else
            LATAbits.LATA0 = 0;
    }
}

return 0;
}

When I debugged the code, it reaches if(U1STAbits.URXDA) and bypasses because URXDA bit is 0, which means that my UART didn't receive any character.

There are two possibilities either the transmitter is not transmitting or the receiver is not receiving, that is why I cleared the U1TXIF.

When I wrote to U1TXREG, IFS0bits.U1TXIF is set, which means the problem is with the receiver.


EDIT 2

I debugged it using an oscilloscope, and through the debugger, and this is what I found:

  • Put RPO2R and RPIN18R below the assembly code, I found that after setting the IOLOCK bit, I don't see any changes in the registers, and U1TX pin doesn't go to HIGH after setting the U1TXEN bit, which means that there are no issues with the PPS

  • probed the U1TX with the ground, and tried to capture any digital signal coming from the U1TX pin, there wasn't any signal, which means that the UART isn't configured properly.


EDIT 3

something weird happened.

I tried redoing the code and putting it in functions to make easier to understand for me.

#pragma config FWDTEN = OFF
#pragma config ICS = PGD2

#pragma config FNOSC = FRC//8MHz Fcy = 4MHz

#include "xc.h"

#define DELAY_105uS asm volatile ("REPEAT, #4201"); Nop ();

void uart_init (void);
void PPS_select (void);
void send_char (char ch);
char receive_char (void);

void start_uart (void);

void init_blink_led (void);

void blink_led (void);

int main(void) 
{
    uart_init ();
    PPS_select ();
    init_blink_led ();    


    start_uart ();

    //send_char('C');

    while(1)
    {
        send_char('C');

        if(IFS0bits.U1RXIF)
            blink_led ();
    }

    return 0;
}

void uart_init (void)
{
    //BRG
    U1BRG = 25;

    //U1MODE
    U1MODE = 0;

    U1MODEbits.UEN = 0;
    U1MODEbits.ABAUD = 0;
    U1MODEbits.BRGH = 0;
    U1MODEbits.PDSEL = 0;
    U1MODEbits.STSEL = 0;

    //U1STA
    U1STAbits.UTXISEL1 = 0;
    U1STAbits.UTXISEL0 = 0;//U1TXIF set when write to buffer
    U1STAbits.URXISEL = 0;//U1RXIF set when buffer receives char    
}

void send_char (char ch)
{
    //check if buffer is full
    while(U1STAbits.UTXBF);

    //check if there is no transmission
    while(!U1STAbits.TRMT);

    //send char ch
    U1TXREG = ch;
}

char receive_char (void)
{
    char receivedChar = 0;

    //Parity error?
    while(U1STAbits.PERR);

    //any other error?
    while(U1STAbits.FERR);

    while(U1STAbits.OERR){U1STAbits.OERR = 0;}

    while(!U1STAbits.RIDLE);

    if(IFS0bits.U1RXIF)
    {
        IFS0bits.U1RXIF = 0;
        receivedChar = U1RXREG;
    }

    return receivedChar;

    //return U1RXREG
}

void init_blink_led (void)
{
    //timer1 (250 ms), RA0 digital output
    T1CON = 0;
    T1CONbits.TCS = 0;
    T1CONbits.TCKPS = 2;

    /*
     t = count*Tcy*8
     * Fcy = 4MHz Tcy = 0.25 us 
     * t = 250 ms = 250 000 us
     * 
     * count = 250000/(0.25*8) = 1000000 
     */

    PR1 = 15625;

    TRISAbits.TRISA0 = 0;//output    
}

void blink_led (void)
{
    //if U1RXIF (received)
    //blink led
    T1CONbits.TON = 1;//start timer

    if(IFS0bits.T1IF)
    {
        IFS0bits.T1IF = 0;
        //LATA0bits.LATA0 = ~LATAbits.LATA0;

        LATAbits.LATA0 = ~LATAbits.LATA0;
    }

}

void PPS_select (void)
{
    //U1RX -> RD10 -> RP3
    TRISDbits.TRISD10 = 1;//input

    //U1TX -> RD9 -> RP4
    TRISDbits.TRISD9 = 0;//output

    RPINR18bits.U1RXR = 3;//RP3 -> U1RX
    RPOR2bits.RP4R = 3;//RP4 -> U1TX

    asm volatile    ("MOV #OSCCON, w1   \n"
                    "MOV #0x46, w2      \n"
                    "MOV #0x57, w3      \n"
                    "MOV.b w2, [w1]     \n"
                    "MOV.b w3, [w1]     \n"
                    "BSET OSCCON, #6");
}

void start_uart (void)
{
    U1MODEbits.UARTEN = 1;
    U1STAbits.UTXEN = 1;
    U1STAbits.URXEN = 1;

    IFS0bits.U1TXIF = 0;
    IFS0bits.U1RXIF = 0;

    DELAY_105uS     
}

At first I put the send_char('C') above the while(1) block, similar to the code of my original post, and saw no change, once UTXEN bit was set, the voltage of the U1TX pin remains HIGH.

However when I put the send_char('C') inside the while(1) block and programmed the code into my Development Board (Explorer 16/32 with a PIC24FJ1024GB610). I started seeing a stream of pulses at that pin, just like in the picture below (right). I captured the signal in the left.

enter image description here

However, it was a totally different story when I tried debugging it.

At IFS0bits.U1TXIF = 0

  • UTXBF = 0
  • TRMT = 1
  • URXDA = 0
  • RIDLE = 0
  • U1TXIF = 0
  • U1RXIF = 0
  • The voltage of the U1TX pin is HIGH

At the beginning of the send_char(char ch) function

  • UTXBF = 0
  • TRMT = 1
  • URXDA = 0
  • RIDLE = 1
  • U1TXIF = 0
  • U1RXIF = 0

After stepping out of U1TXREG = ch

  • U1TXBF = 0
  • TRMT = 0
  • URXDA = 0
  • RIDLE = 1
  • U1TXIF = 1
  • U1RXIF = 0

When the debugger reaches the send_char(char ch) for the second time at the while(!U1STAbits.TRMT) it gets stuck there because there is still a transmission going on TRMT = 0.

I think when debugging the UART, the transmission becomes slower than stepping in and out of the code.

However, that doesn't explain why I couldn't capture the signal when the send_char(char ch) function or the U1TXREG were out of the was out of the while loop.

\$\endgroup\$
2
  • \$\begingroup\$ Note that writing to U1TXREG will likely still cause the UART module to try and send a character even if other things (TRIS, ANSEL, PPS) are miss-configured. Therefore U1TXIF will probably still set weather or not a character actually came out of the IO pin. If you are sure of the rest of the configuration and hardware then checking U1TXIF is valid, but if you are not sure of those other factors then U1TXIF being set does not prove the character actually came out. I suggest you check with a scope if you have one, or at least a DMM. \$\endgroup\$
    – user4574
    May 12, 2020 at 14:07
  • \$\begingroup\$ @user4574, Yes I found the PPS to be configured correctly (in Edit 2), when I write to U1TXREG it sets the U1TXIF, I will try changing the Interrupt flag gets set by changing UTXISEL = 0b10. Interrupt will be be generated when the data gets transferred out of the TX buffer, and the TSR. Lets see what happens \$\endgroup\$ May 12, 2020 at 15:03

2 Answers 2

0
\$\begingroup\$

It looks to me like you are almost there. It appears you aren't giving enough time for the data to get transmitted before you are trying to read the receive buffer.

Example 7-1 of the dsPIC33/PIC24 Family Reference Manual - UART should help. Essentially you have to check that the URXDA bit (UART1 Receive Buffer Data Available bit) in U1STA register is set before you read the receive buffer.

You should probably also check for other receive errors as well, so I have included the other error checking included in Example 7-1 above for completeness.

I don't have any way of checking if below works, but please give this a try and let me know the outcome.

#pragma config FWDTEN = OFF
#pragma config ICS = PGD2

#pragma config FNOSC = FRC//8MHz Fcy = 4MHz

#include "xc.h" 

#define DELAY_105uS asm volatile ("REPEAT, #4201"); Nop();//105us delay

int main(void) 
{
    /*LED*/
    TRISAbits.TRISA0 = 0;


    /*UART1 Setup*/
    U1BRG = 25; //((4000000)/(16*9600)) - 1

    U1MODEbits.UEN = 0;//only U1RX and U1TX
    U1MODEbits.BRGH = 0;//normal baud rate speed
    U1MODEbits.PDSEL = 0;//8 bits, No Parity
    U1MODEbits.STSEL = 0;//One Stop Bit

    U1STAbits.UTXISEL1 = 0;
    U1STAbits.UTXISEL0 = 0;//TX Int Flag set when a char is transferred from TXBUF -> TSR
    U1STAbits.URXISEL = 0;//RX Int Flag set when char is trans from RSR -> RX BUF

    /*PPS Setup*/

    //U1RX -> RD10 -> RP3
    TRISDbits.TRISD10 = 1;//input

    //U1TX -> RD9 -> RP4
    TRISDbits.TRISD9 = 0;//output

    RPINR18bits.U1RXR = 3;//RP3 -> U1RX
    RPOR2bits.RP4R = 3;//RP4 -> U1TX

    asm volatile    ("MOV #OSCCON, w1   \n"
                    "MOV #0x46, w2      \n"
                    "MOV #0x57, w3      \n"
                    "MOV.b w2, [w1]     \n"
                    "MOV.b w3, [w1]     \n"
                    "BSET OSCCON, #6");

    /*Enable UART, TX, RX and the Delay*/
    U1MODEbits.UARTEN = 1;
    U1STAbits.UTXEN = 1;
    U1STAbits.URXEN = 1;

    DELAY_105uS

    U1TXREG = 'a';



    while(1)
    {
        /* Check for receive errors */
        if(U1STAbits.FERR == 1)
        {
            continue;
        }

        /* Must clear the overrun error to keep UART receiving */
        if(U1STAbits.OERR == 1)
        {
            U1STAbits.OERR = 0;
            continue;
        }

        /* Get the data */
        if(U1STAbits.URXDA == 1) // Check if data buffer has any data in it
        {
            if(U1RXREG == 'a')
                LATAbits.LATA0 = 1;
            else
                LATAbits.LATA0 = 0;
        }
    }

    return 0;
}
\$\endgroup\$
10
  • \$\begingroup\$ Thank you for the response, I copied this code, and added a few stuff like while(U1STAbits.UTXBF);//if buffer is full don't write char and while(U1STAbits.TRMT == 0)//if transmission in progress don't write char, it didn't work the reason was when it reaches if(U1STAbits.URXDA), it doesn't go inside because URXDA is 0, which means that it is not recieving any char. It either means it is not sending, or it is not receiving I will put the new code and findings in a new edit. \$\endgroup\$ May 12, 2020 at 2:44
  • \$\begingroup\$ @Forat Have you checked with an oscilloscop? \$\endgroup\$
    – Mike
    May 12, 2020 at 7:03
  • \$\begingroup\$ @Mike Yes I have checked, when the U1TXEN bit gets set the pin goes to HIGH, I tried capturing the signal (setting the trigger level, getting triggered at a negative pulse, setting the appropriate time base), there is no signal detected \$\endgroup\$ May 12, 2020 at 13:11
  • \$\begingroup\$ could you see the TX Signal? \$\endgroup\$
    – Mike
    May 12, 2020 at 16:02
  • 1
    \$\begingroup\$ @Forat You've probably done this already but maybe double check again that you have connected your pins correctly physically. Maybe double check this by connecting an LED to each of the pins [RP4 = RD9 for transmit and RP3=RD10 for receive] and make a simple program that just toggles the LEDs on RD9 and RD10 \$\endgroup\$
    – Qbort
    May 13, 2020 at 14:02
0
+50
\$\begingroup\$

Forat,

The code you posted does only a minimal configuration of the PIC24FJ1024GB610. This approach can result in your application failing to work as expected.

Here is my take on your application that sets up everything I could think of:

/*
 * File:   main.c
 * Author: dan1138
 *
 * Created on May 13, 2020, 9:09 AM
 * 
 *                                                                      PIC24FJ1024GB610
 *             +------------------+             +------------------+             +------------------+             +------------------+
 *           1 : <> RG15          :     PGC  26 : <> RB6/RP6/PGC2  :          51 : <> RF3/RP16      :          76 : <> RD1/RP24      :
 *     3V3   2 : <> VDD           :     PGD  27 : <> RB7/RP7/PGD2  :          52 : <> RF2/RP30      :          77 : <> RD2/RP23      :
 *           3 : <> RE5           :          28 : <> RA9           :          53 : <> RF8/RP15      :          78 : <> RD3/RP22      :
 *           4 : <> RE6           :          29 : <> RA10          :          54 : <> RF7           :          79 : <> RD12/RPI42    :
 *           5 : <> RE7           :     3V3  30 : <> VDD           :          55 : <> RF6           :          80 : <> RD13          :
 *           6 : <> RC1/RPI38     :     GND  31 : <> VSS           :          56 : <> RG3           :          81 : <> RD4/RP25      :
 *           7 : <> RC2/RPI39     :          32 : <> RB8/RP8       :          57 : <> RG2           :          82 : <> RD5/RP20      :
 *           8 : <> RC3/RPI40     :          33 : <> RB9/RP9       :          58 : <> RA2           :          83 : <> RD6           :
 *           9 : <> RC4/RPI41     :          34 : <> RB10          :          59 : <> RA3           :          84 : <> RD7           :
 *          10 : <> RG6/RP21      :          35 : <> RB11          :          60 : <> RA4           :    10uF  85 : <> VCAP          :
 *          11 : <> RG7/RP26      :     GND  36 : <> VSS           :          61 : <> RA5           :          86 : <> N/C           :
 *          12 : <> RG8/RP19      :     3V3  37 : <> VDD           :     3v3  62 : <> VDD           :          87 : <> RF0           :
 *     VPP  13 : <> MCLR          :          38 : <> RA1           :          63 : <> RC12          :          88 : <> RF1           :
 *          14 : <> RG9/RP27      :          39 : <> RF13/RP31     :          64 : <> RC15          :          89 : <> RG1           :
 *     GND  15 : <> VSS           :          40 : <> RF12/RPI32    :     GND  65 : <> VSS           :          90 : <> RG0           :
 *     3V3  16 : <> VDD           :          41 : <> RB12          :          66 : <> RA14/RPI36    :          91 : <> RA6           :
 *          17 : <> RA0           :          42 : <> RB13          :          67 : <> RA15/RPI35    :          92 : <> RA7           :
 *          18 : <> RE8/RPI33     :          43 : <> RB14/RP14     :          68 : <> RD8/RP2       :          93 : <> RE0           :
 *          19 : <> RE9/RPI34     :          44 : <> RB15/RP29     :    U1TX  69 : <> RD9/RP4       :          94 : <> RE1           :
 *          20 : <> RB5/RP18/PGC3 :     GND  45 : <> VSS           :    U1RX  70 : <> RD10/RP3      :          95 : <> RG14          :
 *          21 : <> RB4/RP28/PGD3 :     3V3  46 : <> VDD           :          71 : <> RD11/RP12     :          96 : <> RG12          :
 *          22 : <> RB3           :          47 : <> RD14/RPI43    :          72 : <> RD0/RP11      :          97 : <> RG13          :
 *          23 : <> RB2/RP13      :          48 : <> RD15/RP5      :          73 : <> RC13          :          98 : <> RE2           :
 *          24 : <> RB1/RP1/PGC1  :          49 : <> RF4/RP10      :          74 : <> RC14/RPI37    :          99 : <> RE3           :
 *          25 : <> RB0/RP0/PGD1  :          50 : <> RF5/RP17      :     GND  75 : <> VSS           :         100 : <> RE4           :
 *             +------------------+             +------------------+             +------------------+             +------------------+
 *                                                                      TQFP-100
 */

#pragma config BTMODE = SINGLE          // Boot Mode Configuration bits (Device is in Single Boot (legacy) mode)
#pragma config BWRP = OFF               // Boot Segment Write-Protect bit (Boot Segment may be written)
#pragma config BSS = DISABLED           // Boot Segment Code-Protect Level bits (No Protection (other than BWRP))
#pragma config BSEN = OFF               // Boot Segment Control bit (No Boot Segment)
#pragma config GWRP = OFF               // General Segment Write-Protect bit (General Segment may be written)
#pragma config GSS = DISABLED           // General Segment Code-Protect Level bits (No Protection (other than GWRP))
#pragma config CWRP = OFF               // Configuration Segment Write-Protect bit (Configuration Segment may be written)
#pragma config CSS = DISABLED           // Configuration Segment Code-Protect Level bits (No Protection (other than CWRP))
#pragma config AIVTDIS = ON             // Alternate Interrupt Vector Table bit (Enabled AIVT)
#pragma config BSLIM = 0x1FFF           // Boot Segment Flash Page Address Limit bits (Boot Segment Flash page address  limit)
#pragma config FNOSC = FRC              // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config PLLMODE = PLL96DIV2      // PLL Mode Selection (96 MHz PLL. (8 MHz input))
#pragma config IESO = OFF               // Two-speed Oscillator Start-up Enable bit (Start up with user-selected oscillator source)
#pragma config POSCMD = HS              // Primary Oscillator Mode Select bits (HS Crystal Oscillator Mode)
#pragma config OSCIOFCN = ON            // OSC2 Pin Function bit (OSC2 is general purpose digital I/O pin)
#pragma config SOSCSEL = ON             // SOSC Power Selection Configuration bits (SOSC is used in crystal (SOSCI/SOSCO) mode)
#pragma config PLLSS = PLL_FRC          // PLL Secondary Selection Configuration bit (PLL is fed by the on-chip Fast RC (FRC) oscillator)
#pragma config IOL1WAY = OFF            // Peripheral pin select configuration bit (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD           // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)
#pragma config WDTPS = PS32768          // Watchdog Timer Postscaler bits (1:32,768)
#pragma config FWPSA = PR128            // Watchdog Timer Prescaler bit (1:128)
#pragma config FWDTEN = ON_SWDTEN       // Watchdog Timer Enable bits (WDT Enabled/Disabled (controlled using SWDTEN bit))
#pragma config WINDIS = OFF             // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config WDTWIN = WIN25           // Watchdog Timer Window Select bits (WDT Window is 25% of WDT period)
#pragma config WDTCMX = WDTCLK          // WDT MUX Source Select bits (WDT clock source is determined by the WDTCLK Configuration bits)
#pragma config WDTCLK = LPRC            // WDT Clock Source Select bits (WDT uses LPRC)
#pragma config BOREN = SBOREN           // Brown Out Enable bit (Controlled by SBOREN)
#pragma config LPCFG = OFF              // Low power regulator control (No Retention Sleep)
#pragma config DNVPEN = ENABLE          // Downside Voltage Protection Enable bit (Downside protection enabled using ZPBOR when BOR is inactive)
#pragma config ICS = PGD2               // ICD Communication Channel Select bits (Communicate on PGEC1 and PGED1)
#pragma config JTAGEN = OFF             // JTAG Enable bit (JTAG is disabled)
#pragma config BTSWP = OFF              // BOOTSWP Disable (BOOTSWP instruction disabled)
#pragma config ALTCMPI = DISABLE        // Alternate Comparator Input Enable bit (C1INC, C2INC, and C3INC are on their standard pin locations)
#pragma config TMPRPIN = OFF            // Tamper Pin Enable bit (TMPRN pin function is disabled)
#pragma config SOSCHP = OFF             // SOSC High Power Enable bit (valid only when SOSCSEL = 1 (Enable SOSC low power mode)
#pragma config ALTVREF = ALTREFEN       // Alternate Voltage Reference Location Enable bit (VREF+ and CVREF+ on RA10, VREF- and CVREF- on RA9)

#include "xc.h"

/* 
 * Define the target system clock frequency.
 * 
 * The initialization MUST set the system clock to support these definitions.
 * 
 */
#define FSYS (32000000UL)
#define FCYC (FSYS/2UL)
/* define map input pin numbers */ 
enum 
{   
    RPI_RB0  = 0 ,
    RPI_RB1  = 1 ,
    RPI_RD8  = 2 ,
    RPI_RD10 = 3 ,
    RPI_RD9  = 4 ,
    RPI_RD15 = 5 ,
    RPI_RB6  = 6 ,
    RPI_RB7  = 7 ,
    RPI_RB8  = 8 ,
    RPI_RB9  = 9 ,
    RPI_RF4  = 10,
    RPI_RD0  = 11,
    RPI_RD11 = 12,
    RPI_RB2  = 13,
    RPI_RB14 = 14,
    RPI_RF8  = 15,
    RPI_RF3  = 16,
    RPI_RF5  = 17,
    RPI_RB5  = 18,
    RPI_RG8  = 19,
    RPI_RD5  = 20,
    RPI_RG6  = 21,
    RPI_RD3  = 22,
    RPI_RD2  = 23,
    RPI_RD1  = 24,
    RPI_RD4  = 25,
    RPI_RG7  = 26,
    RPI_RG9  = 27,
    RPI_RB4  = 28,
    RPI_RB15 = 29,
    RPI_RF2  = 30,
    RPI_RF13 = 31,
    RPI_RF12 = 32,
    RPI_RE8  = 33,
    RPI_RE9  = 34,
    RPI_RA15 = 35,
    RPI_RA14 = 36,
    RPI_RC14 = 37,
    RPI_RC1  = 38,
    RPI_RC2  = 39,
    RPI_RC3  = 40,
    RPI_RC4  = 41,
    RPI_RD12 = 42,
    RPI_RD14 = 43,
    RPI_NONE = 0x3F 
};  

/* define map output function numbers */ 
enum 
{   
    RPO_NONE    = 0,    /* (Pin Disabled)            */
    RPO_C1OUT   = 1,    /* Comparator 1 Output       */
    RPO_C2OUT   = 2,    /* Comparator 2 Output       */
    RPO_C3OUT   = 26,   /* Comparator 3 Output       */
    RPO_SDO1    = 7,    /* SPI1 Data Output          */
    RPO_SCK1OUT = 8,    /* SPI1 Clock Output         */
    RPO_SS1OUT  = 9,    /* SPI1 Slave Select Output  */
    RPO_SDO2    = 10,   /* SPI2 Data Output          */
    RPO_SCK2OUT = 11,   /* SPI2 Clock Output         */
    RPO_SS2OUT  = 12,   /* SPI2 Slave Select Output  */
    RPO_SDO3    = 23,   /* SPI3 Data Output          */
    RPO_SCK3OUT = 24,   /* SPI3 Clock Output         */
    RPO_SS3OUT  = 25,   /* SPI3 Slave Select Output  */
    RPO_OC1     = 13,   /* Output Compare 1          */
    RPO_OC2     = 14,   /* Output Compare 2          */
    RPO_OC3     = 15,   /* Output Compare 3          */
    RPO_OCM4    = 16,   /* CCP4 Output Compare       */
    RPO_OCM5    = 17,   /* CCP5 Output Compare       */
    RPO_OCM6    = 18,   /* CCP6 Output Compare       */
    RPO_OCM7    = 27,   /* CCP7 Output Compare       */
    RPO_U1TX    = 3,    /* UART1 Transmit            */
    RPO_U1RTS   = 4,    /* UART1 Request-to-Send     */
    RPO_U2TX    = 5,    /* UART2 Transmit            */
    RPO_U2RTS   = 6,    /* UART2 Request-to-Send     */
    RPO_U3TX    = 19,   /* UART3 Transmit            */
    RPO_U3RTS   = 20,   /* UART3 Request-to-Send     */
    RPO_U4TX    = 21,   /* UART4 Transmit            */
    RPO_U4RTS   = 22,   /* UART4 Request-to-Send     */
    RPO_REFO    = 28,   /* Reference Clock Output    */
    RPO_CLC1OUT = 29,   /* CLC1 Output               */
    RPO_CLC2OUT = 30,   /* CLC2 Output               */
    RPO_RTCC    = 31,   /* RTCC Output               */
};  

/* Initialize this PIC */ 
void PIC_Init(void) 
{   
    unsigned int ClockSwitchTimeout;

    /* 
    ** Disable all interrupt sources 
    */ 
    __builtin_disi(0x3FFF); /* disable interrupts for 16383 cycles */ 
    IEC0 = 0; 
    IEC1 = 0; 
    IEC2 = 0; 
    IEC3 = 0; 
    IEC4 = 0; 
    IEC5 = 0; 
    IEC6 = 0; 
    IEC7 = 0; 
    __builtin_disi(0x0000); /* enable interrupts */ 

    /*
     * At Power On Reset the configuration words set the system clock
     * to use the FRC oscillator. At this point we need to enable the
     * PLL to get the system clock running at 32MHz.
     * 
     * Clock switching on the 24FJ family with the PLL can be a bit tricky.
     * 
     * First we need to check if the configuration words enabled clock
     * switching at all, then turn off the PLL, then setup the PLL and
     * finally enable it. Sounds simple, I know. Make sure you verify this 
     * clock setup on the real hardware.
     */
    if(!OSCCONbits.CLKLOCK) /* if primary oscillator switching is unlocked */
    {
        /* Select primary oscillator as FRC */
        __builtin_write_OSCCONH(0b000);

        /* Request switch primary to new selection */
        __builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));

        /* wait, with timeout, for clock switch to complete */
        for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);

        CLKDIV   = 0x0000; /* set for FRC clock 8MHZ operations */

        /* Select primary oscillator as FRCPLL */
        __builtin_write_OSCCONH(0b001);

        /* Request switch primary to new selection */
        __builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));

        /* wait, with timeout, for clock switch to complete */
        for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);

        /* wait, with timeout, for the PLL to lock */
        for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && !OSCCONbits.LOCK;);

        /* at this point the system oscillator should be 32MHz */
    }

    ANSA   =  0x0000; /* Set for digital I/O */ 
    ANSB   =  0x0000; /* Set for digital I/O */ 
    ANSC   =  0x0000; /* Set for digital I/O */ 
    ANSD   =  0x0000; /* Set for digital I/O */ 
    ANSE   =  0x0000; /* Set for digital I/O */ 
    ANSF   =  0x0000; /* Set for digital I/O */ 
    ANSG   =  0x0000; /* Set for digital I/O */ 

    CM1CON  = 0x0000; 
    CM2CON  = 0x0000; 
    CM3CON  = 0x0000; 

    _NSTDIS = 1;    /* disable interrupt nesting */ 

    TRISA   = 0xFFFF; 
    TRISB   = 0xFFFF; 
    TRISC   = 0xFFFF; 
    TRISD   = 0xFFFF; 
    TRISE   = 0xFFFF; 
    TRISF   = 0xFFFF; 
    TRISG   = 0xFFFF; 

    /* Unlock Registers */ 
    __builtin_write_OSCCONL(OSCCON & 0xBF); 

    _U1RXR  = RPI_RD10;     /*  UART1 Receive  pin RD10        */ 
    _RP4R   = RPO_U1TX;     /*  UART1 Transmit pin RD9         */

    /* Lock Registers */ 
    __builtin_write_OSCCONL(OSCCON | 0x40); 

}
/*
** U1BRG register value and baudrate error calculation
*/
#define U1_BAUD 9600L
#define U1_BRGH_VALUE 0

#if U1_BRGH_VALUE == 0
#define U1_BRGH_SCALE 4L
#else
#define U1_BRGH_SCALE 16L
#endif

#define U1_BRGREG ( (FCYC + (U1_BRGH_SCALE * U1_BAUD)/1 )/(U1_BRGH_SCALE * U1_BAUD)-1L)

#if U1_BRGREG > 65535
#error Cannot set up UART2 for the FCYC and BAUDRATE. Correct values in init.h and uart.h files.
#endif

/*
** Check if baud error greater than 2.5 percent
*/
#define REAL_BAUDRATE ( FCYC / ( U1_BRGH_SCALE * ( U1_BRGREG + 1L) ) )
#if (REAL_BAUDRATE > (U1_BAUD + (U1_BAUD * 25L) / 1000L)) || (REAL_BAUDRATE < (U1_BAUD - (U1_BAUD * 25L) / 1000L))
#error UART baudrate error greater than 2.5 percent for the FCYC and U1_BAUD. Correct values in uart.c file.
#endif
#undef REAL_BAUDRATE

volatile char Temp;

void Spin(unsigned long SpinTime)
{
    if(SpinTime) do
    {
        Nop();
    } while (--SpinTime);
}

int main(void) 
{

    PIC_Init();

    _U1TXIE = 0;
    _U1RXIE = 0;
    _U1ERIE = 0;
    _U1RXIP = 0b100;
    _U1TXIP = 0b100;
    _U1ERIP = 0b100;

    U1MODE = 0;
    U1STA  = 0;
    U1BRG             = U1_BRGREG;
    U1MODEbits.BRGH   = U1_BRGH_VALUE;
    U1MODEbits.UARTEN = 1;
    U1STAbits.UTXEN   = 1;
    _U1RXIF = 0;            /* reset RX flag */

    TRISAbits.TRISA0 = 0;   /* make RA0 an output */
    TRISAbits.TRISA1 = 0;   /* make RA1 an output */

    Spin(60000ul);          /* Wait for hardware to settle down */

    Temp = U1RXREG;         /* Empty FIFO */
    Temp = U1RXREG;
    Temp = U1RXREG;
    Temp = U1RXREG;
    Temp = U1RXREG;
    Temp = U1RXREG;

    _U1RXIF = 0;            /* Clear assert for RX event */

    while(1)
    {
        if (U1STAbits.OERR != 0)
        {
            U1STAbits.OERR = 0;
        }
        if(_U1RXIF == 1)
        {
            do
            {
                if (U1STAbits.OERR != 0)
                {
                    U1STAbits.OERR = 0;
                }
                else
                {
                    Temp = U1RXREG;

                    if(Temp == 0x7F)
                    {
                        LATAbits.LATA0 ^= 1;
                    }
                    else
                    {
                        LATAbits.LATA0 = 0;
                    }
                }
            } while(U1STAbits.URXDA != 0);
            _U1RXIF = 0;
        }

        if(U1STAbits.TRMT == 1)
        {
          U1TXREG = 0x7F;
          LATAbits.LATA1 ^= 1;
        }
    }

    return 0;
}

Please give this a spin in your hardware.

Note for new players: This code DOES NOT WORK WITH THE MPLABX SIMULATOR.

EDIT

Fix bugs so it now works in real hardware.

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for writing me this code, there were no syntax errors, unfortunately, it didn't work. I will debug it and go through it in more details, I will reply to you with my observations. \$\endgroup\$ May 14, 2020 at 17:31
  • \$\begingroup\$ @Forat, Yes were some bugs. They are now fixed and it works in real hardware. \$\endgroup\$
    – Dan1138
    May 14, 2020 at 19:57
  • \$\begingroup\$ yes, your code did work, thank you for the help @Dan1138 \$\endgroup\$ May 17, 2020 at 4:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.