How to Generate Clock Output (MCO1/MCO2) on STM32H573

In embedded systems, sharing clock signals externally is often essential for synchronizing peripherals or other microcontrollers. Most of the popular Microcontrollers often need to share their internal clock signals with external components.

The STM32H573 supports this via Microcontroller Clock Outputs (MCO) on two GPIO pins: PA8 (MCO1) and PC9 (MCO2). Whether you’re debugging clock issues or need to drive another IC like an FPGA or sensor hub, MCO is a practical solution.

In this blog post, we will explore how to configure MCO1 and MCO2 on the STM32H573 both using HAL drivers and bare-metal (register-level) code. You’ll also learn how to select clock sources, configure GPIOs, and verify output with an oscilloscope.

📘 Table of Contents:

  1. What is MCO in STM32?
  2. MCO1 and MCO2 Sources in STM32H573.
  3. Configuration Using STM32CubeMX and HAL.
  4. Register-Level Configuration (Bare-Metal).
  5. How to Measure the Clock Output:
  6. Tips and Best Practices.

🔧 What is MCO in STM32?

MCO stands for Microcontroller Clock Output. It allows selected internal clocks (like HSE, HSI, PLL, SYSCLK, etc.) to be routed to specific GPIO pins. STM32H573 provides two MCO outputs:

  • MCO1: Available on PA8
  • MCO2: Available on PC9

Each MCO has configurable:

  • Clock source.
  • Prescaler (to divide the frequency)

MCO Sources:

1. MCO1 (PA8) Sources:
  • HSI clock selected (hsi_ck) (default after reset)
  • LSE oscillator clock selected (lse_ck)
  • HSE clock selected (hse_ck)
  • PLL1 clock selected (pll1_q_ck)
  • HSI48 clock selected (hsi48_ck)
2. MCO2 (PC9) Sources:
  • system clock selected (sys_ck) (default after reset)
  • PLL2 oscillator clock selected (pll2_p_ck)
  • HSE clock selected (hse_ck)
  • PLL1 clock selected (pll1_p_ck)
  • CSI clock selected (csi_ck)
  • LSI clock selected (lsi_ck)

Both MCO1 and MCO2 support prescaler values from 0 to 15, where 0000 disables the clock output (default after reset). To enable MCO output, the prescaler must be set to a value between 0001 (division by 1) and 1111 (division by 15).

 

🛠️ HAL-Based Configuration:

 

// Configure GPIO (PA8 as MCO1)
__HAL_RCC_GPIOA_CLK_ENABLE();

//Set GPIO Pin PA8 to support MCO
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF0_MCO;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

// Set MCO1 source and prescaler
HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);

 

🛠️Bare-Metal Configuration (Register-Level):

In this section, I explain how to configure MCO1 or MCO2 without HAL/CubeMX — using only registers.

✅ 1. Enable GPIO Port Clock:

 

RCC->AHB2ENR|= RCC_AHB2ENR_GPIOAEN; // For MCO1 (PA8)

 

✅ 2. Configure PA8 as Alternate Function:

 

//alternate function
const uint8_t pin = 8;
const uint8_t af  = 0;  // AF0 = MCO

const uint8_t afr_index = pin >> 3;          // 8 / 8 = 1 → AFR[1]
const uint8_t afr_pos   = (pin & 0x07) * 4;   // (8 % 8) * 4 = 0

GPIOA->AFR[afr_index] &= ~(0xFUL << afr_pos);  // Clear AF bits
GPIOA->AFR[afr_index] |=  (af    << afr_pos);  // Set AF0 (MCO)

// 2. Configure PA8 as Alternate Function (AF0 = MCO)
GPIOA->MODER &= ~(0x3U << (pin * 2));      // Clear mode
GPIOA->MODER |=  (0x2U << (pin * 2));      // Set to Alternate Function mode (10)

GPIOA->OTYPER &= ~(1U << pin);            // Push-pull
GPIOA->OSPEEDR |= (0x3U << (pin * 2));    // Very High speed
GPIOA->PUPDR &= ~(0x3U << (pin * 2));     // No pull-up/pull-down

 

✅ 3. Enable Clock Source (e.g., HSE):

 

RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY)); // Wait for HSE ready

 

✅ 4. Select MCO1 Source and Prescaler:

 

// Set RCC_CFGR MCO1[1:0] = 01 (HSE)
// Set RCC_CFGR MCO1PRE[2:0] = 000 (/1)
RCC->CFGR &= ~(RCC_CFGR_MCO1 | RCC_CFGR_MCO1PRE); // Clear
RCC->CFGR |= (1 << RCC_CFGR_MCO1_Pos);            // HSE

 

🧪 How to Measure the Clock Output:

  • Connect an oscilloscope probe to PA8 (MCO1) or PC9 (MCO2).
  • Set the time base to visualize MHz-range signals.
  • Confirm voltage levels (typically 3.3V logic).
  • You should see a clean square wave at the configured frequency.

 

✅ Tips and Warnings

  • Always verify if the selected clock source (e.g., HSE, PLL1) is enabled and ready.
  • Avoid driving too many loads with MCO — use a buffer or clock driver IC if needed.
  • MCO signals are not jitter-free like a crystal oscillator, not suitable for sensitive RF circuits.