3

My baud rate should be 115200, but it is 892.9

void UART0_Init(int pclk, int baudrate)
{
    unsigned long int DLest;
    //unsigned long int pclk;
    unsigned int temp;
    // Turn on power to UART0
    SC->PCONP |=  PCUART0_POWERON;

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

    UART0->LCR = 0x83;      // 8 bits, no Parity, 1 Stop bit, DLAB=1
    DLest =  (pclk / 16) /  baudrate;   // Set baud rate
    UART0->DLM = DLest / 256;
    UART0->DLL = DLest % 256;
   // UART0->FDR =
    UART0->IER = 0x7;       //enable RBR (b0), THRE(b1), RLS(b2)
    UART0->LCR = 0x03;      // 8 bits, no Parity, 1 Stop bit DLAB = 0
    UART0->FCR = 0x07;      // Enable and reset TX and RX FIFO
}
void prvSetupHardware( void )
{
    /* Disable peripherals power. */
    SC->PCONP = 0;

    /* Enable GPIO power. */
    SC->PCONP = PCONP_PCGPIO;

    /* Disable TPIU. */
    PINCON->PINSEL10 = 0;

    if ( SC->PLL0STAT & ( 1 << 25 ) )
    {
        /* Enable PLL, disconnected. */
        SC->PLL0CON = 1;            
        SC->PLL0FEED = PLLFEED_FEED1;
        SC->PLL0FEED = PLLFEED_FEED2;
    }

    /* Disable PLL, disconnected. */
    SC->PLL0CON = 0;                
    SC->PLL0FEED = PLLFEED_FEED1;
    SC->PLL0FEED = PLLFEED_FEED2;

    /* Enable main OSC. */
    SC->SCS |= 0x20;            
    while( !( SC->SCS & 0x40 ) );

    /* select main OSC, 12MHz, as the PLL clock source. */
    SC->CLKSRCSEL = 0x1;        
    SC->PCLKSEL0 = 0xAAAAAAAA;  /* PCLK is 1/2 CCLK */
    SC->PCLKSEL1 = 0xAAAAAAAA;


    /*Fcc0 = 400MHz, M = 50, N = 3*/
    SC->PLL0CFG = 0x20031;

    SC->PLL0FEED = PLLFEED_FEED1;
    SC->PLL0FEED = PLLFEED_FEED2;

    /* Enable PLL, disconnected. */
    SC->PLL0CON = 1;                
    SC->PLL0FEED = PLLFEED_FEED1;
    SC->PLL0FEED = PLLFEED_FEED2;

    /* Set clock divider. */
    /*Clock = 100MHz, Fcc0 = 400MHz*/
    SC->CCLKCFG = 0x03;//divided by 4.

    /* Configure flash accelerator. */
    SC->FLASHCFG = 0x403a;

    /* Check lock bit status. */
    while( ( ( SC->PLL0STAT & ( 1 << 26 ) ) == 0 ) );   

    /* Enable and connect. */
    SC->PLL0CON = 3;                
    SC->PLL0FEED = PLLFEED_FEED1;
    SC->PLL0FEED = PLLFEED_FEED2;
    while( ( ( SC->PLL0STAT & ( 1 << 25 ) ) == 0 ) );   

    /* Configure the clock for the USB. */

    if( SC->PLL1STAT & ( 1 << 9 ) )
    {
        /* Enable PLL, disconnected. */
        SC->PLL1CON = 1;            
        SC->PLL1FEED = PLLFEED_FEED1;
        SC->PLL1FEED = PLLFEED_FEED2;
    }

    /* Disable PLL, disconnected. */
    SC->PLL1CON = 0;                
    SC->PLL1FEED = PLLFEED_FEED1;
    SC->PLL1FEED = PLLFEED_FEED2;

    SC->PLL1CFG = 0x23;
    SC->PLL1FEED = PLLFEED_FEED1;
    SC->PLL1FEED = PLLFEED_FEED2;

    /* Enable PLL, disconnected. */
    SC->PLL1CON = 1;                
    SC->PLL1FEED = PLLFEED_FEED1;
    SC->PLL1FEED = PLLFEED_FEED2;
    while( ( ( SC->PLL1STAT & ( 1 << 10 ) ) == 0 ) );

    /* Enable and connect. */
    SC->PLL1CON = 3;                
    SC->PLL1FEED = PLLFEED_FEED1;
    SC->PLL1FEED = PLLFEED_FEED2;
    while( ( ( SC->PLL1STAT & ( 1 << 9 ) ) == 0 ) );


    /* Configure the LEDs. */
    vParTestInitialise();

    /*pclk = 100MHZ/2, baud = 115200 */
    UART0_Init(100000000/2, 115200);


    /* Set the sleep mode to highest level sleep*/
    SC->PCON = 0x0;
    SCB->SCR = 0x0;

    /*set push button interrupt */
    PINCON->PINSEL4 |= 0x00100000;
    SC->EXTMODE =0;
    NVIC_SetPriority( EINT0_IRQn, configUIButton1_INTERRUPT_PRIORITY );
    NVIC_EnableIRQ( EINT0_IRQn );
    NVIC_SetPriority( UART0_IRQn, configUIButton1_INTERRUPT_PRIORITY + 1 );
    NVIC_EnableIRQ( UART0_IRQn );

}

I have confirmed that my cclk is running at 100MHz.


I replace the UART init code with code from an example project by Kunil (uart_interrupt_demo):

    void uart_init(int baudrate) {
int errorStatus = -1; //< Failure
        long int SystemFrequency = 100000000;
        // UART clock (FCCO / PCLK_UART0)
        unsigned int uClk = SystemFrequency / 4;
        unsigned int calcBaudrate = 0;
        unsigned int temp = 0;

        unsigned int mulFracDiv, dividerAddFracDiv;
        unsigned int divider = 0;
        unsigned int mulFracDivOptimal = 1;
        unsigned int dividerAddOptimal = 0;
        unsigned int dividerOptimal = 0;

        unsigned int relativeError = 0;
        unsigned int relativeOptimalError = 100000;

    // Turn on power to UART0
    SC->PCONP |=  PCUART0_POWERON;
    // Change P0.2 and P0.3 mode to TXD0 and RXD0
    PINCON->PINSEL0 = (1 << 4) | (1 << 6);

    // Set 8N1 mode
    UART0->LCR = 0x83;

    // Set the baud rate
    uClk = uClk >> 4; /* div by 16 */

        /*
         *  The formula is :
         * BaudRate= uClk * (mulFracDiv/(mulFracDiv+dividerAddFracDiv) / (16 * DLL)
         */

        /*
         * The value of mulFracDiv and dividerAddFracDiv should comply to the following expressions:
         * 0 < mulFracDiv <= 15, 0 <= dividerAddFracDiv <= 15
         */
        for (mulFracDiv = 1; mulFracDiv <= 15; mulFracDiv++) {
            for (dividerAddFracDiv = 0; dividerAddFracDiv <= 15; dividerAddFracDiv++) {
                temp = (mulFracDiv * uClk) / (mulFracDiv + dividerAddFracDiv);

                divider = temp / baudrate;
                if ((temp % baudrate) > (baudrate / 2))
                    divider++;

                if (divider > 2 && divider < 65536) {
                    calcBaudrate = temp / divider;

                    if (calcBaudrate <= baudrate) {
                        relativeError = baudrate - calcBaudrate;
                    } else {
                        relativeError = calcBaudrate - baudrate;
                    }

                    if (relativeError < relativeOptimalError) {
                        mulFracDivOptimal = mulFracDiv;
                        dividerAddOptimal = dividerAddFracDiv;
                        dividerOptimal = divider;
                        relativeOptimalError = relativeError;
                        if (relativeError == 0)
                            break;
                    }
                }
            }

            if (relativeError == 0)
                break;
        }

        if (relativeOptimalError
                < ((baudrate * UART_ACCEPTED_BAUDRATE_ERROR) / 100)) {

            UART0->LCR |= DLAB_ENABLE;
            UART0->DLM = (unsigned char) ((dividerOptimal >> 8) & 0xFF);
            UART0->DLL = (unsigned char) dividerOptimal;
            UART0->LCR &= ~DLAB_ENABLE;

            UART0->FDR = ((mulFracDivOptimal << 4) & 0xF0) | (dividerAddOptimal
                    & 0x0F);

            errorStatus = 0; //< Success
        }


    // Enable TX and RX FIFO
    UART0->FCR |= FIFO_ENABLE;

    // Set FIFO to trigger when at least 14 characters available
    UART0->FCR |= (3 << 6);

    // Enable UART RX interrupt (for LPC17xx UART)
    UART0->IER = RBR_IRQ_ENABLE;

    // Enable the UART interrupt (for Cortex-CM3 NVIC)
    NVIC_EnableIRQ(UART0_IRQn);
}

And it works!

I have to go through and see what i had wrong. I suspect the order of register settings was off.

dsolimano
  • 8,870
  • 3
  • 48
  • 63
michael
  • 2,577
  • 5
  • 39
  • 62
  • For a pclk of 25MHz and baud of 115200, DLest comes out to 13.56. There is an example in the user manual that uses 12MHz and 115200, which give roughly half my calculated DLest. So I know that part works. I think my pclk isn't actually getting set to cclk/4, but I am new to this platform and am having trouble deciding a rock solid test to determine that. – michael Sep 10 '10 at 22:34
  • Did you look at the CMSIS library that NXP provides for lpc17xx devices? – Turbo J Sep 10 '10 at 23:58
  • Actually, now that I divide 100MHz by two instead of four, the baud is 2*892.9 – michael Sep 11 '10 at 00:07
  • If I pass pclk to UART0_Init as anything below 25000000, the baud is always the same (892.9). I even passed 1. – michael Sep 11 '10 at 00:41

3 Answers3

2

Have a look at the Errata sheet. You can't set SC->PCLKSEL0 after you fired up the main PLL, so the divider stays at CCLK/4. Just move the line
/* Setup the peripheral bus to be the same as the PLL output (64 MHz). */ SC->PCLKSEL0 = 0x05555555;

a few lines up, before you enable the PLL.

Turbo J
  • 7,563
  • 1
  • 23
  • 43
  • The comment also mentions a wrong PLL output rate (64MHz), I suspect a Copy&Paste bug here. Its also CCLK rate and *not* PLL0 rate. – Turbo J Sep 10 '10 at 23:34
  • yep that was a copy paste bug. I fixed that, and moved the pclksel0 and pclksel1 above the pll stuff. But I still have way too low a baud rate. – michael Sep 10 '10 at 23:53
0

I wanted a simplified driver for my LPC1768 UART1 port so I have written a baud rate calculator application based on CMSIS code.

You provide the Peripheral Clock Frequency and Desired Baud Rate and it will generate the DLL, Fractional Multiplier & Divider and finally recalculates with the computed values to indicate the Baud rate achievable.

I have tested it with a peripheral clock of 25MHz and baud rates of 115200 & 9600 with success. It does not calculate DLM which is generally 0 for bauds above 4800.

It is available at a freeware download site. http://sabercathost.com/6y6

I have a reputation to maintain and to that end the application I uploaded does not contain a virus.

HTH Mark

As requested by Drew I've appended the CMSIS function I used in my code below.

/*********************************************************************//**
 * @brief       Determines best dividers to get a target clock rate
 * @param[in]   UARTx   Pointer to selected UART peripheral, should be:
 *              - LPC_UART0: UART0 peripheral
 *              - LPC_UART1: UART1 peripheral
 *              - LPC_UART2: UART2 peripheral
 *              - LPC_UART3: UART3 peripheral
 * @param[in]   baudrate Desired UART baud rate.
 * @return      Error status, could be:
 *              - SUCCESS
 *              - ERROR
 **********************************************************************/
static Status uart_set_divisors(LPC_UART_TypeDef *UARTx, uint32_t baudrate)
{
Status errorStatus = ERROR;

uint32_t uClk;
uint32_t d, m, bestd, bestm, tmp;
uint64_t best_divisor, divisor;
uint32_t current_error, best_error;
uint32_t recalcbaud;

/* get UART block clock */
if (UARTx == LPC_UART0)
{
    uClk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_UART0);
}
else if (UARTx == (LPC_UART_TypeDef *)LPC_UART1)
{
    uClk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_UART1);
}
else if (UARTx == LPC_UART2)
{
    uClk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_UART2);
}
else if (UARTx == LPC_UART3)
{
    uClk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_UART3);
}


/* In the Uart IP block, baud rate is calculated using FDR and DLL-DLM registers
* The formula is :
* BaudRate= uClk * (mulFracDiv/(mulFracDiv+dividerAddFracDiv) / (16 * (DLL)
* It involves floating point calculations. That's the reason the formulae are adjusted with
* Multiply and divide method.*/
/* The value of mulFracDiv and dividerAddFracDiv should comply to the following expressions:
* 0 < mulFracDiv <= 15, 0 <= dividerAddFracDiv <= 15 */
best_error = 0xFFFFFFFF; /* Worst case */
bestd = 0;
bestm = 0;
best_divisor = 0;
for (m = 1 ; m <= 15 ;m++)
{
    for (d = 0 ; d < m ; d++)
    {
      divisor = ((uint64_t)uClk<<28)*m/(baudrate*(m+d));
      current_error = divisor & 0xFFFFFFFF;

      tmp = divisor>>32;

      /* Adjust error */
      if(current_error > ((uint32_t)1<<31)){
        current_error = -current_error;
        tmp++;
        }

      if(tmp<1 || tmp>65536) /* Out of range */
      continue;

      if( current_error < best_error){
        best_error = current_error;
        best_divisor = tmp;
        bestd = d;
        bestm = m;
        if(best_error == 0) break;
        }
    } /* end of inner for loop */

    if (best_error == 0)
      break;
} /* end of outer for loop  */

if(best_divisor == 0) return ERROR; /* can not find best match */

recalcbaud = (uClk>>4) * bestm/(best_divisor * (bestm + bestd));

/* reuse best_error to evaluate baud error*/
if(baudrate>recalcbaud) best_error = baudrate - recalcbaud;
else best_error = recalcbaud -baudrate;

best_error = best_error * 100 / baudrate;

if (best_error < UART_ACCEPTED_BAUDRATE_ERROR)
    {
        if (((LPC_UART1_TypeDef *)UARTx) == LPC_UART1)
        {
            ((LPC_UART1_TypeDef *)UARTx)->LCR |= UART_LCR_DLAB_EN;
            ((LPC_UART1_TypeDef *)UARTx)->/*DLIER.*/DLM = UART_LOAD_DLM(best_divisor);
            ((LPC_UART1_TypeDef *)UARTx)->/*RBTHDLR.*/DLL = UART_LOAD_DLL(best_divisor);
            /* Then reset DLAB bit */
            ((LPC_UART1_TypeDef *)UARTx)->LCR &= (~UART_LCR_DLAB_EN) & UART_LCR_BITMASK;
            ((LPC_UART1_TypeDef *)UARTx)->FDR = (UART_FDR_MULVAL(bestm) \
                    | UART_FDR_DIVADDVAL(bestd)) & UART_FDR_BITMASK;
        }
        else
        {
            UARTx->LCR |= UART_LCR_DLAB_EN;
            UARTx->/*DLIER.*/DLM = UART_LOAD_DLM(best_divisor);
            UARTx->/*RBTHDLR.*/DLL = UART_LOAD_DLL(best_divisor);
            /* Then reset DLAB bit */
            UARTx->LCR &= (~UART_LCR_DLAB_EN) & UART_LCR_BITMASK;
            UARTx->FDR = (UART_FDR_MULVAL(bestm) \
                    | UART_FDR_DIVADDVAL(bestd)) & UART_FDR_BITMASK;
        }
        errorStatus = SUCCESS;
    }

    return errorStatus;
}
Mark
  • 166
  • 1
  • 13
0

Suspect that clk for UART is further divided from the cclk. You need to check the datasheet and update accordingly.

fseto
  • 9,898
  • 1
  • 19
  • 14