|
| 1 | +/* |
| 2 | + * Copyright (c) 2024 Silicon Laboratories Inc. |
| 3 | + * SPDX-License-Identifier: Apache-2.0 |
| 4 | + */ |
| 5 | +#include <zephyr/drivers/gpio.h> |
| 6 | +#include <zephyr/shell/shell.h> |
| 7 | +#include <zephyr/logging/log.h> |
| 8 | +#include <zephyr/kernel.h> |
| 9 | + |
| 10 | +#include "rail.h" |
| 11 | +#include "rail_config.h" |
| 12 | +#include "pa_conversions_efr32.h" |
| 13 | + |
| 14 | +LOG_MODULE_REGISTER(app); |
| 15 | + |
| 16 | +#ifdef RAIL0_CHANNEL_GROUP_1_PROFILE_WISUN_OFDM |
| 17 | +# if !defined(HARDWARE_BOARD_HAS_EFF) |
| 18 | + BUILD_ASSERT(SL_RAIL_UTIL_PA_SELECTION_SUBGHZ == RAIL_TX_POWER_MODE_OFDM_PA, |
| 19 | + "Please use the OFDM PA settings in the sl_rail_util_pa_config.h " |
| 20 | + "for OFDM phys"); |
| 21 | +# endif |
| 22 | +# if defined(HARDWARE_BOARD_HAS_EFF) && RAIL_SUPPORTS_EFF |
| 23 | + BUILD_ASSERT(SL_RAIL_UTIL_PA_SELECTION_SUBGHZ >= RAIL_TX_POWER_MODE_OFDM_PA_EFF_30DBM, |
| 24 | + "Please use the OFDM PA for EFF settings in the sl_rail_util_pa_config.h " |
| 25 | + "for OFDM phys"); |
| 26 | +# endif |
| 27 | +#endif |
| 28 | + |
| 29 | +static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); |
| 30 | +static const struct gpio_dt_spec led_rx = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); |
| 31 | +#if DT_NODE_EXISTS(DT_ALIAS(led1)) |
| 32 | +static const struct gpio_dt_spec led_tx = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); |
| 33 | +#else |
| 34 | +static const struct gpio_dt_spec led_tx = led_rx; |
| 35 | +#endif |
| 36 | + |
| 37 | +enum { |
| 38 | + EV_RAIL_RX = BIT(0), |
| 39 | + EV_BTN_PRESSED = BIT(1), |
| 40 | +}; |
| 41 | + |
| 42 | +struct { |
| 43 | + RAIL_Handle_t rail_handle; |
| 44 | + struct k_event events; |
| 45 | + struct k_mutex tx_lock; |
| 46 | + int channel; |
| 47 | + const uint8_t *payload; |
| 48 | + int payload_len; |
| 49 | +} app_ctx; |
| 50 | + |
| 51 | +void rx_packets(RAIL_Handle_t rail_handle) |
| 52 | +{ |
| 53 | + uint8_t rx_frame[32]; |
| 54 | + RAIL_RxPacketHandle_t handle; |
| 55 | + RAIL_RxPacketInfo_t info; |
| 56 | + RAIL_Status_t status; |
| 57 | + |
| 58 | + for (;;) { |
| 59 | + handle = RAIL_GetRxPacketInfo(rail_handle, RAIL_RX_PACKET_HANDLE_OLDEST_COMPLETE, |
| 60 | + &info); |
| 61 | + if (handle == RAIL_RX_PACKET_HANDLE_INVALID) { |
| 62 | + return; |
| 63 | + } |
| 64 | + if (info.packetBytes < sizeof(rx_frame)) { |
| 65 | + RAIL_CopyRxPacket(rx_frame, &info); |
| 66 | + } |
| 67 | + status = RAIL_ReleaseRxPacket(rail_handle, handle); |
| 68 | + if (status) { |
| 69 | + LOG_ERR("RAIL_ReleaseRxPacket(): %d", status); |
| 70 | + } |
| 71 | + if (info.packetBytes < sizeof(rx_frame)) { |
| 72 | + LOG_HEXDUMP_INF(rx_frame, info.packetBytes, "rx data:"); |
| 73 | + } else { |
| 74 | + LOG_INF("rx: skip large packet"); |
| 75 | + } |
| 76 | + gpio_pin_set_dt(&led_rx, 0); |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +void tx_packet(RAIL_Handle_t rail_handle, int channel, const uint8_t *payload, int len) |
| 81 | +{ |
| 82 | + RAIL_Status_t status; |
| 83 | + int ret; |
| 84 | + |
| 85 | + ret = RAIL_WriteTxFifo(rail_handle, payload, len, true); |
| 86 | + if (ret != len) { |
| 87 | + LOG_ERR("RAIL_WriteTxFifo(): %d", ret); |
| 88 | + return; |
| 89 | + } |
| 90 | + gpio_pin_set_dt(&led_tx, 1); |
| 91 | + status = RAIL_StartTx(rail_handle, channel, RAIL_TX_OPTIONS_DEFAULT, NULL); |
| 92 | + if (status) { |
| 93 | + LOG_ERR("RAIL_StartTx(): %d ", status); |
| 94 | + } |
| 95 | + LOG_HEXDUMP_INF(payload, len, "tx data:"); |
| 96 | +} |
| 97 | + |
| 98 | +void btn_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins) |
| 99 | +{ |
| 100 | + /* This function is called from an ISR context. So, transfer the real |
| 101 | + * processing to the main loop. |
| 102 | + */ |
| 103 | + k_event_post(&app_ctx.events, EV_BTN_PRESSED); |
| 104 | +} |
| 105 | + |
| 106 | +void cli_send(const struct shell *sh, size_t argc, char **argv) |
| 107 | +{ |
| 108 | + k_mutex_lock(&app_ctx.tx_lock, K_FOREVER); |
| 109 | + tx_packet(app_ctx.rail_handle, app_ctx.channel, app_ctx.payload, app_ctx.payload_len); |
| 110 | + k_mutex_unlock(&app_ctx.tx_lock); |
| 111 | +} |
| 112 | + |
| 113 | +void rail_on_event(RAIL_Handle_t rail_handle, RAIL_Events_t events) |
| 114 | +{ |
| 115 | + RAIL_Status_t status; |
| 116 | + |
| 117 | + if (events & RAIL_EVENTS_RX_COMPLETION) { |
| 118 | + if (events & RAIL_EVENT_RX_PACKET_RECEIVED) { |
| 119 | + gpio_pin_set_dt(&led_rx, 1); |
| 120 | + RAIL_HoldRxPacket(rail_handle); |
| 121 | + k_event_post(&app_ctx.events, EV_RAIL_RX); |
| 122 | + } else { |
| 123 | + LOG_ERR("radio rx error: %08llx", events); |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + if (events & RAIL_EVENTS_TX_COMPLETION) { |
| 128 | + if (!(events & RAIL_EVENT_TX_PACKET_SENT)) { |
| 129 | + LOG_ERR("radio tx error: %08llx", events); |
| 130 | + } |
| 131 | + gpio_pin_set_dt(&led_tx, 0); |
| 132 | + } |
| 133 | + |
| 134 | + if (events & RAIL_EVENTS_TXACK_COMPLETION) { |
| 135 | + /* We do not configure Tx ack. Catch the event anyway */ |
| 136 | + LOG_INF("received ack completion"); |
| 137 | + } |
| 138 | + |
| 139 | + if (events & RAIL_EVENT_CAL_NEEDED) { |
| 140 | + status = RAIL_Calibrate(rail_handle, NULL, RAIL_CAL_ALL_PENDING); |
| 141 | + if (status) { |
| 142 | + LOG_ERR("RAIL_Calibrate(): %d", status); |
| 143 | + } |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +static void rail_on_rf_ready(RAIL_Handle_t rail_handle) |
| 148 | +{ |
| 149 | + LOG_INF("radio is ready"); |
| 150 | +} |
| 151 | + |
| 152 | +static void rail_on_channel_config(RAIL_Handle_t rail_handle, |
| 153 | + const RAIL_ChannelConfigEntry_t *entry) |
| 154 | +{ |
| 155 | + sl_rail_util_pa_on_channel_config_change(rail_handle, entry); |
| 156 | +} |
| 157 | + |
| 158 | +static RAIL_Handle_t rail_init(void) |
| 159 | +{ |
| 160 | + static uint8_t tx_fifo[256] __aligned(4); |
| 161 | + RAIL_Config_t rail_config = { |
| 162 | + .eventsCallback = &rail_on_event, |
| 163 | + }; |
| 164 | + RAIL_DataConfig_t data_config = { |
| 165 | + .txSource = TX_PACKET_DATA, |
| 166 | + .rxSource = RX_PACKET_DATA, |
| 167 | + .txMethod = PACKET_MODE, |
| 168 | + .rxMethod = PACKET_MODE, |
| 169 | + }; |
| 170 | + RAIL_StateTransitions_t transitions = { |
| 171 | + .success = RAIL_RF_STATE_RX, |
| 172 | + .error = RAIL_RF_STATE_RX, |
| 173 | + }; |
| 174 | + RAIL_Handle_t rail_handle; |
| 175 | + RAIL_Status_t status; |
| 176 | + int ret; |
| 177 | + |
| 178 | + rail_handle = RAIL_Init(&rail_config, &rail_on_rf_ready); |
| 179 | + if (!rail_handle) { |
| 180 | + LOG_ERR("RAIL_Init() failed"); |
| 181 | + } |
| 182 | + status = RAIL_ConfigData(rail_handle, &data_config); |
| 183 | + if (status) { |
| 184 | + LOG_ERR("RAIL_ConfigData(): %d", status); |
| 185 | + } |
| 186 | + status = RAIL_ConfigChannels(rail_handle, channelConfigs[0], &rail_on_channel_config); |
| 187 | + if (status) { |
| 188 | + LOG_ERR("RAIL_ConfigChannels(): %d", status); |
| 189 | + } |
| 190 | + status = RAIL_SetPtiProtocol(rail_handle, RAIL_PTI_PROTOCOL_CUSTOM); |
| 191 | + if (status) { |
| 192 | + LOG_ERR("RAIL_SetPtiProtocol(): %d", status); |
| 193 | + } |
| 194 | + status = RAIL_ConfigCal(rail_handle, RAIL_CAL_TEMP | RAIL_CAL_ONETIME); |
| 195 | + if (status) { |
| 196 | + LOG_ERR("RAIL_ConfigCal(): %d", status); |
| 197 | + } |
| 198 | + status = RAIL_ConfigEvents(rail_handle, RAIL_EVENTS_ALL, |
| 199 | + RAIL_EVENTS_RX_COMPLETION | |
| 200 | + RAIL_EVENTS_TX_COMPLETION | |
| 201 | + RAIL_EVENTS_TXACK_COMPLETION | |
| 202 | + RAIL_EVENT_CAL_NEEDED); |
| 203 | + if (status) { |
| 204 | + LOG_ERR("RAIL_ConfigEvents(): %d", status); |
| 205 | + } |
| 206 | + status = RAIL_SetTxTransitions(rail_handle, &transitions); |
| 207 | + if (status) { |
| 208 | + LOG_ERR("RAIL_SetTxTransitions(): %d", status); |
| 209 | + } |
| 210 | + status = RAIL_SetRxTransitions(rail_handle, &transitions); |
| 211 | + if (status) { |
| 212 | + LOG_ERR("RAIL_SetRxTransitions(): %d", status); |
| 213 | + } |
| 214 | + ret = RAIL_SetTxFifo(rail_handle, tx_fifo, 0, sizeof(tx_fifo)); |
| 215 | + if (ret != sizeof(tx_fifo)) { |
| 216 | + LOG_ERR("RAIL_SetTxFifo(): %d != %d", ret, sizeof(tx_fifo)); |
| 217 | + } |
| 218 | + |
| 219 | + return rail_handle; |
| 220 | +} |
| 221 | + |
| 222 | +static void rail_isr_installer(void) |
| 223 | +{ |
| 224 | +#ifdef CONFIG_SOC_SERIES_EFR32MG24 |
| 225 | + IRQ_CONNECT(SYNTH_IRQn, 0, SYNTH_IRQHandler, NULL, 0); |
| 226 | +#else |
| 227 | + IRQ_CONNECT(RDMAILBOX_IRQn, 0, RDMAILBOX_IRQHandler, NULL, 0); |
| 228 | +#endif |
| 229 | + IRQ_CONNECT(RAC_SEQ_IRQn, 0, RAC_SEQ_IRQHandler, NULL, 0); |
| 230 | + IRQ_CONNECT(RAC_RSM_IRQn, 0, RAC_RSM_IRQHandler, NULL, 0); |
| 231 | + IRQ_CONNECT(PROTIMER_IRQn, 0, PROTIMER_IRQHandler, NULL, 0); |
| 232 | + IRQ_CONNECT(MODEM_IRQn, 0, MODEM_IRQHandler, NULL, 0); |
| 233 | + IRQ_CONNECT(FRC_IRQn, 0, FRC_IRQHandler, NULL, 0); |
| 234 | + IRQ_CONNECT(BUFC_IRQn, 0, BUFC_IRQHandler, NULL, 0); |
| 235 | + IRQ_CONNECT(AGC_IRQn, 0, AGC_IRQHandler, NULL, 0); |
| 236 | +} |
| 237 | + |
| 238 | +int main(void) |
| 239 | +{ |
| 240 | + static const uint8_t default_payload[] = { |
| 241 | + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, |
| 242 | + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF |
| 243 | + }; |
| 244 | + struct gpio_callback sw0_cb; |
| 245 | + RAIL_Status_t status; |
| 246 | + uint32_t events; |
| 247 | + int ret; |
| 248 | + |
| 249 | + k_event_init(&app_ctx.events); |
| 250 | + k_mutex_init(&app_ctx.tx_lock); |
| 251 | + gpio_pin_configure_dt(&led_tx, GPIO_OUTPUT_INACTIVE); |
| 252 | + gpio_pin_configure_dt(&led_rx, GPIO_OUTPUT_INACTIVE); |
| 253 | + gpio_pin_configure_dt(&sw0, GPIO_INPUT); |
| 254 | + gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_EDGE_TO_ACTIVE); |
| 255 | + gpio_init_callback(&sw0_cb, btn_pressed, BIT(sw0.pin)); |
| 256 | + gpio_add_callback(sw0.port, &sw0_cb); |
| 257 | + |
| 258 | + rail_isr_installer(); |
| 259 | + sl_rail_util_pa_init(); |
| 260 | + app_ctx.rail_handle = rail_init(); |
| 261 | + app_ctx.channel = 0; |
| 262 | + app_ctx.payload = default_payload; |
| 263 | + app_ctx.payload_len = sizeof(default_payload); |
| 264 | + |
| 265 | + ret = RAIL_SetFixedLength(app_ctx.rail_handle, app_ctx.payload_len); |
| 266 | + if (ret != app_ctx.payload_len) { |
| 267 | + LOG_ERR("RAIL_SetFixedLength(): %d ", ret); |
| 268 | + } |
| 269 | + status = RAIL_StartRx(app_ctx.rail_handle, app_ctx.channel, NULL); |
| 270 | + if (status) { |
| 271 | + LOG_ERR("RAIL_StartRx(): %d ", status); |
| 272 | + } |
| 273 | + |
| 274 | +#ifdef CONFIG_PM |
| 275 | + status = RAIL_InitPowerManager(); |
| 276 | + if (status) { |
| 277 | + LOG_ERR("RAIL_InitPowerManager(): %d", status); |
| 278 | + } |
| 279 | +#endif |
| 280 | + |
| 281 | + for (;;) { |
| 282 | + events = k_event_wait(&app_ctx.events, 0xFFFFFFFF, true, K_FOREVER); |
| 283 | + if (events & EV_RAIL_RX) { |
| 284 | + rx_packets(app_ctx.rail_handle); |
| 285 | + } |
| 286 | + if (events & EV_BTN_PRESSED) { |
| 287 | + k_mutex_lock(&app_ctx.tx_lock, K_FOREVER); |
| 288 | + tx_packet(app_ctx.rail_handle, app_ctx.channel, |
| 289 | + app_ctx.payload, app_ctx.payload_len); |
| 290 | + k_mutex_unlock(&app_ctx.tx_lock); |
| 291 | + } |
| 292 | + } |
| 293 | + |
| 294 | + return 0; |
| 295 | +} |
| 296 | + |
| 297 | +SHELL_STATIC_SUBCMD_SET_CREATE(radio_cmds, |
| 298 | + SHELL_CMD_ARG(send, NULL, "Send a packet", cli_send, 1, 0), |
| 299 | + SHELL_SUBCMD_SET_END |
| 300 | +); |
| 301 | +SHELL_CMD_ARG_REGISTER(radio, &radio_cmds, "Radio control", NULL, 2, 0); |
0 commit comments