From b415d57a99d4f7e506c9af22a9709bf6d3729679 Mon Sep 17 00:00:00 2001 From: "Xenofon (Fontas) Fafoutis" Date: Thu, 23 Nov 2017 12:28:02 +0000 Subject: [PATCH] enhanced BLE support for cc26xx-cc13xx --- arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.c | 280 +++++++++++++++--------- arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.h | 50 +++++ 2 files changed, 222 insertions(+), 108 deletions(-) diff --git a/arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.c b/arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.c index 3faf75d25..e5a38cac7 100644 --- a/arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.c +++ b/arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (c) 2017, University of Bristol - http://www.bristol.ac.uk/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -71,7 +72,7 @@ #define BLE_ADV_TYPE_DEVINFO 0x01 #define BLE_ADV_TYPE_NAME 0x09 #define BLE_ADV_TYPE_MANUFACTURER 0xFF -#define BLE_ADV_NAME_BUF_LEN 32 +#define BLE_ADV_NAME_BUF_LEN BLE_ADV_MAX_SIZE #define BLE_ADV_PAYLOAD_BUF_LEN 64 #define BLE_UUID_SIZE 16 /*---------------------------------------------------------------------------*/ @@ -82,8 +83,6 @@ static uint8_t payload[BLE_ADV_PAYLOAD_BUF_LEN]; static int p = 0; static int i; /*---------------------------------------------------------------------------*/ -static uint16_t tx_power = 0x9330; -/*---------------------------------------------------------------------------*/ /* BLE beacond config */ static struct ble_beacond_config { clock_time_t interval; @@ -108,6 +107,38 @@ static uint32_t ble_overrides[] = { 0xFFFFFFFF, /* End of override list */ }; /*---------------------------------------------------------------------------*/ +/* TX Power dBm lookup table - values from SmartRF Studio */ +typedef struct output_config { + radio_value_t dbm; + uint16_t tx_power; /* Value for the CMD_RADIO_SETUP.txPower field */ +} output_config_t; + +static const output_config_t output_power[] = { + { 5, 0x9330 }, + { 4, 0x9324 }, + { 3, 0x5a1c }, + { 2, 0x4e18 }, + { 1, 0x4214 }, + { 0, 0x3161 }, + { -3, 0x2558 }, + { -6, 0x1d52 }, + { -9, 0x194e }, + { -12, 0x144b }, + { -15, 0x0ccb }, + { -18, 0x0cc9 }, + { -21, 0x0cc7 }, +}; + +#define OUTPUT_CONFIG_COUNT (sizeof(output_power) / sizeof(output_config_t)) + +/* Max and Min Output Power in dBm */ +#define OUTPUT_POWER_MIN (output_power[OUTPUT_CONFIG_COUNT - 1].dbm) +#define OUTPUT_POWER_MAX (output_power[0].dbm) +#define OUTPUT_POWER_UNKNOWN 0xFFFF + +/* Default TX Power - position in output_power[] */ +static const output_config_t *tx_power_current = &output_power[0]; +/*---------------------------------------------------------------------------*/ PROCESS(rf_ble_beacon_process, "CC13xx / CC26xx RF BLE Beacon Process"); /*---------------------------------------------------------------------------*/ static int @@ -132,7 +163,7 @@ send_ble_adv_nc(int channel, uint8_t *adv_payload, int adv_payload_len) cmd.channel = channel; /* Set up BLE Advertisement parameters */ - params->pDeviceAddress = (uint16_t *)&linkaddr_node_addr.u8[LINKADDR_SIZE - 2]; + params->pDeviceAddress = (uint16_t *)BLE_ADDRESS_PTR; params->endTrigger.triggerType = TRIG_NEVER; params->endTime = TRIG_NEVER; @@ -157,6 +188,33 @@ send_ble_adv_nc(int channel, uint8_t *adv_payload, int adv_payload_len) return RF_CORE_CMD_OK; } /*---------------------------------------------------------------------------*/ +/* Returns the current TX power in dBm */ +radio_value_t +rf_ble_get_tx_power(void) +{ + return tx_power_current->dbm; +} +/*---------------------------------------------------------------------------*/ +/* + * Set TX power to 'at least' power dBm + * This works with a lookup table. If the value of 'power' does not exist in + * the lookup table, TXPOWER will be set to the immediately higher available + * value + */ +void +rf_ble_set_tx_power(radio_value_t power) +{ + int i; + + /* First, find the correct setting and save it */ + for(i = OUTPUT_CONFIG_COUNT - 1; i >= 0; --i) { + if(power <= output_power[i].dbm) { + tx_power_current = &output_power[i]; + break; + } + } +} +/*---------------------------------------------------------------------------*/ void rf_ble_beacond_config(clock_time_t interval, const char *name) { @@ -211,7 +269,6 @@ rf_ble_beacond_stop() { process_exit(&rf_ble_beacon_process); } -/*---------------------------------------------------------------------------*/ static uint8_t rf_radio_setup() { @@ -223,7 +280,7 @@ rf_radio_setup() /* Create radio setup command */ rf_core_init_radio_op((rfc_radioOp_t *)&cmd, sizeof(cmd), CMD_RADIO_SETUP); - cmd.txPower = tx_power; + cmd.txPower = tx_power_current->tx_power; cmd.pRegOverride = ble_overrides; cmd.config.frontEndMode = RF_CORE_RADIO_SETUP_FRONT_END_MODE; cmd.config.biasMode = RF_CORE_RADIO_SETUP_BIAS_MODE; @@ -246,13 +303,114 @@ rf_radio_setup() return RF_CORE_CMD_OK; } /*---------------------------------------------------------------------------*/ -PROCESS_THREAD(rf_ble_beacon_process, ev, data) +/*---------------------------------------------------------------------------*/ +void +rf_ble_beacon_single(uint8_t channel, uint8_t *data, uint8_t len) { - uint8_t was_on; - int j; uint32_t cmd_status; bool interrupts_disabled; + uint8_t j, channel_selected; + uint8_t was_on; + /* Adhere to the maximum BLE advertisement payload size */ + if(len > BLE_ADV_NAME_BUF_LEN) { + len = BLE_ADV_NAME_BUF_LEN; + } + + /* + * Under ContikiMAC, some IEEE-related operations will be called from an + * interrupt context. We need those to see that we are in BLE mode. + */ + interrupts_disabled = ti_lib_int_master_disable(); + ble_mode_on = RF_BLE_ACTIVE; + if(!interrupts_disabled) { + ti_lib_int_master_enable(); + } + + /* + * First, determine our state: + * + * If we are running CSMA, we are likely in IEEE RX mode. We need to + * abort the IEEE BG Op before entering BLE mode. + * If we are ContikiMAC, we are likely off, in which case we need to + * boot the CPE before entering BLE mode + */ + was_on = rf_core_is_accessible(); + + if(was_on) { + /* + * We were on: If we are in the process of receiving a frame, abort the + * BLE beacon burst. Otherwise, terminate the primary radio Op so we + * can switch to BLE mode + */ + if(NETSTACK_RADIO.receiving_packet()) { + PRINTF("rf_ble_beacon_single: We were receiving\n"); + + /* Abort this pass */ + return; + } + + rf_core_primary_mode_abort(); + } else { + + oscillators_request_hf_xosc(); + + /* We were off: Boot the CPE */ + if(rf_core_boot() != RF_CORE_CMD_OK) { + /* Abort this pass */ + PRINTF("rf_ble_beacon_single: rf_core_boot() failed\n"); + return; + } + + oscillators_switch_to_hf_xosc(); + + /* Enter BLE mode */ + if(rf_radio_setup() != RF_CORE_CMD_OK) { + /* Continue so we can at least try to restore our previous state */ + PRINTF("rf_ble_beacon_single: Error entering BLE mode\n"); + } else { + + for(j = 0; j < 3; j++) { + channel_selected = (channel >> j) & 0x01; + if(channel_selected == 1) { + if(send_ble_adv_nc(37 + j, data, len) != RF_CORE_CMD_OK) { + /* Continue... */ + PRINTF("rf_ble_beacon_single: Channel=%d, " + "Error advertising\n", 37 + j); + } + } + } + } + + /* Send a CMD_STOP command to RF Core */ + if(rf_core_send_cmd(CMDR_DIR_CMD(CMD_STOP), &cmd_status) != RF_CORE_CMD_OK) { + /* Continue... */ + PRINTF("rf_ble_beacon_single: status=0x%08lx\n", cmd_status); + } + + if(was_on) { + /* We were on, go back to previous primary mode */ + rf_core_primary_mode_restore(); + } else { + /* We were off. Shut back off */ + rf_core_power_down(); + + /* Switch HF clock source to the RCOSC to preserve power */ + oscillators_switch_to_hf_rc(); + } + + interrupts_disabled = ti_lib_int_master_disable(); + + ble_mode_on = RF_BLE_IDLE; + + if(!interrupts_disabled) { + ti_lib_int_master_enable(); + } + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(rf_ble_beacon_process, ev, data) +{ PROCESS_BEGIN(); while(1) { @@ -278,115 +436,21 @@ PROCESS_THREAD(rf_ble_beacon_process, ev, data) strlen(beacond_config.adv_name)); p += strlen(beacond_config.adv_name); + /* + * Send BLE_ADV_MESSAGES beacon bursts. Each burst on all three + * channels, with a BLE_ADV_DUTY_CYCLE interval between bursts + */ for(i = 0; i < BLE_ADV_MESSAGES; i++) { - /* - * Under ContikiMAC, some IEEE-related operations will be called from an - * interrupt context. We need those to see that we are in BLE mode. - */ - interrupts_disabled = ti_lib_int_master_disable(); - ble_mode_on = RF_BLE_ACTIVE; - if(!interrupts_disabled) { - ti_lib_int_master_enable(); - } - /* - * Send BLE_ADV_MESSAGES beacon bursts. Each burst on all three - * channels, with a BLE_ADV_DUTY_CYCLE interval between bursts - * - * First, determine our state: - * - * If we are running CSMA, we are likely in IEEE RX mode. We need to - * abort the IEEE BG Op before entering BLE mode. - * If we are ContikiMAC, we are likely off, in which case we need to - * boot the CPE before entering BLE mode - */ - was_on = rf_core_is_accessible(); + rf_ble_beacon_single(BLE_ADV_CHANNEL_ALL, payload, p); - if(was_on) { - /* - * We were on: If we are in the process of receiving a frame, abort the - * BLE beacon burst. Otherwise, terminate the primary radio Op so we - * can switch to BLE mode - */ - if(NETSTACK_RADIO.receiving_packet()) { - PRINTF("rf_ble_beacon_process: We were receiving\n"); - - /* Abort this pass */ - break; - } - - rf_core_primary_mode_abort(); - } else { - /* Request the HF XOSC to source the HF clock. */ - oscillators_request_hf_xosc(); - - /* We were off: Boot the CPE */ - if(rf_core_boot() != RF_CORE_CMD_OK) { - PRINTF("rf_ble_beacon_process: rf_core_boot() failed\n"); - - /* Abort this pass */ - break; - } - - /* Trigger a switch to the XOSC, so that we can use the FS */ - oscillators_switch_to_hf_xosc(); - } - - /* Enter BLE mode */ - if(rf_radio_setup() != RF_CORE_CMD_OK) { - PRINTF("cc26xx_rf_ble_beacon_process: Error entering BLE mode\n"); - /* Continue so we can at least try to restore our previous state */ - } else { - /* Send advertising packets on all 3 advertising channels */ - for(j = 37; j <= 39; j++) { - if(send_ble_adv_nc(j, payload, p) != RF_CORE_CMD_OK) { - PRINTF("cc26xx_rf_ble_beacon_process: Channel=%d, " - "Error advertising\n", j); - /* Break the loop, but don't return just yet */ - break; - } - } - } - - /* Send a CMD_STOP command to RF Core */ - if(rf_core_send_cmd(CMDR_DIR_CMD(CMD_STOP), &cmd_status) != RF_CORE_CMD_OK) { - PRINTF("cc26xx_rf_ble_beacon_process: status=0x%08lx\n", cmd_status); - /* Continue... */ - } - - if(was_on) { - /* We were on, go back to previous primary mode */ - rf_core_primary_mode_restore(); - } else { - /* We were off. Shut back off */ - rf_core_power_down(); - - /* Switch HF clock source to the RCOSC to preserve power */ - oscillators_switch_to_hf_rc(); - } etimer_set(&ble_adv_et, BLE_ADV_DUTY_CYCLE); - interrupts_disabled = ti_lib_int_master_disable(); - - ble_mode_on = RF_BLE_IDLE; - - if(!interrupts_disabled) { - ti_lib_int_master_enable(); - } - /* Wait unless this is the last burst */ if(i < BLE_ADV_MESSAGES - 1) { PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&ble_adv_et)); } } - - interrupts_disabled = ti_lib_int_master_disable(); - - ble_mode_on = RF_BLE_IDLE; - - if(!interrupts_disabled) { - ti_lib_int_master_enable(); - } } PROCESS_END(); } diff --git a/arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.h b/arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.h index 6d91dc13d..761ca88d6 100644 --- a/arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.h +++ b/arch/cpu/cc26xx-cc13xx/rf-core/rf-ble.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (c) 2017, University of Bristol - http://www.bristol.ac.uk/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,6 +46,7 @@ /*---------------------------------------------------------------------------*/ #include "contiki.h" #include "rf-core/rf-core.h" +#include "radio.h" #include /*---------------------------------------------------------------------------*/ @@ -57,6 +59,23 @@ #define RF_BLE_IDLE 0 #define RF_BLE_ACTIVE 1 /*---------------------------------------------------------------------------*/ +/* Memory address of BLE MAC address. Can be overwritten by the user by defining BLE_ADDRESS_PTR. */ +#ifndef BLE_ADDRESS_PTR +#define BLE_ADDRESS_PTR (0x500012E8) +#endif +/*---------------------------------------------------------------------------*/ +/* BLE Advertisement channels. Not to be changed by the user. */ +#define BLE_ADV_CHANNEL_MASK 0x07 + +#define BLE_ADV_CHANNEL_37 0x01 +#define BLE_ADV_CHANNEL_38 0x02 +#define BLE_ADV_CHANNEL_39 0x04 + +#define BLE_ADV_CHANNEL_ALL (BLE_ADV_CHANNEL_37 | BLE_ADV_CHANNEL_38 | BLE_ADV_CHANNEL_39) +/*---------------------------------------------------------------------------*/ +/* Maximum BLE advertisement size. Not to be changed by the user. */ +#define BLE_ADV_MAX_SIZE 31 +/*---------------------------------------------------------------------------*/ /** * \brief Set the device name to use with the BLE advertisement/beacon daemon * \param interval The interval (ticks) between two consecutive beacon bursts @@ -89,6 +108,37 @@ void rf_ble_beacond_stop(void); * \retval 0 The BLE daemon is not active, or disabled */ uint8_t rf_ble_is_active(void); + +/** + * \brief Set TX power for BLE advertisements + * \param power The 'at least' TX power in dBm, values avove 5 dBm will be ignored + * + * Set TX power to 'at least' power dBm. + * This works with a lookup table. If the value of 'power' does not exist in + * the lookup table, TXPOWER will be set to the immediately higher available + * value + */ +void rf_ble_set_tx_power(radio_value_t power); + +/** + * \brief Get TX power for BLE advertisements + * \return The TX power for BLE advertisements + */ +radio_value_t rf_ble_get_tx_power(void); + +/** + * \brief Transmit a single BLE advertisement in one or more advertisement channels + * \param channel Bitmask of advertisement channels to be used: use + * BLE_ADV_CHANNEL_37 for channel 37, BLE_ADV_CHANNEL_38 for channel 38, + * BLE_ADV_CHANNEL_39 for channel 39, or any 'or' combination of those + * \param data A pointer to the advertisement data buffer + * \param len The length of the advertisement data. If more than BLE_ADV_MAX_SIZE, + * the first BLE_ADV_MAX_SIZE bytes will be used. + * + * Transmits a given advertisement payload once in one or any arbitrary combination + * of advertisement channels. + */ +void rf_ble_beacon_single(uint8_t channel, uint8_t *data, uint8_t len); /*---------------------------------------------------------------------------*/ #endif /* RF_BLE_H_ */ /*---------------------------------------------------------------------------*/