Understanding the Clock Subsytem

2025-02-19

As mentioned, the vendor provides a repo of examples for their various chips. You can study them to use the various peripherals. So we won't repeat them here. But we'll discuss one crucial component - the clock.

One peculiarity of these Si-Labs chips (at-least the efr32Xg21 family) is they take a fixed 38.4MHz crystal for the external source. This simplifies a lot of the clock setup configs.

Here is a basic diagram with the most used components.

clock circuit

Note that the chip boots on FSRCO then switches to HFRCODPLL before executing user firmware.

Setting up for external crystal#

Our ZYZBP008 module comes with an external crystal attached (according to the datasheet, it is mandatory to source from the external clock to enable the radio unit). Here is a code excerpt from the example repo on how to configure for the external source:


#include "em_cmu.h"
#include "em_chip.h"

int main(void)
{
  CHIP_Init();

  // Start the HFXO with safe default parameters
  CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_DEFAULT;
  CMU_HFXOInit(&hfxoInit);
  CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
  //                            enable^     ^wait until clock succeeds before returning

  // Switch the SYSCLK to the HFXO.
  CMU_ClockSelectSet(cmuClock_SYSCLK, cmuSelect_HFXO);
  
  while(1);
  
  return 0;
}

Lookup for these functions and their possible options in the clock component documentation. CMU_HFXOINIT_DEFAULT is defined in em_cmu.h and apparently suitable in most usual cases.

To verify that it works, we will test the UART peripheral (which is time sensitive).

#include "em_cmu.h"
#include "em_chip.h"
#include "em_gpio.h"
#include "em_usart.h"

#define BSP_BCC_TXPORT  gpioPortA // A05 - TX
#define BSP_BCC_TXPIN   5 //
#define BSP_BCC_RXPORT  gpioPortA // A06 - RX
#define BSP_BCC_RXPIN   6 //

int main (void)
{
  CHIP_Init();

  // Start the HFXO with safe default parameters
  CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_DEFAULT;
  CMU_HFXOInit(&hfxoInit);
  CMU_OscillatorEnable(cmuOsc_HFXO, true, true);

  // Switch the SYSCLK to the HFXO.
  CMU_ClockSelectSet(cmuClock_SYSCLK, cmuSelect_HFXO);
  
  CMU_ClockEnable(cmuClock_GPIO, true);
  GPIO_PinModeSet(gpioPortA, 0/*pin 4*/, gpioModePushPull /*push-pull output*/, 1/*output level*/);

  CMU_ClockEnable(cmuClock_USART0, true);
  
  USART_InitAsync_TypeDef initAsync = USART_INITASYNC_DEFAULT;
  initAsync.baudrate = 115200;
  
  GPIO->USARTROUTE[0].TXROUTE = (BSP_BCC_TXPORT << _GPIO_USART_TXROUTE_PORT_SHIFT)
            | (BSP_BCC_TXPIN << _GPIO_USART_TXROUTE_PIN_SHIFT);
  GPIO->USARTROUTE[0].RXROUTE = (BSP_BCC_RXPORT << _GPIO_USART_RXROUTE_PORT_SHIFT)
            | (BSP_BCC_RXPIN << _GPIO_USART_RXROUTE_PIN_SHIFT);
  
  GPIO->USARTROUTE[0].ROUTEEN = GPIO_USART_ROUTEEN_RXPEN | GPIO_USART_ROUTEEN_TXPEN;
  USART_InitAsync(USART0, &initAsync);

  GPIO_PinModeSet(BSP_BCC_TXPORT, BSP_BCC_TXPIN, gpioModePushPull, 1);
  GPIO_PinModeSet(BSP_BCC_RXPORT, BSP_BCC_RXPIN, gpioModeInput, 0);
  
  //USART_Tx (USART_TypeDef * usart, uint8_t data)
  
  char buf[] = "Hello";
  
  while(1) {
    GPIO_PinOutToggle(gpioPortA, 0/*pin 4*/);
    
    for (int i = 0; i < 6; i++) {
      USART_Tx (USART0, buf[i]);
      USART_Tx (USART0, '\n');
    }
    
    for (volatile uint32_t i = 0; i < 100000; i++) { } // busy delay
  }
  
  return 0;
}

The above code should print Hello via serial repeatedly with the roughly specified pauses.