SPARK Wireless Core
When integrating the SPARK Wireless Core into a project using custom hardware, users need to consider two main actions:
Implement the backend: This involves creating the necessary backend interfaces for the SPARK Wireless Core to interact effectively with the custom hardware.
Provide a context switch mechanism within their application: This mechanism allows for efficient switching between different operational contexts within the application and the Wireless Core, facilitating resource management.
Implement the backend
Users need to create their own backend to successfully port the Wireless Core onto custom hardware. This requires implementing the function prototypes found in core/wireless/swc_hal_facade.h. In addition to this guide, the API’s Doxygen documentation provides guidance on how to implement the interface correctly.
The facade functions are organized into groups described below.
Context Switching and Interrupt Management
Functions related to managing context switches and setting interrupt callbacks, crucial for multitasking and efficient response to system events.
/** @brief Manually triggers the radio #1's IRQ pin interrupt.
*
* This function is specifically designed to manually activate the IRQ pin interrupt for radio #1,
* prompting the system to handle an interrupt event as if the radio hardware had issued it. The
* subsequent interrupt handling routine invokes an internal SWC library function tailored to
* process this event.
*
* @note Unlike generic context switch mechanisms, this function does not require the user to set an
* application-level callback for the interrupt. However, users must implement a separate mechanism
* to configure a callback for the radio #1 IRQ to ensure proper handling of the triggered event
* within the SWC library's context.
*/
void swc_hal_radio_1_context_switch(void);
/** @brief Sets the callback function for radio #1's IRQ interrupt.
*
* This function registers a internal SWC library function callback to be invoked when the IRQ pin
* interrupt for radio #1 is triggered.
*
* @param[in] callback A pointer to the callback function that will be executed upon
* an interrupt from radio #1's IRQ pin.
*/
void swc_hal_set_radio_1_irq_callback(void (*callback)(void));
/** @brief Sets the callback function for the DMA receive (RX) interrupt for radio #1.
*
* This function allows for the registration of a internal SWC library function callback
* to be invoked when the DMA RX operation for radio #1 completes,
*
* @param[in] callback A pointer to the callback function that will be executed upon the
* completion of a DMA RX operation for radio #1.
*/
void swc_hal_set_radio_1_dma_rx_callback(void (*callback)(void));
/** @brief Disables the IRQ external interrupt for radio #1.
*
* This function deactivates the external interrupt request (IRQ) for radio #1, preventing
* the interrupt handler from being invoked in response to radio #1's IRQ events.
*/
void swc_hal_radio_1_disable_irq_it(void);
/** @brief Enables the IRQ external interrupt for radio #1.
*
* This function enables the external interrupt request (IRQ) for radio #1, allowing
* the system to respond to IRQ signals from the radio.
*/
void swc_hal_radio_1_enable_irq_it(void);
/** @brief Disables the DMA SPI interrupt for radio #1.
*
* This function deactivates the interrupt request (IRQ) associated with the DMA SPI
* operation for radio #1, preventing DMA related interrupt handling.
*/
void swc_hal_radio_1_disable_dma_irq_it(void);
/** @brief Enables the DMA SPI interrupt for radio #1.
*
* This function activates the interrupt request (IRQ) for the DMA SPI operation for radio #1,
* allowing DMA related interrupt handling.
*/
void swc_hal_radio_1_enable_dma_irq_it(void);
SPI Communication
SPI bus communication functions for data exchange between the MCU and ASIC radios, underpinning wireless communication functionality.
/** @brief Set the on-board controller SPI chip-select pin of the radio #1.
*/
void swc_hal_radio_1_spi_set_cs(void);
/** @brief Reset the on-board controller SPI chip-select pin of the radio #1.
*/
void swc_hal_radio_1_spi_reset_cs(void);
/** @brief Read and Write data full duplex on the radio #1 in blocking mode.
*
* @param[in] tx_data Data buffer to write.
* @param[out] rx_data Data received.
* @param[in] size Size of the data.
*/
void swc_hal_radio_1_spi_transfer_full_duplex_blocking(uint8_t *tx_data, uint8_t *rx_data, uint16_t size);
/** @brief Read and Write data full duplex on the radio #1 in non-blocking mode.
*
* @param[in] tx_data Data buffer to write.
* @param[out] rx_data Data received.
* @param[in] size Size of the data.
*/
void swc_hal_radio_1_spi_transfer_full_duplex_non_blocking(uint8_t *tx_data, uint8_t *rx_data, uint16_t size);
/** @brief Read the status of the radio's SPI of the radio #1.
*
* @retval true SPI is busy.
* @retval false SPI is not busy.
*/
bool swc_hal_radio_1_is_spi_busy(void);
GPIO Controls for Radios
Functions for direct manipulation of GPIO pins connected to the radio ASIC, essential for radio module control and radio IRQ pin state monitoring.
/** @brief Reads the status of radio #1's IRQ pin.
* @retval True If the pin is high.
* @retval False If the pin is low.
*/
bool swc_hal_radio_1_read_irq_pin(void);
/** @brief Sets the reset pin of radio #1.
*/
void swc_hal_radio_1_set_reset_pin(void);
/** @brief Resets the reset pin of radio #1.
*/
void swc_hal_radio_1_reset_reset_pin(void);
Timer and Delay Management
Functions for managing a hardware timer.
/** @brief Get the free running timer tick count.
*
* This function is ack as a watchdog timer for the Stop and Wait feature,
* indicating to the Wireless Core when to abort re-transmission efforts.
*
* @note Users not using the Stop and Wait feature should implement a pseudo-version of
* this function that returns UINT64_MAX, ensuring backend compatibility with the facade.
*
* @return Tick count.
*/
uint64_t swc_hal_get_tick_free_running_timer(void);
/** @brief Returns the configured tick frequency of the free running timer.
*
* Enables the Wireless Core to calculate accurate tick counts for delays and
* other timing-related operations by providing the free running timer's frequency in hertz.
*
* @note Users not using the Stop and Wait feature should implement a pseudo-version of
* this function that returns 0, ensuring backend compatibility with the facade.
*
* @return The free running timer's configured frequency in Hz.
*/
uint32_t swc_hal_get_free_running_timer_frequency_hz(void);
Dual Radio Timer Management
Functions for managing a hardware timer specifically designed for synchronizing dual radio operations. This includes initializing the timer, setting timer periods, handling callbacks, and managing timer interrupts.
/** @brief Initializes the timer for dual-radio support.
*
* Configures the multi-radio timer with the following options:
* - Counter Up mode
* - Auto-reload preload disabled
* - Tick frequency set within this range: 18MHz and 22MHz
* - Interrupt generation at the end of each period
*
* @note This function is part of a dual-radio support system, allowing a single MCU to manage
* two separate radio ASICs. Users implementing support for a single radio ASIC do not have to provide
* an implementation for this function in their backend.
*
*/
void swc_hal_multi_radio_timer_init(void);
/** @brief Set multi-radio callback.
*
* This function registers a internal SWC library function callback to be invoked when the IRQ pin
* interrupt for multi radio timer is triggered.
*
* @note This function is part of a dual-radio support system, allowing a single MCU to manage
* two separate radio ASICs. Users implementing support for a single radio ASIC do not have to provide
* an implementation for this function in their backend.
*
* @param[in] callback A pointer to the callback function that will be executed.
*/
void swc_hal_set_multi_radio_timer_callback(void (*callback)(void));
/** @brief Starts the multi-radio timer.
*
* Initiates the countdown for the multi-radio timer.
*
* @note This function is part of a dual-radio support system, allowing a single MCU to manage
* two separate radio ASICs. Users implementing support for a single radio ASIC do not have to provide
* an implementation for this function in their backend.
*
*/
void swc_hal_timer_multi_radio_timer_start(void);
/** @brief Starts the multi-radio timer.
*
* Initiates the countdown for the multi-radio timer.
*
* @note This function is part of a dual-radio support system, allowing a single MCU to manage
* two separate radio ASICs. Users implementing support for a single radio ASIC do not have to provide
* an implementation for this function in their backend.
*
*/
void swc_hal_timer_multi_radio_timer_start(void);
/** @brief Starts the multi-radio timer.
*
* Halt the countdown for the multi-radio timer.
*
* @note This function is part of a dual-radio support system, allowing a single MCU to manage
* two separate radio ASICs. Users implementing support for a single radio ASIC do not have to provide
* an implementation for this function in their backend.
*
*/
void swc_hal_timer_multi_radio_timer_stop(void);
/** @brief Sets the period of the multi-radio timer.
*
* Defines the duration of the timer cycle for dual-radio operations, specified in ticks.
* Adjusting the period allows for the synchronization of actions across multiple radio
* units within a defined timeline.
*
* @note This function is part of a dual-radio support system, allowing a single MCU to manage
* two separate radio ASICs. Users implementing support for a single radio ASIC do not have to provide
* an implementation for this function in their backend.
*
*
* @param[in] period The timer period, in ticks.
*/
void swc_hal_timer_multi_radio_timer_set_period(uint16_t period);
/** @brief Set the period of the dual radio timer to the maximum value.
*
* @note This function is part of a dual-radio support system, allowing a single MCU to manage
* two separate radio ASICs. Users implementing support for a single radio ASIC do not have to provide
* an implementation for this function in their backend.
*
*/
void swc_hal_timer_multi_radio_timer_set_max_period(void);
/** @brief Returns the configured tick frequency of the multi radio timer.
*
* Enables the Wireless Core to calculate accurate tick counts for delays and
* other timing-related operations by providing the multi radio timer's frequency in hertz.
*
* @return The multi radio timer's configured frequency in Hz.
*/
uint32_t swc_hal_get_timer_multi_frequency_hz(void);
Note
The Wireless Core is designed to interface two radio modules with a single MCU, duplicating peripherals requirements. This is an optional feature. To accommodate this, the facade includes mirrored facade functions. Weak functions have been implemented to the backend to ensure compatibility with the facade in single-radio operation. For a dual-radio application, users must ensure that functions for radio 1 and radio 2 are fully implemented. For a single-radio application, only radio 1 functions need to be implemented.
Provide a context switch mechanism
Users must provide a mechanism to facilitate context switching via their application layer, as this is not included in the facade/backend architecture due to its hardware-agnostic implementation.
Note
This mechanism must be specified through the swc_init() function via its callback argument.
/** @brief Wireless Core initialization.
*
* This is the first API call that needs to be made when initializing and
* configuring the Wireless Core.
*
* @param[in] cfg Wireless Core configuration.
* @param[in] callback A function pointer to the logic initiating context switches.
* @param[out] err Wireless Core error code.
*/
void swc_init(swc_cfg_t cfg, void (*callback)(void), swc_error_t *err);
The context switch initiated by the Wireless Core allows for the handling of callback functions at a lower priority. The context switch mechanism should use a lower priority than the transceiver IRQ and the SPI’s DMA IRQ, ensuring that the system’s real-time performance operations are not compromised. If the user’s application has any time critical IRQs to handle, they should be set at a higher priority than the context switch mechanism. For more information regarding the IRQ priorities used by SPARK, see IRQ Priority Scheme.
The following functions would be raised in the application within that context switch scope.
swc_connection_set_tx_success_callback
swc_connection_set_tx_fail_callback
swc_connection_set_tx_dropped_callback
swc_connection_set_rx_success_callback
swc_connection_set_event_callback
Refer to the Wireless Core API - Callbacks section for more information about these callback functions.
In a bare-metal environment
Use the
SetPendSV()function to trigger a Pendable Service Interrupt (PendSV).Configure PendSV_IRQ_handler to execute
swc_connection_callbacks_processing_handler()at a safe, lower priority.
Hello_world example
Implement the context_switch trigger in the dedicated backend.
void hw_facade_context_switch_trigger(void)
{
SetPendSV();
}
Implement the PendSV_IRQ_Handler callback register function in the dedicated backend.
void hw_facade_set_context_switch_handler(void (*callback)(void))
{
set_pendsv_IRQ_handler_callback(callback);
}
Register the swc_connection_callbacks_processing_handler to be invoked within the PendSV_IRQ_Handler.
hw_facade_set_context_switch_handler(swc_connection_callbacks_processing_handler);
Register the context_switch trigger via the swc_init() API function.
swc_init(core_cfg, hw_facade_context_switch_trigger, err);
In an RTOS environment
Utilize a semaphore or signal a condition that prompts the RTOS scheduler to lower the task’s priority and execute
swc_connection_callbacks_processing_handler().
Hello_world_RTOS example
Implement the context_switch trigger in the application scope.
/** @brief Callback context switch implementation for the SWC interface.
*/
static void callback_context_trigger(void)
{
osSemaphoreRelease(swc_process_sem);
}
Register the context_switch trigger via the
swc_init()API function.
swc_init(core_cfg, callback_context_trigger, err);
Acquire the semaphore prior to executing the
swc_connection_callbacks_processing_handler()function.
while (1) {
iface_button_handling(reset_stats, NULL);
osSemaphoreAcquire(swc_process_sem, osWaitForever);
swc_connection_callbacks_processing_handler();
}