ARM LPC1768 UART0 configuration, wrong baud rate
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 */
PINCO开发者_JAVA技巧N->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.
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.
Suspect that clk for UART is further divided from the cclk. You need to check the datasheet and update accordingly.
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;
}
精彩评论