From a44c069769118a7e1b59b1dc22abc3c831c75485 Mon Sep 17 00:00:00 2001 From: Tao Gong Date: Thu, 1 Nov 2018 09:30:19 -0400 Subject: [PATCH] Initial working version --- .ccsproject | 17 + .cproject | 259 +++++++ .launches/empty_min_CC2650STK_TI.launch | 16 + .launches/sniffer_CC2650STK.launch | 19 + .project | 28 + .settings/org.eclipse.cdt.codan.core.prefs | 3 + .settings/org.eclipse.cdt.debug.core.prefs | 2 + .settings/org.eclipse.core.resources.prefs | 27 + .xdchelp | 0 Board.h | 93 +++ CC2650STK.c | 647 ++++++++++++++++++ CC2650STK.cmd | 100 +++ CC2650STK.h | 326 +++++++++ Debug/.gitignore | 6 + DeviceFamily.h | 211 ++++++ README.html | 71 ++ README.md | 48 ++ Release/.gitignore | 6 + ccfg.c | 51 ++ makefile.defs | 24 + release.cfg | 638 +++++++++++++++++ rf_ieee_cmd.h | 631 +++++++++++++++++ source/command_handler.c | 384 +++++++++++ source/command_handler.h | 82 +++ source/command_packet.h | 128 ++++ source/common.c | 76 ++ source/common.h | 95 +++ source/control_task.c | 303 ++++++++ source/control_task.h | 58 ++ source/data_packet.h | 75 ++ source/data_task.c | 197 ++++++ source/data_task.h | 49 ++ source/general_packet.h | 73 ++ source/host_if.c | 107 +++ source/host_if.h | 76 ++ source/host_if_task.c | 64 ++ source/host_if_task.h | 48 ++ source/packet_handler.c | 184 +++++ source/packet_handler.h | 78 +++ source/packet_queue.c | 154 +++++ source/packet_queue.h | 91 +++ source/packet_sniffer_fw.c | 103 +++ source/phy/devices/cc26x0lp/phy_io_config.c | 51 ++ source/phy/devices/cc26x0lp/phy_tables.c | 97 +++ source/phy/phy_if_ieee_15_4.c | 96 +++ source/phy/phy_if_ieee_15_4.h | 91 +++ source/phy/phy_if_prop.c | 103 +++ source/phy/phy_if_prop.h | 91 +++ source/phy/phy_if_prop_15_4g.c | 106 +++ source/phy/phy_if_prop_15_4g.h | 91 +++ source/phy/phy_io_config.h | 56 ++ source/phy/phy_manager.c | 277 ++++++++ source/phy/phy_manager.h | 125 ++++ source/phy/phy_rf_api.h | 53 ++ source/phy/phy_tables.h | 141 ++++ source/radio_if.c | 296 ++++++++ source/radio_if.h | 163 +++++ source/radio_if_dataqueue.c | 281 ++++++++ source/radio_if_dataqueue.h | 93 +++ .../cc26x0lp/15.4/smartrf_settings_15_4_0.c | 219 ++++++ .../cc26x0lp/15.4/smartrf_settings_15_4_0.h | 30 + source/task_event.c | 69 ++ source/task_event.h | 53 ++ source/test_command_handler.c | 202 ++++++ source/test_command_handler.h | 59 ++ source/timestamp.c | 221 ++++++ source/timestamp.h | 76 ++ source/user_if_task.c | 109 +++ source/user_if_task.h | 50 ++ source/version.h | 47 ++ src/.exclude | 1 + src/makefile.libs | 62 ++ src/sysbios/m3_Hwi_asm.obj | Bin 0 -> 15108 bytes src/sysbios/m3_Hwi_asm_switch.obj | Bin 0 -> 9260 bytes src/sysbios/m3_IntrinsicsSupport_asm.obj | Bin 0 -> 3064 bytes src/sysbios/m3_TaskSupport_asm.obj | Bin 0 -> 6512 bytes src/sysbios/makefile | 107 +++ src/sysbios/rom_sysbios.aem3 | Bin 0 -> 3393974 bytes src/sysbios/rom_sysbios.obj | Bin 0 -> 1704536 bytes targetConfigs/CC2650F128.ccxml | 30 + targetConfigs/readme.txt | 9 + 81 files changed, 9203 insertions(+) create mode 100644 .ccsproject create mode 100644 .cproject create mode 100644 .launches/empty_min_CC2650STK_TI.launch create mode 100644 .launches/sniffer_CC2650STK.launch create mode 100644 .project create mode 100644 .settings/org.eclipse.cdt.codan.core.prefs create mode 100644 .settings/org.eclipse.cdt.debug.core.prefs create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .xdchelp create mode 100644 Board.h create mode 100644 CC2650STK.c create mode 100644 CC2650STK.cmd create mode 100644 CC2650STK.h create mode 100644 Debug/.gitignore create mode 100644 DeviceFamily.h create mode 100644 README.html create mode 100644 README.md create mode 100644 Release/.gitignore create mode 100644 ccfg.c create mode 100644 makefile.defs create mode 100644 release.cfg create mode 100644 rf_ieee_cmd.h create mode 100644 source/command_handler.c create mode 100644 source/command_handler.h create mode 100644 source/command_packet.h create mode 100644 source/common.c create mode 100644 source/common.h create mode 100644 source/control_task.c create mode 100644 source/control_task.h create mode 100644 source/data_packet.h create mode 100644 source/data_task.c create mode 100644 source/data_task.h create mode 100644 source/general_packet.h create mode 100644 source/host_if.c create mode 100644 source/host_if.h create mode 100644 source/host_if_task.c create mode 100644 source/host_if_task.h create mode 100644 source/packet_handler.c create mode 100644 source/packet_handler.h create mode 100644 source/packet_queue.c create mode 100644 source/packet_queue.h create mode 100644 source/packet_sniffer_fw.c create mode 100644 source/phy/devices/cc26x0lp/phy_io_config.c create mode 100644 source/phy/devices/cc26x0lp/phy_tables.c create mode 100644 source/phy/phy_if_ieee_15_4.c create mode 100644 source/phy/phy_if_ieee_15_4.h create mode 100644 source/phy/phy_if_prop.c create mode 100644 source/phy/phy_if_prop.h create mode 100644 source/phy/phy_if_prop_15_4g.c create mode 100644 source/phy/phy_if_prop_15_4g.h create mode 100644 source/phy/phy_io_config.h create mode 100644 source/phy/phy_manager.c create mode 100644 source/phy/phy_manager.h create mode 100644 source/phy/phy_rf_api.h create mode 100644 source/phy/phy_tables.h create mode 100644 source/radio_if.c create mode 100644 source/radio_if.h create mode 100644 source/radio_if_dataqueue.c create mode 100644 source/radio_if_dataqueue.h create mode 100644 source/smartrf_settings/cc26x0lp/15.4/smartrf_settings_15_4_0.c create mode 100644 source/smartrf_settings/cc26x0lp/15.4/smartrf_settings_15_4_0.h create mode 100644 source/task_event.c create mode 100644 source/task_event.h create mode 100644 source/test_command_handler.c create mode 100644 source/test_command_handler.h create mode 100644 source/timestamp.c create mode 100644 source/timestamp.h create mode 100644 source/user_if_task.c create mode 100644 source/user_if_task.h create mode 100644 source/version.h create mode 100644 src/.exclude create mode 100644 src/makefile.libs create mode 100644 src/sysbios/m3_Hwi_asm.obj create mode 100644 src/sysbios/m3_Hwi_asm_switch.obj create mode 100644 src/sysbios/m3_IntrinsicsSupport_asm.obj create mode 100644 src/sysbios/m3_TaskSupport_asm.obj create mode 100644 src/sysbios/makefile create mode 100644 src/sysbios/rom_sysbios.aem3 create mode 100644 src/sysbios/rom_sysbios.obj create mode 100644 targetConfigs/CC2650F128.ccxml create mode 100644 targetConfigs/readme.txt diff --git a/.ccsproject b/.ccsproject new file mode 100644 index 0000000..ed5c90f --- /dev/null +++ b/.ccsproject @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..8f00122 --- /dev/null +++ b/.cproject @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.launches/empty_min_CC2650STK_TI.launch b/.launches/empty_min_CC2650STK_TI.launch new file mode 100644 index 0000000..6021a6b --- /dev/null +++ b/.launches/empty_min_CC2650STK_TI.launch @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/.launches/sniffer_CC2650STK.launch b/.launches/sniffer_CC2650STK.launch new file mode 100644 index 0000000..1610807 --- /dev/null +++ b/.launches/sniffer_CC2650STK.launch @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..9edb910 --- /dev/null +++ b/.project @@ -0,0 +1,28 @@ + + + sniffer_CC2650STK + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.rtsc.xdctools.buildDefinitions.XDC.xdcNature + com.ti.ccstudio.core.ccsNature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/.settings/org.eclipse.cdt.codan.core.prefs b/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 0000000..98b6350 --- /dev/null +++ b/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +inEditor=false +onBuild=false diff --git a/.settings/org.eclipse.cdt.debug.core.prefs b/.settings/org.eclipse.cdt.debug.core.prefs new file mode 100644 index 0000000..58d4fb2 --- /dev/null +++ b/.settings/org.eclipse.cdt.debug.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.cdt.debug.core.toggleBreakpointModel=com.ti.ccstudio.debug.CCSBreakpointMarker diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..9f85c7a --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,27 @@ +eclipse.preferences.version=1 +encoding//Debug/makefile=UTF-8 +encoding//Debug/objects.mk=UTF-8 +encoding//Debug/source/phy/devices/cc26x0lp/subdir_rules.mk=UTF-8 +encoding//Debug/source/phy/devices/cc26x0lp/subdir_vars.mk=UTF-8 +encoding//Debug/source/phy/subdir_rules.mk=UTF-8 +encoding//Debug/source/phy/subdir_vars.mk=UTF-8 +encoding//Debug/source/smartrf_settings/cc26x0lp/15.4/subdir_rules.mk=UTF-8 +encoding//Debug/source/smartrf_settings/cc26x0lp/15.4/subdir_vars.mk=UTF-8 +encoding//Debug/source/subdir_rules.mk=UTF-8 +encoding//Debug/source/subdir_vars.mk=UTF-8 +encoding//Debug/sources.mk=UTF-8 +encoding//Debug/subdir_rules.mk=UTF-8 +encoding//Debug/subdir_vars.mk=UTF-8 +encoding//Release/makefile=UTF-8 +encoding//Release/objects.mk=UTF-8 +encoding//Release/source/phy/devices/cc26x0lp/subdir_rules.mk=UTF-8 +encoding//Release/source/phy/devices/cc26x0lp/subdir_vars.mk=UTF-8 +encoding//Release/source/phy/subdir_rules.mk=UTF-8 +encoding//Release/source/phy/subdir_vars.mk=UTF-8 +encoding//Release/source/smartrf_settings/cc26x0lp/15.4/subdir_rules.mk=UTF-8 +encoding//Release/source/smartrf_settings/cc26x0lp/15.4/subdir_vars.mk=UTF-8 +encoding//Release/source/subdir_rules.mk=UTF-8 +encoding//Release/source/subdir_vars.mk=UTF-8 +encoding//Release/sources.mk=UTF-8 +encoding//Release/subdir_rules.mk=UTF-8 +encoding//Release/subdir_vars.mk=UTF-8 diff --git a/.xdchelp b/.xdchelp new file mode 100644 index 0000000..e69de29 diff --git a/Board.h b/Board.h new file mode 100644 index 0000000..764c94d --- /dev/null +++ b/Board.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015-2016, Texas Instruments Incorporated + * All rights reserved. + * + * 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. + */ + +#ifndef __BOARD_H +#define __BOARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "CC2650STK.h" + + +#define Board_PIN_LED1 Board_STK_LED1 + +/* These #defines allow us to reuse TI-RTOS across other device families */ +#define Board_LED1 Board_STK_LED1 +#define Board_LED2 Board_STK_LED2 +#define Board_LED0 Board_LED2 + +#define Board_BUTTON0 Board_KEY_LEFT +#define Board_BUTTON1 Board_KEY_RIGHT + +#define Board_I2C0 Board_I2C +#define Board_I2C_TMP Board_I2C0 +#define Board_UART0 Board_UART +#define Board_AES0 Board_AES +#define Board_WATCHDOG0 CC2650STK_WATCHDOG0 + +#define Board_initGeneral() { \ + Power_init(); \ + if (PIN_init(BoardGpioInitTable) != PIN_SUCCESS) \ + {System_abort("Error with PIN_init\n"); \ + } \ +} + +#define Board_initGPIO() +#define Board_initPWM() PWM_init() +#define Board_initI2C() I2C_init() +#define Board_initSPI() SPI_init() +#define Board_initUART() UART_init() +#define Board_initWatchdog() Watchdog_init() +#define GPIO_toggle(n) +#define GPIO_write(n,m) + +/* Board specific I2C addresses */ + +/* Interface #0 */ +#define Board_HDC1000_ADDR (0x43) +#define Board_TMP007_ADDR (0x44) +#define Board_OPT3001_ADDR (0x45) +#define Board_BMP280_ADDR (0x77) + +/* Interface #1 */ +#define Board_MPU9250_ADDR (0x68) +#define Board_MPU9250_MAG_ADDR (0x0C) + +#ifdef __cplusplus +} +#endif + +#endif /* __BOARD_H */ diff --git a/CC2650STK.c b/CC2650STK.c new file mode 100644 index 0000000..5e3da59 --- /dev/null +++ b/CC2650STK.c @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2015-2016, Texas Instruments Incorporated + * All rights reserved. + * + * 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. + */ + +/* + * ====================== CC2650STK.c ========================================= + * This file is responsible for setting up the board specific items for the + * CC2650 SensorTag. + */ + + +/* + * ====================== Includes ============================================ + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* + * ========================= IO driver initialization ========================= + * From main, PIN_init(BoardGpioInitTable) should be called to setup safe + * settings for this board. + * When a pin is allocated and then de-allocated, it will revert to the state + * configured in this table. + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(BoardGpioInitTable, ".const:BoardGpioInitTable") +#pragma DATA_SECTION(PINCC26XX_hwAttrs, ".const:PINCC26XX_hwAttrs") +#endif + +const PIN_Config BoardGpioInitTable[] = { + + Board_STK_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* LED initially off */ + Board_STK_LED2 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* LED initially off */ + Board_KEY_LEFT | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_BOTHEDGES | PIN_HYSTERESIS, /* Button is active low */ + Board_KEY_RIGHT | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_BOTHEDGES | PIN_HYSTERESIS, /* Button is active low */ + Board_RELAY | PIN_INPUT_EN | PIN_PULLDOWN | PIN_IRQ_BOTHEDGES | PIN_HYSTERESIS, /* Relay is active high */ + Board_MPU_INT | PIN_INPUT_EN | PIN_PULLDOWN | PIN_IRQ_NEGEDGE | PIN_HYSTERESIS, /* MPU_INT is active low */ + Board_TMP_RDY | PIN_INPUT_EN | PIN_PULLUP | PIN_HYSTERESIS, /* TMP_RDY is active high */ + Board_BUZZER | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* Buzzer initially off */ + Board_MPU_POWER | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* MPU initially on */ + Board_MIC_POWER | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MIN, /* MIC initially off */ + Board_SPI_FLASH_CS | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MIN, /* External flash chip select */ + Board_SPI_DEVPK_CS | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MIN, /* DevPack chip select */ + Board_AUDIO_DI | PIN_INPUT_EN | PIN_PULLDOWN, /* Audio DI */ + Board_AUDIODO | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MIN, /* Audio data out */ + Board_AUDIO_CLK | PIN_INPUT_EN | PIN_PULLDOWN, /* DevPack */ + Board_DP2 | PIN_INPUT_EN | PIN_PULLDOWN, /* DevPack */ + Board_DP1 | PIN_INPUT_EN | PIN_PULLDOWN, /* DevPack */ + Board_DP0 | PIN_INPUT_EN | PIN_PULLDOWN, /* DevPack */ + Board_DP3 | PIN_INPUT_EN | PIN_PULLDOWN, /* DevPack */ + Board_UART_RX | PIN_INPUT_EN | PIN_PULLDOWN, /* DevPack */ + Board_UART_TX | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL, /* DevPack */ + Board_DEVPK_ID | PIN_INPUT_EN | PIN_NOPULL, /* Device pack ID - external PU */ + Board_SPI0_MOSI | PIN_INPUT_EN | PIN_PULLDOWN, /* SPI master out - slave in */ + Board_SPI0_MISO | PIN_INPUT_EN | PIN_PULLDOWN, /* SPI master in - slave out */ + Board_SPI0_CLK | PIN_INPUT_EN | PIN_PULLDOWN, /* SPI clock */ + + PIN_TERMINATE +}; + +const PINCC26XX_HWAttrs PINCC26XX_hwAttrs = { + .intPriority = ~0, + .swiPriority = 0 +}; +/*============================================================================*/ + +/* + * ============================= Power begin ================================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(PowerCC26XX_config, ".const:PowerCC26XX_config") +#endif +const PowerCC26XX_Config PowerCC26XX_config = { + .policyInitFxn = NULL, + .policyFxn = &PowerCC26XX_standbyPolicy, + .calibrateFxn = &PowerCC26XX_calibrate, + .enablePolicy = TRUE, + .calibrateRCOSC_LF = TRUE, + .calibrateRCOSC_HF = TRUE, +}; +/* + * ============================= Power end =================================== + */ + +/* + * ============================= UART begin =================================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(UART_config, ".const:UART_config") +#pragma DATA_SECTION(uartCC26XXHWAttrs, ".const:uartCC26XXHWAttrs") +#endif + +/* Include drivers */ +#include +#include + +/* UART objects */ +UARTCC26XX_Object uartCC26XXObjects[CC2650STK_UARTCOUNT]; +unsigned char uartCC26XXRingBuffer[CC2650STK_UARTCOUNT][32]; + +/* UART hardware parameter structure, also used to assign UART pins */ +const UARTCC26XX_HWAttrsV2 uartCC26XXHWAttrs[CC2650STK_UARTCOUNT] = { + { + .baseAddr = UART0_BASE, + .powerMngrId = PowerCC26XX_PERIPH_UART0, + .intNum = INT_UART0_COMB, + .intPriority = ~0, + .swiPriority = 0, + .txPin = Board_UART_TX, + .rxPin = Board_UART_RX, + .ctsPin = PIN_UNASSIGNED, + .rtsPin = PIN_UNASSIGNED, + .ringBufPtr = uartCC26XXRingBuffer[0], + .ringBufSize = sizeof(uartCC26XXRingBuffer[0]) + } +}; + +/* UART configuration structure */ +const UART_Config UART_config[] = { + { + .fxnTablePtr = &UARTCC26XX_fxnTable, + .object = &uartCC26XXObjects[0], + .hwAttrs = &uartCC26XXHWAttrs[0] + }, + {NULL, NULL, NULL} +}; +/* + * ============================= UART end ===================================== + */ + +/* + * ============================= UDMA begin =================================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(UDMACC26XX_config, ".const:UDMACC26XX_config") +#pragma DATA_SECTION(udmaHWAttrs, ".const:udmaHWAttrs") +#endif + +/* Include drivers */ +#include + +/* UDMA objects */ +UDMACC26XX_Object udmaObjects[CC2650STK_UDMACOUNT]; + +/* UDMA configuration structure */ +const UDMACC26XX_HWAttrs udmaHWAttrs[CC2650STK_UDMACOUNT] = { + { + .baseAddr = UDMA0_BASE, + .powerMngrId = PowerCC26XX_PERIPH_UDMA, + .intNum = INT_DMA_ERR, + .intPriority = ~0 + } +}; + +/* UDMA configuration structure */ +const UDMACC26XX_Config UDMACC26XX_config[] = { + { + .object = &udmaObjects[0], + .hwAttrs = &udmaHWAttrs[0] + }, + {NULL, NULL} +}; +/* + * ============================= UDMA end ===================================== + */ + +/* + * ========================== SPI DMA begin =================================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(SPI_config, ".const:SPI_config") +#pragma DATA_SECTION(spiCC26XXDMAHWAttrs, ".const:spiCC26XXDMAHWAttrs") +#endif + +/* Include drivers */ +#include + +/* SPI objects */ +SPICC26XXDMA_Object spiCC26XXDMAObjects[CC2650STK_SPICOUNT]; + +/* SPI configuration structure, describing which pins are to be used */ +const SPICC26XXDMA_HWAttrsV1 spiCC26XXDMAHWAttrs[CC2650STK_SPICOUNT] = { + { + .baseAddr = SSI0_BASE, + .intNum = INT_SSI0_COMB, + .intPriority = ~0, + .swiPriority = 0, + .powerMngrId = PowerCC26XX_PERIPH_SSI0, + .defaultTxBufValue = 0, + .rxChannelBitMask = 1< + +/* I2C objects */ +I2CCC26XX_Object i2cCC26xxObjects[CC2650STK_I2CCOUNT]; + +/* I2C configuration structure, describing which pins are to be used */ +const I2CCC26XX_HWAttrsV1 i2cCC26xxHWAttrs[CC2650STK_I2CCOUNT] = { + { + .baseAddr = I2C0_BASE, + .powerMngrId = PowerCC26XX_PERIPH_I2C0, + .intNum = INT_I2C_IRQ, + .intPriority = ~0, + .swiPriority = 0, + .sdaPin = Board_I2C0_SDA0, + .sclPin = Board_I2C0_SCL0, + } +}; + +const I2C_Config I2C_config[] = { + { + .fxnTablePtr = &I2CCC26XX_fxnTable, + .object = &i2cCC26xxObjects[0], + .hwAttrs = &i2cCC26xxHWAttrs[0] + }, + {NULL, NULL, NULL} +}; +/* + * ========================== I2C end ========================================= + */ + +/* + * ========================== Crypto begin ==================================== + * NOTE: The Crypto implementation should be considered experimental + * and not validated! + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(CryptoCC26XX_config, ".const:CryptoCC26XX_config") +#pragma DATA_SECTION(cryptoCC26XXHWAttrs, ".const:cryptoCC26XXHWAttrs") +#endif + +/* Include drivers */ +#include + +/* Crypto objects */ +CryptoCC26XX_Object cryptoCC26XXObjects[CC2650STK_CRYPTOCOUNT]; + +/* Crypto configuration structure, describing which pins are to be used */ +const CryptoCC26XX_HWAttrs cryptoCC26XXHWAttrs[CC2650STK_CRYPTOCOUNT] = { + { + .baseAddr = CRYPTO_BASE, + .powerMngrId = PowerCC26XX_PERIPH_CRYPTO, + .intNum = INT_CRYPTO_RESULT_AVAIL_IRQ, + .intPriority = ~0, + } +}; + +/* Crypto configuration structure */ +const CryptoCC26XX_Config CryptoCC26XX_config[] = { + { + .object = &cryptoCC26XXObjects[0], + .hwAttrs = &cryptoCC26XXHWAttrs[0] + }, + {NULL, NULL} +}; +/* + * ========================== Crypto end ====================================== + */ + +/* + * ============================= PDM begin ==================================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(PDMCC26XX_config, ".const:PDMCC26XX_config") +#pragma DATA_SECTION(pdmCC26XXHWAttrs, ".const:pdmCC26XXHWAttrs") +#pragma DATA_SECTION(pdmC26XXI2SHWAttrs, ".const:pdmC26XXI2SHWAttrs") +#pragma DATA_SECTION(PDMCC26XX_I2S_config, ".const:PDMCC26XX_I2S_config") +#endif + +/* Include drivers */ +#include +#include + +/* PDM objects, one for PDM driver, one for PDM/I2S helper file */ +PDMCC26XX_Object pdmCC26XXObjects[CC2650STK_PDMCOUNT]; +PDMCC26XX_I2S_Object pdmCC26XXI2SObjects[CC2650STK_PDMCOUNT]; + +/* PDM driver hardware attributes */ +const PDMCC26XX_HWAttrs pdmCC26XXHWAttrs[CC2650STK_PDMCOUNT] = { + { + .micPower = Board_MIC_POWER, + .taskPriority = 2 + } +}; + +/* PDM configuration structure */ +const PDMCC26XX_Config PDMCC26XX_config[] = { + { + .object = &pdmCC26XXObjects[0], + .hwAttrs = &pdmCC26XXHWAttrs[0] + } +}; + +/* PDM_I2S hardware attributes */ +const PDMCC26XX_I2S_HWAttrs pdmC26XXI2SHWAttrs[CC2650STK_PDMCOUNT] = { + { + .baseAddr = I2S0_BASE, + .intNum = INT_I2S_IRQ, + .powerMngrId = PowerCC26XX_PERIPH_I2S, + .intPriority = ~0, + .mclkPin = PIN_UNASSIGNED, + .bclkPin = Board_AUDIO_CLK, + .wclkPin = PIN_UNASSIGNED, + .ad0Pin = Board_AUDIO_DI, + } +}; + +/* PDM_I2S configuration structure */ +const PDMCC26XX_I2S_Config PDMCC26XX_I2S_config[] = { + { + .object = &pdmCC26XXI2SObjects[0], + .hwAttrs = &pdmC26XXI2SHWAttrs[0] + }, + { NULL, NULL } +}; + +/* + * ============================= PDM end ====================================== + */ + +/* + * ========================= RF driver begin ================================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(RFCC26XX_hwAttrs, ".const:RFCC26XX_hwAttrs") +#endif + +/* Include drivers */ +#include + +/* RF hwi and swi priority */ +const RFCC26XX_HWAttrs RFCC26XX_hwAttrs = { + .hwiCpe0Priority = ~0, + .hwiHwPriority = ~0, + .swiCpe0Priority = 0, + .swiHwPriority = 0, +}; + +/* + * ========================== RF driver end =================================== + */ + +/* + * ========================= Display begin ==================================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(Display_config, ".const:Display_config") +#pragma DATA_SECTION(displaySharpHWattrs, ".const:displaySharpHWattrs") +#pragma DATA_SECTION(displayUartHWAttrs, ".const:displayUartHWAttrs") +#endif + +#include +#include +#include + +/* Structures for UartPlain Blocking */ +DisplayUart_Object displayUartObject; + +#ifndef BOARD_DISPLAY_UART_STRBUF_SIZE +#define BOARD_DISPLAY_UART_STRBUF_SIZE 128 +#endif +static char uartStringBuf[BOARD_DISPLAY_UART_STRBUF_SIZE]; + +const DisplayUart_HWAttrs displayUartHWAttrs = { + .uartIdx = Board_UART, + .baudRate = 115200, + .mutexTimeout = BIOS_WAIT_FOREVER, + .strBuf = uartStringBuf, + .strBufLen = BOARD_DISPLAY_UART_STRBUF_SIZE, +}; + +/* Structures for SHARP */ +DisplaySharp_Object displaySharpObject; + +#ifndef BOARD_DISPLAY_SHARP_SIZE +#define BOARD_DISPLAY_SHARP_SIZE 96 // 96->96x96 is the most common board, alternative is 128->128x128. +#endif +static uint8_t sharpDisplayBuf[BOARD_DISPLAY_SHARP_SIZE * BOARD_DISPLAY_SHARP_SIZE / 8]; + +const DisplaySharp_HWAttrs displaySharpHWattrs = { + .spiIndex = Board_SPI0, + .csPin = Board_LCD_CS, + .extcominPin = Board_LCD_EXTCOMIN, + .powerPin = Board_LCD_POWER, + .enablePin = Board_LCD_ENABLE, + .pixelWidth = BOARD_DISPLAY_SHARP_SIZE, + .pixelHeight = BOARD_DISPLAY_SHARP_SIZE, + .displayBuf = sharpDisplayBuf, +}; + +/* As the pins for UART and Watch Devpack conflict, prefer UART by default */ +#if !defined(BOARD_DISPLAY_EXCLUDE_UART) && !defined(BOARD_DISPLAY_EXCLUDE_LCD) +# define BOARD_DISPLAY_EXCLUDE_LCD +#endif + +/* Array of displays */ +const Display_Config Display_config[] = { +#if !defined(BOARD_DISPLAY_EXCLUDE_UART) + { + .fxnTablePtr = &DisplayUart_fxnTable, + .object = &displayUartObject, + .hwAttrs = &displayUartHWAttrs, + }, +#endif +#if !defined(BOARD_DISPLAY_EXCLUDE_LCD) + { + .fxnTablePtr = &DisplaySharp_fxnTable, + .object = &displaySharpObject, + .hwAttrs = &displaySharpHWattrs + }, +#endif + { NULL, NULL, NULL } // Terminator +}; + +/* + * ========================= Display end ====================================== + */ + +/* + * ============================ GPTimer begin ================================= + * Remove unused entries to reduce flash usage both in Board.c and Board.h + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(GPTimerCC26XX_config, ".const:GPTimerCC26XX_config") +#pragma DATA_SECTION(gptimerCC26xxHWAttrs, ".const:gptimerCC26xxHWAttrs") +#endif + +/* GPTimer hardware attributes, one per timer part (Timer 0A, 0B, 1A, 1B..) */ +const GPTimerCC26XX_HWAttrs gptimerCC26xxHWAttrs[CC2650STK_GPTIMERPARTSCOUNT] = { + { .baseAddr = GPT0_BASE, .intNum = INT_GPT0A, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT0, .pinMux = GPT_PIN_0A, }, + { .baseAddr = GPT0_BASE, .intNum = INT_GPT0B, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT0, .pinMux = GPT_PIN_0B, }, + { .baseAddr = GPT1_BASE, .intNum = INT_GPT1A, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT1, .pinMux = GPT_PIN_1A, }, + { .baseAddr = GPT1_BASE, .intNum = INT_GPT1B, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT1, .pinMux = GPT_PIN_1B, }, + { .baseAddr = GPT2_BASE, .intNum = INT_GPT2A, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT2, .pinMux = GPT_PIN_2A, }, + { .baseAddr = GPT2_BASE, .intNum = INT_GPT2B, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT2, .pinMux = GPT_PIN_2B, }, + { .baseAddr = GPT3_BASE, .intNum = INT_GPT3A, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT3, .pinMux = GPT_PIN_3A, }, + { .baseAddr = GPT3_BASE, .intNum = INT_GPT3B, .intPriority = (~0), .powerMngrId = PowerCC26XX_PERIPH_GPT3, .pinMux = GPT_PIN_3B, }, +}; + +/* GPTimer objects, one per full-width timer (A+B) (Timer 0, Timer 1..) */ +GPTimerCC26XX_Object gptimerCC26XXObjects[CC2650STK_GPTIMERCOUNT]; + +/* GPTimer configuration (used as GPTimer_Handle by driver and application) */ +const GPTimerCC26XX_Config GPTimerCC26XX_config[CC2650STK_GPTIMERPARTSCOUNT] = { + { &gptimerCC26XXObjects[0], &gptimerCC26xxHWAttrs[0], GPT_A }, + { &gptimerCC26XXObjects[0], &gptimerCC26xxHWAttrs[1], GPT_B }, + { &gptimerCC26XXObjects[1], &gptimerCC26xxHWAttrs[2], GPT_A }, + { &gptimerCC26XXObjects[1], &gptimerCC26xxHWAttrs[3], GPT_B }, + { &gptimerCC26XXObjects[2], &gptimerCC26xxHWAttrs[4], GPT_A }, + { &gptimerCC26XXObjects[2], &gptimerCC26xxHWAttrs[5], GPT_B }, + { &gptimerCC26XXObjects[3], &gptimerCC26xxHWAttrs[6], GPT_A }, + { &gptimerCC26XXObjects[3], &gptimerCC26xxHWAttrs[7], GPT_B }, +}; + +/* + * ============================ GPTimer end =================================== + */ + + + +/* + * ============================= PWM begin ==================================== + * Remove unused entries to reduce flash usage both in Board.c and Board.h + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(PWM_config, ".const:PWM_config") +#pragma DATA_SECTION(pwmtimerCC26xxHWAttrs, ".const:pwmtimerCC26xxHWAttrs") +#endif + +/* PWM configuration, one per PWM output. */ +PWMTimerCC26XX_HwAttrs pwmtimerCC26xxHWAttrs[CC2650STK_PWMCOUNT] = { + { .pwmPin = Board_PWMPIN0, .gpTimerUnit = Board_GPTIMER0A }, + { .pwmPin = Board_PWMPIN1, .gpTimerUnit = Board_GPTIMER0B }, + { .pwmPin = Board_PWMPIN2, .gpTimerUnit = Board_GPTIMER1A }, + { .pwmPin = Board_PWMPIN3, .gpTimerUnit = Board_GPTIMER1B }, + { .pwmPin = Board_PWMPIN4, .gpTimerUnit = Board_GPTIMER2A }, + { .pwmPin = Board_PWMPIN5, .gpTimerUnit = Board_GPTIMER2B }, + { .pwmPin = Board_PWMPIN6, .gpTimerUnit = Board_GPTIMER3A }, + { .pwmPin = Board_PWMPIN7, .gpTimerUnit = Board_GPTIMER3B }, +}; + +/* PWM object, one per PWM output */ +PWMTimerCC26XX_Object pwmtimerCC26xxObjects[CC2650STK_PWMCOUNT]; + +extern const PWM_FxnTable PWMTimerCC26XX_fxnTable; + +/* PWM configuration (used as PWM_Handle by driver and application) */ +const PWM_Config PWM_config[CC2650STK_PWMCOUNT + 1] = { + { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[0], &pwmtimerCC26xxHWAttrs[0] }, + { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[1], &pwmtimerCC26xxHWAttrs[1] }, + { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[2], &pwmtimerCC26xxHWAttrs[2] }, + { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[3], &pwmtimerCC26xxHWAttrs[3] }, + { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[4], &pwmtimerCC26xxHWAttrs[4] }, + { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[5], &pwmtimerCC26xxHWAttrs[5] }, + { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[6], &pwmtimerCC26xxHWAttrs[6] }, + { &PWMTimerCC26XX_fxnTable, &pwmtimerCC26xxObjects[7], &pwmtimerCC26xxHWAttrs[7] }, + { NULL, NULL, NULL } +}; + + +/* + * ============================= PWM end ====================================== + */ + +/* + * =============================== Watchdog =============================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(Watchdog_config, ".const:Watchdog_config") +#pragma DATA_SECTION(watchdogCC26XXHWAttrs, ".const:watchdogCC26XXHWAttrs") +#endif + +#include +#include + +WatchdogCC26XX_Object watchdogCC26XXObjects[CC2650STK_WATCHDOGCOUNT]; + +const WatchdogCC26XX_HWAttrs watchdogCC26XXHWAttrs[CC2650STK_WATCHDOGCOUNT] = { + { + .baseAddr = WDT_BASE, + .intNum = INT_WDT_IRQ, + .reloadValue = 1000 /* Reload value in milliseconds */ + }, +}; + +const Watchdog_Config Watchdog_config[] = { + { + .fxnTablePtr = &WatchdogCC26XX_fxnTable, + .object = &watchdogCC26XXObjects[0], + .hwAttrs = &watchdogCC26XXHWAttrs[0] + }, + {NULL, NULL, NULL}, +}; + +/* + * ======== CC26XX_LAUNCHXL_initWatchdog ======== + */ +void CC26XX_LAUNCHXL_initWatchdog(void) +{ + Watchdog_init(); +} diff --git a/CC2650STK.cmd b/CC2650STK.cmd new file mode 100644 index 0000000..ccb9738 --- /dev/null +++ b/CC2650STK.cmd @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015-2016, Texas Instruments Incorporated + * All rights reserved. + * + * 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. + */ +/* + * ======== CC2650STK.cmd ======== + * CC26x0F128 PG2 linker configuration file for Code Composer Studio + */ + +--stack_size=1024 /* C stack is also used for ISR stack */ + +HEAPSIZE = 0xC00; /* Size of heap buffer used by HeapMem */ + +/* Override default entry point. */ +--entry_point ResetISR +/* Allow main() to take args */ +--args 0x8 +/* Suppress warnings and errors: */ +/* - 10063: Warning about entry point not being _c_int00 */ +/* - 16011, 16012: 8-byte alignment errors. Observed when linking in object */ +/* files compiled using Keil (ARM compiler) */ +--diag_suppress=10063,16011,16012 + +/* The starting address of the application. Normally the interrupt vectors */ +/* must be located at the beginning of the application. */ +#define FLASH_BASE 0x0 +#define FLASH_SIZE 0x20000 +#define RAM_BASE 0x20000000 +#define RAM_SIZE 0x5000 +#define GPRAM_BASE 0x11000000 +#define GPRAM_SIZE 0x2000 + +/* System memory map */ + +MEMORY +{ + /* Application stored in and executes from internal flash */ + FLASH (RX) : origin = FLASH_BASE, length = FLASH_SIZE + /* Application uses internal RAM for data */ + SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE + GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE +} + +/* Section allocation in memory */ + +SECTIONS +{ + .text : > FLASH + .TI.ramfunc : {} load=FLASH, run=SRAM, table(BINIT) + .const : > FLASH + .constdata : > FLASH + .rodata : > FLASH + .cinit : > FLASH + .pinit : > FLASH + .init_array : > FLASH + .emb_text : > FLASH + .ccfg : > FLASH (HIGH) + + .data : > SRAM + .bss : > SRAM + .sysmem : > SRAM + .nonretenvar : > SRAM + .gpram_data : > GPRAM + + /* Heap buffer used by HeapMem */ + .priheap : { + __primary_heap_start__ = .; + . += HEAPSIZE; + __primary_heap_end__ = .; + } > SRAM align 8 + + .stack : > SRAM (HIGH) +} diff --git a/CC2650STK.h b/CC2650STK.h new file mode 100644 index 0000000..6cfd69f --- /dev/null +++ b/CC2650STK.h @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2015-2016, Texas Instruments Incorporated + * All rights reserved. + * + * 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. + */ +/** ============================================================================ + * @file CC2650STK.h + * + * @brief CC2650SENSORTAG Board Specific header file. + * + * NB! This is the board file for PCB versions 1.2 and 1.3 + * + * ============================================================================ + */ +#ifndef __CC2650STK_SENSORTAG_BOARD_H__ +#define __CC2650STK_SENSORTAG_BOARD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** ============================================================================ + * Includes + * ==========================================================================*/ +#include +#include + +/** ============================================================================ + * Externs + * ==========================================================================*/ +extern const PIN_Config BoardGpioInitTable[]; + +/** ============================================================================ + * Defines + * ==========================================================================*/ + +/* Same RF Configuration as 7x7 EM */ +#define CC2650EM_7ID +#define CC2650STK + +/* Mapping of pins to board signals using general board aliases + * + */ + +/* Discrete outputs */ +#define Board_STK_LED1 IOID_10 +#define Board_STK_LED2 IOID_15 +#define Board_BUZZER IOID_21 +#define Board_LED_ON 1 +#define Board_LED_OFF 0 +#define Board_BUZZER_ON 1 +#define Board_BUZZER_OFF 0 + +/* Discrete inputs */ +#define Board_KEY_LEFT IOID_0 +#define Board_KEY_RIGHT IOID_4 +#define Board_RELAY IOID_3 + +/* Sensor outputs */ +#define Board_MPU_INT IOID_7 +#define Board_TMP_RDY IOID_1 + +/* I2C */ +#define Board_I2C0_SDA0 IOID_5 +#define Board_I2C0_SCL0 IOID_6 +#define Board_I2C0_SDA1 IOID_8 +#define Board_I2C0_SCL1 IOID_9 + +/* SPI */ +#define Board_SPI_FLASH_CS IOID_14 +#define Board_SPI_DEVPK_CS IOID_20 +#define Board_FLASH_CS_ON 0 +#define Board_FLASH_CS_OFF 1 +#define Board_DEVPK_CS_ON 1 +#define Board_DEVPK_CS_OFF 0 + +#define Board_SPI0_MISO IOID_18 +#define Board_SPI0_MOSI IOID_19 +#define Board_SPI0_CLK IOID_17 +#define Board_SPI0_CSN PIN_UNASSIGNED +#define Board_SPI1_MISO PIN_UNASSIGNED +#define Board_SPI1_MOSI PIN_UNASSIGNED +#define Board_SPI1_CLK PIN_UNASSIGNED +#define Board_SPI1_CSN PIN_UNASSIGNED + +/* UART when connected to SRF06EB */ +#define Board_EB_UART_TX IOID_16 +#define Board_EB_UART_RX IOID_17 + +/* Power control */ +#define Board_MPU_POWER IOID_12 +#define Board_MPU_POWER_ON 1 +#define Board_MPU_POWER_OFF 0 + +/* Audio */ +#define Board_MIC_POWER IOID_13 +#define Board_MIC_POWER_ON 1 +#define Board_MIC_POWER_OFF 0 +#define Board_AUDIO_DI IOID_2 +#define Board_AUDIO_CLK IOID_11 + +/* UART pins used by driver */ +#define Board_UART_TX Board_DP5_UARTTX +#define Board_UART_RX Board_DP4_UARTRX + +/* DevPack common */ +#define Board_AUDIOFS_TDO IOID_16 +#define Board_AUDIODO IOID_22 +#define Board_DP2 IOID_23 +#define Board_DP1 IOID_24 +#define Board_DP0 IOID_25 +#define Board_DP3 IOID_27 +#define Board_DP4_UARTRX IOID_28 +#define Board_DP5_UARTTX IOID_29 +#define Board_DEVPK_ID IOID_30 +#define Board_SPI_DEVPK_CS IOID_20 + +/* LCD DevPack */ +#define Board_LCD_EXTCOMIN IOID_22 +#define Board_LCD_EXTMODE IOID_28 +#define Board_LCD_ENABLE IOID_29 +#define Board_LCD_POWER PIN_UNASSIGNED +#define Board_LCD_CS Board_SPI_DEVPK_CS +#define Board_LCD_CS_ON 1 +#define Board_LCD_CS_OFF 0 + +/* LED-Audio DevPack */ +#define Board_DEVPK_LIGHT_BLUE IOID_23 +#define Board_DEVPK_LIGHT_GREEN IOID_24 +#define Board_DEVPK_LIGHT_WHITE IOID_25 +#define Board_DEVPK_LIGHT_RED IOID_27 + +/* PWM outputs */ +#define Board_PWMPIN0 Board_STK_LED1 +#define Board_PWMPIN1 Board_STK_LED2 +#define Board_PWMPIN2 PIN_UNASSIGNED +#define Board_PWMPIN3 PIN_UNASSIGNED +#define Board_PWMPIN4 PIN_UNASSIGNED +#define Board_PWMPIN5 PIN_UNASSIGNED +#define Board_PWMPIN6 PIN_UNASSIGNED +#define Board_PWMPIN7 PIN_UNASSIGNED + +/** ============================================================================ + * Instance identifiers + * ==========================================================================*/ +/* Generic I2C instance identifiers */ +#define Board_I2C CC2650STK_I2C0 +/* Generic PDM instance identifiers */ +#define Board_PDM CC2650STK_PDM0 +/* Generic SPI instance identifiers */ +#define Board_SPI0 CC2650STK_SPI0 +#define Board_SPI1 CC2650STK_SPI1 +/* Generic UART instance identifiers */ +#define Board_UART CC2650STK_UART0 +/* Generic Crypto instance identifiers */ +#define Board_CRYPTO CC2650STK_CRYPTO0 +/* Generic GPTimer instance identifiers */ +#define Board_GPTIMER0A CC2650STK_GPTIMER0A +#define Board_GPTIMER0B CC2650STK_GPTIMER0B +#define Board_GPTIMER1A CC2650STK_GPTIMER1A +#define Board_GPTIMER1B CC2650STK_GPTIMER1B +#define Board_GPTIMER2A CC2650STK_GPTIMER2A +#define Board_GPTIMER2B CC2650STK_GPTIMER2B +#define Board_GPTIMER3A CC2650STK_GPTIMER3A +#define Board_GPTIMER3B CC2650STK_GPTIMER3B +/* Generic PWM instance identifiers */ +#define Board_PWM0 CC2650STK_PWM0 +#define Board_PWM1 CC2650STK_PWM1 +#define Board_PWM2 CC2650STK_PWM2 +#define Board_PWM3 CC2650STK_PWM3 +#define Board_PWM4 CC2650STK_PWM4 +#define Board_PWM5 CC2650STK_PWM5 +#define Board_PWM6 CC2650STK_PWM6 +#define Board_PWM7 CC2650STK_PWM7 + +/** ============================================================================ + * Number of peripherals and their names + * ==========================================================================*/ + +/*! + * @def CC2650STK_I2CName + * @brief Enum of I2C names on the CC2650 dev board + */ +typedef enum CC2650STK_I2CName { + CC2650STK_I2C0 = 0, + + CC2650STK_I2CCOUNT +} CC2650STK_I2CName; + +/*! + * @def CC2650STK_CryptoName + * @brief Enum of Crypto names on the CC2650 dev board + */ +typedef enum CC2650STK_CryptoName { + CC2650STK_CRYPTO0 = 0, + + CC2650STK_CRYPTOCOUNT +} CC2650STK_CryptoName; + +/*! + * @def CC2650STK_PdmName + * @brief Enum of PDM names on the CC2650 dev board + */ +typedef enum CC2650STK_PDMName { + CC2650STK_PDM0 = 0, + CC2650STK_PDMCOUNT +} CC2650STK_PDMName; + +/*! + * @def CC2650STK_SPIName + * @brief Enum of SPI names on the CC2650 dev board + */ +typedef enum CC2650STK_SPIName { + CC2650STK_SPI0 = 0, + CC2650STK_SPI1, + + CC2650STK_SPICOUNT +} CC2650STK_SPIName; + +/*! + * @def CC2650STK_UARTName + * @brief Enum of UARTs on the CC2650 dev board + */ +typedef enum CC2650STK_UARTName { + CC2650STK_UART0 = 0, + + CC2650STK_UARTCOUNT +} CC2650STK_UARTName; + +/*! + * @def CC2650STK_UdmaName + * @brief Enum of DMA buffers + */ +typedef enum CC2650STK_UdmaName { + CC2650STK_UDMA0 = 0, + + CC2650STK_UDMACOUNT +} CC2650STK_UdmaName; +/*! + * @def CC2650STK_GPTimerName + * @brief Enum of GPTimer parts + */ +typedef enum CC2650STK_GPTimerName +{ + CC2650STK_GPTIMER0A = 0, + CC2650STK_GPTIMER0B, + CC2650STK_GPTIMER1A, + CC2650STK_GPTIMER1B, + CC2650STK_GPTIMER2A, + CC2650STK_GPTIMER2B, + CC2650STK_GPTIMER3A, + CC2650STK_GPTIMER3B, + CC2650STK_GPTIMERPARTSCOUNT +} CC2650STK_GPTimerName; + +/*! + * @def CC2650STK_GPTimers + * @brief Enum of GPTimers + */ +typedef enum CC2650STK_GPTimers +{ + CC2650STK_GPTIMER0 = 0, + CC2650STK_GPTIMER1, + CC2650STK_GPTIMER2, + CC2650STK_GPTIMER3, + CC2650STK_GPTIMERCOUNT +} CC2650STK_GPTimers; + +/*! + * @def CC2650STK_PWM + * @brief Enum of PWM outputs on the board + */ +typedef enum CC2650STK_PWM +{ + CC2650STK_PWM0 = 0, + CC2650STK_PWM1, + CC2650STK_PWM2, + CC2650STK_PWM3, + CC2650STK_PWM4, + CC2650STK_PWM5, + CC2650STK_PWM6, + CC2650STK_PWM7, + CC2650STK_PWMCOUNT +} CC2650STK_PWM; + +/*! + * @def CC2650STK_WatchdogName + * @brief Enum of Watchdogs on the CC2650STK dev board + */ +typedef enum CC2650STK_WatchdogName { + CC2650STK_WATCHDOG0 = 0, + + CC2650STK_WATCHDOGCOUNT +} CC2650STK_WatchdogName; + +#ifdef __cplusplus +} +#endif + +#endif /* __CC2650STK_SENSORTAG_BOARD_H__ */ diff --git a/Debug/.gitignore b/Debug/.gitignore new file mode 100644 index 0000000..885a51a --- /dev/null +++ b/Debug/.gitignore @@ -0,0 +1,6 @@ +/makefile +/objects.mk +/source/ +/sources.mk +/subdir_rules.mk +/subdir_vars.mk diff --git a/DeviceFamily.h b/DeviceFamily.h new file mode 100644 index 0000000..50a2d4e --- /dev/null +++ b/DeviceFamily.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2017, Texas Instruments Incorporated + * All rights reserved. + * + * 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. + */ +/** ============================================================================ + * @file DeviceFamily.h + * + * @brief Infrastructure to select correct driverlib path and identify devices + * + * This module enables the selection of the correct driverlib path for the current + * device. It also facilitates the use of per-device conditional compilation + * to enable minor variations in drivers between devices. + * + * In order to use this functionality, DeviceFamily_XYZ must be defined as one of + * the supported values. The DeviceFamily_ID and DeviceFamily_DIRECTORY defines + * are set based on DeviceFamily_XYZ. + */ + +#ifndef ti_devices_DeviceFamily__include +#define ti_devices_DeviceFamily__include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * DeviceFamily_ID_XYZ values. + * + * DeviceFamily_ID may be used in the preprocessor for conditional compilation. + * DeviceFamily_ID is set to one of these values based on the top level + * DeviceFamily_XYZ define. + */ +#define DeviceFamily_ID_CC13X0 1 +#define DeviceFamily_ID_CC26X0 2 +#define DeviceFamily_ID_CC26X0R2 3 +#define DeviceFamily_ID_CC13X2_V1 4 +#define DeviceFamily_ID_CC13X2_V2 5 +#define DeviceFamily_ID_CC13X2 DeviceFamily_ID_CC13X2_V1 +#define DeviceFamily_ID_CC26X2_V1 6 +#define DeviceFamily_ID_CC26X2_V2 7 +#define DeviceFamily_ID_CC26X2 DeviceFamily_ID_CC26X2_V1 +#define DeviceFamily_ID_CC3200 8 +#define DeviceFamily_ID_CC3220 9 +#define DeviceFamily_ID_MSP432P401x 10 +#define DeviceFamily_ID_MSP432P4x1xI 11 +#define DeviceFamily_ID_MSP432P4x1xT 12 +#define DeviceFamily_ID_MSP432E401Y 13 +#define DeviceFamily_ID_MSP432E411Y 14 + +/* + * DeviceFamily_PARENT_XYZ values. + * + * DeviceFamily_PARENT may be used in the preprocessor for conditional + * compilation. DeviceFamily_PARENT is set to one of these values based + * on the top-level DeviceFamily_XYZ define. + */ +#define DeviceFamily_PARENT_CC13X0_CC26X0 1 +#define DeviceFamily_PARENT_CC13X2_CC26X2 2 +#define DeviceFamily_PARENT_MSP432P401R 3 +#define DeviceFamily_PARENT_MSP432P4111 4 + +/* + * Lookup table that sets DeviceFamily_ID, DeviceFamily_DIRECTORY, and + * DeviceFamily_PARENT based on the DeviceFamily_XYZ define. + * If DeviceFamily_XYZ is undefined, a compiler error is thrown. If + * multiple DeviceFamily_XYZ are defined, the first one encountered is used. + */ +#if defined(DeviceFamily_CC13X0) + #define DeviceFamily_ID DeviceFamily_ID_CC13X0 + #define DeviceFamily_DIRECTORY cc13x0 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X0_CC26X0 + +#elif defined(DeviceFamily_CC13X2) + #define DeviceFamily_ID DeviceFamily_ID_CC13X2 + #define DeviceFamily_DIRECTORY cc13x2_cc26x2_v1 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X2_CC26X2 + +#elif defined(DeviceFamily_CC13X2_V1) + #define DeviceFamily_ID DeviceFamily_ID_CC13X2_V1 + #define DeviceFamily_DIRECTORY cc13x2_cc26x2_v1 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X2_CC26X2 + +#elif defined(DeviceFamily_CC13X2_V2) + #define DeviceFamily_ID DeviceFamily_ID_CC13X2_V2 + #define DeviceFamily_DIRECTORY cc13x2_cc26x2_v2 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X2_CC26X2 + +#elif defined(DeviceFamily_CC26X0) + #define DeviceFamily_ID DeviceFamily_ID_CC26X0 + #define DeviceFamily_DIRECTORY cc26x0 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X0_CC26X0 + +#elif defined(DeviceFamily_CC26X0R2) + #define DeviceFamily_ID DeviceFamily_ID_CC26X0R2 + #define DeviceFamily_DIRECTORY cc26x0r2 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X0_CC26X0 + +#elif defined(DeviceFamily_CC26X2) + #define DeviceFamily_ID DeviceFamily_ID_CC26X2 + #define DeviceFamily_DIRECTORY cc13x2_cc26x2_v1 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X2_CC26X2 + +#elif defined(DeviceFamily_CC26X2_V1) + #define DeviceFamily_ID DeviceFamily_ID_CC26X2_V1 + #define DeviceFamily_DIRECTORY cc13x2_cc26x2_v1 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X2_CC26X2 + +#elif defined(DeviceFamily_CC26X2_V2) + #define DeviceFamily_ID DeviceFamily_ID_CC26X2_V2 + #define DeviceFamily_DIRECTORY cc13x2_cc26x2_v2 + #define DeviceFamily_PARENT DeviceFamily_PARENT_CC13X2_CC26X2 + +#elif defined(DeviceFamily_CC3200) + #define DeviceFamily_ID DeviceFamily_ID_CC3200 + #define DeviceFamily_DIRECTORY cc32xx + +#elif defined(DeviceFamily_CC3220) + #define DeviceFamily_ID DeviceFamily_ID_CC3220 + #define DeviceFamily_DIRECTORY cc32xx + +#elif defined(DeviceFamily_MSP432P401x) || defined(__MSP432P401R__) + #define DeviceFamily_ID DeviceFamily_ID_MSP432P401x + #define DeviceFamily_DIRECTORY msp432p4xx + #define DeviceFamily_PARENT DeviceFamily_PARENT_MSP432P401R + #if !defined(__MSP432P401R__) + #define __MSP432P401R__ + #endif + +#elif defined(DeviceFamily_MSP432P4x1xI) + #define DeviceFamily_ID DeviceFamily_ID_MSP432P4x1xI + #define DeviceFamily_DIRECTORY msp432p4xx + #define DeviceFamily_PARENT DeviceFamily_PARENT_MSP432P4111 + #if !defined(__MSP432P4111__) + #define __MSP432P4111__ + #endif + +#elif defined(DeviceFamily_MSP432P4x1xT) + #define DeviceFamily_ID DeviceFamily_ID_MSP432P4x1xT + #define DeviceFamily_DIRECTORY msp432p4xx + #define DeviceFamily_PARENT DeviceFamily_PARENT_MSP432P4111 + #if !defined(__MSP432P4111__) + #define __MSP432P4111__ + #endif + +#elif defined(DeviceFamily_MSP432E401Y) + #define DeviceFamily_ID DeviceFamily_ID_MSP432E401Y + #define DeviceFamily_DIRECTORY msp432e4 + #if !defined(__MSP432E401Y__) + #define __MSP432E401Y__ + #endif + +#elif defined(DeviceFamily_MSP432E411Y) + #define DeviceFamily_ID DeviceFamily_ID_MSP432E411Y + #define DeviceFamily_DIRECTORY msp432e4 + #if !defined(__MSP432E411Y__) + #define __MSP432E411Y__ + #endif +#else + #error "DeviceFamily_XYZ undefined. You must defined DeviceFamily_XYZ!" +#endif + +/*! + * @brief Macro to include correct driverlib path. + * + * @pre DeviceFamily_XYZ which sets DeviceFamily_DIRECTORY must be defined + * first. + * + * @param x A token containing the path of the file to include based on + * the root device folder. The preceding forward slash must be + * omitted. For example: + * - #include DeviceFamily_constructPath(inc/hw_memmap.h) + * - #include DeviceFamily_constructPath(driverlib/ssi.h) + * + * @return Returns an include path. + * + */ +//#define DeviceFamily_constructPath(x) +#define DeviceFamily_constructPath(x) #x + +#ifdef __cplusplus +} +#endif + +#endif /* ti_devices_DeviceFamily__include */ diff --git a/README.html b/README.html new file mode 100644 index 0000000..43bfa78 --- /dev/null +++ b/README.html @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + +
+ + Texas Instruments + + + +
+

+

Table of Contents

+ +

Example Summary

+

This example is intended to be a starting point for new development where a minimal footprint is needed.

+

Peripherals Exercised

+
    +
  • Board_LED0 - Indicates that the board was initialized within main()
  • +
+

Example Usage

+
    +
  • The example lights Board_LED0 as part of the initialization in main(). Then a heartBeat task toggles the LED at a rate determined by the arg0 parameter for the constructed Task instance in the .c file.
  • +
+

Application Design Details

+

This examples is the same as the Empty example except many development and debug features are disabled. For example:

+
    +
  • No Kernel Idle task
  • +
  • No stack overflow checking
  • +
  • No Logs or Asserts are enabled
  • +
+
+

The ROM is being used in this example. This is controlled by the following lines in the .cfg file:

+
+
+
var ROM = xdc.useModule('ti.sysbios.rom.ROM');
+    ROM.romName = ROM.CC2650;
+

Since the kernel in the ROM is being used, there is no logging or assert checking done by the kernel.

+
+
+

For IAR users using any SensorTag(STK) Board, the XDS110 debugger must be selected with the 4-wire JTAG connection within your projects’ debugger configuration.

+
+

References

+
    +
  • For GNU and IAR users, please read the following website for details about enabling semi-hosting in order to view console output.

  • +
  • Please refer to the Memory Footprint Reduction section in the TI-RTOS User Guide spruhd4.pdf for a complete and detailed list of the differences between the empty minimal and empty projects.

  • +
+ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..d3a1663 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +## Example Summary + +This example is intended to be a starting point for new development where +a minimal footprint is needed. + +## Peripherals Exercised + +* `Board_LED0` - Indicates that the board was initialized within `main()` + +## Example Usage + +* The example lights `Board_LED0` as part of the initialization in `main()`. +Then a heartBeat task toggles the LED at a rate determined by the `arg0` +parameter for the constructed Task instance in the .c file. + +## Application Design Details + +This examples is the same as the Empty example except many development +and debug features are disabled. For example: + +* No Kernel Idle task +* No stack overflow checking +* No Logs or Asserts are enabled + + +> The ROM is being used in this example. This is controlled +> by the following lines in the *.cfg* file: + +> ``` +var ROM = xdc.useModule('ti.sysbios.rom.ROM'); + ROM.romName = ROM.CC2650; +``` +> Since the kernel in the ROM is being used, there is no logging or assert +checking done by the kernel. + +> For IAR users using any SensorTag(STK) Board, the XDS110 debugger must be +selected with the 4-wire JTAG connection within your projects' debugger +configuration. + +## References + +* For GNU and IAR users, please read the following website for details + about enabling [semi-hosting](http://processors.wiki.ti.com/index.php/TI-RTOS_Examples_SemiHosting) + in order to view console output. + +* Please refer to the __Memory Footprint Reduction__ section in the +TI-RTOS User Guide *spruhd4.pdf* for a complete and detailed list of the +differences between the empty minimal and empty projects. diff --git a/Release/.gitignore b/Release/.gitignore new file mode 100644 index 0000000..885a51a --- /dev/null +++ b/Release/.gitignore @@ -0,0 +1,6 @@ +/makefile +/objects.mk +/source/ +/sources.mk +/subdir_rules.mk +/subdir_vars.mk diff --git a/ccfg.c b/ccfg.c new file mode 100644 index 0000000..ba9c9a1 --- /dev/null +++ b/ccfg.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, Texas Instruments Incorporated + * All rights reserved. + * + * 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. + */ + +/* + * ======== ccfg.c ======== + * Customer Configuration for CC26xx and CC13xx devices. This file is used to + * configure Boot ROM, start-up code, and SW radio behaviour. + * + * By default, driverlib startup_files/ccfg.c settings are used. However, if + * changes are required there are two means to do so: + * + * 1. Remove this file and copy driverlib's startup_files/ccfg.c file in + * its place. Make all changes to the file. Changes made are local to + * the project and will not affect other projects. + * + * 2. Perform changes to driverlib startup_files/ccfg.c file. Changes + * made to this file will be applied to all projects. This file must + * remain unmodified. + */ + +#define SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM 0x0 +#include diff --git a/makefile.defs b/makefile.defs new file mode 100644 index 0000000..5a0353c --- /dev/null +++ b/makefile.defs @@ -0,0 +1,24 @@ +#File used to help "Clean Project" in CCS completely clean the kernel files +CFG_SRCDIR = ../src + +ifneq (,$(findstring :,$(WINDIR)$(windir)$(COMSPEC)$(comspec))) + # if Windows, use copy to touch file dates + TOUCH = copy /b $(subst /,\,$@)+,, $(subst /,\,$@) +else + TOUCH = touch $@ +endif + +# include Config generated top-level makefile +-include $(CFG_SRCDIR)/makefile.libs + +ifneq (clean,$(MAKECMDGOALS)) +# ensure this file is reloaded when .cfg files change but after config runs +$(CFG_SRCDIR)/makefile.libs: $(GEN_OPTS) $(CFG_SRCS) + -@$(if $(wildcard $@),$(TOUCH),:) +endif + +#add generated makefile to list of files to delete during a clean +GEN_MISC_FILES__QUOTED += "$(CFG_SRCDIR)/makefile.libs" + +#add generated source dir to list of directories to delete during a clean +#GEN_MISC_DIRS__QTD += "$(CFG_SRCDIR)" diff --git a/release.cfg b/release.cfg new file mode 100644 index 0000000..20c51a2 --- /dev/null +++ b/release.cfg @@ -0,0 +1,638 @@ +/* + * Copyright (c) 2015-2017, Texas Instruments Incorporated + * All rights reserved. + * + * 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. + */ + + + +/* ================ Boot configuration ================ */ +var Boot = xdc.useModule('ti.sysbios.family.arm.cc26xx.Boot'); +/* + * This module contains family specific Boot APIs and configuration settings. + * See the SYS/BIOS API guide for more information. + */ + + + +/* ================ Clock configuration ================ */ +var Clock = xdc.useModule('ti.sysbios.knl.Clock'); +/* + * When using Power and calibrateRCOSC is set to true, this should be set to 10. + * The timer used by the Clock module supports TickMode_DYNAMIC. This enables us + * to set the tick period to 10 us without generating the overhead of additional + * interrupts. + * + * Note: The calibrateRCOSC parameter is set within the Power configuration + * structure in the "Board.c" file. + */ +Clock.tickPeriod = 10; + + + +/* ================ Defaults (module) configuration ================ */ +var Defaults = xdc.useModule('xdc.runtime.Defaults'); +/* + * A flag to allow module names to be loaded on the target. Module name + * strings are placed in the .const section for debugging purposes. + * + * Pick one: + * - true (default) + * Setting this parameter to true will include name strings in the .const + * section so that Errors and Asserts are easier to debug. + * - false + * Setting this parameter to false will reduce footprint in the .const + * section. As a result, Error and Assert messages will contain an + * "unknown module" prefix instead of the actual module name. + * + * When using BIOS in ROM: + * This option must be set to false. + */ +//Defaults.common$.namedModule = true; +Defaults.common$.namedModule = false; + + + +/* ================ Error configuration ================ */ +var Error = xdc.useModule('xdc.runtime.Error'); +/* + * This function is called to handle all raised errors, but unlike + * Error.raiseHook, this function is responsible for completely handling the + * error with an appropriately initialized Error_Block. + * + * Pick one: + * - Error.policyDefault (default) + * Calls Error.raiseHook with an initialized Error_Block structure and logs + * the error using the module's logger. + * - Error.policySpin + * Simple alternative that traps on a while(1) loop for minimized target + * footprint. + * Using Error.policySpin, the Error.raiseHook will NOT called. + * - Error.policyMin + * Lightweight policy function that does minimum processing and returns. + */ +Error.policyFxn = Error.policyDefault; +//Error.policyFxn = Error.policySpin; +//Error.policyFxn = Error.policyMin; + +/* + * If Error.policyFxn is set to Error.policyDefault, this function is called + * whenever an error is raised by the Error module. + * + * Pick one: + * - Error.print (default) + * Errors are formatted and output via System_printf() for easier + * debugging. + * - null + * Errors are not formatted or logged. This option reduces code footprint. + * - non-null function + * Errors invoke custom user function. See the Error module documentation + * for more details. + */ +//Error.raiseHook = Error.print; +Error.raiseHook = null; +//Error.raiseHook = "&myErrorFxn"; + +/* + * If Error.policyFxn is set to Error.policyDefault, this option applies to the + * maximum number of times the Error.raiseHook function can be recursively + * invoked. This option limits the possibility of an infinite recursion that + * could lead to a stack overflow. + * The default value is 16. + */ +Error.maxDepth = 2; + + + +/* ================ Hwi configuration ================ */ +var halHwi = xdc.useModule('ti.sysbios.hal.Hwi'); +var m3Hwi = xdc.useModule('ti.sysbios.family.arm.m3.Hwi'); +/* + * Checks for Hwi (system) stack overruns while in the Idle loop. + * + * Pick one: + * - true (default) + * Checks the top word for system stack overflows during the idle loop and + * raises an Error if one is detected. + * - false + * Disabling the runtime check improves runtime performance and yields a + * reduced flash footprint. + */ +//halHwi.checkStackFlag = true; +halHwi.checkStackFlag = false; + +/* + * The following options alter the system's behavior when a hardware exception + * is detected. + * + * Pick one: + * - Hwi.enableException = true + * This option causes the default m3Hwi.excHandlerFunc function to fully + * decode an exception and dump the registers to the system console. + * This option raises errors in the Error module and displays the + * exception in ROV. + * - Hwi.enableException = false + * This option reduces code footprint by not decoding or printing the + * exception to the system console. + * It however still raises errors in the Error module and displays the + * exception in ROV. + * - Hwi.excHandlerFunc = null + * This is the most aggressive option for code footprint savings; but it + * can difficult to debug exceptions. It reduces flash footprint by + * plugging in a default while(1) trap when exception occur. This option + * does not raise an error with the Error module. + */ +//m3Hwi.enableException = true; +//m3Hwi.enableException = false; +m3Hwi.excHandlerFunc = null; + +/* + * Enable hardware exception generation when dividing by zero. + * + * Pick one: + * - 0 (default) + * Disables hardware exceptions when dividing by zero + * - 1 + * Enables hardware exceptions when dividing by zero + */ +m3Hwi.nvicCCR.DIV_0_TRP = 0; +//m3Hwi.nvicCCR.DIV_0_TRP = 1; + + + +/* ================ Idle configuration ================ */ +var Idle = xdc.useModule('ti.sysbios.knl.Idle'); +/* + * The Idle module is used to specify a list of functions to be called when no + * other tasks are running in the system. + * + * Functions added here will be run continuously within the idle task. + * + * Function signature: + * Void func(Void); + */ +//Idle.addFunc("&myIdleFunc"); + +Idle.addFunc('&Power_idleFunc'); /* add the Power module's idle function */ + + + +/* ================ Kernel (SYS/BIOS) configuration ================ */ +var BIOS = xdc.useModule('ti.sysbios.BIOS'); +/* + * Enable asserts in the BIOS library. + * + * Pick one: + * - true (default) + * Enables asserts for debugging purposes. + * - false + * Disables asserts for a reduced code footprint and better performance. + * + * When using BIOS in ROM: + * This option must be set to false. + */ +//BIOS.assertsEnabled = true; +BIOS.assertsEnabled = false; + +/* + * A flag to determine if xdc.runtime sources are to be included in a custom + * built BIOS library. + * + * Pick one: + * - false (default) + * The pre-built xdc.runtime library is provided by the respective target + * used to build the application. + * - true + * xdc.runtime library sources are to be included in the custom BIOS + * library. This option yields the most efficient library in both code + * footprint and runtime performance. + */ +//BIOS.includeXdcRuntime = false; +BIOS.includeXdcRuntime = true; + +/* + * The SYS/BIOS runtime is provided in the form of a library that is linked + * with the application. Several forms of this library are provided with the + * SYS/BIOS product. + * + * Pick one: + * - BIOS.LibType_Custom + * Custom built library that is highly optimized for code footprint and + * runtime performance. + * - BIOS.LibType_Debug + * Custom built library that is non-optimized that can be used to + * single-step through APIs with a debugger. + * + */ +BIOS.libType = BIOS.LibType_Custom; +//BIOS.libType = BIOS.LibType_Debug; + +/* + * Runtime instance creation enable flag. + * + * Pick one: + * - true (default) + * Allows Mod_create() and Mod_delete() to be called at runtime which + * requires a default heap for dynamic memory allocation. + * - false + * Reduces code footprint by disallowing Mod_create() and Mod_delete() to + * be called at runtime. Object instances are constructed via + * Mod_construct() and destructed via Mod_destruct(). + * + * When using BIOS in ROM: + * This option must be set to true. + */ +BIOS.runtimeCreatesEnabled = true; +//BIOS.runtimeCreatesEnabled = false; + +/* + * Enable logs in the BIOS library. + * + * Pick one: + * - true (default) + * Enables logs for debugging purposes. + * - false + * Disables logging for reduced code footprint and improved runtime + * performance. + * + * When using BIOS in ROM: + * This option must be set to false. + */ +//BIOS.logsEnabled = true; +BIOS.logsEnabled = false; + + + +/* ================ Memory configuration ================ */ +var Memory = xdc.useModule('xdc.runtime.Memory'); +/* + * The Memory module itself simply provides a common interface for any + * variety of system and application specific memory management policies + * implemented by the IHeap modules(Ex. HeapMem, HeapBuf). + */ + +/* + * Use HeapMem primary heap instance to use linker-defined memory region + * Add HeapTrack on top to find over-writes, invalid frees, and + * aid in finding the correct sizing of the heap and memory leaks. + */ +var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem'); +HeapMem.primaryHeapBaseAddr = "&__primary_heap_start__"; +HeapMem.primaryHeapEndAddr = "&__primary_heap_end__"; + +var heapMemParams = new HeapMem.Params(); +heapMemParams.usePrimaryHeap = true; + +//var HeapTrack = xdc.useModule('ti.sysbios.heaps.HeapTrack'); +//var heapTrackParams = new HeapTrack.Params; +//heapTrackParams.heap = HeapMem.create(heapMemParams); +//Program.global.heap0 = HeapTrack.create(heapTrackParams); +Program.global.heap0 = HeapMem.create(heapMemParams); + +Memory.defaultHeapInstance = Program.global.heap0; + + + +/* ================ Program configuration ================ */ +/* + * Program.stack must be set to 0 to allow the setting + * of the system stack size to be determined in the example's + * linker command file. + */ +Program.stack = 0; + + +/* + * Uncomment to enable Semihosting for GNU targets to print to the CCS console. + * Please read the following TIRTOS Wiki page for more information on Semihosting: + * http://processors.wiki.ti.com/index.php/TI-RTOS_Examples_SemiHosting + */ + +if (Program.build.target.$name.match(/gnu/)) { + //var SemiHost = xdc.useModule('ti.sysbios.rts.gnu.SemiHostSupport'); +} +/* ================ ROM configuration ================ */ +/* + * To use BIOS in flash, comment out the code block below. + */ +var ROM = xdc.useModule('ti.sysbios.rom.ROM'); +ROM.romName = ROM.CC2650; + + + +/* ================ Semaphore configuration ================ */ +var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore'); +/* + * Enables global support for Task priority pend queuing. + * + * Pick one: + * - true (default) + * This allows pending tasks to be serviced based on their task priority. + * - false + * Pending tasks are services based on first in, first out basis. + * + * When using BIOS in ROM: + * This option must be set to false. + */ +//Semaphore.supportsPriority = true; +Semaphore.supportsPriority = false; + +/* + * Allows for the implicit posting of events through the semaphore, + * disable for additional code saving. + * + * Pick one: + * - true + * This allows the Semaphore module to post semaphores and events + * simultaneously. + * - false (default) + * Events must be explicitly posted to unblock tasks. + * + * When using BIOS in ROM: + * This option must be set to false. + */ +//Semaphore.supportsEvents = true; +Semaphore.supportsEvents = false; + + + +/* ================ Swi configuration ================ */ +var Swi = xdc.useModule('ti.sysbios.knl.Swi'); +/* + * A software interrupt is an object that encapsulates a function to be + * executed and a priority. Software interrupts are prioritized, preempt tasks + * and are preempted by hardware interrupt service routines. + * + * This module is included to allow Swi's in a users' application. + */ +/* + * Reduce the number of swi priorities from the default of 16. + * Decreasing the number of swi priorities yields memory savings. + */ +Swi.numPriorities = 6; + + + +/* ================ System configuration ================ */ +var System = xdc.useModule('xdc.runtime.System'); +/* + * The Abort handler is called when the system exits abnormally. + * + * Pick one: + * - System.abortStd (default) + * Call the ANSI C Standard 'abort()' to terminate the application. + * - System.abortSpin + * A lightweight abort function that loops indefinitely in a while(1) trap + * function. + * - A custom abort handler + * A user-defined function. See the System module documentation for + * details. + */ +//System.abortFxn = System.abortStd; +System.abortFxn = System.abortSpin; +//System.abortFxn = "&myAbortSystem"; + +/* + * The Exit handler is called when the system exits normally. + * + * Pick one: + * - System.exitStd (default) + * Call the ANSI C Standard 'exit()' to terminate the application. + * - System.exitSpin + * A lightweight exit function that loops indefinitely in a while(1) trap + * function. + * - A custom exit function + * A user-defined function. See the System module documentation for + * details. + */ +//System.exitFxn = System.exitStd; +System.exitFxn = System.exitSpin; +//System.exitFxn = "&myExitSystem"; + +/* + * Minimize exit handler array in the System module. The System module includes + * an array of functions that are registered with System_atexit() which is + * called by System_exit(). The default value is 8. + */ +System.maxAtexitHandlers = 2; + +/* + * Enable System_printf() to display floats. Use the longer '%f%$L%$S%$F' + * if your code has SYS/BIOS instrumentation enabled (Asserts/Error/Log), + * as is typical with the 'debug' profile. + */ +//System.extendedFormats = '%f%$L%$S%$F'; +System.extendedFormats = '%f%$S'; + +/* + * The System.SupportProxy defines a low-level implementation of System + * functions such as System_printf(), System_flush(), etc. + * + * Pick one pair: + * - SysMin + * This module maintains an internal configurable circular buffer that + * stores the output until System_flush() is called. + * The size of the circular buffer is set via SysMin.bufSize. + * - SysCallback + * SysCallback allows for user-defined implementations for System APIs. + * The SysCallback support proxy has a smaller code footprint and can be + * used to supply custom System_printf services. + * The default SysCallback functions point to stub functions. See the + * SysCallback module's documentation. + */ +//var SysMin = xdc.useModule('xdc.runtime.SysMin'); +//SysMin.bufSize = 1024; +//System.SupportProxy = SysMin; +var SysCallback = xdc.useModule('xdc.runtime.SysCallback'); +System.SupportProxy = SysCallback; +//SysCallback.abortFxn = "&myUserAbort"; +//SysCallback.exitFxn = "&myUserExit"; +//SysCallback.flushFxn = "&myUserFlush"; +//SysCallback.putchFxn = "&myUserPutch"; +//SysCallback.readyFxn = "&myUserReady"; + + + +/* ================ Task configuration ================ */ +var Task = xdc.useModule('ti.sysbios.knl.Task'); +/* + * Check task stacks for overflow conditions. + * + * Pick one: + * - true (default) + * Enables runtime checks for task stack overflow conditions during + * context switching ("from" and "to") + * - false + * Disables runtime checks for task stack overflow conditions. + * + * When using BIOS in ROM: + * This option must be set to false. + */ +//Task.checkStackFlag = true; +Task.checkStackFlag = false; + +/* + * Set the default task stack size when creating tasks. + * + * The default is dependent on the device being used. Reducing the default stack + * size yields greater memory savings. + */ +Task.defaultStackSize = 512; + +/* + * Enables the idle task. + * + * Pick one: + * - true (default) + * Creates a task with priority of 0 which calls idle hook functions. This + * option must be set to true to gain power savings provided by the Power + * module. + * - false + * No idle task is created. This option consumes less memory as no + * additional default task stack is needed. + * To gain power savings by the Power module without having the idle task, + * add Idle.run as the Task.allBlockedFunc. + */ +Task.enableIdleTask = true; +//Task.enableIdleTask = false; +//Task.allBlockedFunc = Idle.run; + +/* + * If Task.enableIdleTask is set to true, this option sets the idle task's + * stack size. + * + * Reducing the idle stack size yields greater memory savings. + */ +Task.idleTaskStackSize = 512; + +/* + * Reduce the number of task priorities. + * The default is 16. + * Decreasing the number of task priorities yield memory savings. + */ +Task.numPriorities = 6; + + + +/* ================ Text configuration ================ */ +var Text = xdc.useModule('xdc.runtime.Text'); +/* + * These strings are placed in the .const section. Setting this parameter to + * false will save space in the .const section. Error, Assert and Log messages + * will print raw ids and args instead of a formatted message. + * + * Pick one: + * - true (default) + * This option loads test string into the .const for easier debugging. + * - false + * This option reduces the .const footprint. + */ +//Text.isLoaded = true; +Text.isLoaded = false; + + + +/* ================ Types configuration ================ */ +var Types = xdc.useModule('xdc.runtime.Types'); +/* + * This module defines basic constants and types used throughout the + * xdc.runtime package. + */ + + + +/* ================ Application Specific Instances ================ */ + +/* ================ Diagnostics configuration ================ */ +//var Diags = xdc.useModule('xdc.runtime.Diags'); +/* + * You use the Diags module to set and clear bits in a module's diagnostics + * mask for the purpose of controlling diagnostics within that module. A + * module diagnostics mask controls both Assert and Log statements + * within that module, disabling these statements yields + * code savings. + */ + +/* ================ Logging configuration ================ */ +//var Log = xdc.useModule('xdc.runtime.Log'); +/* + * Modules and the application code generate Log_Event events by calling + * the Log module's functions. + * Disabling all Log statements here will allow the optimizer to completely + * remove all Log code from the application. + * + * Note: In order to generate Log events in your application both the Diags + * and the Log mask must be set. See the SYS/BIOS API guide for + * more information. + */ + +/* + * LoggingSetup configures TI-RTOS modules to capture user-specified information + * such as CPU Load, Task Load and Task Execution so that it can be + * displayed by System Analyzer. + */ +//var LoggingSetup = xdc.useModule('ti.uia.sysbios.LoggingSetup'); +//LoggingSetup.loadLoggerSize = 256; +//LoggingSetup.mainLoggerSize = 512; +//LoggingSetup.sysbiosLoggerSize = 1024; + +/* ================ Main configuration ================ */ +var Main = xdc.useModule('xdc.runtime.Main'); +/* Configuration of this Main module is used for all code not in a module */ + + + +/* ================ POSIX configuration ================ */ +//var Settings = xdc.useModule('ti.posix.tirtos.Settings'); + +if (Program.build.target.$name.match(/iar/)) { + /* + * For the IAR target, the 'ti.posix.tirtos.Settings' uses the + * MultithreadSupport module. By default, the MultithreadSupport module + * use the '--threaded_lib' library which provides a separate 'errno' + * per thread and makes other rts libraries reentrant. This library has + * a larger footprint which can be a problem for some apps, so we + * override the default and disable it here. + */ + var MultithreadSupport = + xdc.useModule('ti.sysbios.rts.iar.MultithreadSupport'); + MultithreadSupport.enableMultithreadSupport = false; +} + + +/* ================ Event configuration ================ */ +var Event = xdc.useModule('ti.sysbios.knl.Event'); + + + +/* ================ Mailbox configuration ================ */ +var Mailbox = xdc.useModule('ti.sysbios.knl.Mailbox'); + +var driversConfig = xdc.useModule('ti.drivers.Config'); +driversConfig.libType = driversConfig.LibType_NonInstrumented; diff --git a/rf_ieee_cmd.h b/rf_ieee_cmd.h new file mode 100644 index 0000000..463f023 --- /dev/null +++ b/rf_ieee_cmd.h @@ -0,0 +1,631 @@ +/****************************************************************************** +* Filename: rf_ieee_cmd.h +* Revised: 2017-11-10 10:42:47 +0100 (Fri, 10 Nov 2017) +* Revision: 18052 +* +* Description: CC13x2/CC26x2 API for IEEE 802.15.4 commands +* +* Copyright (c) 2015 - 2017, Texas Instruments Incorporated +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1) Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2) 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. +* +* 3) Neither the name of the ORGANIZATION 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 HOLDER 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. +* +******************************************************************************/ + +#ifndef __IEEE_CMD_H +#define __IEEE_CMD_H + +#ifndef __RFC_STRUCT +#define __RFC_STRUCT +#endif + +#ifndef __RFC_STRUCT_ATTR +#if defined(__GNUC__) +#define __RFC_STRUCT_ATTR __attribute__ ((aligned (4))) +#elif defined(__TI_ARM__) +#define __RFC_STRUCT_ATTR __attribute__ ((__packed__,aligned (4))) +#else +#define __RFC_STRUCT_ATTR +#endif +#endif + +//! \addtogroup rfc +//! @{ + +//! \addtogroup ieee_cmd +//! @{ + +#include +//#include "rf_mailbox.h" +//#include "rf_common_cmd.h" +#include "DeviceFamily.h" +#include DeviceFamily_constructPath(driverlib/rf_mailbox.h) +#include DeviceFamily_constructPath(driverlib/rf_common_cmd.h) + +typedef struct __RFC_STRUCT rfc_CMD_IEEE_RX_s rfc_CMD_IEEE_RX_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_ED_SCAN_s rfc_CMD_IEEE_ED_SCAN_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_TX_s rfc_CMD_IEEE_TX_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_CSMA_s rfc_CMD_IEEE_CSMA_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_RX_ACK_s rfc_CMD_IEEE_RX_ACK_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_ABORT_BG_s rfc_CMD_IEEE_ABORT_BG_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_MOD_CCA_s rfc_CMD_IEEE_MOD_CCA_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_MOD_FILT_s rfc_CMD_IEEE_MOD_FILT_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_MOD_SRC_MATCH_s rfc_CMD_IEEE_MOD_SRC_MATCH_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_ABORT_FG_s rfc_CMD_IEEE_ABORT_FG_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_STOP_FG_s rfc_CMD_IEEE_STOP_FG_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_CCA_REQ_s rfc_CMD_IEEE_CCA_REQ_t; +typedef struct __RFC_STRUCT rfc_ieeeRxOutput_s rfc_ieeeRxOutput_t; +typedef struct __RFC_STRUCT rfc_shortAddrEntry_s rfc_shortAddrEntry_t; +typedef struct __RFC_STRUCT rfc_ieeeRxCorrCrc_s rfc_ieeeRxCorrCrc_t; + +//! \addtogroup CMD_IEEE_RX +//! @{ +#define CMD_IEEE_RX 0x2801 +//! IEEE 802.15.4 Receive Command +struct __RFC_STRUCT rfc_CMD_IEEE_RX_s { + uint16_t commandNo; //!< The command ID number 0x2801 + uint16_t status; //!< \brief An integer telling the status of the command. This value is + //!< updated by the radio CPU during operation and may be read by the + //!< system CPU at any time. + rfc_radioOp_t *pNextOp; //!< Pointer to the next operation to run after this operation is done + ratmr_t startTime; //!< Absolute or relative start time (depending on the value of startTrigger) + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } startTrigger; //!< Identification of the trigger that starts the operation + struct { + uint8_t rule:4; //!< Condition for running next command: Rule for how to proceed + uint8_t nSkip:4; //!< Number of skips + 1 if the rule involves skipping. 0: same, 1: next, 2: skip next, ... + } condition; + uint8_t channel; //!< \brief Channel to tune to in the start of the operation
+ //!< 0: Use existing channel
+ //!< 11--26: Use as IEEE 802.15.4 channel, i.e. frequency is (2405 + 5 × (channel - 11)) MHz
+ //!< 60--207: Frequency is (2300 + channel) MHz
+ //!< Others: Reserved + struct { + uint8_t bAutoFlushCrc:1; //!< If 1, automatically remove packets with CRC error from Rx queue + uint8_t bAutoFlushIgn:1; //!< If 1, automatically remove packets that can be ignored according to frame filtering from Rx queue + uint8_t bIncludePhyHdr:1; //!< If 1, include the received PHY header field in the stored packet; otherwise discard it + uint8_t bIncludeCrc:1; //!< If 1, include the received CRC field in the stored packet; otherwise discard it + uint8_t bAppendRssi:1; //!< If 1, append an RSSI byte to the packet in the Rx queue + uint8_t bAppendCorrCrc:1; //!< If 1, append a correlation value and CRC result byte to the packet in the Rx queue + uint8_t bAppendSrcInd:1; //!< If 1, append an index from the source matching algorithm + uint8_t bAppendTimestamp:1; //!< If 1, append a timestamp to the packet in the Rx queue + } rxConfig; + dataQueue_t* pRxQ; //!< Pointer to receive queue + rfc_ieeeRxOutput_t *pOutput; //!< Pointer to output structure (NULL: Do not store results) + struct { + uint16_t frameFiltEn:1; //!< \brief 0: Disable frame filtering
+ //!< 1: Enable frame filtering + uint16_t frameFiltStop:1; //!< \brief 0: Receive all packets to the end
+ //!< 1: Stop receiving frame once frame filtering has caused the frame to be rejected. + uint16_t autoAckEn:1; //!< \brief 0: Disable auto ACK
+ //!< 1: Enable auto ACK. + uint16_t slottedAckEn:1; //!< \brief 0: Non-slotted ACK
+ //!< 1: Slotted ACK. + uint16_t autoPendEn:1; //!< \brief 0: Auto-pend disabled
+ //!< 1: Auto-pend enabled + uint16_t defaultPend:1; //!< The value of the pending data bit in auto ACK packets that are not subject to auto-pend + uint16_t bPendDataReqOnly:1; //!< \brief 0: Use auto-pend for any packet
+ //!< 1: Use auto-pend for data request packets only + uint16_t bPanCoord:1; //!< \brief 0: Device is not PAN coordinator
+ //!< 1: Device is PAN coordinator + uint16_t maxFrameVersion:2; //!< Reject frames where the frame version field in the FCF is greater than this value + uint16_t fcfReservedMask:3; //!< Value to be AND-ed with the reserved part of the FCF; frame rejected if result is non-zero + uint16_t modifyFtFilter:2; //!< \brief Treatment of MSB of frame type field before frame-type filtering:
+ //!< 0: No modification
+ //!< 1: Invert MSB
+ //!< 2: Set MSB to 0
+ //!< 3: Set MSB to 1 + uint16_t bStrictLenFilter:1; //!< \brief 0: Accept acknowledgement frames of any length >= 5
+ //!< 1: Accept only acknowledgement frames of length 5 + } frameFiltOpt; //!< Frame filtering options + struct { + uint8_t bAcceptFt0Beacon:1; //!< \brief Treatment of frames with frame type 000 (beacon):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt1Data:1; //!< \brief Treatment of frames with frame type 001 (data):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt2Ack:1; //!< \brief Treatment of frames with frame type 010 (ACK):
+ //!< 0: Reject, unless running ACK receive command
+ //!< 1: Always accept + uint8_t bAcceptFt3MacCmd:1; //!< \brief Treatment of frames with frame type 011 (MAC command):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt4Reserved:1; //!< \brief Treatment of frames with frame type 100 (reserved):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt5Reserved:1; //!< \brief Treatment of frames with frame type 101 (reserved):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt6Reserved:1; //!< \brief Treatment of frames with frame type 110 (reserved):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt7Reserved:1; //!< \brief Treatment of frames with frame type 111 (reserved):
+ //!< 0: Reject
+ //!< 1: Accept + } frameTypes; //!< Frame types to receive in frame filtering + struct { + uint8_t ccaEnEnergy:1; //!< Enable energy scan as CCA source + uint8_t ccaEnCorr:1; //!< Enable correlator based carrier sense as CCA source + uint8_t ccaEnSync:1; //!< Enable sync found based carrier sense as CCA source + uint8_t ccaCorrOp:1; //!< \brief Operator to use between energy based and correlator based CCA
+ //!< 0: Report busy channel if either ccaEnergy or ccaCorr are busy
+ //!< 1: Report busy channel if both ccaEnergy and ccaCorr are busy + uint8_t ccaSyncOp:1; //!< \brief Operator to use between sync found based CCA and the others
+ //!< 0: Always report busy channel if ccaSync is busy
+ //!< 1: Always report idle channel if ccaSync is idle + uint8_t ccaCorrThr:2; //!< Threshold for number of correlation peaks in correlator based carrier sense + } ccaOpt; //!< CCA options + int8_t ccaRssiThr; //!< RSSI threshold for CCA + uint8_t __dummy0; + uint8_t numExtEntries; //!< Number of extended address entries + uint8_t numShortEntries; //!< Number of short address entries + uint32_t* pExtEntryList; //!< Pointer to list of extended address entries + uint32_t* pShortEntryList; //!< Pointer to list of short address entries + uint64_t localExtAddr; //!< The extended address of the local device + uint16_t localShortAddr; //!< The short address of the local device + uint16_t localPanID; //!< The PAN ID of the local device + uint16_t __dummy1; + uint8_t __dummy2; + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } endTrigger; //!< Trigger that causes the device to end the Rx operation + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to end the Rx + //!< operation +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_ED_SCAN +//! @{ +#define CMD_IEEE_ED_SCAN 0x2802 +//! IEEE 802.15.4 Energy Detect Scan Command +struct __RFC_STRUCT rfc_CMD_IEEE_ED_SCAN_s { + uint16_t commandNo; //!< The command ID number 0x2802 + uint16_t status; //!< \brief An integer telling the status of the command. This value is + //!< updated by the radio CPU during operation and may be read by the + //!< system CPU at any time. + rfc_radioOp_t *pNextOp; //!< Pointer to the next operation to run after this operation is done + ratmr_t startTime; //!< Absolute or relative start time (depending on the value of startTrigger) + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } startTrigger; //!< Identification of the trigger that starts the operation + struct { + uint8_t rule:4; //!< Condition for running next command: Rule for how to proceed + uint8_t nSkip:4; //!< Number of skips + 1 if the rule involves skipping. 0: same, 1: next, 2: skip next, ... + } condition; + uint8_t channel; //!< \brief Channel to tune to in the start of the operation
+ //!< 0: Use existing channel
+ //!< 11--26: Use as IEEE 802.15.4 channel, i.e. frequency is (2405 + 5 × (channel - 11)) MHz
+ //!< 60--207: Frequency is (2300 + channel) MHz
+ //!< Others: Reserved + struct { + uint8_t ccaEnEnergy:1; //!< Enable energy scan as CCA source + uint8_t ccaEnCorr:1; //!< Enable correlator based carrier sense as CCA source + uint8_t ccaEnSync:1; //!< Enable sync found based carrier sense as CCA source + uint8_t ccaCorrOp:1; //!< \brief Operator to use between energy based and correlator based CCA
+ //!< 0: Report busy channel if either ccaEnergy or ccaCorr are busy
+ //!< 1: Report busy channel if both ccaEnergy and ccaCorr are busy + uint8_t ccaSyncOp:1; //!< \brief Operator to use between sync found based CCA and the others
+ //!< 0: Always report busy channel if ccaSync is busy
+ //!< 1: Always report idle channel if ccaSync is idle + uint8_t ccaCorrThr:2; //!< Threshold for number of correlation peaks in correlator based carrier sense + } ccaOpt; //!< CCA options + int8_t ccaRssiThr; //!< RSSI threshold for CCA + uint8_t __dummy0; + int8_t maxRssi; //!< The maximum RSSI recorded during the ED scan + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } endTrigger; //!< Trigger that causes the device to end the Rx operation + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to end the Rx + //!< operation +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_TX +//! @{ +#define CMD_IEEE_TX 0x2C01 +//! IEEE 802.15.4 Transmit Command +struct __RFC_STRUCT rfc_CMD_IEEE_TX_s { + uint16_t commandNo; //!< The command ID number 0x2C01 + uint16_t status; //!< \brief An integer telling the status of the command. This value is + //!< updated by the radio CPU during operation and may be read by the + //!< system CPU at any time. + rfc_radioOp_t *pNextOp; //!< Pointer to the next operation to run after this operation is done + ratmr_t startTime; //!< Absolute or relative start time (depending on the value of startTrigger) + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } startTrigger; //!< Identification of the trigger that starts the operation + struct { + uint8_t rule:4; //!< Condition for running next command: Rule for how to proceed + uint8_t nSkip:4; //!< Number of skips + 1 if the rule involves skipping. 0: same, 1: next, 2: skip next, ... + } condition; + struct { + uint8_t bIncludePhyHdr:1; //!< \brief 0: Find PHY header automatically
+ //!< 1: Insert PHY header from the buffer + uint8_t bIncludeCrc:1; //!< \brief 0: Append automatically calculated CRC
+ //!< 1: Insert FCS (CRC) from the buffer + uint8_t :1; + uint8_t payloadLenMsb:5; //!< \brief Most significant bits of payload length. Should only be non-zero to create long + //!< non-standard packets for test purposes + } txOpt; + uint8_t payloadLen; //!< Number of bytes in the payload + uint8_t* pPayload; //!< Pointer to payload buffer of size payloadLen + ratmr_t timeStamp; //!< Time stamp of transmitted frame +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_CSMA +//! @{ +#define CMD_IEEE_CSMA 0x2C02 +//! IEEE 802.15.4 CSMA-CA Command +struct __RFC_STRUCT rfc_CMD_IEEE_CSMA_s { + uint16_t commandNo; //!< The command ID number 0x2C02 + uint16_t status; //!< \brief An integer telling the status of the command. This value is + //!< updated by the radio CPU during operation and may be read by the + //!< system CPU at any time. + rfc_radioOp_t *pNextOp; //!< Pointer to the next operation to run after this operation is done + ratmr_t startTime; //!< Absolute or relative start time (depending on the value of startTrigger) + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } startTrigger; //!< Identification of the trigger that starts the operation + struct { + uint8_t rule:4; //!< Condition for running next command: Rule for how to proceed + uint8_t nSkip:4; //!< Number of skips + 1 if the rule involves skipping. 0: same, 1: next, 2: skip next, ... + } condition; + uint16_t randomState; //!< The state of the pseudo-random generator + uint8_t macMaxBE; //!< The IEEE 802.15.4 MAC parameter macMaxBE + uint8_t macMaxCSMABackoffs; //!< The IEEE 802.15.4 MAC parameter macMaxCSMABackoffs + struct { + uint8_t initCW:5; //!< The initialization value for the CW parameter + uint8_t bSlotted:1; //!< \brief 0: non-slotted CSMA
+ //!< 1: slotted CSMA + uint8_t rxOffMode:2; //!< \brief 0: RX stays on during CSMA backoffs
+ //!< 1: The CSMA-CA algorithm will suspend the receiver if no frame is being received
+ //!< 2: The CSMA-CA algorithm will suspend the receiver if no frame is being received, + //!< or after finishing it (including auto ACK) otherwise
+ //!< 3: The CSMA-CA algorithm will suspend the receiver immediately during back-offs + } csmaConfig; + uint8_t NB; //!< The NB parameter from the IEEE 802.15.4 CSMA-CA algorithm + uint8_t BE; //!< The BE parameter from the IEEE 802.15.4 CSMA-CA algorithm + uint8_t remainingPeriods; //!< The number of remaining periods from a paused backoff countdown + int8_t lastRssi; //!< RSSI measured at the last CCA operation + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } endTrigger; //!< Trigger that causes the device to end the CSMA-CA operation + ratmr_t lastTimeStamp; //!< Time of the last CCA operation + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to end the + //!< CSMA-CA operation +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_RX_ACK +//! @{ +#define CMD_IEEE_RX_ACK 0x2C03 +//! IEEE 802.15.4 Receive Acknowledgement Command +struct __RFC_STRUCT rfc_CMD_IEEE_RX_ACK_s { + uint16_t commandNo; //!< The command ID number 0x2C03 + uint16_t status; //!< \brief An integer telling the status of the command. This value is + //!< updated by the radio CPU during operation and may be read by the + //!< system CPU at any time. + rfc_radioOp_t *pNextOp; //!< Pointer to the next operation to run after this operation is done + ratmr_t startTime; //!< Absolute or relative start time (depending on the value of startTrigger) + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } startTrigger; //!< Identification of the trigger that starts the operation + struct { + uint8_t rule:4; //!< Condition for running next command: Rule for how to proceed + uint8_t nSkip:4; //!< Number of skips + 1 if the rule involves skipping. 0: same, 1: next, 2: skip next, ... + } condition; + uint8_t seqNo; //!< Sequence number to expect + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } endTrigger; //!< Trigger that causes the device to give up acknowledgement reception + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to give up + //!< acknowledgement reception +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_ABORT_BG +//! @{ +#define CMD_IEEE_ABORT_BG 0x2C04 +//! IEEE 802.15.4 Abort Background Level Command +struct __RFC_STRUCT rfc_CMD_IEEE_ABORT_BG_s { + uint16_t commandNo; //!< The command ID number 0x2C04 + uint16_t status; //!< \brief An integer telling the status of the command. This value is + //!< updated by the radio CPU during operation and may be read by the + //!< system CPU at any time. + rfc_radioOp_t *pNextOp; //!< Pointer to the next operation to run after this operation is done + ratmr_t startTime; //!< Absolute or relative start time (depending on the value of startTrigger) + struct { + uint8_t triggerType:4; //!< The type of trigger + uint8_t bEnaCmd:1; //!< \brief 0: No alternative trigger command
+ //!< 1: CMD_TRIGGER can be used as an alternative trigger + uint8_t triggerNo:2; //!< The trigger number of the CMD_TRIGGER command that triggers this action + uint8_t pastTrig:1; //!< \brief 0: A trigger in the past is never triggered, or for start of commands, give an error
+ //!< 1: A trigger in the past is triggered as soon as possible + } startTrigger; //!< Identification of the trigger that starts the operation + struct { + uint8_t rule:4; //!< Condition for running next command: Rule for how to proceed + uint8_t nSkip:4; //!< Number of skips + 1 if the rule involves skipping. 0: same, 1: next, 2: skip next, ... + } condition; +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_MOD_CCA +//! @{ +#define CMD_IEEE_MOD_CCA 0x2001 +//! IEEE 802.15.4 Modify CCA Parameter Command +struct __RFC_STRUCT rfc_CMD_IEEE_MOD_CCA_s { + uint16_t commandNo; //!< The command ID number 0x2001 + struct { + uint8_t ccaEnEnergy:1; //!< Enable energy scan as CCA source + uint8_t ccaEnCorr:1; //!< Enable correlator based carrier sense as CCA source + uint8_t ccaEnSync:1; //!< Enable sync found based carrier sense as CCA source + uint8_t ccaCorrOp:1; //!< \brief Operator to use between energy based and correlator based CCA
+ //!< 0: Report busy channel if either ccaEnergy or ccaCorr are busy
+ //!< 1: Report busy channel if both ccaEnergy and ccaCorr are busy + uint8_t ccaSyncOp:1; //!< \brief Operator to use between sync found based CCA and the others
+ //!< 0: Always report busy channel if ccaSync is busy
+ //!< 1: Always report idle channel if ccaSync is idle + uint8_t ccaCorrThr:2; //!< Threshold for number of correlation peaks in correlator based carrier sense + } newCcaOpt; //!< New value of ccaOpt for the running background level operation + int8_t newCcaRssiThr; //!< New value of ccaRssiThr for the running background level operation +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_MOD_FILT +//! @{ +#define CMD_IEEE_MOD_FILT 0x2002 +//! IEEE 802.15.4 Modify Frame Filtering Parameter Command +struct __RFC_STRUCT rfc_CMD_IEEE_MOD_FILT_s { + uint16_t commandNo; //!< The command ID number 0x2002 + struct { + uint16_t frameFiltEn:1; //!< \brief 0: Disable frame filtering
+ //!< 1: Enable frame filtering + uint16_t frameFiltStop:1; //!< \brief 0: Receive all packets to the end
+ //!< 1: Stop receiving frame once frame filtering has caused the frame to be rejected. + uint16_t autoAckEn:1; //!< \brief 0: Disable auto ACK
+ //!< 1: Enable auto ACK. + uint16_t slottedAckEn:1; //!< \brief 0: Non-slotted ACK
+ //!< 1: Slotted ACK. + uint16_t autoPendEn:1; //!< \brief 0: Auto-pend disabled
+ //!< 1: Auto-pend enabled + uint16_t defaultPend:1; //!< The value of the pending data bit in auto ACK packets that are not subject to auto-pend + uint16_t bPendDataReqOnly:1; //!< \brief 0: Use auto-pend for any packet
+ //!< 1: Use auto-pend for data request packets only + uint16_t bPanCoord:1; //!< \brief 0: Device is not PAN coordinator
+ //!< 1: Device is PAN coordinator + uint16_t maxFrameVersion:2; //!< Reject frames where the frame version field in the FCF is greater than this value + uint16_t fcfReservedMask:3; //!< Value to be AND-ed with the reserved part of the FCF; frame rejected if result is non-zero + uint16_t modifyFtFilter:2; //!< \brief Treatment of MSB of frame type field before frame-type filtering:
+ //!< 0: No modification
+ //!< 1: Invert MSB
+ //!< 2: Set MSB to 0
+ //!< 3: Set MSB to 1 + uint16_t bStrictLenFilter:1; //!< \brief 0: Accept acknowledgement frames of any length >= 5
+ //!< 1: Accept only acknowledgement frames of length 5 + } newFrameFiltOpt; //!< New value of frameFiltOpt for the running background level operation + struct { + uint8_t bAcceptFt0Beacon:1; //!< \brief Treatment of frames with frame type 000 (beacon):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt1Data:1; //!< \brief Treatment of frames with frame type 001 (data):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt2Ack:1; //!< \brief Treatment of frames with frame type 010 (ACK):
+ //!< 0: Reject, unless running ACK receive command
+ //!< 1: Always accept + uint8_t bAcceptFt3MacCmd:1; //!< \brief Treatment of frames with frame type 011 (MAC command):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt4Reserved:1; //!< \brief Treatment of frames with frame type 100 (reserved):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt5Reserved:1; //!< \brief Treatment of frames with frame type 101 (reserved):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt6Reserved:1; //!< \brief Treatment of frames with frame type 110 (reserved):
+ //!< 0: Reject
+ //!< 1: Accept + uint8_t bAcceptFt7Reserved:1; //!< \brief Treatment of frames with frame type 111 (reserved):
+ //!< 0: Reject
+ //!< 1: Accept + } newFrameTypes; //!< New value of frameTypes for the running background level operation +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_MOD_SRC_MATCH +//! @{ +#define CMD_IEEE_MOD_SRC_MATCH 0x2003 +//! IEEE 802.15.4 Enable/Disable Source Matching Entry Command +struct __RFC_STRUCT rfc_CMD_IEEE_MOD_SRC_MATCH_s { + uint16_t commandNo; //!< The command ID number 0x2003 + struct { + uint8_t bEnable:1; //!< \brief 0: Disable entry
+ //!< 1: Enable entry + uint8_t srcPend:1; //!< New value of the pending bit for the entry + uint8_t entryType:1; //!< \brief 0: Short address
+ //!< 1: Extended address + } options; + uint8_t entryNo; //!< Index of entry to enable or disable +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_ABORT_FG +//! @{ +#define CMD_IEEE_ABORT_FG 0x2401 +//! IEEE 802.15.4 Abort Foreground Level Command +struct __RFC_STRUCT rfc_CMD_IEEE_ABORT_FG_s { + uint16_t commandNo; //!< The command ID number 0x2401 +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_STOP_FG +//! @{ +#define CMD_IEEE_STOP_FG 0x2402 +//! IEEE 802.15.4 Gracefully Stop Foreground Level Command +struct __RFC_STRUCT rfc_CMD_IEEE_STOP_FG_s { + uint16_t commandNo; //!< The command ID number 0x2402 +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup CMD_IEEE_CCA_REQ +//! @{ +#define CMD_IEEE_CCA_REQ 0x2403 +//! IEEE 802.15.4 CCA and RSSI Information Request Command +struct __RFC_STRUCT rfc_CMD_IEEE_CCA_REQ_s { + uint16_t commandNo; //!< The command ID number 0x2403 + int8_t currentRssi; //!< The RSSI currently observed on the channel + int8_t maxRssi; //!< The maximum RSSI observed on the channel since Rx was started + struct { + uint8_t ccaState:2; //!< \brief Value of the current CCA state
+ //!< 0: Idle
+ //!< 1: Busy
+ //!< 2: Invalid + uint8_t ccaEnergy:2; //!< \brief Value of the current energy detect CCA state
+ //!< 0: Idle
+ //!< 1: Busy
+ //!< 2: Invalid + uint8_t ccaCorr:2; //!< \brief Value of the current correlator based carrier sense CCA state
+ //!< 0: Idle
+ //!< 1: Busy
+ //!< 2: Invalid + uint8_t ccaSync:1; //!< \brief Value of the current sync found based carrier sense CCA state
+ //!< 0: Idle
+ //!< 1: Busy + } ccaInfo; +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup ieeeRxOutput +//! @{ +//! Output structure for CMD_IEEE_RX + +struct __RFC_STRUCT rfc_ieeeRxOutput_s { + uint8_t nTxAck; //!< Total number of transmitted ACK frames + uint8_t nRxBeacon; //!< Number of received beacon frames + uint8_t nRxData; //!< Number of received data frames + uint8_t nRxAck; //!< Number of received acknowledgement frames + uint8_t nRxMacCmd; //!< Number of received MAC command frames + uint8_t nRxReserved; //!< Number of received frames with reserved frame type + uint8_t nRxNok; //!< Number of received frames with CRC error + uint8_t nRxIgnored; //!< Number of frames received that are to be ignored + uint8_t nRxBufFull; //!< Number of received frames discarded because the Rx buffer was full + int8_t lastRssi; //!< RSSI of last received frame + int8_t maxRssi; //!< Highest RSSI observed in the operation + uint8_t __dummy0; + ratmr_t beaconTimeStamp; //!< Time stamp of last received beacon frame +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup shortAddrEntry +//! @{ +//! Structure for short address entries + +struct __RFC_STRUCT rfc_shortAddrEntry_s { + uint16_t shortAddr; //!< Short address + uint16_t panId; //!< PAN ID +} __RFC_STRUCT_ATTR; + +//! @} + +//! \addtogroup ieeeRxCorrCrc +//! @{ +//! Receive status byte that may be appended to message in receive buffer + +struct __RFC_STRUCT rfc_ieeeRxCorrCrc_s { + struct { + uint8_t corr:6; //!< The correlation value + uint8_t bIgnore:1; //!< 1 if the packet should be rejected by frame filtering, 0 otherwise + uint8_t bCrcErr:1; //!< 1 if the packet was received with CRC error, 0 otherwise + } status; +} __RFC_STRUCT_ATTR; + +//! @} + +//! @} +//! @} +#endif diff --git a/source/command_handler.c b/source/command_handler.c new file mode 100644 index 0000000..e288779 --- /dev/null +++ b/source/command_handler.c @@ -0,0 +1,384 @@ +/****************************************************************************** +* Filename: command_handler.c +* +* Description: Source file for the command handler. This module handles +* commands and command responses on the host interface. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#include "command_handler.h" +#include "host_if.h" +#include "version.h" +#include "common.h" + +#define DEFAULT_FW_ID 100 +#ifndef FW_ID +#define FW_ID DEFAULT_FW_ID +#endif + +/* driverlib header files */ +#include "DeviceFamily.h" +#include DeviceFamily_constructPath(driverlib/chipinfo.h) + + +// Storage for response packets +static CommandResponsePacket_Obj CommandHandler_commandResponse; +static CommandPingResponsePacket_Obj CommandHandler_commandPingResponse; + +// Local functions +static void CommandHandler_constructResponsePacket(CommandResponsePacket_Obj* cmdResponse, uint8_t status); +static void CommandHandler_constructPingResponsePacket(CommandPingResponsePacket_Obj* cmdPingResponse, uint8_t status); +static void CommandHandler_forwardResponsePacket(uint8_t* cmdResponse, uint16_t packetLength); +static uint8_t CommandHandler_computeFcs(uint8_t* dataBuffer, uint16_t length); +static uint8_t CommandHandler_verifyPacket(CommandPacket_Obj* cmdPacket); +static uint8_t CommandHandler_searchForPacketHeader(CommandPacket_Obj* cmdPacket); +static void CommandHandler_flushHostInterface(uint16_t expectedLength); +static uint16_t CommandHandler_getChipId(void); +static uint8_t CommandHandler_getChipRevision(void); +static uint16_t CommandHandler_getFwRevision(void); +static uint8_t CommandHandler_getFwId(void); + + +uint8_t CommandHandler_getCommand(CommandPacket_Obj* cmdPacket) +{ + // Search for packet header + uint8_t status = CommandHandler_searchForPacketHeader(cmdPacket); + if(status != COMMAND_OK) + { + return status; + } + + // Compute length of remaining packet content + uint16_t remainingLen = cmdPacket->genPacketHdr.packetLen + sizeof(CommandPacketFooter_Obj); + + // Check if command length is valid + if(remainingLen > (MAX_COMMAND_PAYLOAD_LENGTH + sizeof(CommandPacketFooter_Obj))) + { + // The length indicates that the packet is longer than maximum length of a + // command packet. + + // The remaining bytes must be read/removed from host IF + CommandHandler_flushHostInterface(remainingLen); + return COMMAND_INVALID; + } + + // Start timeout timer + + // Read the rest of the packet + HostIF_readBuffer((uint8_t*) cmdPacket->payload, remainingLen); + + status = CommandHandler_verifyPacket(cmdPacket); + return status; +} + + +void CommandHandler_respondCommand(uint8_t status) +{ + CommandHandler_constructResponsePacket(&CommandHandler_commandResponse, status); + CommandHandler_forwardResponsePacket((uint8_t*)&CommandHandler_commandResponse, sizeof(CommandHandler_commandResponse)); +} + + +void CommandHandler_respondPingCommand(uint8_t status) +{ + CommandHandler_constructPingResponsePacket(&CommandHandler_commandPingResponse, status); + CommandHandler_forwardResponsePacket((uint8_t*)&CommandHandler_commandPingResponse, sizeof(CommandHandler_commandPingResponse)); +} + + +//! \brief Construct command response packet +//! +//! \param[in] cmdResponse +//! Pointer to buffer for the command response packet +//! +//! \param[in] status +//! Status to set in the command response +//! +//! \return None +void CommandHandler_constructResponsePacket(CommandResponsePacket_Obj* cmdResponse, uint8_t status) +{ + cmdResponse->genPacketHdr.sof = START_OF_FRAME_DELIMITER; + cmdResponse->genPacketHdr.packetType = PACKET_TYPE_COMMAND_RESPONSE; + cmdResponse->genPacketHdr.packetLen = COMMAND_RESPONSE_PAYLOAD_LENGTH; + cmdResponse->status = status; + cmdResponse->cmdPacketFtr.fcs = CommandHandler_computeFcs(&cmdResponse->genPacketHdr.packetType, \ + sizeof(cmdResponse->genPacketHdr.packetType) + sizeof(cmdResponse->genPacketHdr.packetLen) + sizeof(cmdResponse->status)); + + cmdResponse->cmdPacketFtr.genPacketFooter.eof = END_OF_FRAME_DELIMITER; +} + + +//! \brief Construct command response packet +//! +//! \param[in] cmdResponse +//! Pointer to buffer for the command response packet +//! +//! \param[in] status +//! Status to set in the command response +//! +//! \return None +void CommandHandler_constructPingResponsePacket(CommandPingResponsePacket_Obj* cmdPingResponse, uint8_t status) +{ + cmdPingResponse->genPacketHdr.sof = START_OF_FRAME_DELIMITER; + + cmdPingResponse->genPacketHdr.packetType = PACKET_TYPE_COMMAND_RESPONSE; + cmdPingResponse->genPacketHdr.packetLen = COMMAND_PING_RESPONSE_PAYLOAD_LENGTH; + + cmdPingResponse->status = status; + cmdPingResponse->chipId = CommandHandler_getChipId(); + cmdPingResponse->chipRev = CommandHandler_getChipRevision(); + cmdPingResponse->fwId = CommandHandler_getFwId(); + cmdPingResponse->fwRev = CommandHandler_getFwRevision(); + + cmdPingResponse->cmdPacketFtr.fcs = CommandHandler_computeFcs(&cmdPingResponse->genPacketHdr.packetType, \ + sizeof(cmdPingResponse->genPacketHdr.packetType) + sizeof(cmdPingResponse->genPacketHdr.packetLen) + COMMAND_PING_RESPONSE_PAYLOAD_LENGTH); + + cmdPingResponse->cmdPacketFtr.genPacketFooter.eof = END_OF_FRAME_DELIMITER; +} + + +//! \brief Forward command response packet to host +//! +//! \param[in] cmdResponse +//! Pointer to command response packet +//! +//! \param[in] packetLength +//! Length of command response packet +//! +//! \return None +void CommandHandler_forwardResponsePacket(uint8_t* cmdResponse, uint16_t packetLength) +{ + HostIF_writeBuffer((uint8_t*)cmdResponse, packetLength); +} + + +//! \brief Compute frame check sequence value from a data buffer +//! +//! \param[in] dataBuffer +//! Pointer to start of the data buffer to compute FCS over. +//! +//! \param[in] length +//! Length of the data buffer to compute FCS over. +//! +//! \return Computed frame sequence value +uint8_t CommandHandler_computeFcs(uint8_t* dataBuffer, uint16_t length) +{ + uint32_t result=0; + for(int i=0; igenPacketHdr) + cmdPacket->genPacketHdr.packetLen; + + uint8_t expectedFcs = CommandHandler_computeFcs(&cmdPacket->genPacketHdr.packetType, + PACKET_TYPE_LENGTH + PACKET_LEN_LENGTH + cmdPacket->genPacketHdr.packetLen); + if(expectedFcs != *((uint8_t*)(cmdPacket) + fcsIndex)) + { + status = COMMAND_FCS_FAILED; + } + + return status; +} + + +//! \brief Search for packet header in bytes read from host interface. +//! +//! This function blocks until first byte of SOF is read from host. +//! It then blocks until a second byte is read. +//! - If the two bytes does not match SOF it returns with error. +//! - If the two bytes matches SOF, it blocks until the complete packet +//! header is read, and returns success. +//! +//! \param[in] cmdPacket +//! Pointer to buffer for command packet. If a successful packet +//! header is found, this function will populate the packet header values +//! of the given packet. +//! +//! \return COMMAND_OK or COMMAND_INVALID +static uint8_t CommandHandler_searchForPacketHeader(CommandPacket_Obj* cmdPacket) +{ + uint8_t status = COMMAND_INVALID; + uint8_t readValue = 0; + uint8_t sofByte0Value = START_OF_FRAME_DELIMITER & 0xFF; + uint8_t sofByte1Value = (START_OF_FRAME_DELIMITER & 0xFF00) >> 8; + + // Read bytes from host IF until 0xFD is found (first byte of Start of Frame) + while(readValue != sofByte0Value) + { + HostIF_readBuffer(&readValue, 1); + } + + // It is expected that next byte is second byte of SOF (0x50) + HostIF_readBuffer(&readValue, 1); + if(readValue != sofByte1Value) + { + // Packet is invalid + status = COMMAND_INVALID; + } + else + { + // Correct SOF is found. Read rest of header + cmdPacket->genPacketHdr.sof = START_OF_FRAME_DELIMITER; + HostIF_readBuffer((uint8_t*)&(cmdPacket->genPacketHdr.packetType), sizeof(GeneralPacketHeader_Obj) - SOF_LENGTH); + status = COMMAND_OK; + } + + return status; +} + + +//! \brief Read and throw bytes from Host IF. +//! +//! This function is used to flush the host interface. It can be used in +//! the case where the received command is too long (no available buffer +//! space for received command). The function blocks until the expected +//! number of bytes are read. +//! +//! \param[in] expectedLength +//! Expected number of bytes to receive and throw. +//! +//! \return None +static void CommandHandler_flushHostInterface(uint16_t expectedLength) +{ +#define READ_CHUNK_SIZE 8 + uint8_t buffer[READ_CHUNK_SIZE]; + uint16_t remainingBytes = expectedLength; + uint16_t readSize = 0; + int32_t numReadBytes; + + while(remainingBytes) + { + readSize = (remainingBytes < READ_CHUNK_SIZE) ? remainingBytes : READ_CHUNK_SIZE; + numReadBytes = HostIF_readBuffer(buffer, readSize); + remainingBytes -= readSize; + if(numReadBytes < READ_CHUNK_SIZE && remainingBytes) + { + // Number of bytes was less than the chunk size. A UART read timeout + // has probably occured. Abort this loop to avoid be stuck here + // forever waiting for data that are not received. + break; + } + } +} + + +//! \brief Get chip id +//! +//! \return Chip id hex value +static uint16_t CommandHandler_getChipId(void) +{ + ChipType_t chipType = ChipInfo_GetChipType(); + switch(chipType) + { + case CHIP_TYPE_CC1310: + return 0x1310; + case CHIP_TYPE_CC1350: + return 0x1350; + case CHIP_TYPE_CC2620: + return 0x2620; + case CHIP_TYPE_CC2630: + return 0x2630; + case CHIP_TYPE_CC2640: + return 0x2640; + case CHIP_TYPE_CC2650: + return 0x2650; +/* case CHIP_TYPE_CC2652: + return 0x2652; + case CHIP_TYPE_CC1352: + return 0x1352; + case CHIP_TYPE_CC1352P: + return 0x1353; + case CHIP_TYPE_CC1312: + return 0x1312;*/ + default: + return 0; + } +} + + +//! \brief Get chip revision +//! +//! \return Chip revision value +static uint8_t CommandHandler_getChipRevision(void) +{ + HwRevision_t hwRevision = ChipInfo_GetHwRevision(); + + switch(hwRevision) + { + case HWREV_1_0: + return 0x10; + case HWREV_2_0: + return 0x20; + case HWREV_2_1: + return 0x21; + case HWREV_2_2: + return 0x22; + case HWREV_2_3: + return 0x23; + default: + return 0; + } +} + + +//! \brief Get firmware revision +//! +//! \return Firmware major revision (MSB) and minor revision (LSB) +static uint16_t CommandHandler_getFwRevision(void) +{ + return (MAJOR_VERSION << 8) | MINOR_VERSION; +} + +//! \brief Get firmware ID +//! +//! \return Firmware Id +static uint8_t CommandHandler_getFwId(void) +{ + + return FW_ID; +} + diff --git a/source/command_handler.h b/source/command_handler.h new file mode 100644 index 0000000..7b743a4 --- /dev/null +++ b/source/command_handler.h @@ -0,0 +1,82 @@ +/****************************************************************************** +* Filename: command_handler.h +* +* Description: Header file for the command handler. This module handles +* commands and command responses on the host interface. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef COMMAND_HANDLER_H +#define COMMAND_HANDLER_H + +#include "command_packet.h" + +// Status values +#define COMMAND_OK 0 +#define COMMAND_TIMED_OUT 1 +#define COMMAND_FCS_FAILED 2 +#define COMMAND_INVALID 3 +#define COMMAND_INVALID_STATE 4 +#define COMMAND_INVALID_PARAMETER 5 + +//! \brief Get command from host interface. +//! +//! This function will block until either: +//! - A complete command is successfully received, or +//! - A complete command or parts of it is received but there are errors +//! in the command. +//! +//! \param[in] cmdPacket +//! Pointer to buffer large enough for the received command packet. +//! The buffer must be allocated by the caller. +//! +//! \return \li \c COMMAND_OK - successful reception of a command. +//! \li \c COMMAND_TIMED_OUT - Parts of the command was received but not the +//! complete command. +//! \li \c COMMAND_INVALID - The command is invalid. +extern uint8_t CommandHandler_getCommand(CommandPacket_Obj* cmdPacket); + + +//! \brief Send command response on host interface. +//! +//! \param[in] status - Status of the received host command. +extern void CommandHandler_respondCommand(uint8_t status); + + +//! \brief Send command response for CMD_PING on host interface. +//! +//! \param[in] status - Status of the received ping command. +extern void CommandHandler_respondPingCommand(uint8_t status); + +#endif + diff --git a/source/command_packet.h b/source/command_packet.h new file mode 100644 index 0000000..1289d27 --- /dev/null +++ b/source/command_packet.h @@ -0,0 +1,128 @@ +/****************************************************************************** +* Filename: command_packet.h +* +* Description: Defines for the command packet format. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef COMMAND_PACKET_H +#define COMMAND_PACKET_H + +#include "general_packet.h" + +// Command packet format +#define MAX_COMMAND_PAYLOAD_LENGTH 255 +#define FCS_LENGTH 1 +#define COMMAND_RESPONSE_PAYLOAD_LENGTH 1 +#define COMMAND_PING_RESPONSE_PAYLOAD_LENGTH 7 + +// Command packet types +#define PACKET_TYPE_COMMAND_PING 0x40 +#define PACKET_TYPE_COMMAND_START 0x41 +#define PACKET_TYPE_COMMAND_STOP 0x42 +#define PACKET_TYPE_COMMAND_PAUSE 0x43 +#define PACKET_TYPE_COMMAND_RESUME 0x44 +#define PACKET_TYPE_COMMAND_CFG_FREQUENCY 0x45 +#define PACKET_TYPE_COMMAND_CFG_PHY 0x47 + +// Test command packet types +#define PACKET_TYPE_TEST_COMMAND_TRANSMIT_SEQUENCE 0x60 + +// Command response packet types +#define PACKET_TYPE_COMMAND_RESPONSE 0x80 + +// Command packet payload fields +#define COMMAND_CFG_FREQUENCY_DATA_LENGTH 4 +#define COMMAND_CFG_FREQUENCY_FREQ_OFFSET 0 // Offset of frequency field in command data +#define COMMAND_CFG_FREQUENCY_FRACT_FREQ_OFFSET 2 // Offset of fractional frequency field in command data +#define COMMAND_CFG_PHY_DATA_LENGTH 1 +#define COMMAND_CFG_PHY_DATA_OFFSET 0 // Offset of PHY number field in command data + + +// Test Command packet payload fields +#define TEST_COMMAND_TRANSMIT_SEQUENCE_REPETITIONS_SIZE 2 +#define TEST_COMMAND_TRANSMIT_SEQUENCE_REPETITIONS_OFFSET 0 // offset from start of command data +#define TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_LENGTH_SIZE 2 +#define TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_LENGTH_OFFSET 0 // Offset from start of serie +#define TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_NUMBER_SIZE 2 +#define TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_NUMBER_OFFSET 2 // Offset from start of serie + + +// Command Response Status values +#define STATUS_OK 0 +#define STATUS_TIMEOUT 1 +#define STATUS_FCS_FAILED 2 + +// Command packet footer +#pragma pack(1) +typedef struct CommandPacketFooter_Obj +{ + uint8_t fcs; + GeneralPacketFooter_Obj genPacketFooter; +} CommandPacketFooter_Obj; + +// Command packet +#pragma pack(1) +typedef struct CommandPacket_Obj +{ + GeneralPacketHeader_Obj genPacketHdr; + uint8_t payload[MAX_COMMAND_PAYLOAD_LENGTH + sizeof(CommandPacketFooter_Obj)]; // space for payload and general packet footer +} CommandPacket_Obj; + +// Command response packet +#pragma pack(1) +typedef struct CommandResponsePacket_Obj +{ + GeneralPacketHeader_Obj genPacketHdr; + uint8_t status; + CommandPacketFooter_Obj cmdPacketFtr; +} CommandResponsePacket_Obj; + + +// Command response packet +#pragma pack(1) +typedef struct CommandPingResponsePacket_Obj +{ + GeneralPacketHeader_Obj genPacketHdr; + uint8_t status; + uint16_t chipId; + uint8_t chipRev; + uint8_t fwId; + uint16_t fwRev; + CommandPacketFooter_Obj cmdPacketFtr; +} CommandPingResponsePacket_Obj; + + + +#endif + diff --git a/source/common.c b/source/common.c new file mode 100644 index 0000000..54a4af4 --- /dev/null +++ b/source/common.c @@ -0,0 +1,76 @@ +/****************************************************************************** +* Filename: common.c +* +* Description: Source file common functionality. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include "common.h" + +inline uint32_t Common_get32BitValueLE(uint8_t* buffer) +{ + uint32_t value = 0; + value |= (uint32_t)buffer[3] << 24; + value |= (uint32_t)buffer[2] << 16; + value |= (uint32_t)buffer[1] << 8; + value |= (uint32_t)buffer[0]; + + return value; +} + + +inline uint16_t Common_get16BitValueLE(uint8_t* buffer) +{ + uint16_t value = 0; + value |= (uint16_t)buffer[1] << 8; + value |= (uint16_t)buffer[0]; + + return value; +} + + +inline void Common_set16BitValueLE(uint8_t* buffer, uint16_t value) +{ + buffer[0] = value & 0xFF; + buffer[1] = (value & 0xFF00) >> 8; +} + + +inline void Common_set32BitValueLE(uint8_t* buffer, uint32_t value) +{ + buffer[0] = value & 0xFF; + buffer[1] = (value & 0xFF00) >> 8; + buffer[2] = (value & 0xFF0000) >> 16; + buffer[3] = (value & 0xFF000000) >> 24; +} diff --git a/source/common.h b/source/common.h new file mode 100644 index 0000000..1b08950 --- /dev/null +++ b/source/common.h @@ -0,0 +1,95 @@ +/****************************************************************************** +* Filename: common.h +* +* Description: Header file for common functionality +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef COMMON_H +#define COMMON_H + +#include + +//! \brief Get little endian value of 32 bit byte field that may start at an +//! unaligned address. +//! (32 bit pointer access should be avoided at non-word aligned +//! address. Use this function instead. ) +//! +//! \param[in] buffer +//! Pointer to first byte of the 32 bit value +//! +//! \return 32 bit value +//! +extern inline uint32_t Common_get32BitValueLE(uint8_t* buffer); + + +//! \brief Get little endian value of 16 bit byte field that may start at an +//! unaligned address. +//! (16 bit pointer access should be avoided at non-word aligned +//! address. Use this function instead. ) +//! +//! \param[in] buffer +//! Pointer to first byte of the 16 bit value +//! +//! \return 16 bit value +//! +extern inline uint16_t Common_get16BitValueLE(uint8_t* buffer); + + +//! \brief Set little endian value of 16 bit byte field that may start at an +//! unaligned address. +//! (16 bit pointer access should be avoided at non-word aligned +//! address. Use this function instead. ) +//! +//! \param[out] buffer +//! Pointer to first byte of the 16 bit value +//! +//! \param[in] value to set +//! +extern inline void Common_set16BitValueLE(uint8_t* buffer, uint16_t value); + + +//! \brief Set little endian value of 32 bit byte field that may start at an +//! unaligned address. +//! (32 bit pointer access should be avoided at non-word aligned +//! address. Use this function instead. ) +//! +//! \param[out] buffer +//! Pointer to first byte of the 32 bit value +//! +//! \param[in] value to set +//! +extern inline void Common_set32BitValueLE(uint8_t* buffer, uint32_t value); + +#endif + diff --git a/source/control_task.c b/source/control_task.c new file mode 100644 index 0000000..c29a71a --- /dev/null +++ b/source/control_task.c @@ -0,0 +1,303 @@ +/****************************************************************************** +* Filename: control_task.c +* +* Description: Source file for the Control Task. This task controls the state +* of application. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#include "host_if.h" +#include "radio_if.h" +#include "control_task.h" +#include "command_packet.h" +#include "command_handler.h" +#include "test_command_handler.h" +#include "timestamp.h" +#include "task_event.h" + +#include + +// Sniffer control state. WAIT_FOR_COMMAND is initial state. +static SnifferState ControlTask_state = STATE_WAIT_FOR_COMMAND; + +// Command handling related +static CommandPacket_Obj ControlTask_command; +static uint8_t ControlTask_handleCommand(CommandPacket_Obj* command); +static uint8_t ControlTask_handleCommandStart(void); +static uint8_t ControlTask_handleCommandStop(void); +static uint8_t ControlTask_handleCommandCfgFrequency(uint8_t* commandData, uint16_t length); +static uint8_t ControlTask_handleCommandCfgPhy(uint8_t* commandData, uint16_t length); +static bool ControlTask_isCmdPing(CommandPacket_Obj* cmdPacket); + +// This function is not static to make it accessible from test task +void ControlTask_setControlState(SnifferState state); + +// Other local functions +static void ControlTask_notifyUserIfTask(void); + + +//! \brief Control task function +//! The arguments are not used. +//! +//! This task controls the state of the application. It waits for +//! commands from host. At reception of a host command it will handle +//! the command and change state accordingly. This task is blocked in the +//! function CommandHandler_getCommand until a host command is received. +//! After the first command from the host interface is received, it will +//! notify the user interface task by sending the event EVENT_ID_USER_IF_TASK_END +//! to it. This event causes the user interface task to exit itself. +//! +void controlTask(UArg a0, UArg a1) +{ + // Initialize the modules to be used by this task + HostIF_init(); + RadioIF_init(); + TaskEvent_init(); + + while (true) + { + // Wait for command from host + uint8_t status = CommandHandler_getCommand(&ControlTask_command); + if(status == COMMAND_OK) + { + // After first command is received successfully, state is changed + // to INIT and User Interface task is notified. + if(ControlTask_state == STATE_WAIT_FOR_COMMAND) + { + ControlTask_notifyUserIfTask(); + ControlTask_state = STATE_INIT; + } + + status = ControlTask_handleCommand(&ControlTask_command); + } + // Command is completed, respond to host + if(ControlTask_isCmdPing(&ControlTask_command)) + { + CommandHandler_respondPingCommand(status); + } + else + { + CommandHandler_respondCommand(status); + } + } +} + + +//! \brief handle a host command +//! +//! \param[in] cmdPacket +//! Pointer to the received command packet +//! +//! \return status that indicates successful or failed handling of the command. +//! COMMAND_OK - The command was handled successfully. +//! COMMAND_INVALID - The command is invalid (e.g. payload does not match +//! the command type.) +//! COMMAND_INVALID_STATE: The received command is not valid for the +//! current state of the sniffer. +uint8_t ControlTask_handleCommand(CommandPacket_Obj* command) +{ + uint8_t status = COMMAND_OK; + + switch(command->genPacketHdr.packetType) + { + case PACKET_TYPE_COMMAND_START: + if(ControlTask_state != STATE_STARTED) + { + status = ControlTask_handleCommandStart(); + ControlTask_state = STATE_STARTED; + } + break; + case PACKET_TYPE_COMMAND_STOP: + if(ControlTask_state != STATE_STOPPED) + { + status = ControlTask_handleCommandStop(); + ControlTask_state = STATE_STOPPED; + } + break; + case PACKET_TYPE_COMMAND_CFG_FREQUENCY: + status = ControlTask_handleCommandCfgFrequency((uint8_t*)&command->payload, command->genPacketHdr.packetLen); + break; + case PACKET_TYPE_COMMAND_CFG_PHY: + status = ControlTask_handleCommandCfgPhy((uint8_t*)&command->payload, command->genPacketHdr.packetLen); + break; + case PACKET_TYPE_COMMAND_PING: + status = COMMAND_OK; + break; + case PACKET_TYPE_TEST_COMMAND_TRANSMIT_SEQUENCE: + if(ControlTask_state != STATE_STARTED) + { + status = TestCommandHandler_transmitSequence((uint8_t*)&command->payload, command->genPacketHdr.packetLen); + } + break; + default: + status = COMMAND_INVALID; + break; + } + + return status; +} + + +//! \brief handle CMD_START +//! +//! \return status that indicates successful or failed handling of the command. +uint8_t ControlTask_handleCommandStart(void) +{ + RadioIF_startRx(); + + return COMMAND_OK; +} + + +//! \brief handle CMD_STOP +//! +//! \return status that indicates successful or failed handling of the command. +uint8_t ControlTask_handleCommandStop(void) +{ + RadioIF_stopRx(); + + // Reset timestamp module + Timestamp_init(); + + return COMMAND_OK; +} + + +//! \brief handle CMD_CFG_FREQUENCY +//! +//! \param[in] commandData +//! payload data for the CMD_CFG_FREQUENCY command. +//! +//! \param[in] length +//! length of payload data +//! +//! \return status that indicates successful or failed handling of the command. +//! COMMAND_OK - The command was handled successfully. +//! COMMAND_INVALID - The command is invalid (e.g. payload does not match +//! the command type.) +//! COMMAND_INVALID_STATE: The received command is not valid for the +//! current state of the sniffer. +uint8_t ControlTask_handleCommandCfgFrequency(uint8_t* commandData, uint16_t length) +{ + if(length != COMMAND_CFG_FREQUENCY_DATA_LENGTH) + { + // Length of data field is invalid for this command + // The command is ignored + return COMMAND_INVALID; + } + + if( (ControlTask_state != STATE_STOPPED) && (ControlTask_state != STATE_INIT) ) + { + // The command is only valid in STOPPED and INIT state + // The command is ignored in other states. + return COMMAND_INVALID_STATE; + } + + uint16_t* frequency = (uint16_t*)(commandData + COMMAND_CFG_FREQUENCY_FREQ_OFFSET); + uint16_t* fractFrequency = (uint16_t*)(commandData + COMMAND_CFG_FREQUENCY_FRACT_FREQ_OFFSET); + RadioIF_setFrequency(*frequency, *fractFrequency); + + return COMMAND_OK; +} + + +//! \brief handle CMD_CFG_PHY +//! +//! \param[in] commandData +//! payload data for the CMD_CFG_PHY command. +//! +//! \param[in] length +//! length of payload data (a length of 1 byte is expected) +//! +//! \return status that indicates successful or failed handling of the command. +//! COMMAND_OK - The command was handled successfully. +//! COMMAND_INVALID - The command is invalid (e.g. payload does not match +//! the command type.) +//! COMMAND_INVALID_STATE: The received command is not valid for the +//! current state of the sniffer. +uint8_t ControlTask_handleCommandCfgPhy(uint8_t* commandData, uint16_t length) +{ + if(length != COMMAND_CFG_PHY_DATA_LENGTH) + { + // Length of data field is invalid for this command + // The command is ignored + return COMMAND_INVALID; + } + + if( (ControlTask_state != STATE_STOPPED) && (ControlTask_state != STATE_INIT) ) + { + // The command is only valid in STOPPED and INIT state + // The command is ignored in other states. + return COMMAND_INVALID_STATE; + } + + uint8_t phyNumber = *(commandData + COMMAND_CFG_PHY_DATA_OFFSET); + + uint8_t status = COMMAND_OK; + if(!RadioIf_setPhy(phyNumber)) + { + status = COMMAND_INVALID_PARAMETER; + } + + return status; +} + + +//! \brief Notify user interface task. +//! +//! Upon reception of this notification the user interface shall exit +//! itself. +void ControlTask_notifyUserIfTask(void) +{ + Event_post(TaskEvent_Handle, EVENT_ID_USER_IF_TASK_END); +} + + +//! \brief Set application control state +//! +//! This function is used from test FW to set control state of the +//! application, for example to check that the correct command responses +//! are given dependent on control state. +void ControlTask_setControlState(SnifferState controlState) +{ + ControlTask_state = controlState; +} + + +//! \brief Check if command is CMD_PING +bool ControlTask_isCmdPing(CommandPacket_Obj* cmdPacket) +{ + return (cmdPacket->genPacketHdr.packetType == PACKET_TYPE_COMMAND_PING); +} + + diff --git a/source/control_task.h b/source/control_task.h new file mode 100644 index 0000000..7d42136 --- /dev/null +++ b/source/control_task.h @@ -0,0 +1,58 @@ +/****************************************************************************** +* Filename: control_task.h +* +* Description: Header file for the Control Task. This task controls the state +* of application. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef CONTROL_TASK_H +#define CONTROL_TASK_H + +#include + +// Control states +typedef enum SnifferState +{ + STATE_WAIT_FOR_COMMAND, + STATE_INIT, + STATE_STARTED, + STATE_STOPPED, +} SnifferState; + +//! \brief Control task function +//! The arguments are not used. +extern void controlTask(UArg a0, UArg a1); + +#endif + diff --git a/source/data_packet.h b/source/data_packet.h new file mode 100644 index 0000000..01657db --- /dev/null +++ b/source/data_packet.h @@ -0,0 +1,75 @@ +/****************************************************************************** +* Filename: data_packet.h +* +* Description: Defines for the data packet format. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#ifndef DATA_PACKET_H +#define DATA_PACKET_H + +#include + +#include "general_packet.h" + +// Data and error packet formats +#define MAX_DATA_PAYLOAD_LENGTH 2047 +#define MAX_EXTRA_RADIO_ADDED_BYTES 10 // Up to 10 extra bytes are added by the radio (Timestamp, CRC, RSSI and Status fields) +#define TIMESTAMP_LENGHT 6 +#define ERROR_REPORT_PAYLOAD_LENGTH 1 + +// Packet types +#define PACKET_TYPE_DATA 0xC0 +#define PACKET_TYPE_ERROR 0xC1 + +// Data packet +#pragma pack(1) +typedef struct DataPacket_Obj +{ + GeneralPacketHeader_Obj genPacketHeader; + uint8_t timeStamp[TIMESTAMP_LENGHT]; + uint8_t payload[MAX_DATA_PAYLOAD_LENGTH + MAX_EXTRA_RADIO_ADDED_BYTES + sizeof(GeneralPacketFooter_Obj)]; // space for payload and general packet footer +} DataPacket_Obj; + +// Error packet +#pragma pack(1) +typedef struct ErrorReportPacket_Obj +{ + GeneralPacketHeader_Obj genPacketHeader; + uint8_t error[ERROR_REPORT_PAYLOAD_LENGTH]; + GeneralPacketFooter_Obj genPacketFooter; +} ErrorReportPacket_Obj; + +#endif + diff --git a/source/data_task.c b/source/data_task.c new file mode 100644 index 0000000..68abb3b --- /dev/null +++ b/source/data_task.c @@ -0,0 +1,197 @@ +/****************************************************************************** +* Filename: data_task.c +* +* Description: Source file for the Data Task. This task process data +* received from the radio. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include +#include +#include +#include +#include "host_if.h" +#include "radio_if.h" +#include "radio_if_dataqueue.h" +#include "packet_handler.h" +#include "data_packet.h" +#include "timestamp.h" +#include "packet_queue.h" + +#include + +static void DataTask_rfCoreEventCb(uint32_t rfCoreEvent); +static void DataTask_initEventHandler(void); +static void DataTask_processPacket(void); +static void DataTask_handleBufferOverflow(void); + +#define EVENT_ID_RX_ENTRY_DONE Event_Id_01 +#define EVENT_ID_RX_BUFFER_FULL Event_Id_02 +static Event_Handle DataTask_EventHandle; + + +//! \brief Data task function +//! The arguments are not used. +//! +//! This task blocks waiting for either +//! - a new packet available in the radio interface data queue, +//! - or an event triggered when the RX buffers are full. +//! When a new packet is available it takes the packet +//! out of the queue, process the packet and forwards it to the host. +//! +void dataTask(UArg a0, UArg a1) +{ + // Initialize the modules to be used by this task + HostIF_init(); + Timestamp_init(); + PacketQueue_init(); + + DataTask_initEventHandler(); + + while (true) + { + // Event_pend blocks until one or more of the events are received + UInt events = Event_pend(DataTask_EventHandle, Event_Id_NONE, EVENT_ID_RX_ENTRY_DONE | EVENT_ID_RX_BUFFER_FULL, BIOS_WAIT_FOREVER); + + // Process the received packets + if(events & EVENT_ID_RX_ENTRY_DONE) + { + // Process all packets in the RX packet queue + while(RadioIF_hasPacket()) + { + DataTask_processPacket(); + } + } + + if(events & EVENT_ID_RX_BUFFER_FULL) + { + // Process all packets in the RX packet queue + while(RadioIF_hasPacket()) + { + DataTask_processPacket(); + } + + DataTask_handleBufferOverflow(); + } + } +} + + + +//! \brief Process a data packet +//! +//! This function first allocates a packet buffer slot on the Host IF +//! queue. Then it reads the packet from the RF Core queue, processes +//! the timestamp field and formats it according to the Host IF format. +//! Finally, it enqueues the packet on the Host IF packet queue. This will +//! signal to the Host IF packet that a new packet is ready to be sent on +//! the Host IF interface. +void DataTask_processPacket(void) +{ + // Allocate a buffer slot for the new packet + DataPacket_Obj* p = PacketQueue_allocBufferSlot(); + + // Disable task switch to make sure the processing of one packet is not interrupted. + int key = Task_disable(); + + // Take a packet from the RX packet queue + uint16_t len = RadioIF_takePacket((uint8_t*)p->payload, sizeof(p->payload)); + + len = Timestamp_extractTimestamp((uint8_t*)p->payload, len, p); + + // Do packet processing + uint16_t payloadLength = PacketHandler_processPacket(p, len); + + uint16_t packetLength = PACKET_NON_PAYLOAD_LENGTH + payloadLength; + Task_restore(key); + + // Enqueue packet on the Host IF queue + PacketQueue_enqueue(p, packetLength); +} + + +//! \brief Handle a buffer overflow. +//! +//! This function handles an RX buffer overflow and sends an error packet +//! on the Host interface. +static void DataTask_handleBufferOverflow(void) +{ + // Allocate a buffer slot for an error packet + DataPacket_Obj* p = PacketQueue_allocBufferSlot(); + + PacketHandler_processError((uint8_t*)p, RADIO_IF_STATUS_RX_BUFFER_OVERFLOW); + RadioIF_handleBufferOverflow(); + + // Enqueue error packet on the Host IF queue + PacketQueue_enqueue(p, sizeof(ErrorReportPacket_Obj)); +} + + +//! \brief Initialize event handling +//! +void DataTask_initEventHandler(void) +{ + // Create event with default parameters + DataTask_EventHandle = Event_create(NULL, NULL); + + if (DataTask_EventHandle == NULL) + { + System_abort("Event create failed"); + } + + // Register Radio timer callback function + RadioIF_registerRfCoreEventCb(&DataTask_rfCoreEventCb); +} + + +//! \brief RF Core event callback handler. +//! +//! This function handles two events from RF Core: RX_ENTRY_DONE and +//! RX_BUFFER_FULL events. +//! +//! \param[in] rfCoreEvent +//! RF Core event number +void DataTask_rfCoreEventCb(uint32_t rfCoreEvent) +{ + if(rfCoreEvent & RX_ENTRY_DONE_EVENT) + { + Event_post(DataTask_EventHandle, EVENT_ID_RX_ENTRY_DONE); + } + + if(rfCoreEvent & RX_BUFFER_FULL_EVENT) + { + Event_post(DataTask_EventHandle, EVENT_ID_RX_BUFFER_FULL); + } +} + diff --git a/source/data_task.h b/source/data_task.h new file mode 100644 index 0000000..c893e64 --- /dev/null +++ b/source/data_task.h @@ -0,0 +1,49 @@ +/****************************************************************************** +* Filename: data_task.h +* +* Description: Header file for the data Task. This task handles data received +* from the radio. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef DATA_TASK_H +#define DATA_TASK_H + +#include + +//! \brief Data task function +//! The arguments are not used. +extern void dataTask(UArg a0, UArg a1); + +#endif + diff --git a/source/general_packet.h b/source/general_packet.h new file mode 100644 index 0000000..06ea18d --- /dev/null +++ b/source/general_packet.h @@ -0,0 +1,73 @@ +/****************************************************************************** +* Filename: general_packet.h +* +* Description: Defines for the general host interface packet format. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef GENERAL_PACKET_H +#define GENERAL_PACKET_H + +#include + +// General host interface packet format +#define SOF_LENGTH 2 +#define PACKET_TYPE_LENGTH 1 +#define PACKET_LEN_LENGTH 2 +#define MAX_PAYLOAD_LENGTH 2047 +#define EOF_LENGTH 2 +#define PACKET_NON_PAYLOAD_LENGTH (SOF_LENGTH + PACKET_TYPE_LENGTH + PACKET_LEN_LENGTH + TIMESTAMP_LENGHT + EOF_LENGTH) +#define START_OF_FRAME_DELIMITER 0x5340 +#define END_OF_FRAME_DELIMITER 0x4540 + + +// General packet header +#pragma pack(1) +typedef struct GeneralPacketHeader_Obj +{ + uint16_t sof; + uint8_t packetType; + uint16_t packetLen; +} GeneralPacketHeader_Obj; + + +// General packet footer +#pragma pack(1) +typedef struct GeneralPacketFooter_Obj +{ + uint16_t eof; +} GeneralPacketFooter_Obj; + + +#endif + diff --git a/source/host_if.c b/source/host_if.c new file mode 100644 index 0000000..285ea19 --- /dev/null +++ b/source/host_if.c @@ -0,0 +1,107 @@ +/****************************************************************************** +* Filename: host_if.c +* +* Description: Source file for the host interface. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include "host_if.h" +#include "Board.h" +#include +#include +#include +#include + +static UART_Handle HostIF_Uart_Handle; +static UART_Params HostIF_uartParams; +static bool HostIF_isInitialized = false; + +// Semaphore that for protecting uart calls against simultanous access from +// multiple tasks. Simulatanous access to uart from more than one task in same +// direction is not supported by the TI-RTOS driver. +static Semaphore_Struct HostIF_semStructUart; +static Semaphore_Handle HostIF_SemUart_Handle; + +// UART read timeout +#define UART_READ_TIMEOUT_PERIOD 20000 // ticks of 10 us -> 200ms + +void HostIF_init() +{ + if(!HostIF_isInitialized) + { + UART_init(); + UART_Params_init(&HostIF_uartParams); + HostIF_uartParams.writeDataMode = UART_DATA_BINARY; + HostIF_uartParams.readDataMode = UART_DATA_BINARY; + HostIF_uartParams.readReturnMode = UART_RETURN_FULL; + HostIF_uartParams.readEcho = UART_ECHO_OFF; + HostIF_uartParams.baudRate = 921600; + HostIF_uartParams.readTimeout = UART_READ_TIMEOUT_PERIOD; + HostIF_Uart_Handle = UART_open(Board_UART0, &HostIF_uartParams); + + if (HostIF_Uart_Handle == NULL) + { + System_abort("Error opening the UART"); + } + + // Initialize the uart semaphore + Semaphore_Params semParams; + + // Construct a new binary semaphore and initialize it to 1 + Semaphore_Params_init(&semParams); + semParams.mode = Semaphore_Mode_BINARY; + Semaphore_construct(&HostIF_semStructUart, 1, &semParams); + + HostIF_SemUart_Handle = Semaphore_handle(&HostIF_semStructUart); + + HostIF_isInitialized = true; + } +} + + +void HostIF_writeBuffer(uint8_t* buffer, uint16_t len) +{ + // Suspend the task until UART is available + Semaphore_pend(HostIF_SemUart_Handle, BIOS_WAIT_FOREVER); + UART_write(HostIF_Uart_Handle, buffer, len); + + // Signal the other tasks waiting for UART semaphore + Semaphore_post(HostIF_SemUart_Handle); +} + + +int32_t HostIF_readBuffer(uint8_t* buffer, uint16_t len) +{ + return UART_read(HostIF_Uart_Handle, buffer, len); +} diff --git a/source/host_if.h b/source/host_if.h new file mode 100644 index 0000000..bcca702 --- /dev/null +++ b/source/host_if.h @@ -0,0 +1,76 @@ +/****************************************************************************** +* Filename: host_if.h +* +* Description: Header file for host interface. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef HOST_IF_H +#define HOST_IF_H + +#include + +//! \brief Initialize the host interface +//! +//! \return None +extern void HostIF_init(void); + + +//! \brief Write data to the host interface +//! +//! \param[in] buffer +//! Pointer to the buffer with data to write. +//! +//! \param[in] len +//! Length of the data buffer +//! +//! \return None +extern void HostIF_writeBuffer(uint8_t* buffer, uint16_t len); + + +//! \brief Read data from the host interface +//! +//! This function blocks until the number of bytes indicated by the 'len' +//! argument is received. +//! +//! \param[in] buffer +//! Pointer to the buffer with data to write. +//! +//! \param[in] len +//! number of bytes to read. +//! +//! \return Number of bytes read from UART or -1 if an error occured +extern int32_t HostIF_readBuffer(uint8_t* buffer, uint16_t len); + +#endif + diff --git a/source/host_if_task.c b/source/host_if_task.c new file mode 100644 index 0000000..175f22f --- /dev/null +++ b/source/host_if_task.c @@ -0,0 +1,64 @@ +/****************************************************************************** +* Filename: host_if_task.c +* +* Description: Source file for the Host IF Task. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include +#include "packet_queue.h" +#include "host_if.h" + + +//! \brief Host IF task function +//! The arguments are not used. +//! +//! This task takes packets from the Host interface queue and sends the packets +//! out on the Host interface. +//! +void hostIfTask(UArg a0, UArg a1) +{ + DataPacket_Obj* dataPacket; + uint16_t packetLength; + + while(1) + { + PacketQueue_dequeue(&dataPacket, &packetLength); + HostIF_writeBuffer((uint8_t*)dataPacket, packetLength); + // HostIF_writeBuffer blocks until the packet is written. + // The buffer slot can now be freed. + PacketQueue_freeBufferSlot(dataPacket); + } +} + diff --git a/source/host_if_task.h b/source/host_if_task.h new file mode 100644 index 0000000..a16ce2f --- /dev/null +++ b/source/host_if_task.h @@ -0,0 +1,48 @@ +/****************************************************************************** +* Filename: host_if_task.h +* +* Description: Header file for the Host IF Task. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef HOST_IF_TASK_H +#define HOST_IF_TASK_H + +#include + +//! \brief Host IF task function +//! The arguments are not used. +extern void hostIfTask(UArg a0, UArg a1); + +#endif + diff --git a/source/packet_handler.c b/source/packet_handler.c new file mode 100644 index 0000000..e451f0f --- /dev/null +++ b/source/packet_handler.c @@ -0,0 +1,184 @@ +/****************************************************************************** +* Filename: packet_handler.c +* +* Description: Source file for packet handler. This module handles data and +* and error packet, and forwarding of these packets to host. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include "packet_handler.h" +#include "host_if.h" +#include "phy_manager.h" +#include "common.h" +#include + + +// Local functions +static void PacketHandler_constructDataPacket(DataPacket_Obj* dataPacket, uint16_t payloadLength); +static void PacketHandler_constructErrorPacket(ErrorReportPacket_Obj* errorReportPacket, uint8_t error); +static void PacketHandler_convertStatusToCc1200FormatIeee154Api(uint8_t* status); +static void PacketHandler_convertStatusToCc1200FormatPropApi(uint8_t* status); + + + +extern uint16_t PacketHandler_processPacket(DataPacket_Obj* dataPacket, uint16_t payloadLength) +{ + PacketHandler_constructDataPacket(dataPacket, payloadLength); + + return payloadLength; +} + + +//! \brief Construct data packet +//! +//! The caller of this function is responsible for allocating the memory +//! large enough for a data packet, and for writing the payload of the data +//! packet before this function is called. This function will populate the +//! the rest of the datapacket. +//! +//! \param[in] dataPacket +//! Pointer to buffer for the data packet +//! +//! \param[in] payloadLength +//! Length of the payload field of the data packet +//! +//! \return None +void PacketHandler_constructDataPacket(DataPacket_Obj* dataPacket, uint16_t payloadLength) +{ + dataPacket->genPacketHeader.sof = START_OF_FRAME_DELIMITER; + dataPacket->genPacketHeader.packetType = PACKET_TYPE_DATA; + dataPacket->genPacketHeader.packetLen = TIMESTAMP_LENGHT + payloadLength; + + uint8_t* status = (uint8_t*)(dataPacket->payload + payloadLength - 1); + + if(PhyManager_getRfApi() == IEEE_802_15_4) + { + PacketHandler_convertStatusToCc1200FormatIeee154Api(status); + } + else + { + PacketHandler_convertStatusToCc1200FormatPropApi(status); + } + + uint8_t* eof = (uint8_t*)(dataPacket->payload + payloadLength); + Common_set16BitValueLE(eof, END_OF_FRAME_DELIMITER); +} + + +extern void PacketHandler_processError(uint8_t* errorPacket, uint8_t error) +{ + PacketHandler_constructErrorPacket((ErrorReportPacket_Obj*)errorPacket, error); +} + + +//! \brief Construct error packet +//! +//! The caller of this function is responsible for allocating the memory +//! large enough for an error packet. This function will populate the +//! the error packet. +//! +//! \param[in] errorReportPacket +//! Pointer to buffer for the error packet. +//! +//! \param[in] error +//! Error code to set in the error packet. +//! +//! \return None +void PacketHandler_constructErrorPacket(ErrorReportPacket_Obj* errorReportPacket, uint8_t error) +{ + errorReportPacket->genPacketHeader.sof = START_OF_FRAME_DELIMITER; + errorReportPacket->genPacketHeader.packetType = PACKET_TYPE_ERROR; + errorReportPacket->genPacketHeader.packetLen = ERROR_REPORT_PAYLOAD_LENGTH; + *errorReportPacket->error = error; + errorReportPacket->genPacketFooter.eof = END_OF_FRAME_DELIMITER; +} + + +//! \brief Convert the status byte to CC1200 format (CC26xx IEEE 15.4 API version +//! of this function). +//! +//! \param[in] status +//! Pointer to the status byte added by RF Core +//! +//! \return None +void PacketHandler_convertStatusToCc1200FormatIeee154Api(uint8_t* status) +{ + // Get value of the result bit field of the status byte (bit 7) + uint8_t result = (*status & 0x80) >> 7; + + // Initialize status byte in CC1200 format. + // Set CRC_OK false and LQI to invalid value (0) + *status = 0; + + // The following values shall give CRC_OK true in new format: + // - Result = 0 -> 'Packet received withour CRC error' + // Other values keeps CRC_OK false. + if(result == 0) + { + // Set CRC_OK -> 1 + *status |= 0x80; + } +} + + +//! \brief Convert the status byte to CC1200 format (CC13xx PROP API version +//! of this function). +//! +//! \param[in] status +//! Pointer to the status byte added by RF Core +//! +//! \return None +void PacketHandler_convertStatusToCc1200FormatPropApi(uint8_t* status) +{ + // Get value of the result bit field of the status byte (bits 6-7) + uint8_t result = (*status & 0xC0) >> 6; + + // Initialize status byte in CC1200 format. + // Set CRC_OK false and LQI to invalid value (0) + *status = 0; + + // The following values shall give CRC_OK true in new format: + // - Result = 0 -> 'Packet Received correctly, not ingored' + // - Result = 2 -> 'Packet received correct, but can be ignored' + // Other values keeps CRC_OK false. + if((result == 0) || (result == 0x2)) + { + // Set CRC_OK -> 1 + *status |= 0x80; + } +} + + + + diff --git a/source/packet_handler.h b/source/packet_handler.h new file mode 100644 index 0000000..905441d --- /dev/null +++ b/source/packet_handler.h @@ -0,0 +1,78 @@ +/****************************************************************************** +* Filename: packet_handler.h +* +* Description: Header file for packet handler. This module handles data and +* and error packet, and forwarding of these packets to host. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef PACKET_HANDLER_H +#define PACKET_HANDLER_H + +#include +#include "data_packet.h" + + +//! \brief Process data received over the air. +//! +//! The caller of this function is responsible for allocating the memory +//! large enough for a data packet, and for writing the payload of the data +//! packet before this function is called. This function will populate the +//! the rest of the datapacket, and forward it to the host. +//! +//! \param[in] dataPacket +//! Pointer to buffer large enough for a complete data packet. +//! +//! \param[in] payloadLength +//! Length of the payload data. +//! +//! \return Length of the processed packet +extern uint16_t PacketHandler_processPacket(DataPacket_Obj* dataPacket, uint16_t payloadLength); + + +//! \brief Process error condition +//! +//! This function constructs an error packet. The caller of this function is +//! responsible for allocating memory large enough for an error packet. +//! +//! \param[in] errorPacket +//! Pointer to buffer large enough for a complete error packet. +//! +//! \param[in] error +//! Error code. +//! +//! \return None +extern void PacketHandler_processError(uint8_t* errorPacket, uint8_t error); + +#endif + diff --git a/source/packet_queue.c b/source/packet_queue.c new file mode 100644 index 0000000..123de92 --- /dev/null +++ b/source/packet_queue.c @@ -0,0 +1,154 @@ +/****************************************************************************** +* Filename: packet_queue.c +* +* Description: Source file for the packet queue module. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include "packet_queue.h" +#include "data_packet.h" + +#include +#include +#include + + +#define NUM_BUFFER_SLOTS 3 + +typedef struct Message_Obj +{ + DataPacket_Obj* p; // Pointer to a data packet buffer slot + uint16_t length; // Data packet length +} Message_Obj; + + +static DataPacket_Obj PacketQueue_dataPacket[NUM_BUFFER_SLOTS]; + +// Queue of pointers to free buffer slots +static Mailbox_Handle freeQueue; + +// Queue of pointers to full (occupied) buffer slots +static Mailbox_Handle fullQueue; + +static bool PacketQueue_isInitialized = false; + + +void PacketQueue_init(void) +{ + if(!PacketQueue_isInitialized) + { + // Create mailbox objects with default error handling and parameters + freeQueue = Mailbox_create(sizeof(Message_Obj), NUM_BUFFER_SLOTS, NULL, NULL); + fullQueue = Mailbox_create(sizeof(Message_Obj), NUM_BUFFER_SLOTS, NULL, NULL); + + for(int i=0; i +#include +#include +#include +#include +#include +#include "Board.h" + +#include "host_if_task.h" +#include "data_task.h" +#include "user_if_task.h" +#include "control_task.h" + +/***** Defines *****/ +#define HOST_IF_TASK_PRIORITY 1 +#define DATA_TASK_PRIORITY 2 +#define USER_IF_TASK_PRIORITY 3 +#define CONTROL_TASK_PRIORITY 4 + + +static Task_Params hostIfTaskParams; +static Task_Params dataTaskParams; +static Task_Params userIfTaskParams; +static Task_Params controlTaskParams; + + +void task_init(void) +{ + Task_Params_init(&hostIfTaskParams); + hostIfTaskParams.priority = HOST_IF_TASK_PRIORITY; + Task_create(hostIfTask, &hostIfTaskParams, NULL); + + Task_Params_init(&dataTaskParams); + dataTaskParams.priority = DATA_TASK_PRIORITY; + Task_create(dataTask, &dataTaskParams, NULL); + + Task_Params_init(&userIfTaskParams); + userIfTaskParams.priority = USER_IF_TASK_PRIORITY; + Task_create(userIfTask, &userIfTaskParams, NULL); + + Task_Params_init(&controlTaskParams); + controlTaskParams.priority = CONTROL_TASK_PRIORITY; + Task_create(controlTask, &controlTaskParams, NULL); +} + + +//! \brief main function +int main(void) +{ + // Call board init functions. + Board_initGeneral(); + +#ifndef STATIC_TASK + // Initialize tasks + task_init(); +#endif + + // Start BIOS + BIOS_start(); + + return (0); +} + + + diff --git a/source/phy/devices/cc26x0lp/phy_io_config.c b/source/phy/devices/cc26x0lp/phy_io_config.c new file mode 100644 index 0000000..2de7074 --- /dev/null +++ b/source/phy/devices/cc26x0lp/phy_io_config.c @@ -0,0 +1,51 @@ +/****************************************************************************** +* Filename: phy_tables.c +* +* Description: Source file for PHY IO configuration +* +* Copyright (C) 2018 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. +* +******************************************************************************/ + +#include "../../phy_io_config.h" + + +void Phy_IoPinInit(void) +{ + // No phy specific IO pin configuration required for this board +} + + +void Phy_configureIoPins(const Phy_RfApi rfApi) +{ + // No phy specific IO pin configuration required for this board +} diff --git a/source/phy/devices/cc26x0lp/phy_tables.c b/source/phy/devices/cc26x0lp/phy_tables.c new file mode 100644 index 0000000..155beb5 --- /dev/null +++ b/source/phy/devices/cc26x0lp/phy_tables.c @@ -0,0 +1,97 @@ +/****************************************************************************** +* Filename: phy_tables.c +* +* Description: Source file PHY tables +* +* Copyright (C) 2018 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. +* +******************************************************************************/ + +#include "../../phy_tables.h" +#include <15.4/smartrf_settings_15_4_0.h> + +// Table of RF Proprietary PHY settings (These settings use Proprietary RF core commands) +const Phy_Prop_Obj Phy_phyTableProp[] = +{ + // No RF Proprietary settings for this device + {NULL, NULL, NULL, NULL, NULL} +}; + + +// Table of IEEE 15.4g PHY settings (These settings use Proprietary RF core commands in IEEE 802.15.4g mode) +const Phy_Prop_15_4_g_Obj Phy_phyTableProp_15_4_g[] = +{ + // No IEEE 802.15.4g settings for this device + {NULL, NULL, NULL, NULL, NULL} +}; + + +// Table of IEEE 15.4 PHY settings (These settings use IEEE 802.15.4 RF core commands) +const Phy_Ieee_15_4_Obj Phy_phyTableIeee_15_4[] = +{ + // IEEE 802.15.4 at 2.4GHz + {&Ieee154_0_cmdRadioSetup, &Ieee154_0_cmdFs, &Ieee154_0_cmdIeeeRx, &Ieee154_0_cmdIeeeTx, &Ieee154_0_mode}, + {NULL, NULL, NULL, NULL, NULL} +}; + + +// Table of all supported Rf API and PHY setting combinations +// Note: This table must match the PHY tables for each RF API +const Phy_Obj Phy_supportedPhys[] = +{ + {IEEE_802_15_4, 0}, +}; + + + +uint8_t Phy_getNumSupportedPhys(void) +{ + return sizeof(Phy_supportedPhys)/sizeof(Phy_supportedPhys[0]); +} + + +uint8_t Phy_getNumPropPhys(void) +{ + return (sizeof(Phy_phyTableProp)/sizeof(Phy_phyTableProp[0])) - 1; +} + + +uint8_t Phy_getNumProp_15_4_g_Phys(void) +{ + return (sizeof(Phy_phyTableProp_15_4_g)/sizeof(Phy_phyTableProp_15_4_g[0])) - 1; +} + + +uint8_t Phy_getNumIeee_15_4_Phys(void) +{ + return (sizeof(Phy_phyTableIeee_15_4)/sizeof(Phy_phyTableIeee_15_4[0])) - 1; +} diff --git a/source/phy/phy_if_ieee_15_4.c b/source/phy/phy_if_ieee_15_4.c new file mode 100644 index 0000000..8fbf890 --- /dev/null +++ b/source/phy/phy_if_ieee_15_4.c @@ -0,0 +1,96 @@ +/****************************************************************************** +* Filename: phy_if_ieee_15_4.c +* +* Description: Source file for PHY interface IEEE 802.15.4 +* +* Copyright (C) 2018 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. +* +******************************************************************************/ + +#include "phy_if_ieee_15_4.h" +#include "phy_tables.h" +#include "../radio_if_dataqueue.h" + + +void PhyIf_setFrequencyIeee_15_4(uint8_t phyIndex, uint16_t frequency, uint16_t fractFrequency) +{ + rfc_CMD_FS_t* pFsCmd = Phy_phyTableIeee_15_4[phyIndex].pFsCmd; + + pFsCmd->frequency = frequency; + pFsCmd->fractFreq = fractFrequency; +} + + +void PhyIf_configureSetupCmdIeee_15_4(uint8_t phyIndex) +{ + // No configuration required for the setup command in IEEE 802.15.4 mode +} + + +void PhyIf_configureRxCmdIeee_15_4(uint8_t phyIndex) +{ +// IEEE 15.4 mode is not supported for CC13X0 and rfc_CMD_IEEE_RX_t is not +// defined for this device +#ifndef DeviceFamily_CC13X0 + rfc_CMD_IEEE_RX_t* pIeeeRxCmd = Phy_phyTableIeee_15_4[phyIndex].pRxCmd; + + // Modify CMD_IEEE_RX command + pIeeeRxCmd->pRxQ = RadioIF_dataQueueGet(); + pIeeeRxCmd->rxConfig.bAutoFlushCrc = 0; + pIeeeRxCmd->rxConfig.bAutoFlushIgn = 0; + pIeeeRxCmd->rxConfig.bIncludePhyHdr = 1; + pIeeeRxCmd->rxConfig.bIncludeCrc = 1; + pIeeeRxCmd->rxConfig.bAppendRssi = 1; + pIeeeRxCmd->rxConfig.bAppendCorrCrc = 1; + pIeeeRxCmd->rxConfig.bAppendSrcInd = 0; + pIeeeRxCmd->rxConfig.bAppendTimestamp = 1; +#endif +} + + +void PhyIf_configureTxCmdIeee_15_4(uint8_t* packet, uint16_t packetLength, uint8_t phyIndex) +{ +// IEEE 15.4 mode is not supported for CC13X0 and rfc_CMD_IEEE_TX_t is not +// defined for this device +#ifndef DeviceFamily_CC13X0 + rfc_CMD_IEEE_TX_t* pIeeeTxCmd = Phy_phyTableIeee_15_4[phyIndex].pTxCmd; + + pIeeeTxCmd->payloadLen = packetLength; + pIeeeTxCmd->pPayload = packet; + pIeeeTxCmd->startTrigger.triggerType = TRIG_NOW; +#endif +} + + + + + diff --git a/source/phy/phy_if_ieee_15_4.h b/source/phy/phy_if_ieee_15_4.h new file mode 100644 index 0000000..18fd7ba --- /dev/null +++ b/source/phy/phy_if_ieee_15_4.h @@ -0,0 +1,91 @@ +/****************************************************************************** +* Filename: phy_if_ieee_15_4.h +* +* Description: Header file for PHY interface IEEE 802.15.4 mode +* +* Copyright (C) 2018 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. +* +******************************************************************************/ +#ifndef PHY_IF_IEEE_15_4_H +#define PHY_IF_IEEE_15_4_H + +#include + +//! \brief Configure RF Core Setup command for the IEEE 802.15.4 PHYs +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of IEEE 802.15.4 PHY settings +//! +//! \return None +void PhyIf_configureSetupCmdIeee_15_4(uint8_t phyIndex); + + +//! \brief Configure RF Core RX command for the IEEE 802.15.4 PHYs +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of IEEE 802.15.4 PHY settings +//! +//! \return None +void PhyIf_configureRxCmdIeee_15_4(uint8_t phyIndex); + + +//! \brief Configure RF Core TX command for the IEEE 802.15.4 PHYs +//! +//! \param[in] packet +//! Pointer to packet +//! +//! \param[in] packetLength +//! Length of the packet +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of IEEE 802.15.4 PHY settings +//! +//! \return None +void PhyIf_configureTxCmdIeee_15_4(uint8_t* packet, uint16_t packetLength, uint8_t phyIndex); + + +//! \brief Set frequency value +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of IEEE 802.15.4 PHY settings +//! +//! \param[in] frequency +//! Frequency in MHz +//! +//! \param[in] fractFrequency +//! Fractional frequency in 1 MHz/65536 fractions. +//! +//! \return None +void PhyIf_setFrequencyIeee_15_4(uint8_t phyIndex, uint16_t frequency, uint16_t fractFrequency); + +#endif + diff --git a/source/phy/phy_if_prop.c b/source/phy/phy_if_prop.c new file mode 100644 index 0000000..62214f6 --- /dev/null +++ b/source/phy/phy_if_prop.c @@ -0,0 +1,103 @@ +/****************************************************************************** +* Filename: phy_if_prop.c +* +* Description: Source file for PHY interface proprietary +* +* Copyright (C) 2018 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. +* +******************************************************************************/ + +#include "phy_if_prop.h" +#include "phy_tables.h" +#include "../radio_if_dataqueue.h" + + +void PhyIf_setFrequencyProp(uint8_t phyIndex, uint16_t frequency, uint16_t fractFrequency) +{ +// Proprietary mode is not supported for CC26X0 and CMD_PROP_RADIO_DIV_SETUP is not +// defined for this device +#ifndef DeviceFamily_CC26X0 + rfc_CMD_PROP_RADIO_DIV_SETUP_t* pSetupCmd = Phy_phyTableProp[phyIndex].pSetupCmd; + rfc_CMD_FS_t* pFsCmd = Phy_phyTableProp[phyIndex].pFsCmd; + + pSetupCmd->centerFreq = frequency; + + pFsCmd->frequency = frequency; + pFsCmd->fractFreq = fractFrequency; +#endif +} + + +void PhyIf_configureSetupCmdProp(uint8_t phyIndex) +{ + // No configuration done for Setup command +} + + +void PhyIf_configureRxCmdProp(uint8_t phyIndex) +{ + rfc_CMD_PROP_RX_t* pPropRxCmd = Phy_phyTableProp[phyIndex].pRxCmd; + + // Modify CMD_PROP_RX command + pPropRxCmd->pQueue = RadioIF_dataQueueGet(); + pPropRxCmd->rxConf.bAutoFlushIgnored = 1; + pPropRxCmd->rxConf.bAutoFlushCrcErr = 0; + pPropRxCmd->maxPktLen = 0x80; + pPropRxCmd->pktConf.bRepeatOk = 1; + pPropRxCmd->pktConf.bRepeatNok = 1; + + // Append 1 status byte, 1 RSSI byte and 4 bytes timestamp + pPropRxCmd->rxConf.bAppendStatus = 1; + pPropRxCmd->rxConf.bAppendRssi = 1; + pPropRxCmd->rxConf.bAppendTimestamp = 1; + + // Append CRC bytes + pPropRxCmd->rxConf.bIncludeCrc = 0x1; +} + + +void PhyIf_configureTxCmdProp(uint8_t* packet, uint16_t packetLength, uint8_t phyIndex) +{ + rfc_CMD_PROP_TX_t* pPropTxCmd = Phy_phyTableProp[phyIndex].pTxCmd; + + pPropTxCmd->pktLen = packetLength; + pPropTxCmd->pPkt = packet; + pPropTxCmd->startTrigger.triggerType = TRIG_NOW; +} + + + + + + + + diff --git a/source/phy/phy_if_prop.h b/source/phy/phy_if_prop.h new file mode 100644 index 0000000..3ee56dd --- /dev/null +++ b/source/phy/phy_if_prop.h @@ -0,0 +1,91 @@ +/****************************************************************************** +* Filename: phy_if_prop.h +* +* Description: Header file for PHY interface proprietary +* +* Copyright (C) 2018 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. +* +******************************************************************************/ +#ifndef PHY_IF_PROP_H +#define PHY_IF_PROP_H + +#include + +//! \brief Configure RF Core Setup command for the proprietary PHYs +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of proprietary PHY settings +//! +//! \return None +void PhyIf_configureSetupCmdProp(uint8_t phyIndex); + + +//! \brief Configure RF Core RX command for the proprietary PHYs +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of proprietary PHY settings +//! +//! \return None +void PhyIf_configureRxCmdProp(uint8_t phyIndex); + + +//! \brief Configure RF Core TX command for the proprietary PHYs +//! +//! \param[in] packet +//! Pointer to packet +//! +//! \param[in] packetLength +//! Length of the packet +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of proprietary PHY settings +//! +//! \return None +void PhyIf_configureTxCmdProp(uint8_t* packet, uint16_t packetLength, uint8_t phyIndex); + + +//! \brief Set frequency value +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of proprietary PHY settings +//! +//! \param[in] frequency +//! Frequency in MHz +//! +//! \param[in] fractFrequency +//! Fractional frequency in 1 MHz/65536 fractions. +//! +//! \return None +void PhyIf_setFrequencyProp(uint8_t phyIndex, uint16_t frequency, uint16_t fractFrequency); + +#endif + diff --git a/source/phy/phy_if_prop_15_4g.c b/source/phy/phy_if_prop_15_4g.c new file mode 100644 index 0000000..5ec894b --- /dev/null +++ b/source/phy/phy_if_prop_15_4g.c @@ -0,0 +1,106 @@ +/****************************************************************************** +* Filename: phy_if_prop_15_4g.c +* +* Description: Source file for PHY interface proprietary +* +* Copyright (C) 2018 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. +* +******************************************************************************/ + +#include "phy_if_prop_15_4g.h" +#include "phy_tables.h" +#include "../radio_if_dataqueue.h" + + +void PhyIf_setFrequencyProp_15_4g(uint8_t phyIndex, uint16_t frequency, uint16_t fractFrequency) +{ +// Proprietary 15.4g mode is not supported for CC26X0 and CMD_PROP_RADIO_DIV_SETUP is not +// defined for this device +#ifndef DeviceFamily_CC26X0 + rfc_CMD_PROP_RADIO_DIV_SETUP_t* pSetupCmd = Phy_phyTableProp_15_4_g[phyIndex].pSetupCmd; + rfc_CMD_FS_t* pFsCmd = Phy_phyTableProp_15_4_g[phyIndex].pFsCmd; + + + pSetupCmd->centerFreq = frequency; + + pFsCmd->frequency = frequency; + pFsCmd->fractFreq = fractFrequency; +#endif +} + + +void PhyIf_configureSetupCmdProp_15_4g(uint8_t phyIndex) +{ + // No configuration required for the setup command in IEEE 802.15.4g mode +} + + +void PhyIf_configureRxCmdProp_15_4g(uint8_t phyIndex) +{ + rfc_CMD_PROP_RX_ADV_t* pPropRxAdvCmd = Phy_phyTableProp_15_4_g[phyIndex].pRxCmd; + + // Modify CMD_PROP_RX_ADV command + pPropRxAdvCmd->pQueue = RadioIF_dataQueueGet(); + pPropRxAdvCmd->rxConf.bAutoFlushIgnored = 1; + pPropRxAdvCmd->rxConf.bAutoFlushCrcErr = 0; + pPropRxAdvCmd->maxPktLen = 2047; + pPropRxAdvCmd->pktConf.bRepeatOk = 1; + pPropRxAdvCmd->pktConf.bRepeatNok = 1; + + pPropRxAdvCmd->rxConf.bIncludeHdr = 0x1; + pPropRxAdvCmd->rxConf.bAppendRssi = 0x1; + pPropRxAdvCmd->rxConf.bAppendTimestamp = 0x1; + pPropRxAdvCmd->rxConf.bAppendStatus = 0x1; + + // Append CRC bytes + pPropRxAdvCmd->rxConf.bIncludeCrc = 0x1; +} + + +void PhyIf_configureTxCmdProp_15_4g(uint8_t* packet, uint16_t packetLength, uint8_t phyIndex) +{ + rfc_CMD_PROP_TX_ADV_t* pPropTxAdvCmd = Phy_phyTableProp_15_4_g[phyIndex].pTxCmd; + + // TRIG_ABSTIME with current time is used instaed of TRIG_NOW because of a + // bug in RF Core with the combination of 15.4g and TRIG_NOW (?) + + pPropTxAdvCmd->pktLen = packetLength; + pPropTxAdvCmd->pPkt = packet; + pPropTxAdvCmd->startTrigger.triggerType = TRIG_ABSTIME; + pPropTxAdvCmd->startTrigger.pastTrig = 1; + pPropTxAdvCmd->startTime = RF_getCurrentTime(); +} + + + + + diff --git a/source/phy/phy_if_prop_15_4g.h b/source/phy/phy_if_prop_15_4g.h new file mode 100644 index 0000000..b0012ef --- /dev/null +++ b/source/phy/phy_if_prop_15_4g.h @@ -0,0 +1,91 @@ +/****************************************************************************** +* Filename: phy_if_prop_15_4g.h +* +* Description: Header file for PHY interface proprietary IEEE 802.15.4g mode +* +* Copyright (C) 2018 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. +* +******************************************************************************/ +#ifndef PHY_IF_PROP_15_4_G_H +#define PHY_IF_PROP_15_4_G_H + +#include + +//! \brief Configure RF Core Setup command for the proprietary IEEE 802.15.4g PHYs +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of proprietary PHY settings +//! +//! \return None +void PhyIf_configureSetupCmdProp_15_4g(uint8_t phyIndex); + + +//! \brief Configure RF Core RX command for the proprietary IEEE 802.15.4g PHYs +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of proprietary IEEE 802.15.4g PHY settings +//! +//! \return None +void PhyIf_configureRxCmdProp_15_4g(uint8_t phyIndex); + + +//! \brief Configure RF Core TX command for the proprietary IEEE 802.15.4g PHYs +//! +//! \param[in] packet +//! Pointer to packet +//! +//! \param[in] packetLength +//! Length of the packet +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of proprietary IEEE 802.15.4g PHY settings +//! +//! \return None +void PhyIf_configureTxCmdProp_15_4g(uint8_t* packet, uint16_t packetLength, uint8_t phyIndex); + + +//! \brief Set frequency value +//! +//! \param[in] phyIndex +//! Index of the PHY setting in the table of proprietary IEEE 802.15.4g PHY settings +//! +//! \param[in] frequency +//! Frequency in MHz +//! +//! \param[in] fractFrequency +//! Fractional frequency in 1 MHz/65536 fractions. +//! +//! \return None +void PhyIf_setFrequencyProp_15_4g(uint8_t phyIndex, uint16_t frequency, uint16_t fractFrequency); + +#endif + diff --git a/source/phy/phy_io_config.h b/source/phy/phy_io_config.h new file mode 100644 index 0000000..6260c54 --- /dev/null +++ b/source/phy/phy_io_config.h @@ -0,0 +1,56 @@ +/****************************************************************************** +* Filename: phy_io_config.h +* +* Description: Header file for PHY IO configuration +* +* Copyright (C) 2018 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. +* +******************************************************************************/ +#ifndef PHY_IO_CONFIG_H +#define PHY_IO_CONFIG_H + + +#include "phy_rf_api.h" + + +//! \brief Initialize IO pins dependent on RF API +//! +//! \return +extern void Phy_IoPinInit(void); + +//! \brief Configure IO pins for the current RF API +//! +//! \return +extern void Phy_configureIoPins(const Phy_RfApi rfApi); + +#endif + diff --git a/source/phy/phy_manager.c b/source/phy/phy_manager.c new file mode 100644 index 0000000..d5749d8 --- /dev/null +++ b/source/phy/phy_manager.c @@ -0,0 +1,277 @@ +/****************************************************************************** +* Filename: phy_manager.c +* +* Description: Source file PHY manager functionality. +* +* Copyright (C) 2018 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. +* +******************************************************************************/ + +#include "phy_manager.h" +#include "phy_tables.h" +#include "phy_io_config.h" +#include "phy_if_prop.h" +#include "phy_if_prop_15_4g.h" +#include "phy_if_ieee_15_4.h" + +// Local functions +static bool PhyManager_isPhyNumberValid(uint8_t phyNumber); +static void PhyManager_configureCommands(void); + +// The currently used RF API (Proprietary, Proprietary in IEEE 802.15.4g mode, or IEEE 802.15.4) +// The default RF API is PROPRIETARY +static Phy_RfApi PhyManager_currentRfApi = PROPRIETARY; + +// The currently used PHY index (the index of the current setting in the table of settings for the currently used RF API) +// The default selected PHY index is 0 (that is the first PHY setting in the PHY table) +static uint8_t PhyManager_currentPhyIndex = 0; + +// Function pointer definitions for PHY interface functions +typedef void (*setFreqFunc)(uint8_t tableIndex, uint16_t frequency, uint16_t fractFrequency); +typedef void (*cfgSetupCmdFunc)(uint8_t phyIndex); +typedef void (*cfgRxCmdFunc)(uint8_t phyIndex); +typedef void (*cfgTxCmdFunc)(uint8_t* packet, uint16_t packetLength, uint8_t phyIndex); + +// A struct object with PHY interface function pointers +typedef struct Phy_Api_Obj +{ + setFreqFunc setFrequency; + cfgSetupCmdFunc configureSetupCmd; + cfgRxCmdFunc configureRxCmd; + cfgTxCmdFunc configureTxCmd; +} Phy_Api_Obj; + + +// Table of PHY interface functions. There is one array element for each PHY type, where each element contains function pointer to PHY interface functions +// +// Note!: The order of these elements must be in exactly the same order as the enums in the RfApi enum type, since +// the value of the RfApi is used as index in the array. +Phy_Api_Obj phyApis[] = +{ + {PhyIf_setFrequencyProp, PhyIf_configureSetupCmdProp, PhyIf_configureRxCmdProp, PhyIf_configureTxCmdProp}, // PHY interface functions for RF Proprietary PHYs + {PhyIf_setFrequencyProp_15_4g, PhyIf_configureSetupCmdProp_15_4g, PhyIf_configureRxCmdProp_15_4g, PhyIf_configureTxCmdProp_15_4g}, // PHY interface functions for PHYs using RF Proprietary in IEEE 802.15.4g mode + {PhyIf_setFrequencyIeee_15_4, PhyIf_configureSetupCmdIeee_15_4, PhyIf_configureRxCmdIeee_15_4, PhyIf_configureTxCmdIeee_15_4} // PHY interface functions for PHYs using IEEE 802.15.4 mode +}; + + +void PhyManager_init(void) +{ + Phy_IoPinInit(); +} + + +bool PhyManager_setPhy(uint8_t phyNumber) +{ + // Check that PHY number is valid + if(!PhyManager_isPhyNumberValid(phyNumber)) + { + return false; + } + + // Set currently used RfApi and Phy index + PhyManager_currentRfApi = Phy_supportedPhys[phyNumber].rfApi; + PhyManager_currentPhyIndex = Phy_supportedPhys[phyNumber].phyIndex; + + // Configure RF commands for currently selected PHY setting + PhyManager_configureCommands(); + + // Configure IO pins for currently selected RF API + Phy_configureIoPins(PhyManager_currentRfApi); + + return true; +} + + +void PhyManager_setFrequency(uint16_t frequency, uint16_t fractFrequency) +{ + phyApis[PhyManager_currentRfApi].setFrequency(PhyManager_currentPhyIndex, frequency, fractFrequency); +} + + +RF_RadioSetup* PhyManager_getSetupCmd(void) +{ + if(PhyManager_currentRfApi == PROPRIETARY) + { + return (RF_RadioSetup*)Phy_phyTableProp[PhyManager_currentPhyIndex].pSetupCmd; + } + else if(PhyManager_currentRfApi == PROPRIETARY_15_4_G) + { + return (RF_RadioSetup*)Phy_phyTableProp_15_4_g[PhyManager_currentPhyIndex].pSetupCmd; + } + else if(PhyManager_currentRfApi == IEEE_802_15_4) + { + return (RF_RadioSetup*)Phy_phyTableIeee_15_4[PhyManager_currentPhyIndex].pSetupCmd; + } + + return NULL; +} + + +RF_Op* PhyManager_getFsCmd(void) +{ + if(PhyManager_currentRfApi == PROPRIETARY) + { + return (RF_Op*)Phy_phyTableProp[PhyManager_currentPhyIndex].pFsCmd; + } + else if(PhyManager_currentRfApi == PROPRIETARY_15_4_G) + { + return (RF_Op*)Phy_phyTableProp_15_4_g[PhyManager_currentPhyIndex].pFsCmd; + } + else if(PhyManager_currentRfApi == IEEE_802_15_4) + { + return (RF_Op*)Phy_phyTableIeee_15_4[PhyManager_currentPhyIndex].pFsCmd; + } + + return NULL; +} + + +RF_Op* PhyManager_getRxCmd(void) +{ + if(PhyManager_currentRfApi == PROPRIETARY) + { + return (RF_Op*)Phy_phyTableProp[PhyManager_currentPhyIndex].pRxCmd; + } + else if(PhyManager_currentRfApi == PROPRIETARY_15_4_G) + { + return (RF_Op*)Phy_phyTableProp_15_4_g[PhyManager_currentPhyIndex].pRxCmd; + } + else if(PhyManager_currentRfApi == IEEE_802_15_4) + { + return (RF_Op*)Phy_phyTableIeee_15_4[PhyManager_currentPhyIndex].pRxCmd; + } + + return NULL; +} + + +RF_Op* PhyManager_getTxCmd(void) +{ + if(PhyManager_currentRfApi == PROPRIETARY) + { + return (RF_Op*)Phy_phyTableProp[PhyManager_currentPhyIndex].pTxCmd; + } + else if(PhyManager_currentRfApi == PROPRIETARY_15_4_G) + { + return (RF_Op*)Phy_phyTableProp_15_4_g[PhyManager_currentPhyIndex].pTxCmd; + } + else if(PhyManager_currentRfApi == IEEE_802_15_4) + { + return (RF_Op*)Phy_phyTableIeee_15_4[PhyManager_currentPhyIndex].pTxCmd; + } + + return NULL; +} + + +RF_Mode* PhyManager_getRfMode(void) +{ + if(PhyManager_currentRfApi == PROPRIETARY) + { + return (RF_Mode*)Phy_phyTableProp[PhyManager_currentPhyIndex].pRfMode; + } + else if(PhyManager_currentRfApi == PROPRIETARY_15_4_G) + { + return (RF_Mode*)Phy_phyTableProp_15_4_g[PhyManager_currentPhyIndex].pRfMode; + } + else if(PhyManager_currentRfApi == IEEE_802_15_4) + { + return (RF_Mode*)Phy_phyTableIeee_15_4[PhyManager_currentPhyIndex].pRfMode; + } + + return NULL; +} + + +void PhyManager_configureTxCommand(uint8_t* packet, uint16_t packetLength) +{ + phyApis[PhyManager_currentRfApi].configureTxCmd(packet, packetLength, PhyManager_currentPhyIndex); +} + + +extern Phy_RfApi PhyManager_getRfApi(void) +{ + return PhyManager_currentRfApi; +} + + +//! \brief Configure RF Core commands +//! Note: The configuration of RF command used for TX is handled in a +//! separate function, and not in this. +//! +//! \return None +void PhyManager_configureCommands(void) +{ + phyApis[PhyManager_currentRfApi].configureSetupCmd(PhyManager_currentPhyIndex); + phyApis[PhyManager_currentRfApi].configureRxCmd(PhyManager_currentPhyIndex); +} + + +//! \brief Check if PHY number is valid +//! +//! \param[in] phyNumber +//! PHY number +//! +//! \return true if PHY number is valid, and false otherwise +bool PhyManager_isPhyNumberValid(uint8_t phyNumber) +{ + if(phyNumber > (Phy_getNumSupportedPhys() - 1)) + { + return false; + } + + Phy_RfApi rfApi = Phy_supportedPhys[phyNumber].rfApi; + uint8_t phyIndex = Phy_supportedPhys[phyNumber].phyIndex; + + bool status = false; + if(rfApi == PROPRIETARY) + { + status = phyIndex > (Phy_getNumPropPhys() - 1) ? false : true; + } + else if(rfApi == PROPRIETARY_15_4_G) + { + status = phyIndex > (Phy_getNumProp_15_4_g_Phys() - 1) ? false : true; + } + else if(rfApi == IEEE_802_15_4) + { + status = phyIndex > (Phy_getNumIeee_15_4_Phys() - 1) ? false : true; + } + + return status; +} + + + + + + + diff --git a/source/phy/phy_manager.h b/source/phy/phy_manager.h new file mode 100644 index 0000000..9f28e5e --- /dev/null +++ b/source/phy/phy_manager.h @@ -0,0 +1,125 @@ +/****************************************************************************** +* Filename: phy_manager.h +* +* Description: Header file for PHY manager functionality +* +* Copyright (C) 2018 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. +* +******************************************************************************/ +#ifndef PHY_MANAGER_H +#define PHY_MANAGER_H + +#include +// TI-RTOS radio driver +#include + +#include "phy_rf_api.h" + + +//! \brief Initialize PHY manager +//! +void PhyManager_init(void); + + +//! \brief Set PHY (RF API and PHY setting) +//! +//! \param[in] phyNumber +//! The number of the PHY to use. This number decodes into an RF API and a PHY setting +//! +//! \return true if phyNumber is valid, or false otherwise +extern bool PhyManager_setPhy(uint8_t phyNumber); + + + +//! \brief Set frequency value for currently used PHY +//! +//! \param[in] frequency +//! Frequency in MHz +//! +//! \param[in] fractFrequency +//! Fractional frequency in 1 MHz/65536 fractions. +//! +//! \return None +extern void PhyManager_setFrequency(uint16_t frequency, uint16_t fractFrequency); + + +//! \brief Configure RF command used for TX +//! +//! \param[in] packet +//! Pointer to packet +//! +//! \param[in] packetLength +//! Length of the packet +//! +//! \return None +extern void PhyManager_configureTxCommand(uint8_t* packet, uint16_t packetLength); + + +//! \brief Get pointer to the currently used RF Core Setup Command (for example: CMD_PROP_RADIO_DIV_SETUP) +//! +//! \return Pointer to RF Core Setup command +extern RF_RadioSetup* PhyManager_getSetupCmd(void); + + +//! \brief Get pointer to the currently used RF Core FS Command (CMD_FS) +//! +//! \return Pointer to RF Core FS command +extern RF_Op* PhyManager_getFsCmd(void); + + +//! \brief Get pointer to the currently used RF Core RX Command (for example: CMD_PROP_RX) +//! +//! \return Pointer to RF Core RX command +extern RF_Op* PhyManager_getRxCmd(void); + + +//! \brief Get pointer to the currently used RF Core TX Command (for example: CMD_PROP_TX) +//! +//! \return Pointer to RF Core TX command +extern RF_Op* PhyManager_getTxCmd(void); + + +//! \brief Get pointer to the currently used RF mode object +//! +//! \return Pointer to RF mode object +extern RF_Mode* PhyManager_getRfMode(void); + + +//! \brief Get currently used RF API type +//! (With RF API it is meant the RF Core API type, for example Proprietary, +//! proprietary in IEEE 802.15.4g mode, or IEEE 802.15.4) +//! +//! \return Pointer to RF mode object +extern Phy_RfApi PhyManager_getRfApi(void); + +#endif + diff --git a/source/phy/phy_rf_api.h b/source/phy/phy_rf_api.h new file mode 100644 index 0000000..a4351ef --- /dev/null +++ b/source/phy/phy_rf_api.h @@ -0,0 +1,53 @@ +/****************************************************************************** +* Filename: phy_rf_api.h +* +* Description: Header file for PHY RF API types +* +* Copyright (C) 2018 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. +* +******************************************************************************/ +#ifndef PHY_RF_API_H +#define PHY_RF_API_H + + +// RF API types +typedef enum Phy_RfApi +{ + PROPRIETARY=0, + PROPRIETARY_15_4_G=1, + IEEE_802_15_4=2, +} Phy_RfApi; + + + +#endif + diff --git a/source/phy/phy_tables.h b/source/phy/phy_tables.h new file mode 100644 index 0000000..a8d20c1 --- /dev/null +++ b/source/phy/phy_tables.h @@ -0,0 +1,141 @@ +/****************************************************************************** +* Filename: phy_tables.h +* +* Description: Header file for PHY tables +* +* Copyright (C) 2018 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. +* +******************************************************************************/ +#ifndef PHY_TABLES_H +#define PHY_TABLES_H + +// TI-RTOS radio driver +#include + + + +// There are no rf_ieee_cmd.h for DeviceFamily_CC13X0 +#ifndef DeviceFamily_CC13X0 +// A separate include file is required for IEEE command definitions. +// RF.h does not include the header file for this. +//#include "DeviceFamily.h" +//#include DeviceFamily_constructPath(driverlib/rf_ieee_cmd.h) +#include "rf_ieee_cmd.h" +#else +// For DeviceFamily_CC13X0 define the IEEE 15.4 RF commands as void +// These are not used for CC13X0, but required to build the project. +#define rfc_CMD_IEEE_RX_t void +#define rfc_CMD_IEEE_TX_t void +#endif + +#include "phy_rf_api.h" + + +// Struct definition for RF Proprietary PHY settings +typedef struct Phy_Prop_Obj +{ + rfc_CMD_PROP_RADIO_DIV_SETUP_t* pSetupCmd; + rfc_CMD_FS_t* pFsCmd; + rfc_CMD_PROP_RX_t* pRxCmd; + rfc_CMD_PROP_TX_t* pTxCmd; + RF_Mode* pRfMode; +} Phy_Prop_Obj; + + +// Struct definition for IEEE 802.15.4g Proprietary PHY settings +typedef struct Phy_Prop_15_4_g_Obj +{ + rfc_CMD_PROP_RADIO_DIV_SETUP_t* pSetupCmd; + rfc_CMD_FS_t* pFsCmd; + rfc_CMD_PROP_RX_ADV_t* pRxCmd; + rfc_CMD_PROP_TX_ADV_t* pTxCmd; + RF_Mode* pRfMode; +} Phy_Prop_15_4_g_Obj; + + +// Struct definition for IEEE 802.15.4 PHY settings +typedef struct Phy_Ieee_15_4_Obj +{ + rfc_CMD_RADIO_SETUP_t* pSetupCmd; + rfc_CMD_FS_t* pFsCmd; + rfc_CMD_IEEE_RX_t* pRxCmd; + rfc_CMD_IEEE_TX_t* pTxCmd; + RF_Mode* pRfMode; +} Phy_Ieee_15_4_Obj; + + +// Struct definition for a PHY (That is combination of RF API and PHY setting) +typedef struct Phy_Obj +{ + Phy_RfApi rfApi; + uint8_t phyIndex; +} Phy_Obj; + + +// Table of proprietary PHY settings +extern const Phy_Prop_Obj Phy_phyTableProp[]; + +// Table of proprietary IEEE 802.15.4g PHY settings +extern const Phy_Prop_15_4_g_Obj Phy_phyTableProp_15_4_g[]; + +// Table of IEEE 802.15.4 PHY settings +extern const Phy_Ieee_15_4_Obj Phy_phyTableIeee_15_4[]; + +// Table of all supported PHYs (Combinations of RF API and setting) +extern const Phy_Obj Phy_supportedPhys[]; + + +//! \brief Get number of supported PHYs (Combinations of RF API and setting) +//! +//! \return Number of supported PHYs +extern uint8_t Phy_getNumSupportedPhys(void); + + +//! \brief Get number of proprietary PHY settings +//! +//! \return Number of proprietary PHY settings +extern uint8_t Phy_getNumPropPhys(void); + + +//! \brief Get number of proprietary IEEE 802.15.4g PHY settings +//! +//! \return Number of proprietary IEEE 802.15.4g PHY settings +extern uint8_t Phy_getNumProp_15_4_g_Phys(void); + + +//! \brief Get number of IEEE 802.15.4 PHY settings +//! +//! \return Number of IEEE 802.15.4 PHY settings +extern uint8_t Phy_getNumIeee_15_4_Phys(void); + +#endif + diff --git a/source/radio_if.c b/source/radio_if.c new file mode 100644 index 0000000..5dafda9 --- /dev/null +++ b/source/radio_if.c @@ -0,0 +1,296 @@ +/****************************************************************************** +* Filename: radio_if.c +* +* Description: Source file for configuration of the radio interface. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#include "radio_if.h" +#include "radio_if_dataqueue.h" +#include "phy_manager.h" + +// TI-RTOS radio driver +#include + +// TI-RTOS +#include + +// Default PHY number (the PHY number selected at startup) +#define DEFAULT_PHY_NUMBER 0 + +uint8_t RadioIF_numBufferOverflow = 0; +RF_Object RadioIF_RF_Obj; +RF_Handle RadioIF_Rf_Handle; +RF_CmdHandle RadioIF_Cmd_Handle; + +static bool RadioIF_isInitialized = false; +static ratOverflowCb RadioIF_ratCbFunc = NULL; + +static rfCoreEventCb RadioIF_rfCoreEventCbFunc = NULL; +// Local functions +//static void RadioIF_ratCallback(RF_Handle h, RF_RatHandle rh, RF_EventMask e, uint32_t timeout); +static void RadioIF_ratCallback(RF_Handle h, RF_CmdHandle X, RF_EventMask e); +static void RadioIF_flushDataQueue(void); +static void RadioIF_enableRadioTimerOverflowCallback(void); +static void RadioIF_startRfCoreRx(void); +static void RadioIF_rfCoreEventCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e); +static void RadioIF_registerRfCoreEventCbApiSpecific(rfCoreEventCb func); + +// Radio Timer Channel available for user code (Cortex M3) +#define RAT_CHANNEL_USER 5 + +void RadioIF_init(void) +{ + if(!RadioIF_isInitialized) + { + RadioIF_dataQueueInit(); + PhyManager_setPhy(DEFAULT_PHY_NUMBER); + RadioIF_isInitialized = true; + } +} + + +void RadioIF_startRx(void) +{ + RadioIF_flushDataQueue(); + RadioIF_startRfCoreRx(); + RadioIF_enableRadioTimerOverflowCallback(); +} + + +void RadioIF_stopRx(void) +{ + // Cancel and flush CMD_PROP_RX + RF_flushCmd(RadioIF_Rf_Handle, RF_CMDHANDLE_FLUSH_ALL, 0); + + // Close RF driver + RF_close(RadioIF_Rf_Handle); + + // Clear RX queue entries and reset queue + RadioIF_flushDataQueue(); +} + + +uint8_t RadioIF_getStatus(void) +{ + uint8_t status = RADIO_IF_STATUS_OK; + + if(RadioIF_numBufferOverflow>0) + { + status = RADIO_IF_STATUS_RX_BUFFER_OVERFLOW; + } + + return status; +} + + +//! \brief Clears all entries in the radio data queue and resets +//! read and write pointers. +//! +//! \return None +void RadioIF_flushDataQueue(void) +{ + rfc_CMD_CLEAR_RX_t cmd; + cmd.commandNo = CMD_CLEAR_RX; + cmd.pQueue = RadioIF_dataQueueGet(); + RF_runImmediateCmd(RadioIF_Rf_Handle, (uint32_t*)&cmd); + RadioIF_resetDataQueue(); +} + + +void RadioIF_handleBufferOverflow(void) +{ + // Make sure this function is not interrupted by HWI or task switch + int key = Hwi_disable(); + RadioIF_flushDataQueue(); + + // Restart the RF Core in RX + RadioIF_startRfCoreRx(); + + // Update status + RadioIF_numBufferOverflow=0; + Hwi_restore(key); +} + + +void RadioIF_setFrequency(uint16_t frequency, uint16_t fractFrequency) +{ + PhyManager_setFrequency(frequency, fractFrequency); +} + + +void RadioIF_initTx(void) +{ + RF_Params rfParams; + RF_Params_init(&rfParams); + + // Open RF driver + RadioIF_Rf_Handle = RF_open(&RadioIF_RF_Obj, PhyManager_getRfMode(), PhyManager_getSetupCmd(), &rfParams); + + // Issue the RF Core FS command + RF_postCmd(RadioIF_Rf_Handle, PhyManager_getFsCmd(), RF_PriorityNormal, NULL, 0); +} + + +void RadioIF_sendPacket(uint8_t* packet, uint16_t packetLength) +{ + PhyManager_configureTxCommand(packet, packetLength); + + // For IEEE 802.15.4 we must subscribe to the RF_EventLastFGCmdDone event, otherwise the function hangs + #define RF_EventLastFGCmdDone (1 << 3) + RF_EventMask result = RF_runCmd(RadioIF_Rf_Handle, PhyManager_getTxCmd(), RF_PriorityNormal, NULL, (RF_EventLastCmdDone | RF_EventLastFGCmdDone)); +} + + +void RadioIF_disableTx(void) +{ + // Cancel and flush CMD_PROP_RX + RF_flushCmd(RadioIF_Rf_Handle, RF_CMDHANDLE_FLUSH_ALL, 0); + + // Close RF driver + RF_close(RadioIF_Rf_Handle); +} + + +//! \brief Callback function called upon RAT events +//! +//! \return None +//void RadioIF_ratCallback(RF_Handle h, RF_RatHandle rh, RF_EventMask e, uint32_t timeout) +void RadioIF_ratCallback(RF_Handle h, RF_CmdHandle X, RF_EventMask e) +{ + if(e & RF_EventRatCh) + { + if(RadioIF_ratCbFunc) + { + uint32_t currentTime = RF_getCurrentTime(); + RadioIF_ratCbFunc(currentTime); + + // Re-enable overflow interrupt + RadioIF_enableRadioTimerOverflowCallback(); + } + } +} + + +void RadioIF_enableRadioTimerOverflowCallback(void) +{ + uint32_t compareTime = 0; + uint32_t currentTime = RF_getCurrentTime(); + + // Set compareTime to next MSB number of RAT timer with the rest of bytes 0x0 + // (0x10000000, 0x20000000, 0x30000000, ....) + compareTime = (currentTime & 0xF0000000) + 0x10000000; + +/* RF_RatConfigCompare ratCompareCfg; + ratCompareCfg.callback = &RadioIF_ratCallback; + ratCompareCfg.channel = RF_RatChannelAny; + ratCompareCfg.timeout = compareTime; + + // Configure RAT compare callback at timer overflow value + RF_RatHandle ratHandle = RF_ratCompare(RadioIF_Rf_Handle, &ratCompareCfg, NULL); +*/ + rfc_CMD_SET_RAT_CMP_t ratCmpCmd; + int8_t ratHandle = RF_ratCompare(RadioIF_Rf_Handle, &ratCmpCmd, compareTime, &RadioIF_ratCallback); + +} + + +void RadioIF_registerRatOverflowCb(ratOverflowCb func) +{ + RadioIF_ratCbFunc = func; +} + + +bool RadioIf_setPhy(uint8_t phyNumber) +{ + return PhyManager_setPhy(phyNumber); +} + + +//! \brief Start RF Core in RX +//! +//! \return None +void RadioIF_startRfCoreRx(void) +{ + RF_Params rfParams; + RF_Params_init(&rfParams); + + // Open RF driver + RadioIF_Rf_Handle = RF_open(&RadioIF_RF_Obj, PhyManager_getRfMode(), PhyManager_getSetupCmd(), &rfParams); + + // Issue CMD_FS + RF_runCmd(RadioIF_Rf_Handle, PhyManager_getFsCmd(), RF_PriorityNormal, NULL, 0); + + // Issue Rf Core RX command to start RX + RadioIF_Cmd_Handle = RF_postCmd(RadioIF_Rf_Handle, PhyManager_getRxCmd(), RF_PriorityNormal, &RadioIF_rfCoreEventCallback, IRQ_RX_BUF_FULL | IRQ_RX_ENTRY_DONE); +} + + +extern void RadioIF_registerRfCoreEventCb(rfCoreEventCb func) +{ + RadioIF_registerRfCoreEventCbApiSpecific(func); +} + + +//! \brief Callback function called upon RF Core events +//! +//! \return None +void RadioIF_rfCoreEventCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e) +{ + uint32_t events = 0; + + if(e & RF_EventRxEntryDone) + { + events |= RX_ENTRY_DONE_EVENT; + } + + if(e & RF_EventRxBufFull) + { + events |= RX_BUFFER_FULL_EVENT; + if(PhyManager_getRfApi() == IEEE_802_15_4) + { + // Abort RX command (For IEEE 15.4 API RX does not end automatically on this event) + RF_cancelCmd(RadioIF_Rf_Handle, RadioIF_Cmd_Handle, 0); + } + } + + if(events && RadioIF_rfCoreEventCbFunc) + { + RadioIF_rfCoreEventCbFunc(events); + } +} + +void RadioIF_registerRfCoreEventCbApiSpecific(rfCoreEventCb func) +{ + RadioIF_rfCoreEventCbFunc = func; +} diff --git a/source/radio_if.h b/source/radio_if.h new file mode 100644 index 0000000..7e8519a --- /dev/null +++ b/source/radio_if.h @@ -0,0 +1,163 @@ +/****************************************************************************** +* Filename: radio_if.h +* +* Description: Header file for configuration of the radio interface. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef RADIO_IF_H +#define RADIO_IF_H + +#include +#include + +#define RADIO_TIMER_FREQUENCY_MHZ 4 // 4 MHz +#define RADIO_TIMER_HALF_CYCLE_VALUE 0x80000000 +#define RADIO_TIMER_FIRST_QUARTER_CYCLE_VALUE 0x40000000 +#define RADIO_TIMER_THIRD_QUARTER_CYCLE_VALUE 0xC0000000 + + +// Selected IRQ event numbers from rf_mailbox +#define RX_ENTRY_DONE_EVENT 1 +#define RX_BUFFER_FULL_EVENT 2 + + +// Radio Interface status +typedef enum RadioIfStatus +{ + RADIO_IF_STATUS_OK, + RADIO_IF_STATUS_RX_BUFFER_OVERFLOW, +} RadioIfStatus; + +//! Typedef for RAT timer overflow callback function +//! The ratCbTime is the time when the callback happened +typedef void (*ratOverflowCb)(uint32_t ratCbTime); + +typedef void (*rxEntryDoneCb)(void); +typedef void (*rfCoreEventCb)(uint32_t rfCoreEvent); + +//! \brief Initialize the radio interface +//! +//! \return None +extern void RadioIF_init(void); + + +//! \brief Start radio receive mode +//! +//! \return None +extern void RadioIF_startRx(void); + + +//! \brief Stop radio receive mode +//! +//! \return None +extern void RadioIF_stopRx(void); + + +//! \brief Get status of the radio interface. +//! +//! \return One of the values defined by RadioIfStatus +extern uint8_t RadioIF_getStatus(void); + + +//! \brief Handle an RX buffer overflow situation +//! +//! \return None +extern void RadioIF_handleBufferOverflow(void); + + +//! \brief Set frequency value +//! The radio shall be stopped before calling this function. The new +//! frequency takes effect when the radio is restarted after calling +//! this function. +//! +//! \param[in] frequency +//! Frequency in MHz +//! +//! \param[in] fractFrequency +//! Fractional frequency in 1 MHz/65536 fractions. +//! +//! \return None +extern void RadioIF_setFrequency(uint16_t frequency, uint16_t fractFrequency); + + +//! \brief Initialize radio for packet transmit +//! +//! \return None +extern void RadioIF_initTx(void); + + +//! \brief Send packet. +//! The radio shall be stopped before calling this function. +//! +//! \param[in] packet +//! Pointer to packet data buffer +//! +//! \param[in] packetLength +//! Length of packet data buffer +//! +//! \return None +extern void RadioIF_sendPacket(uint8_t* packet, uint16_t packetLength); + + +//! \brief Deinitialize/disable radio for packet transmit +//! This function does clean up and close RF driver after packet transmit. +//! +//! \return None +extern void RadioIF_disableTx(void); + + +//! \brief Register function for Radio timer overflow callback +//! +//! The callback function will be called two timer per timer cycle +//! (When timer reaches half cycle value and when it wraps around) +//! +//! \return None +extern void RadioIF_registerRatOverflowCb(ratOverflowCb func); + + +extern void RadioIF_registerRxEntryDoneCb(rxEntryDoneCb func); +extern void RadioIF_registerRfCoreEventCb(rfCoreEventCb func); + +//! \brief Set PHY (RF API and PHY setting) +//! +//! \param[in] phyNumber +//! The number of the PHY to use. This number decodes into an RF API and a PHY setting. +//! (Examples of RF API are Proprietary, Proprietary in IEEE 802.15.4g mode, and +//! IEEE 802.15.4) +//! +//! \return true if phyNumber is valid, or false otherwise +extern bool RadioIf_setPhy(uint8_t phyNumber); + +#endif + diff --git a/source/radio_if_dataqueue.c b/source/radio_if_dataqueue.c new file mode 100644 index 0000000..bc1c082 --- /dev/null +++ b/source/radio_if_dataqueue.c @@ -0,0 +1,281 @@ +/****************************************************************************** +* Filename: radio_if_dataqueue.c +* +* Description: Source file for configuration of the radio interface data queue. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + + +#include "radio_if_dataqueue.h" +#include "phy_manager.h" +#include "common.h" +#include "DeviceFamily.h" +#include DeviceFamily_constructPath(driverlib/rf_prop_mailbox.h) +#include DeviceFamily_constructPath(driverlib/rf_mailbox.h) +#include DeviceFamily_constructPath(driverlib/rf_data_entry.h) +#include + + +// Defines for the data queue +#define RF_QUEUE_DATA_ENTRY_HEADER_SIZE 8 // 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))) + +#define MAX_LENGTH 2047 // Max length the radio will accept +#ifndef NUM_DATA_ENTRIES +#define NUM_DATA_ENTRIES 3 // Number of data entries +#endif + + +// In 802.15.4G mode (using CMD_PROP_RX_ADV) the entries will contain the following: +// 2 header bytes (RF_cmdPropRxAdv.rxConf.bIncludeHdr = 0x1, RF_cmdPropRxAdv.hdrConf.numHdrBits = 16) +// 1 RSSI byte (RF_cmdPropRxAdv.rxConf.bAppendRssi = 0x1) +// 1 status byte (RF_cmdPropRxAdv.rxConf.bAppendStatus = 0x1) +// 4 CRC bytes (RF_cmdPropRxAdv.rxConf.bIncludeCrc = 0x1, RF_cmdPropRxAdv.formatConf.whitenMode = 0x4) +// 4 Timestamp bytes (RF_cmdPropRxAdv.rxConf.bAppendTimestamp = 0x1) +#define NUM_APPENDED_BYTES 12 +#define ENTRY_LENGTH_CONFIG_SIZE 2 + +static dataQueue_t RadioIF_dataQueue; + + + +#if defined(DeviceFamily_CC13X0) || defined(DeviceFamily_CC26X0) +// Place the RX buffer in GPRAM for CC13x0 family to save SRAM space +#ifdef __ICCARM__ +#pragma location=".gpram" +#elif __TI_COMPILER_VERSION__ +#pragma DATA_SECTION(RadioIF_rxDataBuffer, ".gpram_data") +#endif +#endif +// Storage for the data entries +static uint8_t RadioIF_rxDataBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES, MAX_LENGTH, NUM_APPENDED_BYTES)]; + +// Data entry pointer to keep track of read items +static rfc_dataEntryGeneral_t* RadioIF_readEntry; + +// Local functions +static uint8_t RadioIF_createDataQueue(dataQueue_t *dataQueue, uint8_t *buf, uint16_t buf_len, uint8_t numEntries, uint16_t length); +static void RadioIF_nextEntry(void); +static void handle154gPacket(uint8_t* buffer, uint16_t elementLength); +static inline bool isFourOctetFcsType(uint8_t phyHeaderLowByte); +static inline void invertBytes(uint8_t* buffer, uint8_t length); + +void RadioIF_dataQueueInit(void) +{ + RadioIF_createDataQueue(&RadioIF_dataQueue, RadioIF_rxDataBuffer, sizeof(RadioIF_rxDataBuffer), NUM_DATA_ENTRIES, MAX_LENGTH + NUM_APPENDED_BYTES); +} + + +dataQueue_t* RadioIF_dataQueueGet(void) +{ + return &RadioIF_dataQueue; +} + + +bool RadioIF_hasPacket(void) +{ + return (RadioIF_readEntry->status == DATA_ENTRY_FINISHED); +} + + +uint16_t RadioIF_takePacket(uint8_t* buffer, uint16_t maxLen) +{ + if(!RadioIF_hasPacket()) return 0; + + uint16_t elementLength = Common_get16BitValueLE(&RadioIF_readEntry->data); + + // Discard packets larger than buffer size + if(elementLength <= maxLen) + { + memcpy(buffer, (uint8_t*)(&RadioIF_readEntry->data + ENTRY_LENGTH_CONFIG_SIZE), elementLength); + + if(PhyManager_getRfApi() == PROPRIETARY_15_4_G) + { + // Perform required buffer modications for IEEE 802.15.4g packets + handle154gPacket(buffer, elementLength); + } + } + + // free the entry for radio to use + RadioIF_nextEntry(); + + return elementLength; +} + + +void RadioIF_resetDataQueue(void) +{ + uint8_t* firstEntry = RadioIF_rxDataBuffer; + + // Set read pointer and data queue current entry pointer both to first entry + RadioIF_dataQueue.pCurrEntry = firstEntry; + RadioIF_readEntry = (rfc_dataEntryGeneral_t*)firstEntry; +} + + +//! \brief Free current read entry for radio to use, and move to next entry +//! +//! \return None +void RadioIF_nextEntry(void) +{ + // Set status to pending + RadioIF_readEntry->status = DATA_ENTRY_PENDING; + + // Move read entry pointer to next entry + RadioIF_readEntry = (rfc_dataEntryGeneral_t*)RadioIF_readEntry->pNextEntry; +} + + +//! \brief Create data queue +//! +//! \param[in] dataQueue +//! Pointer to data queue object +//! +//! \param[in] buf +//! Buffer for RX data entries +//! +//! \param[in] bufLength +//! Buffer length +//! +//! \param[in] numEntries +//! Number of data entries +//! +//! \param[in] length +//! Length of each data entry +//! +//! \return 0 if configuration is successful or 1 if queue does not fit in buffer. +uint8_t RadioIF_createDataQueue(dataQueue_t *dataQueue, uint8_t *buf, uint16_t bufLength, uint8_t numEntries, uint16_t length) +{ + if (bufLength < (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); + + // Configure each data entry + 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 = ENTRY_LENGTH_CONFIG_SIZE; // Two bytes length indicator + ((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 + RadioIF_readEntry = (rfc_dataEntryGeneral_t*)first_entry; + + return (0); +} + + +//! \brief Check if a 15.4g packet has 4 or 2 bytes FCS type +//! +//! \param[in] phyHeaderLowByte +//! Low byte of PHY header as read from RFCore in MSbit format +//! +//! \return true for 4 octet FCS, false for 2 octects FCS +static inline bool isFourOctetFcsType(uint8_t phyHeaderLowByte) +{ + // FCS Type is bit 3 MSB -> bit 4 LSB + return (~phyHeaderLowByte & 0x10); +} + +//! \brief Inverts all the bits for each byte in buffer +//! +//! \param[in] buffer +//! Pointer to start of data buffer +//! +//! \param[in] length +//! Buffer length (number of bytes to invert) +static inline void invertBytes(uint8_t* buffer, uint8_t length) +{ + for(int i=0; i= NUM_APPENDED_BYTES) + { + // Find CRC offset from buffer start + uint16_t crcOffset = elementLength - 4 - 1 - 4 - 1; // Subtract the appended 4 bytes CRC, 1 byte RSSI, 4 bytes timestamp and 1 byte RSSI + + // Invert the 4 CRC byte values + invertBytes(&buffer[crcOffset], 4); + } + } +} + + + + + + diff --git a/source/radio_if_dataqueue.h b/source/radio_if_dataqueue.h new file mode 100644 index 0000000..f3658de --- /dev/null +++ b/source/radio_if_dataqueue.h @@ -0,0 +1,93 @@ +/****************************************************************************** +* Filename: radio_if_dataqueue.h +* +* Description: Header file for configuration of the radio interface data queue. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef RADIO_IF_DATAQUEUE_H +#define RADIO_IF_DATAQUEUE_H + + +/* driverlib header files */ +#include "DeviceFamily.h" +#include DeviceFamily_constructPath(driverlib/rf_data_entry.h) +#include + +// Length of field added by Rf Core +#define RADIO_IF_RSSI_LEN 1 +#define RADIO_IF_TIMESTAMP_LEN 4 +#define RADIO_IF_STATUS_LEN 1 + +//! \brief Initialize the radio data queue +//! +//! \return None +extern void RadioIF_dataQueueInit(void); + + +//! \brief Get pointer to radio data queue +//! +//! \return Pointer to data queue +extern dataQueue_t* RadioIF_dataQueueGet(void); + + +//! \brief Check if the data queue has received one or more packets +//! +//! \return true if one or more packets are received, false otherwise. +extern bool RadioIF_hasPacket(void); + + +//! \brief Take a packet from the data queue +//! +//! This function will copy data from the data queue to the buffer given +//! as parameter. +//! +//! \param[in] buffer +//! Pointer to buffer where the radio packet will be copied. This +//! buffer must be allocated by the caller. +//! +//! \param[in] maxLen +//! Max number of bytes to copy. +//! +//! \return number of bytes copied to the data buffer. +extern uint16_t RadioIF_takePacket(uint8_t* buffer, uint16_t maxLen); + + +//! \brief Resets data queue internally used read and write pointers +//! +//! \return None +extern void RadioIF_resetDataQueue(void); + + +#endif + diff --git a/source/smartrf_settings/cc26x0lp/15.4/smartrf_settings_15_4_0.c b/source/smartrf_settings/cc26x0lp/15.4/smartrf_settings_15_4_0.c new file mode 100644 index 0000000..e6a50e3 --- /dev/null +++ b/source/smartrf_settings/cc26x0lp/15.4/smartrf_settings_15_4_0.c @@ -0,0 +1,219 @@ +//********************************************************************************* +// Generated by SmartRF Studio version 2.7.0 (build #21) +// Tested for SimpleLink SDK version: No known SDK for this device +// Device: CC2650 Rev. 2.2 +// +//********************************************************************************* + + +//********************************************************************************* +// Parameter summary +// IEEE Channel: 11 +// Frequency: 2405 MHz +// SFD: 0 +// Preamble (32 bit): 01010101... +// TX Power: 5 dBm + +#include "DeviceFamily.h" +#include DeviceFamily_constructPath(driverlib/rf_mailbox.h) +#include DeviceFamily_constructPath(driverlib/rf_common_cmd.h) +//#include DeviceFamily_constructPath(driverlib/rf_ieee_cmd.h) +#include "rf_ieee_cmd.h" +#include +#include "smartrf_settings_15_4_0.h" + + +// TI-RTOS RF Mode Object +RF_Mode Ieee154_0_mode = +{ + .rfMode = RF_MODE_IEEE_15_4, + .cpePatchFxn = 0, + .mcePatchFxn = 0, + .rfePatchFxn = 0, +}; + +// Overrides for CMD_RADIO_SETUP +static uint32_t pOverrides[] = +{ + // override_synth_ieee_15_4.xml + // Synth: Set recommended RTRIM to 5 + HW_REG_OVERRIDE(0x4038,0x0035), + // Synth: Set Fref to 3.43 MHz + (uint32_t)0x000784A3, + // Synth: Set loop bandwidth after lock to 80 kHz + (uint32_t)0xA47E0583, + // Synth: Set loop bandwidth after lock to 80 kHz + (uint32_t)0xEAE00603, + // Synth: Set loop bandwidth after lock to 80 kHz + (uint32_t)0x00010623, + // Synth: Configure PLL bias + HW32_ARRAY_OVERRIDE(0x405C,1), + // Synth: Configure PLL bias + (uint32_t)0x1801F800, + // Synth: Configure PLL latency + HW32_ARRAY_OVERRIDE(0x402C,1), + // Synth: Configure PLL latency + (uint32_t)0x00608402, + // Synth: Use 24 MHz XOSC as synth clock, enable extra PLL filtering + (uint32_t)0x02010403, + // Synth: Configure extra PLL filtering + HW32_ARRAY_OVERRIDE(0x4034,1), + // Synth: Configure extra PLL filtering + (uint32_t)0x177F0408, + // Synth: Configure extra PLL filtering + (uint32_t)0x38000463, + // override_phy_ieee_15_4.xml + // Synth: Increase synth programming timeout + (uint32_t)0x05000243, + // Rx: Adjust Rx FIFO threshold to avoid overflow + (uint32_t)0x002082C3, + // override_frontend_id.xml + // Rx: Set RSSI offset to adjust reported RSSI by -2 dB + (uint32_t)0x000288A3, + // Rx: Configure LNA bias current trim offset + (uint32_t)0x000F8883, + // Rx: Adjust AGC DC filter + HW_REG_OVERRIDE(0x50DC,0x002B), + // Disable pointer check in RF Core to allow for using GPRAM for buffers + (uint32_t)0x00018063, + (uint32_t)0xFFFFFFFF, +}; + + +// CMD_RADIO_SETUP +// Radio Setup Command for Pre-Defined Schemes +rfc_CMD_RADIO_SETUP_t Ieee154_0_cmdRadioSetup = +{ + .commandNo = 0x0802, + .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, + .mode = 0x01, + .__dummy0 = 0x00, + .config.frontEndMode = 0x0, + .config.biasMode = 0x0, + .config.analogCfgMode = 0x0, + .config.bNoFsPowerUp = 0x0, + .txPower = 0x9330, + .pRegOverride = pOverrides, +}; + +// CMD_FS +// Frequency Synthesizer Programming Command +rfc_CMD_FS_t Ieee154_0_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 = 0x0965, + .fractFreq = 0x0000, + .synthConf.bTxMode = 0x1, + .synthConf.refFreq = 0x0, + .__dummy0 = 0x00, + .__dummy1 = 0x00, + .__dummy2 = 0x00, + .__dummy3 = 0x0000, +}; + +// CMD_IEEE_TX +// The command ID number 0x2C01 +rfc_CMD_IEEE_TX_t Ieee154_0_cmdIeeeTx = +{ + .commandNo = 0x2C01, + .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, + .txOpt.bIncludePhyHdr = 0x0, + .txOpt.bIncludeCrc = 0x0, + .txOpt.payloadLenMsb = 0x0, + .payloadLen = 0x1E, + .pPayload = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .timeStamp = 0x00000000, +}; + +// CMD_IEEE_RX +// The command ID number 0x2801 +rfc_CMD_IEEE_RX_t Ieee154_0_cmdIeeeRx = +{ + .commandNo = 0x2801, + .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, + .channel = 0x00, + .rxConfig.bAutoFlushCrc = 0x0, + .rxConfig.bAutoFlushIgn = 0x0, + .rxConfig.bIncludePhyHdr = 0x0, + .rxConfig.bIncludeCrc = 0x0, + .rxConfig.bAppendRssi = 0x1, + .rxConfig.bAppendCorrCrc = 0x1, + .rxConfig.bAppendSrcInd = 0x0, + .rxConfig.bAppendTimestamp = 0x0, + .pRxQ = 0, // INSERT APPLICABLE POINTER: (dataQueue_t*)&xxx + .pOutput = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx + .frameFiltOpt.frameFiltEn = 0x0, + .frameFiltOpt.frameFiltStop = 0x0, + .frameFiltOpt.autoAckEn = 0x0, + .frameFiltOpt.slottedAckEn = 0x0, + .frameFiltOpt.autoPendEn = 0x0, + .frameFiltOpt.defaultPend = 0x0, + .frameFiltOpt.bPendDataReqOnly = 0x0, + .frameFiltOpt.bPanCoord = 0x0, + .frameFiltOpt.maxFrameVersion = 0x3, + .frameFiltOpt.fcfReservedMask = 0x0, + .frameFiltOpt.modifyFtFilter = 0x0, + .frameFiltOpt.bStrictLenFilter = 0x0, + .frameTypes.bAcceptFt0Beacon = 0x1, + .frameTypes.bAcceptFt1Data = 0x1, + .frameTypes.bAcceptFt2Ack = 0x1, + .frameTypes.bAcceptFt3MacCmd = 0x1, + .frameTypes.bAcceptFt4Reserved = 0x1, + .frameTypes.bAcceptFt5Reserved = 0x1, + .frameTypes.bAcceptFt6Reserved = 0x1, + .frameTypes.bAcceptFt7Reserved = 0x1, + .ccaOpt.ccaEnEnergy = 0x0, + .ccaOpt.ccaEnCorr = 0x0, + .ccaOpt.ccaEnSync = 0x0, + .ccaOpt.ccaCorrOp = 0x1, + .ccaOpt.ccaSyncOp = 0x1, + .ccaOpt.ccaCorrThr = 0x0, + .ccaRssiThr = 0x64, + .__dummy0 = 0x00, + .numExtEntries = 0x00, + .numShortEntries = 0x00, + .pExtEntryList = 0, // INSERT APPLICABLE POINTER: (uint32_t*)&xxx + .pShortEntryList = 0, // INSERT APPLICABLE POINTER: (uint32_t*)&xxx + .localExtAddr = 0x0000000012345678, + .localShortAddr = 0xABBA, + .localPanID = 0x0000, + .__dummy1 = 0x000000, + .endTrigger.triggerType = 0x1, + .endTrigger.bEnaCmd = 0x0, + .endTrigger.triggerNo = 0x0, + .endTrigger.pastTrig = 0x0, + .endTime = 0x00000000, +}; diff --git a/source/smartrf_settings/cc26x0lp/15.4/smartrf_settings_15_4_0.h b/source/smartrf_settings/cc26x0lp/15.4/smartrf_settings_15_4_0.h new file mode 100644 index 0000000..f8e56e9 --- /dev/null +++ b/source/smartrf_settings/cc26x0lp/15.4/smartrf_settings_15_4_0.h @@ -0,0 +1,30 @@ +#ifndef _SMARTRF_SETTINGS_15_4_0_H_ +#define _SMARTRF_SETTINGS_15_4_0_H_ + +//********************************************************************************* +// Generated by SmartRF Studio version 2.7.0 (build #21) +// Tested for SimpleLink SDK version: No known SDK for this device +// Device: CC2650 Rev. 2.2 +// +//********************************************************************************* +#include "DeviceFamily.h" +#include DeviceFamily_constructPath(driverlib/rf_mailbox.h) +#include DeviceFamily_constructPath(driverlib/rf_common_cmd.h) +//#include DeviceFamily_constructPath(driverlib/rf_ieee_cmd.h) +#include "rf_ieee_cmd.h" +#include + + +// TI-RTOS RF Mode Object +extern RF_Mode Ieee154_0_mode; + + +// RF Core API commands +extern rfc_CMD_RADIO_SETUP_t Ieee154_0_cmdRadioSetup; +extern rfc_CMD_FS_t Ieee154_0_cmdFs; +extern rfc_CMD_IEEE_TX_t Ieee154_0_cmdIeeeTx; +extern rfc_CMD_IEEE_RX_t Ieee154_0_cmdIeeeRx; + + +#endif // _SMARTRF_SETTINGS_15_4_0_H_ + diff --git a/source/task_event.c b/source/task_event.c new file mode 100644 index 0000000..8074e4c --- /dev/null +++ b/source/task_event.c @@ -0,0 +1,69 @@ +/****************************************************************************** +* Filename: task_event.c +* +* Description: Source file for task event module. This module handles event +* signalling between the tasks in the system. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include +#include +#include "task_event.h" + +#include + +Event_Handle TaskEvent_Handle; + +static bool isInitialized = false; + +void TaskEvent_init(void) +{ + if(!isInitialized) + { + /* Default instance configuration params */ + TaskEvent_Handle = Event_create(NULL, NULL); + + if (TaskEvent_Handle == NULL) + { + System_abort("Event create failed"); + } + + isInitialized=true; + } +} + + + + + diff --git a/source/task_event.h b/source/task_event.h new file mode 100644 index 0000000..68f95a5 --- /dev/null +++ b/source/task_event.h @@ -0,0 +1,53 @@ +/****************************************************************************** +* Filename: task_event.h +* +* Description: Header file for task event module. This module handles event +* signalling between the tasks in the system. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef TASK_EVENT_H +#define TASK_EVENT_H + +#include + +#define EVENT_ID_USER_IF_TASK_END Event_Id_00 + +// Event handle to be shared between tasks +extern Event_Handle TaskEvent_Handle; + +//! \brief Initialize event system +extern void TaskEvent_init(void); + +#endif + diff --git a/source/test_command_handler.c b/source/test_command_handler.c new file mode 100644 index 0000000..ea1f6ba --- /dev/null +++ b/source/test_command_handler.c @@ -0,0 +1,202 @@ +/****************************************************************************** +* Filename: test_command_handler.c +* +* Description: Source file for test command handler. This module handles test +* commands. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include "test_command_handler.h" +#include "command_packet.h" +#include "command_handler.h" +#include "phy_manager.h" +#include "radio_if.h" + +#define TRANSMIT_SEQUENCE_MAX_PAYLOAD_LENGTH 2047 +#define PACKET_LENGHT_OFFSET_15_4_G -4 // Length offset setting for packet header length used for 15.4g settings +#define HEADER_LENGTH_15_4_G 2 // Header for 15.4G support +#define HEADER_LENGTH 0 // No header for Easylink support + +static uint8_t packet[TRANSMIT_SEQUENCE_MAX_PAYLOAD_LENGTH]; + +void TestCommandHandle_initializePacketPayload(uint8_t* packet, uint16_t packetLength); +void TestCommandHandle_initializePacketHeader(uint8_t* packet, uint16_t packetLength); +uint8_t TestCommandHandler_transmitOneSequence(uint8_t* commandData, uint16_t length); +void TestCommandHandler_transmitSeries(uint16_t packetLen, uint16_t numPackets); + +uint8_t TestCommandHandler_transmitSequence(uint8_t* commandData, uint16_t length) +{ + RadioIF_init(); + RadioIF_initTx(); + + if( (length < TEST_COMMAND_TRANSMIT_SEQUENCE_REPETITIONS_SIZE + TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_LENGTH_SIZE + TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_NUMBER_SIZE) + || (length % 2)) + { + RadioIF_disableTx(); + return COMMAND_INVALID; + } + + TestCommandHandle_initializePacketPayload(packet, TRANSMIT_SEQUENCE_MAX_PAYLOAD_LENGTH); + + uint8_t* pSequenceRepetitions = (commandData + TEST_COMMAND_TRANSMIT_SEQUENCE_REPETITIONS_OFFSET); + uint16_t sequenceRepetitions = *(uint16_t*)pSequenceRepetitions; + + for(int i = 0; i < sequenceRepetitions; i++) + { + uint8_t status = TestCommandHandler_transmitOneSequence(commandData, length); + if(status != COMMAND_OK) + { + RadioIF_disableTx(); + return status; + } + } + + RadioIF_disableTx(); + return COMMAND_OK; +} + + +//! \brief Transmit packet sequence +//! +//! \param[in] commandData +//! payload data for the TEST_CMD_TRANSMIT_SEQUENCE. +//! +//! \param[in] length +//! length of payload data +//! +//! \return status that indicates successful or failed handling of the command. +//! COMMAND_OK - The command was handled successfully. +//! COMMAND_INVALID - The command is invalid (e.g. payload does not match +//! the command type.) +uint8_t TestCommandHandler_transmitOneSequence(uint8_t* commandData, uint16_t length) +{ + uint16_t numSeries = (length - TEST_COMMAND_TRANSMIT_SEQUENCE_REPETITIONS_SIZE) / (TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_LENGTH_SIZE + TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_NUMBER_SIZE); + + for(int i = 0; i < numSeries; i++) + { + uint8_t* serieStart = (commandData + TEST_COMMAND_TRANSMIT_SEQUENCE_REPETITIONS_SIZE); + serieStart += i * (TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_LENGTH_SIZE + TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_NUMBER_SIZE); + + uint8_t* pPacketLen = serieStart + TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_LENGTH_OFFSET; + uint16_t packetLen = *(uint16_t*)pPacketLen; + + uint8_t* pNumPackets = serieStart + TEST_COMMAND_TRANSMIT_SEQUENCE_PACKET_NUMBER_OFFSET; + uint16_t numPackets = *(uint16_t*)pNumPackets; + + if(packetLen > MAX_PAYLOAD_LENGTH) + { + return COMMAND_INVALID; + } + + TestCommandHandler_transmitSeries(packetLen, numPackets); + } + + return COMMAND_OK; +} + + +//! \brief Transmit packet series +//! A series is a number of packets with same packet length +//! +//! \param[in] packetLen +//! Packet length +//! +//! \param[in] numPackets +//! Number of packets +//! +//! \return None +void TestCommandHandler_transmitSeries(uint16_t packetLen, uint16_t numPackets) +{ + TestCommandHandle_initializePacketHeader(packet, packetLen); + + for(int i = 0; i < numPackets; i++) + { + RadioIF_sendPacket(packet, packetLen); + } +} + + +//! \brief Initialize packet payload +//! The payload is initialized as a 1 byte counter +//! +//! \param[in] packet +//! Pointer to packet buffer +//! +//! \param[in] packetLength +//! Length of the packet +//! +//! \return None +void TestCommandHandle_initializePacketPayload(uint8_t* packet, uint16_t packetLength) +{ + int headerLen = HEADER_LENGTH; + if(PhyManager_getRfApi() == PROPRIETARY_15_4_G) + { + headerLen = HEADER_LENGTH_15_4_G; + } + + for(int i = headerLen; i < packetLength; i++) + { + packet[i] = i; + } +} + + +//! \brief Initialize packet header +//! Set packet header for 802.15.4g frame format and whitenMode=7 +//! +//! \param[in] packet +//! Pointer to packet buffer +//! +//! \param[in] packetLength +//! Length of the packet +//! +//! \return None +void TestCommandHandle_initializePacketHeader(uint8_t* packet, uint16_t packetLength) +{ + if(PhyManager_getRfApi() == PROPRIETARY_15_4_G) + { + // create the 15_4_g header (2 bytes used here) + packet[0] = (packetLength-HEADER_LENGTH_15_4_G - (PACKET_LENGHT_OFFSET_15_4_G)) & 0xFF; + packet[1] = ((packetLength-HEADER_LENGTH_15_4_G - (PACKET_LENGHT_OFFSET_15_4_G)) & 0xFF00) >> 8; + + // Set bit 11 in header to enable whitening for whitenMode=7 + packet[1] |= 1 << 3; + } +} + + + + + + diff --git a/source/test_command_handler.h b/source/test_command_handler.h new file mode 100644 index 0000000..ee99a41 --- /dev/null +++ b/source/test_command_handler.h @@ -0,0 +1,59 @@ +/****************************************************************************** +* Filename: test_command_handler.h +* +* Description: Header file for test command handler. This module handles test +* commands. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef TEST_COMMAND_HANDLER_H +#define TEST_COMMAND_HANDLER_H + +#include + +//! \brief execute a TEST_CMD_TRANSMIT_SEQUENCE command +//! +//! \param[in] commandData +//! payload data for the TEST_CMD_TRANSMIT_SEQUENCE. +//! +//! \param[in] length +//! length of payload data +//! +//! \return status that indicates successful or failed handling of the command. +//! COMMAND_OK - The command was handled successfully. +//! COMMAND_INVALID - The command is invalid (e.g. payload does not match +//! the command type.) +extern uint8_t TestCommandHandler_transmitSequence(uint8_t* commandData, uint16_t length); + +#endif + diff --git a/source/timestamp.c b/source/timestamp.c new file mode 100644 index 0000000..b6796a9 --- /dev/null +++ b/source/timestamp.c @@ -0,0 +1,221 @@ +/****************************************************************************** +* Filename: timestamp.c +* +* Description: Source file timestamp functionality. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include "timestamp.h" +#include "common.h" +#include "radio_if.h" +#include "radio_if_dataqueue.h" +#include "phy_manager.h" +#include + + +// Local functions +static uint64_t Timestamp_convertToUs(uint32_t radioTimestamp, uint8_t tickFrequency); +static void Timestamp_setPacketTimestamp(uint64_t timeStampUs, DataPacket_Obj* dataPacket); +static void Timestamp_ratOverflowCb(uint32_t ratCbTime); + +static uint64_t Timestamp_getFullRadioTimestamp(uint32_t radioTimeStamp); + +// Local variables +// These variables are not static to allow access from test application +uint32_t Timestamp_ratOverflowCounter = 0; // Keeps track of how many times the radio timer has wrapped around +uint32_t Timestamp_lastRatCbTime = 0; + +// defines +#define NUM_TIMESTAMP_BYTES 6 // 6 Timestamp bytes in data packet +#define NUM_BITS_PER_BYTE 8 + +void Timestamp_init(void) +{ + // Reset local variables + Timestamp_ratOverflowCounter = 0; + + // Register Radio timer callback function + RadioIF_registerRatOverflowCb(&Timestamp_ratOverflowCb); +} + + +uint16_t Timestamp_getRatOverflowCounter(void) +{ + return Timestamp_ratOverflowCounter; +} + + +uint16_t Timestamp_extractTimestamp(uint8_t* packetBuffer, uint16_t length, DataPacket_Obj* dataPacket) +{ + uint32_t radioTimestamp = 0; + + if(PhyManager_getRfApi() == IEEE_802_15_4) + { + int timestampIndex = length - RADIO_IF_TIMESTAMP_LEN; + radioTimestamp = Common_get32BitValueLE(&packetBuffer[timestampIndex]); + } + else + { + int timestampIndex = (length - RADIO_IF_STATUS_LEN - RADIO_IF_TIMESTAMP_LEN); + uint16_t statusIndex = length - RADIO_IF_STATUS_LEN; + radioTimestamp = Common_get32BitValueLE(&packetBuffer[timestampIndex]); + + // Remove timestamp bytes from data packet payload + uint8_t status = packetBuffer[statusIndex]; + packetBuffer[timestampIndex] = status; // Overwrites first timestamp byte + } + + // Set timestamp of data packet + uint64_t timeStampUs = Timestamp_convertToUs(radioTimestamp, RADIO_TIMER_FREQUENCY_MHZ); + Timestamp_setPacketTimestamp(timeStampUs, dataPacket); + + // Return updated payload length after timestamp bytes are removed + return length -= RADIO_IF_TIMESTAMP_LEN; +} + + +//! \brief Convert the 4 bytes timestamp value set by RF Core to 8 bytes timestamp +//! in microseconds. +//! +//! \param[in] radioTimestamp +//! Timestamp set by RF Core +//! +//! \param[in] tickFrequency +//! Tick frequency of Radio timer +//! +//! \return Timestamp in microseconds +uint64_t Timestamp_convertToUs(uint32_t radioTimestamp, uint8_t tickFrequency) +{ + // Add contribution from the overflow counter + uint64_t fullRadioTimeStamp = Timestamp_getFullRadioTimestamp(radioTimestamp); + + if(tickFrequency != 0) + { + return fullRadioTimeStamp/tickFrequency; + } + + return 0; +} + + +//! \brief Set 6 bytes timestamp field in data packet +//! +//! \param[in] timeStampUs +//! Timestamp in microseconds. +//! +//! \param[out] dataPacket +//! Pointer to data packet +//! +//! \return None +void Timestamp_setPacketTimestamp(uint64_t timeStampUs, DataPacket_Obj* dataPacket) +{ + // Set the 6 timestamp bytes in data packet in little endian order + for(int i = 0; i < NUM_TIMESTAMP_BYTES; i++) + { + uint8_t mumBitShifts = i * NUM_BITS_PER_BYTE; + dataPacket->timeStamp[i] = (timeStampUs & ((uint64_t)0xFF << mumBitShifts)) >> mumBitShifts; + } +} + + +//! \brief Get full radio timestamp including contributions from overflow counter +//! +//! \param[in] radioTimeStamp +//! 4 bytes timestamp as set by RF Core. +//! +//! \return 8 bytes extended timestamp with same resolution as radio timestamp +uint64_t Timestamp_getFullRadioTimestamp(uint32_t radioTimeStamp) +{ + uint64_t fullRadioTimeStamp = radioTimeStamp; + + // The Radio timer overflow callback may happen just before or just after + // a packet is received. This must be accounted for when wrap counter + // contribution is added to the packet's timestamp. + + // Temporarily disable SWI when the local variables are accessed. These + // variables are both accessed by the RAT Overflow callback function + // (which runs in SWI context) and this function. + uint32_t key = Swi_disable(); + + if(Timestamp_ratOverflowCounter > 0) + { + uint32_t tempOverflowCounter = Timestamp_ratOverflowCounter; + + if((radioTimeStamp > RADIO_TIMER_THIRD_QUARTER_CYCLE_VALUE) && (Timestamp_lastRatCbTime < RADIO_TIMER_FIRST_QUARTER_CYCLE_VALUE)) + { + // The packet was received just before timer wrap, but wrap counter has already been increased. + // We need to subtract 1 from the temporary wrap counter value for this packet. + tempOverflowCounter--; + } + else if((radioTimeStamp < RADIO_TIMER_FIRST_QUARTER_CYCLE_VALUE) && (Timestamp_lastRatCbTime > RADIO_TIMER_THIRD_QUARTER_CYCLE_VALUE)) + { + // The packet was received just after timer wrap, but wrap counter has not yet been increased. + // We need to add 1 to the temporary wrap counter value for this packet. + tempOverflowCounter++; + } + + fullRadioTimeStamp += ((uint64_t)tempOverflowCounter << 32); + } + + // Re-enable SWI before return from function + Swi_restore(key); + + return fullRadioTimeStamp; +} + + +//! \brief Callback function for Radio Timer (RAT) interrupt +//! +//! This function updates the file scope variables +//! Timestamp_ratOverflowCounter and Timestamp_lastRatCbTime. +//! +//! \param[in] ratCbTime +//! The time (value of RAT timer) when the callback happened +//! +//! \return None +void Timestamp_ratOverflowCb(uint32_t ratCbTime) +{ + // If the new RAT Callback time is less than last RAT callback time + // the RAT timer has wrapped around since last callback. + if(ratCbTime < Timestamp_lastRatCbTime) + { + Timestamp_ratOverflowCounter++; + } + + Timestamp_lastRatCbTime = ratCbTime; +} + + + + diff --git a/source/timestamp.h b/source/timestamp.h new file mode 100644 index 0000000..5fbd11c --- /dev/null +++ b/source/timestamp.h @@ -0,0 +1,76 @@ +/****************************************************************************** +* Filename: timestamp.h +* +* Description: Header file for timestamp functionality +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef TIMESTAMP_H +#define TIMESTAMP_H + +#include "data_packet.h" + +//! \brief Initialize timestamp module +//! This function resets the timestamp timer overflow counter. +//! +//! \return None +extern void Timestamp_init(void); + + +//! \brief Get current value of radio timer overflow counter +//! +//! \return Radio timer overflow counter value +extern uint16_t Timestamp_getRatOverflowCounter(void); + + +//! \brief Extract timestamp from radio packet +//! +//! The timestamp bytes of radio packet payload is extracted and removed +//! from the radio packet payload. The length and data packet timestamp +//! field are updated. +//! +//! \param[in] packetBuffer +//! Pointer to buffer for the data packet as read from radio +//! +//! \param[in] length +//! Length of the payload field of the data packet. +//! +//! \param[in/out] Pointer to data packet to update +//! +//! \return Updated payload length after timestamp bytes are removed +extern uint16_t Timestamp_extractTimestamp(uint8_t* packetBuffer, uint16_t length, DataPacket_Obj* dataPacket); + + + +#endif + diff --git a/source/user_if_task.c b/source/user_if_task.c new file mode 100644 index 0000000..6c22e73 --- /dev/null +++ b/source/user_if_task.c @@ -0,0 +1,109 @@ +/****************************************************************************** +* Filename: user_if_task.c +* +* Description: User Interface task function implementation. This task handles +* periodic updates of the user interface. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "Board.h" + +#include "host_if.h" +#include "task_event.h" +#include "version.h" + +// Period of user updates (1 second) +#define USER_IF_TIMEOUT_PERIOD 100000 // ticks of 10 us + +// Pin table +static PIN_Config UserIfTask_pinTable[] = +{ + Board_PIN_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, + PIN_TERMINATE +}; +static PIN_Handle UserIfTask_ledPin_Handle; +static PIN_State UserIfTask_ledPinState; + +#define USER_MESSAGE "TI Packet Sniffer ver." +static const char UserIfTask_message[] = USER_MESSAGE VERSION_STRING "\n"; + + +//! \brief User Interface task function +//! The arguments are not used. +//! +//! This task will periodically output a message to the host interface +//! and blink an LED to inform the user that the sniffer is running. The +//! period of the user updates is set to once per second. This task will +//! run until the event EVENT_ID_USER_IF_TASK_END is received. This task +// will exit when receiving EVENT_ID_USER_IF_TASK_END. +//! +void userIfTask(UArg a0, UArg a1) +{ + // Initialize the modules to be used by this task + HostIF_init(); + TaskEvent_init(); + + // Open LED pins + UserIfTask_ledPin_Handle = PIN_open(&UserIfTask_ledPinState, UserIfTask_pinTable); + if(!UserIfTask_ledPin_Handle) + { + System_abort("Error initializing board LED pins\n"); + } + + while(1) + { + // Let task sleep for timeout period + Task_sleep(USER_IF_TIMEOUT_PERIOD); + + // Update user interface + HostIF_writeBuffer((uint8_t*)UserIfTask_message, sizeof(UserIfTask_message)); + PIN_setOutputValue(UserIfTask_ledPin_Handle, Board_PIN_LED1,!PIN_getOutputValue(Board_PIN_LED1)); + + // Wait for end task event + UInt events = Event_pend(TaskEvent_Handle, EVENT_ID_USER_IF_TASK_END, Event_Id_NONE, BIOS_NO_WAIT); + if(events & EVENT_ID_USER_IF_TASK_END) + { + // End this task + PIN_close(UserIfTask_ledPin_Handle); + Task_exit(); + } + } +} diff --git a/source/user_if_task.h b/source/user_if_task.h new file mode 100644 index 0000000..f69a3ac --- /dev/null +++ b/source/user_if_task.h @@ -0,0 +1,50 @@ +/****************************************************************************** +* Filename: user_if_task.h +* +* Description: Header file for the User Interface Task. This task handles +* periodic updates of the user interface. +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef USER_IF_TASK_H +#define USER_IF_TASK_H + + +#include + +//! \brief User Interface task function +//! The arguments are not used. +extern void userIfTask(UArg a0, UArg a1); + +#endif + diff --git a/source/version.h b/source/version.h new file mode 100644 index 0000000..795dbf1 --- /dev/null +++ b/source/version.h @@ -0,0 +1,47 @@ +/****************************************************************************** +* Filename: version.h +* +* Description: version number +* +* Copyright (C) 2017-2018 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. +* +******************************************************************************/ +#ifndef VERSION_H +#define VERSION_H + + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 5 +#define VERSION_STRING "1.5.0" + +#endif + diff --git a/src/.exclude b/src/.exclude new file mode 100644 index 0000000..8c86331 --- /dev/null +++ b/src/.exclude @@ -0,0 +1 @@ +This file exists to prevent Eclipse/CDT from adding the C sources contained in this directory (or below) to any enclosing project. diff --git a/src/makefile.libs b/src/makefile.libs new file mode 100644 index 0000000..645f7d3 --- /dev/null +++ b/src/makefile.libs @@ -0,0 +1,62 @@ +# +# This file was generated based on the configuration script: +# C:\Users\gongt\workspace_v8\sniffer_CC2650STK\release.cfg +# +# This makefile may be included in other makefiles that need to build +# the libraries containing the compiled source files generated as +# part of the configuration step. + +# +# ======== GEN_SRC_DIR ========= +# The path to the sources generated during configuration +# +# This path must be either absolute or relative to the build directory. +# +# The absolute path to the generated source directory (at the time the +# sources were generated) is: +# C:\Users\gongt\workspace_v8\sniffer_CC2650STK\src +# +GEN_SRC_DIR ?= ../src + +ifeq (,$(wildcard $(GEN_SRC_DIR))) +$(error "ERROR: GEN_SRC_DIR must be set to the directory containing the generated sources") +endif + +# +# ======== .force ======== +# The .force goal is used to force the build of any goal that names it as +# a prerequisite +# +.PHONY: .force + +# +# ======== library macros ======== +# +sysbios_SRC = $(GEN_SRC_DIR)/sysbios +sysbios_LIB = $(GEN_SRC_DIR)/sysbios/rom_sysbios.aem3 + +# +# ======== dependencies ======== +# +all: $(sysbios_LIB) +clean: .sysbios_clean + + +# ======== convenient build goals ======== +.PHONY: sysbios +sysbios: $(GEN_SRC_DIR)/sysbios/rom_sysbios.aem3 + +# CDT managed make executables depend on $(OBJS) +OBJS += $(sysbios_LIB) + +# +# ======== rules ======== +# +$(sysbios_LIB): .force + @echo making $@ ... + @$(MAKE) -C $(sysbios_SRC) + +.sysbios_clean: + @echo cleaning $(sysbios_SRC) ... + -@$(MAKE) --no-print-directory -C $(sysbios_SRC) clean + diff --git a/src/sysbios/m3_Hwi_asm.obj b/src/sysbios/m3_Hwi_asm.obj new file mode 100644 index 0000000000000000000000000000000000000000..7be357d2b27a681ddcf5ba3c8e34ed4890b13dd6 GIT binary patch literal 15108 zcmd5@Yiu0V6}~g;IL?d2u>k{Vx{ch{6!0s-cIX2q>(?fBVr&y3HDNrvJGO`H?rdhp zjvWz5Qkohlw5SLW0;Dvpfc~Ic8cL%ElG1>viYlclR0^bts4A656+`+6h?br+kKLKG zcXqc@)#TdVJKsIuJ?Gr_y*t-;cK7!Pg23D$um+Y#jMcBPB=gp=Fg!G{YuPnyof9%X$Z^MS_3E^GFmW^&ZU-90`xvHh16T*=_=c;Ej zy%k5iIS+gg7HW^~J-1-?zMc)Eo9FgqUX`!xo0aP})=gB6c77-O+xveRqYl+FYX*9)DptVVB+N9M7) z8=yv!raHD7X?1K&(WOYFwe9-m;gOA74L!cqQOri7VfbA)G!R~A7)mOZRP^v*Bs|s9 z(b&?sIvmk5GrBrHVTK#xSBG2Hu5G)nwYjA|Jfcj?MmU-_Onow?q)mK|YkEf0Wm8Eo z;RhGAISl>SU|}p6q}ts~dQ7oDAK-lZRa)#9xL#TR-j? zE-y9e>FT&PmmL?flG*ctOqqWF?192bSp08Lipn92m4WhxiWA6OtR5$c31gjL{#KX?-=#1B5b#484T z9l`wsD+KQ&_;G>{6MTZ;j|r~yI*9%eTto0@3BHElE`s9(?{5aMO$2Wsc%0x_f}bY%HG=;_@FM%bkh9-Fa5up*g6|~w34)Ii{5HWA`7sTn@Pkjk zK=1~F#|gfR-~$A|Nbo6w|3z@!JWmG$e^wK`iC~rBy9ho&@Jj@rBKSPPHI;sU8VTM= zaEjo23H}km#|i#3!QyisgR&4FnGoY!dt^!7mW}Ho*&V z3+D#tPuCK>iC~T32M9h)@Sg~tBe=HKH4ZV@?;v<9!TSk*mf$x4?B~ch(ku3^i4@>+r~CHOM7%EMm;d);<|`#pFW z$Y%h%Tk_>lzfS_b!trmp7-GNi$wTZzl7GeJ-uxo#2J8mZpCR}Og0b(4zq9+S_*=WL zim%&!RD8qko8r@UpA>)Z+Je&_gaDGVDMOWjyZb44@)n^5#&MK>%=ooY~_2|$@fbq zANXzMgLnh-Al~G}-*RFr-!I@jj^nq5VQ^a*>{$2(2fOtEe3$3SL0wSrz15gb#3g+) zZK^3n8kxx`h7{2ic(*X9CDr&0D->^@G&7SXN8GLJnl5n!U8+28NZnKL`l4T%Qj*>; z=GR9z4@)BxU^0Q$3q(Vv(XYj~b7lQ%jHat;Q_;uZg^VFZv6ut0v0Y7%Z_b#imS&PP zB}+-wFeMY7vB)~asU7e!($?P5vbMcrjs3i~t@Zl$Hecx@nyyHjW4CoFM%*W`%RK-u znUab$Y(m}n#L-@?O9VdZyEcB=Kc~vM#3#zO0zeVR&;C zWY5k=EJpja@o{jKa_%L_T}kQZ_qA}{2&ge{K9$z%*(Rq`5yFan*+=?L^N#|U&Y$0*|K@Qe}c zCJF;RL}8$FC=B!~pObXt0VSpBGg4rSNBa~x!yl0-8Bo)_vbZ8nNn8=99Il8{3RlFf zRum4KiQq!G1Wu=QMFF0o+5k^c6yRw-7pd(bWgKSKV)Z>_%2Gs2rQmhaAoOR&KMA6A zNHH>6+E6%Rm{@glG9yJ319HYU3%ZiPc7_gODkkvT54Gm5!fEasHpaDnD&xy6ok-)70GgxDE1Xeb~lv9~VQiVmAtHGu4gn0$Ib&KbO zt0HLW%DE9#3VXYu>4|lT1T2ldcCq&&xH1kYV?-9k`w(&(u3eN^2yz}kIi_cLMa|E^ zP6Iis^?%Lg3^H3cw69d!4`5**b-G!gyAG`!UxTO|M|19#v~RaczZ`>IT;bMmSS5QO zo1RnOwuY0bM^|?A?cxYvLMhm5*$THP{vDX(m73npQGD^&-B+D`sH+dfhh ztW|dM`Xn5Pd>zf%=-_&9&m5foR>0w`?*$yE0N#=AaA46j{R6??O>r#S0*LYM849NLKH$SIyH1UbN+Tm*Cn6}TmU25FyPAVy1OIZu|CM`i`7i&4k+h|)< z18bHe9)!_ta>cOB*oW&Om=-;~(>huQbzMschwFl>p$dGQ!XZ|u$Egsi2fQCvcli$Z z-ov_ex-QJ>rQmr!x88fydpX7DG#@iymMY{peJ04FrKhA{$O@ipsZnbhaknc0hNERn z_+GscIXyyhVt_d5yPt&#!!-*ll zDWt%_iEodl$FyRkbvk?%CB?0;xQg+LH#(Xt^tteoX(Te_HBv~}v?UrT&ghP0W zYuZ=#LQTGc!S`7$@NL%GwX0h@imNXyXQArCQD4X@r#6D-qC#z6ChYw^jb6xgHV5^s z5?qq@8Y7AEOa>E+7vEFgSOrDN1?4Hq z))jM7PZ!siug;>X+(wF&bkSv&B<9OflA6#wU9cqE85a_)4<;Obx3G(}FaA$@VI%yr zM5AoNu_iWYVlOpjQl=bZjk=O7V}N*DtIRCsE^gDanu8(@}Rcnbh8B^t+)@OA_WXX;krj0xu{=vs=kv{*H!u_gn8N8-4d7A@xuMxW% z@NVSTrMzc=_ti3Z?*Xri!+Q(Tpvz@kQ4`$f8gPLpejlFQv4Y9B11|%2rM?8u=o1Ef zulqOngR0Gi_u-#b8UW${uDJQ}%H9{D6L@*G@Dd->k>`#z#1inl{bdF4y!n7)<;xI# z4IX_^bSt1f%4_!U+^cLnUWbRb*rRU`vD*netOqQIdo>jGQQjc%ZgF_-T*u=*1-uT2 z2Sd*aJl+`(59Jsz{x0#a4kka>KX=^mcnu!ja!VUuUx$ZR{wh?80v@i~>=fyt=n1mX3xGWI44 IJOSZ<0Icvq+W-In literal 0 HcmV?d00001 diff --git a/src/sysbios/m3_Hwi_asm_switch.obj b/src/sysbios/m3_Hwi_asm_switch.obj new file mode 100644 index 0000000000000000000000000000000000000000..7e267b225f3c8d96bd11b4264661b32e91f2ebc7 GIT binary patch literal 9260 zcmd^FYit}>6}~gOcGs?*_|-g`M>l3cu2j}uJ2($YW7jl^O-x>kL?xr??0W1>v)sfm$fmn&7-97W&`<-*oopa|t#(w6pGbc4oV?s36$Et|2?ESo^=`c&;qK}vwmyzxCwF z@4uhauWhufme%zh9Z#HHUwZ2IW@7w#vwZ3ccjAAyPcEZwX(O?Ec3r2ImzVrD5lt5Y zjq&i}+Su`a&&J2`O0JR5XNDFQpazGb293dtF)&~Z4EL9v()4`ZbNh2PXv49ONe zpK~q8otiBbXT7PbCFimWZ}RH2CqEVVXW8*2KBE^V zlcTO{&E*P~lRP(;yplPZ&ZLKuW2N$mfx8d%NeiOIHvyC7LhM#0(0po-+>}NMy8h4$G z9>xSuF?Hlam}o3kaM%tQp{W~>WH>oJ|r8Y!x`X{gQmszlQCm!jgpmS#M5|l7EL0X z?=Ti7V{V8~TeFtO$NM?Mcnm#YjQSChlzJxqBN8Vg#%?uAGEO6&#!JSxwo8Uxv^_ps zxgs0Xk*%{)YvZTmV`N23$)kQ==vj%eZUP#++m2tcZ$m^tFY)?k@vCd>d~N;zY!HboUylAdo|LZHFBiyB2s==5wEzlKWOBD@38h@1pHbK^WnxC zsj)S!ePU(hJiaRvD-#nFXP_WAoAnqya-A%e=xE-T_-NwUk$ce(ty80dPOVF;6zXYB z>y9O;Y3+-e!Xp(2v~Wjr zP~EOl7}XGHzZ%7Zw80_=5990ifworR4(e?N$i5l??FP2BuU)>#!Dv5zvg1tagKnxE za9N_eRd0(g>9K2i>vetdIbHvquDz~n2S76|GiNi6R=DQYdnQ}-c5RQgSL;Otn?x>) z>+j#a4-8SJ>pB~$!7)|?-_;_!;{1wB$dg-=pA!f?E{as^EJS+{RXF{I@H3 zhk|!1xI@963hrVw&V|JEZUy%!_&x>iQt)mC?_uAo@xNEW_bYgxg7+)<0Q-53zk>>X zK*31`A5w5Hdrk7!izoLT1^*3L6m{uw)&ouzH?tEpxQUMq&;kw~M97QyVrcj}5$Kjc z<9y8Pu~5-usbfZ6CcP)X4+MBGV_y#ZtOfeB2!#Az2=rG1&HW?;JG@8ac4(K|L7yca z^u<8aR7IHE9SZFB1$Ky&+d=d8pm}@Ht7HdFFB3>}``*AFvpsJY{ad4Wp(lw4eLm1M zwh42)j|6s@khmT02e*Uf?LzZ*p?SN|&yzhgx5w;7iy7Syj(Y+u`h|&#*NZrJ1z5Dd zJHV1Z;fHJ3a}7JL!AJA;B#2Fm^-?KBSg>FhY!=SqdNS+GrbBQO4l^haS8 z`y;f9O)(a$MV0Z${aSg?E)~-=1#_0d^c)_hdnvf8Hxq@F)4WArn#V{E`>OIQjt7`Z zH}T{H1C~B6M-?9JkK}`c;617E&H>8@2*LZd!YcxcSaBI_uo-(p;d#KPi4chB#|Oaj zT>UEWF1aA!tGqve^IQbp6Zm7;*CO!N5zq?}cq`~E{kcRHHNI}FZ7+uK5-5|4e}XkH zsX7Bzd2fLCas-}>HS@<2cn7eS{vrbJP4Ipcfj5sq^_vi0kJ8_PcE&m*@J7Kq7J>J4 zMBXdlO+@hbFYrDYfp-*t%L?Ps!A#Dt3tlqBpUV42MBXbAd2dJLZPejqlzw#Y#Q8O( zA1W^k-lGwCFGTeBNAN}>_}h)iFD$-M^aE2sS-AK-c#{H1n(uoh{4#h0Fr-6&Iw(gG zdmR4oC|2Z8*xTY!XDNKeJV==4CVVRL7w}{|svzEX6@UMb{2?`5!g&9bc(_G%c^^nT zik~FRUmSaKDyM#k=QE7gCh@w+8H9Lr_LKY910J#byV)hcfg}^>W5hW!+0-AJRF0$gzxF3|syi+*y Iw}$Zk4K&5QfB*mh literal 0 HcmV?d00001 diff --git a/src/sysbios/m3_IntrinsicsSupport_asm.obj b/src/sysbios/m3_IntrinsicsSupport_asm.obj new file mode 100644 index 0000000000000000000000000000000000000000..022e8e347a4bc9e03f936a4b02eba9b3a7530c14 GIT binary patch literal 3064 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 aujOa<7oNDNXFszF?>7wI``i!`hJOIx(AGEr literal 0 HcmV?d00001 diff --git a/src/sysbios/makefile b/src/sysbios/makefile new file mode 100644 index 0000000..55696d7 --- /dev/null +++ b/src/sysbios/makefile @@ -0,0 +1,107 @@ + +XOPTS = -I"C:/ti/xdctools_3_32_00_06_core/packages/" -Dxdc_target_types__=C:/ti/tirtos_cc13xx_cc26xx_2_21_00_06/products/bios_6_46_01_37/packages/ti/targets/arm/elf/std.h -Dxdc_target_name__=M3 + +vpath % C:/ti/tirtos_cc13xx_cc26xx_2_21_00_06/products/bios_6_46_01_37/packages/ti/sysbios/ +vpath %.c C:/ti/xdctools_3_32_00_06_core/packages/ + +CCOPTS = --endian=little -mv7M3 --abi=eabi -q -ms --opt_for_speed=0 --program_level_compile -o3 -g --optimize_with_debug -Dti_sysbios_knl_Task_minimizeLatency__D=FALSE -Dti_sysbios_family_arm_cc26xx_Boot_driverlibVersion=2 -Dti_sysbios_knl_Clock_stopCheckNext__D=TRUE -Dti_sysbios_family_arm_m3_Hwi_enableException__D=TRUE -Dti_sysbios_family_arm_m3_Hwi_disablePriority__D=32U -Dti_sysbios_family_arm_m3_Hwi_numSparseInterrupts__D=0U + +XDC_ROOT = C:/ti/xdctools_3_32_00_06_core/packages/ + +BIOS_ROOT = C:/ti/tirtos_cc13xx_cc26xx_2_21_00_06/products/bios_6_46_01_37/packages/ti/sysbios/ + +BIOS_DEFS = -Dti_sysbios_BIOS_swiEnabled__D=TRUE -Dti_sysbios_BIOS_taskEnabled__D=TRUE -Dti_sysbios_BIOS_clockEnabled__D=TRUE -Dti_sysbios_BIOS_runtimeCreatesEnabled__D=TRUE -Dti_sysbios_knl_Task_moduleStateCheckFlag__D=FALSE -Dti_sysbios_knl_Task_objectCheckFlag__D=FALSE -Dti_sysbios_hal_Hwi_DISABLE_ALL_HOOKS -Dti_sysbios_knl_Swi_DISABLE_ALL_HOOKS -Dti_sysbios_BIOS_smpEnabled__D=FALSE -Dti_sysbios_Build_useHwiMacros -Dti_sysbios_knl_Swi_numPriorities__D=6 -Dti_sysbios_knl_Task_deleteTerminatedTasks__D=FALSE -Dti_sysbios_knl_Task_numPriorities__D=6 -Dti_sysbios_knl_Task_checkStackFlag__D=FALSE -Dti_sysbios_knl_Task_initStackFlag__D=TRUE -Dti_sysbios_knl_Task_DISABLE_ALL_HOOKS -Dti_sysbios_knl_Clock_TICK_SOURCE=ti_sysbios_knl_Clock_TickSource_TIMER -Dti_sysbios_knl_Clock_TICK_MODE=ti_sysbios_knl_Clock_TickMode_DYNAMIC -Dti_sysbios_hal_Core_delegate_getId=ti_sysbios_hal_CoreNull_getId__E -Dti_sysbios_hal_Core_delegate_interruptCore=ti_sysbios_hal_CoreNull_interruptCore__E -Dti_sysbios_hal_Core_delegate_lock=ti_sysbios_hal_CoreNull_lock__E -Dti_sysbios_hal_Core_delegate_unlock=ti_sysbios_hal_CoreNull_unlock__E -Dti_sysbios_hal_Core_numCores__D=1 -Dti_sysbios_hal_CoreNull_numCores__D=1 -Dti_sysbios_utils_Load_taskEnabled__D=TRUE -Dti_sysbios_utils_Load_swiEnabled__D=FALSE -Dti_sysbios_utils_Load_hwiEnabled__D=FALSE -Dti_sysbios_family_arm_m3_Hwi_dispatcherSwiSupport__D=TRUE -Dti_sysbios_family_arm_m3_Hwi_dispatcherTaskSupport__D=TRUE -Dti_sysbios_family_arm_m3_Hwi_dispatcherAutoNestingSupport__D=TRUE -Dti_sysbios_family_arm_m3_Hwi_dispatcherIrpTrackingSupport__D=TRUE -Dti_sysbios_knl_Semaphore_supportsEvents__D=FALSE -Dti_sysbios_knl_Semaphore_supportsPriority__D=FALSE -Dxdc_runtime_Assert_DISABLE_ALL -Dxdc_runtime_Log_DISABLE_ALL + +BIOS_INC = -I"C:/ti/tirtos_cc13xx_cc26xx_2_21_00_06/products/bios_6_46_01_37/packages/" + +TARGET_INC = -I"C:/ti/tirtos_cc13xx_cc26xx_2_21_00_06/products/bios_6_46_01_37/packages/" + +INCS = $(BIOS_INC) $(TARGET_INC) --include_path="C:/Users/gongt/workspace_v8/sniffer_CC2650STK/source/smartrf_settings/cc26x0lp" --include_path="C:/Users/gongt/workspace_v8/sniffer_CC2650STK/source/phy" --include_path="C:/Users/gongt/workspace_v8/sniffer_CC2650STK/source" --include_path="C:/Users/gongt/workspace_v8/sniffer_CC2650STK" --include_path="C:/Users/gongt/workspace_v8/sniffer_CC2650STK" --include_path="C:/ti/tirtos_cc13xx_cc26xx_2_21_00_06/products/cc26xxware_2_24_03_17272" --include_path="C:/ti/ccsv8/tools/compiler/ti-cgt-arm_18.1.3.LTS/include" -IC:/ti/tirtos_cc13xx_cc26xx_2_21_00_06/products/bios_6_46_01_37/packages/ + +CC = C:/ti/ccsv8/tools/compiler/ti-cgt-arm_18.1.3.LTS/bin/armcl -c $(CCOPTS) -I C:/ti/ccsv8/tools/compiler/ti-cgt-arm_18.1.3.LTS/include +ASM = C:/ti/ccsv8/tools/compiler/ti-cgt-arm_18.1.3.LTS/bin/armcl -c $(CCOPTS) -I C:/ti/ccsv8/tools/compiler/ti-cgt-arm_18.1.3.LTS/include +AR = C:/ti/ccsv8/tools/compiler/ti-cgt-arm_18.1.3.LTS/bin/armar rq + +DEL = C:/ti/xdctools_3_32_00_06_core/packages/../bin/rm -f +CP = C:/ti/xdctools_3_32_00_06_core/packages/../bin/cp -f + +define RM + $(if $(wildcard $1),$(DEL) $1,:) +endef + +define ASSEMBLE + @echo asmem3 $< ... + @$(ASM) $(BIOS_DEFS) $(XOPTS) $(INCS) $< +endef + +all: rom_sysbios.aem3 + +m3_Hwi_asm.obj: family/arm/m3/Hwi_asm.sv7M makefile + @-$(call RM, $@) + $(ASSEMBLE) --output_file=m3_Hwi_asm.obj + +m3_Hwi_asm_switch.obj: family/arm/m3/Hwi_asm_switch.sv7M makefile + @-$(call RM, $@) + $(ASSEMBLE) --output_file=m3_Hwi_asm_switch.obj + +m3_IntrinsicsSupport_asm.obj: family/arm/m3/IntrinsicsSupport_asm.sv7M makefile + @-$(call RM, $@) + $(ASSEMBLE) --output_file=m3_IntrinsicsSupport_asm.obj + +m3_TaskSupport_asm.obj: family/arm/m3/TaskSupport_asm.sv7M makefile + @-$(call RM, $@) + $(ASSEMBLE) --output_file=m3_TaskSupport_asm.obj + + +rom_sysbios.obj: BIOS.c knl/Clock.c knl/Idle.c knl/Intrinsics.c knl/Event.c knl/Mailbox.c knl/Queue.c knl/Semaphore.c knl/Swi.c knl/Swi_andn.c knl/Task.c family/arm/m3/Hwi.c family/arm/m3/TaskSupport.c family/arm/cc26xx/Boot.c family/arm/cc26xx/Timer.c family/arm/cc26xx/TimestampProvider.c hal/Hwi.c hal/Hwi_stack.c hal/Hwi_startup.c heaps/HeapMem.c gates/GateHwi.c gates/GateMutex.c xdc/runtime/xdc_noinit.c xdc/runtime/Assert.c xdc/runtime/Core-mem.c xdc/runtime/Core-smem.c xdc/runtime/Core-label.c xdc/runtime/Core-params.c xdc/runtime/Diags.c xdc/runtime/Error.c xdc/runtime/Gate.c xdc/runtime/Log.c xdc/runtime/Memory.c xdc/runtime/Registry.c xdc/runtime/Startup.c xdc/runtime/System.c xdc/runtime/SysCallback.c xdc/runtime/Text.c xdc/runtime/Timestamp.c makefile + @-$(call RM, $@) + @echo clem3 $< ... + @$(CC) $(BIOS_DEFS) $(XOPTS) $(INCS) --output_file=rom_sysbios.obj \ + $(BIOS_ROOT)BIOS.c \ + $(BIOS_ROOT)knl/Clock.c \ + $(BIOS_ROOT)knl/Idle.c \ + $(BIOS_ROOT)knl/Intrinsics.c \ + $(BIOS_ROOT)knl/Event.c \ + $(BIOS_ROOT)knl/Mailbox.c \ + $(BIOS_ROOT)knl/Queue.c \ + $(BIOS_ROOT)knl/Semaphore.c \ + $(BIOS_ROOT)knl/Swi.c \ + $(BIOS_ROOT)knl/Swi_andn.c \ + $(BIOS_ROOT)knl/Task.c \ + $(BIOS_ROOT)family/arm/m3/Hwi.c \ + $(BIOS_ROOT)family/arm/m3/TaskSupport.c \ + $(BIOS_ROOT)family/arm/cc26xx/Boot.c \ + $(BIOS_ROOT)family/arm/cc26xx/Timer.c \ + $(BIOS_ROOT)family/arm/cc26xx/TimestampProvider.c \ + $(BIOS_ROOT)hal/Hwi.c \ + $(BIOS_ROOT)hal/Hwi_stack.c \ + $(BIOS_ROOT)hal/Hwi_startup.c \ + $(BIOS_ROOT)heaps/HeapMem.c \ + $(BIOS_ROOT)gates/GateHwi.c \ + $(BIOS_ROOT)gates/GateMutex.c \ + $(XDC_ROOT)xdc/runtime/xdc_noinit.c \ + $(XDC_ROOT)xdc/runtime/Assert.c \ + $(XDC_ROOT)xdc/runtime/Core-mem.c \ + $(XDC_ROOT)xdc/runtime/Core-smem.c \ + $(XDC_ROOT)xdc/runtime/Core-label.c \ + $(XDC_ROOT)xdc/runtime/Core-params.c \ + $(XDC_ROOT)xdc/runtime/Diags.c \ + $(XDC_ROOT)xdc/runtime/Error.c \ + $(XDC_ROOT)xdc/runtime/Gate.c \ + $(XDC_ROOT)xdc/runtime/Log.c \ + $(XDC_ROOT)xdc/runtime/Memory.c \ + $(XDC_ROOT)xdc/runtime/Registry.c \ + $(XDC_ROOT)xdc/runtime/Startup.c \ + $(XDC_ROOT)xdc/runtime/System.c \ + $(XDC_ROOT)xdc/runtime/SysCallback.c \ + $(XDC_ROOT)xdc/runtime/Text.c \ + $(XDC_ROOT)xdc/runtime/Timestamp.c \ + +rom_sysbios.aem3: rom_sysbios.obj m3_Hwi_asm.obj m3_Hwi_asm_switch.obj m3_IntrinsicsSupport_asm.obj m3_TaskSupport_asm.obj + @-$(call RM, $@) + @echo arem3 $^ ... + @$(AR) $@ $^ C:/ti/tirtos_cc13xx_cc26xx_2_21_00_06/products/bios_6_46_01_37/packages/ti/sysbios/rom/cortexm/cc26xx/golden/CC26xx/rom_sysbios_config.obj + +clean: + @$(DEL) ..\makefile.libs + @-$(call RM, *) diff --git a/src/sysbios/rom_sysbios.aem3 b/src/sysbios/rom_sysbios.aem3 new file mode 100644 index 0000000000000000000000000000000000000000..62ea1482cf732125cb9bc2e5d4a618cfbdcd5ad9 GIT binary patch literal 3393974 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

*Xv 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 z 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 z�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