Hello
I have made the simplest code for UART1 for just sending a character through the UART1 (PTE0, PTE1) and receiving it back, Using TWR-K20D50M board. when I am sending any character I am receiving garbage values corresponding to each button press. for some characters I am getting exact values like for a, A, t, T but for others I am getting some other characters. I am using KEIL uvision4 MDK. I am posting the code below. I think the problem is of Baud rate but not very sure how to rectify it. I have changed the baud rate on both P&E virtual serial toolkit as well as in my code but it didn't worked.
/*****************************************************************************
* PROJECT : UART 1
* BOARD : TWR-K20D50M (Freescale)
* AUTHOR : Amit Kumar
* DATE : 12/09/2013
*****************************************************************************/
#include<MK20D5.H> // Header file
/*****************************************************************************
* Function to initialize Gate Clock
******************************************************************************/
void GateClock_Init(void)
{
SIM->SOPT5 |= SIM_SOPT5_UART1RXSRC(0); // UART Rx pin for receiving
SIM->SCGC5 |= SIM_SCGC5_PORTE_MASK; // Enabling Clock gate control to PORT E
PORTE->PCR[0] |= PORT_PCR_MUX(3); // Selecting the alternative 3 of port C i.e UART1_TX
PORTE->PCR[1] |= PORT_PCR_MUX(3); // Selecting the alternative 3 of port C i.e UART1_RX
}
/*****************************************************************************
* Function to initialize UART 1
******************************************************************************/
void UART1_init (int sysclk, int baud)
{
register uint16_t ubd, brfa;
uint8_t temp;
SIM->SCGC4 |= SIM_SCGC4_UART1_MASK; // Enabling Clock gate control to UART 1
/* Make sure that the transmitter and receiver are disabled while we
* change settings.
*/
UART1->C2 &= ~(UART_C2_TE_MASK | UART_C2_RE_MASK); // Disabling transmitter and receiver
/* Configure the UART for 8-bit mode, no parity */
UART1->C1 = 0; /* We need all default settings, so entire register is cleared */
/* Calculate baud settings */
ubd = (uint16_t)((sysclk*1000)/(baud * 16));
/* Save off the current value of the UARTx_BDH except for the SBR */
temp = UART1->BDH & ~(UART_BDH_SBR(0x1F));
UART1->BDH = temp | UART_BDH_SBR(((ubd & 0x1F00) >> 8));
UART1->BDL = (uint8_t)(ubd & UART_BDL_SBR_MASK);
/* Determine if a fractional divider is needed to get closer to the baud rate */
brfa = (((sysclk*32000)/(baud * 16)) - (ubd * 32));
/* Save off the current value of the UARTx_C4 register except for the BRFA */
temp = UART1->C4 & ~(UART_C4_BRFA(0x1F));
UART1->C4 = temp | UART_C4_BRFA(brfa);
/* Enable receiver and transmitter */
UART1->C2 |= (UART_C2_TE_MASK | UART_C2_RE_MASK );
}
/*****************************************************************************
* Function to Send character to UART 1
******************************************************************************/
void uart_putchar (char ch)
{
/* Wait until space is available in the FIFO */
while(!(UART1->S1 & UART_S1_TDRE_MASK));
/* Send the character */
UART1->D = (uint8_t)ch;
}
/*****************************************************************************
* Function to Check for any byte recieved from UART 1
******************************************************************************/
int uart_getchar_present ()
{
return (UART1->S1) & UART_S1_RDRF_MASK;
}
/*****************************************************************************
* Function to Recieve character from UART 1
******************************************************************************/
char uart_getchar ()
{
/* Wait until character has been received */
while (!(UART1->S1 & UART_S1_RDRF_MASK));
/* Return the 8-bit data from the receiver */
return UART1->D;
}
int main(void)
{
GateClock_Init();
UART1_init(50000, 38400); // System clock frequency, Baud rate
uart_putchar('A');
while(1)
{
uart_putchar(uart_getchar()); // Sending the recieved data
}
}
Hi Amit,
How did you configure the MCG? which mode is used in your application? FEI or PEE? Please also provide the related code snippet for a review.
Thanks for your patience!!
B.R
Kan
Hello
I have provided the full code. I just want to do the basic transmission of characters. I thought we don't need to configure the MCG in this case as we are using the system clock directly I took help from the reference manual present in the freescale website. check this link http://cache.freescale.com/files/32bit/doc/quick_ref_guide/KQRUG.pdf?fsrch=1&sr=1 I went through the instructions for UART.
Hi Amit,
I see, I will test your code on TWR-K20D50M, and will let you know when I have any progress.
Thanks for your patience!!
B.R
Kan
Hi Kan
I tried to configure the MCG. I added the below code in my clock init function of the posted code. Now I am not receiving anything in response to keys pressed. Earlier letter 'a' and 't' was coming fine and on pressing other keys I was receiving some different characters like when pressed 'q' I Received 'y' and for few more keys I received 'y'.
MCG->C1 |=( MCG_C1_CLKS(1) | MCG_C1_IRCLKEN_MASK ); | // Internal reference clock is selected, IRC is enabled |
MCG->C2 = MCG_C2_IRCS_MASK; | // Fast internal reference clock selected. |
MCG->SC = MCG_SC_FCRDIV(0); | // Divide Factor is 1 |
Hi Amit,
I looked into your code above, it should be ok, but I found you use this line as below to initialize the UART1:
UART1_init(50000, 38400); // System clock frequency, Baud rate
If you dont configure MCG out of reset, the default output is 23986176Hz, nearly 24MHz, not 50MHz, but you can not use 24000KHz to calculate the baudrate either, you should use 23986176Hz in the calculation, and for your latest test, seems you used the fast IRC as the system clock, but it is not trimmed precisely, which would cause the same problem. so it is recommended to configure MCG in PEE mode (PLL active in that case) to get the system clock that you needed.
Hope that helps,
B.R
Kan
Hi Kan
Can you provide me the simplest code for MCG configuration using PLL to generate 50 MHz (or any frequency) which I can append in the above code to make my UART working?
Thanks in advance.
Hi Amit,
Please kindly refer to the following for details. It is from the MCG driver for Kinetis 50MHz part.
/*********************************************************************************************/
/* Functon name : pll_init
*
* Mode transition: Option to move from FEI to PEE mode or to just initialize the PLL
*
* This function initializess either PLL0 or PLL1. Either OSC0 or OSC1 can be selected for the
* reference clock source. The oscillators can be configured to use a crystal or take in an
* external square wave clock.
* NOTE : This driver does not presently (as of Sept 9 2011) support the use of OSC1 as the
* reference clock for the MCGOUT clock used for the system clocks.
* The PLL outputs a PLLCLK and PLLCLK2X. PLLCLK2X is the actual PLL frequency and PLLCLK is
* half this frequency. PLLCLK is used for MCGOUT and is also typically used by the
* peripherals that can select the PLL as a clock source. So the PLL frequency generated will
* be twice the desired frequency.
* Using the function parameter names the PLL frequency is calculated as follows:
* PLL freq = ((crystal_val / prdiv_val) * vdiv_val)
* Refer to the readme file in the mcg driver directory for examples of pll_init configurations.
* All parameters must be provided, for example crystal_val must be provided even if the
* oscillator associated with that parameter is already initialized.
* The various passed parameters are checked to ensure they are within the allowed range. If any
* of these checks fail the driver will exit and return a fail/error code. An error code will
* also be returned if any error occurs during the PLL initialization sequence. Refer to the
* readme file in the mcg driver directory for a list of all these codes.
*
* Parameters: crystal_val - external clock frequency in Hz either from a crystal or square
* wave clock source
* hgo_val - selects whether low power or high gain mode is selected
* for the crystal oscillator. This has no meaning if an
* external clock is used.
* erefs_val - selects external clock (=0) or crystal osc (=1)
* prdiv_val - value to divide the external clock source by to create the desired
* PLL reference clock frequency
* vdiv_val - value to multiply the PLL reference clock frequency by
* mcgout_select - 0 if the PLL is just to be enabled, non-zero if the PLL is used
* to provide the MCGOUT clock for the system.
*
* Return value : PLL frequency (Hz) divided by 2 or error code
*/
int pll_init(int crystal_val, unsigned char hgo_val, unsigned char erefs_val, signed char prdiv_val, signed char vdiv_val, unsigned char mcgout_select)
{
unsigned char frdiv_val;
unsigned char temp_reg;
unsigned char prdiv, vdiv;
short i;
int ref_freq;
int pll_freq;
// check if in FEI mode
if (!((((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) == 0x0) && // check CLKS mux has selcted FLL output
(MCG_S & MCG_S_IREFST_MASK) && // check FLL ref is internal ref clk
(!(MCG_S & MCG_S_PLLST_MASK)))) // check PLLS mux has selected FLL
{
return 0x1; // return error code
}
// check external frequency is less than the maximum frequency
if (crystal_val > 50000000) {return 0x21;}
// check crystal frequency is within spec. if crystal osc is being used as PLL ref
if (erefs_val)
{
if ((crystal_val < 3000000) || (crystal_val > 32000000)) {return 0x22;} // return 1 if one of the available crystal options is not available
}
// make sure HGO will never be greater than 1. Could return an error instead if desired.
if (hgo_val > 0)
{
hgo_val = 1; // force hgo_val to 1 if > 0
}
// Check PLL divider settings are within spec.
if ((prdiv_val < 1) || (prdiv_val > 25)) {return 0x41;}
if ((vdiv_val < 24) || (vdiv_val > 55)) {return 0x42;}
// Check PLL reference clock frequency is within spec.
ref_freq = crystal_val / prdiv_val;
if ((ref_freq < 2000000) || (ref_freq > 4000000)) {return 0x43;}
// Check PLL output frequency is within spec.
pll_freq = (crystal_val / prdiv_val) * vdiv_val;
if ((pll_freq < 48000000) || (pll_freq > 100000000)) {return 0x45;}
// configure the MCG_C2 register
// the RANGE value is determined by the external frequency. Since the RANGE parameter affects the FRDIV divide value
// it still needs to be set correctly even if the oscillator is not being used
temp_reg = MCG_C2;
temp_reg &= ~(MCG_C2_RANGE_MASK | MCG_C2_HGO_MASK | MCG_C2_EREFS_MASK); // clear fields before writing new values
if (crystal_val <= 3000000)
{
temp_reg |= (MCG_C2_RANGE(1) | (hgo_val << MCG_C2_HGO_SHIFT) | (erefs_val << MCG_C2_EREFS_SHIFT));
}
else
{
temp_reg |= (MCG_C2_RANGE(2) | (hgo_val << MCG_C2_HGO_SHIFT) | (erefs_val << MCG_C2_EREFS_SHIFT));
}
MCG_C2 = temp_reg;
// determine FRDIV based on reference clock frequency
// since the external frequency has already been checked only the maximum frequency for each FRDIV value needs to be compared here.
if (crystal_val <= 1250000) {frdiv_val = 0;}
else if (crystal_val <= 2500000) {frdiv_val = 1;}
else if (crystal_val <= 5000000) {frdiv_val = 2;}
else if (crystal_val <= 10000000) {frdiv_val = 3;}
else if (crystal_val <= 20000000) {frdiv_val = 4;}
else {frdiv_val = 5;}
// Select external oscillator and Reference Divider and clear IREFS to start ext osc
// If IRCLK is required it must be enabled outside of this driver, existing state will be maintained
// CLKS=2, FRDIV=frdiv_val, IREFS=0, IRCLKEN=0, IREFSTEN=0
temp_reg = MCG_C1;
temp_reg &= ~(MCG_C1_CLKS_MASK | MCG_C1_FRDIV_MASK | MCG_C1_IREFS_MASK); // Clear values in these fields
temp_reg = MCG_C1_CLKS(2) | MCG_C1_FRDIV(frdiv_val); // Set the required CLKS and FRDIV values
MCG_C1 = temp_reg;
// if the external oscillator is used need to wait for OSCINIT to set
if (erefs_val)
{
for (i = 0 ; i < 10000 ; i++)
{
if (MCG_S & MCG_S_OSCINIT_MASK) break; // jump out early if OSCINIT sets before loop finishes
}
if (!(MCG_S & MCG_S_OSCINIT_MASK)) return 0x23; // check bit is really set and return with error if not set
}
/* THIS CHECK IS REMOVED DUE TO BUG WITH SLOW IRC IN REV. 1.0
// wait for Reference clock Status bit to clear
for (i = 0 ; i < 2000 ; i++)
{
if (!(MCG_S & MCG_S_IREFST_MASK)) break; // jump out early if IREFST clears before loop finishes
}
if (MCG_S & MCG_S_IREFST_MASK) return 0x11; // check bit is really clear and return with error if not set
*/
// Wait for clock status bits to show clock source is ext ref clk
for (i = 0 ; i < 2000 ; i++)
{
if (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) == 0x2) break; // jump out early if CLKST shows EXT CLK slected before loop finishes
}
if (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2) return 0x1A; // check EXT CLK is really selected and return with error if not
// Now in FBE
// It is recommended that the clock monitor is enabled when using an external clock as the clock source/reference.
// It is enabled here but can be removed if this is not required.
MCG_C6 |= MCG_C6_CME_MASK;
// Configure PLL
// Configure MCG_C5
// If the PLL is to run in STOP mode then the PLLSTEN bit needs to be OR'ed in here or in user code.
temp_reg = MCG_C5;
temp_reg &= ~MCG_C5_PRDIV_MASK;
temp_reg |= MCG_C5_PRDIV(prdiv_val - 1); //set PLL ref divider
MCG_C5 = temp_reg;
// Configure MCG_C6
// The PLLS bit is set to enable the PLL, MCGOUT still sourced from ext ref clk
// The loss of lock interrupt can be enabled by seperately OR'ing in the LOLIE bit in MCG_C6
temp_reg = MCG_C6; // store present C6 value
temp_reg &= ~MCG_C6_VDIV_MASK; // clear VDIV settings
temp_reg |= MCG_C6_PLLS_MASK | MCG_C6_VDIV(vdiv_val - 24); // write new VDIV and enable PLL
MCG_C6 = temp_reg; // update MCG_C6
// wait for PLLST status bit to set
for (i = 0 ; i < 2000 ; i++)
{
if (MCG_S & MCG_S_PLLST_MASK) break; // jump out early if PLLST sets before loop finishes
}
if (!(MCG_S & MCG_S_PLLST_MASK)) return 0x16; // check bit is really set and return with error if not set
// Wait for LOCK bit to set
for (i = 0 ; i < 2000 ; i++)
{
if (MCG_S & MCG_S_LOCK_MASK) break; // jump out early if LOCK sets before loop finishes
}
if (!(MCG_S & MCG_S_LOCK_MASK)) return 0x44; // check bit is really set and return with error if not set
// Use actual PLL settings to calculate PLL frequency
prdiv = ((MCG_C5 & MCG_C5_PRDIV_MASK) + 1);
vdiv = ((MCG_C6 & MCG_C6_VDIV_MASK) + 24);
// now in PBE
MCG_C1 &= ~MCG_C1_CLKS_MASK; // clear CLKS to switch CLKS mux to select PLL as MCG_OUT
// Wait for clock status bits to update
for (i = 0 ; i < 2000 ; i++)
{
if (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) == 0x3) break; // jump out early if CLKST = 3 before loop finishes
}
if (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3) return 0x1B; // check CLKST is set correctly and return with error if not
// Now in PEE
return ((crystal_val / prdiv) * vdiv); //MCGOUT equals PLL output frequency
} // pll_init
Hope that helps,
B.R
Kan