LPC1768, uart using FDR at higher baud rates


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
No problem. It's not like I've been looking here every hour or anything like that.:o:)
Thanks,
Larry


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi pausten,
Could you zip up your final project and post it as an attachment?
Nice work.


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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;
}


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello Paul,
it should work, even at higher baudrates (according datasheet) ... can you show us part of your UART_int program?
