Search code examples
armembeddedstm32cortex-m

STM32L152 UART baudrate halfes using HSE


I'm trying to configure the baudrate of USART1 on an STM32L152. When using the external Clock the baudrate is half of what I configured (e.g. 57600 instead of 115200). However, when using the internal HSI everything is correct. The internal is 16 MHz and the external is an 8 MHz crystal that is used to drive the PLL for a 32 MHz system clock.

This is the RCC init code, which is pretty much standard I guess.

int RCC_Configuration(void)
{
  /* DISABLE HSI and target clocks prior to clock config */
  RCC_HSICmd(DISABLE);
  RCC_PLLCmd(DISABLE);
  RCC_HSEConfig(RCC_HSE_OFF);

  /* Set HSE as sys clock*/
  RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);

  /* Enable ADC & SYSCFG clocks */
  RCC_APB2Periph_SYSCFG , ENABLE);

  /* Allow access to the RTC */
  PWR_RTCAccessCmd(ENABLE);

  /* Reset RTC Backup Domain */
  RCC_RTCResetCmd(ENABLE);
  RCC_RTCResetCmd(DISABLE);

  /* LSI used as RTC source clock */
  /* The RTC Clock may varies due to LSI frequency dispersion. */
  /* Enable the LSI OSC */
  RCC_LSICmd(ENABLE);

  /* Wait until LSE is ready */
  while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);

  /* Select the RTC Clock Source */
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);

  /* Enable the RTC */
  RCC_RTCCLKCmd(ENABLE);

  /* Wait for RTC APB registers synchronisation */
  RTC_WaitForSynchro();

  // ENABLE HSE
  RCC_HSEConfig(RCC_HSE_ON);
  ErrorStatus HSEStartUpStatus = RCC_WaitForHSEStartUp();

  if(HSEStartUpStatus == SUCCESS)
  {
      /* 32Mhz = 8Mhz * 12 / 3 */
      RCC_PLLConfig(RCC_PLLSource_HSE, RCC_PLLMul_12, RCC_PLLDiv_3);

      /* Enable PLL */
      RCC_PLLCmd(ENABLE);

      /* Wait till PLL is ready */
      while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
      {
      }

      /* Select PLL as system clock source */
      RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

      /* Wait till PLL is used as system clock source */
      while(RCC_GetSYSCLKSource() != 0x0C)  // 0x0C = PLL
      {
      }

      /* Enable the PWR clock */
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
      PWR->CR = PWR_CR_VOS_0;                /* Select the Voltage Range 1 (1.8V) */
      while((PWR->CSR & PWR_CSR_VOSF) != 0); /* Wait for Voltage Regulator Ready  */

      /* HCLK = SYSCLK */
      RCC_HCLKConfig(RCC_SYSCLK_Div1);

      /* PCLK1 = HCLK/2 */
      RCC_PCLK1Config(RCC_HCLK_Div2);

      /* PCLK2 = HCLK */
      RCC_PCLK2Config(RCC_HCLK_Div1);

      /* Enable the GPIOs clocks */
      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC| RCC_AHBPeriph_GPIOD| RCC_AHBPeriph_GPIOE| RCC_AHBPeriph_GPIOH, ENABLE);
      /* Enable comparator, LCD and PWR mngt clocks */
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_COMP | RCC_APB1Periph_LCD | RCC_APB1Periph_PWR,ENABLE);
  }

  return 0;

}

I'm using STDperiph to configure the UART1, which on this mcu will run on PCLK2. Checked all the init methods and the register contents. The mantissa and fraction of the baudrate register are correctly calculated and should yield the correct baud rate no matter what the PCLK value is.

This is the UART init code:

void usartinit(void)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

USART_InitStructure.USART_BaudRate = 115200 ;
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;

/* Enable GPIO clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

GPIO_PinAFConfig(USARTx_GPIO, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(USARTx_GPIO, GPIO_PinSource10, GPIO_AF_USART1);

/* Configure USART Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin = USARTx_TX;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(USARTx_GPIO, &GPIO_InitStructure);

/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = USARTx_RX;
GPIO_Init(USARTx_GPIO, &GPIO_InitStructure);

/* USART configuration */
USART_Init(USART1, &USART_InitStructure);

/* Enable USART */
USART_Cmd(USART1, ENABLE);
}

The only possibility I can think of right now is that the crystal is just 4 MHz, but this is a Nucleo board and the MCO from the attached STLink is used for HSE, which definitely is 8 MHz.

What's the blatant mistake I'm making?


Solution

  • Ok, finally figured it out. On the Nucleo boards in the default configuration, the HSE clock is connected to the MCO clock output of the STLink Programmer on the board. On multiple of my boards however this clock signal gets distorted so much that the target uC only sees 4 MHz. If I output the HSE on the MCO of the target it produces a 4 MHz square wave with a weird duty cycle of 75%. When probing the MCO input signal with a scope the probe capacitance is sufficent to produce the correct 8 MHz input.

    So, I guess don't trust your eval boards... Will now get a few crystals and populate the "real" external clock on those boards.