cancel
Showing results for 
Search instead for 
Did you mean: 

F4 usart clock problem USART1 timing slightly off Workaround Found

lizerd
Associate III
Posted on August 10, 2013 at 21:57

 

 

The original post was too long to process during our migration. Please click on the attachment to read the original post.
6 REPLIES 6
lizerd
Associate III
Posted on August 11, 2013 at 10:47

From the original project i worked on, i used the USART1 when i found the problem.

the thing is that USART1 only has a small shift in timing, not as large as the USART3.

So my project has worked 10% of the time, and it took some time before a found the problem.

So a changed my

previously

example to switch to USART1 again, so i can show the problem.

I initialize the SystemInit(); at startup.

And the picture shows the USART1 TX output at 9600 baud setting in STM32

0690X00000602ieQAA.jpg

I added a timing reference picture for 9600 Baud, and here we can see that i hole frame should be 1024uS

0690X0000060Mm8QAE.gif

Next Step

I know that it has to do with the clock settings, so i put together a example with Timer3(APB1) and Timer10(APB2).

I made it so both should output PWM on one pin each so i could check so both APB1 and APB2 is running as thy should.

because USART1 is on the APB2 bus and USART3 is on APB1.

0690X00000602ifQAA.jpg

Both Tim3 and Tim10 is set to 530 KHz

I initialize the SystemInit(); at startup.

0690X00000602ijQAA.jpg

here we can see that TIM3 CH1 is the selected one, and the Frequency show 5375KHz.

And TIM10 CH1 is a bit of in phase, but we can see it has the same timing and frequency as TIM3 .

So the APB1 and APB2 seams to be running at the right frequency.

but i still get 1124uS frame timing for USART1 and 3255uSframe timing on USART3 when thy are set to 9600baud

my settings from the clock configuration that i am using

0690X00000602p5QAA.jpg

the Timer3 and 10 code

/**
* @brief Configure the TIM10 Ouput Channels.
* @param None
* @retval None
*/
void TIM_Config_TIM10(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint16_t CCR1_Val = 333;
uint16_t PrescalerValue = 0;
/* TIM10 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10, ENABLE);
/* GPIOF clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* GPIOF Configuration: TIM10 CH1 (PB8) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOB, &GPIO_InitStructure); 
/* Connect TIM pins to AF3 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_TIM10);
/* ---------------------------------------------------------------------------
TIM10 Configuration: generate 1 PWM signal:
In this example TIM10 input clock (TIM10CLK) is set to 2 * APB2 clock (PCLK2), 
since APB2 prescaler is different from 1. 
TIM10CLK = 2 * PCLK2 
PCLK2 = HCLK / 2 
=> TIM10CLK = HCLK = SystemCoreClock
To get TIM10 counter clock at 21 MHz, the prescaler is computed as follows:
Prescaler = (TIM10CLK / TIM10 counter clock) - 1
Prescaler = (SystemCoreClock /21 MHz) - 1
To get TIM10 output clock at 530 KHz, the period (TIM10_ARR) is computed as follows:
ARR = (TIM10 counter clock / TIM10 output clock) - 1
= 665
TIM10 Channel1 duty cycle = (TIM10_CCR1/ TIM10_ARR)* 100 = 50%
Note: 
SystemCoreClock variable holds HCLK frequency and is defined in system_stm32f4xx.c file.
Each time the core clock (HCLK) changes, user had to call SystemCoreClockUpdate()
function to update SystemCoreClock variable value. Otherwise, any configuration
based on this variable will be incorrect. 
--------------------------------------------------------------------------- */ 
CCR1_Val = 333;
/* Compute the prescaler value */
PrescalerValue = (uint16_t) (168000000 / 21000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 665;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM10, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM10, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM10, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM10, ENABLE);
/* TIM10 enable counter */
TIM_Cmd(TIM10, ENABLE);
}
/**
* @brief Configure the TIM10 Ouput Channels.
* @param None
* @retval None
*/
void TIM_Config_TIM3(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint16_t CCR1_Val = 333;
uint16_t PrescalerValue = 0;
uint32_t TIM3CLK = 0;
/* TIM3 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* GPIOC clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
/* GPIOF Configuration: TIM3 CH1 (PC6) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOC, &GPIO_InitStructure); 
/* Connect TIM pins to AF2 */
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM3);
/* ---------------------------------------------------------------------------
TIM3 Configuration: generate 1 PWM signal:
In this example TIM3 input clock (TIM3CLK) is set to 2 * APB1 clock (PCLK1), 
since APB1 prescaler is different from 1. 
TIM3CLK = 2 * PCLK1 
PCLK1 = HCLK / 4 
=> TIM3CLK = HCLK/2 = SystemCoreClock/2
To get TIM3 counter clock at 21 MHz, the prescaler is computed as follows:
Prescaler = (TIM3CLK / TIM3 counter clock) - 1
Prescaler = (TIM3CLK /21 MHz) - 1
To get TIM3 output clock at 530 KHz, the period (TIM3_ARR) is computed as follows:
ARR = (TIM3 counter clock / TIM3 output clock) - 1
= 665
TIM3 Channel1 duty cycle = (TIM3_CCR1/ TIM3_ARR)* 100 = 50%
Note: 
SystemCoreClock variable holds HCLK frequency and is defined in system_stm32f4xx.c file.
Each time the core clock (HCLK) changes, user had to call SystemCoreClockUpdate()
function to update SystemCoreClock variable value. Otherwise, any configuration
based on this variable will be incorrect. 
--------------------------------------------------------------------------- */ 
CCR1_Val = 333;
TIM3CLK = 84000000;
/* Compute the prescaler value */
PrescalerValue = (uint16_t) (TIM3CLK / 21000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 665;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
}

Any ideas ????? i hope because i going nuts 🙂

I have added the code so the hole thing can be check.

(see

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/DispForm.aspx?ID=33044&RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/F4%20usart%20clock%20problem

View Properties/Attachments)

________________

Attachments :

F4_UART_V2.rar : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I1Bw&d=%2Fa%2F0X0000000bkD%2FeqrwktMXL4cZBSeSE6duWntL.QuHrkNBImNYAFzfPbs&asPdf=false
lizerd
Associate III
Posted on August 11, 2013 at 12:05

Okay, found a workaround, but not why the ST code fails.

I started calculating the BRR by hand, and later found some code that i modified to a function that returns the BRR In the USART config, i added this call in the end, just before turning the USART on.

// Set real BRR 
USARTx->BRR = CalcUSART_BRR(9600, 42000000,0);

in this example i am using USART3, and the function i created wants (baud, APB clock, oversamplingEnabled) And now it works for both USART1 and USART3 with both oversampling ON and OFF

/* Disable the USART OverSampling by 8 */
USART_OverSampling8Cmd(USARTx, DISABLE); 
/* USARTx configuration ----------------------------------------------------*/
/* USARTx configured as follow:
- BaudRate = 9600 baud
- Word Length = 8 Bits
- one Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/ 
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USARTx, &USART_InitStructure);
/* NVIC configuration */
/* Configure the Priority Group to 2 bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USARTx_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Set real BRR
USARTx->BRR = CalcUSART_BRR(9600, 42000000,0);
/* Enable USART */
USART_Cmd(USARTx, ENABLE);
}
/**
* @brief Calculates the USART BRR.
* @param None
* @retval None
*/
uint16_t CalcUSART_BRR(uint32_t BAUD, uint32_t APBclock, uint8_t Sampling8Enable)
{
uint32_t tmpreg = 0x00;
uint32_t integerdivider = 0x00;
uint32_t fractionaldivider = 0x00;
// If USART_OverSampling8Cmd is Enable, Sampling8Enable should be 1(true)
// Else Sampling8Enable should be 0(false)
if(Sampling8Enable == 0)
{
APBclock = APBclock / 2;
}
integerdivider = ((25 * APBclock) / (2 * (BAUD))); 
tmpreg = (integerdivider / 100) << 
4
;
fractionaldivider
= 
integerdivider
- (100 * (tmpreg >> 4));
tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t)0x07);
return (uint16_t)tmpreg;
}

Posted on August 12, 2013 at 00:05

You sure the HSE_VALUE has been correctly defined?

You should also be able to route internal clocks via the MCOx pins if you need to verify them.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
lizerd
Associate III
Posted on August 12, 2013 at 09:25

Hi Clive.

Yes that was my first check.

I added 2 lines in main.c so i could check the HSI_VALUE

and added __NO_SYSTEM_INIT in preprocessor definition so i could check HSI_VALUE before the

SystemInit();

The debugger says it is 8MHz, but it's strange because the UART timefram time do not change if a change the HSI_VALUE to something else, like 1MHz

int main(void)

{

// Only used to check the osc reference values while debug so they are correct

uint32_t T1 = HSE_VALUE;

uint32_t T2 = HSI_VALUE;

/* System Clocks Configuration **********************************************/

SystemInit();

I tested to fetch the clock data

I debugged this code

int main(void)

{

// Only used to check the osc reference values while debug so they are correct

uint32_t T1 = HSE_VALUE;

uint32_t T2 = HSI_VALUE;

uint8_t dummy = 0;

RCC_ClocksTypeDef RCC_ClocksStatus;

/* Configure the USART Baud Rate */

RCC_GetClocksFreq(&RCC_ClocksStatus);

dummy = 1; <- I put the first breakpoint here 

/* System Clocks Configuration **********************************************/

SystemInit();

/* Configure the USART Baud Rate */

RCC_GetClocksFreq(&RCC_ClocksStatus);

dummy = 2; <- I put the second breakpoint here 

and i got

At first breakpoint (all in decimal)

0690X00000602pAQAQ.jpg

At second breakpoint (all in decimal)

0690X00000602mWQAQ.jpg

Thats weird, you can se that HSI_VALUE (T1) = 8MHz

but i don't think the clock is running at 525MHz 🙂

i tested to change the HSI_VALUE to 1MHz

result

0690X00000602pPQAQ.jpg

you can see that T1 = 1MHz, and the clockstatus has not changed at all

so i debugged the function

RCC_GetClocksFreq(&RCC_ClocksStatus);

and got

0690X00000602pFQAQ.jpg

I debugged the code line by line and got (HSI_VALUE@8MHz)

/* Get SYSCLK source -------------------------------------------------------*/

tmp = RCC->CFGR & RCC_CFGR_SWS;

= 8

pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;

=1

pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;

=8

i change this part of the code (added a temp) so i could see the register data

if (pllsource != 0)

{

/* HSE used as PLL clock source */

temp = ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);

pllvco = (HSE_VALUE / pllm) * temp;

}

temp is set to 336

pllvco 1050000000 <- this is strange, becuse pllm = 8, temp=336

then if i calculate the HSE_VALUE from this a get 25MHz, hug ???

hmm oki

I did not think this was possible, but while i debug check the HSE_VALUE in main.c

and it says 8MHz

and when it gos into the RCC_GetClocksFreq function in stm32f4xx_rcc.c

i check the HSE_VALUE again, and now it says 25MHz

the stm32f4xx.h is the only place a define the HSE_VALUE = 8MHz

and i can not find any other defines in my code

klauskvik
Associate II
Posted on August 12, 2013 at 13:02

I don't know if this will fix your problem, but we found an error in the CMSIS library.

The email send a while ago to ST:

We are using the CMSIS call USART_INIT to generate the settings for the USART.

However, at 9600 baud rate and a clock frequency of 7.37MHz (same apbcloc), the gene rated baudrate is measured to be 2% off. We have checke the USART_INIT function and it seems the calculation implemented is wrong.

/* Determine the integer part */

if ((USARTx->CR1 & USART_CR1_OVER8) != 0) {

/* Integer part computing in case Oversampling mode is 8 Samples */ integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct-

>USART_BaudRate)));

}

else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */ {

/* Integer part computing in case Oversampling mode is 16 Samples */ integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct-

>USART_BaudRate)));

}

tmpreg = (integerdivider / 100) << 4;

/* Determine the fractional part */

fractionaldivider = integerdivider - (100 * (tmpreg >> 4));

/* Implement the fractional part in the register */ if ((USARTx->CR1 & USART_CR1_OVER8) != 0) { tmpreg |= ((((fractionaldivider * 😎 + 50) / 100)) & ((uint8 _t)0x07); } else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */ { tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F); }

IntegerDivider and FractionalDivider is calculated correctly, but Integerdivider is not rounded off, but Fractional divider is (which leads to wrong value in BRR). So we get roll over of fractional divider without integerdivider beeing incremented

The solution was to correct the USART_INIT function:

/* Configure the USART Baud Rate -------------------------------------------*/

  RCC_GetClocksFreq(&RCC_ClocksStatus);

  if (USARTx == USART1)

  {

    apbclock = RCC_ClocksStatus.PCLK2_Frequency;

  }

  else

  {

    apbclock = RCC_ClocksStatus.PCLK1_Frequency;

  }

  /* Determine the integer part */

  if ((USARTx->CR1 & USART_CR1_OVER8) != 0)

  {

    /* Integer part computing in case Oversampling mode is 8 Samples */

    integerdivider = ((32 * apbclock) / (8 * (USART_InitStruct->USART_BaudRate)));    /* clock divider is multiplied by 32 to avoid rounding (one extra bit for rounding, with oversampling at 16) */

  }

  else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */

  {

    /* Integer part computing in case Oversampling mode is 16 Samples */

    integerdivider = ((32 * apbclock) / (16 * (USART_InitStruct->USART_BaudRate)));  /* clock divider is multiplied by 32 to avoid rounding (one extra bit for rounding, with oversampling at 16) */

  }

  tmpreg = ((integerdivider + 1) / 32) << 4; /* add one to perform correct rounding and divide by 32 to perform integer rounding. finally shift by 4 to correct bit possition */

  /* Determine the fractional part */

  /* again add one for rounding, reverse format calculation for tempReg and subtract to obtain fraction. */

  /* finally shift right by one bit to remove the excess bit which was added for rounding purposes */

  fractionaldivider = (integerdivider + 1 - (32 * (tmpreg >> 4))) >> 1;

  /* Implement the fractional part in the register */

  if ((USARTx->CR1 & USART_CR1_OVER8) != 0)

  {

    tmpreg |= (fractionaldivider) & ((uint8_t)0x07);

  }

  else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */

  {

    tmpreg |= (fractionaldivider) & ((uint8_t)0x0F);

  }

  /* Write to USART BRR */

  USARTx->BRR = (uint16_t)tmpreg;

lizerd
Associate III
Posted on August 13, 2013 at 08:55

Yeeh i found the problem,

I'am using Rowlys Crossworks as IDE. And it has it's own stm32f4xx.h file that is used instead of the one that i point to, but only for some files.

The question is, WHY do not the compiler complain when the it find a new HSE_VALUE definition.

hmm but now it works, anyhow yeeeh

if anyone wounder where the files are..

C:\Users\username\AppData\Local\Rowley Associates Limited\CrossWorks for ARM\packages\targets\STM32\include