/* 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;
} |