/* Determines and sets best dividers to get a target baud rate */ uint32_t Chip_UART_SetBaudFDR(LPC_USART_T *pUART, uint32_t baudrate) { uint32_t uClk; uint32_t actualRate = 0, d, m, bestd, bestm, tmp; uint32_t current_error, best_error; uint64_t best_divisor, divisor; uint32_t recalcbaud; /* Get Clock rate */ #if defined(CHIP_LPC175X_6X) uClk = Chip_Clock_GetPeripheralClockRate(Chip_UART_GetClkIndex(pUART)); #else uClk = Chip_Clock_GetPeripheralClockRate(); #endif /* 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++) { /* The result here is a fixed point number. The integer portion is in the upper 32 bits. * The fractional portion is in the lower 32 bits. */ divisor = ((uint64_t) uClk << 28) * m / (baudrate * (m + d)); /* The fractional portion is the error. */ current_error = divisor & 0xFFFFFFFF; /* Snag the integer portion of the divisor. */ tmp = divisor >> 32; /* If closer to the next divisor... */ if (current_error > ((uint32_t) 1 << 31)) { /* Increment to the next divisor... */ tmp++; /* Now the error is the distance to the next divisor... */ current_error = -current_error; } /* Can't use a divisor that's less than 1 or more than 65535. */ if ((tmp < 1) || (tmp > 65535)) { /* Out of range */ continue; } /* Also, if fractional divider is enabled can't use a divisor that is less than 3. */ if ((d != 0) && (tmp < 3)) { /* Out of range */ continue; } /* Do we have a new best? */ if (current_error < best_error) { best_error = current_error; best_divisor = tmp; bestd = d; bestm = m; /* If error is 0, that's perfect. We're done. */ if (best_error == 0) { break; } } } /* for (d) */ /* If error is 0, that's perfect. We're done. */ if (best_error == 0) { break; } } /* for (m) */ if (best_divisor == 0) { /* can not find best match */ return 0; } 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; /* Update UART registers */ Chip_UART_EnableDivisorAccess(pUART); Chip_UART_SetDivisorLatches(pUART, UART_LOAD_DLL(best_divisor), UART_LOAD_DLM(best_divisor)); Chip_UART_DisableDivisorAccess(pUART); /* Set best fractional divider */ pUART->FDR = (UART_FDR_MULVAL(bestm) | UART_FDR_DIVADDVAL(bestd)); /* Return actual baud rate */ actualRate = recalcbaud; return actualRate; } |