nRF54L15: How to explicitly enable and keep HFCLK running for TIMER00 in Zephyr?

Hi,

I am using an nRF54L15 and I would like to explicitly enable the HFCLK (32MHz). I want to use it to drive TIMER00, which will run during the whole application runtime. I have my custom board - also I tried to resolder the XTALL.

Ideally I would use the Zephyr clock API, but I also tried a more direct approach like this and I got always stuck on the while:

NRF_CLOCK->TASKS_XOSTART = 1;

while (NRF_CLOCK->EVENTS_XOSTARTED == 0);

In my device tree overlay I added:

&hfxo {
    status = "okay";
    clock-frequency = <DT_FREQ_M(32)>;
    load-capacitors = "internal";
    load-capacitance-femtofarad = <15000>;  // ~8 pF per pin
};

And in my prj.conf I have:

CONFIG_CLOCK_CONTROL=y
CONFIG_CLOCK_CONTROL_NRF=y

But it  does not work. Could you please advise what is the correct way to explicitly start and keep HFCLK running on nRF54L15 in Zephyr, preferably using the Zephyr API?

Maybe my HW design is not perfect?
Thank you.

Even AI solution like this could not help me: I get stuck in a loop.

void my_hfclk_start(void)
{
    struct onoff_manager *mgr =
        z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);

    __ASSERT_NO_MSG(mgr != NULL);

    sys_notify_init_spinwait(&hfclk_cli.notify);

    int ret = onoff_request(mgr, &hfclk_cli);
    __ASSERT_NO_MSG(ret >= 0);

    /* Wait for clock to be ready */
    int res;
    int err;
    do {
        err = sys_notify_fetch_result(&hfclk_cli.notify, &res);
    } while (err);
}
Parents
  • Hello,

    Can you verify is the issue is on the hardware by trying the following code?

    NRF_CLOCK->TASKS_XOSTART = 1;
    while (NRF_CLOCK->EVENTS_XOSTARTED == 0); /* stuck here = HW problem */
    while (NRF_CLOCK->EVENTS_XOTUNED == 0);   /* stuck here = tuning problem */

  • Hi,

    I changed the 32 MHz crystal (XTAL) and now my TIMER0 can run from the external clock. So the problem was obviously in the hardware. However, it works only when I turn on the HFXO via this function:

    C
    void my_hfclk_start(void)
    {
        struct onoff_manager *mgr =
            z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
    
        __ASSERT_NO_MSG(mgr != NULL);
    
        sys_notify_init_spinwait(&hfclk_cli.notify);
    
        int ret = onoff_request(mgr, &hfclk_cli);
        __ASSERT_NO_MSG(ret >= 0);
    
        /* Wait for clock to be ready */
        int res;
        int err;
        do {
            err = sys_notify_fetch_result(&hfclk_cli.notify, &res);
        } while (err);
    }

    I would like to know why I cannot turn on the external XOSC via this method:

    NRF_CLOCK->TASKS_XOSTART = 1;
    while (NRF_CLOCK->EVENTS_XOSTARTED == 0) { }
    while (NRF_CLOCK->EVENTS_XOTUNED   == 0) { }
    
    

    It all passes, but it still runs from the internal clock. Do you have any ideas?

    I also tried to add the internal capacitor manually like this:

    float intcap_f = (((15.0f - 5.5f) * (float)(xosc_slope + 791))
                      + (float)(xosc_offset * 4)) / 256.0f;
    NRF_OSCILLATORS->XOSC32M.CONFIG.INTCAP = (uint32_t)(int32_t)(intcap_f + 0.5f);


    Thank you very much.
Reply
  • Hi,

    I changed the 32 MHz crystal (XTAL) and now my TIMER0 can run from the external clock. So the problem was obviously in the hardware. However, it works only when I turn on the HFXO via this function:

    C
    void my_hfclk_start(void)
    {
        struct onoff_manager *mgr =
            z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
    
        __ASSERT_NO_MSG(mgr != NULL);
    
        sys_notify_init_spinwait(&hfclk_cli.notify);
    
        int ret = onoff_request(mgr, &hfclk_cli);
        __ASSERT_NO_MSG(ret >= 0);
    
        /* Wait for clock to be ready */
        int res;
        int err;
        do {
            err = sys_notify_fetch_result(&hfclk_cli.notify, &res);
        } while (err);
    }

    I would like to know why I cannot turn on the external XOSC via this method:

    NRF_CLOCK->TASKS_XOSTART = 1;
    while (NRF_CLOCK->EVENTS_XOSTARTED == 0) { }
    while (NRF_CLOCK->EVENTS_XOTUNED   == 0) { }
    
    

    It all passes, but it still runs from the internal clock. Do you have any ideas?

    I also tried to add the internal capacitor manually like this:

    float intcap_f = (((15.0f - 5.5f) * (float)(xosc_slope + 791))
                      + (float)(xosc_offset * 4)) / 256.0f;
    NRF_OSCILLATORS->XOSC32M.CONFIG.INTCAP = (uint32_t)(int32_t)(intcap_f + 0.5f);


    Thank you very much.
Children
  • Hello,

    It's good you have solved the issue.

    On the nRF54L15, after triggering the task TASKS_XOSTART, the crystal oscillator starts and the PLL automatically lock to it as reference.

    the following code cant lock to PLL

    NRF_CLOCK->TASKS_XOSTART = 1;
    while (NRF_CLOCK->EVENTS_XOSTARTED == 0) { }
    while (NRF_CLOCK->EVENTS_XOTUNED   == 0) { }

    You may try to try keeping PLL run exclusively by adding the following code

    NRF_CLOCK->TASKS_PLLSTART = 1;
    while (NRF_CLOCK->EVENTS_PLLSTARTED == 0)

    However, your first approach like changing hardware and the function my_hfclk_start() solves the issue. That seems a valid way to solve the issue.

    Thanks.

    BR
    Kazi

Related