[HAL][K20D50M] Serial baudrate improvements / clk_freqs.h fix

Three changes, first it fixes:
https://github.com/mbedmicro/mbed/issues/761 (which was reported
slightly wrong because K20 has again different clocking from KLXX for
uarts).

Second it adds mcgpllfll to clk_freqs, which again is different for K20
compared to KLXX .

Finally it adds the fractional baudrate divider for more accurate serial
baudrates.
pull/774/head
Sissors 2014-12-08 21:15:24 +01:00
parent 10156c5006
commit a6c9b1c40b
2 changed files with 45 additions and 23 deletions

View File

@ -89,6 +89,24 @@ static uint32_t extosc_frequency(void) {
return 0;
}
//Get MCG PLL/2 or FLL frequency, depending on which one is active, sets PLLFLLSEL bit
static uint32_t mcgpllfll_frequency(void) {
if ((MCG->C1 & MCG_C1_CLKS_MASK) != MCG_C1_CLKS(0)) //PLL/FLL is not selected
return 0;
uint32_t MCGClock = SystemCoreClock * (1u + ((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV1_MASK) >> SIM_CLKDIV1_OUTDIV1_SHIFT));
if ((MCG->C6 & MCG_C6_PLLS_MASK) == 0x0u) { //FLL is selected
SIM->SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK; //MCG peripheral clock is FLL output
return MCGClock;
} else { //PLL is selected
SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_MASK; //MCG peripheral clock is PLL output
return MCGClock;
}
//It is possible the SystemCoreClock isn't running on the PLL, and the PLL is still active
//for the peripherals, this is however an unlikely setup
}
#ifdef __cplusplus
}

View File

@ -16,13 +16,11 @@
#include "mbed_assert.h"
#include "serial_api.h"
// math.h required for floating point operations for baud rate calculation
#include <math.h>
#include <string.h>
#include "cmsis.h"
#include "pinmap.h"
#include "clk_freqs.h"
static const PinMap PinMap_UART_TX[] = {
{PTB17, UART_0, 3},
@ -60,17 +58,15 @@ void serial_init(serial_t *obj, PinName tx, PinName rx) {
obj->uart = (UART_Type *)uart;
// enable clk
switch (uart) {
case UART_0:
SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_MASK;
SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK;
SIM->SCGC4 |= SIM_SCGC4_UART0_MASK;
case UART_0:
mcgpllfll_frequency();
SIM->SCGC4 |= SIM_SCGC4_UART0_MASK;
break;
case UART_1:
SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK;
mcgpllfll_frequency();
SIM->SCGC4 |= SIM_SCGC4_UART1_MASK;
break;
case UART_2:
SIM->SCGC5 |= SIM_SCGC5_PORTD_MASK;
SIM->SCGC4 |= SIM_SCGC4_UART2_MASK;
break;
}
@ -98,8 +94,12 @@ void serial_init(serial_t *obj, PinName tx, PinName rx) {
pinmap_pinout(rx, PinMap_UART_RX);
// set rx/tx pins in PullUp mode
pin_mode(tx, PullUp);
pin_mode(rx, PullUp);
if (tx != NC) {
pin_mode(tx, PullUp);
}
if (rx != NC) {
pin_mode(rx, PullUp);
}
obj->uart->C2 |= (UART_C2_RE_MASK | UART_C2_TE_MASK);
@ -115,25 +115,29 @@ void serial_free(serial_t *obj) {
void serial_baud(serial_t *obj, int baudrate) {
// save C2 state
uint32_t c2_state = (obj->uart->C2 & (UART_C2_RE_MASK | UART_C2_TE_MASK));
uint8_t c2_state = (obj->uart->C2 & (UART_C2_RE_MASK | UART_C2_TE_MASK));
// Disable UART before changing registers
obj->uart->C2 &= ~(UART_C2_RE_MASK | UART_C2_TE_MASK);
uint32_t PCLK = (obj->uart == UART0) ? SystemCoreClock : SystemCoreClock/2;
// First we check to see if the basic divide with no DivAddVal/MulVal
// ratio gives us an integer result. If it does, we set DivAddVal = 0,
// MulVal = 1. Otherwise, we search the valid ratio value range to find
// the closest match. This could be more elegant, using search methods
// and/or lookup tables, but the brute force method is not that much
// slower, and is more maintainable.
uint32_t PCLK;
if (obj->uart != UART2) {
PCLK = mcgpllfll_frequency();
}
else {
PCLK = bus_frequency();
}
uint16_t DL = PCLK / (16 * baudrate);
uint32_t BRFA = (2 * PCLK) / baudrate - 32 * DL;
// set BDH and BDL
obj->uart->BDH = (obj->uart->BDH & ~(0x1f)) | ((DL >> 8) & 0x1f);
obj->uart->BDL = (obj->uart->BDL & ~(0xff)) | ((DL >> 0) & 0xff);
obj->uart->C4 &= ~0x1F;
obj->uart->C4 |= BRFA & 0x1F;
// restore C2 state
obj->uart->C2 |= c2_state;
}