LPC1768, uart using FDR at higher baud rates

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC1768, uart using FDR at higher baud rates

4,798 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pausten on Sat Jan 01 16:52:52 MST 2011
Hi,

I've modified some of the uart example code to configure the baud rates using the uart fractional divider register (as per the data sheet) as I could not get comms to work at higher (> 115200 Bps) baud rates (system clock = 100MHz), I belive, due to baud rate inaccuracies.

This is working as I can now send data at 921600 Bps from the LPC1768 to a terminal emulation program on my PC and data is displayed correctly. I belive the usb-serial (TTL) adapter runs ok at 921600 as I've used it successfully using other microcontrollers at 921600 Bps. It could be pickup on the prototype board I'm using but the wire runs are quite short and I wouldn't have expected it to be a problem.

I've uploaded a couple of images from the scope showing the LPC1768 TX and RX data pins (UART0). The SW shows an ASCII 'U' character being transmitted (chosen as it has an alternating bit pattern). This shows no problems with the signal levels. This chracter was received by the PC without error. The LPC1768 SW was just sending U characters periodically in this case.

I then changed the SW on the LPC1768 so that it echo's back the character it receives. The second image shows this. This shows an incorrect character being echo'ed back to the PC, looks like the reception of the character by the LPC1768 is where the problem lies. At lower baud rates this works fine. The images show a rate of 460800 Bps.

I was wondering if anyone else has had trouble running error free over an LPC1768 serial port at higher baud rates ?

Paul
0 Kudos
8 Replies

3,091 Views
alfredlee
Contributor I

This thread turned up when I search for 921600 UART operation on the LPC1758. It has valuable information. However, the wisdom is buried in the code and took me a while to distill the gist of it. I will re-word it here in case it is helpful to future lookers.

The MULVAL/DIVADDVAL register pair implementation is very clever. It directly maps to the solution as you will see as follow:

Simple-mindedly the Divisor is computed as:

Divisor = PCLK / (16 * BAUD) = 48 MHz / (16 * 921600) = 3.255

since this is an integer division, the actual baud achieved is:

BAUDnew = PCLK / (16 * Divisor) = 48 MHz / (16 * 3) = 1000000

The error is:

1000000 / 921600 = 1.085

Thus the BAUDnew need to be divided by 1.085 to arrived at exactly 921600.

You use the MULVAL/DIVADDVAL registers to solve this by finding the best value that is the closest to the fraction, e.g. 1/12 = 0.083 which is close enough.

If you only have a fixed baud to support, it may be easier to hand compute the fraction or compute all possibility and sort them in a spread sheet to look up.

(bonus, for the Linux folks, this one liner prints and sorts all fractions so you can look it up:

perl -e 'for($mul=1;$mul<16;$mul++){for($div=1;$div<$mul;$div++){printf("%4.3f,$div/$mul\n",$div/$mul)}}' | sort

Well, it's only 106 lines so I might as well post it here to save you some troubles:

0.067,1/15
0.071,1/14
0.077,1/13
0.083,1/12
0.091,1/11
0.100,1/10
0.111,1/9
0.125,1/8
0.133,2/15
0.143,1/7
0.143,2/14
0.154,2/13
0.167,1/6
0.167,2/12
0.182,2/11
0.200,1/5
0.200,2/10
0.200,3/15
0.214,3/14
0.222,2/9
0.231,3/13
0.250,1/4
0.250,2/8
0.250,3/12
0.267,4/15
0.273,3/11
0.286,2/7
0.286,4/14
0.300,3/10
0.308,4/13
0.333,1/3
0.333,2/6
0.333,3/9
0.333,4/12
0.333,5/15
0.357,5/14
0.364,4/11
0.375,3/8
0.385,5/13
0.400,2/5
0.400,4/10
0.400,6/15
0.417,5/12
0.429,3/7
0.429,6/14
0.444,4/9
0.455,5/11
0.462,6/13
0.467,7/15
0.500,1/2
0.500,2/4
0.500,3/6
0.500,4/8
0.500,5/10
0.500,6/12
0.500,7/14
0.533,8/15
0.538,7/13
0.545,6/11
0.556,5/9
0.571,4/7
0.571,8/14
0.583,7/12
0.600,3/5
0.600,6/10
0.600,9/15
0.615,8/13
0.625,5/8
0.636,7/11
0.643,9/14
0.667,10/15
0.667,2/3
0.667,4/6
0.667,6/9
0.667,8/12
0.692,9/13
0.700,7/10
0.714,10/14
0.714,5/7
0.727,8/11
0.733,11/15
0.750,3/4
0.750,6/8
0.750,9/12
0.769,10/13
0.778,7/9
0.786,11/14
0.800,12/15
0.800,4/5
0.800,8/10
0.818,9/11
0.833,10/12
0.833,5/6
0.846,11/13
0.857,12/14
0.857,6/7
0.867,13/15
0.875,7/8
0.889,8/9
0.900,9/10
0.909,10/11
0.917,11/12
0.923,12/13
0.929,13/14
0.933,14/15

0 Kudos

3,091 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by boltnut on Mon Aug 25 08:54:15 MST 2014
According to UM10375 page 203 algorithm, UART_PCLK=12MHz, BR=115200,
the suggested UART setup would be: DLM, DLL=4, DIVADDVAL=5, and MULVAL=8.
But NXP LPC13xx_SampleSoftware v107 demo code used DIVADDVAL=0, and MULVAL=1
as default values. Why does NOT the UART demo code (DIVADDVAL=0 and MULVAL=1) follow the formula ?
I had verified the code is working fine with PC USB-to-UART kit
0 Kudos

3,091 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by larryvc on Thu Mar 24 23:37:51 MST 2011
No problem. It's not like I've been looking here every hour or anything like that.:o:)

Thanks,
Larry
0 Kudos

3,090 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pausten on Thu Mar 24 23:32:24 MST 2011
Hi,

Sorry it's taken a few days to get back to you on this but I wanted to check that the test code still worked, before posting it.

Hope it's useful to you.

Paul
0 Kudos

3,090 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by larryvc on Wed Mar 16 13:58:13 MST 2011
Hi pausten,

Could you zip up your final project and post it as an attachment?

Nice work.
0 Kudos

3,090 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pausten on Tue Jan 11 22:32:10 MST 2011
I found the problem. The baud rate achieved is less accurate for lower values of pclk. I therefore changed the code that programs the uart so that it uses a pclk value equal to the system clock frequency rather than divided by four (as previously).

int initUart0(int baudRate) {
    int     pclk;
    int     dlEest;
    float     divAddVal, mulVal;

    // PCLK_UART0 is being set = SystemCoreClock
    pclk = SystemCoreClock;

    //Get the correct fractional values for the given pclk and required baud rate
    if( getFractionValues(pclk, baudRate, &dlEest, &divAddVal, &mulVal) == -1 ) {
        return -1;
    }

    // Turn on power to UART0
    LPC_SC->PCONP |=  PCUART0_POWERON;

    // Turn on UART0 peripheral clock
    LPC_SC->PCLKSEL0 &= ~(PCLK_UART0_MASK);
    LPC_SC->PCLKSEL0 |=  (1 << PCLK_UART0);     // PCLK_periph = CCLK

    // Set PINSEL0 so that P0.2 = TXD0, P0.3 = RXD0
    LPC_PINCON->PINSEL0 &= ~0xf0;
    LPC_PINCON->PINSEL0 |= ((1 << 4) | (1 << 6));

    LPC_UART0->LCR = 0x83;        // 8 bits, no Parity, 1 Stop bit, DLAB=1
    LPC_UART0->DLM = dlEest / 256;
    LPC_UART0->DLL = dlEest % 256;
    //Setup the fractional divider register
    LPC_UART0->FDR = (((int)mulVal)<<4)|(int)divAddVal;
    LPC_UART0->LCR = 0x03;        // 8 bits, no Parity, 1 Stop bit DLAB = 0
    LPC_UART0->FCR = 0x07;        // Enable and reset TX and RX FIFO

    LPC_UART0->LCR = 0x03;        // 8 bits, no Parity, 1 Stop bit, DLAB=0

    //Debug code
    //Calc actual baud rate achieved
    //int uartBaudRate = pclk/( 16*dlEest*(1+(divAddVal/mulVal)) );
    //printf("\ndlEest         =%d\n\rSystemCoreClock=%d\nuartBaudRate=%d\n\rpclk=%d\n\r",dlEest,SystemCoreClock,uartBaudRate,pclk);

    return 0;
}

I also made a change to the  code that generates the fractional baud rate values but this did not make any difference to the result, prior to making the above change.

/*
* Get the fraction values required to set an accurate BR
*
* Return -1 on error, 0 on success.
*/
static int getFractionValues(int pclk, int baudRate, int *dlEst, float *divAddVal, float *mulVal) {
    double  dlEstFloat = pclk/(16.0*baudRate);
    double     FRestSeed = 1.5;
    double  FRest;
    int     DLest;

    //If this pclk and baud rate give and integer division
    //we don't need the fractional calculation
    if( isIntValue(dlEstFloat) ) {
        *dlEst = (int)dlEstFloat;
        *divAddVal=0.0;
        *mulVal=1.0;
        return 0;
    }

    while(1) {
        DLest = (int)(pclk/(16.0*baudRate*FRestSeed));
        FRest = pclk/(16.0*baudRate*DLest);
        //If we have the required accuracy
        if( FRest >= 1.1 && FRest < 1.9) {
            break;
        }

        if( FRestSeed <= 1.5 ) {
            FRestSeed-=0.001;
            if( FRestSeed < 1.1 ) {
                FRestSeed=1.5001;
            }
        }
        else {
            FRestSeed=FRestSeed+0.001;
            if( FRestSeed >= 1.9 ) {
                return -1;
            }
        }
    }
    *dlEst=(int)DLest;
    return getFRValues(FRest, divAddVal, mulVal);
}

Paul
0 Kudos

3,090 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pausten on Mon Jan 03 10:34:09 MST 2011
Hi,

I read the datasheet and I agree that it appears higher baud rates should work.
I prototyped the code in python to enable me to test the algorithm I used to generate the fractional baud rate values. I checked that the code produced the same results for the two examples in the data sheet and then transferred the code to C. The C code gives the correct baud rates as I tested the bit transition time using the Oscilloscope when sending 'U' characters (at 115200, 230400, 460800 and 921600 Bps
) from the LPC1768 and the PC receives these fine at all baud rates so it appears that the baud rate is being set correctly.

The main elements of the C code I used to set the baud rate is shown below.

Thanks for your help on this.

Paul


float FRList[BR_LOOKUP_SIZE] = {1.000,1.067,1.071,1.077,1.083,1.091,1.100,1.111,1.125,1.133,1.143,1.154,1.167,1.182,1.200,1.214,1.222,1.231,1.250,
1.267,1.273,1.286,1.300,1.308,1.333,1.357,1.364,1.375,1.385,1.400,1.417,1.429,1.444,1.455,1.462,1.467,1.500,1.533,1.538,1.545,1.556,
1.571,1.583,1.600,1.615,1.625,1.636,1.643,1.667,1.692,1.700,1.714,1.727,1.733,1.750,1.769,1.778,1.786,1.800,1.818,1.833,1.846,1.857,
1.867,1.875,1.889,1.900,1.909,1.917,1.923,1.929,1.933};

float DIVADDVALList[BR_LOOKUP_SIZE] = {0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.0,1.0,2.0,1.0,2.0,1.0,3.0,2.0,3.0,1.0,4.0,3.0,2.0,3.0,4.0,1.0,5.0,4.0,3.0,
5.0,2.0,5.0,3.0,4.0,5.0,6.0,7.0,1.0,8.0,7.0,6.0,5.0,4.0,7.0,3.0,8.0,5.0,7.0,9.0,2.0,9.0,7.0,5.0,8.0,11.0,3.0,10.0,7.0,11.0,4.0,9.0,5.0,
11.0,6.0,13.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0};

float MULVALList[BR_LOOKUP_SIZE] = {1.0,15.0,14.0,13.0,12.0,11.0,10.0,9.0,8.0,15.0,7.0,13.0,6.0,11.0,5.0,14.0,9.0,13.0,4.0,15.0,11.0,7.0,10.0,13.0,3.0,
14.0,11.0,8.0,13.0,5.0,12.0,7.0,9.0,11.0,13.0,15.0,2.0,15.0,13.0,11.0,9.0,7.0,12.0,5.0,13.0,8.0,11.0,14.0,3.0,13.0,10.0,7.0,11.0,15.0,
4.0,13.0,9.0,14.0,5.0,11.0,6.0,13.0,7.0,15.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0};



/**
* Return 1 if the double is an int value, 0 if not
*/
static int isIntValue(double value) {
    int intValue = (int)value;
    if( value == intValue ) {
        return 1;
    }
    return 0;
}



/*
* Get the fraction values for the given FRest value.
*/
static int getFRValues(double FRest, float *divAddVal, float *mulVal) {
    float lastDiff = -1;
    float thisDiff;
    int index;
    //Look through the lookup table and find the index of the value
    //that provides the smallest difference between the FRest value
    //and the lookup table value.
    for( index=0 ; index<BR_LOOKUP_SIZE ; index++ ) {
        if( FRest > FRList[index] ) {
            thisDiff = FRest-FRList[index];
        }
        else {
            thisDiff = FRList[index]-FRest;
        }
        if( lastDiff != -1 && thisDiff > lastDiff ) {
          //Set the fractional values required
          *divAddVal=DIVADDVALList[index-1];
          *mulVal=MULVALList[index-1];
          return 0;
        }
        lastDiff=thisDiff;
    }
    return -1;
}

/*
* Get the fraction values required to set an accurate BR
*
* Return -1 on error, 0 on success.
*/
static int getFractionValues(int pclk, int baudRate, int *dlEst, float *divAddVal, float *mulVal) {
    float dlEstFloat = pclk/(16.0*baudRate);

    //If this pclk and baud rate give and integer division
    //we don't need the fractional calculation
    if( isIntValue(dlEstFloat) ) {
    *dlEst = (int)dlEstFloat;
    *divAddVal=0.0;
    *mulVal=1.0;
    return 0;
    }

    double     FRest = 1.5;
    int     DLest;

    while(1) {
        DLest = (int)(pclk/(16.0*baudRate*FRest));
        FRest = pclk/(16.0*baudRate*DLest);
        //If we have the required accuracy
        if( FRest >= 1.1 && FRest < 1.9) {
            break;
        }

        if( FRest <= 1.5 ) {
            FRest=FRest-0.001;
            if( FRest < 1.1 ) {
                FRest=1.5001;
            }
            else {
                FRest=FRest+0.001;
                if( FRest >= 1.9 ) {
                    return -1;
                }
            }
        }
    }
    *dlEst=(int)DLest;
    return getFRValues(FRest, divAddVal, mulVal);
}


Some of this code was taken from the RDB1768cmsis_UART_printf example project.

/*
* Functions to setup the UART 0,1,2 & 3 using the fraction baud rate divider.
* Gives a more accurate (within 1.1%) baud rate regardless of the system
* clock frequency.
*
* Return -1 on error, 0 on success.
*/
int initUart0(int baudRate) {
    int     pclk;
    int     dlEest;
    float     divAddVal, mulVal;

    // PCLK_UART0 is being set to 1/4 of SystemCoreClock
    pclk = SystemCoreClock / 4;

    //Get the correct fractional values for the given pclk and required baud rate
    if( getFractionValues(pclk, baudRate, &dlEest, &divAddVal, &mulVal) == -1 ) {
        return -1;
    }

    // Turn on power to UART0
    LPC_SC->PCONP |=  PCUART0_POWERON;

    // Turn on UART0 peripheral clock
    LPC_SC->PCLKSEL0 &= ~(PCLK_UART0_MASK);
    LPC_SC->PCLKSEL0 |=  (0 << PCLK_UART0);        // PCLK_periph = CCLK/4

    // Set PINSEL0 so that P0.2 = TXD0, P0.3 = RXD0
    LPC_PINCON->PINSEL0 &= ~0xf0;
    LPC_PINCON->PINSEL0 |= ((1 << 4) | (1 << 6));

    LPC_UART0->LCR = 0x83;        // 8 bits, no Parity, 1 Stop bit, DLAB=1
    LPC_UART0->DLM = dlEest / 256;
    LPC_UART0->DLL = dlEest % 256;
    //Setup the fractional divider register
    LPC_UART0->FDR = (((int)mulVal)<<4)|(int)divAddVal;
    LPC_UART0->LCR = 0x03;        // 8 bits, no Parity, 1 Stop bit DLAB = 0
    LPC_UART0->FCR = 0x07;        // Enable and reset TX and RX FIFO

    LPC_UART0->LCR = 0x03;        // 8 bits, no Parity, 1 Stop bit, DLAB=0

    return 0;
}



//This is the code I used to echo characters received by the LPC1768 back to the PC
int main(void) {

    initUart0(921600);

    while(1) {
        UART0_Sendchar( UART0_Getchar() );
    }

}

I replaced the above loop with the following to test the transmittion of data
from the LPC1768 to the PC
    while(1) {
        UART0_Sendchar('U');
        c=0;
        while(c < 1E6) {
            c++;
        }
    }

The example code I used

// ***********************
// Function to send character over UART
void UART0_Sendchar(char c)
{
    while( (LPC_UART0->LSR & LSR_THRE) == 0 );    // Block until tx empty
   
    LPC_UART0->THR = c;
}


// ***********************
// Function to get character from UART
char UART0_Getchar()
{
    char c;
    while( (LPC_UART0->LSR & LSR_RDR) == 0 );  // Nothing received so just block    
    c = LPC_UART0->RBR; // Read Receiver buffer register
    return c;
}
0 Kudos

3,090 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_Europe on Sun Jan 02 15:36:58 MST 2011
Hello Paul,

it should work, even at higher baudrates (according datasheet) ... can you show us part of your UART_int program?
0 Kudos