Verifying PIC24EP Clock Frequency by UART Module

Select the protocol

In order to communicate successfully, both sides must use the same scheme, and the same baud rate (more on that later). The default in Windows driver is 8 bits data, 1 stop bit, no parity, often abbreviated as 8-N-1. This works fine for the example, so no need to change it. We also do not need hardware flow control in this simple example.

Select speed mode and compute baud parameter

Here comes the essence of this article. In order for the UART1 to run at desired baud rate, a parameter must be computed from the system clock frequency. Note in a high-end 16 bit PIC such as PIC24EP, the UART module has high/low speed mode select by setting BRGH bit. (0 = low speed, 1 = high speed). We will use BRGH = 1.

Using high speed mode, and given FCY=70e6 and the desired baud rate, we can compute a value to be written to U1BRG

U1BRG = FCY/(4*baud_rate) – 1

With this formula, values for common baud rates are precomputed and defined at the top of source code as follows.

 RPINR18bits.U1RXR = 43;  // U1RX to RP43
 RPOR4bits.RP42R = 1;     // U1TX to RP42

The higher the baud rate, the less error in clock frequency the module could tolerate. So to push the envelope, we will use BRATE115200, the highest baud rate our terminal program could handle.

The code below shows how to initialize UART1 with characteristics as described above

void initU1()  {
    U1MODEbits.STSEL = 0;    // 1-stop bit
    U1MODEbits.PDSEL = 0;    // No parity, 8-data bits
    U1MODEbits.ABAUD = 0;   // Auto-Baud disables
    U1MODEbits.BRGH = 1;  // high speed mode
    U1BRG = BRATE115200;
    U1MODEbits.UARTEN = 1;      // enable UART
    U1STAbits.UTXEN = 1;        // enable UART TX
    IFS0bits.U1RXIF = 0;	//Clear RX interrupt flag
    IEC0bits.U1RXIE = 1;	//Enable RX interrupt
}

Implement transmit/receive functions

Assuming the UART initialization is done correctly, our PIC24EC can communicate with its partner, in this case a host PC. The goal of this application is to receive some simple commands, take action, and send back responses. All messages are in ASCII format. The application software must therefore be able to access receive/transmit buffers of UART1.

Let’s first look at the transmit functions. We only want to send back to host some short text messages such as “ready,” “Timer 1 ON,” “Timer 1 OFF.” This does not take much time to execute, so no interrupt-based scheme is needed. Indeed, we just write some normal C functions for the task.

void PutChrU1(unsigned char c)
{   
    while(U1STAbits.UTXBF) {}
    U1TXREG = c;
}
 
void PutStrU1( char *data)
{
  do
  {  // Transmit a byte
    if(*data>0x01) {PutChrU1(*data);}
  } while( *data++ );
}

The baseline function to send a character is PutChrU1( ), which is called by PutStrU1( ) to transmit a string of characters. For example, PutStrU1(“Ready\r\n”); would send a ready message followed by line feed and carriage return.

Now consider the receiving part. Since we do not know when a message would arrive, it is better to use an ISR instead of a polling scheme. The ISR for UART receiver is named _U1RXInterrupt(). In general, it is a good practice to keep an ISR short an simple. So in a real application, we might only want to read from the receive buffer and put in somewhere in the memory, then exit from ISR. The command is then interpreted elsewhere. For this simple example consisting only 2 commands, we choose to do everything inside the ISR because we do not want to overwhelm the reader with codes irrelevant to the main topic of this article.

Comments

comments

Comments are closed.