EmbeddedRelated.com
Forums
Memfault Beyond the Launch

Code to configure LPC2300 UART with arbitrary clock rate

Started by perdrix 8 months ago4 replieslatest reply 8 months ago90 views

I'm looking some code that configures a UART for 115200 baud and assumes that the PCLK to for the UART is 12MHz.  Which works fine if my CCLK is 48MHz and the peripheral clock divider is 4.

  U1LCR = 0x83;                              /* 8 bits, no Parity, 1 Stop bit*/
  U1DLL = 3;                                 /* for 12MHz PCLK Clock         */
  U1FDR = 0x67;                              /* Fractional Divider           */
  U1LCR = 0x03;                              /* DLAB = 0                     */

That really doesn't work if CCLK is e.g. 72MHz, and this code doesn't work too well either:

    Fdiv = ( pclk / 16 ) / baudrate ;    /*baud rate */
    U1DLM = Fdiv / 256;                            
    U1DLL = Fdiv % 256;
Has anyone got some code that correctly configures the DLL, DLM and FDR values given a desired baud rate?

The LPC23xx user manual has a flowchart and a lookup table which is pretty scary - I'm hoping someone has done the hard work already

Thanks, David

[ - ]
Reply by KocsonyaAugust 29, 2023

I don't know why you find it scary, really. They give you the equation for calculating the baud rate, which is pretty simple, for the baud rate is PCLK divided by 16 divided by the divisor value.

I don't know what your PCLK is, but if I assume that you just boosted CCLK to 72MHz and left the divider at 4, then it would be 18MHz. Therefore, in your case, the division factor should be 18MHz / 16 / 115200 baud = 9.765625.

If you then truncate it to integer 9 and load that to DLM and DLL, then of course your actual baud rate will be 125.0 kbaud, which is way out of tolerance.  If instead of truncating you round the value to 10 then it will be 112.5 kbaud. This latter is actually only -2.34% error, which is within the 3% specified for RS-232, but you can do better.

If you look at the equation, the fractional divider gives you an option to multiply the divisor in the DL with a value in [1,2). Since both divaddval and mulval are 4 bits plus there are restrictions, even an exhaustive search is very limited (256 combinations, of which only 36 are valid), so you can whip up your favourite scripting language and in about 5 minutes write a short script that calculates these values. Basically, you need to try the DL values from 9 down to 4, run through all legal combinations of divaddval and mulval and calculate DL*(1+divaddval/mulval) and see which combination is closest to the required 9.765625. In your case, DL=9, divadd=1 mul=12 gives 9.75, not a bad choice (115384 baud, +0.15% error) but DL=8 divadd=2 mul=9 gives the slightly better 9.777.. (115057 baud, -0.12% error). This calculation is really, really simple (not even highschool math) and should be a trivial programming exercise. And the LPC manual is pretty clear on how to do it.

[ - ]
Reply by DilbertoAugust 28, 2023

Hi, David!

   I can't say if it helps, but that's the code I used some 10+ years ago for the LP2388 UART initialization: https://drive.google.com/file/d/1O1ouRyZV6dimaLkok...

   I don't know if it compiles, but the main idea is here.

   It uses a 48 MHz CPU clock, but I think it should work, provided the UART clock is 12 MHz.

   Good luck!

Daniel.

[ - ]
Reply by perdrixAugust 29, 2023

I couldn't access your code, but unless it is a lot cleverer that what I showed, it is unlikely to work as if CCLK is 72MHz, then the UART clock is 18/36/72MHz depending on which divider you choose.  

[ - ]
Reply by perdrixAugust 29, 2023

I came up with this which appears to work:

static float FR_Vals[] = 
{
    1.500, 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.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
};
    
static uint8_t divaddvals[] =
{
     1,  1,  1,  1,  1,  1,  1,  1,
     1,  2,  1,  2,  1,  2,  1,  3,
     2,  3,  1,  4,  3,  2,  3,  4,
     1,  5,  4,  3,  5,  2,  5,  3,
     4,  5,  6,  7,  8,  7,  6,  5,
     4,  7,  3,  8,  5,  7,  9,  2,
     9,  7,  5,  8, 11,  3, 10,  7,
    11,  4,  9,  5, 11,  6, 13,  7,
     8,  9, 10, 11, 12, 13, 14
};

static uint8_t mulvals[] =
{
     2, 15, 14, 13, 12, 11, 10,  9,
     8, 15,  7, 13,  6, 11,  5, 14,
     9, 13,  4, 15, 11,  7, 10, 13,
     3, 14, 11,  8, 13,  5, 12,  7,
     9, 11, 13, 15, 15, 13, 11,  9,
     7, 12,  5, 13,  8, 11, 14,  3,
    13, 10,  7, 11, 15,  4, 13,  9,
    14,  5, 11,  6, 13,  7, 15,  8,
     9, 10, 11, 12, 13, 14, 15
};

uint8_t closestIndex(float value)
{
    uint8_t result = 0;
    float curr = FR_Vals[0];
    for (uint8_t i = 0; i < sizeof(FR_Vals)/sizeof(float); i++)
    {
        if (fabsf(value - FR_Vals[i]) < fabsf(value - curr))
            result = i;
    }
    return result;
}
    
/*----------------------------------------------------------------------------
 *       init_serial:  Initialize Serial Interface
 *---------------------------------------------------------------------------*/
void init_serial (void) {
  /* Initialize the serial interface */

  /* Configure UART1 for 115200 baud. */
    const uint32_t baudrate = 115200;
    
    PINSEL0 &= ~0xC0000000;
    PINSEL0 |=  0x40000000;                    /* Enable TxD1 pin              */
    PINSEL1 &= ~0x00000003;
    PINSEL1 |=  0x00000001;                    /* Enable RxD1 pin              */

    uint16_t divaddval = 0, mulval = 1;
    /* PCLK_UART1=CCLK/2 */
    PCLKSEL0 &= ~(3<<8);               /* PCLK_UART1 = CCLK/4 (e.g. 18MHz) */
    PCLKSEL0 |=  (2<<8);               /* PCLK_UART1 = CCLK/2 (e.g. 36MHz) */

    float pclk = (float)SystemCoreClock/2; // Say 36MHz or 24MHz
    
    float DL_est = pclk/(16 * baudrate);
    float FR_est = 0.0;
    uint32_t DL = (uint32_t) DL_est;
    if (0 != DL_est - DL)
    {
        bool solved = false;
        for (uint8_t i = 0; i < sizeof(FR_Vals)/sizeof(float); i++)
        {
            FR_est = FR_Vals[i];    // Start with 1.5
            DL_est = floorf(pclk/(16*baudrate*FR_est));
            DL = DL_est;
            FR_est = pclk/(16*baudrate*DL);
            if (1.05 < FR_est && FR_est < 1.95)
            {
                solved = true;
                break;
            }
            // Not solved try next FR_est value
        }
            
        if (!solved) do {} while(true);
        
        uint8_t index = closestIndex(FR_est);
        
        divaddval = divaddvals[index];
        mulval = mulvals[index];
    }
    
    U1LCR = 0x83;                              /* 8 bits, no Parity, 1 Stop bit, DLAB = 1 */
    U1FDR = (mulval<<4) | divaddval;
    U1DLM = (DL >> 8) & 0xFF;            
    U1DLL = DL & 0xFF,

    U1LCR = 0x03;                              /* DLAB = 0                     */


}

Cheers, David

Memfault Beyond the Launch