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
Parents Reply
  • 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 \
     
    8424.gzll_timeslots.c
    /* 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;
    }
Children
Related