MPSL Timer0 Not Firing More Than Once, Needed for MPSL_TIMESLOT_SIGNAL_ACTION_EXTEND and MPSL_TIMESLOT_SIGNAL_ACTION_REQUEST

Hi, I am using NCS v2 and the VSCode NCS extension writing in C++.

I am seeing Timer0 not fire more than once for me after the MPSL_TIMESLOT_SIGNAL_START event.

Ultimately, as an MPSL timeslot time nears its end, I want to try several different requests with MPSL, like extending the timeslot, and if that fails, to request a subsequent timeslot.

In my understanding, the only way to make requests to MPSL is to return an indicative value from an MPSL-issued callback.

I'm trying to trigger MPSL to call me (so that I can request things in the reply) by causing Timer0 to trip, thereby causing MPSL_TIMESLOT_SIGNAL_TIMER0, thereby giving me my chance to return my request.

Resources I've consulted

1) The Nordic documentation on MPSL timeslots (link

It indicates you can make requests to extend but does not indicate how.

I cannot find any answer to this question in the API documentation either.

No MPSL examples from Nordic that I can find show this either.

Hence my attempt was to trigger Timer0 events to get my chance.

2) This devzone article RE: Trigger radio signal in multiprotocol. and its (link) implementation showing the details.

"NRF_TIMER0->EVENTS_COMPARE[1] event ... When this even is triggered the softdevice will signal NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0"

If I'm understanding it right, it is saying that two different CC triggers are monitored by MPSL and somehow influence (how?) whether  the NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0 is called?

I have tried using the CC[1] and it does not cause Timer0 to fire again after the first use of CC[0].  Meaning I can't get Timer0 to fire a second time after using CC[0] the first time, regardless of whether I trigger it on CC[0] or CC[1].

I'm confused by this.

From the Nordic Timeslot page I linked above:

For these purposes, the application is granted access to the TIMER0 peripheral for the length of the timeslot. This timer is started from zero at the start of the timeslot and is configured to run at 1 MHz. The recommended practice is to set up a timer interrupt that expires before the timeslot expires, with enough time left for the timeslot to do any clean-up actions before the timeslot ends. Such a timer interrupt can also be used to request an extension of the timeslot, but there must still be enough time to clean up if the extension is not granted.

This technically could still be in line with the devzone response of #2, but it would have to be so vaguely interpreted to be compatible.

A simplistic reading of this would be "You get Timer0 to play with during the slot, do whatever you want."

From my experience, I'd also add to the meaning that "Whenever you trip Timer0 you get a callback via NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0."

As in, no special meanings associated with tripping Timer0 other than that you get a chance to wake up and talk to MPSL.

Question 1

My thought was that Timer0 interrupts always caused MPSL to fire the MPSL_TIMESLOT_SIGNAL_TIMER0 event, giving me a chance to do whatever I wanted.

Is that true?

Question 2

Are there multiple CC channels that need to be operated, differently from one another?

What is the specific meaning of each, and what is the specific way they are meant to be used?

Question 3

Can Timer0 be used multiple times within a given timeslot? 

Can I clear (NRF_TIMER0->TASKS_CLEAR), start (NRF_TIMER0->TASKS_START), and stop (NRF_TIMER0->TASKS_STOP), etc, at will, and expect MPSL_TIMESLOT_SIGNAL_TIMER0 to be fired?

How does that answer change depending on whether it's CC[0...3]?

Question 4

If none of the above answers this question, what is the right way to make multiple requests to MPSL during a timeslot?

Recall, my objective is to

- Start the timeslot

- Set a timer near the end of the slot

- Request an Extension

- If the extension fails, Request a subsequent slot

A Few Thoughts

The documentation for the interface to MPSL is very very sparse.

The API page an inventory, not an explanation.

The examples from Nordic I can find for MPSL are very minimal in their implementation in how they exercise the API (as in, they don't exercise all aspects of the API).

Nordic should put more narration around the API documentation, showing concrete specific details of how to work the API under common and expected use cases (eg extending multiple times, finally rejected, ultimately asking for next slot).  Others too I'm sure.

Thanks.

  • Hello Douglas,

    Just letting you know that I am looking into it, and will get back to you.

    Regards,

    Elfving

  • Hello Douglas, sorry about the delay

    what is the right way to make multiple requests to MPSL during a timeslot?

    First of all, have you had a look at the timeslot sample in our SDK? You say you have had a look at some example in our SDK, but I believe the timeslot sample could clear up a lot of the confusion as it demonstrates a lot of this functionality.

    And I believe the signal you are circling in red on the diagram is MPSL_TIMESLOT_SIGNAL_TIMER0.

    The documentation for the interface to MPSL is very very sparse.

    Maybe so. I guess what we have available is this page on MPSL along with its subpages, the sample on timeslot, and the info on the timers mentioned in the product specification. 

    I can understand that the documentation can seem limited. Is there a particular part that you find lacking?

    My thought was that Timer0 interrupts always caused MPSL to fire the MPSL_TIMESLOT_SIGNAL_TIMER0 event, giving me a chance to do whatever I wanted.

    Yes, I think it should always fire MPSL_TIMESLOT_SIGNAL_TIMER0. Though I am not sure if I understand what you mean by it would give you a chance to do whatever you wanted. While in the timeslot you can use the radio and extend the timeslot though.

    Are there multiple CC channels that need to be operated, differently from one another?

    Yes, there are several CC timer channels. The nRF52840 for instance has 5. Timer0 is used by the SDC, and is a bit different in that is used for various things related to that, like timeslots. You will for instance only have access to it while using an MPSL timeslot. Though the rest of the CC timer channels can be used freely.

    Can Timer0 be used multiple times within a given timeslot? 

    Within a single timeslot, yes. Though not multiple times while it is already in use. And not at all while not in a timeslot.

    Regards,

    Elfving

  • Hi,

    First of all, have you had a look at the timeslot sample in our SDK?

    Yes, it doesn't answer my question at all.

    it demonstrates a lot of this functionality

    The example does not attempt to extend the timeslot.  This is a major part of my question.  The example is not helpful for answering my question.

    I believe the signal you are circling in red on the diagram is MPSL_TIMESLOT_SIGNAL_TIMER0

    Ok can you confirm that with 100% certainty please?

    This entire post is attempting to get clarity around how to use a massively under-documented API.

    I think it should always fire MPSL_TIMESLOT_SIGNAL_TIMER0. Though I am not sure if I understand what you mean by it would give you a chance to do whatever you wanted. While in the timeslot you can use the radio and extend the timeslot though.

    Where can I see specific working code which attempts to first extend a timeslot, and if that fails, to request an additional timeslot?

    Your comment prefaced with "I think" makes me think you aren't sure of your answer.  I am asking for help because I have exhausted my attempts to learn the answer through trial-and-error.

    I have attempted to use TIMER0 the way you describe and it does not work.

    I need exact answers around how to do what I want.

    To recap, I want:

    • To start a timeslot
    • As the timeslot nears its end, I want to request an extension to the timeslot
    • If I am rejected for the extension, I want to request an additional timeslot

    Please tell me specifically how to do this.  I cannot find documentation or any explanation or example which describes how to do this.

    Can Timer0 be used multiple times within a given timeslot? 

    Within a single timeslot, yes

    I have explained to you that I cannot get TIMER0, CC[0] to fire more than one time within a timeslot.

    I also have not succeeded in getting TIMER0, CC[1] to fire subsequent to CC[0] having fired, within a timeslot.

    Are there multiple CC channels that need to be operated, differently from one another?

    Yes, there are several CC timer channels. The nRF52840 for instance has 5. Timer0 is used by the SDC, and is a bit different in that is used for various things related to that, like timeslots. You will for instance only have access to it while using an MPSL timeslot. Though the rest of the CC timer channels can be used freely.

    In my original post, I linked to a different DevZone post, which a Nordic employee pointed to (link) this code, and stated that there are two CC channels (0 and 1) which the SoftDevice uses for two different purposes, one to schedule an extension request, the other to end the timeslot.

    This code, written by Nordic, explicitly uses two different CCs for these operations.

            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_START:
    ...
                NRF_TIMER0->INTENSET            = TIMER_INTENSET_COMPARE0_Msk | TIMER_INTENSET_COMPARE1_Msk ;
                NRF_TIMER0->CC[0]               = TS_LEN_US - TS_SAFETY_MARGIN_US;
                NRF_TIMER0->CC[1]               = (TS_LEN_US - TS_EXTEND_MARGIN_US);
    ...
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
                if (NRF_TIMER0->EVENTS_COMPARE[0] &&
                    (NRF_TIMER0->INTENSET & (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENCLR_COMPARE0_Pos)))
                {
    ...
                    /* This is the "timeslot is about to end" timeout. */
    ...
                }
    ...
    
                if (NRF_TIMER0->EVENTS_COMPARE[1] &&
                    (NRF_TIMER0->INTENSET & (TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENCLR_COMPARE1_Pos)))
                {
    ...
                    /* This is the "Time to extend timeslot" timeout. */
    ...
                }
    ...
    

    After re-reading this code again, it appears that the code is triggering CC[1] to fire before CC[0], which I haven't tried before.  I will try this next.

    At the moment however, the code I'm linking to does not agree with what I'm being told as answers in this thread.  It shouldn't be this difficult to get straight answers about how to use this API.  DOCUMENT IT.

  • As an update, I changed my code to do as the example does.

    That is, I use CC[1] to fire and attempt to extend.  On extend failure, I do nothing and wait for CC[0].  On extend success, I increment both CC[0] and CC[1] by my extend timeout period, from where they are (as in, I don't reset the timer).

    On CC[0], I request the next slot, knowing that CC[1] failed to extend.

    Unlike my prior attempts, I was able to get CC[1] to fire multiple times within a timeslot, but only in the above scenario.

    If this is the expected (and seemingly mandated) behavior, it would be useful for Nordic to document this and confirm this post is accurate or make corrections to what I'm saying here.  I'm leaving this note for others who are trying to work through the MPSL system API details.

  • Hello Douglas! I am deeply sorry about the delay on this case

    douglas.malnati said:

    Ok can you confirm that with 100% certainty please?

    I've found out that it can be several things, which is why "<--->" has been used to describe it. When you know you want to extend the timeslot in advance, MPSL_TIMESLOT_SIGNAL_TIMER0 might typically be what you use, though you might also choose to request an extension after getting eg. MPSL_TIMESLOT_SIGNAL_RADIO or MPSL_TIMESLOT_SIGNAL_OVERSTAYED. The radio interrupt is an especially common/valid reason to do so.

    This is also why exact description of what CCs should be used where is not described, as it is up to you what you want to use.

    douglas.malnati said:

    Where can I see specific working code which attempts to first extend a timeslot, and if that fails, to request an additional timeslot?

    douglas.malnati said:

    To recap, I want:

    • To start a timeslot
    • As the timeslot nears its end, I want to request an extension to the timeslot
    • If I am rejected for the extension, I want to request an additional timeslot
    douglas.malnati said:

    I have explained to you that I cannot get TIMER0, CC[0] to fire more than one time within a timeslot.

    Here is a quick example(/a modification to the original one) I made showing how to get an extension, as well as firing multiple times. It uses just one CC as well.

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    /*
    MODIFIED SAMPLE TO INCLUDE EXTENSIONS ++
    */
    
    #include <zephyr/kernel.h>
    #include <zephyr/console/console.h>
    #include <string.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/sys/ring_buffer.h>
    #include <zephyr/types.h>
    #include <zephyr/irq.h>
    #include <zephyr/logging/log.h>
    
    #include <mpsl_timeslot.h>
    #include <mpsl.h>
    #include <hal/nrf_timer.h>
    #include <drivers/gpio.h> 
    
    LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
    
    #define TIMESLOT_REQUEST_DISTANCE_US (1000000)
    #define TIMESLOT_LENGTH_US           (200)
    #define EXTRA 							10
    #define TIMER_EXPIRY_US_EARLY (TIMESLOT_LENGTH_US- MPSL_TIMESLOT_EXTENSION_MARGIN_MIN_US - EXTRA)
    #define TIMER_EXPIRY_US (TIMESLOT_LENGTH_US - 50)
    #define TIMER_EXPIRY_US_MULTI 		 (50)
    
    
    #define MPSL_THREAD_PRIO             CONFIG_MPSL_THREAD_COOP_PRIO
    #define STACKSIZE                    CONFIG_MAIN_STACK_SIZE
    #define THREAD_PRIORITY              K_LOWEST_APPLICATION_THREAD_PRIO
    
    static bool request_in_cb = true;
    
    
    uint16_t number_of_times_extended = 0;
    
    #define LED0_NODE DT_ALIAS(led0)
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    
    static bool user_request_extension_fail = false; 
    static bool request_multi_fire = false;
    static bool request_extension = false; 
    
    static uint32_t extension_time_us = MPSL_TIMESLOT_EXTENSION_TIME_MIN_US*5000;
    static uint32_t invalid_extension_time_us = MPSL_TIMESLOT_EXTENSION_TIME_MIN_US*0;
    static uint8_t timeslot_number = 0;
    static uint8_t firing_number = 0;
    
    
    /* MPSL API calls that can be requested for the non-preemptible thread */
    enum mpsl_timeslot_call {
    	OPEN_SESSION,
    	MAKE_REQUEST,
    	CLOSE_SESSION,
    };
    
    /* Timeslot requests */
    static mpsl_timeslot_request_t timeslot_request_earliest = {
    	.request_type = MPSL_TIMESLOT_REQ_TYPE_EARLIEST,
    	.params.earliest.hfclk = MPSL_TIMESLOT_HFCLK_CFG_NO_GUARANTEE,
    	.params.earliest.priority = MPSL_TIMESLOT_PRIORITY_NORMAL,
    	.params.earliest.length_us = TIMESLOT_LENGTH_US,
    	.params.earliest.timeout_us = 1000000
    };
    static mpsl_timeslot_request_t timeslot_request_normal = {
    	.request_type = MPSL_TIMESLOT_REQ_TYPE_NORMAL,
    	.params.normal.hfclk = MPSL_TIMESLOT_HFCLK_CFG_NO_GUARANTEE,
    	.params.normal.priority = MPSL_TIMESLOT_PRIORITY_NORMAL,
    	.params.normal.distance_us = TIMESLOT_REQUEST_DISTANCE_US,
    	.params.normal.length_us = TIMESLOT_LENGTH_US
    };
    
    
    static mpsl_timeslot_signal_return_param_t signal_callback_return_param;
    
    /* Two ring buffers for printing the signal type with different priority from timeslot callback */
    RING_BUF_DECLARE(callback_high_priority_ring_buf, 10);
    RING_BUF_DECLARE(callback_low_priority_ring_buf, 10);
    
    
    /* Message queue for requesting MPSL API calls to non-preemptible thread */
    K_MSGQ_DEFINE(mpsl_api_msgq, sizeof(enum mpsl_timeslot_call), 10, 4);
    
    /* Not really implementented, but could display when in the timeslot using this */
    int display_timeslot(bool mode){
    	int ret=0;
    	if (!device_is_ready(led.port)) {
    		LOG_INF("led not ready\n");
    		return -1;
    	}
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		LOG_INF("led error \n");
    		return -1;
    	}
    	if (mode){
    		ret = gpio_pin_set_dt(&led,1);
    		if (ret < 0){
    			return -1;
    		}
    	}
    	return 0;
    }
    
    ISR_DIRECT_DECLARE(swi1_isr)
    {
    	uint8_t signal_type = 0;
    
    	while (!ring_buf_is_empty(&callback_high_priority_ring_buf)) {
    		if (ring_buf_get(&callback_high_priority_ring_buf, &signal_type, 1) == 1) {
    			switch (signal_type) {
    			case MPSL_TIMESLOT_SIGNAL_START:
    				LOG_INF("Callback: Timeslot start\n");
    				break;
    			case MPSL_TIMESLOT_SIGNAL_TIMER0:
    				LOG_INF("Callback: Timer0 signal\n");
    				break;
    			case MPSL_TIMESLOT_SIGNAL_EXTEND_FAILED: 
    				LOG_INF("Callback: Wops\n");
    				break;			
    			default:
    				LOG_INF("Callback: Other signal: %d\n", signal_type);
    				break;
    			}
    		}
    	}
    
    	while (!ring_buf_is_empty(&callback_low_priority_ring_buf)) {
    		if (ring_buf_get(&callback_low_priority_ring_buf, &signal_type, 1) == 1) {
    			switch (signal_type) {
    			case MPSL_TIMESLOT_SIGNAL_SESSION_IDLE:
    				LOG_INF("Callback: Session idle\n");
    				break;
    			case MPSL_TIMESLOT_SIGNAL_SESSION_CLOSED:
    				LOG_INF("Callback: Session closed\n");
    				break;
    			case MPSL_TIMESLOT_SIGNAL_EXTEND_FAILED: 
    				LOG_INF("Wops, extension fail in ISRbuffer 2\n");
    				break;
    			default:
    				LOG_INF("Callback: Other signal: %d\n", signal_type);
    				break;
    			}
    		}
    	}
    
    	return 1;
    }
    
    static mpsl_timeslot_signal_return_param_t *mpsl_timeslot_callback(
    	mpsl_timeslot_session_id_t session_id,
    	uint32_t signal_type)
    {
    	(void) session_id; /* unused parameter */
    	uint8_t input_data = (uint8_t)signal_type;
    	uint32_t input_data_len;
    
    	mpsl_timeslot_signal_return_param_t *p_ret_val = NULL;
    
    	switch (signal_type) {
    
    	case MPSL_TIMESLOT_SIGNAL_START:
    		/* No return action */
    		timeslot_number+=1;
    		signal_callback_return_param.callback_action =
    			MPSL_TIMESLOT_SIGNAL_ACTION_NONE;
    		p_ret_val = &signal_callback_return_param;
    
    		/* Setup timer to trigger an interrupt (and thus the TIMER0
    		 * signal) before timeslot end.
    		 */
    		if (request_in_cb && request_extension ) {
    			/*	Extension requested. CC set to expire within a smaller timeframe in order to request an expansion of the timeslot */
    			nrf_timer_bit_width_set(NRF_TIMER0, NRF_TIMER_BIT_WIDTH_32);
    			nrf_timer_cc_set(NRF_TIMER0, NRF_TIMER_CC_CHANNEL0, TIMER_EXPIRY_US_EARLY);
    			nrf_timer_int_enable(NRF_TIMER0, NRF_TIMER_INT_COMPARE0_MASK);
    		}
    		else if(request_multi_fire){
    			nrf_timer_bit_width_set(NRF_TIMER0, NRF_TIMER_BIT_WIDTH_32);
    			nrf_timer_cc_set(NRF_TIMER0, NRF_TIMER_CC_CHANNEL0, TIMER_EXPIRY_US_MULTI);
    			nrf_timer_int_enable(NRF_TIMER0, NRF_TIMER_INT_COMPARE0_MASK);
    
    		}
    		else {
    			/*Extension not requested. CC in place to tell that session needs to end or that new session should be requested */
    			nrf_timer_bit_width_set(NRF_TIMER0, NRF_TIMER_BIT_WIDTH_32);
    			nrf_timer_cc_set(NRF_TIMER0, NRF_TIMER_CC_CHANNEL0, TIMER_EXPIRY_US_EARLY);
    			nrf_timer_int_enable(NRF_TIMER0, NRF_TIMER_INT_COMPARE0_MASK);
    		}
    		input_data_len = ring_buf_put(&callback_high_priority_ring_buf, &input_data, 1);
    		if (input_data_len != 1) {
    			LOG_ERR("Full ring buffer, enqueue data with length %d", input_data_len);
    			k_oops();
    		}
    		/*Add led blink call to another function*/
    		display_timeslot(true);
    
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_TIMER0:
    		/* Clear event */
    		if (!request_multi_fire){
    			nrf_timer_int_disable(NRF_TIMER0, NRF_TIMER_INT_COMPARE0_MASK);
    			nrf_timer_event_clear(NRF_TIMER0, NRF_TIMER_EVENT_COMPARE0);
    		}
    
    		if (request_in_cb && !request_extension && !request_multi_fire) { 
    			/* Request new timeslot when callback returns. This should occur TIMER_EXPIRY_US after timeslot began*/
    
    			signal_callback_return_param.params.request.p_next = 
    				&timeslot_request_normal;
    			signal_callback_return_param.callback_action =
    				MPSL_TIMESLOT_SIGNAL_ACTION_REQUEST;
    			LOG_INF("Requesting normal slot..\n");
    
    		}
    		else if (request_in_cb && request_extension && !request_multi_fire) {
    			/* Request extension. This occurs TIMER_EXPIRY_US_EARLY after timeslot began  */
    			if (user_request_extension_fail && number_of_times_extended==10){ /* However in this case we want it to fail */
    				signal_callback_return_param.params.extend.length_us = 
    				invalid_extension_time_us;
    			}
    			else{
    				signal_callback_return_param.params.extend.length_us = 
    				extension_time_us; 
    			}
    
    			signal_callback_return_param.callback_action =
    				MPSL_TIMESLOT_SIGNAL_ACTION_EXTEND;
    
    		}
    		else if(request_multi_fire){
    			/* Set new timer */
    			nrf_timer_bit_width_set(NRF_TIMER0, NRF_TIMER_BIT_WIDTH_32);
    			nrf_timer_cc_set(NRF_TIMER0, NRF_TIMER_CC_CHANNEL0, TIMER_EXPIRY_US_MULTI);
    			nrf_timer_int_enable(NRF_TIMER0, NRF_TIMER_INT_COMPARE0_MASK);
    
    			LOG_INF("This is timeslot number: %d, and firing number: %d\n", timeslot_number, firing_number+=1);
    
    			/* Timeslot will be ended */
    			signal_callback_return_param.callback_action =
    				MPSL_TIMESLOT_SIGNAL_ACTION_NONE;
    		}
    
    		else {
    			/* Timeslot will be ended */
    			signal_callback_return_param.callback_action =
    				MPSL_TIMESLOT_SIGNAL_ACTION_END;
    			display_timeslot(false);
    			LOG_INF("Not requesting extension..\n");
    
    		}
    
    		p_ret_val = &signal_callback_return_param;
    		input_data_len = ring_buf_put(&callback_high_priority_ring_buf, &input_data, 1);
    		if (input_data_len != 1) {
    			LOG_ERR("Full ring buffer, enqueue data with length %d", input_data_len);
    			k_oops();
    		}
    		break;
    
    //added cases:
    	case MPSL_TIMESLOT_SIGNAL_EXTEND_SUCCEEDED:
    		signal_callback_return_param.callback_action =
    			MPSL_TIMESLOT_SIGNAL_ACTION_NONE;
    		/* Set next trigger time to be the current + Timer expiry early */
    		uint32_t current_cc = nrf_timer_cc_get(NRF_TIMER0, NRF_TIMER_CC_CHANNEL0);
    		uint32_t next_trigger_time = current_cc + extension_time_us;
    		nrf_timer_bit_width_set(NRF_TIMER0, NRF_TIMER_BIT_WIDTH_32);
    		nrf_timer_cc_set(NRF_TIMER0, NRF_TIMER_CC_CHANNEL0, next_trigger_time);
    		nrf_timer_int_enable(NRF_TIMER0, NRF_TIMER_INT_COMPARE0_MASK);
    
    		LOG_INF("Extend Successfull number: %d, CC: %d, next: %d\n", 
    													number_of_times_extended++, 
    													current_cc, 
    													next_trigger_time);
    
    		display_timeslot(true);
    		p_ret_val = &signal_callback_return_param;
    
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_EXTEND_FAILED:
    		LOG_INF("Extension failed!");
    		if (user_request_extension_fail){
    			signal_callback_return_param.params.request.p_next = 
    				&timeslot_request_earliest;
    			signal_callback_return_param.callback_action =
    				MPSL_TIMESLOT_SIGNAL_ACTION_REQUEST;
    			LOG_INF("..requesting normal slot..\n");
    			request_extension = false;
    		}
    		else{
    			signal_callback_return_param.callback_action =
    				MPSL_TIMESLOT_SIGNAL_ACTION_END;
    		
    		}
    
    		p_ret_val = &signal_callback_return_param;
    
    		display_timeslot(false);
    
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_RADIO:
    		LOG_INF("something failed!");
    		signal_callback_return_param.callback_action =
    			MPSL_TIMESLOT_SIGNAL_ACTION_NONE;
    		p_ret_val = &signal_callback_return_param;
    		display_timeslot(false);
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_OVERSTAYED:
    		LOG_INF("something overstayed!");
    		signal_callback_return_param.callback_action =
    			MPSL_TIMESLOT_SIGNAL_ACTION_END;
    		p_ret_val = &signal_callback_return_param;
    		display_timeslot(false);
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_CANCELLED:
    		LOG_INF("something cancelled!");
    		signal_callback_return_param.callback_action =
    			MPSL_TIMESLOT_SIGNAL_ACTION_NONE;
    		p_ret_val = &signal_callback_return_param;
    		display_timeslot(false);
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_BLOCKED:
    		LOG_INF("something blocked!");
    		signal_callback_return_param.callback_action =
    			MPSL_TIMESLOT_SIGNAL_ACTION_NONE;
    		p_ret_val = &signal_callback_return_param;
    		display_timeslot(false);
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_INVALID_RETURN:
    		LOG_INF("something gave invalid return\n");
    		signal_callback_return_param.callback_action =
    			MPSL_TIMESLOT_SIGNAL_ACTION_END;
    		p_ret_val = &signal_callback_return_param;
    		display_timeslot(false);
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_SESSION_IDLE:
    		LOG_INF("idle");
    
    		input_data_len = ring_buf_put(&callback_low_priority_ring_buf, &input_data, 1);
    		if (input_data_len != 1) {
    			LOG_ERR("Full ring buffer, enqueue data with length %d", input_data_len);
    			k_oops();
    		}
    		signal_callback_return_param.callback_action = 
    			MPSL_TIMESLOT_SIGNAL_ACTION_NONE;
    		p_ret_val = &signal_callback_return_param;
    		display_timeslot(false);
    		break;
    
    	case MPSL_TIMESLOT_SIGNAL_SESSION_CLOSED:
    		LOG_INF("Session closed");
    		input_data_len = ring_buf_put(&callback_low_priority_ring_buf, &input_data, 1);
    		if (input_data_len != 1) {
    			LOG_ERR("Full ring buffer, enqueue data with length %d", input_data_len);
    			k_oops();
    		p_ret_val = &signal_callback_return_param;
    
    		}
    		signal_callback_return_param.callback_action =
    			MPSL_TIMESLOT_SIGNAL_ACTION_NONE;
    		display_timeslot(false);
    		break;
    
    
    	default:
    		LOG_ERR("unexpected signal: %u", signal_type);
    		k_oops();
    		break;
    	}
    	
    #if defined(CONFIG_SOC_SERIES_NRF53X)
    	NVIC_SetPendingIRQ(SWI1_IRQn);
    	//NVIC_SetPendingIRQ(SWI2_IRQn); 
    
    #elif defined(CONFIG_SOC_SERIES_NRF52X)
    	NVIC_SetPendingIRQ(SWI1_EGU1_IRQn);
    	//NVIC_SetPendingIRQ(SWI2_EGU2_IRQn); 
    
    #endif
    	return p_ret_val;
    }
    
    static void mpsl_timeslot_demo(void)
    {
    	int err;
    	char input_char;
    	enum mpsl_timeslot_call api_call;
    
    	printk("-----------------------------------------------------\n");
    	printk("Press a key to open session and request timeslots:\n");
    	printk("* 'a' for a session where each timeslot makes a new request\n");
    	printk("* 'b' for a session with a single timeslot request\n");
    	printk("* 'c' for a session with continous extended timeslot request\n"); //added
    	printk("* 'd' for 10 extended timeslot requests, which then fails, and a new is requested\n"); //added
    	printk("* 'e' for a session with multiple firings of timer0, to demo its feasability\n"); //added
    
    	input_char = console_getchar();
    	printk("%c\n", input_char);
    
    	if (input_char == 'a') {
    		request_in_cb = true;
    		request_extension=false;
    	} else if (input_char == 'b') {
    		request_in_cb = false;
    		request_extension=false;
    	} else if (input_char == 'c') {
    		request_in_cb = true;
    		request_extension = true;
    	} else if (input_char == 'd') {
    		request_in_cb = true;
    		request_extension = true;
    		user_request_extension_fail = true;
    	} else if (input_char == 'e') {
    		request_multi_fire = true;
    		timeslot_number=0;
    	} else {
    		return;
    	}
    
    	api_call = OPEN_SESSION;
    	err = k_msgq_put(&mpsl_api_msgq, &api_call, K_FOREVER);
    	if (err) {
    		LOG_ERR("Message sent error: %d", err);
    		k_oops();
    	}
    
    	api_call = MAKE_REQUEST;
    	err = k_msgq_put(&mpsl_api_msgq, &api_call, K_FOREVER);
    	if (err) {
    		LOG_ERR("Message sent error: %d", err);
    		k_oops();
    	}
    
    	printk("Press any key to close the session.\n");
    	console_getchar();
    
    	api_call = CLOSE_SESSION;
    	err = k_msgq_put(&mpsl_api_msgq, &api_call, K_FOREVER);
    	if (err) {
    		LOG_ERR("Message sent error: %d", err);
    		k_oops();
    	}
    }
    
    /* To ensure thread safe operation, call all MPSL APIs from a non-preemptible
     * thread.
     */
    static void mpsl_nonpreemptible_thread(void)
    {
    	int err;
    	enum mpsl_timeslot_call api_call = 0;
    
    	/* Initialize to invalid session id */
    	mpsl_timeslot_session_id_t session_id = 0xFFu;
    
    	while (1) {
    		if (k_msgq_get(&mpsl_api_msgq, &api_call, K_FOREVER) == 0) {
    			switch (api_call) {
    			case OPEN_SESSION:
    				err = mpsl_timeslot_session_open(
    					mpsl_timeslot_callback,
    					&session_id);
    				if (err) {
    					LOG_ERR("Timeslot session open error: %d", err);
    					k_oops();
    				}
    				break;
    			case MAKE_REQUEST:
    				err = mpsl_timeslot_request(
    					session_id,
    					&timeslot_request_earliest);
    				if (err) {
    					LOG_ERR("Timeslot request error: %d", err);
    					k_oops();
    				}
    				break;
    			case CLOSE_SESSION:
    				err = mpsl_timeslot_session_close(session_id);
    				if (err) {
    					LOG_ERR("Timeslot session close error: %d", err);
    					k_oops();
    				}
    				break;
    			default:
    				LOG_ERR("Wrong timeslot API call");
    				k_oops();
    				break;
    			}
    		}
    	}
    }
    
    void main(void)
    {
    
    	int err = console_init();
    
    	if (err) {
    		LOG_ERR("Initialize console device error");
    		k_oops();
    	}
    
    	printk("-----------------------------------------------------\n");
    	printk("             Nordic MPSL Timeslot sample\n");
    
    #if defined(CONFIG_SOC_SERIES_NRF53X)
    	IRQ_DIRECT_CONNECT(SWI1_IRQn, 1, swi1_isr, 0);
    	irq_enable(SWI1_IRQn);
    
    	//IRQ_DIRECT_CONNECT(SWI2_IRQn, 1, swi2_isr, 0); 
    	//irq_enable(SWI2_IRQn);	 
    
    #elif defined(CONFIG_SOC_SERIES_NRF52X)
    	IRQ_DIRECT_CONNECT(SWI1_EGU1_IRQn, 1, swi1_isr, 0);
    	irq_enable(SWI1_EGU1_IRQn);
    
    	//IRQ_DIRECT_CONNECT(SWI2_EGU2_IRQn, 1, swi2_isr, 0); 
    	//irq_enable(SWI2_EGU2_IRQn); 
    #endif
    
    	while (1) {
    		mpsl_timeslot_demo();
    		k_sleep(K_MSEC(1000));
    	}
    }
    
    K_THREAD_DEFINE(mpsl_nonpreemptible_thread_id, STACKSIZE,
    		mpsl_nonpreemptible_thread, NULL, NULL, NULL,
    		K_PRIO_COOP(MPSL_THREAD_PRIO), 0, 0);
    

    douglas.malnati said:

    That is, I use CC[1] to fire and attempt to extend.  On extend failure, I do nothing and wait for CC[0].  On extend success, I increment both CC[0] and CC[1] by my extend timeout period, from where they are (as in, I don't reset the timer).

    That sounds like a good way of doing it, though you wouldn't have to use a second CC if you use the extend failure event.

    douglas.malnati said:
    If this is the expected (and seemingly mandated) behavior, it would be useful for Nordic to document this and confirm this post is accurate or make corrections to what I'm saying here.

    I agree that we should document it if any particular use of CCs where mandated, but I wouldn't say it is. Though I agree that the documentation could be improved. I'll let the team working on this know about your feedback.

    Regards,

    Elfving

Related