From 9692a1a31771627341690ca4bcf5e049d4ef15f6 Mon Sep 17 00:00:00 2001 From: Nanosonde <2073569+nanosonde@users.noreply.github.com> Date: Mon, 2 Nov 2020 14:54:16 +0100 Subject: [PATCH] initial commit of CC1310 RF driver with first working RX version --- examples/knx-cc1310/RFQueue.c | 133 +++++++ examples/knx-cc1310/RFQueue.h | 79 ++++ examples/knx-cc1310/knx_wrapper.cpp | 8 +- .../smartrf_settings/smartrf_settings.c | 300 +++++++++++++++ .../smartrf_settings/smartrf_settings.h | 28 ++ src/knx/rf_physical_layer_cc1310.cpp | 364 ++++++++++++++++++ 6 files changed, 908 insertions(+), 4 deletions(-) create mode 100644 examples/knx-cc1310/RFQueue.c create mode 100644 examples/knx-cc1310/RFQueue.h create mode 100644 examples/knx-cc1310/smartrf_settings/smartrf_settings.c create mode 100644 examples/knx-cc1310/smartrf_settings/smartrf_settings.h create mode 100644 src/knx/rf_physical_layer_cc1310.cpp diff --git a/examples/knx-cc1310/RFQueue.c b/examples/knx-cc1310/RFQueue.c new file mode 100644 index 0000000..52a68ad --- /dev/null +++ b/examples/knx-cc1310/RFQueue.c @@ -0,0 +1,133 @@ +/****************************************************************************** +* Filename: rf_queue.c +* Revised: $ $ +* Revision: $ $ +* +* Description: Help functions for handling queues +* +* Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ +* +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +******************************************************************************/ +/* Standard C Libraries */ +#include +#include + +/* Application Header files */ +#include "RFQueue.h" +#include +#include DeviceFamily_constructPath(driverlib/rf_data_entry.h) + +/* Receive entry pointer to keep track of read items */ +rfc_dataEntryGeneral_t* readEntry; + +//***************************************************************************** +// +//! Get the current dataEntry +//! +//! \return rfc_dataEntry* +// +//***************************************************************************** +rfc_dataEntryGeneral_t* +RFQueue_getDataEntry() +{ + return (readEntry); +} + +//***************************************************************************** +// +//! Move to next dataEntry +//! +//! \return None +// +//***************************************************************************** +uint8_t +RFQueue_nextEntry() +{ + /* Set status to pending */ + readEntry->status = DATA_ENTRY_PENDING; + + /* Move read entry pointer to next entry */ + readEntry = (rfc_dataEntryGeneral_t*)readEntry->pNextEntry; + + return (readEntry->status); +} + +//***************************************************************************** +// +//! Define a queue +//! +//! \param dataQueue is a pointer to the queue to use +//! \param buf is the prealocated byte buffer to use +//! \param buf_len is the number of preallocated bytes +//! \param numEntries are the number of dataEntries to split the buffer into +//! \param length is the length of data in every dataEntry +//! +//! \return uint8_t +// +//***************************************************************************** +uint8_t +RFQueue_defineQueue(dataQueue_t *dataQueue, uint8_t *buf, uint16_t buf_len, uint8_t numEntries, uint16_t length) +{ + + if (buf_len < (numEntries * (length + RF_QUEUE_DATA_ENTRY_HEADER_SIZE + RF_QUEUE_QUEUE_ALIGN_PADDING(length)))) + { + /* queue does not fit into buffer */ + return (1); + } + + /* Padding needed for 4-byte alignment? */ + uint8_t pad = 4-((length + RF_QUEUE_DATA_ENTRY_HEADER_SIZE)%4); + + /* Set the Data Entries common configuration */ + uint8_t *first_entry = buf; + int i; + for (i = 0; i < numEntries; i++) + { + buf = first_entry + i * (RF_QUEUE_DATA_ENTRY_HEADER_SIZE + length + pad); + ((rfc_dataEntry_t*)buf)->status = DATA_ENTRY_PENDING; // Pending - starting state + ((rfc_dataEntry_t*)buf)->config.type = DATA_ENTRY_TYPE_GEN; // General Data Entry + ((rfc_dataEntry_t*)buf)->config.lenSz = 0; // No length indicator byte in data + ((rfc_dataEntry_t*)buf)->length = length; // Total length of data field + + ((rfc_dataEntryGeneral_t*)buf)->pNextEntry = &(((rfc_dataEntryGeneral_t*)buf)->data)+length+pad; + } + /* Make circular Last.Next -> First */ + ((rfc_dataEntry_t*)buf)->pNextEntry = first_entry; + + /* Create Data Entry Queue and configure for circular buffer Data Entries */ + dataQueue->pCurrEntry = first_entry; + dataQueue->pLastEntry = NULL; + + /* Set read pointer to first entry */ + readEntry = (rfc_dataEntryGeneral_t*)first_entry; + + return (0); +} diff --git a/examples/knx-cc1310/RFQueue.h b/examples/knx-cc1310/RFQueue.h new file mode 100644 index 0000000..c8b125d --- /dev/null +++ b/examples/knx-cc1310/RFQueue.h @@ -0,0 +1,79 @@ +/****************************************************************************** +* Filename: rf_queue.h +* Revised: $ $ +* Revision: $ $ +* +* Description: Help functions for handling queues +* +* Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ +* +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +******************************************************************************/ + +//***************************************************************************** +// +//! \addtogroup rfqueue_api +//! @{ +// +//***************************************************************************** + +#ifndef RF_QUEUE_H +#define RF_QUEUE_H + +#include +#include DeviceFamily_constructPath(driverlib/rf_data_entry.h) + +#define RF_QUEUE_DATA_ENTRY_HEADER_SIZE 8 // Contant header size of a Generic Data Entry + +#define RF_QUEUE_QUEUE_ALIGN_PADDING(length) (4-((length + RF_QUEUE_DATA_ENTRY_HEADER_SIZE)%4)) // Padding offset + +#define RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(numEntries, dataSize, appendedBytes) \ +(numEntries*(RF_QUEUE_DATA_ENTRY_HEADER_SIZE + dataSize + appendedBytes + RF_QUEUE_QUEUE_ALIGN_PADDING(dataSize + appendedBytes))) + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint8_t RFQueue_nextEntry(); +extern rfc_dataEntryGeneral_t* RFQueue_getDataEntry(); +extern uint8_t RFQueue_defineQueue(dataQueue_t *queue ,uint8_t *buf, uint16_t buf_len, uint8_t numEntries, uint16_t length); + +#ifdef __cplusplus +} +#endif + +#endif + +//***************************************************************************** +// +//! Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/examples/knx-cc1310/knx_wrapper.cpp b/examples/knx-cc1310/knx_wrapper.cpp index a5dbfd1..798cdb5 100644 --- a/examples/knx-cc1310/knx_wrapper.cpp +++ b/examples/knx-cc1310/knx_wrapper.cpp @@ -3,12 +3,12 @@ #include "knx_wrapper.h" -KnxFacade *pKnx = nullptr; +KnxFacade *pKnx = nullptr; void setup() { - pKnx = new KnxFacade; - KnxFacade &knx = *pKnx; + pKnx = new KnxFacade; + KnxFacade &knx = *pKnx; knx.platform().init(); @@ -28,7 +28,7 @@ void setup() void loop() { - KnxFacade &knx = *pKnx; + KnxFacade &knx = *pKnx; knx.loop(); } diff --git a/examples/knx-cc1310/smartrf_settings/smartrf_settings.c b/examples/knx-cc1310/smartrf_settings/smartrf_settings.c new file mode 100644 index 0000000..c983d4d --- /dev/null +++ b/examples/knx-cc1310/smartrf_settings/smartrf_settings.c @@ -0,0 +1,300 @@ +//********************************************************************************* +// Generated by SmartRF Studio version 2.6.0 (build #8) +// Tested for SimpleLink SDK version: CC13x0 SDK 1.30.xx.xx +// Device: CC1310 Rev. 2.1 +// +//********************************************************************************* + + +//********************************************************************************* +// Parameter summary +// Address: off +// Address0: 0xAA +// Address1: 0xBB +// Frequency: 868.29999 MHz +// Data Format: Serial mode disable +// Deviation: 50.000 kHz +// Packet Length Config: Variable +// Max Packet Length: 128 +// Packet Length: 20 +// RX Filter BW: 196 kHz +// Symbol Rate: 32.76825 kBaud +// Sync Word Length: 24 Bits +// TX Power: 14 dBm (requires define CCFG_FORCE_VDDR_HH = 1 in ccfg.c, see CC13xx/CC26xx Technical Reference Manual) +// Whitening: No whitening + +#include +#include DeviceFamily_constructPath(driverlib/rf_mailbox.h) +#include DeviceFamily_constructPath(driverlib/rf_common_cmd.h) +#include DeviceFamily_constructPath(driverlib/rf_prop_cmd.h) +#include +#include DeviceFamily_constructPath(rf_patches/rf_patch_cpe_wmbus_smode.h) +#include DeviceFamily_constructPath(rf_patches/rf_patch_mce_wmbus_smode.h) +#include DeviceFamily_constructPath(rf_patches/rf_patch_rfe_wmbus_smode.h) +#include "smartrf_settings.h" + + +// TI-RTOS RF Mode Object +RF_Mode RF_prop = +{ + .rfMode = RF_MODE_PROPRIETARY_SUB_1, + .cpePatchFxn = &rf_patch_cpe_wmbus_smode, + .mcePatchFxn = &rf_patch_mce_wmbus_smode, + .rfePatchFxn = &rf_patch_rfe_wmbus_smode, +}; + +// Overrides for CMD_PROP_RADIO_DIV_SETUP +static uint32_t pOverrides[] = +{ + // PHY: Run the MCE and RFE patches + MCE_RFE_OVERRIDE(1,0,0,1,0,0), + // override_synth_prop_863_930_div5.xml + // Synth: Set recommended RTRIM to 7 + HW_REG_OVERRIDE(0x4038,0x0037), + // Synth: Set Fref to 4 MHz + (uint32_t)0x000684A3, + // Synth: Configure fine calibration setting + HW_REG_OVERRIDE(0x4020,0x7F00), + // Synth: Configure fine calibration setting + HW_REG_OVERRIDE(0x4064,0x0040), + // Synth: Configure fine calibration setting + (uint32_t)0xB1070503, + // Synth: Configure fine calibration setting + (uint32_t)0x05330523, + // Synth: Set loop bandwidth after lock to 20 kHz + (uint32_t)0x0A480583, + // Synth: Set loop bandwidth after lock to 20 kHz + (uint32_t)0x7AB80603, + // Synth: Configure VCO LDO (in ADI1, set VCOLDOCFG=0x9F to use voltage input reference) + ADI_REG_OVERRIDE(1,4,0x9F), + // Synth: Configure synth LDO (in ADI1, set SLDOCTL0.COMP_CAP=1) + ADI_HALFREG_OVERRIDE(1,7,0x4,0x4), + // Synth: Use 24 MHz XOSC as synth clock, enable extra PLL filtering + (uint32_t)0x02010403, + // Synth: Configure extra PLL filtering + (uint32_t)0x00108463, + // Synth: Increase synth programming timeout (0x04B0 RAT ticks = 300 us) + (uint32_t)0x04B00243, + // override_synth_disable_bias_div5.xml + // Synth: Set divider bias to disabled + HW32_ARRAY_OVERRIDE(0x405C,1), + // Synth: Set divider bias to disabled (specific for loDivider=5) + (uint32_t)0x18000200, + // override_phy_rx_aaf_bw_0xd.xml + // Rx: Set anti-aliasing filter bandwidth to 0xD (in ADI0, set IFAMPCTL3[7:4]=0xD) + ADI_HALFREG_OVERRIDE(0,61,0xF,0xD), + // override_phy_gfsk_rx.xml + // Rx: Set LNA bias current trim offset to 3 + (uint32_t)0x00038883, + // Rx: Freeze RSSI on sync found event + HW_REG_OVERRIDE(0x6084,0x35F1), + // Tx: Configure PA ramping setting (0x61). Rx: Set AGC reference level to 0x1F + HW_REG_OVERRIDE(0x6088,0x611F), + // Tx: Configure PA ramping setting and setting AGC settle wait = 21 samples + HW_REG_OVERRIDE(0x608C,0x8112), + // Rx: Set RSSI offset to adjust reported RSSI by +7 dB + (uint32_t)0x00F988A3, + // TX power override + // Tx: Set PA trim to max (in ADI0, set PACTL0=0xF8) + ADI_REG_OVERRIDE(0,12,0xF8), + // Set AGC win size = 7 samples + HW_REG_OVERRIDE(0x6064,0x1306), + (uint32_t)0xFFFFFFFF, +}; + +// CMD_PROP_RADIO_DIV_SETUP +rfc_CMD_PROP_RADIO_DIV_SETUP_t RF_cmdPropRadioDivSetup = +{ + .commandNo = 0x3807, + .status = 0x0000, + .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .startTime = 0x00000000, + .startTrigger.triggerType = 0x0, + .startTrigger.bEnaCmd = 0x0, + .startTrigger.triggerNo = 0x0, + .startTrigger.pastTrig = 0x0, + .condition.rule = 0x1, + .condition.nSkip = 0x0, + .modulation.modType = 0x0, + .modulation.deviation = 0xC8, + .symbolRate.preScale = 0xF, + .symbolRate.rateWord = 0x53E3, + .rxBw = 0x27, // S2-mode (for S1-mode, rxBw = 0x29) + .preamConf.nPreamBytes = 0x3, + .preamConf.preamMode = 0x0, + .formatConf.nSwBits = 0x18, + .formatConf.bBitReversal = 0x0, + .formatConf.bMsbFirst = 0x1, + .formatConf.fecMode = 0x0a, // manchester coding + .formatConf.whitenMode = 0x0, + .config.frontEndMode = 0x0, + .config.biasMode = 0x1, + .config.analogCfgMode = 0x0, + .config.bNoFsPowerUp = 0x0, + .txPower = 0xA73F, + .pRegOverride = pOverrides, + .centerFreq = 0x0364, + .intFreq = 0x8000, + .loDivider = 0x05, +}; + +// CMD_FS +rfc_CMD_FS_t RF_cmdFs = +{ + .commandNo = 0x0803, + .status = 0x0000, + .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .startTime = 0x00000000, + .startTrigger.triggerType = 0x0, + .startTrigger.bEnaCmd = 0x0, + .startTrigger.triggerNo = 0x0, + .startTrigger.pastTrig = 0x0, + .condition.rule = 0x1, + .condition.nSkip = 0x0, + .frequency = 0x0364, + .fractFreq = 0x4CCD, + .synthConf.bTxMode = 0x0, + .synthConf.refFreq = 0x0, + .__dummy0 = 0x00, + .__dummy1 = 0x00, + .__dummy2 = 0x00, + .__dummy3 = 0x0000, +}; + +// CMD_PROP_TX +rfc_CMD_PROP_TX_t RF_cmdPropTx = +{ + .commandNo = 0x3801, + .status = 0x0000, + .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .startTime = 0x00000000, + .startTrigger.triggerType = 0x0, + .startTrigger.bEnaCmd = 0x0, + .startTrigger.triggerNo = 0x0, + .startTrigger.pastTrig = 0x0, + .condition.rule = 0x1, + .condition.nSkip = 0x0, + .pktConf.bFsOff = 0x0, + .pktConf.bUseCrc = 0x1, + .pktConf.bVarLen = 0x1, + .pktLen = 0x14, // SET APPLICATION PAYLOAD LENGTH + .syncWord = 0x547696, + .pPkt = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx +}; + +// CMD_PROP_RX +rfc_CMD_PROP_RX_t RF_cmdPropRx = +{ + .commandNo = 0x3802, + .status = 0x0000, + .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .startTime = 0x00000000, + .startTrigger.triggerType = 0x0, + .startTrigger.bEnaCmd = 0x0, + .startTrigger.triggerNo = 0x0, + .startTrigger.pastTrig = 0x0, + .condition.rule = 0x1, + .condition.nSkip = 0x0, + .pktConf.bFsOff = 0x0, + .pktConf.bRepeatOk = 0x0, + .pktConf.bRepeatNok = 0x0, + .pktConf.bUseCrc = 0x1, + .pktConf.bVarLen = 0x1, + .pktConf.bChkAddress = 0x0, + .pktConf.endType = 0x0, + .pktConf.filterOp = 0x0, + .rxConf.bAutoFlushIgnored = 0x0, + .rxConf.bAutoFlushCrcErr = 0x0, + .rxConf.bIncludeHdr = 0x1, + .rxConf.bIncludeCrc = 0x0, + .rxConf.bAppendRssi = 0x0, + .rxConf.bAppendTimestamp = 0x0, + .rxConf.bAppendStatus = 0x1, + .syncWord = 0x547696, + .maxPktLen = 0x80, // MAKE SURE DATA ENTRY IS LARGE ENOUGH + .address0 = 0xAA, + .address1 = 0xBB, + .endTrigger.triggerType = 0x1, + .endTrigger.bEnaCmd = 0x0, + .endTrigger.triggerNo = 0x0, + .endTrigger.pastTrig = 0x0, + .endTime = 0x00000000, + .pQueue = 0, // INSERT APPLICABLE POINTER: (dataQueue_t*)&xxx + .pOutput = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx +}; + +// CMD_PROP_RX_ADV +rfc_CMD_PROP_RX_ADV_t RF_cmdPropRxAdv = +{ + .commandNo = 0x3804, + .status = 0x0000, + .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .startTime = 0x00000000, + .startTrigger.triggerType = 0x0, + .startTrigger.bEnaCmd = 0x0, + .startTrigger.triggerNo = 0x0, + .startTrigger.pastTrig = 0x0, + .condition.rule = 0x1, + .condition.nSkip = 0x0, + .pktConf.bFsOff = 0x0, + .pktConf.bRepeatOk = 0x0, + .pktConf.bRepeatNok = 0x0, + .pktConf.bUseCrc = 0x0, + .pktConf.bCrcIncSw = 0x0, + .pktConf.bCrcIncHdr = 0x1, + .pktConf.endType = 0x0, + .pktConf.filterOp = 0x1, + .rxConf.bAutoFlushIgnored = 0x0, + .rxConf.bAutoFlushCrcErr = 0x0, + .rxConf.bIncludeHdr = 0x1, + .rxConf.bIncludeCrc = 0x0, + .rxConf.bAppendRssi = 0x0, + .rxConf.bAppendTimestamp = 0x0, + .rxConf.bAppendStatus = 0x0, + .syncWord0 = 0x547696, + .syncWord1 = 0, + .maxPktLen = 0, + .hdrConf.numHdrBits = 8, + .hdrConf.lenPos = 0, + .hdrConf.numLenBits = 8, + .addrConf.addrType = 0, + .addrConf.addrSize = 0, + .addrConf.addrPos = 0, + .addrConf.numAddr = 1, + .lenOffset = 0, + .endTrigger.triggerType = 0x1, + .endTrigger.bEnaCmd = 0x0, + .endTrigger.triggerNo = 0x0, + .endTrigger.pastTrig = 0x0, + .endTime = 0x00000000, + .pAddr = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .pQueue = 0, // INSERT APPLICABLE POINTER: (dataQueue_t*)&xxx + .pOutput = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx +}; + +// CMD_TX_TEST +rfc_CMD_TX_TEST_t RF_cmdTxTest = +{ + .commandNo = 0x0808, + .status = 0x0000, + .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .startTime = 0x00000000, + .startTrigger.triggerType = 0x0, + .startTrigger.bEnaCmd = 0x0, + .startTrigger.triggerNo = 0x0, + .startTrigger.pastTrig = 0x0, + .condition.rule = 0x1, + .condition.nSkip = 0x0, + .config.bUseCw = 0x0, + .config.bFsOff = 0x1, + .config.whitenMode = 0x2, + .__dummy0 = 0x00, + .txWord = 0xABCD, + .__dummy1 = 0x00, + .endTrigger.triggerType = 0x1, + .endTrigger.bEnaCmd = 0x0, + .endTrigger.triggerNo = 0x0, + .endTrigger.pastTrig = 0x0, + .syncWord = 0x930B51DE, + .endTime = 0x00000000, +}; diff --git a/examples/knx-cc1310/smartrf_settings/smartrf_settings.h b/examples/knx-cc1310/smartrf_settings/smartrf_settings.h new file mode 100644 index 0000000..debf273 --- /dev/null +++ b/examples/knx-cc1310/smartrf_settings/smartrf_settings.h @@ -0,0 +1,28 @@ +#ifndef _SMARTRF_SETTINGS_H_ +#define _SMARTRF_SETTINGS_H_ + +//********************************************************************************* +// Generated by SmartRF Studio version 2.9.0 (build #168) +// Tested for SimpleLink SDK version: CC13x0 SDK 2.10.xx.xx +// Device: CC1310 Rev. 2.1 +// +//********************************************************************************* + +#include +#include DeviceFamily_constructPath(driverlib/rf_mailbox.h) +#include DeviceFamily_constructPath(driverlib/rf_common_cmd.h) +#include DeviceFamily_constructPath(driverlib/rf_prop_cmd.h) +#include + +// TI-RTOS RF Mode Object +extern RF_Mode RF_prop; + +// RF Core API commands +extern rfc_CMD_PROP_RADIO_DIV_SETUP_t RF_cmdPropRadioDivSetup; +extern rfc_CMD_FS_t RF_cmdFs; +extern rfc_CMD_PROP_TX_t RF_cmdPropTx; +extern rfc_CMD_PROP_RX_t RF_cmdPropRx; +extern rfc_CMD_PROP_RX_ADV_t RF_pCmdPropRxAdv; +extern rfc_CMD_TX_TEST_t RF_cmdTxTest; + +#endif // _SMARTRF_SETTINGS_H_ diff --git a/src/knx/rf_physical_layer_cc1310.cpp b/src/knx/rf_physical_layer_cc1310.cpp new file mode 100644 index 0000000..0de17d9 --- /dev/null +++ b/src/knx/rf_physical_layer_cc1310.cpp @@ -0,0 +1,364 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Copyright(c) 2020 - Matthias Meier */ + + +#include "config.h" +#ifdef USE_RF + +#include +#include +#include + +#include "rf_physical_layer.h" +#include "rf_data_link_layer.h" + +#include "smartrf_settings/smartrf_settings.h" +#include +#include "RFQueue.h" + +#include "cc1310_platform.h" +#include "Board.h" + +#include "bits.h" +#include "platform.h" + +static RF_Object rfObject; +RF_Handle rfHandle; +RF_CmdHandle rxCommandHandle; +uint8_t lastRssi; + +#define DATA_ENTRY_HEADER_SIZE 12 /* Constant header size of a Generic Data Entry - 12 for DATA_ENTRY_TYPE_PARTIAL, 8 for DATA_ENTRY_TYPE_GEN */ +#define MAX_LENGTH 255 +#define LENGTH_POSITION 0 +#define APPENDED_BYTES 10 + +static uint8_t rxDataEntryBuffer[DATA_ENTRY_HEADER_SIZE + MAX_LENGTH + LENGTH_POSITION + APPENDED_BYTES] __attribute__((aligned(4))); +rfc_dataEntryPartial_t* partialReadEntry = (rfc_dataEntryPartial_t*)&rxDataEntryBuffer; + +static dataQueue_t dataQueue; +static rfc_dataEntryGeneral_t* currentDataEntry; +static uint8_t packetLength; +static uint8_t* packetDataPointer; + +volatile static int frags; +volatile static int rf_done, rf_err, err; +int8_t len1, len2; + +rfc_propRxOutput_t rxStatistics; +static volatile uint8_t lengthWritten = false; +static volatile uint8_t rxDone = false; +int32_t packetStartTime; + +#define PACKET_SIZE(lField) ((((lField - 10 /*size of first pkt*/))/16 + 2 /*CRC in first pkt */) * 2 /*to bytes*/ +lField + 1 /*size of len byte*/) + +void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e) +{ + frags++; + if (e & RF_EventNDataWritten && frags==1) + { + //uint16_t block1Crc = rxDataEntryBuffer[DATA_ENTRY_HEADER_SIZE + 10] << 8 | rxDataEntryBuffer[DATA_ENTRY_HEADER_SIZE + 11]; + if ((rxDataEntryBuffer[DATA_ENTRY_HEADER_SIZE + 1] != 0x44) || (rxDataEntryBuffer[DATA_ENTRY_HEADER_SIZE + 2] != 0xFF) /*|| (crc16(&rxDataEntryBuffer[DATA_ENTRY_HEADER_SIZE], 0, 10) != block1Crc)*/) { + // cancel because not a KNX package ID + RF_cancelCmd(rfHandle, rxCommandHandle, 0 /*stop gracefully*/); + return; + } + uint8_t len = rxDataEntryBuffer[DATA_ENTRY_HEADER_SIZE + 0]; + struct rfc_CMD_PROP_SET_LEN_s RF_cmdPropSetLen = + { + .commandNo = CMD_PROP_SET_LEN, // command identifier + .rxLen = (uint16_t)PACKET_SIZE(len) + }; + + len1=len; len2 = RF_cmdPropSetLen.rxLen; + //RF_cmdPropSetLen.rxLen = 40; + RF_runDirectCmd(rfHandle, (uint32_t)&RF_cmdPropSetLen); + packetStartTime = millis(); + } + else if (e & (RF_EventLastCmdDone | RF_EventCmdStopped | RF_EventCmdAborted | RF_EventCmdCancelled)) { + rf_done = true; + rf_err = e & (RF_EventCmdStopped | RF_EventCmdAborted | RF_EventCmdCancelled); + } + + else /* unknown reason - should not occure */ + { + partialReadEntry->status = DATA_ENTRY_PENDING; + err++; + } +} + + +RfPhysicalLayer::RfPhysicalLayer(RfDataLinkLayer& rfDataLinkLayer, Platform& platform) + : _rfDataLinkLayer(rfDataLinkLayer), + _platform(platform) +{ +} + +void RfPhysicalLayer::setOutputPowerLevel(int8_t dBm) +{ +/* + * TODO: Complete if needed + * Refs: + * https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz/f/156/t/540423?How-to-dynamically-change-tx-power-from-10dBm-to-14dBm-when-running-CC1310-application- + * https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz/f/156/t/561302?Increase-TX-power-of-cc1310-stepwise + * Power Table from Conticki: + const prop_mode_tx_power_config_t prop_mode_tx_power_779_930[] = { + { 14, 0xa73f }, // needs CCFG_FORCE_VDDR_HH=1 + { 13, 0xa63f }, // 12.5dB + { 12, 0xb818 }, + { 11, 0x50da }, + { 10, 0x38d3 }, + { 9, 0x2ccd }, + { 8, 0x24cb }, + { 7, 0x20c9 }, + { 6, 0x1cc7 }, + { 5, 0x18c6 }, + { 4, 0x18c5 }, + { 3, 0x14c4 }, + { 2, 0x1042 }, + { 1, 0x10c3 }, + { 0, 0x0041 }, + { -10, 0x08c0 }, + {-128, 0xFFFF }, + }; + * code to change power to eg. 14dBm : + * RF_yield(rfHandle); + * RF_cmdPropRadioDivSetup.txPower = 0xA73F; + * RF_EventMask result = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx, RF_PriorityNormal, NULL, 0); + */ +} + + +bool RfPhysicalLayer::InitChip() +{ + RF_Params rfParams; + RF_Params_init(&rfParams); + + if( RFQueue_defineQueue(&dataQueue, + rxDataEntryBuffer, + sizeof(rxDataEntryBuffer), + 1, + MAX_LENGTH + APPENDED_BYTES)) + { + println("Failed to allocate space for all data entries"); + while(1); + } + + partialReadEntry->length = 255; + partialReadEntry->config.type = DATA_ENTRY_TYPE_PARTIAL; // --> DATA_ENTRY_TYPE_PARTIAL adds a 12 Byte Header + partialReadEntry-> config.irqIntv = 12; + partialReadEntry-> config.lenSz = 0; // len field not handeld by rf core because it does not include CRC bytes + partialReadEntry->status = DATA_ENTRY_PENDING; + partialReadEntry->pNextEntry = (uint8_t*)partialReadEntry; + + dataQueue.pCurrEntry = (uint8_t*)partialReadEntry; + dataQueue.pLastEntry = NULL; + + /* 4.1. Modify CMD_PROP_RX command for application needs */ + RF_cmdPropRx.pQueue = &dataQueue; /* Set the Data Entity queue for received data */ + RF_cmdPropRx.maxPktLen = 0; /* Unlimited length */ + RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 0x0; /* Auto-flush packets with invalid CRC */ + RF_cmdPropRx.pktConf.bRepeatNok = 0x0; /* Exit RX after a packet is recived */ + RF_cmdPropRx.pktConf.bRepeatOk = 0x0; /* Exit RX after a packet is recived */ + RF_cmdPropRx.pOutput = (uint8_t*)&rxStatistics; + + /* Request access to the radio */ + rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams); + + /* Set the frequency */ + RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0); + return true; +} + +void RfPhysicalLayer::stopChip() +{ + RF_cancelCmd(rfHandle, rxCommandHandle, 0 /*stop RF abort */); + RF_pendCmd(rfHandle, rxCommandHandle, 0); + RF_yield(rfHandle); +} + + +uint16_t RfPhysicalLayer::packetSize (uint8_t lField) +{ + uint16_t nrBytes; + uint8_t nrBlocks; + + // The 2 first blocks contains 25 bytes when excluding CRC and the L-field + // The other blocks contains 16 bytes when excluding the CRC-fields + // Less than 26 (15 + 10) + if ( lField < 26 ) + nrBlocks = 2; + else + nrBlocks = (((lField - 26) / 16) + 3); + + // Add all extra fields, excluding the CRC fields + nrBytes = lField + 1; + + // Add the CRC fields, each block has 2 CRC bytes + nrBytes += (2 * nrBlocks); + return nrBytes; +} + +void RfPhysicalLayer::loop() +{ + static uint8_t lastRxOk; + + switch (_loopState) + { + case TX_START: + { + println("TX_START..."); + _rfDataLinkLayer.loadNextTxFrame(&sendBuffer, &sendBufferLength); + pktLen = PACKET_SIZE(sendBuffer[0]); + //pktLen = packetSize(sendBuffer[0]); + if (PACKET_SIZE(sendBuffer[0]) != packetSize(sendBuffer[0]) || PACKET_SIZE(sendBuffer[0]) != sendBufferLength) + { + printf("Error: SendBuffer[0]=%d, SendBufferLength=%d PACKET_SIZE=%d, packetSize=%d\n", sendBuffer[0], sendBufferLength, PACKET_SIZE(sendBuffer[0]), packetSize(sendBuffer[0])); + } + // Calculate total number of bytes in the KNX RF packet from L-field + // Check for valid length + if ((pktLen == 0) || (pktLen > 290)) + { + println("TX packet length error!"); + break; + } + + if (pktLen > 255) + { + println("Unhandled: TX packet > 255"); + break; + } + + RF_cmdPropTx.pktLen = pktLen; + RF_cmdPropTx.pPkt = sendBuffer; + RF_cmdPropTx.startTrigger.triggerType = TRIG_NOW; + RF_EventMask res = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx, RF_PriorityNormal, NULL, 0); + + delete sendBuffer; + + if (res != RF_EventLastCmdDone) + { + printf("Unexpected result command %llu\n", res); + } + + _loopState = RX_START; + } + break; + + case RX_START: + { + //print("RX_START...\n"); + frags = 0; + rf_done = rf_err = false; + err = 0; + lastRxOk = rxStatistics.nRxOk; + //RF_EventMask res = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx, RF_PriorityNormal, &callback, IRQ_RX_N_DATA_WRITTEN); + + rxCommandHandle = RF_postCmd(rfHandle, (RF_Op*)&RF_cmdPropRx, RF_PriorityNormal, &callback, IRQ_RX_N_DATA_WRITTEN); + if (rxCommandHandle == RF_ALLOC_ERROR) + { + println("Error: nRF_pendCmd() failed"); + return; + } + _loopState = RX_ACTIVE; + } + break; + + case RX_ACTIVE: + { + if (!_rfDataLinkLayer.isTxQueueEmpty() && !syncStart) + { + RF_cancelCmd(rfHandle, rxCommandHandle, 1 /*stop gracefully*/); + RF_pendCmd(rfHandle, rxCommandHandle, 0); + RFQueue_nextEntry(); + _loopState = TX_START; + break; + } + + // Check if we have an incomplete packet reception + if (!rf_done && syncStart && (millis() - packetStartTime > RX_PACKET_TIMEOUT)) + { + println("RX packet timeout!"); + RF_cancelCmd(rfHandle, rxCommandHandle, 1 /*stop gracefully*/); + RF_pendCmd(rfHandle, rxCommandHandle, 0); + RFQueue_nextEntry(); + _loopState = RX_START; + break; + } + else if (rf_done) + { + RF_EventMask res = RF_pendCmd(rfHandle, rxCommandHandle, 0); +/* + if (rxStatistics.nRxOk == lastRxOk) + { + println("Rx empty or invalid"); + } + else if ( rxStatistics.lastRssi < -120) + { + println("\nIgnoring Rx with rssi < -120dB"); + } +*/ + if (res == RF_EventCmdCancelled || res == RF_EventCmdStopped || res == RF_EventCmdAborted) + { + println("RF terminated because of RF_flushCmd() or RF_cancelCmd()"); + } + else if (res != RF_EventLastCmdDone) + { + printf("Unexpected Rx result command %llu\n", res); + } + else if (rf_err) + { + print("Rx is no KNX frame\n\n"); + } + else + { + printf("len1=%d, len1=%d, frags=%d, err=%d\n", len1, len2, frags, err); + printf("nRxOk = %d ", rxStatistics.nRxOk); //!< Number of packets that have been received with payload, CRC OK and not ignored + printf("nRxNok = %d ", rxStatistics.nRxNok); //!< Number of packets that have been received with CRC error + printf("nRxIgnored = %d ", rxStatistics.nRxIgnored); //!< Number of packets that have been received with CRC OK and ignored due to address mismatch + printf("nRxStopped = %d ", rxStatistics.nRxStopped); //!< Number of packets not received due to illegal length or address mismatch with pktConf.filterOp = 1 + printf("nRxBufFull = %d ", rxStatistics.nRxBufFull); //!< Number of packets that have been received and discarded due to lack of buffer space + printf("lastRssi = %d\n", rxStatistics.lastRssi); //!< RSSI of last received packet + + currentDataEntry = RFQueue_getDataEntry(); + /* Handle the packet data, located at ¤tDataEntry->data: + * For unknown reason, there is a header of 4 bytes whereas nr bytes rececived +1 is at offset [2]*/ + packetLength = *(uint8_t *)(&(currentDataEntry->data)+4); + + if (PACKET_SIZE(packetLength) != PACKET_SIZE(packetLength)) + printf("Error RX: packetLength=%d PACKET_SIZE=%d, packetSize=%d\n", packetLength, PACKET_SIZE(packetLength), packetSize(packetLength)); + + packetLength = PACKET_SIZE(packetLength); // add CRC size + packetDataPointer = (uint8_t *)(&(currentDataEntry->data)+4); + + if (packetLength+1 != *(uint8_t *)(&(currentDataEntry->data)+2)) { + printf("Size mismatch: %d %d\n", packetLength, *(uint8_t *)(&(currentDataEntry->data)+2)); + printf("Data Start: 0x%x 0x%x 0x%x\n", packetDataPointer[0], packetDataPointer[1], packetDataPointer[2]); + } + + lastRssi = rxStatistics.lastRssi; // TODO: save rssi only if frame was addressed to this node + printHex("RX: ", packetDataPointer, packetLength); + _rfDataLinkLayer.frameBytesReceived(packetDataPointer, packetLength); + RFQueue_nextEntry(); + } + _loopState = RX_START; + } + } + break; + } +} + +#endif