This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF52 clock in and out

I have two related questions about nRF52 (nrf52832) which I have not been able to pinpoint from the documentation.

  1. Can the 32MHz crystal connection be replaced by a 32MHz input if there is an existing clock in the system that meets the precision, tolerance, etc? On many processors, one of the two crystal pins may be used as an input while the other is left open if there is a driven external source, but I see no information about this for nRF52.

  2. What is the most power-efficient way to get a high frequency clock output from the nRF52? Other ARM processors seem to offer direct "HFCLK" outputs to pins, but I can't find any related features in the nRF52 documentation. The best I've been able to guess is that the SPIM or PWM blocks could be used to provide a clock (8MHz or 16MHz respectively?) but each of those draws additional power and we don't (in this case) need the remaining functionality of the peripherals.

Thank you.

Parents
  • Hi,

    1. See Martin's answer.

    2. The peripherals only run at 16MHz, this is why it is not possible to get HFCLK output from the nRF52. Max output is 8MHz, since you need to toggle on and off a pin. This is achieved using GPIOTE and PPI with the following code which creates a 8MHz output on pin 18:

       int main(void)
       {
       nrf_gpio_range_cfg_output(18); //Configure pin 18 as output
      
       NRF_CLOCK->TASKS_HFCLKSTART = 1; //Start high frequency clock
       while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0)
       {
            //Wait for HFCLK to start
       }
       NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; //Clear event
       
       
       //Configure GPIOTE to toggle pin 18 
       NRF_GPIOTE->CONFIG[0] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos |
                               GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos |
                               18 << GPIOTE_CONFIG_PSEL_Pos | 
                               GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos;
       
       //Configure timer
       NRF_TIMER1->PRESCALER = 0;
       NRF_TIMER1->CC[0] = 1;  // Adjust the output frequency by adjusting the CC.
       NRF_TIMER1->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos;
       NRF_TIMER1->TASKS_START = 1;
       
       //Configure PPI
       NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
       NRF_PPI->CH[0].TEP = (uint32_t) &NRF_GPIOTE->TASKS_OUT[0];
       
       NRF_PPI->CHENSET = PPI_CHENSET_CH0_Enabled << PPI_CHENSET_CH0_Pos;
       
       while (true)
       {
           // do nothing
       }
       }
      

    Best regards,

    Øyvind

Reply
  • Hi,

    1. See Martin's answer.

    2. The peripherals only run at 16MHz, this is why it is not possible to get HFCLK output from the nRF52. Max output is 8MHz, since you need to toggle on and off a pin. This is achieved using GPIOTE and PPI with the following code which creates a 8MHz output on pin 18:

       int main(void)
       {
       nrf_gpio_range_cfg_output(18); //Configure pin 18 as output
      
       NRF_CLOCK->TASKS_HFCLKSTART = 1; //Start high frequency clock
       while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0)
       {
            //Wait for HFCLK to start
       }
       NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; //Clear event
       
       
       //Configure GPIOTE to toggle pin 18 
       NRF_GPIOTE->CONFIG[0] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos |
                               GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos |
                               18 << GPIOTE_CONFIG_PSEL_Pos | 
                               GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos;
       
       //Configure timer
       NRF_TIMER1->PRESCALER = 0;
       NRF_TIMER1->CC[0] = 1;  // Adjust the output frequency by adjusting the CC.
       NRF_TIMER1->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos;
       NRF_TIMER1->TASKS_START = 1;
       
       //Configure PPI
       NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
       NRF_PPI->CH[0].TEP = (uint32_t) &NRF_GPIOTE->TASKS_OUT[0];
       
       NRF_PPI->CHENSET = PPI_CHENSET_CH0_Enabled << PPI_CHENSET_CH0_Pos;
       
       while (true)
       {
           // do nothing
       }
       }
      

    Best regards,

    Øyvind

Children
  • This is excellent information - I believe that 8MHz would work well for our needs. Is there any simple way to estimate of the power impact of the GPIOTE solution listed above? I assume it's more complex that just CfV^2 for the I/O pin.

  • Well we have a pin capacitance of 4pF at each GPIO, the pulse moves at 8MHz and we know the relation X_C = 1/ ω C, this gives a equivalent resistance of approximately 5kΩ. Then you will have to add what you end up connecting to the pad. In the end the power consumed will be approximately equal to the power going through the equivalent resistor.

    Alternately you can set up this on your development kit and measure the difference in current draw with and without 8MHz output. Please refer to the current measurement guide for practical information.

  • The back-of-the-envelope is very helpful, since I don't have the tools to measure currents in the uA range at my immediate disposal. I was just wondering if the GPIOTE functions would contribute noticeable additional power draw - the data sheet only lists IGPIOTE,IN and doesn't give any info about frequency dependency (uA/MHz). If I assume a 1.8V supply and only consider the pad capacitance (data sheet says 3pF except for NFC, but I'll stick with your 4pF for consistency), your formula and the resulting 5kΩ gives about 360uA per pin. In contrast, the formula from the datasheet (pg. 151: IGPIO=VDD Cload f) gives about 58uA per pin. I'm not sure which of these is a better estimate, but they are rather different.

  • Hello,

    I am also interested in outputting a 4MHz clock from nRF52 (actually I am using nRF52832). I have a few comments question on the code that you have supplied:

    1. Concerning this piece of code: 
      nrf_gpio_range_cfg_output(18); //Configure pin 18 as output
      I am using nRF5_SDK_14.2.0_17b948a, and there seems that the nrf_gpio_range_cfg_output function takes 2 arguments (both ends of the range), so you need nrf_gpio_cfg_output driver function instead.
    2. Concerning this piece of code:
      NRF_CLOCK->TASKS_HFCLKSTART = 1; //Start high frequency clock
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {
          //Wait for HFCLK to start
      }
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; //Clear event
      I think it would be cleaner to use the driver functions for code futureproofness/portability, would the following make it:
        if(!nrf_drv_clock_init_check())
          {
            err_code = nrf_drv_clock_init();
            APP_ERROR_CHECK(err_code);
          }
      
    3. Same comment for the PPI configuration, wouldn't it be cleaner if we used some driver call for this piece of code 
      //Configure PPI
      NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
      NRF_PPI->CH[0].TEP = (uint32_t) &NRF_GPIOTE->TASKS_OUT[0];

      NRF_PPI->CHENSET = PPI_CHENSET_CH0_Enabled << PPI_CHENSET_CH0_Pos;
      like this:
          void my_clock_start_handler(nrf_drv_clock_evt_type_t event)
          {
            if(event == NRF_DRV_CLOCK_EVT_HFCLK_STARTED)
              NRF_TIMER1->TASKS_START = 1;
            else
              APP_ERROR_HANDLER(NRF_ERROR_FORBIDDEN);
          }
          
          nrf_drv_clock_handler_item_t my_clock_handler_item_s = {
            .p_next = NULL,
            .event_handler = &my_clock_start_handler
          };
          
          void my_clock_start(void)
          {
          
            nrf_drv_ppi_init();
            err_code = nrf_drv_ppi_channel_alloc	(&my_4MHz_clck_ppi_channel_s);
            APP_ERROR_CHECK(err_code);
            
            err_code = nrf_drv_ppi_channel_assign	(my_4MHz_clck_ppi_channel_s,
           					 (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0],
           					 (uint32_t) &NRF_GPIOTE->TASKS_OUT[0] );	  
            APP_ERROR_CHECK(err_code);
          
            err_code = nrf_drv_ppi_channel_enable(my_4MHz_clck_ppi_channel_s);
            APP_ERROR_CHECK(err_code);
          
            nrf_drv_clock_hfclk_request(&my_clock_handler_item_s);
          }
      
    4. Now, last but not least, I have one more question. I noticed that the code above does not work any longer after any call to NVIC_DisableIRQ(POWER_CLOCK_IRQn);
      For instance, when the SoftDevice is present, and the nrf_sdh_enable_request function from nrf-sdk/components/softdevice/common/nrf_sdh.c is called, then this function calls function sdh_state_observer_notify from the same file which in turn runs some state observers hooks, amongst which the sd_state_evt_handler from the nrf-sdk/components/drivers_nrf/clock/nrf_drv_clock.c file, which in turns calls NVIC_DisableIRQ(POWER_CLOCK_IRQn);.
      I would be quite greatfull to get some feedback why this happens…
  • Replying to myself.

    I just found why my code does not start the 4MHz code after the start of the BlueTooth. Because I was trying to write clean/futureproof code I used the nrf_drv_clock_hfclk_request function in order to make sure that the fast clock at 16MHz is running and stable before to start the timer.

    If instead of this I directly do the NRF_TIMER1->TASKS_START = 1; like in the original code, then the 4MHz clock is generated.

    So, my comment is that the way I start the BlueTooth does something wrong with the clock driver. Your help about this is welcome…

Related