ARM LPC1768 UART0 configuration, wrong baud rate

michael picture michael · Sep 11, 2010 · Viewed 7.2k times · Source

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.

Answer

Turbo J picture Turbo J · Sep 11, 2010

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.