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

NRF_GZLL_MODE_SUSPEND nRF52 SDK 15

I would like to use Gazell along with BLE using the TimeSlot API. As part of this I need to set the Gazell mode to NRF_GZLL_MODE_SUSPEND while waiting for the next timeslot.

I've adapted the examples from the nRF51, and it looks like I have one problem to solve before it will work.

The problem is that whenever I use 

nrf_gzll_set_mode(NRF_GZLL_MODE_SUSPEND);
The call does not return error, but the mode does not change to suspend mode, and always remains 0 (device mode).
Also I never get a nrf_gzll_mode_changed() callback when trying to change the mode.
Gazell is enabled at the time I try to change the mode.
Is there something else I need to do to be able to change Gazell mode?
SDK 15.0.0, SD S132, nRF52832
Thanks,
Brian
  • Hi,

     

    Also I never get a nrf_gzll_mode_changed() callback when trying to change the mode.

     If you are running directly from the timeslot callback, you're in interrupt handler context, highest priority. You are then essentially blocking that function to be called until you return.

     

    Is there something else I need to do to be able to change Gazell mode?

    Could you try this?

    nrf_gzll_set_mode(NRF_GZLL_MODE_SUSPEND);
    while(nrf_gzll_get_mode() != NRF_GZLL_MODE_SUSPEND);
    current_mode = nrf_gzll_get_mode();
     

    But; do not run it like this directly in the timeslot handler.

    You should rather just call nrf_gzll_set_mode(), then let the timeslot handler return, so that its not blocking in interrupt priority 0. At this point, the nrf_gzll_mode_changed() should also have been triggered.

     

    Kind regards,

    Håkon

  • Yes, I've tried setting the mode and letting the handler return. I still do not see a call to nrf_gzll_mode_changed() and the mode is not changed. I have set all of the Gazell-related defines similar to Gazell examples. I am not using the App Scheduler, is it required?

  • As an update, I am only getting one NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0 signal from the SD. After the first compare is reached I clear EVENTS_COMPARE[0] and restart the timer but the signal never triggers again. If I do not clear EVENTS_COMPARE[0] I can see that CC[0] is increasing as expected. In the attached file, the state ends up at NRF_RADIO_SESSION_IDLE. It may be a required #define, or am I missing something else? So far I am defining these additional flags (same ASMFLAGS as well) for Gazell+BLE:

    CFLAGS += -DGAZELL_ALTERNATIVE_RESOURCES
    CFLAGS += -DGAZELL_PRESENT
    CFLAGS += -DSWI_DISABLE0
    CFLAGS += -DUSE_SD_HW_RESOURCES
    Also I am using 
    LIB_FILES += \
    $(SDK_ROOT)/components/proprietary_rf/gzll/gcc/gzll_nrf52_sd_resources_gcc.a \
     
    /* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved.
     *
     * The information contained herein is property of Nordic Semiconductor ASA.
     * Terms and conditions of usage are described in detail in NORDIC
     * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
     *
     * Licensees are granted free, non-transferable use of the information. NO
     * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
     * the file.
     *
     */
     
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "nrf_assert.h"
    #include "nrf_soc.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_sdh.h"
    #include "nrf_gzll.h"
    #include "nrf_gzll_error.h"
    #include "nrf_delay.h"
    #include "app_error.h"
    #include "gzll_timeslots.h"
    #include "app_util_platform.h"
    #include "persistent_stats.h"
    
    #define NRF_LOG_MODULE_NAME gzll_ts
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    NRF_LOG_MODULE_REGISTER();
    
    
    /*lint -e526 "Symbol RADIO_IRQHandler not defined" */
    // this is implmented in the gazell library
    void RADIO_IRQHandler(void);
    
    #define PIPE_TO_HOST            0
    #define PAYLOAD_LENGTH          32
    #define MAX_TX_ATTEMPTS         100         /**< Maximum number of Transmit attempts. 0 = infinite */
    #define DUMMY_PACKET            0x80        /**< Initial payload.*/
    #define HSC_GZLL_BASE_ADDRESS   0xE7E7E7E7  // bkbk NRF_GZLL_DEFAULT_BASE_ADDRESS_0 
    #define TS_SAFETY_MARGIN_US     1000        // timer expires by this amount before timeslot expires
    
    static nrf_radio_request_t      m_timeslot_request;
    static uint32_t                 m_slot_length_us;
    
    static volatile bool            m_cmd_received = false;
    static volatile bool            m_gzll_initialized = false;
    static volatile nrf_gzll_mode_t m_gzll_mode;
    
    static uint8_t                  m_gzll_packet[PAYLOAD_LENGTH];
    static nrf_radio_signal_callback_return_param_t signal_callback_return_param;
    
    #if 0 // GZLL_TX_STATISTICS
    static nrf_gzll_tx_statistics_t m_statistics = { 0, 0, {0}, {0} };   /**< Struct containing transmission statistics. */
    #endif
    
    void nrf_evt_signal_handler(uint32_t evt_id, void * p_context);
    
    NRF_SDH_SOC_OBSERVER(m_time_slot_soc_observer, 0, nrf_evt_signal_handler, NULL);
    
    /**@brief Request next timeslot event in earliest configuration
     */
    uint32_t request_next_event_earliest(void)
    {
        m_slot_length_us                               = 15000;
        m_timeslot_request.request_type                = NRF_RADIO_REQ_TYPE_EARLIEST;
        m_timeslot_request.params.earliest.hfclk       = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
        m_timeslot_request.params.earliest.priority    = NRF_RADIO_PRIORITY_NORMAL;
        m_timeslot_request.params.earliest.length_us   = m_slot_length_us;
        m_timeslot_request.params.earliest.timeout_us  = 1000000;
        return sd_radio_request(&m_timeslot_request);
    }
    
    void configure_next_event_earliest(void)
    {
        m_slot_length_us                               = 25000;
        m_timeslot_request.request_type                = NRF_RADIO_REQ_TYPE_EARLIEST;
        m_timeslot_request.params.earliest.hfclk       = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
        m_timeslot_request.params.earliest.priority    = NRF_RADIO_PRIORITY_NORMAL;
        m_timeslot_request.params.earliest.length_us   = m_slot_length_us;
        m_timeslot_request.params.earliest.timeout_us  = 1000000;
    }
    
    /**@brief Configure next timeslot event in normal configuration
     */
    void configure_next_event_normal(void)
    {
        m_slot_length_us                              = 15000;
        m_timeslot_request.request_type               = NRF_RADIO_REQ_TYPE_NORMAL;
        m_timeslot_request.params.normal.hfclk        = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED;
        m_timeslot_request.params.normal.priority     = NRF_RADIO_PRIORITY_HIGH;
        m_timeslot_request.params.normal.distance_us  = 100000;
        m_timeslot_request.params.normal.length_us    = m_slot_length_us;
    }
    
    void nrf_evt_signal_handler(uint32_t evt_id, void * p_context)
    {
        uint32_t err_code;
        
        switch (evt_id)
        {
            case NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN:
                NRF_LOG_INFO("radio cb invalid return!");
                //No implementation needed
                break;
            
            case NRF_EVT_RADIO_SESSION_IDLE:
                NRF_LOG_INFO("gzll idle!");
    //            err_code = request_next_event_earliest();
    //            APP_ERROR_CHECK(err_code);
                break;
            
            case NRF_EVT_RADIO_SESSION_CLOSED:
                NRF_LOG_INFO("radio session closed");
                //No implementation needed
                break;
            
            case NRF_EVT_RADIO_BLOCKED:
                NRF_LOG_INFO("radio blocked!");
                err_code = request_next_event_earliest();
                APP_ERROR_CHECK(err_code);
                break;
    
            case NRF_EVT_RADIO_CANCELED:
                NRF_LOG_INFO("radio cancelled");
                err_code = request_next_event_earliest();
                APP_ERROR_CHECK(err_code);
                break;
    
            default:
                break;
        }
    }
    
    /**
     * Start of the timeslot 
     *   initialize Gazell if necessary
     *   set up timer interrupt which should fire with enough time left to 
     *     request the next slot, otherwise the state will go to idle
     */
    static void m_on_start(void)
    {
        bool res;
        signal_callback_return_param.params.request.p_next = NULL;
        signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
        
        if (!m_gzll_initialized)
        {
            res = nrf_gzll_init(NRF_GZLL_MODE_DEVICE);
            ASSERT(res);
            res = nrf_gzll_set_device_channel_selection_policy(NRF_GZLL_DEVICE_CHANNEL_SELECTION_POLICY_USE_CURRENT);
            ASSERT(res);
            res = nrf_gzll_set_xosc_ctl(NRF_GZLL_XOSC_CTL_MANUAL);
            ASSERT(res);
            nrf_gzll_set_max_tx_attempts(MAX_TX_ATTEMPTS);
            res = nrf_gzll_set_base_address_0(HSC_GZLL_BASE_ADDRESS);
            ASSERT(res);
            res = nrf_gzll_enable();
            ASSERT(res);
            m_gzll_initialized = true;
        }
        else
        {
            res = nrf_gzll_set_mode(NRF_GZLL_MODE_DEVICE);
            ASSERT(res);
        }
    
        // (re)start the timer to interrupt us before the timeslot expires
        NRF_TIMER0->CC[0] = m_slot_length_us - TS_SAFETY_MARGIN_US;
        NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
        NVIC_EnableIRQ(TIMER0_IRQn);
    }
    
    // callback from SD when mode has changed
    void nrf_gzll_mode_changed(void)
    {
        NRF_LOG_INFO("gzll mode change!");
        m_gzll_mode = nrf_gzll_get_mode();
    }
    
    static void m_on_multitimer(void)
    {
        NRF_TIMER0->EVENTS_COMPARE[0] = 0;
        if (nrf_gzll_get_mode() != NRF_GZLL_MODE_SUSPEND)
        {
            signal_callback_return_param.params.request.p_next = NULL;
            signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
            (void)nrf_gzll_set_mode(NRF_GZLL_MODE_SUSPEND);
            NRF_TIMER0->CC[0] = m_slot_length_us - TS_SAFETY_MARGIN_US;
            NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
    #ifdef WATCH_TIMER0_COUNT
    //        NRF_TIMER0->TASKS_CAPTURE[0] = 1;
    //        NRF_LOG_DEBUG("cnt: %d", NRF_TIMER0->CC[0]);
    //        nrf_delay_ms(1);
    #endif
        }
        else
        {
            // so far, this always has no effect. the mode is always the same mode set in nrf_gazell_init()
            ASSERT(nrf_gzll_get_mode() == NRF_GZLL_MODE_SUSPEND);
            configure_next_event_normal();
            signal_callback_return_param.params.request.p_next = &m_timeslot_request;
            signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
        }
    }
    
    
    // This is the only context that can access TIMER0 (i.e. TIMER0)
    nrf_radio_signal_callback_return_param_t * m_radio_callback(uint8_t signal_type)
    {
        switch(signal_type)
        {
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_START:
                m_on_start();
                break;
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO:
                NRF_LOG_INFO("radio callback!!");
                signal_callback_return_param.params.request.p_next = NULL;
                signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
                RADIO_IRQHandler();
                break;
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
                m_on_multitimer();
                break;
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
                //No implementation needed
                NRF_LOG_INFO("extend succeeded");
                break;
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
                NRF_LOG_INFO("extend failed");
                //No implementation needed
     /*
                //Try scheduling a new timeslot
                configure_next_event_earliest();
                signal_callback_return_param.params.request.p_next = &m_timeslot_request;
                signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
                break;
    */
            default:
                //No implementation needed
                NRF_LOG_INFO("unhandled signal_type");
                break;
    
         }
        return (&signal_callback_return_param);
    }
    
    uint32_t timeslot_sd_init(void)
    {
        uint32_t err_code;
        err_code = sd_radio_session_open(m_radio_callback);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        err_code = request_next_event_earliest();
        if (err_code != NRF_SUCCESS)
        {
            (void)sd_radio_session_close();
            return err_code;
        }
        (void)nrf_gzll_set_mode(NRF_GZLL_MODE_SUSPEND);
        return NRF_SUCCESS;
    }
    
    
    void nrf_gzll_device_tx_success(uint32_t pipe, nrf_gzll_device_tx_info_t tx_info)
    {
        uint32_t ack_payload_length = PAYLOAD_LENGTH;
        NRF_LOG_INFO("tx success");
        if (tx_info.payload_received_in_ack)
        {      
            if (nrf_gzll_fetch_packet_from_rx_fifo(pipe, m_gzll_packet, &ack_payload_length))
            {
                NRF_LOG_INFO("payload rx in ACK");
                 m_cmd_received = true;
            }
        }
    }
    
    void nrf_gzll_device_tx_failed(uint32_t pipe, nrf_gzll_device_tx_info_t tx_info)
    {
        NRF_LOG_ERROR("tx failed");
    }
    
    void nrf_gzll_host_rx_data_ready(uint32_t pipe, nrf_gzll_host_rx_info_t rx_info)
    {
        NRF_LOG_INFO("rx data rdy");
    }
    
    void nrf_gzll_disabled(void)
    {
        NRF_LOG_INFO("gzll disabled");
    }
    
    bool debug_cmd_available(void)
    {
        return m_cmd_received;
    }
    
    char get_debug_cmd(void)
    {
        char cmd = m_gzll_packet[0];
        m_cmd_received = false;
        return cmd;
    }
    
    void update_gzll_stats(bool clear_after_read)
    {
    #if 0 // GZLL_TX_STATISTICS
        CRITICAL_REGION_ENTER();
    
        // Print all transmission statistics.
        NRF_LOG_RAW_INFO("\r\n");
        NRF_LOG_INFO("Total transmitted packets:   %4u",  m_statistics.packets_num);
        NRF_LOG_INFO("Total transmission time-outs: %03u\r\n", m_statistics.timeouts_num);
    
        for (uint8_t i = 0; i < nrf_gzll_get_channel_table_size(); i++)
        {
            NRF_LOG_INFO("Channel %u: %03u packets transmitted, %03u transmissions failed.",
                            i,
                            m_statistics.channel_packets[i],
                            m_statistics.channel_timeouts[i]);
        }
    
        CRITICAL_REGION_EXIT();
    
        g_stats.gzll_num_tx_success = m_statistics.packets_num;
        g_stats.gzll_num_tx_fail = m_statistics.timeouts_num;
    
        if (clear_after_read)
        {
            // Reset statistics buffers.
            nrf_gzll_reset_tx_statistics();
        }
    #endif
    
        return;
    }
  • Could you try to use the other gazell library, ie: not the "sd_resources" tagged one and see if this one behaves similar? The library you are using now, uses the same peripherals as the SoftDevice, among them, timer0, which is used in the timeslot handling.

     

    Kind regards,

    Håkon

  • Hi Hakon,

    If I understand correctly, the sd_resources library is the version that gives us access to TIMER0 (and PPI, etc.) If I use the other one, I get a SD hardfault when accessng timer0. It looks like the library is specifically set up to give us this high-priority timer0 to use for timing of requesting new radio slot requests.

    I'm able to get TIMER0 interrupts, set the CC value, etc. I've tried using all of the CC registers but still not able to change Gazell mode which is necessary for BLE concurrence. So, it looks like my main problem is:

    ** I'm unable to change the gazell mode using nrf_gzll_set_mode (NRF_GZLL_MODE_SUSPEND) at any time. I have tried many ways to do it in and out of interrupt context, asynchronously waiting for long intervals, etc. It never changes to anything and stays the same as the mode specified during init.

    Since I don't have access to the library code, I can't see if there is some other #define I need to set or unset, so this info would be very helpful. Or if I am missing something else can you point me in a better direction?

    Thanks much in advance,

    Brian

Related