EmbeddedRelated.com
Forums

Re: Exact 115200 bps baud rate generation in LPC 2378

Started by arm7...@mmktronic.de August 31, 2009
--- In l..., "Jan Vanek" wrote:
>
> Hi,
>
> I have slightly improved the search algorithm I posted some time ago to
> allow for smaller resulting baud rate. It can be used in run-time. I think
> it finds the best possible value, whereas the algorithm on the page seems to
> only get very close, e.g. for 12'000'000Hz and 9600 the page gives resulting
> baud-rate 9603 whereas my algorithm finds settings for 9601.
>
> u32 findDivisorAndPrescale(u32 frequency, u32 baudRate, bool
> allowSmallerBaudRate, u32& mulVal, u32& divAddVal)
> {
> // The FDR is set to prescale the UART_CLK with factor M/(M+D),
> // where both M and D are in <0, 15>. This is to achieve smaller
> // error when calculating the divisor (the UnDL).
> // UART_CLK M
> // UARTbaudrate = --------- x -----
> // 16 x UnDL (M+D)
>
> u32 divisor = frequency / (16 * baudRate);
> u32 baudRateError = (frequency / (16 * divisor)) - baudRate;
> if (allowSmallerBaudRate)
> {
> u32 otherDivisor = divisor + 1;
> u32 otherBaudRateError = baudRate - (frequency / (16 *
> otherDivisor));
> if (baudRateError > otherBaudRateError)
> {
> divisor = otherDivisor;
> baudRateError = otherBaudRateError;
> }
> }
> mulVal = 1;
> divAddVal = 0;
> for (u32 mv = 0; ++mv < 16;)
> {
> for (u32 dav = 0; ++dav < 16;)
> {
> u32 prescaledClock = (frequency * mv) / (16 * (mv + dav));
>.......
Hi Jan,

I wrote my baudrate calculation routine too - and at
UART_CLK = 12MHz and 9600Bd got a result of 9603Bd.
I suppose, Your algorithm delivers 9601Bd due of a some deviation of its behavior from the NXP's recipe (taken from "User Manual LPC23XX"):
//The value of MULVAL and DIVADDVAL should
//comply to the following conditions:
//1. 0 < MULVAL ≤ 15
//2. 0 ≤ DIVADDVAL < 15
//3. DIVADDVAL I guess, the 3rd condition has been ignored in Your code.

Here my routine:

//*********************************************
//////////////////////////////////////////////////////////////////////////////////////
// LPC23xx UART Fractional Divider Calculation by Marcin M. Krajka, mmk20090830
// Use it free, please.
// Note: Remarks partially taken from the baudrate calculation routine by Jan Vanek, thanks Jan!
// Any bugs found? Please, let me know: arm7mmktronics.de
//////////////////////////////////////////////////////////////////////////////////////
U32 GetUART_DL_MD(U32 ulUARTClockFreq, U32 ulBaudRate)
{
// The FDR (Fractional Divider Register) prescales the UARTClockFreq
// with factor M/(M+D), where:
// M = 1 .. 15
// D = 0 .. (M-1) (see NXP LPC23xx User Manual, e.g. U0FDR)
// This causes a smaller error of the calculated Baud rate
// than with standard values (M = 1, D = 0):
// UARTClockFreq M
// BaudRate = --------------- * -----
// 16 * UnDL M + D
//
// Result of this routine is concatenation of 4 bytes: 0x00, UnDLM, UnDLL and UnFDR.
// Use it on the following manner:
// U32 ulTempVar = GetUART_DL_MD(ulMyUARTClockFreq, ulMyBaudRate);
// if ((ulTempVar & 0xFF000000) == 0) {
// UnFDR = ulTempVar & 0xFF;
// UnDLL = (ulTempVar >> 8) & 0xFF;
// UnDLM = (ulTempVar >> 16) & 0xFF;
// }
// else {
// //error: DL overflow, too big to fit 16 bits!!!
// }

#define ACCURACY_FACTOR 1000000ULL
// 1000000ULL -> accuracy is 1ppm, must use U64 type for error calculation
// Requirement#1: (15 * ACCURACY_FACTOR * ulUARTClockFreq + 232 * ulBaudRate) < (2**64)
// Requirement#2: ulBaudRate < 9256.4kBd (i.e. 9.2MBd)

U32 ulDLOpt, ulErrOpt, ulMOpt, ulDOpt;
U32 ulDL, ulErr, ulM, ulD;
U64 ullDLAccurate;

// optimal variables initialization
ulDLOpt = 0x0001FFFF; // DL overflow as default value
ulErrOpt = ACCURACY_FACTOR; // huge error as default value
ulMOpt = 1;
ulDOpt = 0;
//
for (ulM = 1; ulM < 16; ulM++) {
for (ulD = 0; ulD < ulM; ulD++) {
// calculate (DL * ACCURACY_FACTOR)
ullDLAccurate = (ACCURACY_FACTOR * ulUARTClockFreq * ulM + 8UL * ulBaudRate * (ulD + ulM))/
(16UL * ulBaudRate * (ulD + ulM));
// calculate "integer round" of DL
ulDL = (ullDLAccurate + (ACCURACY_FACTOR >> 1)) / ACCURACY_FACTOR;
// calculate the magnitude of relative error (in ppm) between them
if (ACCURACY_FACTOR * ulDL > ullDLAccurate) {
ulErr = (ACCURACY_FACTOR * ulDL - ullDLAccurate) / ulDL;
}
else {
ulErr = (ullDLAccurate - ACCURACY_FACTOR * ulDL) / ulDL;
}
// use actual values as optimal,
// if the new error is less than the previous one
// and DL is a valid U16 number
if ((ulErr < ulErrOpt) && (ulDL < 0x00010000)) {
ulMOpt = ulM;
ulDOpt = ulD;
ulErrOpt = ulErr;
ulDLOpt = ulDL;
}
}
}
// concatenate results: OVL:DLM:DLL:FDR (byte FDR consists of two nibbles MULVAL:DIVADDVAL)
return (ulDLOpt << 8) | (ulMOpt << 4) | ulDOpt;
}
//*********************************************
Here the test code:
//*********************************************
U8 ucDLM, ucDLL, ucM, ucD, ucOvfl, ucFDR;
U32 ulTempVar, ulBaudRate, ulUARTCLKFREQ;
double fdTemp;

ulTempVar = GetUART_DL_MD(ulUARTCLKFREQ, ulBaudRate);
ucFDR = ulTempVar & 0xFF;
ucDLL = (ulTempVar >> 8) & 0xFF;
ucDLM = (ulTempVar >> 16) & 0xFF;
ucD = ucFDR & 0xF;
ucM = ucFDR >> 4;
ucOvfl = (ulTempVar >> 24) & 0xFF;

printf("\r\n FDR=0x%02X, DLM=0x%02X, DLL=0x%02X", ucFDR, ucDLM, ucDLL);
printf("\r\n M=%u, D=%u, DL=%u, OVL=%u", ucM, ucD, (ulTempVar >> 8) & 0xFFFF, ucOvfl);

fdTemp = (1.0 * ulResult)/(16.0 * (256.0 * ucDLM + ucDLL) * (1.0 + (1.0 * ucD) / ucM));

printf("\r\n BaudRate=%.3fBd, Error=%.6f%%", fdTemp, (fdTemp - ul)/ul * 100.0 );
//*********************************************
Here some test answers:

//12MHz, 9600Bd
//*********************************************
Get UARTCLKFREQ: 12000000
Get BaudRate : 9600
FDR=0xA1, DLM=0x00, DLL=0x47
M, D=1, DLq, OVL=0
BaudRate03.073Bd, Error=0.032010%
//*********************************************

//max UART frequency 72MHz, min baudrate 75Bd:
//*********************************************
Get UARTCLKFREQ: 72000000
Get BaudRate : 75
FDR=0x10, DLM=0xEA, DLL=0x60
M=1, D=0, DL`000, OVL=0
BaudRateu.000Bd, Error=0.000000%
//*********************************************

//the highest hypothetic UART frequency is 152.0412MHz
// at baudrate 75Bd:
//*********************************************
Get UARTCLKFREQ: 152041200
Get BaudRate : 75
FDR=0xFE, DLM=0xFF, DLL=0xFF
M, D, DLe535, OVL=0
BaudRateu.000Bd, Error=0.000000%
//*********************************************

//low UART frequency of 12Mhz
// at baudrate 230.4kBd (error would be
// innacceptable 8.5% without a fractional divider!):
//*********************************************
Get UARTCLKFREQ: 12000000
Get BaudRate : 230400
FDR=0x85, DLM=0x00, DLL=0x02
M=8, D=5, DL=2, OVL=0
BaudRate#0769.231Bd, Error=0.160256%
//*********************************************
Enjoy it!

Marcin

An Engineer's Guide to the LPC2100 Series

Hi Marcin,

I was not aware about the 3rd condition, because the LPC288x user manual
doesn't state it. The 3rd condition basically says you can prescale with
factor in range (0.5, 1.0>, whereas my code allows prescaling with factor
less than 0.5. Obviously it can be changed very easily, but since I now
don't see why there should be this 0.5 limit, I keep it as it is.

After some time I grasped the idea of your code, but I didn't do precise
verification, or checking how this relative-error really behaves. The only
small change I could suggest is to count the uID from 1, because value 0
means no-prescaling. You'd have to calculate the values/error for the
no-prescaling case before the loops once, and then you'd save 15 repetitions
of the inner-loop body (where uID were 0). I agree, not a big win :-)

With regards,
Jan
On Mon, Aug 31, 2009 at 4:54 AM, wrote:

> --- In l..., "Jan Vanek" wrote:
> >
> > Hi,
> >
> > I have slightly improved the search algorithm I posted some time ago to
> > allow for smaller resulting baud rate. It can be used in run-time. I
> think
> > it finds the best possible value, whereas the algorithm on the page seems
> to
> > only get very close, e.g. for 12'000'000Hz and 9600 the page gives
> resulting
> > baud-rate 9603 whereas my algorithm finds settings for 9601.
> >
> > u32 findDivisorAndPrescale(u32 frequency, u32 baudRate, bool
> > allowSmallerBaudRate, u32& mulVal, u32& divAddVal)
> > {
> > // The FDR is set to prescale the UART_CLK with factor M/(M+D),
> > // where both M and D are in <0, 15>. This is to achieve smaller
> > // error when calculating the divisor (the UnDL).
> > // UART_CLK M
> > // UARTbaudrate = --------- x -----
> > // 16 x UnDL (M+D)
> >
> > u32 divisor = frequency / (16 * baudRate);
> > u32 baudRateError = (frequency / (16 * divisor)) - baudRate;
> > if (allowSmallerBaudRate)
> > {
> > u32 otherDivisor = divisor + 1;
> > u32 otherBaudRateError = baudRate - (frequency / (16 *
> > otherDivisor));
> > if (baudRateError > otherBaudRateError)
> > {
> > divisor = otherDivisor;
> > baudRateError = otherBaudRateError;
> > }
> > }
> > mulVal = 1;
> > divAddVal = 0;
> > for (u32 mv = 0; ++mv < 16;)
> > {
> > for (u32 dav = 0; ++dav < 16;)
> > {
> > u32 prescaledClock = (frequency * mv) / (16 * (mv + dav));
> >.......
> Hi Jan,
>
> I wrote my baudrate calculation routine too - and at
> UART_CLK = 12MHz and 9600Bd got a result of 9603Bd.
> I suppose, Your algorithm delivers 9601Bd due of a some deviation of its
> behavior from the NXP's recipe (taken from "User Manual LPC23XX"):
> //The value of MULVAL and DIVADDVAL should
> //comply to the following conditions:
> //1. 0 < MULVAL ≤ 15
> //2. 0 ≤ DIVADDVAL < 15
> //3. DIVADDVAL > I guess, the 3rd condition has been ignored in Your code.
>
> Here my routine:
>
> //*********************************************
>
> //////////////////////////////////////////////////////////////////////////////////////
> // LPC23xx UART Fractional Divider Calculation by Marcin M. Krajka,
> mmk20090830
> // Use it free, please.
> // Note: Remarks partially taken from the baudrate calculation routine by
> Jan Vanek, thanks Jan!
> // Any bugs found? Please, let me know: arm7mmktronics.de
>
> //////////////////////////////////////////////////////////////////////////////////////
> U32 GetUART_DL_MD(U32 ulUARTClockFreq, U32 ulBaudRate)
> {
> // The FDR (Fractional Divider Register) prescales the UARTClockFreq
> // with factor M/(M+D), where:
> // M = 1 .. 15
> // D = 0 .. (M-1) (see NXP LPC23xx User Manual, e.g. U0FDR)
> // This causes a smaller error of the calculated Baud rate
> // than with standard values (M = 1, D = 0):
> // UARTClockFreq M
> // BaudRate = --------------- * -----
> // 16 * UnDL M + D
> //
> // Result of this routine is concatenation of 4 bytes: 0x00, UnDLM,
> UnDLL and UnFDR.
> // Use it on the following manner:
> // U32 ulTempVar = GetUART_DL_MD(ulMyUARTClockFreq, ulMyBaudRate);
> // if ((ulTempVar & 0xFF000000) == 0) {
> // UnFDR = ulTempVar & 0xFF;
> // UnDLL = (ulTempVar >> 8) & 0xFF;
> // UnDLM = (ulTempVar >> 16) & 0xFF;
> // }
> // else {
> // //error: DL overflow, too big to fit 16 bits!!!
> // }
>
> #define ACCURACY_FACTOR 1000000ULL
> // 1000000ULL -> accuracy is 1ppm, must use U64 type for error
> calculation
> // Requirement#1: (15 * ACCURACY_FACTOR * ulUARTClockFreq + 232 *
> ulBaudRate) < (2**64)
> // Requirement#2: ulBaudRate < 9256.4kBd (i.e. 9.2MBd)
>
> U32 ulDLOpt, ulErrOpt, ulMOpt, ulDOpt;
> U32 ulDL, ulErr, ulM, ulD;
> U64 ullDLAccurate;
>
> // optimal variables initialization
> ulDLOpt = 0x0001FFFF; // DL overflow as default value
> ulErrOpt = ACCURACY_FACTOR; // huge error as default value
> ulMOpt = 1;
> ulDOpt = 0;
> //
> for (ulM = 1; ulM < 16; ulM++) {
> for (ulD = 0; ulD < ulM; ulD++) {
> // calculate (DL * ACCURACY_FACTOR)
> ullDLAccurate = (ACCURACY_FACTOR * ulUARTClockFreq * ulM + 8UL *
> ulBaudRate * (ulD + ulM))/
> (16UL * ulBaudRate * (ulD + ulM));
> // calculate "integer round" of DL
> ulDL = (ullDLAccurate + (ACCURACY_FACTOR >> 1)) /
> ACCURACY_FACTOR;
> // calculate the magnitude of relative error (in ppm) between
> them
> if (ACCURACY_FACTOR * ulDL > ullDLAccurate) {
> ulErr = (ACCURACY_FACTOR * ulDL - ullDLAccurate) / ulDL;
> }
> else {
> ulErr = (ullDLAccurate - ACCURACY_FACTOR * ulDL) / ulDL;
> }
> // use actual values as optimal,
> // if the new error is less than the previous one
> // and DL is a valid U16 number
> if ((ulErr < ulErrOpt) && (ulDL < 0x00010000)) {
> ulMOpt = ulM;
> ulDOpt = ulD;
> ulErrOpt = ulErr;
> ulDLOpt = ulDL;
> }
> }
> }
> // concatenate results: OVL:DLM:DLL:FDR (byte FDR consists of two
> nibbles MULVAL:DIVADDVAL)
> return (ulDLOpt << 8) | (ulMOpt << 4) | ulDOpt;
> }
> //*********************************************
> Here the test code:
> //*********************************************
> U8 ucDLM, ucDLL, ucM, ucD, ucOvfl, ucFDR;
> U32 ulTempVar, ulBaudRate, ulUARTCLKFREQ;
> double fdTemp;
>
> ulTempVar = GetUART_DL_MD(ulUARTCLKFREQ, ulBaudRate);
> ucFDR = ulTempVar & 0xFF;
> ucDLL = (ulTempVar >> 8) & 0xFF;
> ucDLM = (ulTempVar >> 16) & 0xFF;
> ucD = ucFDR & 0xF;
> ucM = ucFDR >> 4;
> ucOvfl = (ulTempVar >> 24) & 0xFF;
>
> printf("\r\n FDR=0x%02X, DLM=0x%02X, DLL=0x%02X", ucFDR, ucDLM, ucDLL);
> printf("\r\n M=%u, D=%u, DL=%u, OVL=%u", ucM, ucD, (ulTempVar >> 8) &
> 0xFFFF, ucOvfl);
>
> fdTemp = (1.0 * ulResult)/(16.0 * (256.0 * ucDLM + ucDLL) * (1.0 + (1.0
> * ucD) / ucM));
>
> printf("\r\n BaudRate=%.3fBd, Error=%.6f%%", fdTemp, (fdTemp - ul)/ul *
> 100.0 );
> //*********************************************
> Here some test answers:
>
> //12MHz, 9600Bd
> //*********************************************
> Get UARTCLKFREQ: 12000000
> Get BaudRate : 9600
> FDR=0xA1, DLM=0x00, DLL=0x47
> M=10, D=1, DL=71, OVL=0
> BaudRate=9603.073Bd, Error=0.032010%
> //*********************************************
>
> //max UART frequency 72MHz, min baudrate 75Bd:
> //*********************************************
> Get UARTCLKFREQ: 72000000
> Get BaudRate : 75
> FDR=0x10, DLM=0xEA, DLL=0x60
> M=1, D=0, DL=60000, OVL=0
> BaudRate=75.000Bd, Error=0.000000%
> //*********************************************
>
> //the highest hypothetic UART frequency is 152.0412MHz
> // at baudrate 75Bd:
> //*********************************************
> Get UARTCLKFREQ: 152041200
> Get BaudRate : 75
> FDR=0xFE, DLM=0xFF, DLL=0xFF
> M=15, D=14, DL=65535, OVL=0
> BaudRate=75.000Bd, Error=0.000000%
> //*********************************************
>
> //low UART frequency of 12Mhz
> // at baudrate 230.4kBd (error would be
> // innacceptable 8.5% without a fractional divider!):
> //*********************************************
> Get UARTCLKFREQ: 12000000
> Get BaudRate : 230400
> FDR=0x85, DLM=0x00, DLL=0x02
> M=8, D=5, DL=2, OVL=0
> BaudRate=230769.231Bd, Error=0.160256%
> //*********************************************
> Enjoy it!
>
> Marcin
>
>
Jan,

I've implemented your code on an LPC1114 and it has problems. The
DIVADDVAL must be less than MULVAL. In addition, if the fractional
divider is active (DIVADDVAL > 0), the divisor value must be 3 or
greater. These conditions are stated in both the LPC2300 and LPC1100
user manuals.

Given an input clock of 6MHz and a desired baud rate of 115200, your
code gives the following results:

divisor = 1
DIVADDVAL = 9
MULVAL = 4

Now, here is where you may have gone wrong in your testing. With
those values, the LPC1114 uart transmits properly, but the receive
buad rate is off.

With the following code, all the required conditions are met.

for (unsigned int mv = 1; mv < 16; ++mv)
{
for (unsigned int dav = 0; dav < mv; ++dav)
{
unsigned int prescaledClock = (freq * mv) / (16 * (mv + dav));
unsigned int newDivisor = prescaledClock / baud;
if (newDivisor > 2)
{

Given an input clock of 6MHz and a desired baud rate of 115200, this
code gives the following results:

divisor = 3
DIVADDVAL = 1
MULVAL = 12

With these values the the LPC1114 uart works properly for both
transmit and receive.

Jeff

--- In l..., Jan Vanek wrote:
>
> Hi Marcin,
>
> I was not aware about the 3rd condition, because the LPC288x user manual
> doesn't state it. The 3rd condition basically says you can prescale with
> factor in range (0.5, 1.0>, whereas my code allows prescaling with factor
> less than 0.5. Obviously it can be changed very easily, but since I now
> don't see why there should be this 0.5 limit, I keep it as it is.
>
> After some time I grasped the idea of your code, but I didn't do precise
> verification, or checking how this relative-error really behaves. The only
> small change I could suggest is to count the uID from 1, because value 0
> means no-prescaling. You'd have to calculate the values/error for the
> no-prescaling case before the loops once, and then you'd save 15 repetitions
> of the inner-loop body (where uID were 0). I agree, not a big win :-)
>
> With regards,
> Jan
>
>
> On Mon, Aug 31, 2009 at 4:54 AM, wrote:
>
> > --- In l..., "Jan Vanek" wrote:
> > >
> > > Hi,
> > >
> > > I have slightly improved the search algorithm I posted some time ago to
> > > allow for smaller resulting baud rate. It can be used in run-time. I
> > think
> > > it finds the best possible value, whereas the algorithm on the page seems
> > to
> > > only get very close, e.g. for 12'000'000Hz and 9600 the page gives
> > resulting
> > > baud-rate 9603 whereas my algorithm finds settings for 9601.
> > >
> > > u32 findDivisorAndPrescale(u32 frequency, u32 baudRate, bool
> > > allowSmallerBaudRate, u32& mulVal, u32& divAddVal)
> > > {
> > > // The FDR is set to prescale the UART_CLK with factor M/(M+D),
> > > // where both M and D are in <0, 15>. This is to achieve smaller
> > > // error when calculating the divisor (the UnDL).
> > > // UART_CLK M
> > > // UARTbaudrate = --------- x -----
> > > // 16 x UnDL (M+D)
> > >
> > > u32 divisor = frequency / (16 * baudRate);
> > > u32 baudRateError = (frequency / (16 * divisor)) - baudRate;
> > > if (allowSmallerBaudRate)
> > > {
> > > u32 otherDivisor = divisor + 1;
> > > u32 otherBaudRateError = baudRate - (frequency / (16 *
> > > otherDivisor));
> > > if (baudRateError > otherBaudRateError)
> > > {
> > > divisor = otherDivisor;
> > > baudRateError = otherBaudRateError;
> > > }
> > > }
> > > mulVal = 1;
> > > divAddVal = 0;
> > > for (u32 mv = 0; ++mv < 16;)
> > > {
> > > for (u32 dav = 0; ++dav < 16;)
> > > {
> > > u32 prescaledClock = (frequency * mv) / (16 * (mv + dav));
> > >.......
> >
> >
> > Hi Jan,
> >
> > I wrote my baudrate calculation routine too - and at
> > UART_CLK = 12MHz and 9600Bd got a result of 9603Bd.
> > I suppose, Your algorithm delivers 9601Bd due of a some deviation of its
> > behavior from the NXP's recipe (taken from "User Manual LPC23XX"):
> > //The value of MULVAL and DIVADDVAL should
> > //comply to the following conditions:
> > //1. 0 < MULVAL ≤ 15
> > //2. 0 ≤ DIVADDVAL < 15
> > //3. DIVADDVAL > > I guess, the 3rd condition has been ignored in Your code.
> >
> > Here my routine:
> >
> > //*********************************************
> >
> > //////////////////////////////////////////////////////////////////////////////////////
> > // LPC23xx UART Fractional Divider Calculation by Marcin M. Krajka,
> > mmk20090830
> > // Use it free, please.
> > // Note: Remarks partially taken from the baudrate calculation routine by
> > Jan Vanek, thanks Jan!
> > // Any bugs found? Please, let me know: arm7mmktronics.de
> >
> > //////////////////////////////////////////////////////////////////////////////////////
> > U32 GetUART_DL_MD(U32 ulUARTClockFreq, U32 ulBaudRate)
> > {
> > // The FDR (Fractional Divider Register) prescales the UARTClockFreq
> > // with factor M/(M+D), where:
> > // M = 1 .. 15
> > // D = 0 .. (M-1) (see NXP LPC23xx User Manual, e.g. U0FDR)
> > // This causes a smaller error of the calculated Baud rate
> > // than with standard values (M = 1, D = 0):
> > // UARTClockFreq M
> > // BaudRate = --------------- * -----
> > // 16 * UnDL M + D
> > //
> > // Result of this routine is concatenation of 4 bytes: 0x00, UnDLM,
> > UnDLL and UnFDR.
> > // Use it on the following manner:
> > // U32 ulTempVar = GetUART_DL_MD(ulMyUARTClockFreq, ulMyBaudRate);
> > // if ((ulTempVar & 0xFF000000) == 0) {
> > // UnFDR = ulTempVar & 0xFF;
> > // UnDLL = (ulTempVar >> 8) & 0xFF;
> > // UnDLM = (ulTempVar >> 16) & 0xFF;
> > // }
> > // else {
> > // //error: DL overflow, too big to fit 16 bits!!!
> > // }
> >
> > #define ACCURACY_FACTOR 1000000ULL
> > // 1000000ULL -> accuracy is 1ppm, must use U64 type for error
> > calculation
> > // Requirement#1: (15 * ACCURACY_FACTOR * ulUARTClockFreq + 232 *
> > ulBaudRate) < (2**64)
> > // Requirement#2: ulBaudRate < 9256.4kBd (i.e. 9.2MBd)
> >
> > U32 ulDLOpt, ulErrOpt, ulMOpt, ulDOpt;
> > U32 ulDL, ulErr, ulM, ulD;
> > U64 ullDLAccurate;
> >
> > // optimal variables initialization
> > ulDLOpt = 0x0001FFFF; // DL overflow as default value
> > ulErrOpt = ACCURACY_FACTOR; // huge error as default value
> > ulMOpt = 1;
> > ulDOpt = 0;
> > //
> > for (ulM = 1; ulM < 16; ulM++) {
> > for (ulD = 0; ulD < ulM; ulD++) {
> > // calculate (DL * ACCURACY_FACTOR)
> > ullDLAccurate = (ACCURACY_FACTOR * ulUARTClockFreq * ulM + 8UL *
> > ulBaudRate * (ulD + ulM))/
> > (16UL * ulBaudRate * (ulD + ulM));
> > // calculate "integer round" of DL
> > ulDL = (ullDLAccurate + (ACCURACY_FACTOR >> 1)) /
> > ACCURACY_FACTOR;
> > // calculate the magnitude of relative error (in ppm) between
> > them
> > if (ACCURACY_FACTOR * ulDL > ullDLAccurate) {
> > ulErr = (ACCURACY_FACTOR * ulDL - ullDLAccurate) / ulDL;
> > }
> > else {
> > ulErr = (ullDLAccurate - ACCURACY_FACTOR * ulDL) / ulDL;
> > }
> > // use actual values as optimal,
> > // if the new error is less than the previous one
> > // and DL is a valid U16 number
> > if ((ulErr < ulErrOpt) && (ulDL < 0x00010000)) {
> > ulMOpt = ulM;
> > ulDOpt = ulD;
> > ulErrOpt = ulErr;
> > ulDLOpt = ulDL;
> > }
> > }
> > }
> > // concatenate results: OVL:DLM:DLL:FDR (byte FDR consists of two
> > nibbles MULVAL:DIVADDVAL)
> > return (ulDLOpt << 8) | (ulMOpt << 4) | ulDOpt;
> > }
> > //*********************************************
> >
> >
> > Here the test code:
> >
> >
> > //*********************************************
> > U8 ucDLM, ucDLL, ucM, ucD, ucOvfl, ucFDR;
> > U32 ulTempVar, ulBaudRate, ulUARTCLKFREQ;
> > double fdTemp;
> >
> > ulTempVar = GetUART_DL_MD(ulUARTCLKFREQ, ulBaudRate);
> > ucFDR = ulTempVar & 0xFF;
> > ucDLL = (ulTempVar >> 8) & 0xFF;
> > ucDLM = (ulTempVar >> 16) & 0xFF;
> > ucD = ucFDR & 0xF;
> > ucM = ucFDR >> 4;
> > ucOvfl = (ulTempVar >> 24) & 0xFF;
> >
> > printf("\r\n FDR=0x%02X, DLM=0x%02X, DLL=0x%02X", ucFDR, ucDLM, ucDLL);
> > printf("\r\n M=%u, D=%u, DL=%u, OVL=%u", ucM, ucD, (ulTempVar >> 8) &
> > 0xFFFF, ucOvfl);
> >
> > fdTemp = (1.0 * ulResult)/(16.0 * (256.0 * ucDLM + ucDLL) * (1.0 + (1.0
> > * ucD) / ucM));
> >
> > printf("\r\n BaudRate=%.3fBd, Error=%.6f%%", fdTemp, (fdTemp - ul)/ul *
> > 100.0 );
> > //*********************************************
> >
> >
> > Here some test answers:
> >
> > //12MHz, 9600Bd
> > //*********************************************
> > Get UARTCLKFREQ: 12000000
> > Get BaudRate : 9600
> > FDR=0xA1, DLM=0x00, DLL=0x47
> > M=10, D=1, DL=71, OVL=0
> > BaudRate=9603.073Bd, Error=0.032010%
> > //*********************************************
> >
> > //max UART frequency 72MHz, min baudrate 75Bd:
> > //*********************************************
> > Get UARTCLKFREQ: 72000000
> > Get BaudRate : 75
> > FDR=0x10, DLM=0xEA, DLL=0x60
> > M=1, D=0, DL=60000, OVL=0
> > BaudRate=75.000Bd, Error=0.000000%
> > //*********************************************
> >
> > //the highest hypothetic UART frequency is 152.0412MHz
> > // at baudrate 75Bd:
> > //*********************************************
> > Get UARTCLKFREQ: 152041200
> > Get BaudRate : 75
> > FDR=0xFE, DLM=0xFF, DLL=0xFF
> > M=15, D=14, DL=65535, OVL=0
> > BaudRate=75.000Bd, Error=0.000000%
> > //*********************************************
> >
> > //low UART frequency of 12Mhz
> > // at baudrate 230.4kBd (error would be
> > // innacceptable 8.5% without a fractional divider!):
> > //*********************************************
> > Get UARTCLKFREQ: 12000000
> > Get BaudRate : 230400
> > FDR=0x85, DLM=0x00, DLL=0x02
> > M=8, D=5, DL=2, OVL=0
> > BaudRate=230769.231Bd, Error=0.160256%
> > //*********************************************
> >
> >
> > Enjoy it!
> >
> > Marcin
> >
> >
> >
> >
> >
> >
> >
> >
Hi Jeff,

thanks for pointing this out. I remember another lpc2000 member told me once that the DAV must be less the MV but this condition is
not stated in the LPC288X manual, and I implemented it for LPC2888. At that time I considered that condition ilogical and was not
willing to make any changes, after all, everyone is free to make his/her own changes to that code. Now that my code is in files
section and you proved it doesn't work for processors/families more significant than LPC288X, I changed it there. ((I kept the
"++dav" in the for loop because this prevents the DAV being zero - when it is zero there is no prescaling because MV / (MV + DAV) 1; so it saves 15 iterations of the loop.))

Thanks and regards,
Jan
----- Original Message -----
From: "ksdoubleshooter"
To:
Sent: Friday, March 19, 2010 10:20 PM
Subject: [lpc2000] Re: Exact 115200 bps baud rate generation in LPC 2378

Jan,

I've implemented your code on an LPC1114 and it has problems. The
DIVADDVAL must be less than MULVAL. In addition, if the fractional
divider is active (DIVADDVAL > 0), the divisor value must be 3 or
greater. These conditions are stated in both the LPC2300 and LPC1100
user manuals.

Given an input clock of 6MHz and a desired baud rate of 115200, your
code gives the following results:

divisor = 1
DIVADDVAL = 9
MULVAL = 4

Now, here is where you may have gone wrong in your testing. With
those values, the LPC1114 uart transmits properly, but the receive
buad rate is off.

With the following code, all the required conditions are met.

for (unsigned int mv = 1; mv < 16; ++mv)
{
for (unsigned int dav = 0; dav < mv; ++dav)
{
unsigned int prescaledClock = (freq * mv) / (16 * (mv + dav));
unsigned int newDivisor = prescaledClock / baud;
if (newDivisor > 2)
{

Given an input clock of 6MHz and a desired baud rate of 115200, this
code gives the following results:

divisor = 3
DIVADDVAL = 1
MULVAL = 12

With these values the the LPC1114 uart works properly for both
transmit and receive.

Jeff

--- In l..., Jan Vanek wrote:
>
> Hi Marcin,
>
> I was not aware about the 3rd condition, because the LPC288x user manual
> doesn't state it. The 3rd condition basically says you can prescale with
> factor in range (0.5, 1.0>, whereas my code allows prescaling with factor
> less than 0.5. Obviously it can be changed very easily, but since I now
> don't see why there should be this 0.5 limit, I keep it as it is.
>
> After some time I grasped the idea of your code, but I didn't do precise
> verification, or checking how this relative-error really behaves. The only
> small change I could suggest is to count the uID from 1, because value 0
> means no-prescaling. You'd have to calculate the values/error for the
> no-prescaling case before the loops once, and then you'd save 15 repetitions
> of the inner-loop body (where uID were 0). I agree, not a big win :-)
>
> With regards,
> Jan
> On Mon, Aug 31, 2009 at 4:54 AM, wrote:
>
> > --- In l..., "Jan Vanek" wrote:
> > >
> > > Hi,
> > >
> > > I have slightly improved the search algorithm I posted some time ago to
> > > allow for smaller resulting baud rate. It can be used in run-time. I
> > think
> > > it finds the best possible value, whereas the algorithm on the page seems
> > to
> > > only get very close, e.g. for 12'000'000Hz and 9600 the page gives
> > resulting
> > > baud-rate 9603 whereas my algorithm finds settings for 9601.
> > >
> > > u32 findDivisorAndPrescale(u32 frequency, u32 baudRate, bool
> > > allowSmallerBaudRate, u32& mulVal, u32& divAddVal)
> > > {
> > > // The FDR is set to prescale the UART_CLK with factor M/(M+D),
> > > // where both M and D are in <0, 15>. This is to achieve smaller
> > > // error when calculating the divisor (the UnDL).
> > > // UART_CLK M
> > > // UARTbaudrate = --------- x -----
> > > // 16 x UnDL (M+D)
> > >
> > > u32 divisor = frequency / (16 * baudRate);
> > > u32 baudRateError = (frequency / (16 * divisor)) - baudRate;
> > > if (allowSmallerBaudRate)
> > > {
> > > u32 otherDivisor = divisor + 1;
> > > u32 otherBaudRateError = baudRate - (frequency / (16 *
> > > otherDivisor));
> > > if (baudRateError > otherBaudRateError)
> > > {
> > > divisor = otherDivisor;
> > > baudRateError = otherBaudRateError;
> > > }
> > > }
> > > mulVal = 1;
> > > divAddVal = 0;
> > > for (u32 mv = 0; ++mv < 16;)
> > > {
> > > for (u32 dav = 0; ++dav < 16;)
> > > {
> > > u32 prescaledClock = (frequency * mv) / (16 * (mv + dav));
> > >.......
> >
> >
> > Hi Jan,
> >
> > I wrote my baudrate calculation routine too - and at
> > UART_CLK = 12MHz and 9600Bd got a result of 9603Bd.
> > I suppose, Your algorithm delivers 9601Bd due of a some deviation of its
> > behavior from the NXP's recipe (taken from "User Manual LPC23XX"):
> > //The value of MULVAL and DIVADDVAL should
> > //comply to the following conditions:
> > //1. 0 < MULVAL 15
> > //2. 0 DIVADDVAL < 15
> > //3. DIVADDVAL > > I guess, the 3rd condition has been ignored in Your code.
> >
> > Here my routine:
> >
> > //*********************************************
> >
> > //////////////////////////////////////////////////////////////////////////////////////
> > // LPC23xx UART Fractional Divider Calculation by Marcin M. Krajka,
> > mmk20090830
> > // Use it free, please.
> > // Note: Remarks partially taken from the baudrate calculation routine by
> > Jan Vanek, thanks Jan!
> > // Any bugs found? Please, let me know: arm7mmktronics.de
> >
> > //////////////////////////////////////////////////////////////////////////////////////
> > U32 GetUART_DL_MD(U32 ulUARTClockFreq, U32 ulBaudRate)
> > {
> > // The FDR (Fractional Divider Register) prescales the UARTClockFreq
> > // with factor M/(M+D), where:
> > // M = 1 .. 15
> > // D = 0 .. (M-1) (see NXP LPC23xx User Manual, e.g. U0FDR)
> > // This causes a smaller error of the calculated Baud rate
> > // than with standard values (M = 1, D = 0):
> > // UARTClockFreq M
> > // BaudRate = --------------- * -----
> > // 16 * UnDL M + D
> > //
> > // Result of this routine is concatenation of 4 bytes: 0x00, UnDLM,
> > UnDLL and UnFDR.
> > // Use it on the following manner:
> > // U32 ulTempVar = GetUART_DL_MD(ulMyUARTClockFreq, ulMyBaudRate);
> > // if ((ulTempVar & 0xFF000000) == 0) {
> > // UnFDR = ulTempVar & 0xFF;
> > // UnDLL = (ulTempVar >> 8) & 0xFF;
> > // UnDLM = (ulTempVar >> 16) & 0xFF;
> > // }
> > // else {
> > // //error: DL overflow, too big to fit 16 bits!!!
> > // }
> >
> > #define ACCURACY_FACTOR 1000000ULL
> > // 1000000ULL -> accuracy is 1ppm, must use U64 type for error
> > calculation
> > // Requirement#1: (15 * ACCURACY_FACTOR * ulUARTClockFreq + 232 *
> > ulBaudRate) < (2**64)
> > // Requirement#2: ulBaudRate < 9256.4kBd (i.e. 9.2MBd)
> >
> > U32 ulDLOpt, ulErrOpt, ulMOpt, ulDOpt;
> > U32 ulDL, ulErr, ulM, ulD;
> > U64 ullDLAccurate;
> >
> > // optimal variables initialization
> > ulDLOpt = 0x0001FFFF; // DL overflow as default value
> > ulErrOpt = ACCURACY_FACTOR; // huge error as default value
> > ulMOpt = 1;
> > ulDOpt = 0;
> > //
> > for (ulM = 1; ulM < 16; ulM++) {
> > for (ulD = 0; ulD < ulM; ulD++) {
> > // calculate (DL * ACCURACY_FACTOR)
> > ullDLAccurate = (ACCURACY_FACTOR * ulUARTClockFreq * ulM + 8UL *
> > ulBaudRate * (ulD + ulM))/
> > (16UL * ulBaudRate * (ulD + ulM));
> > // calculate "integer round" of DL
> > ulDL = (ullDLAccurate + (ACCURACY_FACTOR >> 1)) /
> > ACCURACY_FACTOR;
> > // calculate the magnitude of relative error (in ppm) between
> > them
> > if (ACCURACY_FACTOR * ulDL > ullDLAccurate) {
> > ulErr = (ACCURACY_FACTOR * ulDL - ullDLAccurate) / ulDL;
> > }
> > else {
> > ulErr = (ullDLAccurate - ACCURACY_FACTOR * ulDL) / ulDL;
> > }
> > // use actual values as optimal,
> > // if the new error is less than the previous one
> > // and DL is a valid U16 number
> > if ((ulErr < ulErrOpt) && (ulDL < 0x00010000)) {
> > ulMOpt = ulM;
> > ulDOpt = ulD;
> > ulErrOpt = ulErr;
> > ulDLOpt = ulDL;
> > }
> > }
> > }
> > // concatenate results: OVL:DLM:DLL:FDR (byte FDR consists of two
> > nibbles MULVAL:DIVADDVAL)
> > return (ulDLOpt << 8) | (ulMOpt << 4) | ulDOpt;
> > }
> > //*********************************************
> >
> >
> > Here the test code:
> >
> >
> > //*********************************************
> > U8 ucDLM, ucDLL, ucM, ucD, ucOvfl, ucFDR;
> > U32 ulTempVar, ulBaudRate, ulUARTCLKFREQ;
> > double fdTemp;
> >
> > ulTempVar = GetUART_DL_MD(ulUARTCLKFREQ, ulBaudRate);
> > ucFDR = ulTempVar & 0xFF;
> > ucDLL = (ulTempVar >> 8) & 0xFF;
> > ucDLM = (ulTempVar >> 16) & 0xFF;
> > ucD = ucFDR & 0xF;
> > ucM = ucFDR >> 4;
> > ucOvfl = (ulTempVar >> 24) & 0xFF;
> >
> > printf("\r\n FDR=0x%02X, DLM=0x%02X, DLL=0x%02X", ucFDR, ucDLM, ucDLL);
> > printf("\r\n M=%u, D=%u, DL=%u, OVL=%u", ucM, ucD, (ulTempVar >> 8) &
> > 0xFFFF, ucOvfl);
> >
> > fdTemp = (1.0 * ulResult)/(16.0 * (256.0 * ucDLM + ucDLL) * (1.0 + (1.0
> > * ucD) / ucM));
> >
> > printf("\r\n BaudRate=%.3fBd, Error=%.6f%%", fdTemp, (fdTemp - ul)/ul *
> > 100.0 );
> > //*********************************************
> >
> >
> > Here some test answers:
> >
> > //12MHz, 9600Bd
> > //*********************************************
> > Get UARTCLKFREQ: 12000000
> > Get BaudRate : 9600
> > FDR=0xA1, DLM=0x00, DLL=0x47
> > M, D=1, DLq, OVL=0
> > BaudRate03.073Bd, Error=0.032010%
> > //*********************************************
> >
> > //max UART frequency 72MHz, min baudrate 75Bd:
> > //*********************************************
> > Get UARTCLKFREQ: 72000000
> > Get BaudRate : 75
> > FDR=0x10, DLM=0xEA, DLL=0x60
> > M=1, D=0, DL`000, OVL=0
> > BaudRateu.000Bd, Error=0.000000%
> > //*********************************************
> >
> > //the highest hypothetic UART frequency is 152.0412MHz
> > // at baudrate 75Bd:
> > //*********************************************
> > Get UARTCLKFREQ: 152041200
> > Get BaudRate : 75
> > FDR=0xFE, DLM=0xFF, DLL=0xFF
> > M, D, DLe535, OVL=0
> > BaudRateu.000Bd, Error=0.000000%
> > //*********************************************
> >
> > //low UART frequency of 12Mhz
> > // at baudrate 230.4kBd (error would be
> > // innacceptable 8.5% without a fractional divider!):
> > //*********************************************
> > Get UARTCLKFREQ: 12000000
> > Get BaudRate : 230400
> > FDR=0x85, DLM=0x00, DLL=0x02
> > M=8, D=5, DL=2, OVL=0
> > BaudRate#0769.231Bd, Error=0.160256%
> > //*********************************************
> >
> >
> > Enjoy it!
> >
> > Marcin
> >
> >
> >
> >
> >
> >
> >
> >
Jan,

The following will save interations:

if (new_divisor > 2)
{
...
}
else
break;

Jeff
--- In l..., "Jan Vanek" wrote:
>
> Hi Jeff,
>
> thanks for pointing this out. I remember another lpc2000 member told me once that the DAV must be less the MV but this condition is
> not stated in the LPC288X manual, and I implemented it for LPC2888. At that time I considered that condition ilogical and was not
> willing to make any changes, after all, everyone is free to make his/her own changes to that code. Now that my code is in files
> section and you proved it doesn't work for processors/families more significant than LPC288X, I changed it there. ((I kept the
> "++dav" in the for loop because this prevents the DAV being zero - when it is zero there is no prescaling because MV / (MV + DAV) =
> 1; so it saves 15 iterations of the loop.))
>
> Thanks and regards,
> Jan
>
>
> ----- Original Message -----
> From: "ksdoubleshooter"
> To:
> Sent: Friday, March 19, 2010 10:20 PM
> Subject: [lpc2000] Re: Exact 115200 bps baud rate generation in LPC 2378
>
>
>
>
>
>
>
> Jan,
>
> I've implemented your code on an LPC1114 and it has problems. The
> DIVADDVAL must be less than MULVAL. In addition, if the fractional
> divider is active (DIVADDVAL > 0), the divisor value must be 3 or
> greater. These conditions are stated in both the LPC2300 and LPC1100
> user manuals.
>
> Given an input clock of 6MHz and a desired baud rate of 115200, your
> code gives the following results:
>
> divisor = 1
> DIVADDVAL = 9
> MULVAL = 4
>
> Now, here is where you may have gone wrong in your testing. With
> those values, the LPC1114 uart transmits properly, but the receive
> buad rate is off.
>
> With the following code, all the required conditions are met.
>
> for (unsigned int mv = 1; mv < 16; ++mv)
> {
> for (unsigned int dav = 0; dav < mv; ++dav)
> {
> unsigned int prescaledClock = (freq * mv) / (16 * (mv + dav));
> unsigned int newDivisor = prescaledClock / baud;
> if (newDivisor > 2)
> {
>
> Given an input clock of 6MHz and a desired baud rate of 115200, this
> code gives the following results:
>
> divisor = 3
> DIVADDVAL = 1
> MULVAL = 12
>
> With these values the the LPC1114 uart works properly for both
> transmit and receive.
>
> Jeff
>
>
>
> --- In l..., Jan Vanek wrote:
> >
> > Hi Marcin,
> >
> > I was not aware about the 3rd condition, because the LPC288x user manual
> > doesn't state it. The 3rd condition basically says you can prescale with
> > factor in range (0.5, 1.0>, whereas my code allows prescaling with factor
> > less than 0.5. Obviously it can be changed very easily, but since I now
> > don't see why there should be this 0.5 limit, I keep it as it is.
> >
> > After some time I grasped the idea of your code, but I didn't do precise
> > verification, or checking how this relative-error really behaves. The only
> > small change I could suggest is to count the uID from 1, because value 0
> > means no-prescaling. You'd have to calculate the values/error for the
> > no-prescaling case before the loops once, and then you'd save 15 repetitions
> > of the inner-loop body (where uID were 0). I agree, not a big win :-)
> >
> > With regards,
> > Jan
> >
> >
> > On Mon, Aug 31, 2009 at 4:54 AM, wrote:
> >
> > > --- In l..., "Jan Vanek" wrote:
> > > >
> > > > Hi,
> > > >
> > > > I have slightly improved the search algorithm I posted some time ago to
> > > > allow for smaller resulting baud rate. It can be used in run-time. I
> > > think
> > > > it finds the best possible value, whereas the algorithm on the page seems
> > > to
> > > > only get very close, e.g. for 12'000'000Hz and 9600 the page gives
> > > resulting
> > > > baud-rate 9603 whereas my algorithm finds settings for 9601.
> > > >
> > > > u32 findDivisorAndPrescale(u32 frequency, u32 baudRate, bool
> > > > allowSmallerBaudRate, u32& mulVal, u32& divAddVal)
> > > > {
> > > > // The FDR is set to prescale the UART_CLK with factor M/(M+D),
> > > > // where both M and D are in <0, 15>. This is to achieve smaller
> > > > // error when calculating the divisor (the UnDL).
> > > > // UART_CLK M
> > > > // UARTbaudrate = --------- x -----
> > > > // 16 x UnDL (M+D)
> > > >
> > > > u32 divisor = frequency / (16 * baudRate);
> > > > u32 baudRateError = (frequency / (16 * divisor)) - baudRate;
> > > > if (allowSmallerBaudRate)
> > > > {
> > > > u32 otherDivisor = divisor + 1;
> > > > u32 otherBaudRateError = baudRate - (frequency / (16 *
> > > > otherDivisor));
> > > > if (baudRateError > otherBaudRateError)
> > > > {
> > > > divisor = otherDivisor;
> > > > baudRateError = otherBaudRateError;
> > > > }
> > > > }
> > > > mulVal = 1;
> > > > divAddVal = 0;
> > > > for (u32 mv = 0; ++mv < 16;)
> > > > {
> > > > for (u32 dav = 0; ++dav < 16;)
> > > > {
> > > > u32 prescaledClock = (frequency * mv) / (16 * (mv + dav));
> > > >.......
> > >
> > >
> > > Hi Jan,
> > >
> > > I wrote my baudrate calculation routine too - and at
> > > UART_CLK = 12MHz and 9600Bd got a result of 9603Bd.
> > > I suppose, Your algorithm delivers 9601Bd due of a some deviation of its
> > > behavior from the NXP's recipe (taken from "User Manual LPC23XX"):
> > > //The value of MULVAL and DIVADDVAL should
> > > //comply to the following conditions:
> > > //1. 0 < MULVAL 15
> > > //2. 0 DIVADDVAL < 15
> > > //3. DIVADDVAL > > > I guess, the 3rd condition has been ignored in Your code.
> > >
> > > Here my routine:
> > >
> > > //*********************************************
> > >
> > > //////////////////////////////////////////////////////////////////////////////////////
> > > // LPC23xx UART Fractional Divider Calculation by Marcin M. Krajka,
> > > mmk20090830
> > > // Use it free, please.
> > > // Note: Remarks partially taken from the baudrate calculation routine by
> > > Jan Vanek, thanks Jan!
> > > // Any bugs found? Please, let me know: arm7mmktronics.de
> > >
> > > //////////////////////////////////////////////////////////////////////////////////////
> > > U32 GetUART_DL_MD(U32 ulUARTClockFreq, U32 ulBaudRate)
> > > {
> > > // The FDR (Fractional Divider Register) prescales the UARTClockFreq
> > > // with factor M/(M+D), where:
> > > // M = 1 .. 15
> > > // D = 0 .. (M-1) (see NXP LPC23xx User Manual, e.g. U0FDR)
> > > // This causes a smaller error of the calculated Baud rate
> > > // than with standard values (M = 1, D = 0):
> > > // UARTClockFreq M
> > > // BaudRate = --------------- * -----
> > > // 16 * UnDL M + D
> > > //
> > > // Result of this routine is concatenation of 4 bytes: 0x00, UnDLM,
> > > UnDLL and UnFDR.
> > > // Use it on the following manner:
> > > // U32 ulTempVar = GetUART_DL_MD(ulMyUARTClockFreq, ulMyBaudRate);
> > > // if ((ulTempVar & 0xFF000000) == 0) {
> > > // UnFDR = ulTempVar & 0xFF;
> > > // UnDLL = (ulTempVar >> 8) & 0xFF;
> > > // UnDLM = (ulTempVar >> 16) & 0xFF;
> > > // }
> > > // else {
> > > // //error: DL overflow, too big to fit 16 bits!!!
> > > // }
> > >
> > > #define ACCURACY_FACTOR 1000000ULL
> > > // 1000000ULL -> accuracy is 1ppm, must use U64 type for error
> > > calculation
> > > // Requirement#1: (15 * ACCURACY_FACTOR * ulUARTClockFreq + 232 *
> > > ulBaudRate) < (2**64)
> > > // Requirement#2: ulBaudRate < 9256.4kBd (i.e. 9.2MBd)
> > >
> > > U32 ulDLOpt, ulErrOpt, ulMOpt, ulDOpt;
> > > U32 ulDL, ulErr, ulM, ulD;
> > > U64 ullDLAccurate;
> > >
> > > // optimal variables initialization
> > > ulDLOpt = 0x0001FFFF; // DL overflow as default value
> > > ulErrOpt = ACCURACY_FACTOR; // huge error as default value
> > > ulMOpt = 1;
> > > ulDOpt = 0;
> > > //
> > > for (ulM = 1; ulM < 16; ulM++) {
> > > for (ulD = 0; ulD < ulM; ulD++) {
> > > // calculate (DL * ACCURACY_FACTOR)
> > > ullDLAccurate = (ACCURACY_FACTOR * ulUARTClockFreq * ulM + 8UL *
> > > ulBaudRate * (ulD + ulM))/
> > > (16UL * ulBaudRate * (ulD + ulM));
> > > // calculate "integer round" of DL
> > > ulDL = (ullDLAccurate + (ACCURACY_FACTOR >> 1)) /
> > > ACCURACY_FACTOR;
> > > // calculate the magnitude of relative error (in ppm) between
> > > them
> > > if (ACCURACY_FACTOR * ulDL > ullDLAccurate) {
> > > ulErr = (ACCURACY_FACTOR * ulDL - ullDLAccurate) / ulDL;
> > > }
> > > else {
> > > ulErr = (ullDLAccurate - ACCURACY_FACTOR * ulDL) / ulDL;
> > > }
> > > // use actual values as optimal,
> > > // if the new error is less than the previous one
> > > // and DL is a valid U16 number
> > > if ((ulErr < ulErrOpt) && (ulDL < 0x00010000)) {
> > > ulMOpt = ulM;
> > > ulDOpt = ulD;
> > > ulErrOpt = ulErr;
> > > ulDLOpt = ulDL;
> > > }
> > > }
> > > }
> > > // concatenate results: OVL:DLM:DLL:FDR (byte FDR consists of two
> > > nibbles MULVAL:DIVADDVAL)
> > > return (ulDLOpt << 8) | (ulMOpt << 4) | ulDOpt;
> > > }
> > > //*********************************************
> > >
> > >
> > > Here the test code:
> > >
> > >
> > > //*********************************************
> > > U8 ucDLM, ucDLL, ucM, ucD, ucOvfl, ucFDR;
> > > U32 ulTempVar, ulBaudRate, ulUARTCLKFREQ;
> > > double fdTemp;
> > >
> > > ulTempVar = GetUART_DL_MD(ulUARTCLKFREQ, ulBaudRate);
> > > ucFDR = ulTempVar & 0xFF;
> > > ucDLL = (ulTempVar >> 8) & 0xFF;
> > > ucDLM = (ulTempVar >> 16) & 0xFF;
> > > ucD = ucFDR & 0xF;
> > > ucM = ucFDR >> 4;
> > > ucOvfl = (ulTempVar >> 24) & 0xFF;
> > >
> > > printf("\r\n FDR=0x%02X, DLM=0x%02X, DLL=0x%02X", ucFDR, ucDLM, ucDLL);
> > > printf("\r\n M=%u, D=%u, DL=%u, OVL=%u", ucM, ucD, (ulTempVar >> 8) &
> > > 0xFFFF, ucOvfl);
> > >
> > > fdTemp = (1.0 * ulResult)/(16.0 * (256.0 * ucDLM + ucDLL) * (1.0 + (1.0
> > > * ucD) / ucM));
> > >
> > > printf("\r\n BaudRate=%.3fBd, Error=%.6f%%", fdTemp, (fdTemp - ul)/ul *
> > > 100.0 );
> > > //*********************************************
> > >
> > >
> > > Here some test answers:
> > >
> > > //12MHz, 9600Bd
> > > //*********************************************
> > > Get UARTCLKFREQ: 12000000
> > > Get BaudRate : 9600
> > > FDR=0xA1, DLM=0x00, DLL=0x47
> > > M=10, D=1, DL=71, OVL=0
> > > BaudRate=9603.073Bd, Error=0.032010%
> > > //*********************************************
> > >
> > > //max UART frequency 72MHz, min baudrate 75Bd:
> > > //*********************************************
> > > Get UARTCLKFREQ: 72000000
> > > Get BaudRate : 75
> > > FDR=0x10, DLM=0xEA, DLL=0x60
> > > M=1, D=0, DL=60000, OVL=0
> > > BaudRate=75.000Bd, Error=0.000000%
> > > //*********************************************
> > >
> > > //the highest hypothetic UART frequency is 152.0412MHz
> > > // at baudrate 75Bd:
> > > //*********************************************
> > > Get UARTCLKFREQ: 152041200
> > > Get BaudRate : 75
> > > FDR=0xFE, DLM=0xFF, DLL=0xFF
> > > M=15, D=14, DL=65535, OVL=0
> > > BaudRate=75.000Bd, Error=0.000000%
> > > //*********************************************
> > >
> > > //low UART frequency of 12Mhz
> > > // at baudrate 230.4kBd (error would be
> > > // innacceptable 8.5% without a fractional divider!):
> > > //*********************************************
> > > Get UARTCLKFREQ: 12000000
> > > Get BaudRate : 230400
> > > FDR=0x85, DLM=0x00, DLL=0x02
> > > M=8, D=5, DL=2, OVL=0
> > > BaudRate=230769.231Bd, Error=0.160256%
> > > //*********************************************
> > >
> > >
> > > Enjoy it!
> > >
> > > Marcin
> > >
> > >
> > >
> > >
> > >
> > >
> > >
> > >
Hi Jeff,

excellent, you're right. I've made the change in files section.

Regards,
Jan

----- Original Message -----
From: "ksdoubleshooter"
To:
Sent: Tuesday, March 23, 2010 4:12 PM
Subject: [lpc2000] Re: Exact 115200 bps baud rate generation in LPC 2378

Jan,

The following will save interations:

if (new_divisor > 2)
{
...
}
else
break;

Jeff
--- In l..., "Jan Vanek" wrote:
>
> Hi Jeff,
>
> thanks for pointing this out. I remember another lpc2000 member told me once that the DAV must be less the MV but this condition
> is
> not stated in the LPC288X manual, and I implemented it for LPC2888. At that time I considered that condition ilogical and was not
> willing to make any changes, after all, everyone is free to make his/her own changes to that code. Now that my code is in files
> section and you proved it doesn't work for processors/families more significant than LPC288X, I changed it there. ((I kept the
> "++dav" in the for loop because this prevents the DAV being zero - when it is zero there is no prescaling because MV / (MV + DAV)
> > 1; so it saves 15 iterations of the loop.))
>
> Thanks and regards,
> Jan
> ----- Original Message -----
> From: "ksdoubleshooter"
> To:
> Sent: Friday, March 19, 2010 10:20 PM
> Subject: [lpc2000] Re: Exact 115200 bps baud rate generation in LPC 2378
>
> Jan,
>
> I've implemented your code on an LPC1114 and it has problems. The
> DIVADDVAL must be less than MULVAL. In addition, if the fractional
> divider is active (DIVADDVAL > 0), the divisor value must be 3 or
> greater. These conditions are stated in both the LPC2300 and LPC1100
> user manuals.
>
> Given an input clock of 6MHz and a desired baud rate of 115200, your
> code gives the following results:
>
> divisor = 1
> DIVADDVAL = 9
> MULVAL = 4
>
> Now, here is where you may have gone wrong in your testing. With
> those values, the LPC1114 uart transmits properly, but the receive
> buad rate is off.
>
> With the following code, all the required conditions are met.
>
> for (unsigned int mv = 1; mv < 16; ++mv)
> {
> for (unsigned int dav = 0; dav < mv; ++dav)
> {
> unsigned int prescaledClock = (freq * mv) / (16 * (mv + dav));
> unsigned int newDivisor = prescaledClock / baud;
> if (newDivisor > 2)
> {
>
> Given an input clock of 6MHz and a desired baud rate of 115200, this
> code gives the following results:
>
> divisor = 3
> DIVADDVAL = 1
> MULVAL = 12
>
> With these values the the LPC1114 uart works properly for both
> transmit and receive.
>
> Jeff
>
> --- In l..., Jan Vanek wrote:
> >
> > Hi Marcin,
> >
> > I was not aware about the 3rd condition, because the LPC288x user manual
> > doesn't state it. The 3rd condition basically says you can prescale with
> > factor in range (0.5, 1.0>, whereas my code allows prescaling with factor
> > less than 0.5. Obviously it can be changed very easily, but since I now
> > don't see why there should be this 0.5 limit, I keep it as it is.
> >
> > After some time I grasped the idea of your code, but I didn't do precise
> > verification, or checking how this relative-error really behaves. The only
> > small change I could suggest is to count the uID from 1, because value 0
> > means no-prescaling. You'd have to calculate the values/error for the
> > no-prescaling case before the loops once, and then you'd save 15 repetitions
> > of the inner-loop body (where uID were 0). I agree, not a big win :-)
> >
> > With regards,
> > Jan
> >
> >
> > On Mon, Aug 31, 2009 at 4:54 AM, wrote:
> >
> > > --- In l..., "Jan Vanek" wrote:
> > > >
> > > > Hi,
> > > >
> > > > I have slightly improved the search algorithm I posted some time ago to
> > > > allow for smaller resulting baud rate. It can be used in run-time. I
> > > think
> > > > it finds the best possible value, whereas the algorithm on the page seems
> > > to
> > > > only get very close, e.g. for 12'000'000Hz and 9600 the page gives
> > > resulting
> > > > baud-rate 9603 whereas my algorithm finds settings for 9601.
> > > >
> > > > u32 findDivisorAndPrescale(u32 frequency, u32 baudRate, bool
> > > > allowSmallerBaudRate, u32& mulVal, u32& divAddVal)
> > > > {
> > > > // The FDR is set to prescale the UART_CLK with factor M/(M+D),
> > > > // where both M and D are in <0, 15>. This is to achieve smaller
> > > > // error when calculating the divisor (the UnDL).
> > > > // UART_CLK M
> > > > // UARTbaudrate = --------- x -----
> > > > // 16 x UnDL (M+D)
> > > >
> > > > u32 divisor = frequency / (16 * baudRate);
> > > > u32 baudRateError = (frequency / (16 * divisor)) - baudRate;
> > > > if (allowSmallerBaudRate)
> > > > {
> > > > u32 otherDivisor = divisor + 1;
> > > > u32 otherBaudRateError = baudRate - (frequency / (16 *
> > > > otherDivisor));
> > > > if (baudRateError > otherBaudRateError)
> > > > {
> > > > divisor = otherDivisor;
> > > > baudRateError = otherBaudRateError;
> > > > }
> > > > }
> > > > mulVal = 1;
> > > > divAddVal = 0;
> > > > for (u32 mv = 0; ++mv < 16;)
> > > > {
> > > > for (u32 dav = 0; ++dav < 16;)
> > > > {
> > > > u32 prescaledClock = (frequency * mv) / (16 * (mv + dav));
> > > >.......
> > >
> > >
> > > Hi Jan,
> > >
> > > I wrote my baudrate calculation routine too - and at
> > > UART_CLK = 12MHz and 9600Bd got a result of 9603Bd.
> > > I suppose, Your algorithm delivers 9601Bd due of a some deviation of its
> > > behavior from the NXP's recipe (taken from "User Manual LPC23XX"):
> > > //The value of MULVAL and DIVADDVAL should
> > > //comply to the following conditions:
> > > //1. 0 < MULVAL 15
> > > //2. 0 DIVADDVAL < 15
> > > //3. DIVADDVAL > > > I guess, the 3rd condition has been ignored in Your code.
> > >
> > > Here my routine:
> > >
> > > //*********************************************
> > >
> > > //////////////////////////////////////////////////////////////////////////////////////
> > > // LPC23xx UART Fractional Divider Calculation by Marcin M. Krajka,
> > > mmk20090830
> > > // Use it free, please.
> > > // Note: Remarks partially taken from the baudrate calculation routine by
> > > Jan Vanek, thanks Jan!
> > > // Any bugs found? Please, let me know: arm7mmktronics.de
> > >
> > > //////////////////////////////////////////////////////////////////////////////////////
> > > U32 GetUART_DL_MD(U32 ulUARTClockFreq, U32 ulBaudRate)
> > > {
> > > // The FDR (Fractional Divider Register) prescales the UARTClockFreq
> > > // with factor M/(M+D), where:
> > > // M = 1 .. 15
> > > // D = 0 .. (M-1) (see NXP LPC23xx User Manual, e.g. U0FDR)
> > > // This causes a smaller error of the calculated Baud rate
> > > // than with standard values (M = 1, D = 0):
> > > // UARTClockFreq M
> > > // BaudRate = --------------- * -----
> > > // 16 * UnDL M + D
> > > //
> > > // Result of this routine is concatenation of 4 bytes: 0x00, UnDLM,
> > > UnDLL and UnFDR.
> > > // Use it on the following manner:
> > > // U32 ulTempVar = GetUART_DL_MD(ulMyUARTClockFreq, ulMyBaudRate);
> > > // if ((ulTempVar & 0xFF000000) == 0) {
> > > // UnFDR = ulTempVar & 0xFF;
> > > // UnDLL = (ulTempVar >> 8) & 0xFF;
> > > // UnDLM = (ulTempVar >> 16) & 0xFF;
> > > // }
> > > // else {
> > > // //error: DL overflow, too big to fit 16 bits!!!
> > > // }
> > >
> > > #define ACCURACY_FACTOR 1000000ULL
> > > // 1000000ULL -> accuracy is 1ppm, must use U64 type for error
> > > calculation
> > > // Requirement#1: (15 * ACCURACY_FACTOR * ulUARTClockFreq + 232 *
> > > ulBaudRate) < (2**64)
> > > // Requirement#2: ulBaudRate < 9256.4kBd (i.e. 9.2MBd)
> > >
> > > U32 ulDLOpt, ulErrOpt, ulMOpt, ulDOpt;
> > > U32 ulDL, ulErr, ulM, ulD;
> > > U64 ullDLAccurate;
> > >
> > > // optimal variables initialization
> > > ulDLOpt = 0x0001FFFF; // DL overflow as default value
> > > ulErrOpt = ACCURACY_FACTOR; // huge error as default value
> > > ulMOpt = 1;
> > > ulDOpt = 0;
> > > //
> > > for (ulM = 1; ulM < 16; ulM++) {
> > > for (ulD = 0; ulD < ulM; ulD++) {
> > > // calculate (DL * ACCURACY_FACTOR)
> > > ullDLAccurate = (ACCURACY_FACTOR * ulUARTClockFreq * ulM + 8UL *
> > > ulBaudRate * (ulD + ulM))/
> > > (16UL * ulBaudRate * (ulD + ulM));
> > > // calculate "integer round" of DL
> > > ulDL = (ullDLAccurate + (ACCURACY_FACTOR >> 1)) /
> > > ACCURACY_FACTOR;
> > > // calculate the magnitude of relative error (in ppm) between
> > > them
> > > if (ACCURACY_FACTOR * ulDL > ullDLAccurate) {
> > > ulErr = (ACCURACY_FACTOR * ulDL - ullDLAccurate) / ulDL;
> > > }
> > > else {
> > > ulErr = (ullDLAccurate - ACCURACY_FACTOR * ulDL) / ulDL;
> > > }
> > > // use actual values as optimal,
> > > // if the new error is less than the previous one
> > > // and DL is a valid U16 number
> > > if ((ulErr < ulErrOpt) && (ulDL < 0x00010000)) {
> > > ulMOpt = ulM;
> > > ulDOpt = ulD;
> > > ulErrOpt = ulErr;
> > > ulDLOpt = ulDL;
> > > }
> > > }
> > > }
> > > // concatenate results: OVL:DLM:DLL:FDR (byte FDR consists of two
> > > nibbles MULVAL:DIVADDVAL)
> > > return (ulDLOpt << 8) | (ulMOpt << 4) | ulDOpt;
> > > }
> > > //*********************************************
> > >
> > >
> > > Here the test code:
> > >
> > >
> > > //*********************************************
> > > U8 ucDLM, ucDLL, ucM, ucD, ucOvfl, ucFDR;
> > > U32 ulTempVar, ulBaudRate, ulUARTCLKFREQ;
> > > double fdTemp;
> > >
> > > ulTempVar = GetUART_DL_MD(ulUARTCLKFREQ, ulBaudRate);
> > > ucFDR = ulTempVar & 0xFF;
> > > ucDLL = (ulTempVar >> 8) & 0xFF;
> > > ucDLM = (ulTempVar >> 16) & 0xFF;
> > > ucD = ucFDR & 0xF;
> > > ucM = ucFDR >> 4;
> > > ucOvfl = (ulTempVar >> 24) & 0xFF;
> > >
> > > printf("\r\n FDR=0x%02X, DLM=0x%02X, DLL=0x%02X", ucFDR, ucDLM, ucDLL);
> > > printf("\r\n M=%u, D=%u, DL=%u, OVL=%u", ucM, ucD, (ulTempVar >> 8) &
> > > 0xFFFF, ucOvfl);
> > >
> > > fdTemp = (1.0 * ulResult)/(16.0 * (256.0 * ucDLM + ucDLL) * (1.0 + (1.0
> > > * ucD) / ucM));
> > >
> > > printf("\r\n BaudRate=%.3fBd, Error=%.6f%%", fdTemp, (fdTemp - ul)/ul *
> > > 100.0 );
> > > //*********************************************
> > >
> > >
> > > Here some test answers:
> > >
> > > //12MHz, 9600Bd
> > > //*********************************************
> > > Get UARTCLKFREQ: 12000000
> > > Get BaudRate : 9600
> > > FDR=0xA1, DLM=0x00, DLL=0x47
> > > M, D=1, DLq, OVL=0
> > > BaudRate03.073Bd, Error=0.032010%
> > > //*********************************************
> > >
> > > //max UART frequency 72MHz, min baudrate 75Bd:
> > > //*********************************************
> > > Get UARTCLKFREQ: 72000000
> > > Get BaudRate : 75
> > > FDR=0x10, DLM=0xEA, DLL=0x60
> > > M=1, D=0, DL`000, OVL=0
> > > BaudRateu.000Bd, Error=0.000000%
> > > //*********************************************
> > >
> > > //the highest hypothetic UART frequency is 152.0412MHz
> > > // at baudrate 75Bd:
> > > //*********************************************
> > > Get UARTCLKFREQ: 152041200
> > > Get BaudRate : 75
> > > FDR=0xFE, DLM=0xFF, DLL=0xFF
> > > M, D, DLe535, OVL=0
> > > BaudRateu.000Bd, Error=0.000000%
> > > //*********************************************
> > >
> > > //low UART frequency of 12Mhz
> > > // at baudrate 230.4kBd (error would be
> > > // innacceptable 8.5% without a fractional divider!):
> > > //*********************************************
> > > Get UARTCLKFREQ: 12000000
> > > Get BaudRate : 230400
> > > FDR=0x85, DLM=0x00, DLL=0x02
> > > M=8, D=5, DL=2, OVL=0
> > > BaudRate#0769.231Bd, Error=0.160256%
> > > //*********************************************
> > >
> > >
> > > Enjoy it!
> > >
> > > Marcin
> > >
> > >
> > >
> > >
> > >
> > >
> > >
> > >
I had a project with the LPC2378 3 years ago and at this time the 3rd condition (DIVADDVAL U1DL = 3; DIVADDVAL = 7; MULVAL = 6
That violates the 3rd condition in the new manual but I never heard of problems. Because of the errata sheet at that time I had to limit the speed to 48MHz so also the revision '-' was usable.

In the meantime there are new revisions of the LPC2378 chip running with this software. (For new projects I considered this 3rd condition, so I won't have any problems, but for the old designs the software was written a long time ago).

Did someone have problems with this DIVADDVAL Will I get problems with newer chip revisions?

Thanks