From 5e5e9b92fd68bf4963844a61017dadec0ed8bb7e Mon Sep 17 00:00:00 2001 From: Jonas Olsson Date: Wed, 25 Feb 2015 13:09:56 +0100 Subject: [PATCH 1/7] Add CC26xx CPU files --- cpu/cc26xx/Makefile.cc26xx | 132 ++ cpu/cc26xx/cc26xx-model.h | 74 + cpu/cc26xx/cc26xx.ld | 103 + cpu/cc26xx/clock.c | 189 ++ cpu/cc26xx/dbg.h | 65 + cpu/cc26xx/debug-uart.h | 49 + cpu/cc26xx/dev/batmon-sensor.c | 143 ++ cpu/cc26xx/dev/batmon-sensor.h | 57 + cpu/cc26xx/dev/cc26xx-rf.c | 2179 +++++++++++++++++++++ cpu/cc26xx/dev/cc26xx-rf.h | 116 ++ cpu/cc26xx/dev/cc26xx-rtc.c | 127 ++ cpu/cc26xx/dev/cc26xx-rtc.h | 100 + cpu/cc26xx/dev/cc26xx-uart.c | 249 +++ cpu/cc26xx/dev/cc26xx-uart.h | 76 + cpu/cc26xx/dev/contiki-watchdog.c | 99 + cpu/cc26xx/dev/gpio-interrupt.c | 97 + cpu/cc26xx/dev/gpio-interrupt.h | 71 + cpu/cc26xx/dev/rfc-api/ble_cmd.h | 1082 ++++++++++ cpu/cc26xx/dev/rfc-api/ble_cmd_field.h | 623 ++++++ cpu/cc26xx/dev/rfc-api/ble_mailbox.h | 119 ++ cpu/cc26xx/dev/rfc-api/common_cmd.h | 1043 ++++++++++ cpu/cc26xx/dev/rfc-api/common_cmd_field.h | 448 +++++ cpu/cc26xx/dev/rfc-api/data_entry.h | 91 + cpu/cc26xx/dev/rfc-api/ieee_cmd.h | 614 ++++++ cpu/cc26xx/dev/rfc-api/ieee_cmd_field.h | 403 ++++ cpu/cc26xx/dev/rfc-api/ieee_mailbox.h | 107 + cpu/cc26xx/dev/rfc-api/mailbox.h | 580 ++++++ cpu/cc26xx/dev/uart1.h | 51 + cpu/cc26xx/fault-handlers.c | 54 + cpu/cc26xx/ieee-addr.c | 90 + cpu/cc26xx/ieee-addr.h | 105 + cpu/cc26xx/lpm.c | 408 ++++ cpu/cc26xx/lpm.h | 172 ++ cpu/cc26xx/mtarch.h | 48 + cpu/cc26xx/putchar.c | 70 + cpu/cc26xx/rtimer-arch.c | 92 + cpu/cc26xx/rtimer-arch.h | 65 + cpu/cc26xx/slip-arch.c | 65 + cpu/cc26xx/ti-lib.h | 667 +++++++ 39 files changed, 10923 insertions(+) create mode 100644 cpu/cc26xx/Makefile.cc26xx create mode 100644 cpu/cc26xx/cc26xx-model.h create mode 100644 cpu/cc26xx/cc26xx.ld create mode 100644 cpu/cc26xx/clock.c create mode 100644 cpu/cc26xx/dbg.h create mode 100644 cpu/cc26xx/debug-uart.h create mode 100644 cpu/cc26xx/dev/batmon-sensor.c create mode 100644 cpu/cc26xx/dev/batmon-sensor.h create mode 100644 cpu/cc26xx/dev/cc26xx-rf.c create mode 100644 cpu/cc26xx/dev/cc26xx-rf.h create mode 100644 cpu/cc26xx/dev/cc26xx-rtc.c create mode 100644 cpu/cc26xx/dev/cc26xx-rtc.h create mode 100644 cpu/cc26xx/dev/cc26xx-uart.c create mode 100644 cpu/cc26xx/dev/cc26xx-uart.h create mode 100644 cpu/cc26xx/dev/contiki-watchdog.c create mode 100644 cpu/cc26xx/dev/gpio-interrupt.c create mode 100644 cpu/cc26xx/dev/gpio-interrupt.h create mode 100644 cpu/cc26xx/dev/rfc-api/ble_cmd.h create mode 100755 cpu/cc26xx/dev/rfc-api/ble_cmd_field.h create mode 100644 cpu/cc26xx/dev/rfc-api/ble_mailbox.h create mode 100644 cpu/cc26xx/dev/rfc-api/common_cmd.h create mode 100755 cpu/cc26xx/dev/rfc-api/common_cmd_field.h create mode 100644 cpu/cc26xx/dev/rfc-api/data_entry.h create mode 100644 cpu/cc26xx/dev/rfc-api/ieee_cmd.h create mode 100755 cpu/cc26xx/dev/rfc-api/ieee_cmd_field.h create mode 100644 cpu/cc26xx/dev/rfc-api/ieee_mailbox.h create mode 100644 cpu/cc26xx/dev/rfc-api/mailbox.h create mode 100644 cpu/cc26xx/dev/uart1.h create mode 100644 cpu/cc26xx/fault-handlers.c create mode 100644 cpu/cc26xx/ieee-addr.c create mode 100644 cpu/cc26xx/ieee-addr.h create mode 100644 cpu/cc26xx/lpm.c create mode 100644 cpu/cc26xx/lpm.h create mode 100644 cpu/cc26xx/mtarch.h create mode 100644 cpu/cc26xx/putchar.c create mode 100644 cpu/cc26xx/rtimer-arch.c create mode 100644 cpu/cc26xx/rtimer-arch.h create mode 100644 cpu/cc26xx/slip-arch.c create mode 100644 cpu/cc26xx/ti-lib.h diff --git a/cpu/cc26xx/Makefile.cc26xx b/cpu/cc26xx/Makefile.cc26xx new file mode 100644 index 000000000..a339bd9f1 --- /dev/null +++ b/cpu/cc26xx/Makefile.cc26xx @@ -0,0 +1,132 @@ +CC = arm-none-eabi-gcc +CPP = arm-none-eabi-cpp +LD = arm-none-eabi-gcc +AR = arm-none-eabi-ar +OBJCOPY = arm-none-eabi-objcopy +OBJDUMP = arm-none-eabi-objdump +NM = arm-none-eabi-nm +SIZE = arm-none-eabi-size + +### TI CC26xxware out-of-tree +### TI_CC26XXWARE is the home directory of the cc26xxware +### It MUST be provided as a path relative to $(CONTIKI) +### For example, if +### CONTIKI = /home/user/contiki +### and TI_CC26XXWARE is stored in +### /home/user/cc26xxware +### then set +### TI_CC26XXWARE = ../cc26xxware +ifndef TI_CC26XXWARE + $(error TI_CC26XXWARE not defined. Please see the README) +endif + +### cc26xxware sources will be added to the MODULES list +TI_CC26XXWARE_SRC = $(TI_CC26XXWARE)/driverlib + +### The directory with startup sources will be added to the CONTIKI_CPU_DIRS +### and the sources therein are added to the sources list explicitly. They are +### also listed explicitly in the linker command (through TARGET_STARTFILES), +### to make sure they always get linked in the image +TI_CC26XXWARE_STARTUP = ../../$(TI_CC26XXWARE)/startup_files +TI_CC26XXWARE_STARTUP_SRCS = ccfg.c startup_gcc.c + +### MODULES will add some of these to the include pach, but we need to add +### them earlier to prevent filename clashes with Contiki core files +CFLAGS += -I$(CONTIKI)/$(TI_CC26XXWARE) -I$(CONTIKI)/$(TI_CC26XXWARE_SRC) +CFLAGS += -I$(CONTIKI)/$(TI_CC26XXWARE)/inc +MODULES += $(TI_CC26XXWARE_SRC) + +LDSCRIPT = $(CONTIKI_CPU)/cc26xx.ld + +CFLAGS += -mcpu=cortex-m3 -mthumb -mlittle-endian +CFLAGS += -ffunction-sections -fdata-sections +CFLAGS += -fshort-enums -fomit-frame-pointer -fno-strict-aliasing +CFLAGS += -Wall -std=c99 + +### Workaround for driverlib's cpu.h which tests if defined(gcc) +### Delete if it gets fixed or if we stop using the driverlib +CFLAGS += -Dgcc=__GNUC__ + +LDFLAGS += -mcpu=cortex-m3 -mthumb -mlittle-endian -nostartfiles +LDFLAGS += -T $(LDSCRIPT) +LDFLAGS += -Wl,--gc-sections,--sort-section=alignment +LDFLAGS += -Wl,-Map=$(@:.elf=-$(TARGET).map),--cref,--no-warn-mismatch +OBJCOPY_FLAGS += -O binary --gap-fill 0xff +OBJDUMP_FLAGS += --disassemble --source --disassembler-options=force-thumb + +### Are we building with code size optimisations? +ifeq ($(SMALL),1) + CFLAGS += -Os +else + CFLAGS += -O2 +endif + +### If the user-specified a Node ID, pass a define +ifdef NODEID + CFLAGS += -DIEEE_ADDR_NODE_ID=$(NODEID) +endif + +### CPU-dependent cleanup files +CLEAN += symbols.c symbols.h *.d *.elf *.hex + +### CPU-dependent directories +CONTIKI_CPU_DIRS = . dev dev/rfc-api $(TI_CC26XXWARE_STARTUP) + +### Use the existing debug I/O in cpu/arm/common +CONTIKI_CPU_DIRS += ../arm/common/dbg-io + +### CPU-dependent source files +CONTIKI_CPU_SOURCEFILES += clock.c rtimer-arch.c cc26xx-rtc.c uart.c +CONTIKI_CPU_SOURCEFILES += cc26xx-rf.c contiki-watchdog.c +CONTIKI_CPU_SOURCEFILES += putchar.c ieee-addr.c batmon-sensor.c +CONTIKI_CPU_SOURCEFILES += slip-arch.c slip.c cc26xx-uart.c lpm.c +CONTIKI_CPU_SOURCEFILES += gpio-interrupt.c + +DEBUG_IO_SOURCEFILES += dbg-printf.c dbg-snprintf.c dbg-sprintf.c strformat.c + +CONTIKI_SOURCEFILES += $(CONTIKI_CPU_SOURCEFILES) $(DEBUG_IO_SOURCEFILES) + +TARGET_START_SOURCEFILES += fault-handlers.c $(TI_CC26XXWARE_STARTUP_SRCS) +TARGET_STARTFILES = $(addprefix $(OBJECTDIR)/,$(call oname, $(TARGET_START_SOURCEFILES))) + +### Don't treat the .elf as intermediate +.PRECIOUS: %.elf %.hex %.bin + +### Always re-build ieee-addr.o in case the command line passes a new NODEID +FORCE: + +$(OBJECTDIR)/ieee-addr.o: ieee-addr.c FORCE | $(OBJECTDIR) + $(TRACE_CC) + $(Q)$(CC) $(CFLAGS) -c $< -o $@ + +### Compilation rules +CUSTOM_RULE_LINK=1 + +%.elf: $(TARGET_STARTFILES) %.co $(PROJECT_OBJECTFILES) $(PROJECT_LIBRARIES) contiki-$(TARGET).a $(LDSCRIPT) + $(TRACE_LD) + $(Q)$(LD) $(LDFLAGS) ${filter-out $(LDSCRIPT) %.a,$^} ${filter %.a,$^} $(TARGET_LIBFILES) -lm -o $@ + +%.hex: %.elf + $(OBJCOPY) -O ihex $< $@ + +%.bin: %.elf + $(OBJCOPY) $(OBJCOPY_FLAGS) $< $@ + +%.lst: %.elf + $(OBJDUMP) $(OBJDUMP_FLAGS) $< > $@ + +### We don't really need the .hex and .bin for the .$(TARGET) but let's make +### sure they get built +%.$(TARGET): %.elf %.hex %.bin + cp $< $@ + +# a target that gives a user-friendly memory profile, taking into account the RAM +# that is statically occupied by the stack as defined in the linker script +# see $(LDSCRIPT) +RAM_SIZE = 0x00003E00 +FLASH_SIZE = 0x0001E000 +STACK_SIZE = 0 +%.size: %.$(TARGET) + @$(SIZE) -A $< | egrep "data|bss" | awk '{s+=$$2} END {s=s+$(STACK_SIZE); f=$(RAM_SIZE)-s; printf "[RAM] used %6d, free %6d\n",s,f;}' + @$(SIZE) -A $< | egrep "text|isr_vector" | awk '{s+=$$2} END {f=$(FLASH_SIZE)-s; printf "[Flash] used %6d, free %6d\n",s,f;}' + diff --git a/cpu/cc26xx/cc26xx-model.h b/cpu/cc26xx/cc26xx-model.h new file mode 100644 index 000000000..44ee1cfe3 --- /dev/null +++ b/cpu/cc26xx/cc26xx-model.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-models CC26xx models + * + * The CC26xx comes out in various flavours. Most notable within the context + * of this Contiki port: The CC2630 with IEEE (but no BLE) support and the + * CC2650 with IEEE and BLE support. + * + * This port supports both models and will automatically turn off the BLE code + * if the CC2630 is selected. + * + * @{ + */ +/** + * \file + * Header file with definitions relating to various CC26xx variants + */ +/*---------------------------------------------------------------------------*/ +#ifndef CC26XX_MODEL_H_ +#define CC26XX_MODEL_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +/*---------------------------------------------------------------------------*/ +#ifdef CC26XX_MODEL_CONF_CPU_VARIANT +#define CC26XX_MODEL_CPU_VARIANT CC26XX_MODEL_CONF_CPU_VARIANT +#else +#define CC26XX_MODEL_CPU_VARIANT 2650 +#endif + +#if (CC26XX_MODEL_CPU_VARIANT != 2630) && (CC26XX_MODEL_CPU_VARIANT != 2650) +#error Incorrect CC26xx variant selected. +#error Check the value of CC26XX_MODEL_CONF_CPU_VARIANT +#error Supported values: 2630 and 2650 +#endif +/*---------------------------------------------------------------------------*/ +#endif /* CC26XX_MODEL_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/cc26xx.ld b/cpu/cc26xx/cc26xx.ld new file mode 100644 index 000000000..27c39c6c5 --- /dev/null +++ b/cpu/cc26xx/cc26xx.ld @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ + +/* CC26XX linker script */ + +/* Entry Point */ +ENTRY(ResetISR) + +MEMORY +{ + /* Flash Size 128 KB minus the CCA area below (76 bytes) */ + FLASH (RX) : ORIGIN = 0x00000000, LENGTH = 0x0001FFAC + + /* + * Customer Configuration Area and Bootloader Backdoor configuration + * in flash, up to 80 bytes + */ + FLASH_CCFG (RX) : ORIGIN = 0x0001FFAC, LENGTH = 84 + + /* RAM Size 20KB (PG2.1) */ + SRAM (RWX) : ORIGIN = 0x20000000, LENGTH = 0x00005000 +} + +/*. Highest address of the stack. Used in startup file .*/ +_estack = ORIGIN(SRAM) + LENGTH(SRAM); /* End of SRAM */ + +/*. Generate a link error if heap and stack don’t fit into RAM .*/ +_Min_Heap_Size = 0; +_Min_Stack_Size = 0x100; + +SECTIONS +{ + .text : + { + _text = .; + KEEP(*(.vectors)) + *(.text*) + *(.rodata*) + _etext = .; + } > FLASH = 0 + + .data : + { + _data = .; + *(vtable) + *(.data*) + _edata = .; + } > SRAM AT > FLASH + + .ARM.exidx : + { + *(.ARM.exidx*) + } > FLASH + + .bss : + { + _bss = .; + *(.bss*) + *(COMMON) + _ebss = .; + } > SRAM + + .ccfg : + { + KEEP(*(.ccfg)) + } > FLASH_CCFG + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(4); + } >SRAM +} diff --git a/cpu/cc26xx/clock.c b/cpu/cc26xx/clock.c new file mode 100644 index 000000000..fdf3b9405 --- /dev/null +++ b/cpu/cc26xx/clock.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup platform + * @{ + * + * \defgroup cc26xx-platforms TI CC26xx-powered Platforms + * @{ + * + * \defgroup cc26xx The TI CC26xx CPU + * @{ + * + * \addtogroup cc26xx-clocks + * @{ + * + * \defgroup cc26xx-software-clock CC26xx Software Clock + * + * Implementation of the clock module for the cc26xx. + * + * The software clock uses the facilities provided by the AON RTC driver + * @{ + * + * \file + * Software clock implementation for the TI CC26xx + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" + +#include "ti-lib.h" +/*---------------------------------------------------------------------------*/ +static volatile clock_time_t count; +static volatile clock_time_t second_countdown; +static volatile unsigned long secs; +/*---------------------------------------------------------------------------*/ +static void +power_domain_on(void) +{ + ti_lib_prcm_power_domain_on(PRCM_DOMAIN_PERIPH); + while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH) != + PRCM_DOMAIN_POWER_ON); +} +/*---------------------------------------------------------------------------*/ +void +clock_init(void) +{ + count = 0; + secs = 0; + second_countdown = CLOCK_SECOND; + + /* + * Here, we configure GPT0 Timer A, which we subsequently use in + * clock_delay_usec + * + * First, enable GPT0 in run mode. We don't need it in sleep mode + */ + ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_TIMER0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* Disable both GPT0 timers */ + HWREG(GPT0_BASE + GPT_O_CTL) &= ~(GPT_CTL_TAEN | GPT_CTL_TBEN); + + /* + * We assume that the clock is running at 48MHz, we use GPT0 Timer A, + * one-shot, countdown, prescaled by 48 gives us 1 tick per usec + */ + ti_lib_timer_configure(GPT0_BASE, + TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_ONE_SHOT); + + /* Global config: split pair (2 x 16-bit wide) */ + HWREG(GPT0_BASE + GPT_O_CFG) = TIMER_CFG_SPLIT_PAIR >> 24; + + /* + * Pre-scale value 47 pre-scales by 48 + * + * ToDo: The theoretical value here should be 47 (to provide x48 prescale) + * However, 49 seems to give results much closer to the desired delay + */ + ti_lib_timer_prescale_set(GPT0_BASE, TIMER_B, 49); + + /* GPT0 / Timer B: One shot, PWM interrupt enable */ + HWREG(GPT0_BASE + GPT_O_TBMR) = + ((TIMER_CFG_B_ONE_SHOT >> 8) & 0xFF) | GPT_TBMR_TBPWMIE; +} +/*---------------------------------------------------------------------------*/ +CCIF clock_time_t +clock_time(void) +{ + return count; +} +/*---------------------------------------------------------------------------*/ +void +clock_update(void) +{ + count++; + if(etimer_pending()) { + etimer_request_poll(); + } + + if(--second_countdown == 0) { + secs++; + second_countdown = CLOCK_SECOND; + } +} +/*---------------------------------------------------------------------------*/ +void +clock_set_seconds(unsigned long sec) +{ + secs = sec; +} +/*---------------------------------------------------------------------------*/ +CCIF unsigned long +clock_seconds(void) +{ + return secs; +} +/*---------------------------------------------------------------------------*/ +void +clock_wait(clock_time_t i) +{ + clock_time_t start; + + start = clock_time(); + while(clock_time() - start < (clock_time_t)i); +} +/*---------------------------------------------------------------------------*/ +void +clock_delay_usec(uint16_t len) +{ + if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_PERIPH) != + PRCM_DOMAIN_POWER_ON) { + power_domain_on(); + } + + ti_lib_timer_load_set(GPT0_BASE, TIMER_B, len); + ti_lib_timer_enable(GPT0_BASE, TIMER_B); + + /* + * Wait for TBEN to clear. CC26xxware does not provide us with a convenient + * function, hence the direct register access here + */ + while(HWREG(GPT0_BASE | GPT_O_CTL) & GPT_CTL_TBEN); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Obsolete delay function but we implement it here since some code + * still uses it + */ +void +clock_delay(unsigned int i) +{ + clock_delay_usec(i); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + * @} + * @} + * @} + */ diff --git a/cpu/cc26xx/dbg.h b/cpu/cc26xx/dbg.h new file mode 100644 index 000000000..a5bff7513 --- /dev/null +++ b/cpu/cc26xx/dbg.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-char-io CC26xx Character I/O + * + * CC26xx CPU-specific functions for debugging and SLIP I/O + * @{ + * + * \file + * Header file for the CC26xx Debug I/O module + */ +#ifndef DBG_H_ +#define DBG_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +/*---------------------------------------------------------------------------*/ +/** + * \brief Print a stream of bytes + * \param seq A pointer to the stream + * \param len The number of bytes to print + * \return The number of printed bytes + * + * This function is an arch-specific implementation required by the dbg-io + * API in cpu/arm/common/dbg-io. It prints a stream of bytes over the + * peripheral used by the platform. + */ +unsigned int dbg_send_bytes(const unsigned char *seq, unsigned int len); +/*---------------------------------------------------------------------------*/ +#endif /* DBG_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/debug-uart.h b/cpu/cc26xx/debug-uart.h new file mode 100644 index 000000000..c2d9b0ffb --- /dev/null +++ b/cpu/cc26xx/debug-uart.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-char-io + * @{ + * + * \file + * This file is here because DBG I/O expects it to be. It just includes + * our own dbg.h which has a non-misleading name and which also adheres + * to Contiki's naming convention + */ +/*---------------------------------------------------------------------------*/ +#ifndef DEBUG_UART_H_ +#define DEBUG_UART_H_ +/*---------------------------------------------------------------------------*/ +#include "dbg.h" +/*---------------------------------------------------------------------------*/ +#endif /* DEBUG_UART_H_ */ +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/cpu/cc26xx/dev/batmon-sensor.c b/cpu/cc26xx/dev/batmon-sensor.c new file mode 100644 index 000000000..8131307a0 --- /dev/null +++ b/cpu/cc26xx/dev/batmon-sensor.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-batmon + * @{ + * + * \file + * Driver for the CC26xx AON battery monitor + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "lib/sensors.h" +#include "batmon-sensor.h" + +#include "ti-lib.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +#define SENSOR_STATUS_DISABLED 0 +#define SENSOR_STATUS_ENABLED 1 + +static int enabled = SENSOR_STATUS_DISABLED; +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns a reading from the sensor + * \param type BATMON_SENSOR_TYPE_TEMP or BATMON_SENSOR_TYPE_VOLT + * + * \return The raw sensor reading, not converted to human-readable form + */ +static int +value(int type) +{ + uint32_t tmp_value; + + if(enabled == SENSOR_STATUS_DISABLED) { + PRINTF("Sensor Disabled\n"); + return 0; + } + + if(type == BATMON_SENSOR_TYPE_TEMP) { + tmp_value = ti_lib_aon_batmon_temperature_get(); + } else if(type == BATMON_SENSOR_TYPE_VOLT) { + tmp_value = ti_lib_aon_batmon_battery_voltage_get(); + } else { + PRINTF("Invalid type\n"); + return 0; + } + + return (int)tmp_value; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the battery monitor sensor. + * + * \param type Activate, enable or disable the sensor. See below + * \param enable If + * + * When type == SENSORS_HW_INIT we turn on the hardware + * When type == SENSORS_ACTIVE and enable==1 we enable the sensor + * When type == SENSORS_ACTIVE and enable==0 we disable the sensor + */ +static int +configure(int type, int enable) +{ + switch(type) { + case SENSORS_HW_INIT: + ti_lib_aon_batmon_enable(); + ti_lib_aon_batmon_measurement_cycle_set(AON_BATMON_CYCLE_32); + enabled = SENSOR_STATUS_ENABLED; + break; + case SENSORS_ACTIVE: + if(enable) { + ti_lib_aon_batmon_enable(); + enabled = SENSOR_STATUS_ENABLED; + } else { + ti_lib_aon_batmon_disable(); + enabled = SENSOR_STATUS_DISABLED; + } + break; + default: + break; + } + return enabled; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the status of the sensor + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the sensor is enabled + */ +static int +status(int type) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return enabled; + break; + default: + break; + } + return SENSOR_STATUS_DISABLED; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(batmon_sensor, "Battery Monitor", value, configure, status); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/cpu/cc26xx/dev/batmon-sensor.h b/cpu/cc26xx/dev/batmon-sensor.h new file mode 100644 index 000000000..12b0c6bbd --- /dev/null +++ b/cpu/cc26xx/dev/batmon-sensor.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-batmon CC26xx BatMon sensor driver + * + * Driver for the on-chip battery voltage and chip temperature sensor. + * @{ + * + * \file + * Header file for the CC26xx battery monitor + */ +/*---------------------------------------------------------------------------*/ +#ifndef BATMON_SENSOR_H_ +#define BATMON_SENSOR_H_ +/*---------------------------------------------------------------------------*/ +#define BATMON_SENSOR_TYPE_TEMP 1 +#define BATMON_SENSOR_TYPE_VOLT 2 +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor batmon_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* BATMON_SENSOR_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/dev/cc26xx-rf.c b/cpu/cc26xx/dev/cc26xx-rf.c new file mode 100644 index 000000000..c791969e5 --- /dev/null +++ b/cpu/cc26xx/dev/cc26xx-rf.c @@ -0,0 +1,2179 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-rf + * @{ + * + * \file + * Implementation of the CC26xx RF driver + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/radio.h" +#include "dev/cc26xx-rf.h" +#include "net/packetbuf.h" +#include "net/rime/rimestats.h" +#include "net/linkaddr.h" +#include "net/netstack.h" +#include "sys/energest.h" +#include "sys/clock.h" +#include "sys/rtimer.h" +#include "lpm.h" +#include "ti-lib.h" +/*---------------------------------------------------------------------------*/ +/* RF core and RF HAL API */ +#include "hw_rfc_dbell.h" +#include "hw_rfc_pwr.h" +/*---------------------------------------------------------------------------*/ +/* RF Core Mailbox API */ +#include "mailbox.h" +#include "common_cmd.h" +#include "common_cmd_field.h" +#include "ble_cmd.h" +#include "ble_cmd_field.h" +#include "ieee_cmd.h" +#include "ieee_cmd_field.h" +#include "data_entry.h" +#include "ble_mailbox.h" +#include "ieee_mailbox.h" +/*---------------------------------------------------------------------------*/ +#include +#include +#include +/*---------------------------------------------------------------------------*/ +#define BUSYWAIT_UNTIL(cond, max_time) \ + do { \ + rtimer_clock_t t0; \ + t0 = RTIMER_NOW(); \ + while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + (max_time))); \ + } while(0) +/*---------------------------------------------------------------------------*/ +#ifndef MIN +#define MIN(n, m) (((n) < (m)) ? (n) : (m)) +#endif +/*---------------------------------------------------------------------------*/ +#ifdef __GNUC__ +#define CC_ALIGN_ATTR(n) __attribute__ ((aligned(n))) +#else +#define CC_ALIGN_ATTR(n) +#endif +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +/* Data entry status field constants */ +#define DATA_ENTRY_STATUS_PENDING 0x00 /* Not in use by the Radio CPU */ +#define DATA_ENTRY_STATUS_ACTIVE 0x01 /* Open for r/w by the radio CPU */ +#define DATA_ENTRY_STATUS_BUSY 0x02 /* Ongoing r/w */ +#define DATA_ENTRY_STATUS_FINISHED 0x03 /* Free to use and to free */ +#define DATA_ENTRY_STATUS_UNFINISHED 0x04 /* Partial RX entry */ +/*---------------------------------------------------------------------------*/ +/* RF stats data structure */ +static uint8_t rf_stats[16] = { 0 }; +/*---------------------------------------------------------------------------*/ +/* RF Command status constants - Correspond to values in the CMDSTA register */ +#define RF_CMD_STATUS_PENDING 0x00 +#define RF_CMD_STATUS_DONE 0x01 +#define RF_CMD_STATUS_ILLEGAL_PTR 0x81 +#define RF_CMD_STATUS_UNKNOWN_CMD 0x82 +#define RF_CMD_STATUS_UNKNOWN_DIR_CMD 0x83 +#define RF_CMD_STATUS_CONTEXT_ERR 0x85 +#define RF_CMD_STATUS_SCHEDULING_ERR 0x86 +#define RF_CMD_STATUS_PAR_ERR 0x87 +#define RF_CMD_STATUS_QUEUE_ERR 0x88 +#define RF_CMD_STATUS_QUEUE_BUSY 0x89 + +/* Status values starting with 0x8 correspond to errors */ +#define RF_CMD_STATUS_ERR_MASK 0x80 + +/* Return values for rf_send_cmd_ok */ +#define RF_CMD_ERROR 0 +#define RF_CMD_OK 1 + +/* The size of the RF commands buffer */ +#define RF_CMD_BUFFER_SIZE 128 +/*---------------------------------------------------------------------------*/ +/* RF Radio Op status constants. Field 'status' in Radio Op command struct */ +#define RF_RADIO_OP_STATUS_IDLE 0x0000 +#define RF_RADIO_OP_STATUS_PENDING 0x0001 +#define RF_RADIO_OP_STATUS_ACTIVE 0x0002 +#define RF_RADIO_OP_STATUS_SKIPPED 0x0003 +#define RF_RADIO_OP_STATUS_DONE_OK 0x0400 +#define RF_RADIO_OP_STATUS_DONE_COUNTDOWN 0x0401 +#define RF_RADIO_OP_STATUS_DONE_RXERR 0x0402 +#define RF_RADIO_OP_STATUS_DONE_TIMEOUT 0x0403 +#define RF_RADIO_OP_STATUS_DONE_STOPPED 0x0404 +#define RF_RADIO_OP_STATUS_DONE_ABORT 0x0405 +#define RF_RADIO_OP_STATUS_ERROR_PAST_START 0x0800 +#define RF_RADIO_OP_STATUS_ERROR_START_TRIG 0x0801 +#define RF_RADIO_OP_STATUS_ERROR_CONDITION 0x0802 +#define RF_RADIO_OP_STATUS_ERROR_PAR 0x0803 +#define RF_RADIO_OP_STATUS_ERROR_POINTER 0x0804 +#define RF_RADIO_OP_STATUS_ERROR_CMDID 0x0805 +#define RF_RADIO_OP_STATUS_ERROR_NO_SETUP 0x0807 +#define RF_RADIO_OP_STATUS_ERROR_NO_FS 0x0808 +#define RF_RADIO_OP_STATUS_ERROR_SYNTH_PROG 0x0809 + +/* Additional Op status values for IEEE mode */ +#define RF_RADIO_OP_STATUS_IEEE_SUSPENDED 0x2001 +#define RF_RADIO_OP_STATUS_IEEE_DONE_OK 0x2400 +#define RF_RADIO_OP_STATUS_IEEE_DONE_BUSY 0x2401 +#define RF_RADIO_OP_STATUS_IEEE_DONE_STOPPED 0x2402 +#define RF_RADIO_OP_STATUS_IEEE_DONE_ACK 0x2403 +#define RF_RADIO_OP_STATUS_IEEE_DONE_ACKPEND 0x2404 +#define RF_RADIO_OP_STATUS_IEEE_DONE_TIMEOUT 0x2405 +#define RF_RADIO_OP_STATUS_IEEE_DONE_BGEND 0x2406 +#define RF_RADIO_OP_STATUS_IEEE_DONE_ABORT 0x2407 +#define RF_RADIO_OP_STATUS_ERROR_WRONG_BG 0x0806 +#define RF_RADIO_OP_STATUS_IEEE_ERROR_PAR 0x2800 +#define RF_RADIO_OP_STATUS_IEEE_ERROR_NO_SETUP 0x2801 +#define RF_RADIO_OP_STATUS_IEEE_ERROR_NO_FS 0x2802 +#define RF_RADIO_OP_STATUS_IEEE_ERROR_SYNTH_PROG 0x2803 +#define RF_RADIO_OP_STATUS_IEEE_ERROR_RXOVF 0x2804 +#define RF_RADIO_OP_STATUS_IEEE_ERROR_TXUNF 0x2805 + +/* Op status values for BLE mode */ +#define RF_RADIO_OP_STATUS_BLE_DONE_OK 0x1400 +#define RF_RADIO_OP_STATUS_BLE_DONE_RXTIMEOUT 0x1401 +#define RF_RADIO_OP_STATUS_BLE_DONE_NOSYNC 0x1402 +#define RF_RADIO_OP_STATUS_BLE_DONE_RXERR 0x1403 +#define RF_RADIO_OP_STATUS_BLE_DONE_CONNECT 0x1404 +#define RF_RADIO_OP_STATUS_BLE_DONE_MAXNACK 0x1405 +#define RF_RADIO_OP_STATUS_BLE_DONE_ENDED 0x1406 +#define RF_RADIO_OP_STATUS_BLE_DONE_ABORT 0x1407 +#define RF_RADIO_OP_STATUS_BLE_DONE_STOPPED 0x1408 +#define RF_RADIO_OP_STATUS_BLE_ERROR_PAR 0x1800 +#define RF_RADIO_OP_STATUS_BLE_ERROR_RXBUF 0x1801 +#define RF_RADIO_OP_STATUS_BLE_ERROR_NO_SETUP 0x1802 +#define RF_RADIO_OP_STATUS_BLE_ERROR_NO_FS 0x1803 +#define RF_RADIO_OP_STATUS_BLE_ERROR_SYNTH_PROG 0x1804 +#define RF_RADIO_OP_STATUS_BLE_ERROR_RXOVF 0x1805 +#define RF_RADIO_OP_STATUS_BLE_ERROR_TXUNF 0x1806 + +/* Bits 15:12 signify the protocol */ +#define RF_RADIO_OP_STATUS_PROTO_MASK 0xF000 +#define RF_RADIO_OP_STATUS_PROTO_GENERIC 0x0000 +#define RF_RADIO_OP_STATUS_PROTO_BLE 0x1000 +#define RF_RADIO_OP_STATUS_PROTO_IEEE 0x2000 +#define RF_RADIO_OP_STATUS_PROTO_PROP 0x3000 + +/* Bits 11:10 signify Running / Done OK / Done with error */ +#define RF_RADIO_OP_MASKED_STATUS 0x0C00 +#define RF_RADIO_OP_MASKED_STATUS_RUNNING 0x0000 +#define RF_RADIO_OP_MASKED_STATUS_DONE 0x0400 +#define RF_RADIO_OP_MASKED_STATUS_ERROR 0x0800 +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the current status of a running Radio Op command + * \param a A pointer with the buffer used to initiate the command + * \return The value of the Radio Op buffer's status field + * + * This macro can be used to e.g. return the status of a previously + * initiated background operation, or of an immediate command + */ +#define RF_RADIO_OP_GET_STATUS(a) GET_FIELD_V(a, radioOp, status) +/*---------------------------------------------------------------------------*/ +/* Special value returned by CMD_IEEE_CCA_REQ when an RSSI is not available */ +#define RF_CMD_CCA_REQ_RSSI_UNKNOWN -128 + +/* Used for the return value of channel_clear */ +#define RF_CCA_CLEAR 1 +#define RF_CCA_BUSY 0 + +/* Used as an error return value for get_cca_info */ +#define RF_GET_CCA_INFO_ERROR 0xFF + +/* + * Values of the individual bits of the ccaInfo field in CMD_IEEE_CCA_REQ's + * status struct + */ +#define RF_CMD_CCA_REQ_CCA_STATE_IDLE 0 /* 00 */ +#define RF_CMD_CCA_REQ_CCA_STATE_BUSY 1 /* 01 */ +#define RF_CMD_CCA_REQ_CCA_STATE_INVALID 2 /* 10 */ +/*---------------------------------------------------------------------------*/ +#define RF_MODE_BLE 0 +#define RF_MODE_IEEE 1 +/*---------------------------------------------------------------------------*/ +/* How long to wait for an ongoing ACK TX to finish before starting frame TX */ +#define TX_WAIT_TIMEOUT (RTIMER_SECOND >> 11) + +/* How long to wait for the RF to enter RX in rf_cmd_ieee_rx */ +#define ENTER_RX_WAIT_TIMEOUT (RTIMER_SECOND >> 10) +/*---------------------------------------------------------------------------*/ +/* TX Power dBm lookup table - values from SmartRF Studio */ +typedef struct output_config { + radio_value_t dbm; + uint8_t register_ib; + uint8_t register_gc; +} output_config_t; + +static const output_config_t output_power[] = { + { 5, 0x29, 0x00 }, + { 4, 0x20, 0x00 }, + { 3, 0x19, 0x00 }, + { 2, 0x25, 0x01 }, + { 1, 0x21, 0x01 }, + { 0, 0x1D, 0x01 }, + { -3, 0x19, 0x03 }, + { -6, 0x13, 0x03 }, + { -9, 0x0F, 0x03 }, +}; + +#define OUTPUT_CONFIG_COUNT (sizeof(output_power) / sizeof(output_config_t)) + +/* Max and Min Output Power in dBm */ +#define OUTPUT_POWER_MIN (output_power[OUTPUT_CONFIG_COUNT - 1].dbm) +#define OUTPUT_POWER_MAX (output_power[0].dbm) +#define OUTPUT_POWER_UNKNOWN 0xFFFF + +/* Default TX Power - position in output_power[] */ +#define CC26XX_RF_TX_POWER 0 +const output_config_t *tx_power_current = &output_power[0]; +/*---------------------------------------------------------------------------*/ +#define RF_CORE_CLOCKS_MASK (RFC_PWR_PWMCLKEN_RFC_M | RFC_PWR_PWMCLKEN_CPE_M \ + | RFC_PWR_PWMCLKEN_CPERAM_M) +/*---------------------------------------------------------------------------*/ +/* RF interrupts */ +#define RX_IRQ IRQ_IEEE_RX_ENTRY_DONE +#define TX_IRQ IRQ_IEEE_TX_FRAME +#define TX_ACK_IRQ IRQ_IEEE_TX_ACK +#define ERROR_IRQ IRQ_INTERNAL_ERROR + +/* + * We don't really care about TX ISR, we just use it to bring the CM3 out + * of sleep, which it enters while the RF is TXing + */ +#define ENABLED_IRQS (RX_IRQ + TX_IRQ + ERROR_IRQ) + +#define cc26xx_rf_cpe0_isr RFCCPE0IntHandler +#define cc26xx_rf_cpe1_isr RFCCPE1IntHandler +/*---------------------------------------------------------------------------*/ +/* + * Buffers used to send commands to the RF core (generic and IEEE commands). + * Some of those buffers are re-usable, some are not. + * + * If you are uncertain, declare a new buffer. + */ +/* + * A buffer to send a CMD_IEEE_RX and to subsequently monitor its status + * Do not use this buffer for any commands other than CMD_IEEE_RX + */ +static uint8_t cmd_ieee_rx_buf[RF_CMD_BUFFER_SIZE] CC_ALIGN_ATTR(4); + +/* + * A buffer used to send immediate and foreground Radio Op (e.g. CMD_IEEE_TX) + * commands. + * + * Do not re-use this buffer to send a command before the previous command + * has been completed. + * + * Do not intermingle preparation of this buffer to send a command with calls + * that might lead to a different command, since the latter will overwrite what + * you have written in preparation for the former. + */ +static uint8_t cmd_immediate_buf[RF_CMD_BUFFER_SIZE] CC_ALIGN_ATTR(4); +/*---------------------------------------------------------------------------*/ +/* BLE macros, variables and buffers */ + +/* BLE Intervals: Send a burst of advertisements every BLE_ADV_INTERVAL secs */ +#define BLE_ADV_INTERVAL (CLOCK_SECOND * 5) +#define BLE_ADV_DUTY_CYCLE (CLOCK_SECOND / 10) +#define BLE_ADV_MESSAGES 10 + +/* BLE Advertisement-related macros */ +#define BLE_ADV_TYPE_DEVINFO 0x01 +#define BLE_ADV_TYPE_NAME 0x09 +#define BLE_ADV_TYPE_MANUFACTURER 0xFF +#define BLE_ADV_NAME_BUF_LEN 32 +#define BLE_ADV_PAYLOAD_BUF_LEN 64 +#define BLE_UUID_SIZE 16 + +#if CC26XX_RF_BLE_SUPPORT +/* BLE buffers / variables */ +static unsigned char ble_cmd_buf[32] CC_ALIGN_ATTR(4) = { 0 }; +static unsigned char ble_tx_rx_buf[128] CC_ALIGN_ATTR(4); +static uint8_t ble_mode_on; + +/* BLE beacond config */ +static struct ble_beacond_config { + clock_time_t interval; + char adv_name[BLE_ADV_NAME_BUF_LEN]; +} beacond_config; + +/* BLE overrides */ +static uint32_t ble_overrides[] = { + 0x00364038, /* Synth: Set RTRIM (POTAILRESTRIM) to 6 */ + 0x000784A3, /* Synth: Set FREF = 3.43 MHz (24 MHz / 7) */ + 0xA47E0583, /* Synth: Set loop bandwidth after lock to 80 kHz (K2) */ + 0xEAE00603, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, LSB) */ + 0x00010623, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, MSB) */ + 0x00456088, /* Adjust AGC reference level */ + 0xFFFFFFFF, /* End of override list */ +}; + +PROCESS(cc26xx_rf_ble_beacon_process, "CC26xx RF BLE Beacon Process"); + +static void init_ble(void); +#else +#define init_ble(...) +#endif /* CC26XX_RF_BLE_SUPPORT */ +/*---------------------------------------------------------------------------*/ +#define RX_BUF_SIZE 140 +/* Four receive buffers entries with room for 1 IEEE802.15.4 frame in each */ +static uint8_t rx_buf_0[RX_BUF_SIZE] CC_ALIGN_ATTR(4); +static uint8_t rx_buf_1[RX_BUF_SIZE] CC_ALIGN_ATTR(4); +static uint8_t rx_buf_2[RX_BUF_SIZE] CC_ALIGN_ATTR(4); +static uint8_t rx_buf_3[RX_BUF_SIZE] CC_ALIGN_ATTR(4); + +/* The RX Data Queue */ +static dataQueue_t rx_data_queue = { 0 }; + +/* Receive entry pointer to keep track of read items */ +volatile static uint8_t *rx_read_entry; +/*---------------------------------------------------------------------------*/ +/* The outgoing frame buffer */ +#define TX_BUF_SIZE 180 + +static uint8_t tx_buf[TX_BUF_SIZE]; +/*---------------------------------------------------------------------------*/ +/* Overrides for IEEE 802.15.4, differential mode */ +static uint32_t ieee_overrides[] = { + 0x00354038, /* Synth: Set RTRIM (POTAILRESTRIM) to 5 */ + 0x4001402D, /* Synth: Correct CKVD latency setting (address) */ + 0x00608402, /* Synth: Correct CKVD latency setting (value) */ + 0x4001405D, /* Synth: Set ANADIV DIV_BIAS_MODE to PG1 (address) */ + 0x1801F800, /* Synth: Set ANADIV DIV_BIAS_MODE to PG1 (value) */ + 0x000784A3, /* Synth: Set FREF = 3.43 MHz (24 MHz / 7) */ + 0xA47E0583, /* Synth: Set loop bandwidth after lock to 80 kHz (K2) */ + 0xEAE00603, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, LSB) */ + 0x00010623, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, MSB) */ + 0x002B50DC, /* Adjust AGC DC filter */ + 0x05000243, /* Increase synth programming timeout */ + 0x002082C3, /* Increase synth programming timeout */ + 0xFFFFFFFF, /* End of override list */ +}; +/*---------------------------------------------------------------------------*/ +PROCESS(cc26xx_rf_process, "CC26xx RF driver"); +/*---------------------------------------------------------------------------*/ +static int on(void); +static int off(void); +static void setup_interrupts(void); +/*---------------------------------------------------------------------------*/ +/* Select the HF XOSC as the source for the HF clock, but don't switch yet */ +static void +request_hf_xosc(void) +{ + /* Enable OSC DIG interface to change clock sources */ + ti_lib_osc_interface_enable(); + + /* Make sure the SMPH clock within AUX is enabled */ + ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK); + while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY); + + if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_XOSC_HF) { + /* + * Request to switch to the crystal to enable radio operation. It takes a + * while for the XTAL to be ready so instead of performing the actual + * switch, we return and we do other stuff while the XOSC is getting ready. + */ + ti_lib_osc_clock_source_set(OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_XOSC_HF); + } + + /* Disable OSC DIG interface */ + ti_lib_osc_interface_disable(); +} +/*---------------------------------------------------------------------------*/ +/* + * Switch to the XOSC. This will block until the XOSC is ready, so this must + * be preceded by a call to select_hf_xosc() + */ +static void +switch_to_hf_xosc(void) +{ + /* Enable OSC DIG interface to change clock sources */ + ti_lib_osc_interface_enable(); + + /* Make sure the SMPH clock within AUX is enabled */ + ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK); + while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY); + + if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_XOSC_HF) { + /* Switch the HF clock source (cc26xxware executes this from ROM) */ + ti_lib_osc_hf_source_switch(); + } + + /* Disable OSC DIG interface */ + ti_lib_osc_interface_disable(); +} +/*---------------------------------------------------------------------------*/ +static void +switch_to_hf_rc_osc(void) +{ + /* Enable OSC DIG interface to change clock sources */ + ti_lib_osc_interface_enable(); + + /* Make sure the SMPH clock within AUX is enabled */ + ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK); + while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY); + + /* Set all clock sources to the HF RC Osc */ + ti_lib_osc_clock_source_set(OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_RCOSC_HF); + + /* Check to not enable HF RC oscillator if already enabled */ + if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_HF) != OSC_RCOSC_HF) { + /* Switch the HF clock source (cc26xxware executes this from ROM) */ + ti_lib_osc_hf_source_switch(); + } + ti_lib_osc_interface_disable(); +} +/*---------------------------------------------------------------------------*/ +static uint8_t +rf_is_accessible(void) +{ + if(ti_lib_prcm_rf_ready() && + ti_lib_prcm_power_domain_status(PRCM_DOMAIN_RFCORE) == + PRCM_DOMAIN_POWER_ON) { + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Sends a command to the RF core. + * + * \param cmd The command value or a pointer to a command buffer + * \param status A pointer to a variable which will hold the status + * \return RF_CMD_OK or RF_CMD_ERROR + * + * This function supports all three types of command (Radio OP, immediate and + * direct) + * + * For immediate and Radio OPs, cmd is a pointer to the data structure + * containing the command and its parameters. This data structure must be + * 4-byte aligned. + * + * For direct commands, cmd contains the value of the command alongside its + * parameters + * + * The caller is responsible of allocating and populating cmd for Radio OP and + * immediate commands + * + * The caller is responsible for allocating status + */ +static uint_fast8_t +rf_send_cmd(uint32_t cmd, uint32_t *status) +{ + uint32_t timeout_count = 0; + + /* + * Make sure ContikiMAC doesn't turn us off from within an interrupt while + * we are accessing RF Core registers + */ + ti_lib_int_master_disable(); + + if(!rf_is_accessible()) { + PRINTF("rf_send_cmd: RF was off\n"); + ti_lib_int_master_enable(); + return RF_CMD_ERROR; + } + + HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = cmd; + do { + *status = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDSTA); + if(++timeout_count > 50000) { + PRINTF("rf_send_cmd: Timeout\n"); + ti_lib_int_master_enable(); + return RF_CMD_ERROR; + } + } while(*status == RF_CMD_STATUS_PENDING); + + ti_lib_int_master_enable(); + + /* + * If we reach here the command is no longer pending. It is either completed + * successfully or with error + */ + return *status == RF_CMD_STATUS_DONE; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Checks whether the RFC domain is accessible and the RFC is in IEEE RX + * \return 1: RFC in RX mode (and therefore accessible too). 0 otherwise + */ +static uint8_t +rf_is_on(void) +{ + if(!rf_is_accessible()) { + return 0; + } + + return RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) == RF_RADIO_OP_STATUS_ACTIVE; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Check the RF's TX status + * \return 1 RF is transmitting + * \return 0 RF is not transmitting + * + * TX mode may be triggered either by a CMD_IEEE_TX or by the automatic + * transmission of an ACK frame. + */ +static uint8_t +transmitting(void) +{ + uint32_t cmd_status; + + /* If we are off, we are not in TX */ + if(!rf_is_accessible()) { + return 0; + } + + memset(cmd_immediate_buf, 0x00, SIZEOF_STRUCT(CMD_IEEE_CCA_REQ)); + GET_FIELD(cmd_immediate_buf, command, commandNo) = CMD_IEEE_CCA_REQ; + + if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) == RF_CMD_ERROR) { + PRINTF("transmitting: CMDSTA=0x%08lx, status=0x%04x\n", + cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf)); + return 0; + } + + if((GET_FIELD(cmd_immediate_buf, CMD_IEEE_CCA_REQ, currentRssi) + == RF_CMD_CCA_REQ_RSSI_UNKNOWN) && + (GET_BITFIELD(cmd_immediate_buf, CMD_IEEE_CCA_REQ, ccaInfo, ccaEnergy) + == RF_CMD_CCA_REQ_CCA_STATE_BUSY)) { + return 1; + } + + return 0; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns CCA information + * \return RF_GET_CCA_INFO_ERROR if the RF was not on + * \return On success, the return value is formatted as per the ccaInfo field + * of CMD_IEEE_CCA_REQ + * + * It is the caller's responsibility to make sure the RF is on. This function + * will return RF_GET_CCA_INFO_ERROR if the RF is off + * + * This function will in fact wait for a valid RSSI signal + */ +static uint8_t +get_cca_info(void) +{ + uint32_t cmd_status; + int8_t rssi; + + if(!rf_is_on()) { + PRINTF("get_cca_info: Not on\n"); + return RF_GET_CCA_INFO_ERROR; + } + + rssi = RF_CMD_CCA_REQ_RSSI_UNKNOWN; + + while(rssi == RF_CMD_CCA_REQ_RSSI_UNKNOWN || rssi == 0) { + memset(cmd_immediate_buf, 0x00, SIZEOF_STRUCT(CMD_IEEE_CCA_REQ)); + GET_FIELD(cmd_immediate_buf, command, commandNo) = CMD_IEEE_CCA_REQ; + + if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) == RF_CMD_ERROR) { + PRINTF("get_cca_info: CMDSTA=0x%08lx, status=0x%04x\n", + cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf)); + + return RF_GET_CCA_INFO_ERROR; + } + + rssi = GET_FIELD(cmd_immediate_buf, CMD_IEEE_CCA_REQ, currentRssi); + } + + /* We have a valid RSSI signal. Return the CCA Info */ + return GET_FIELD(cmd_immediate_buf, CMD_IEEE_CCA_REQ, ccaInfo); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Reads the current signal strength (RSSI) + * \return The current RSSI in dBm or CMD_GET_RSSI_UNKNOWN + * + * This function reads the current RSSI on the currently configured + * channel. + */ +static radio_value_t +get_rssi(void) +{ + uint32_t cmd_status; + int8_t rssi; + uint8_t was_off = 0; + + /* If we are off, turn on first */ + if(!rf_is_on()) { + was_off = 1; + if(on() != RF_CMD_OK) { + PRINTF("get_rssi: on() failed\n"); + return RF_CMD_CCA_REQ_RSSI_UNKNOWN; + } + } + + memset(cmd_immediate_buf, 0x00, SIZEOF_STRUCT(CMD_GET_RSSI)); + GET_FIELD(cmd_immediate_buf, command, commandNo) = CMD_GET_RSSI; + + if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) == RF_CMD_ERROR) { + rssi = RF_CMD_CCA_REQ_RSSI_UNKNOWN; + } + + /* Current RSSI in bits 23:16 of cmd_status */ + rssi = (cmd_status >> 16) & 0xFF; + + /* If we were off, turn back off */ + if(was_off) { + off(); + } + + return rssi; +} +/*---------------------------------------------------------------------------*/ +/* Returns the current TX power in dBm */ +static radio_value_t +get_tx_power(void) +{ + return tx_power_current->dbm; +} +/*---------------------------------------------------------------------------*/ +/* + * Set TX power to 'at least' power dBm + * This works with a lookup table. If the value of 'power' does not exist in + * the lookup table, TXPOWER will be set to the immediately higher available + * value + */ +static void +set_tx_power(radio_value_t power) +{ + uint32_t cmd_status; + int i; + + /* Send a CMD_SET_TX_POWER command to the RF */ + memset(cmd_immediate_buf, 0x00, SIZEOF_STRUCT(CMD_SET_TX_POWER)); + + GET_FIELD(cmd_immediate_buf, command, commandNo) = CMD_SET_TX_POWER; + + for(i = OUTPUT_CONFIG_COUNT - 1; i >= 0; --i) { + if(power <= output_power[i].dbm) { + GET_FIELD(cmd_immediate_buf, CMD_SET_TX_POWER, txPower) = + BITVALUE(CMD_SET_TX_POWER, txPower, IB, output_power[i].register_ib) | + BITVALUE(CMD_SET_TX_POWER, txPower, GC, output_power[i].register_gc) | + BITVALUE(CMD_SET_TX_POWER, txPower, tempCoeff, 0); + + if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) == RF_CMD_OK) { + /* Success: Remember the new setting */ + tx_power_current = &output_power[i]; + } else { + PRINTF("set_tx_power: CMDSTA=0x%08lx\n", cmd_status); + } + return; + } + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Wait till running radio Op command completes + * + * \return RF_CMD_ERROR or RF_CMD_OK + * + * RF_CMD_OK will be returned if the Radio Op returned with + * RF_RADIO_OP_STATUS_DONE_OK + * + * RF_CMD_ERROR will be returned in the radio op returned with any other + * RF_RADIO_OP_STATUS_DONE_xyz + */ +static uint_fast8_t +rf_wait_cmd_completed_ok(uint8_t *cmd) +{ + _TYPE_radioOp_status tmp_status; + uint32_t timeoutCount = 0; + + /* + * 0x04XX=DONE, 0x0400=DONE_OK while all other "DONE" values means done + * but with some kind of error (ref. "Common radio operation status codes") + */ + do { + tmp_status = GET_FIELD_V(cmd, radioOp, status); + if(++timeoutCount > 500000) { + return RF_CMD_ERROR; + } + } while((tmp_status & RF_RADIO_OP_MASKED_STATUS) != RF_RADIO_OP_MASKED_STATUS_DONE); + + return tmp_status == RF_RADIO_OP_STATUS_DONE_OK; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Builds common radio parameters for radio operations + * + * \param *cmd Pointer to buffer to add parameters to + * \param command Radio command number (e.g. COMMAND_RADIO_SETUP) + * + * \note The buffer must be emptied with memset() before calling this function + * + * \return None + */ +static void +rf_build_radio_op_cmd(uint8_t *cmd, uint16_t command) +{ + GET_FIELD(cmd, radioOp, commandNo) = command; + GET_FIELD(cmd, radioOp, status) = IDLE; + GET_FIELD(cmd, radioOp, pNextOp) = NULL; + GET_FIELD(cmd, radioOp, startTime) = 0; + GET_FIELD(cmd, radioOp, startTrigger) = TRIG_NOW; + GET_FIELD(cmd, radioOp, condition) = COND_NEVER; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Sends a CMD_RADIO_SETUP for the selected mode (IEEE or BLE) + * \param mode RF_MODE_BLE or RF_MODE_IEEE + * \return RF_CMD_OK or RF_CMD_ERROR + * + * ToDo: Likely to need one more argument to set bNoAdi on first startup + * vs when coming back from sleep + */ +static uint8_t +rf_radio_setup(uint8_t mode) +{ + uint32_t cmd_status; + + /* Create radio setup command */ + memset(cmd_immediate_buf, 0x00, SIZEOF_RADIO_OP(CMD_RADIO_SETUP)); + rf_build_radio_op_cmd(cmd_immediate_buf, CMD_RADIO_SETUP); + + /* Set output power to the current (or default) value */ + GET_FIELD(cmd_immediate_buf, CMD_RADIO_SETUP, txPower) = + BITVALUE(CMD_RADIO_SETUP, txPower, IB, tx_power_current->register_ib) | + BITVALUE(CMD_RADIO_SETUP, txPower, GC, tx_power_current->register_gc) | + BITVALUE(CMD_RADIO_SETUP, txPower, tempCoeff, 0); + + /* Do mode-dependent things (e.g. apply overrides) */ + if(mode == RF_MODE_IEEE) { + /* Add override control pointer */ + GET_FIELD(cmd_immediate_buf, CMD_RADIO_SETUP, pRegOverride) = ieee_overrides; +#if CC26XX_RF_BLE_SUPPORT + } else if(mode == RF_MODE_BLE) { + /* Add override control pointer */ + GET_FIELD(cmd_immediate_buf, CMD_RADIO_SETUP, pRegOverride) = ble_overrides; +#endif + } else { + PRINTF("rf_radio_setup: Unknown mode %u\n", mode); + return RF_CMD_ERROR; + } + + /* Lastly, set the mode */ + GET_FIELD(cmd_immediate_buf, CMD_RADIO_SETUP, mode) = mode; + + /* Send Radio setup to RF Core */ + if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) != RF_CMD_OK) { + PRINTF("rf_radio_setup: CMD_RADIO_SETUP, CMDSTA=0x%08lx, status=0x%04x\n", + cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf)); + return RF_CMD_ERROR; + } + + /* Wait until radio setup is done */ + if(rf_wait_cmd_completed_ok(cmd_immediate_buf) != RF_CMD_OK) { + PRINTF("rf_radio_setup: CMD_RADIO_SETUP wait, CMDSTA=0x%08lx, status=0x%04x\n", + cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf)); + return RF_CMD_ERROR; + } + + return RF_CMD_OK; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Applies patches (if any) + * \return RF_CMD_OK or RF_CMD_ERROR + * + * Currently patches are not required. + */ +static uint8_t +apply_patches() +{ + uint32_t cmd_status; + + /* Patch of uninitialized pointer */ + *((uint32_t *)0x21000028) = 0x00000000; + + /* Start radio timer (RAT) */ + if(rf_send_cmd(CMDR_DIR_CMD(CMD_START_RAT), &cmd_status) != RF_CMD_OK) { + PRINTF("apply_patches: START_RAT fail, CMDSTA=0x%08lx\n", cmd_status); + return RF_CMD_ERROR; + } + + return RF_CMD_OK; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Set up radio in IEEE802.15.4 RX mode + * + * \return RF_CMD_OK Succeeded + * \return RF_CMD_ERROR Failed + * + * This function assumes that cmd_ieee_rx_buf has been previously populated + * with correct values. This can be done through init_rf_params (sets defaults) + * or through Contiki's extended RF API (set_value, set_object) + */ +static uint8_t +rf_cmd_ieee_rx() +{ + uint32_t cmd_status; + rtimer_clock_t t0; + int ret; + + ret = rf_send_cmd((uint32_t)cmd_ieee_rx_buf, &cmd_status); + + if(ret != RF_CMD_OK) { + PRINTF("rf_cmd_ieee_rx: ret=%d, CMDSTA=0x%08lx, status=0x%04x\n", + ret, cmd_status, RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf)); + } + + t0 = RTIMER_NOW(); + + while(RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) != RF_RADIO_OP_STATUS_ACTIVE && + (RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + ENTER_RX_WAIT_TIMEOUT))); + + /* Wait to enter RX */ + if(RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) != RF_RADIO_OP_STATUS_ACTIVE) { + PRINTF("rf_cmd_ieee_rx: CMDSTA=0x%08lx, status=0x%04x\n", + cmd_status, RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf)); + return RF_CMD_ERROR; + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +static void +init_rx_buffers(void) +{ + /* Two-element circular buffer, hardcoded for now.. */ + GET_FIELD(rx_buf_0, dataEntry, pNextEntry) = rx_buf_1; + GET_FIELD(rx_buf_0, dataEntry, config) = 0x04; + GET_FIELD(rx_buf_0, dataEntry, length) = sizeof(rx_buf_0) - 8; + + GET_FIELD(rx_buf_1, dataEntry, pNextEntry) = rx_buf_2; + GET_FIELD(rx_buf_1, dataEntry, config) = 0x04; + GET_FIELD(rx_buf_1, dataEntry, length) = sizeof(rx_buf_1) - 8; + + GET_FIELD(rx_buf_2, dataEntry, pNextEntry) = rx_buf_3; + GET_FIELD(rx_buf_2, dataEntry, config) = 0x04; + GET_FIELD(rx_buf_2, dataEntry, length) = sizeof(rx_buf_2) - 8; + + /* Point to first element again */ + GET_FIELD(rx_buf_3, dataEntry, pNextEntry) = rx_buf_0; + GET_FIELD(rx_buf_3, dataEntry, config) = 0x04; + GET_FIELD(rx_buf_3, dataEntry, length) = sizeof(rx_buf_3) - 8; +} +/*---------------------------------------------------------------------------*/ +static void +init_rf_params(void) +{ + memset(cmd_ieee_rx_buf, 0x00, SIZEOF_RADIO_OP(CMD_IEEE_RX)); + + GET_FIELD(cmd_ieee_rx_buf, radioOp, commandNo) = CMD_IEEE_RX; + GET_FIELD(cmd_ieee_rx_buf, radioOp, status) = IDLE; + GET_FIELD(cmd_ieee_rx_buf, radioOp, pNextOp) = NULL; + GET_FIELD(cmd_ieee_rx_buf, radioOp, startTime) = 0x00000000; + GET_FIELD(cmd_ieee_rx_buf, radioOp, startTrigger) = TRIG_NOW; + GET_FIELD(cmd_ieee_rx_buf, radioOp, condition) = COND_NEVER; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, channel) = CC26XX_RF_CHANNEL; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, rxConfig) = + BITVALUE(CMD_IEEE_RX, rxConfig, bAutoFlushCrc, 0) | + BITVALUE(CMD_IEEE_RX, rxConfig, bAutoFlushIgn, 0) | + BITVALUE(CMD_IEEE_RX, rxConfig, bIncludePhyHdr, 0) | + BITVALUE(CMD_IEEE_RX, rxConfig, bIncludeCrc, 1) | + BITVALUE(CMD_IEEE_RX, rxConfig, bAppendRssi, 1) | + BITVALUE(CMD_IEEE_RX, rxConfig, bAppendCorrCrc, 1) | + BITVALUE(CMD_IEEE_RX, rxConfig, bAppendSrcInd, 0) | + BITVALUE(CMD_IEEE_RX, rxConfig, bAppendTimestamp, 0); + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, pRxQ) = &rx_data_queue; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, pOutput) = rf_stats; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameFiltOpt) = +#if CC26XX_RF_CONF_PROMISCOUS + BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltEn, 0) | +#else + BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltEn, 1) | +#endif + BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltStop, 1) | +#if CC26XX_RF_CONF_AUTOACK + BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoAckEn, 1) | +#else + BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoAckEn, 0) | +#endif + BITVALUE(CMD_IEEE_RX, frameFiltOpt, slottedAckEn, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoPendEn, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, defaultPend, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, bPendDataReqOnly, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, bPanCoord, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, bStrictLenFilter, 0); + /* Receive all frame types */ + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameTypes) = + BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt0Beacon, 1) | + BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt1Data, 1) | + BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt2Ack, 1) | + BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt3MacCmd, 1) | + BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt4Reserved, 1) | + BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt5Reserved, 1) | + BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt6Reserved, 1) | + BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt7Reserved, 1); + /* Configure CCA settings */ + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, ccaOpt) = + BITVALUE(CMD_IEEE_RX, ccaOpt, ccaEnEnergy, 1) | + BITVALUE(CMD_IEEE_RX, ccaOpt, ccaEnCorr, 0) | + BITVALUE(CMD_IEEE_RX, ccaOpt, ccaEnSync, 0) | + BITVALUE(CMD_IEEE_RX, ccaOpt, ccaCorrOp, 0) | + BITVALUE(CMD_IEEE_RX, ccaOpt, ccaSyncOp, 0); + /* Set CCA RSSI Threshold, 0xA6 corresponds to -90dBm (two's comp.)*/ + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, ccaRssiThr) = 0xA6; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, numExtEntries) = 0x00; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, numShortEntries) = 0x00; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, pExtEntryList) = 0; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, pShortEntryList) = 0; + + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, endTrigger) = TRIG_NEVER; + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, endTime) = 0x00000000; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Turn on power to the RFC and boot it. + * + * \return RF_CMD_OK or RF_CMD_ERROR + */ +static int +power_up(void) +{ + uint32_t cmd_status; + + ti_lib_int_master_disable(); + ti_lib_int_pend_clear(INT_RF_CPE0); + ti_lib_int_pend_clear(INT_RF_CPE1); + ti_lib_int_disable(INT_RF_CPE0); + ti_lib_int_disable(INT_RF_CPE1); + + /* Enable RF Core power domain */ + ti_lib_prcm_power_domain_on(PRCM_DOMAIN_RFCORE); + while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_RFCORE) + != PRCM_DOMAIN_POWER_ON); + + ti_lib_prcm_domain_enable(PRCM_DOMAIN_RFCORE); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + while(!rf_is_accessible()) { + PRINTF("power_up: Not ready\n"); + } + + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0; + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0; + ti_lib_int_enable(INT_RF_CPE0); + ti_lib_int_enable(INT_RF_CPE1); + ti_lib_int_master_enable(); + + /* Let CPE boot */ + HWREG(RFC_PWR_NONBUF_BASE + RFC_PWR_O_PWMCLKEN) = RF_CORE_CLOCKS_MASK; + + /* Send ping (to verify RFCore is ready and alive) */ + if(rf_send_cmd(CMDR_DIR_CMD(CMD_PING), &cmd_status) != RF_CMD_OK) { + PRINTF("power_up: CMD_PING fail, CMDSTA=0x%08lx\n", cmd_status); + return RF_CMD_ERROR; + } + + return RF_CMD_OK; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Disable RFCORE clock domain in the MCU VD and turn off the RFCORE PD + */ +static void +power_down(void) +{ + ti_lib_int_master_disable(); + ti_lib_int_disable(INT_RF_CPE0); + ti_lib_int_disable(INT_RF_CPE1); + + if(rf_is_accessible()) { + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0; + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0; + } + + /* Shut down the RFCORE clock domain in the MCU VD */ + ti_lib_prcm_domain_disable(PRCM_DOMAIN_RFCORE); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* Turn off RFCORE PD */ + ti_lib_prcm_power_domain_off(PRCM_DOMAIN_RFCORE); + while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_RFCORE) + != PRCM_DOMAIN_POWER_OFF); + + ti_lib_int_pend_clear(INT_RF_CPE0); + ti_lib_int_pend_clear(INT_RF_CPE1); + ti_lib_int_enable(INT_RF_CPE0); + ti_lib_int_enable(INT_RF_CPE1); + ti_lib_int_master_enable(); +} +/*---------------------------------------------------------------------------*/ +static int +rx_on(void) +{ + int ret; + + /* Get status of running IEEE_RX (if any) */ + if(rf_is_on()) { + PRINTF("rx_on: We were on. PD=%u, RX=0x%04x \n", rf_is_accessible(), + RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf)); + return RF_CMD_OK; + } + + /* Put CPE in RX using the currently configured parameters */ + ret = rf_cmd_ieee_rx(); + + if(ret) { + ENERGEST_ON(ENERGEST_TYPE_LISTEN); + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +static int +rx_off(void) +{ + uint32_t cmd_status; + int ret; + + /* If we are off, do nothing */ + if(!rf_is_on()) { + return RF_CMD_OK; + } + + /* Wait for ongoing ACK TX to finish */ + while(transmitting()); + + /* Send a CMD_STOP command to RF Core */ + if(rf_send_cmd(CMDR_DIR_CMD(CMD_ABORT), &cmd_status) != RF_CMD_OK) { + PRINTF("RX off: CMD_ABORT status=0x%08lx\n", cmd_status); + /* Continue nonetheless */ + } + + while(rf_is_on()); + + if(RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) == IEEE_DONE_STOPPED || + RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) == IEEE_DONE_ABORT) { + /* Stopped gracefully */ + ENERGEST_OFF(ENERGEST_TYPE_LISTEN); + ret = RF_CMD_OK; + } else { + PRINTF("RX off: BG status=0x%04x\n", RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf)); + ret = RF_CMD_ERROR; + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +static void +rx_isr(void) +{ + process_poll(&cc26xx_rf_process); +} +/*---------------------------------------------------------------------------*/ +void +cc26xx_rf_cpe1_isr(void) +{ + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + ti_lib_int_master_disable(); + PRINTF("RF Error\n"); + + if(!rf_is_accessible()) { + if(power_up() != RF_CMD_OK) { + return; + } + } + + /* Clear interrupt flags */ + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0; + ti_lib_int_master_enable(); + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); +} +/*---------------------------------------------------------------------------*/ +void +cc26xx_rf_cpe0_isr(void) +{ + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + if(!rf_is_accessible()) { + printf("RF ISR called but RF not ready... PANIC!!\n"); + if(power_up() != RF_CMD_OK) { + PRINTF("power_up() failed\n"); + return; + } + } + + ti_lib_int_master_disable(); + if(HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & RX_IRQ) { + rx_isr(); + } + + /* Clear interrupt flags */ + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0; + ti_lib_int_master_enable(); + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); +} +/*---------------------------------------------------------------------------*/ +static void +setup_interrupts(void) +{ + /* We are already turned on by the caller, so this should not happen */ + if(!rf_is_accessible()) { + PRINTF("setup_interrupts: No access\n"); + return; + } + + /* Disable interrupts */ + ti_lib_int_master_disable(); + + /* Set all interrupt channels to CPE0 channel, error to CPE1 */ + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEISL) = ERROR_IRQ; + + /* Acknowledge TX_Frame, Rx_Entry_Done and ERROR */ + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = ENABLED_IRQS; + + /* Clear interrupt flags, active low clear(?) */ + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0; + + ti_lib_int_pend_clear(INT_RF_CPE0); + ti_lib_int_pend_clear(INT_RF_CPE1); + ti_lib_int_enable(INT_RF_CPE0); + ti_lib_int_enable(INT_RF_CPE1); + ti_lib_int_master_enable(); +} +/*---------------------------------------------------------------------------*/ +static uint8_t +request(void) +{ + /* + * We rely on the RDC layer to turn us on and off. Thus, if we are on we + * will only allow sleep, standby otherwise + */ + if(rf_is_on()) { + return LPM_MODE_SLEEP; + } + + return LPM_MODE_MAX_SUPPORTED; +} +/*---------------------------------------------------------------------------*/ +LPM_MODULE(cc26xx_rf_lpm_module, request, NULL, NULL); +/*---------------------------------------------------------------------------*/ +static int +init(void) +{ + lpm_register_module(&cc26xx_rf_lpm_module); + + /* Enable IEEE, BLE and Common-CMD APIs */ + HWREG(PRCM_BASE + PRCM_O_RFCMODESEL) = PRCM_RFCMODESEL_CURR_MODE5; + + /* Wipe out the BLE adv buffer */ + init_ble(); + + /* Initialise RX buffers */ + memset(rx_buf_0, 0, RX_BUF_SIZE); + memset(rx_buf_1, 0, RX_BUF_SIZE); + memset(rx_buf_2, 0, RX_BUF_SIZE); + memset(rx_buf_3, 0, RX_BUF_SIZE); + + /* Set of RF Core data queue. Circular buffer, no last entry */ + rx_data_queue.pCurrEntry = rx_buf_0; + + rx_data_queue.pLastEntry = NULL; + + /* Initialize current read pointer to first element (used in ISR) */ + rx_read_entry = rx_buf_0; + + /* Populate the RF parameters data structure with default values */ + init_rf_params(); + + if(on() != RF_CMD_OK) { + PRINTF("init: on() failed\n"); + return RF_CMD_ERROR; + } + + ENERGEST_ON(ENERGEST_TYPE_LISTEN); + + process_start(&cc26xx_rf_process, NULL); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +prepare(const void *payload, unsigned short payload_len) +{ + int len = MIN(payload_len, sizeof(tx_buf)); + + memcpy(tx_buf, payload, len); + return RF_CMD_OK; +} +/*---------------------------------------------------------------------------*/ +static int +transmit(unsigned short transmit_len) +{ + int ret; + uint8_t was_off = 0; + uint32_t cmd_status; + uint16_t stat; + uint8_t tx_active = 0; + rtimer_clock_t t0; + + if(!rf_is_on()) { + was_off = 1; + if(on() != RF_CMD_OK) { + PRINTF("transmit: on() failed\n"); + return RF_CMD_ERROR; + } + } + + /* + * We are certainly not TXing a frame as a result of CMD_IEEE_TX, but we may + * be in the process of TXing an ACK. In that case, wait for the TX to finish + * or return after approx TX_WAIT_TIMEOUT + */ + t0 = RTIMER_NOW(); + + do { + tx_active = transmitting(); + } while(tx_active == 1 && + (RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + TX_WAIT_TIMEOUT))); + + if(tx_active) { + PRINTF("transmit: Already TXing and wait timed out\n"); + + if(was_off) { + off(); + } + + return RADIO_TX_COLLISION; + } + + /* Send the CMD_IEEE_TX command */ + memset(cmd_immediate_buf, 0, SIZEOF_RADIO_OP(CMD_IEEE_TX)); + + rf_build_radio_op_cmd(cmd_immediate_buf, CMD_IEEE_TX); + + GET_FIELD(cmd_immediate_buf, CMD_IEEE_TX, payloadLen) = transmit_len; + GET_FIELD(cmd_immediate_buf, CMD_IEEE_TX, pPayload) = tx_buf; + + ret = rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status); + + if(ret) { + /* If we enter here, TX actually started */ + ENERGEST_OFF(ENERGEST_TYPE_LISTEN); + ENERGEST_ON(ENERGEST_TYPE_TRANSMIT); + + /* Idle away while the command is running */ + while((RF_RADIO_OP_GET_STATUS(cmd_immediate_buf) & RF_RADIO_OP_MASKED_STATUS) + == RF_RADIO_OP_MASKED_STATUS_RUNNING) { + lpm_sleep(); + } + + stat = RF_RADIO_OP_GET_STATUS(cmd_immediate_buf); + + if(stat == RF_RADIO_OP_STATUS_IEEE_DONE_OK) { + /* Sent OK */ + RIMESTATS_ADD(lltx); + ret = RADIO_TX_OK; + } else { + /* Operation completed, but frame was not sent */ + PRINTF("transmit: ret=%d, CMDSTA=0x%08lx, status=0x%04x\n", ret, + cmd_status, stat); + ret = RADIO_TX_ERR; + } + } else { + /* Failure sending the CMD_IEEE_TX command */ + PRINTF("transmit: ret=%d, CMDSTA=0x%08lx, status=0x%04x\n", + ret, cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf)); + + ret = RADIO_TX_ERR; + } + + /* + * Update ENERGEST state here, before a potential call to off(), which + * will correctly update it if required. + */ + ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT); + ENERGEST_ON(ENERGEST_TYPE_LISTEN); + + if(was_off) { + off(); + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +static int +send(const void *payload, unsigned short payload_len) +{ + prepare(payload, payload_len); + return transmit(payload_len); +} +/*---------------------------------------------------------------------------*/ +static int +read_frame(void *buf, unsigned short buf_len) +{ + int len = 0; + + if(GET_FIELD_V(rx_read_entry, dataEntry, status) == DATA_ENTRY_STATUS_FINISHED) { + /* Set status to 0 "Pending" in element */ + GET_FIELD_V(rx_read_entry, dataEntry, status) = DATA_ENTRY_STATUS_PENDING; + + if(rx_read_entry[8] > 0) { + memcpy(buf, (char *)&rx_read_entry[9], buf_len); + + /* Remove the footer */ + len = MIN(buf_len, rx_read_entry[8] - 4); + + int rssi = (int8_t)rx_read_entry[9 + len + 2]; + + packetbuf_set_attr(PACKETBUF_ATTR_RSSI, rssi); + RIMESTATS_ADD(llrx); + + /* Clear the length byte */ + rx_read_entry[8] = 0; + } + + /* Move read entry pointer to next entry */ + rx_read_entry = GET_FIELD_V(rx_read_entry, dataEntry, pNextEntry); + } + + return len; +} +/*---------------------------------------------------------------------------*/ +static int +channel_clear(void) +{ + uint8_t was_off = 0; + uint8_t cca_info; + int ret = RF_CCA_CLEAR; + + /* + * If we are in the middle of a BLE operation, we got called by ContikiMAC + * from within an interrupt context. Indicate a clear channel + */ +#if CC26XX_RF_BLE_SUPPORT + if(ble_mode_on) { + PRINTF("channel_clear: Interrupt context but BLE in progress\n"); + return RF_CCA_CLEAR; + } +#endif + + if(rf_is_on()) { + /* + * Wait for potential leftover ACK still being sent. + * Strictly speaking, if we are TXing an ACK then the channel is not clear. + * However, channel_clear is only ever called to determine whether there is + * someone else's packet in the air, not ours. + * + * We could probably even simply return that the channel is clear + */ + while(transmitting()); + } else { + was_off = 1; + if(on() != RF_CMD_OK) { + PRINTF("channel_clear: on() failed\n"); + if(was_off) { + off(); + } + return RF_CCA_CLEAR; + } + } + + cca_info = get_cca_info(); + + if(cca_info == RF_GET_CCA_INFO_ERROR) { + PRINTF("channel_clear: CCA error\n"); + ret = RF_CCA_CLEAR; + } else { + /* + * cca_info bits 1:0 - ccaStatus + * Return 1 (clear) if idle or invalid. + */ + ret = (cca_info & 0x03) != RF_CMD_CCA_REQ_CCA_STATE_BUSY; + } + + if(was_off) { + off(); + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +static int +receiving_packet(void) +{ + int ret = 0; + uint8_t cca_info; + uint8_t was_off = 0; + + if(!rf_is_on()) { + was_off = 1; + if(on() != RF_CMD_OK) { + PRINTF("receiving_packet: on() failed\n"); + return RF_CMD_ERROR; + } + } + + /* + * If we are in the middle of a BLE operation, we got called by ContikiMAC + * from within an interrupt context. We are not receiving + */ +#if CC26XX_RF_BLE_SUPPORT + if(ble_mode_on) { + PRINTF("receiving_packet: Interrupt context but BLE in progress\n"); + return 0; + } +#endif + + /* If we are off, we are not receiving */ + if(!rf_is_on()) { + PRINTF("receiving_packet: We were off\n"); + return 0; + } + + /* If we are transmitting (can only be an ACK here), we are not receiving */ + if(transmitting()) { + PRINTF("receiving_packet: We were TXing\n"); + return 0; + } + + cca_info = get_cca_info(); + + if(cca_info == RF_GET_CCA_INFO_ERROR) { + /* If we can't read CCA info, return "not receiving" */ + ret = 0; + } else { + /* Return 1 (receiving) if ccaState is busy */ + ret = (cca_info & 0x03) == RF_CMD_CCA_REQ_CCA_STATE_BUSY; + } + + if(was_off) { + off(); + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +static int +pending_packet(void) +{ + volatile uint8_t *current = rx_data_queue.pCurrEntry; + int rv = 0; + + /* Go through all RX buffers and check their status */ + do { + if(GET_FIELD_V(current, dataEntry, status) == + DATA_ENTRY_STATUS_FINISHED) { + rv = 1; + process_poll(&cc26xx_rf_process); + } + + current = GET_FIELD_V(current, dataEntry, pNextEntry); + } while(current != rx_data_queue.pCurrEntry); + + /* If we didn't find an entry at status finished, no frames are pending */ + return rv; +} +/*---------------------------------------------------------------------------*/ +static int +on(void) +{ + /* + * Request the HF XOSC as the source for the HF clock. Needed before we can + * use the FS. This will only request, it will _not_ perform the switch. + */ + request_hf_xosc(); + + /* + * If we are in the middle of a BLE operation, we got called by ContikiMAC + * from within an interrupt context. Abort, but pretend everything is OK. + */ +#if CC26XX_RF_BLE_SUPPORT + if(ble_mode_on) { + PRINTF("on: Interrupt context but BLE in progress\n"); + return RF_CMD_OK; + } +#endif + + if(rf_is_on()) { + PRINTF("on: We were on. PD=%u, RX=0x%04x \n", rf_is_accessible(), + RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf)); + return RF_CMD_OK; + } + + if(power_up() != RF_CMD_OK) { + PRINTF("on: power_up() failed\n"); + return RF_CMD_ERROR; + } + + if(apply_patches() != RF_CMD_OK) { + PRINTF("on: apply_patches() failed\n"); + return RF_CMD_ERROR; + } + + init_rx_buffers(); + + setup_interrupts(); + + /* + * Trigger a switch to the XOSC, so that we can subsequently use the RF FS + * This will block until the XOSC is actually ready, but give how we + * requested it early on, this won't be too long a wait/ + */ + switch_to_hf_xosc(); + + if(rf_radio_setup(RF_MODE_IEEE) != RF_CMD_OK) { + PRINTF("on: radio_setup() failed\n"); + return RF_CMD_ERROR; + } + + return rx_on(); +} +/*---------------------------------------------------------------------------*/ +static int +off(void) +{ + /* + * If we are in the middle of a BLE operation, we got called by ContikiMAC + * from within an interrupt context. Abort, but pretend everything is OK. + */ +#if CC26XX_RF_BLE_SUPPORT + if(ble_mode_on) { + PRINTF("off: Interrupt context but BLE in progress\n"); + return RF_CMD_OK; + } +#endif + + while(transmitting()); + + power_down(); + + /* Switch HF clock source to the RCOSC to preserve power */ + switch_to_hf_rc_osc(); + + /* We pulled the plug, so we need to restore the status manually */ + GET_FIELD(cmd_ieee_rx_buf, radioOp, status) = IDLE; + + /* + * Just in case there was an ongoing RX (which started after we begun the + * shutdown sequence), we don't want to leave the buffer in state == ongoing + */ + GET_FIELD_V(rx_buf_0, dataEntry, status) = DATA_ENTRY_STATUS_PENDING; + GET_FIELD_V(rx_buf_1, dataEntry, status) = DATA_ENTRY_STATUS_PENDING; + GET_FIELD_V(rx_buf_2, dataEntry, status) = DATA_ENTRY_STATUS_PENDING; + GET_FIELD_V(rx_buf_3, dataEntry, status) = DATA_ENTRY_STATUS_PENDING; + + return RF_CMD_OK; +} +/*---------------------------------------------------------------------------*/ +static radio_result_t +get_value(radio_param_t param, radio_value_t *value) +{ + if(!value) { + return RADIO_RESULT_INVALID_VALUE; + } + + switch(param) { + case RADIO_PARAM_POWER_MODE: + /* On / off */ + *value = rf_is_on() ? RADIO_POWER_MODE_ON : RADIO_POWER_MODE_OFF; + return RADIO_RESULT_OK; + case RADIO_PARAM_CHANNEL: + *value = (radio_value_t)GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, channel); + return RADIO_RESULT_OK; + case RADIO_PARAM_PAN_ID: + *value = (radio_value_t)GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, localPanID); + return RADIO_RESULT_OK; + case RADIO_PARAM_16BIT_ADDR: + *value = (radio_value_t)GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, localShortAddr); + return RADIO_RESULT_OK; + case RADIO_PARAM_RX_MODE: + *value = 0; + if(GET_BITFIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameFiltOpt, frameFiltEn)) { + *value |= RADIO_RX_MODE_ADDRESS_FILTER; + } + if(GET_BITFIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameFiltOpt, autoAckEn)) { + *value |= RADIO_RX_MODE_AUTOACK; + } + + return RADIO_RESULT_OK; + case RADIO_PARAM_TXPOWER: + *value = get_tx_power(); + return RADIO_RESULT_OK; + case RADIO_PARAM_CCA_THRESHOLD: + *value = GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, ccaRssiThr); + return RADIO_RESULT_OK; + case RADIO_PARAM_RSSI: + *value = get_rssi(); + + if(*value == RF_CMD_CCA_REQ_RSSI_UNKNOWN) { + return RADIO_RESULT_ERROR; + } else { + return RADIO_RESULT_OK; + } + case RADIO_CONST_CHANNEL_MIN: + *value = CC26XX_RF_CHANNEL_MIN; + return RADIO_RESULT_OK; + case RADIO_CONST_CHANNEL_MAX: + *value = CC26XX_RF_CHANNEL_MAX; + return RADIO_RESULT_OK; + case RADIO_CONST_TXPOWER_MIN: + *value = OUTPUT_POWER_MIN; + return RADIO_RESULT_OK; + case RADIO_CONST_TXPOWER_MAX: + *value = OUTPUT_POWER_MAX; + return RADIO_RESULT_OK; + default: + return RADIO_RESULT_NOT_SUPPORTED; + } +} +/*---------------------------------------------------------------------------*/ +static radio_result_t +set_value(radio_param_t param, radio_value_t value) +{ + uint8_t was_off = 0; + radio_result_t rv; + + switch(param) { + case RADIO_PARAM_POWER_MODE: + if(value == RADIO_POWER_MODE_ON) { + if(on() != RF_CMD_OK) { + PRINTF("set_value: on() failed (1)\n"); + return RADIO_RESULT_ERROR; + } + return RADIO_RESULT_OK; + } + if(value == RADIO_POWER_MODE_OFF) { + off(); + return RADIO_RESULT_OK; + } + return RADIO_RESULT_INVALID_VALUE; + case RADIO_PARAM_CHANNEL: + if(value < CC26XX_RF_CHANNEL_MIN || + value > CC26XX_RF_CHANNEL_MAX) { + return RADIO_RESULT_INVALID_VALUE; + } + + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, channel) = (uint8_t)value; + break; + case RADIO_PARAM_PAN_ID: + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, localPanID) = (uint16_t)value; + break; + case RADIO_PARAM_16BIT_ADDR: + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, localShortAddr) = (uint16_t)value; + break; + case RADIO_PARAM_RX_MODE: + { + if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER | + RADIO_RX_MODE_AUTOACK)) { + return RADIO_RESULT_INVALID_VALUE; + } + + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameFiltOpt) = + BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltEn, + (value & RADIO_RX_MODE_ADDRESS_FILTER) != 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltStop, 1) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoAckEn, + (value & RADIO_RX_MODE_AUTOACK) != 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, slottedAckEn, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoPendEn, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, defaultPend, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, bPendDataReqOnly, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, bPanCoord, 0) | + BITVALUE(CMD_IEEE_RX, frameFiltOpt, bStrictLenFilter, 0); + + break; + } + case RADIO_PARAM_TXPOWER: + if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) { + return RADIO_RESULT_INVALID_VALUE; + } + + set_tx_power(value); + + return RADIO_RESULT_OK; + case RADIO_PARAM_CCA_THRESHOLD: + GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, ccaRssiThr) = (int8_t)value; + break; + default: + return RADIO_RESULT_NOT_SUPPORTED; + } + + /* If we reach here we had no errors. Apply new settings */ + if(!rf_is_on()) { + was_off = 1; + if(on() != RF_CMD_OK) { + PRINTF("set_value: on() failed (2)\n"); + return RADIO_RESULT_ERROR; + } + } + + if(rx_off() != RF_CMD_OK) { + PRINTF("set_value: rx_off() failed\n"); + rv = RADIO_RESULT_ERROR; + } + + if(rx_on() != RF_CMD_OK) { + PRINTF("set_value: rx_on() failed\n"); + rv = RADIO_RESULT_ERROR; + } + + /* If we were off, turn back off */ + if(was_off) { + off(); + } + + return rv; +} +/*---------------------------------------------------------------------------*/ +static radio_result_t +get_object(radio_param_t param, void *dest, size_t size) +{ + uint8_t *target; + uint8_t *src; + int i; + + if(param == RADIO_PARAM_64BIT_ADDR) { + if(size != 8 || !dest) { + return RADIO_RESULT_INVALID_VALUE; + } + + target = dest; + src = (uint8_t *)(GET_FIELD_PTR(cmd_ieee_rx_buf, CMD_IEEE_RX, localExtAddr)); + + for(i = 0; i < 8; i++) { + target[i] = src[7 - i]; + } + + return RADIO_RESULT_OK; + } + return RADIO_RESULT_NOT_SUPPORTED; +} +/*---------------------------------------------------------------------------*/ +static radio_result_t +set_object(radio_param_t param, const void *src, size_t size) +{ + uint8_t was_off = 0; + radio_result_t rv; + int i; + uint8_t *dst; + + if(param == RADIO_PARAM_64BIT_ADDR) { + if(size != 8 || !src) { + return RADIO_RESULT_INVALID_VALUE; + } + + dst = (uint8_t *)(GET_FIELD_PTR(cmd_ieee_rx_buf, CMD_IEEE_RX, + localExtAddr)); + + for(i = 0; i < 8; i++) { + dst[i] = ((uint8_t *)src)[7 - i]; + } + + if(!rf_is_on()) { + was_off = 1; + if(on() != RF_CMD_OK) { + PRINTF("set_object: on() failed\n"); + return RADIO_RESULT_ERROR; + } + } + + if(rx_off() != RF_CMD_OK) { + PRINTF("set_object: rx_off() failed\n"); + rv = RADIO_RESULT_ERROR; + } + + if(rx_on() != RF_CMD_OK) { + PRINTF("set_object: rx_on() failed\n"); + rv = RADIO_RESULT_ERROR; + } + + /* If we were off, turn back off */ + if(was_off) { + off(); + } + + return rv; + } + return RADIO_RESULT_NOT_SUPPORTED; +} +/*---------------------------------------------------------------------------*/ +const struct radio_driver cc26xx_rf_driver = { + init, + prepare, + transmit, + send, + read_frame, + channel_clear, + receiving_packet, + pending_packet, + on, + off, + get_value, + set_value, + get_object, + set_object, +}; +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(cc26xx_rf_process, ev, data) +{ + int len; + + PROCESS_BEGIN(); + + while(1) { + PROCESS_WAIT_EVENT(); + do { + packetbuf_clear(); + len = read_frame(packetbuf_dataptr(), PACKETBUF_SIZE); + + if(len > 0) { + packetbuf_set_datalen(len); + + NETSTACK_RDC.input(); + } + } while(len > 0); + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +#if CC26XX_RF_BLE_SUPPORT +/*---------------------------------------------------------------------------*/ +/** + * \brief Builds common radio parameters for radio operations + * + * \param *cmd Pointer to buffer to add parameters to + * \param command Radio command number (e.g. COMMAND_RADIO_SETUP) + * + * \note The buffer must be emptied with memset() before calling this function + * + * \return None + */ +static void +rf_build_ble_radio_op_cmd(uint8_t *cmd, uint16_t command) +{ + GET_FIELD(cmd, radioOp, commandNo) = command; + GET_FIELD(cmd, radioOp, status) = IDLE; + GET_FIELD(cmd, radioOp, pNextOp) = NULL; + GET_FIELD(cmd, radioOp, startTime) = 0; + GET_FIELD(cmd, radioOp, startTrigger) = TRIG_NOW; + GET_FIELD(cmd, radioOp, condition) = COND_NEVER; +} +/*---------------------------------------------------------------------------*/ +static void +init_ble() +{ + ble_mode_on = 0; + + memset(beacond_config.adv_name, 0, BLE_ADV_NAME_BUF_LEN); + beacond_config.interval = BLE_ADV_INTERVAL; +} +/*---------------------------------------------------------------------------*/ +static int +send_ble_adv_nc(int channel, uint8_t *output, uint8_t *adv_payload, + int adv_payload_len, uint16_t *dev_address) +{ + uint32_t cmd_status; + int ret; + + /* Erase ble_tx_rx_buf array */ + memset(ble_tx_rx_buf, 0x00, SIZEOF_RADIO_OP(CMD_BLE_ADV_NC)); + rf_build_ble_radio_op_cmd(ble_tx_rx_buf, CMD_BLE_ADV_NC); + + GET_FIELD(ble_tx_rx_buf, bleRadioOp, channel) = channel; + GET_FIELD(ble_tx_rx_buf, bleRadioOp, whitening) = 0; + + memset(ble_cmd_buf, 0x00, SIZEOF_STRUCT(bleAdvPar)); + GET_FIELD(ble_tx_rx_buf, bleRadioOp, pParams) = (uint8_t *)ble_cmd_buf; + GET_FIELD(ble_tx_rx_buf, bleRadioOp, pOutput) = output; + + /* Set up BLE Advertisement parameters */ + GET_FIELD(ble_cmd_buf, bleAdvPar, pRxQ) = NULL; + GET_FIELD(ble_cmd_buf, bleAdvPar, rxConfig) = 0; + GET_FIELD(ble_cmd_buf, bleAdvPar, advConfig) = 0; + GET_FIELD(ble_cmd_buf, bleAdvPar, advLen) = adv_payload_len; + GET_FIELD(ble_cmd_buf, bleAdvPar, scanRspLen) = 0; + GET_FIELD(ble_cmd_buf, bleAdvPar, pAdvData) = adv_payload; + GET_FIELD(ble_cmd_buf, bleAdvPar, pScanRspData) = NULL; + GET_FIELD(ble_cmd_buf, bleAdvPar, pDeviceAddress) = dev_address; + GET_FIELD(ble_cmd_buf, bleAdvPar, pWhiteList) = NULL; + GET_FIELD(ble_cmd_buf, bleAdvPar, endTrigger) = TRIG_NEVER; + GET_FIELD(ble_cmd_buf, bleAdvPar, endTime) = TRIG_NEVER; + + if(rf_send_cmd((uint32_t)ble_tx_rx_buf, &cmd_status) == RF_CMD_ERROR) { + PRINTF("send_ble_adv_nc: Chan=%d CMDSTA=0x%08lx, status=0x%04x\n", + channel, cmd_status, RF_RADIO_OP_GET_STATUS(ble_tx_rx_buf)); + return RF_CMD_ERROR; + } + + /* Wait for the ADV_NC to go out */ + while((RF_RADIO_OP_GET_STATUS(ble_tx_rx_buf) & RF_RADIO_OP_MASKED_STATUS) + == RF_RADIO_OP_MASKED_STATUS_RUNNING); + + if(RF_RADIO_OP_GET_STATUS(ble_tx_rx_buf) == RF_RADIO_OP_STATUS_BLE_DONE_OK) { + /* Sent OK */ + ret = RF_CMD_OK; + } else { + /* Radio Op completed, but ADV NC was not sent */ + PRINTF("send_ble_adv_nc: Chan=%d CMDSTA=0x%08lx, status=0x%04x\n", + channel, cmd_status, RF_RADIO_OP_GET_STATUS(ble_tx_rx_buf)); + ret = RF_CMD_ERROR; + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +static int +send_ble_adv(int channel, uint8_t *adv_payload, int adv_payload_len) +{ + if(send_ble_adv_nc(channel, rf_stats, adv_payload, adv_payload_len, + (uint16_t *)&linkaddr_node_addr.u8[2]) != RF_CMD_OK) { + PRINTF("send_ble_adv: Channel=%d, Error advertising\n", channel); + /* Break the loop, but don't return just yet */ + return RF_CMD_ERROR; + } + + return RF_CMD_OK; +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data) +{ + static struct etimer ble_adv_et; + static uint8_t payload[BLE_ADV_PAYLOAD_BUF_LEN]; + static int p = 0; + static int i; + uint8_t was_on; + int j; + uint32_t cmd_status; + + PROCESS_BEGIN(); + + while(1) { + etimer_set(&ble_adv_et, beacond_config.interval); + + PROCESS_WAIT_EVENT(); + + if(ev == PROCESS_EVENT_EXIT) { + PROCESS_EXIT(); + } + + /* Set the adv payload each pass: The device name may have changed */ + p = 0; + + /* device info */ + payload[p++] = 0x02; /* 2 bytes */ + payload[p++] = BLE_ADV_TYPE_DEVINFO; + payload[p++] = 0x1a; /* LE general discoverable + BR/EDR */ + payload[p++] = 1 + strlen(beacond_config.adv_name); + payload[p++] = BLE_ADV_TYPE_NAME; + memcpy(&payload[p], beacond_config.adv_name, + strlen(beacond_config.adv_name)); + p += strlen(beacond_config.adv_name); + + for(i = 0; i < BLE_ADV_MESSAGES; i++) { + /* + * Under ContikiMAC, some IEEE-related operations will be called from an + * interrupt context. We need those to see that we are in BLE mode. + */ + ti_lib_int_master_disable(); + ble_mode_on = 1; + ti_lib_int_master_enable(); + + /* + * Send BLE_ADV_MESSAGES beacon bursts. Each burst on all three + * channels, with a BLE_ADV_DUTY_CYCLE interval between bursts + * + * First, determine our state: + * + * If we are running NullRDC, we are likely in IEEE RX mode. We need to + * abort the IEEE BG Op before entering BLE mode. + * If we are ContikiMAC, we are likely off, in which case we need to + * boot the CPE before entering BLE mode + */ + was_on = rf_is_accessible(); + + if(was_on) { + /* + * We were on: If we are in the process of receiving an IEEE frame, + * abort the BLE beacon burst. Otherwise, terminate the IEEE BG Op + * so we can switch to BLE mode + */ + if(receiving_packet()) { + PRINTF("cc26xx_rf_ble_beacon_process: We were receiving\n"); + + /* Abort this pass */ + break; + } + + if(rx_off() != RF_CMD_OK) { + PRINTF("cc26xx_rf_ble_beacon_process: rx_off() failed\n"); + + /* Abort this pass */ + break; + } + } else { + /* Request the HF XOSC to source the HF clock. */ + request_hf_xosc(); + + /* We were off: Boot the CPE */ + if(power_up() != RF_CMD_OK) { + PRINTF("cc26xx_rf_ble_beacon_process: power_up() failed\n"); + + /* Abort this pass */ + break; + } + + if(apply_patches() != RF_CMD_OK) { + PRINTF("cc26xx_rf_ble_beacon_process: apply_patches() failed\n"); + + /* Abort this pass */ + break; + } + + /* Trigger a switch to the XOSC, so that we can use the FS */ + switch_to_hf_xosc(); + } + + /* Enter BLE mode */ + if(rf_radio_setup(RF_MODE_BLE) != RF_CMD_OK) { + PRINTF("cc26xx_rf_ble_beacon_process: Error entering BLE mode\n"); + /* Continue so we can at least try to restore our previous state */ + } else { + /* Send advertising packets on all 3 advertising channels */ + for(j = 37; j <= 39; j++) { + if(send_ble_adv(j, payload, p) != RF_CMD_OK) { + PRINTF("cc26xx_rf_ble_beacon_process: Channel=%d," + "Error advertising\n", j); + /* Break the loop, but don't return just yet */ + break; + } + } + } + + /* Send a CMD_STOP command to RF Core */ + if(rf_send_cmd(CMDR_DIR_CMD(CMD_STOP), &cmd_status) != RF_CMD_OK) { + PRINTF("cc26xx_rf_ble_beacon_process: status=0x%08lx\n", cmd_status); + /* Continue... */ + } + + if(was_on) { + /* We were on, go back to IEEE mode */ + if(rf_radio_setup(RF_MODE_IEEE) != RF_CMD_OK) { + PRINTF("cc26xx_rf_ble_beacon_process: radio_setup() failed\n"); + } + + /* Enter IEEE RX mode */ + if(rx_on() != RF_CMD_OK) { + PRINTF("cc26xx_rf_ble_beacon_process: rx_on() failed\n"); + } + } else { + power_down(); + + /* Switch HF clock source to the RCOSC to preserve power */ + switch_to_hf_rc_osc(); + } + etimer_set(&ble_adv_et, BLE_ADV_DUTY_CYCLE); + + ti_lib_int_master_disable(); + ble_mode_on = 0; + ti_lib_int_master_enable(); + + /* Wait unless this is the last burst */ + if(i < BLE_ADV_MESSAGES - 1) { + PROCESS_WAIT_EVENT(); + } + } + + ti_lib_int_master_disable(); + ble_mode_on = 0; + ti_lib_int_master_enable(); + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +#endif /* CC26XX_RF_BLE_SUPPORT */ +/*---------------------------------------------------------------------------*/ +void +cc26xx_rf_ble_beacond_config(clock_time_t interval, const char *name) +{ +#if CC26XX_RF_BLE_SUPPORT + if(name != NULL) { + memset(beacond_config.adv_name, 0, BLE_ADV_NAME_BUF_LEN); + + if(strlen(name) == 0 || strlen(name) >= BLE_ADV_NAME_BUF_LEN) { + return; + } + + memcpy(beacond_config.adv_name, name, strlen(name)); + } + + if(interval != 0) { + beacond_config.interval = interval; + } +#endif +} +/*---------------------------------------------------------------------------*/ +uint8_t +cc26xx_rf_ble_beacond_start() +{ +#if CC26XX_RF_BLE_SUPPORT + if(beacond_config.adv_name[0] == 0) { + return RF_CMD_ERROR; + } + + process_start(&cc26xx_rf_ble_beacon_process, NULL); + + return RF_CMD_OK; +#else + return RF_CMD_ERROR; +#endif +} +/*---------------------------------------------------------------------------*/ +void +cc26xx_rf_ble_beacond_stop() +{ +#if CC26XX_RF_BLE_SUPPORT + process_exit(&cc26xx_rf_ble_beacon_process); +#endif +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/cpu/cc26xx/dev/cc26xx-rf.h b/cpu/cc26xx/dev/cc26xx-rf.h new file mode 100644 index 000000000..4087a037d --- /dev/null +++ b/cpu/cc26xx/dev/cc26xx-rf.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-rf CC26xx RF driver + * + * The CC26xx RF has dual capability: It can operate in IEEE 802.15.4 mode at + * 2.4GHz, but it can also operate in BLE mode. This driver provides a fully + * contiki-compliant .15.4 functionality, but it also provides some very basic + * BLE capability. + * + * @{ + */ +/** + * \file + * Header file for the CC26xx RF driver + */ +#ifndef CC26XX_RF_H_ +#define CC26XX_RF_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "cc26xx-model.h" +#include "dev/radio.h" +/*---------------------------------------------------------------------------*/ +#include +/*---------------------------------------------------------------------------*/ +#ifdef CC26XX_RF_CONF_CHANNEL +#define CC26XX_RF_CHANNEL CC26XX_RF_CONF_CHANNEL +#else +#define CC26XX_RF_CHANNEL 18 +#endif /* CC26XX_RF_CONF_CHANNEL */ + +#ifdef CC26XX_RF_CONF_AUTOACK +#define CC26XX_RF_AUTOACK CC26XX_RF_CONF_AUTOACK +#else +#define CC26XX_RF_AUTOACK 1 +#endif /* CC26XX_RF_CONF_AUTOACK */ + +#if defined (CC26XX_RF_CONF_BLE_SUPPORT) && (CC26XX_MODEL_CPU_VARIANT == 2650) +#define CC26XX_RF_BLE_SUPPORT CC26XX_RF_CONF_BLE_SUPPORT +#else +#define CC26XX_RF_BLE_SUPPORT 0 +#endif +/*--------------------------------------------------------------------------- + * RF Config + *---------------------------------------------------------------------------*/ +/* Constants */ +#define CC26XX_RF_CHANNEL_MIN 11 +#define CC26XX_RF_CHANNEL_MAX 26 +#define CC26XX_RF_CHANNEL_SPACING 5 +#define CC26XX_RF_CHANNEL_SET_ERROR -1 +#define CC26XX_RF_MAX_PACKET_LEN 127 +#define CC26XX_RF_MIN_PACKET_LEN 4 +/*---------------------------------------------------------------------------*/ +/** + * \brief Set the device name to use with the BLE advertisement/beacon daemon + * \param interval The interval (ticks) between two consecutive beacon bursts + * \param name The device name to advertise + * + * If name is NULL it will be ignored. If interval==0 it will be ignored. Thus, + * this function can be used to configure a single parameter at a time if so + * desired. + */ +void cc26xx_rf_ble_beacond_config(clock_time_t interval, const char *name); + +/** + * \brief Start the BLE advertisement/beacon daemon + * \return 1: Success, 0: Failure + * + * Before calling this function, the name to advertise must first be set by + * calling cc26xx_rf_ble_beacond_set_adv_name(). Otherwise, this function will + * return an error. + */ +uint8_t cc26xx_rf_ble_beacond_start(void); + +/** + * \brief Stop the BLE advertisement/beacon daemon + */ +void cc26xx_rf_ble_beacond_stop(void); +/*---------------------------------------------------------------------------*/ +#endif /* CC26XX_RF_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/dev/cc26xx-rtc.c b/cpu/cc26xx/dev/cc26xx-rtc.c new file mode 100644 index 000000000..b06aebd22 --- /dev/null +++ b/cpu/cc26xx/dev/cc26xx-rtc.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-rtc + * @{ + * + */ +/** + * \file + * Implementation of the CC26xx AON RTC driver + */ +#include "contiki.h" +#include "sys/energest.h" +#include "rtimer.h" +#include "lpm.h" + +#include "ti-lib.h" + +#include +/*---------------------------------------------------------------------------*/ +#define cc26xx_rtc_isr(...) AONRTCIntHandler(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* Prototype of a function in clock.c. Called every time the handler fires */ +void clock_update(void); +/*---------------------------------------------------------------------------*/ +void +cc26xx_rtc_init(void) +{ + uint32_t compare_value; + + /* Disable and clear interrupts */ + ti_lib_int_master_disable(); + ti_lib_aon_rtc_disable(); + + ti_lib_aon_rtc_event_clear(AON_RTC_CH0); + ti_lib_aon_rtc_event_clear(AON_RTC_CH2); + + /* Setup the wakeup event */ + ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU0, AON_EVENT_RTC0); + ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU1, AON_EVENT_RTC2); + + /* Configure channel 2 in continuous compare, 128 ticks / sec */ + compare_value = (RTIMER_SECOND / CLOCK_SECOND) + + ti_lib_aon_rtc_current_compare_value_get(); + ti_lib_aon_rtc_compare_value_set(AON_RTC_CH2, compare_value); + ti_lib_aon_rtc_inc_value_ch2_set(RTIMER_SECOND / CLOCK_SECOND); + ti_lib_aon_rtc_mode_ch2_set(AON_RTC_MODE_CH2_CONTINUOUS); + + /* Enable event generation for channels 0 and 2 and enable the RTC */ + ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH2); + ti_lib_aon_rtc_channel_enable(AON_RTC_CH2); + + ti_lib_aon_rtc_enable(); + + ti_lib_int_enable(INT_AON_RTC); + ti_lib_int_master_enable(); +} +/*---------------------------------------------------------------------------*/ +rtimer_clock_t +cc26xx_rtc_get_next_trigger() +{ + rtimer_clock_t ch2 = ti_lib_aon_rtc_compare_value_get(AON_RTC_CH2); + + if(HWREG(AON_RTC_BASE + AON_RTC_O_CHCTL) & AON_RTC_CHCTL_CH0_EN) { + rtimer_clock_t ch0 = ti_lib_aon_rtc_compare_value_get(AON_RTC_CH2); + + return RTIMER_CLOCK_LT(ch0 ,ch2) ? ch0 : ch2; + } + + return ch2; +} +/*---------------------------------------------------------------------------*/ +void +cc26xx_rtc_schedule_one_shot(uint32_t ticks) +{ + /* Set the channel to fire a one-shot compare event at time==ticks */ + ti_lib_aon_rtc_compare_value_set(AON_RTC_CH0, ticks); + ti_lib_aon_rtc_channel_enable(AON_RTC_CH0); +} +/*---------------------------------------------------------------------------*/ +/* The AON RTC interrupt handler */ +void +cc26xx_rtc_isr(void) +{ + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + if(ti_lib_aon_rtc_event_get(AON_RTC_CH0)) { + ti_lib_aon_rtc_event_clear(AON_RTC_CH0); + rtimer_run_next(); + } + + if(ti_lib_aon_rtc_event_get(AON_RTC_CH2)) { + ti_lib_aon_rtc_event_clear(AON_RTC_CH2); + clock_update(); + } + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/cpu/cc26xx/dev/cc26xx-rtc.h b/cpu/cc26xx/dev/cc26xx-rtc.h new file mode 100644 index 000000000..4fbaa1795 --- /dev/null +++ b/cpu/cc26xx/dev/cc26xx-rtc.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-clocks CC26xx clock and timer subsystem + * + * For the CC26xx cpu we use the AON RTC as the basis for all clocks and timers + * + * We configure the AON RTC's channel 2 to run in continuous mode, generating + * 128 interrupts / second. In continuous mode, the next compare event is + * scheduled by the hardware automatically; the events are equidistant and + * this also means we don't need the overhead of re-scheduling within the + * interrupt handler + * + * For rtimers, we use the RTC's channel 0 in one-shot compare mode. When the + * compare event fires, we call rtimer_run_next + * + * The RTC runs in all power modes except 'shutdown' + * + * \sa cpu/cc26xx/clock.c cpu/cc26xx/rtimer-arch.c + * @{ + * + * \defgroup cc26xx-rtc CC26xx AON RTC driver + * + * Underpins the platform's software clocks and timers + * + * @{ + * \file + * Header file for the CC26XX AON RTC driver + */ +#ifndef CC26XX_RTC_H_ +#define CC26XX_RTC_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" + +#include "rtimer.h" + +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise the CC26XX AON RTC module + * + * This timer configures the AON RTC's channel 2 to run in continuous mode + * This function must be called before clock_init() and rtimer_init() + */ +void cc26xx_rtc_init(void); + +/** + * \brief Return the time of the next scheduled rtimer event + * \return The time at which the next rtimer event is due to fire + * + * This function will check both AON RTC channels and will only take CH0's + * compare into account if the channel is actually enabled + */ +rtimer_clock_t cc26xx_rtc_get_next_trigger(void); + +/** + * \brief Schedule an AON RTC channel 0 one-shot compare event + * \param t The time when the event will be fired. This is an absolute + * time, in other words the event will fire AT time \e t, + * not IN \e t ticks + */ +void cc26xx_rtc_schedule_one_shot(uint32_t t); +/*---------------------------------------------------------------------------*/ +#endif /* CC26XX_RTC_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + * @} + */ diff --git a/cpu/cc26xx/dev/cc26xx-uart.c b/cpu/cc26xx/dev/cc26xx-uart.c new file mode 100644 index 000000000..5a45bb03b --- /dev/null +++ b/cpu/cc26xx/dev/cc26xx-uart.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +#include "contiki-conf.h" +#include "cc26xx-uart.h" +#include "hw_types.h" +#include "hw_memmap.h" +#include "sys_ctrl.h" +#include "prcm.h" +#include "ioc.h" +#include "uart.h" +#include "lpm.h" +#include "ti-lib.h" +#include "sys/energest.h" + +#include +/*---------------------------------------------------------------------------*/ +/* Which events to trigger a UART interrupt */ +#define CC26XX_UART_RX_INTERRUPT_TRIGGERS (UART_INT_RX | UART_INT_RT) + +/* All interrupt masks */ +#define CC26XX_UART_INTERRUPT_ALL (UART_INT_OE | UART_INT_BE | UART_INT_PE | \ + UART_INT_FE | UART_INT_RT | UART_INT_TX | \ + UART_INT_RX | UART_INT_CTS) +/*---------------------------------------------------------------------------*/ +#define cc26xx_uart_isr UART0IntHandler +/*---------------------------------------------------------------------------*/ +static int (*input_handler)(unsigned char c); +/*---------------------------------------------------------------------------*/ +static void +power_domain_on(void) +{ + ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL); + while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL) + != PRCM_DOMAIN_POWER_ON); +} +/*---------------------------------------------------------------------------*/ +static void +configure_baud_rate(void) +{ + /* + * Configure the UART for 115,200, 8-N-1 operation. + * This function uses SysCtrlClockGet() to get the system clock + * frequency. This could be also be a variable or hard coded value + * instead of a function call. + */ + ti_lib_uart_config_set_exp_clk(UART0_BASE, + ti_lib_sys_ctrl_peripheral_clock_get( + PRCM_PERIPH_UART0, + SYSCTRL_SYSBUS_ON), + CC26XX_UART_CONF_BAUD_RATE, + (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | + UART_CONFIG_PAR_NONE)); +} +/*---------------------------------------------------------------------------*/ +static void +configure_registers(void) +{ + /* + * Map UART signals to the correct GPIO pins and configure them as + * hardware controlled. + */ + ti_lib_ioc_pin_type_uart(UART0_BASE, BOARD_IOID_UART_RX, BOARD_IOID_UART_TX, + BOARD_IOID_UART_CTS, BOARD_IOID_UART_RTS); + + configure_baud_rate(); + + /* + * Generate an RX interrupt at FIFO 1/2 full. + * We don't really care about the TX interrupt + */ + ti_lib_uart_fifo_level_set(UART0_BASE, UART_FIFO_TX7_8, UART_FIFO_RX4_8); + + /* Configure which interrupts to generate: FIFO level or after RX timeout */ + ti_lib_uart_int_enable(UART0_BASE, CC26XX_UART_RX_INTERRUPT_TRIGGERS); +} +/*---------------------------------------------------------------------------*/ +static void +uart_on(void) +{ + power_domain_on(); + + /* Configure baud rate and enable */ + if((HWREG(UART0_BASE + UART_O_CTL) & UART_CTL_UARTEN) == 0) { + configure_registers(); + + /* Enable UART */ + ti_lib_uart_enable(UART0_BASE); + } +} +/*---------------------------------------------------------------------------*/ +static uint8_t +lpm_permit_max_pm_handler(void) +{ + return LPM_MODE_MAX_SUPPORTED; +} +/*---------------------------------------------------------------------------*/ +static void +lpm_drop_handler(uint8_t mode) +{ + /* Do nothing if the PD is off */ + if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL) + != PRCM_DOMAIN_POWER_ON) { + return; + } + + /* Wait for outstanding TX to complete */ + while(ti_lib_uart_busy(UART0_BASE)); + + /* + * Check our clock gate under Deep Sleep. If it's off, we can shut down. If + * it's on, this means that some other code module wants UART functionality + * during deep sleep, so we stay enabled + */ + if((HWREG(PRCM_BASE + PRCM_O_UARTCLKGDS) & 1) == 0) { + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_UART_RX); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_UART_TX); + + ti_lib_uart_disable(UART0_BASE); + } +} +/*---------------------------------------------------------------------------*/ +static void +lpm_wakeup_handler(void) +{ + uart_on(); +} +/*---------------------------------------------------------------------------*/ +/* Declare a data structure to register with LPM. */ +LPM_MODULE(uart_module, lpm_permit_max_pm_handler, + lpm_drop_handler, lpm_wakeup_handler); +/*---------------------------------------------------------------------------*/ +void +cc26xx_uart_init() +{ + /* Exit without initialising if ports are misconfigured */ + if(BOARD_IOID_UART_RX == IOID_UNUSED || + BOARD_IOID_UART_TX == IOID_UNUSED) { + return; + } + + /* Enable the serial domain and wait for domain to be on */ + power_domain_on(); + + /* Enable the UART clock when running and sleeping */ + ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_UART0); + + /* Apply clock settings and wait for them to take effect */ + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* Disable Interrupts */ + ti_lib_int_master_disable(); + + /* Make sure the peripheral is disabled */ + ti_lib_uart_disable(UART0_BASE); + + /* Disable all UART module interrupts */ + ti_lib_uart_int_disable(UART0_BASE, CC26XX_UART_INTERRUPT_ALL); + + configure_registers(); + + /* Acknowledge UART interrupts */ + ti_lib_int_enable(INT_UART0); + + /* Re-enable processor interrupts */ + ti_lib_int_master_enable(); + + /* Enable UART */ + ti_lib_uart_enable(UART0_BASE); + + /* Register ourselves with the LPM module */ + lpm_register_module(&uart_module); +} +/*---------------------------------------------------------------------------*/ +void +cc26xx_uart_write_byte(uint8_t c) +{ + if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL) + != PRCM_DOMAIN_POWER_ON) { + return; + } + + ti_lib_uart_char_put(UART0_BASE, c); +} +/*---------------------------------------------------------------------------*/ +void +cc26xx_uart_set_input(int (*input)(unsigned char c)) +{ + input_handler = input; + return; +} +/*---------------------------------------------------------------------------*/ +void +cc26xx_uart_isr(void) +{ + char the_char; + uint32_t flags; + + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + /* Read out the masked interrupt status */ + flags = ti_lib_uart_int_status(UART0_BASE, true); + + /* Clear all UART interrupt flags */ + ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL); + + if((flags & CC26XX_UART_RX_INTERRUPT_TRIGGERS) != 0) { + /* + * If this was a FIFO RX or an RX timeout, read all bytes available in the + * RX FIFO. + */ + while(ti_lib_uart_chars_avail(UART0_BASE)) { + the_char = ti_lib_uart_char_get_non_blocking(UART0_BASE); + + if(input_handler != NULL) { + input_handler((unsigned char)the_char); + } + } + } + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); +} diff --git a/cpu/cc26xx/dev/cc26xx-uart.h b/cpu/cc26xx/dev/cc26xx-uart.h new file mode 100644 index 000000000..4647b9339 --- /dev/null +++ b/cpu/cc26xx/dev/cc26xx-uart.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-uart CC26xx UARTs + * + * Driver for the CC26xx UART controller + * @{ + * + * \file + * Header file for the CC26xx UART driver + */ +#ifndef CC26XX_UART_H_ +#define CC26XX_UART_H_ + +#include +/*---------------------------------------------------------------------------*/ +/** \name UART functions + * @{ + */ + +/** + * \brief Initialises the UART controller, configures I/O control + * and interrupts + */ +void cc26xx_uart_init(); + +/** + * \brief Sends a single character down the UART + * \param b The character to transmit + */ +void cc26xx_uart_write_byte(uint8_t b); + +/** + * \brief Assigns a callback to be called when the UART receives a byte + * \param input A pointer to the function + */ +void cc26xx_uart_set_input(int (*input)(unsigned char c)); + +/** @} */ +/*---------------------------------------------------------------------------*/ +#endif /* CC26XX_UART_H_ */ + +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/dev/contiki-watchdog.c b/cpu/cc26xx/dev/contiki-watchdog.c new file mode 100644 index 000000000..8241b5045 --- /dev/null +++ b/cpu/cc26xx/dev/contiki-watchdog.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-clocks + * @{ + * + * \defgroup cc26xx-wdt CC26xx watchdog timer driver + * + * Driver for the CC26xx Watchdog Timer + * + * This file is not called watchdog.c because the filename is in use by + * TI CC26xxware + * @{ + * + * \file + * Implementation of the cc26xx watchdog driver. + */ +#include "watchdog.h" +#include "ti-lib.h" +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialises the CC26xx WDT + * + * Simply sets the reload counter to a default value. The WDT is not started + * yet. To start it, watchdog_start() must be called. + */ +void +watchdog_init(void) +{ + ti_lib_wathdog_reload_set(0xFFFFF); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Starts the CC26xx WDT + */ +void +watchdog_start(void) +{ + ti_lib_wathdog_reset_enable(); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Refreshes the CC26xx WDT + */ +void +watchdog_periodic(void) +{ + ti_lib_wathdog_int_clear(); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Stub function to satisfy API requirements + */ +void +watchdog_stop(void) +{ +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Manually trigger a WDT reboot + */ +void +watchdog_reboot(void) +{ + watchdog_start(); + while(1); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/dev/gpio-interrupt.c b/cpu/cc26xx/dev/gpio-interrupt.c new file mode 100644 index 000000000..a2c6dd42b --- /dev/null +++ b/cpu/cc26xx/dev/gpio-interrupt.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +#include "ioc.h" +#include "gpio-interrupt.h" +#include "sys/energest.h" +#include "lpm.h" +#include "ti-lib.h" + +#include +/*---------------------------------------------------------------------------*/ +#define gpio_interrupt_isr GPIOIntHandler +/*---------------------------------------------------------------------------*/ +/* Handler array */ +static gpio_interrupt_handler_t handlers[NUM_IO_MAX]; +/*---------------------------------------------------------------------------*/ +void +gpio_interrupt_register_handler(uint8_t ioid, gpio_interrupt_handler_t f) +{ + uint8_t interrupts_disabled = ti_lib_int_master_disable(); + + /* Clear interrupts on specified pins */ + ti_lib_gpio_event_clear(1 << ioid); + + handlers[ioid] = f; + + /* Re-enable interrupts */ + if(!interrupts_disabled) { + ti_lib_int_master_enable(); + } +} +/*---------------------------------------------------------------------------*/ +void +gpio_interrupt_init() +{ + int i; + + for(i = 0; i < NUM_IO_MAX; i++) { + handlers[i] = NULL; + } + + ti_lib_int_enable(INT_EDGE_DETECT); +} +/*---------------------------------------------------------------------------*/ +void +gpio_interrupt_isr(void) +{ + uint32_t pin_mask; + uint8_t i; + + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + /* Read interrupt flags */ + pin_mask = (HWREG(GPIO_BASE + GPIO_O_EVFLAGS31_0) & GPIO_PIN_MASK); + + /* Clear the interrupt flags */ + HWREG(GPIO_BASE + GPIO_O_EVFLAGS31_0) = pin_mask; + + /* Run custom ISRs */ + for(i = 0; i < NUM_GPIO_PINS; i++) { + /* Call the handler if there is one registered for this event */ + if((pin_mask & (1 << i)) && handlers[i] != NULL) { + handlers[i](i); + } + } + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); +} +/*---------------------------------------------------------------------------*/ + diff --git a/cpu/cc26xx/dev/gpio-interrupt.h b/cpu/cc26xx/dev/gpio-interrupt.h new file mode 100644 index 000000000..6a64f26f6 --- /dev/null +++ b/cpu/cc26xx/dev/gpio-interrupt.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-gpio-interrupts CC26xx GPIO interrupt handling + * + * The CC26xx GPIO interrupt handler and an API which can be used by other + * parts of the code when they wish to be notified of a GPIO interrupt + * + * @{ + * + * \file + * Header file for the CC26xx GPIO interrupt management + */ +/*---------------------------------------------------------------------------*/ +#ifndef GPIO_INTERRUPT_H_ +#define GPIO_INTERRUPT_H_ +/*---------------------------------------------------------------------------*/ +#include +/*---------------------------------------------------------------------------*/ +typedef void (*gpio_interrupt_handler_t)(uint8_t ioid); +/*---------------------------------------------------------------------------*/ +/** \brief Initialise the GPIO interrupt handling module */ +void gpio_interrupt_init(void); + +/** + * \brief Register a GPIO interrupt handler + * \param f Pointer to a handler to be called when an interrupt is raised on + * ioid + * \param ioid Associate \a f with this ioid. \e ioid must be specified with + * its numeric representation (0, 1, .. 31). Defines for these + * numeric representations are IOID_x + */ +void gpio_interrupt_register_handler(uint8_t ioid, gpio_interrupt_handler_t f); + +#endif /* GPIO_INTERRUPT_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/dev/rfc-api/ble_cmd.h b/cpu/cc26xx/dev/rfc-api/ble_cmd.h new file mode 100644 index 000000000..f7320fe4d --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/ble_cmd.h @@ -0,0 +1,1082 @@ +/****************************************************************************** +* Filename: ble_cmd.h +* Revised: $ $ +* Revision: $ $ +* +* Description: CC26xx/CC13xx API for Bluetooth Low Energy commands +* +* Copyright (C) 2015 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 __BLE_CMD_H +#define __BLE_CMD_H + +#ifndef __RFC_STRUCT +#ifdef __GNUC__ +#define __RFC_STRUCT __attribute__ ((aligned (4))) +#else +#define __RFC_STRUCT +#endif +#endif + +//! \addtogroup rfc +//! @{ + +//! \addtogroup ble_cmd +//! @{ + +#include +#include "mailbox.h" +#include "common_cmd.h" + +typedef struct __RFC_STRUCT rfc_bleMasterPar_s rfc_bleMasterPar_t; +typedef struct __RFC_STRUCT rfc_bleMasterSlavePar_s rfc_bleMasterSlavePar_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_INITIATOR_s rfc_CMD_BLE_INITIATOR_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_TX_TEST_s rfc_CMD_BLE_TX_TEST_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_MASTER_s rfc_CMD_BLE_MASTER_t; +typedef struct __RFC_STRUCT rfc_bleWhiteListEntry_s rfc_bleWhiteListEntry_t; +typedef struct __RFC_STRUCT rfc_bleGenericRxPar_s rfc_bleGenericRxPar_t; +typedef struct __RFC_STRUCT rfc_bleTxTestPar_s rfc_bleTxTestPar_t; +typedef struct __RFC_STRUCT rfc_bleInitiatorOutput_s rfc_bleInitiatorOutput_t; +typedef struct __RFC_STRUCT rfc_bleWhiteListEntryWords_s rfc_bleWhiteListEntryWords_t; +typedef struct __RFC_STRUCT rfc_bleMasterSlaveOutput_s rfc_bleMasterSlaveOutput_t; +typedef struct __RFC_STRUCT rfc_bleRxStatus_s rfc_bleRxStatus_t; +typedef struct __RFC_STRUCT rfc_bleRadioOp_s rfc_bleRadioOp_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_ADV_NC_s rfc_CMD_BLE_ADV_NC_t; +typedef struct __RFC_STRUCT rfc_bleTxTestOutput_s rfc_bleTxTestOutput_t; +typedef struct __RFC_STRUCT rfc_bleAdvPar_s rfc_bleAdvPar_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_ADV_SCAN_s rfc_CMD_BLE_ADV_SCAN_t; +typedef struct __RFC_STRUCT rfc_bleAdvOutput_s rfc_bleAdvOutput_t; +typedef struct __RFC_STRUCT rfc_bleScannerOutput_s rfc_bleScannerOutput_t; +typedef struct __RFC_STRUCT rfc_bleSlavePar_s rfc_bleSlavePar_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_ADV_DIR_s rfc_CMD_BLE_ADV_DIR_t; +typedef struct __RFC_STRUCT rfc_bleInitiatorPar_s rfc_bleInitiatorPar_t; +typedef struct __RFC_STRUCT rfc_bleScannerPar_s rfc_bleScannerPar_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_SLAVE_s rfc_CMD_BLE_SLAVE_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_ADV_s rfc_CMD_BLE_ADV_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_SCANNER_s rfc_CMD_BLE_SCANNER_t; +typedef struct __RFC_STRUCT rfc_bleGenericRxOutput_s rfc_bleGenericRxOutput_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_GENERIC_RX_s rfc_CMD_BLE_GENERIC_RX_t; +typedef struct __RFC_STRUCT rfc_CMD_BLE_ADV_PAYLOAD_s rfc_CMD_BLE_ADV_PAYLOAD_t; + +//! \addtogroup bleRadioOp +//! @{ +struct __RFC_STRUCT rfc_bleRadioOp_s { + uint16_t commandNo; //!< The command ID number + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + uint8_t* pParams; //!< Pointer to command specific parameter structure + uint8_t* pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_SLAVE +//! @{ +#define CMD_BLE_SLAVE 0x1801 +struct __RFC_STRUCT rfc_CMD_BLE_SLAVE_s { + uint16_t commandNo; //!< The command ID number 0x1801 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleSlavePar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleMasterSlaveOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_MASTER +//! @{ +#define CMD_BLE_MASTER 0x1802 +struct __RFC_STRUCT rfc_CMD_BLE_MASTER_s { + uint16_t commandNo; //!< The command ID number 0x1802 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleMasterPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleMasterSlaveOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_ADV +//! @{ +#define CMD_BLE_ADV 0x1803 +struct __RFC_STRUCT rfc_CMD_BLE_ADV_s { + uint16_t commandNo; //!< The command ID number 0x1803 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleAdvPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleAdvOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_ADV_DIR +//! @{ +#define CMD_BLE_ADV_DIR 0x1804 +struct __RFC_STRUCT rfc_CMD_BLE_ADV_DIR_s { + uint16_t commandNo; //!< The command ID number 0x1804 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleAdvPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleAdvOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_ADV_NC +//! @{ +#define CMD_BLE_ADV_NC 0x1805 +struct __RFC_STRUCT rfc_CMD_BLE_ADV_NC_s { + uint16_t commandNo; //!< The command ID number 0x1805 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleAdvPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleAdvOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_ADV_SCAN +//! @{ +#define CMD_BLE_ADV_SCAN 0x1806 +struct __RFC_STRUCT rfc_CMD_BLE_ADV_SCAN_s { + uint16_t commandNo; //!< The command ID number 0x1806 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleAdvPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleAdvOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_SCANNER +//! @{ +#define CMD_BLE_SCANNER 0x1807 +struct __RFC_STRUCT rfc_CMD_BLE_SCANNER_s { + uint16_t commandNo; //!< The command ID number 0x1807 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleScannerPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleScannerOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_INITIATOR +//! @{ +#define CMD_BLE_INITIATOR 0x1808 +struct __RFC_STRUCT rfc_CMD_BLE_INITIATOR_s { + uint16_t commandNo; //!< The command ID number 0x1808 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleInitiatorPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleInitiatorOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_GENERIC_RX +//! @{ +#define CMD_BLE_GENERIC_RX 0x1809 +struct __RFC_STRUCT rfc_CMD_BLE_GENERIC_RX_s { + uint16_t commandNo; //!< The command ID number 0x1809 + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleGenericRxPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleGenericRxOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_TX_TEST +//! @{ +#define CMD_BLE_TX_TEST 0x180A +struct __RFC_STRUCT rfc_CMD_BLE_TX_TEST_s { + uint16_t commandNo; //!< The command ID number 0x180A + 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 if the rule involves skipping + } condition; + uint8_t channel; //!< \brief Channel to use
+ //!< 0–39: BLE advertising/data channel number + //!< 60–207: Custom frequency; (2300 + channel) MHz + //!< 255: Use existing frequency + //!< Others: Reserved + struct { + uint8_t init:7; //!< \brief If bOverride = 1 or custom frequency is used:
+ //!< 0: Do not use whitening
+ //!< Other value: Initialization for 7-bit LFSR whitener + uint8_t bOverride:1; //!< \brief 0: Use default whitening for BLE advertising/data channels
+ //!< 1: Override whitening initialization with value of init + } whitening; + rfc_bleTxTestPar_t *pParams; //!< Pointer to command specific parameter structure + rfc_bleTxTestOutput_t *pOutput; //!< Pointer to command specific output structure +}; + +//! @} + +//! \addtogroup CMD_BLE_ADV_PAYLOAD +//! @{ +#define CMD_BLE_ADV_PAYLOAD 0x1001 +struct __RFC_STRUCT rfc_CMD_BLE_ADV_PAYLOAD_s { + uint16_t commandNo; //!< The command ID number 0x1001 + uint8_t payloadType; //!< \brief 0: Advertising data
+ //!< 1: Scan response data + uint8_t newLen; //!< Length of the new payload + uint8_t* pNewData; //!< Pointer to the buffer containing the new data + rfc_bleAdvPar_t *pParams; //!< Pointer to the parameter structure to update +}; + +//! @} + +//! \addtogroup bleMasterSlavePar +//! @{ +struct __RFC_STRUCT rfc_bleMasterSlavePar_s { + dataQueue_t* pRxQ; //!< Pointer to receive queue + dataQueue_t* pTxQ; //!< Pointer to transmit queue + struct { + uint8_t bAutoFlushIgnored:1; //!< If 1, automatically remove ignored packets from Rx queue + uint8_t bAutoFlushCrcErr:1; //!< If 1, automatically remove packets with CRC error from Rx queue + uint8_t bAutoFlushEmpty:1; //!< If 1, automatically remove empty packets from Rx queue + uint8_t bIncludeLenByte:1; //!< If 1, include the received length byte 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 bAppendStatus:1; //!< If 1, append a status byte to the packet in the Rx queue + uint8_t bAppendTimestamp:1; //!< If 1, append a timestamp to the packet in the Rx queue + } rxConfig; //!< Configuration bits for the receive queue entries + struct { + uint8_t lastRxSn:1; //!< The SN bit of the header of the last packet received with CRC OK + uint8_t lastTxSn:1; //!< The SN bit of the header of the last transmitted packet + uint8_t nextTxSn:1; //!< The SN bit of the header of the next packet to transmit + uint8_t bFirstPkt:1; //!< For slave: 0 if a packet has been transmitted on the connection, 1 otherwise + uint8_t bAutoEmpty:1; //!< 1 if the last transmitted packet was an auto-empty packet + uint8_t bLlCtrlTx:1; //!< 1 if the last transmitted packet was an LL control packet (LLID = 11) + uint8_t bLlCtrlAckRx:1; //!< 1 if the last received packet was the ACK of an LL control packet + uint8_t bLlCtrlAckPending:1; //!< 1 if the last successfully received packet was an LL control packet which has not yet been ACK'ed + } seqStat; + uint8_t maxNack; //!< Maximum number of NACKs received before operation ends. 0: No limit + uint8_t maxPkt; //!< Maximum number of packets transmitted in the operation before it ends. 0: No limit + uint32_t accessAddress; //!< Access address used on the connection + uint8_t crcInit0; //!< CRC initialization value used on the connection – least significant byte + uint8_t crcInit1; //!< CRC initialization value used on the connection – middle byte + uint8_t crcInit2; //!< CRC initialization value used on the connection – most significant byte +}; + +//! @} + +//! \addtogroup bleMasterPar +//! @{ +//! Parameter structure for master (CMD_BLE_MASTER) + +struct __RFC_STRUCT rfc_bleMasterPar_s { + dataQueue_t* pRxQ; //!< Pointer to receive queue + dataQueue_t* pTxQ; //!< Pointer to transmit queue + struct { + uint8_t bAutoFlushIgnored:1; //!< If 1, automatically remove ignored packets from Rx queue + uint8_t bAutoFlushCrcErr:1; //!< If 1, automatically remove packets with CRC error from Rx queue + uint8_t bAutoFlushEmpty:1; //!< If 1, automatically remove empty packets from Rx queue + uint8_t bIncludeLenByte:1; //!< If 1, include the received length byte 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 bAppendStatus:1; //!< If 1, append a status byte to the packet in the Rx queue + uint8_t bAppendTimestamp:1; //!< If 1, append a timestamp to the packet in the Rx queue + } rxConfig; //!< Configuration bits for the receive queue entries + struct { + uint8_t lastRxSn:1; //!< The SN bit of the header of the last packet received with CRC OK + uint8_t lastTxSn:1; //!< The SN bit of the header of the last transmitted packet + uint8_t nextTxSn:1; //!< The SN bit of the header of the next packet to transmit + uint8_t bFirstPkt:1; //!< For slave: 0 if a packet has been transmitted on the connection, 1 otherwise + uint8_t bAutoEmpty:1; //!< 1 if the last transmitted packet was an auto-empty packet + uint8_t bLlCtrlTx:1; //!< 1 if the last transmitted packet was an LL control packet (LLID = 11) + uint8_t bLlCtrlAckRx:1; //!< 1 if the last received packet was the ACK of an LL control packet + uint8_t bLlCtrlAckPending:1; //!< 1 if the last successfully received packet was an LL control packet which has not yet been ACK'ed + } seqStat; + uint8_t maxNack; //!< Maximum number of NACKs received before operation ends. 0: No limit + uint8_t maxPkt; //!< Maximum number of packets transmitted in the operation before it ends. 0: No limit + uint32_t accessAddress; //!< Access address used on the connection + uint8_t crcInit0; //!< CRC initialization value used on the connection – least significant byte + uint8_t crcInit1; //!< CRC initialization value used on the connection – middle byte + uint8_t crcInit2; //!< CRC initialization value used on the connection – most significant byte + 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 connection event as soon as allowed + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to end the + //!< connection event as soon as allowed +}; + +//! @} + +//! \addtogroup bleSlavePar +//! @{ +//! Parameter structure for slave (CMD_BLE_SLAVE) + +struct __RFC_STRUCT rfc_bleSlavePar_s { + dataQueue_t* pRxQ; //!< Pointer to receive queue + dataQueue_t* pTxQ; //!< Pointer to transmit queue + struct { + uint8_t bAutoFlushIgnored:1; //!< If 1, automatically remove ignored packets from Rx queue + uint8_t bAutoFlushCrcErr:1; //!< If 1, automatically remove packets with CRC error from Rx queue + uint8_t bAutoFlushEmpty:1; //!< If 1, automatically remove empty packets from Rx queue + uint8_t bIncludeLenByte:1; //!< If 1, include the received length byte 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 bAppendStatus:1; //!< If 1, append a status byte to the packet in the Rx queue + uint8_t bAppendTimestamp:1; //!< If 1, append a timestamp to the packet in the Rx queue + } rxConfig; //!< Configuration bits for the receive queue entries + struct { + uint8_t lastRxSn:1; //!< The SN bit of the header of the last packet received with CRC OK + uint8_t lastTxSn:1; //!< The SN bit of the header of the last transmitted packet + uint8_t nextTxSn:1; //!< The SN bit of the header of the next packet to transmit + uint8_t bFirstPkt:1; //!< For slave: 0 if a packet has been transmitted on the connection, 1 otherwise + uint8_t bAutoEmpty:1; //!< 1 if the last transmitted packet was an auto-empty packet + uint8_t bLlCtrlTx:1; //!< 1 if the last transmitted packet was an LL control packet (LLID = 11) + uint8_t bLlCtrlAckRx:1; //!< 1 if the last received packet was the ACK of an LL control packet + uint8_t bLlCtrlAckPending:1; //!< 1 if the last successfully received packet was an LL control packet which has not yet been ACK'ed + } seqStat; + uint8_t maxNack; //!< Maximum number of NACKs received before operation ends. 0: No limit + uint8_t maxPkt; //!< Maximum number of packets transmitted in the operation before it ends. 0: No limit + uint32_t accessAddress; //!< Access address used on the connection + uint8_t crcInit0; //!< CRC initialization value used on the connection – least significant byte + uint8_t crcInit1; //!< CRC initialization value used on the connection – middle byte + uint8_t crcInit2; //!< CRC initialization value used on the connection – most significant byte + 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 + } timeoutTrigger; //!< Trigger that defines timeout of the first receive operation + ratmr_t timeoutTime; //!< \brief Time used together with timeoutTrigger that defines timeout of the first + //!< receive operation + uint16_t __dummy0; + uint8_t __dummy1; + 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 connection event as soon as allowed + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to end the + //!< connection event as soon as allowed +}; + +//! @} + +//! \addtogroup bleAdvPar +//! @{ +//! Parameter structure for advertiser (CMD_BLE_ADV*) + +struct __RFC_STRUCT rfc_bleAdvPar_s { + dataQueue_t* pRxQ; //!< Pointer to receive queue + struct { + uint8_t bAutoFlushIgnored:1; //!< If 1, automatically remove ignored packets from Rx queue + uint8_t bAutoFlushCrcErr:1; //!< If 1, automatically remove packets with CRC error from Rx queue + uint8_t bAutoFlushEmpty:1; //!< If 1, automatically remove empty packets from Rx queue + uint8_t bIncludeLenByte:1; //!< If 1, include the received length byte 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 bAppendStatus:1; //!< If 1, append a status byte to the packet in the Rx queue + uint8_t bAppendTimestamp:1; //!< If 1, append a timestamp to the packet in the Rx queue + } rxConfig; //!< Configuration bits for the receive queue entries + struct { + uint8_t advFilterPolicy:2; //!< \brief The advertiser filter policy, as defined in Volume 2, Part E, Section 7.8.5 of + //!< the Bluetooth 4.0 spec + uint8_t deviceAddrType:1; //!< The type of the device address – public (0) or random (1) + uint8_t peerAddrType:1; //!< Directed advertiser: The type of the peer address – public (0) or random (1) + uint8_t bStrictLenFilter:1; //!< 1: Discard messages with illegal length + } advConfig; + uint8_t advLen; //!< Size of advertiser data + uint8_t scanRspLen; //!< Size of scan response data + uint8_t* pAdvData; //!< Pointer to buffer containing ADV*_IND data + uint8_t* pScanRspData; //!< Pointer to buffer containing SCAN_RSP data + uint16_t* pDeviceAddress; //!< Pointer to device address used for this device + rfc_bleWhiteListEntry_t *pWhiteList; //!< Pointer to white list or peer address (directed advertiser) + uint16_t __dummy0; + uint8_t __dummy1; + 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 advertiser event as soon as allowed + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to end the + //!< advertiser event as soon as allowed +}; + +//! @} + +//! \addtogroup bleScannerPar +//! @{ +//! Parameter structure for scanner (CMD_BLE_SCANNER) + +struct __RFC_STRUCT rfc_bleScannerPar_s { + dataQueue_t* pRxQ; //!< Pointer to receive queue + struct { + uint8_t bAutoFlushIgnored:1; //!< If 1, automatically remove ignored packets from Rx queue + uint8_t bAutoFlushCrcErr:1; //!< If 1, automatically remove packets with CRC error from Rx queue + uint8_t bAutoFlushEmpty:1; //!< If 1, automatically remove empty packets from Rx queue + uint8_t bIncludeLenByte:1; //!< If 1, include the received length byte 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 bAppendStatus:1; //!< If 1, append a status byte to the packet in the Rx queue + uint8_t bAppendTimestamp:1; //!< If 1, append a timestamp to the packet in the Rx queue + } rxConfig; //!< Configuration bits for the receive queue entries + struct { + uint8_t scanFilterPolicy:1; //!< \brief The advertiser filter policy, as defined in Volume 2, Part E, Section 7.8.10 of + //!< the Bluetooth 4.0 spec + uint8_t bActiveScan:1; //!< \brief 0: Passive scan
+ //!< 1: Active scan + uint8_t deviceAddrType:1; //!< The type of the device address – public (0) or random (1) + uint8_t :1; + uint8_t bStrictLenFilter:1; //!< 1: Discard messages with illegal length + uint8_t bAutoWlIgnore:1; //!< 1: Automatically set ignore bit in white list + uint8_t bEndOnRpt:1; //!< 1: End scanner operation after each reported ADV*_IND and potentially SCAN_RSP + } scanConfig; + uint16_t randomState; //!< State for pseudo-random number generation used in backoff procedure + uint16_t backoffCount; //!< Parameter backoffCount used in backoff procedure, cf. Bluetooth 4.0 spec + struct { + uint8_t logUpperLimit:4; //!< Binary logarithm of parameter upperLimit used in scanner backoff procedure + uint8_t bLastSucceeded:1; //!< \brief 1 if the last SCAN_RSP was successfully received and upperLimit + //!< not changed + uint8_t bLastFailed:1; //!< \brief 1 if reception of the last SCAN_RSP failed and upperLimit was not + //!< changed + } backoffPar; + uint8_t scanReqLen; //!< Size of scan request data + uint8_t* pScanReqData; //!< Pointer to buffer containing SCAN_REQ data + uint16_t* pDeviceAddress; //!< Pointer to device address used for this device + rfc_bleWhiteListEntry_t *pWhiteList; //!< Pointer to white list + uint16_t __dummy0; + 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 + } timeoutTrigger; //!< Trigger that causes the device to stop receiving as soon as allowed + 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 stop receiving as soon as allowed + ratmr_t timeoutTime; //!< \brief Time used together with timeoutTrigger that causes the device to stop + //!< receiving as soon as allowed, ending with BLE_DONE_RXTIMEOUT + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to stop + //!< receiving as soon as allowed, ending with BLE_DONE_ENDED +}; + +//! @} + +//! \addtogroup bleInitiatorPar +//! @{ +//! Parameter structure for initiator (CMD_BLE_INITIATOR) + +struct __RFC_STRUCT rfc_bleInitiatorPar_s { + dataQueue_t* pRxQ; //!< Pointer to receive queue + struct { + uint8_t bAutoFlushIgnored:1; //!< If 1, automatically remove ignored packets from Rx queue + uint8_t bAutoFlushCrcErr:1; //!< If 1, automatically remove packets with CRC error from Rx queue + uint8_t bAutoFlushEmpty:1; //!< If 1, automatically remove empty packets from Rx queue + uint8_t bIncludeLenByte:1; //!< If 1, include the received length byte 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 bAppendStatus:1; //!< If 1, append a status byte to the packet in the Rx queue + uint8_t bAppendTimestamp:1; //!< If 1, append a timestamp to the packet in the Rx queue + } rxConfig; //!< Configuration bits for the receive queue entries + struct { + uint8_t bUseWhiteList:1; //!< \brief Initiator filter policy, cf. Volume 2, Part E, Section 7.8.10 of the + //!< Bluetooth 4.0 spec:
+ //!< 0: Use specific peer address
+ //!< 1: Use white list + uint8_t bDynamicWinOffset:1; //!< 1: Use dynamic WinOffset insertion + uint8_t deviceAddrType:1; //!< The type of the device address – public (0) or random (1) + uint8_t peerAddrType:1; //!< The type of the peer address – public (0) or random (1) + uint8_t bStrictLenFilter:1; //!< 1: Discard messages with illegal length + } initConfig; + uint8_t __dummy0; + uint8_t connectReqLen; //!< Size of connect request data + uint8_t* pConnectReqData; //!< Pointer to buffer containing LLData to go in the CONNECT_REQ + uint16_t* pDeviceAddress; //!< Pointer to device address used for this device + rfc_bleWhiteListEntry_t *pWhiteList; //!< Pointer to white list or peer address + ratmr_t connectTime; //!< \brief Indication of timer value of the first possible start time of the first connection event. + //!< Set to the calculated value if a connection is made and to the next possible connection + //!< time if not. + uint16_t __dummy1; + 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 + } timeoutTrigger; //!< Trigger that causes the device to stop receiving as soon as allowed + 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 stop receiving as soon as allowed + ratmr_t timeoutTime; //!< \brief Time used together with timeoutTrigger that causes the device to stop + //!< receiving as soon as allowed, ending with BLE_DONE_RXTIMEOUT + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to stop + //!< receiving as soon as allowed, ending with BLE_DONE_ENDED +}; + +//! @} + +//! \addtogroup bleGenericRxPar +//! @{ +//! Parameter structure for generic Rx (CMD_BLE_GENERIC_RX) + +struct __RFC_STRUCT rfc_bleGenericRxPar_s { + dataQueue_t* pRxQ; //!< Pointer to receive queue. May be NULL; if so, received packets are not stored + struct { + uint8_t bAutoFlushIgnored:1; //!< If 1, automatically remove ignored packets from Rx queue + uint8_t bAutoFlushCrcErr:1; //!< If 1, automatically remove packets with CRC error from Rx queue + uint8_t bAutoFlushEmpty:1; //!< If 1, automatically remove empty packets from Rx queue + uint8_t bIncludeLenByte:1; //!< If 1, include the received length byte 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 bAppendStatus:1; //!< If 1, append a status byte to the packet in the Rx queue + uint8_t bAppendTimestamp:1; //!< If 1, append a timestamp to the packet in the Rx queue + } rxConfig; //!< Configuration bits for the receive queue entries + uint8_t bRepeat; //!< \brief 0: End operation after receiving a packet
+ //!< 1: Restart receiver after receiving a packet + uint16_t __dummy0; + uint32_t accessAddress; //!< Access address used on the connection + uint8_t crcInit0; //!< CRC initialization value used on the connection – least significant byte + uint8_t crcInit1; //!< CRC initialization value used on the connection – middle byte + uint8_t crcInit2; //!< CRC initialization value used on the connection – most significant byte + 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 +}; + +//! @} + +//! \addtogroup bleTxTestPar +//! @{ +//! Parameter structure for Tx test (CMD_BLE_TX_TEST) + +struct __RFC_STRUCT rfc_bleTxTestPar_s { + uint16_t numPackets; //!< \brief Number of packets to transmit
+ //!< 0: Transmit unlimited number of packets + uint8_t payloadLength; //!< The number of payload bytes in each packet. + uint8_t packetType; //!< \brief The packet type to be used, encoded according to the Bluetooth 4.0 spec, Volume 2, Part E, + //!< Section 7.8.29 + ratmr_t period; //!< Number of radio timer cycles between the start of each packet + struct { + uint8_t bOverrideDefault:1; //!< \brief 0: Use default packet encoding
+ //!< 1: Override packet contents + uint8_t bUsePrbs9:1; //!< \brief If bOverride is 1:
+ //!< 1: Use PRBS9 encoding of packet + uint8_t bUsePrbs15:1; //!< \brief If bOverride is 1:
+ //!< 1: Use PRBS15 encoding of packet + } config; + uint8_t byteVal; //!< If config.bOverride is 1, value of each byte to be sent + uint8_t __dummy0; + 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 Test Tx operation + ratmr_t endTime; //!< \brief Time used together with endTrigger that causes the device to end the + //!< Test Tx operation +}; + +//! @} + +//! \addtogroup bleMasterSlaveOutput +//! @{ +//! Output structure for master and slave (CMD_BLE_MASTER/CMD_BLE_SLAVE) + +struct __RFC_STRUCT rfc_bleMasterSlaveOutput_s { + uint8_t nTx; //!< \brief Total number of packets (including auto-empty and retransmissions) that have been + //!< transmitted + uint8_t nTxAck; //!< Total number of transmitted packets (including auto-empty) that have been ACK'ed + uint8_t nTxCtrl; //!< Number of unique LL control packets from the Tx queue that have been transmitted + uint8_t nTxCtrlAck; //!< Number of LL control packets from the Tx queue that have been finished (ACK'ed) + uint8_t nTxCtrlAckAck; //!< \brief Number of LL control packets that have been ACK'ed and where an ACK has been sent in + //!< response + uint8_t nTxRetrans; //!< Number of retransmissions that has been done + uint8_t nTxEntryDone; //!< Number of packets from the Tx queue that have been finished (ACK'ed) + uint8_t nRxOk; //!< Number of packets that have been received with payload, CRC OK and not ignored + uint8_t nRxCtrl; //!< Number of LL control packets that have been received with CRC OK and not ignored + uint8_t nRxCtrlAck; //!< \brief Number of LL control packets that have been received with CRC OK and not ignored, and + //!< then ACK'ed + uint8_t nRxNok; //!< Number of packets that have been received with CRC error + uint8_t nRxIgnored; //!< \brief Number of packets that have been received with CRC OK and ignored due to repeated + //!< sequence number + uint8_t nRxEmpty; //!< Number of packets that have been received with CRC OK and no payload + uint8_t nRxBufFull; //!< Number of packets that have been received and discarded due to lack of buffer space + int8_t lastRssi; //!< RSSI of last received packet + struct { + uint8_t bTimeStampValid:1; //!< 1 if a valid time stamp has been written to timeStamp; 0 otherwise + uint8_t bLastCrcErr:1; //!< 1 if the last received packet had CRC error; 0 otherwise + uint8_t bLastIgnored:1; //!< 1 if the last received packet with CRC OK was ignored; 0 otherwise + uint8_t bLastEmpty:1; //!< 1 if the last received packet with CRC OK was empty; 0 otherwise + uint8_t bLastCtrl:1; //!< 1 if the last received packet with CRC OK was empty; 0 otherwise + uint8_t bLastMd:1; //!< 1 if the last received packet with CRC OK had MD = 1; 0 otherwise + uint8_t bLastAck:1; //!< \brief 1 if the last received packet with CRC OK was an ACK of a transmitted packet; + //!< 0 otherwise + } pktStatus; + ratmr_t timeStamp; //!< Slave operation: Time stamp of first received packet +}; + +//! @} + +//! \addtogroup bleAdvOutput +//! @{ +//! Output structure for advertiser (CMD_BLE_ADV*) + +struct __RFC_STRUCT rfc_bleAdvOutput_s { + uint16_t nTxAdvInd; //!< Number of ADV*_IND packets completely transmitted + uint8_t nTxScanRsp; //!< Number of SCAN_RSP packets transmitted + uint8_t nRxScanReq; //!< Number of SCAN_REQ packets received OK and not ignored + uint8_t nRxConnectReq; //!< Number of CONNECT_REQ packets received OK and not ignored + uint8_t __dummy0; + uint16_t nRxNok; //!< Number of packets received with CRC error + uint16_t nRxIgnored; //!< Number of packets received with CRC OK, but ignored + uint8_t nRxBufFull; //!< Number of packets received that did not fit in Rx queue + int8_t lastRssi; //!< The RSSI of the last received packet + ratmr_t timeStamp; //!< Time stamp of the last received packet +}; + +//! @} + +//! \addtogroup bleScannerOutput +//! @{ +//! Output structure for scanner (CMD_BLE_SCANNER) + +struct __RFC_STRUCT rfc_bleScannerOutput_s { + uint16_t nTxScanReq; //!< Number of transmitted SCAN_REQ packets + uint16_t nBackedOffScanReq; //!< Number of SCAN_REQ packets not sent due to backoff procedure + uint16_t nRxAdvOk; //!< Number of ADV*_IND packets received with CRC OK and not ignored + uint16_t nRxAdvIgnored; //!< Number of ADV*_IND packets received with CRC OK, but ignored + uint16_t nRxAdvNok; //!< Number of ADV*_IND packets received with CRC error + uint16_t nRxScanRspOk; //!< Number of SCAN_RSP packets received with CRC OK and not ignored + uint16_t nRxScanRspIgnored; //!< Number of SCAN_RSP packets received with CRC OK, but ignored + uint16_t nRxScanRspNok; //!< Number of SCAN_RSP packets received with CRC error + uint8_t nRxAdvBufFull; //!< Number of ADV*_IND packets received that did not fit in Rx queue + uint8_t nRxScanRspBufFull; //!< Number of SCAN_RSP packets received that did not fit in Rx queue + int8_t lastRssi; //!< The RSSI of the last received packet + uint8_t __dummy0; + ratmr_t timeStamp; //!< Time stamp of the last successfully received ADV*_IND packet that was not ignored +}; + +//! @} + +//! \addtogroup bleInitiatorOutput +//! @{ +//! Output structure for initiator (CMD_BLE_INITIATOR) + +struct __RFC_STRUCT rfc_bleInitiatorOutput_s { + uint8_t nTxConnectReq; //!< Number of transmitted CONNECT_REQ packets + uint8_t nRxAdvOk; //!< Number of ADV*_IND packets received with CRC OK and not ignored + uint16_t nRxAdvIgnored; //!< Number of ADV*_IND packets received with CRC OK, but ignored + uint16_t nRxAdvNok; //!< Number of ADV*_IND packets received with CRC error + uint8_t nRxAdvBufFull; //!< Number of ADV*_IND packets received that did not fit in Rx queue + int8_t lastRssi; //!< The RSSI of the last received packet + ratmr_t timeStamp; //!< Time stamp of the received ADV*_IND packet that caused transmission of CONNECT_REQ +}; + +//! @} + +//! \addtogroup bleGenericRxOutput +//! @{ +//! Output structure for generic Rx (CMD_BLE_GENERIC_RX) + +struct __RFC_STRUCT rfc_bleGenericRxOutput_s { + uint16_t nRxOk; //!< Number of packets received with CRC OK + uint16_t nRxNok; //!< Number of packets received with CRC error + uint16_t nRxBufFull; //!< Number of packets that have been received and discarded due to lack of buffer space + int8_t lastRssi; //!< The RSSI of the last received packet + uint8_t __dummy0; + ratmr_t timeStamp; //!< Time stamp of the last received packet +}; + +//! @} + +//! \addtogroup bleTxTestOutput +//! @{ +//! Output structure for Tx test (CMD_BLE_TX_TEST) + +struct __RFC_STRUCT rfc_bleTxTestOutput_s { + uint16_t nTx; //!< Number of packets transmitted +}; + +//! @} + +//! \addtogroup bleWhiteListEntry +//! @{ +//! White list entry structure + +struct __RFC_STRUCT rfc_bleWhiteListEntry_s { + uint8_t size; //!< Number of while list entries. Used in the first entry of the list only + struct { + uint8_t bEnable:1; //!< 1 if the entry is in use, 0 if the entry is not in use + uint8_t addrType:1; //!< The type address in the entry – public (0) or random (1) + uint8_t bWlIgn:1; //!< \brief 1 if the entry is to be ignored by a scanner, 0 otherwise. Used to mask out + //!< entries that have already been scanned and reported. + } conf; + uint16_t address; //!< Least significant 16 bits of the address contained in the entry + uint32_t addressHi; //!< Most significant 32 bits of the address contained in the entry +}; + +//! @} + +//! \addtogroup bleRxStatus +//! @{ +//! Receive status byte that may be appended to message in receive buffer + +struct __RFC_STRUCT rfc_bleRxStatus_s { + struct { + uint8_t channel:6; //!< \brief The channel on which the packet was received, provided channel is in the range + //!< 0–39; otherwise 0x3F + uint8_t bIgnore:1; //!< 1 if the packet is marked as ignored, 0 otherwise + uint8_t bCrcErr:1; //!< 1 if the packet was received with CRC error, 0 otherwise + } status; +}; + +//! @} + +//! @} +//! @} +#endif diff --git a/cpu/cc26xx/dev/rfc-api/ble_cmd_field.h b/cpu/cc26xx/dev/rfc-api/ble_cmd_field.h new file mode 100755 index 000000000..307e67dc6 --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/ble_cmd_field.h @@ -0,0 +1,623 @@ +/****************************************************************************** +* Filename: ble_cmd_field.h +* Revised: $ $ +* Revision: $ $ +* +* Description: CC26xx/CC13xx API for Bluetooth Low Energy commands +* +* Copyright (C) 2015 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 __BLE_CMD_FIELD_H +#define __BLE_CMD_FIELD_H + +#include +#include "mailbox.h" +#include "common_cmd.h" + +#define _POSITION_bleRadioOp_channel 14 +#define _TYPE_bleRadioOp_channel uint8_t +#define _POSITION_bleRadioOp_whitening 15 +#define _TYPE_bleRadioOp_whitening uint8_t +#define _BITPOS_bleRadioOp_whitening_init 0 +#define _NBITS_bleRadioOp_whitening_init 7 +#define _BITPOS_bleRadioOp_whitening_bOverride 7 +#define _NBITS_bleRadioOp_whitening_bOverride 1 +#define _POSITION_bleRadioOp_pParams 16 +#define _TYPE_bleRadioOp_pParams uint8_t* +#define _POSITION_bleRadioOp_pOutput 20 +#define _TYPE_bleRadioOp_pOutput uint8_t* +#define _SIZEOF_bleRadioOp 24 + +#define _SIZEOF_CMD_BLE_SLAVE 24 + +#define _SIZEOF_CMD_BLE_MASTER 24 + +#define _SIZEOF_CMD_BLE_ADV 24 + +#define _SIZEOF_CMD_BLE_ADV_DIR 24 + +#define _SIZEOF_CMD_BLE_ADV_NC 24 + +#define _SIZEOF_CMD_BLE_ADV_SCAN 24 + +#define _SIZEOF_CMD_BLE_SCANNER 24 + +#define _SIZEOF_CMD_BLE_INITIATOR 24 + +#define _SIZEOF_CMD_BLE_GENERIC_RX 24 + +#define _SIZEOF_CMD_BLE_TX_TEST 24 + +#define _POSITION_CMD_BLE_ADV_PAYLOAD_payloadType 2 +#define _TYPE_CMD_BLE_ADV_PAYLOAD_payloadType uint8_t +#define _POSITION_CMD_BLE_ADV_PAYLOAD_newLen 3 +#define _TYPE_CMD_BLE_ADV_PAYLOAD_newLen uint8_t +#define _POSITION_CMD_BLE_ADV_PAYLOAD_pNewData 4 +#define _TYPE_CMD_BLE_ADV_PAYLOAD_pNewData uint8_t* +#define _POSITION_CMD_BLE_ADV_PAYLOAD_pParams 8 +#define _TYPE_CMD_BLE_ADV_PAYLOAD_pParams uint8_t* +#define _SIZEOF_CMD_BLE_ADV_PAYLOAD 12 + +#define _POSITION_bleMasterSlavePar_pRxQ 0 +#define _TYPE_bleMasterSlavePar_pRxQ dataQueue_t* +#define _POSITION_bleMasterSlavePar_pTxQ 4 +#define _TYPE_bleMasterSlavePar_pTxQ dataQueue_t* +#define _POSITION_bleMasterSlavePar_rxConfig 8 +#define _TYPE_bleMasterSlavePar_rxConfig uint8_t +#define _BITPOS_bleMasterSlavePar_rxConfig_bAutoFlushIgnored 0 +#define _NBITS_bleMasterSlavePar_rxConfig_bAutoFlushIgnored 1 +#define _BITPOS_bleMasterSlavePar_rxConfig_bAutoFlushCrcErr 1 +#define _NBITS_bleMasterSlavePar_rxConfig_bAutoFlushCrcErr 1 +#define _BITPOS_bleMasterSlavePar_rxConfig_bAutoFlushEmpty 2 +#define _NBITS_bleMasterSlavePar_rxConfig_bAutoFlushEmpty 1 +#define _BITPOS_bleMasterSlavePar_rxConfig_bIncludeLenByte 3 +#define _NBITS_bleMasterSlavePar_rxConfig_bIncludeLenByte 1 +#define _BITPOS_bleMasterSlavePar_rxConfig_bIncludeCrc 4 +#define _NBITS_bleMasterSlavePar_rxConfig_bIncludeCrc 1 +#define _BITPOS_bleMasterSlavePar_rxConfig_bAppendRssi 5 +#define _NBITS_bleMasterSlavePar_rxConfig_bAppendRssi 1 +#define _BITPOS_bleMasterSlavePar_rxConfig_bAppendStatus 6 +#define _NBITS_bleMasterSlavePar_rxConfig_bAppendStatus 1 +#define _BITPOS_bleMasterSlavePar_rxConfig_bAppendTimestamp 7 +#define _NBITS_bleMasterSlavePar_rxConfig_bAppendTimestamp 1 +#define _POSITION_bleMasterSlavePar_seqStat 9 +#define _TYPE_bleMasterSlavePar_seqStat uint8_t +#define _BITPOS_bleMasterSlavePar_seqStat_lastRxSn 0 +#define _NBITS_bleMasterSlavePar_seqStat_lastRxSn 1 +#define _BITPOS_bleMasterSlavePar_seqStat_lastTxSn 1 +#define _NBITS_bleMasterSlavePar_seqStat_lastTxSn 1 +#define _BITPOS_bleMasterSlavePar_seqStat_nextTxSn 2 +#define _NBITS_bleMasterSlavePar_seqStat_nextTxSn 1 +#define _BITPOS_bleMasterSlavePar_seqStat_bFirstPkt 3 +#define _NBITS_bleMasterSlavePar_seqStat_bFirstPkt 1 +#define _BITPOS_bleMasterSlavePar_seqStat_bAutoEmpty 4 +#define _NBITS_bleMasterSlavePar_seqStat_bAutoEmpty 1 +#define _BITPOS_bleMasterSlavePar_seqStat_bLlCtrlTx 5 +#define _NBITS_bleMasterSlavePar_seqStat_bLlCtrlTx 1 +#define _BITPOS_bleMasterSlavePar_seqStat_bLlCtrlAckRx 6 +#define _NBITS_bleMasterSlavePar_seqStat_bLlCtrlAckRx 1 +#define _BITPOS_bleMasterSlavePar_seqStat_bLlCtrlAckPending 7 +#define _NBITS_bleMasterSlavePar_seqStat_bLlCtrlAckPending 1 +#define _POSITION_bleMasterSlavePar_maxNack 10 +#define _TYPE_bleMasterSlavePar_maxNack uint8_t +#define _POSITION_bleMasterSlavePar_maxPkt 11 +#define _TYPE_bleMasterSlavePar_maxPkt uint8_t +#define _POSITION_bleMasterSlavePar_accessAddress 12 +#define _TYPE_bleMasterSlavePar_accessAddress uint32_t +#define _POSITION_bleMasterSlavePar_crcInit0 16 +#define _TYPE_bleMasterSlavePar_crcInit0 uint8_t +#define _POSITION_bleMasterSlavePar_crcInit1 17 +#define _TYPE_bleMasterSlavePar_crcInit1 uint8_t +#define _POSITION_bleMasterSlavePar_crcInit2 18 +#define _TYPE_bleMasterSlavePar_crcInit2 uint8_t +#define _POSITION_bleMasterSlavePar_crcInit 16 +#define _TYPE_bleMasterSlavePar_crcInit uint32_t +#define _SIZEOF_bleMasterSlavePar 20 + +#define _POSITION_bleMasterPar_endTrigger 19 +#define _TYPE_bleMasterPar_endTrigger uint8_t +#define _BITPOS_bleMasterPar_endTrigger_triggerType 0 +#define _NBITS_bleMasterPar_endTrigger_triggerType 4 +#define _BITPOS_bleMasterPar_endTrigger_bEnaCmd 4 +#define _NBITS_bleMasterPar_endTrigger_bEnaCmd 1 +#define _BITPOS_bleMasterPar_endTrigger_triggerNo 5 +#define _NBITS_bleMasterPar_endTrigger_triggerNo 2 +#define _BITPOS_bleMasterPar_endTrigger_pastTrig 7 +#define _NBITS_bleMasterPar_endTrigger_pastTrig 1 +#define _POSITION_bleMasterPar_endTime 20 +#define _TYPE_bleMasterPar_endTime ratmr_t +#define _SIZEOF_bleMasterPar 24 + +#define _POSITION_bleSlavePar_timeoutTrigger 19 +#define _TYPE_bleSlavePar_timeoutTrigger uint8_t +#define _BITPOS_bleSlavePar_timeoutTrigger_triggerType 0 +#define _NBITS_bleSlavePar_timeoutTrigger_triggerType 4 +#define _BITPOS_bleSlavePar_timeoutTrigger_bEnaCmd 4 +#define _NBITS_bleSlavePar_timeoutTrigger_bEnaCmd 1 +#define _BITPOS_bleSlavePar_timeoutTrigger_triggerNo 5 +#define _NBITS_bleSlavePar_timeoutTrigger_triggerNo 2 +#define _BITPOS_bleSlavePar_timeoutTrigger_pastTrig 7 +#define _NBITS_bleSlavePar_timeoutTrigger_pastTrig 1 +#define _POSITION_bleSlavePar_timeoutTime 20 +#define _TYPE_bleSlavePar_timeoutTime ratmr_t +#define _POSITION_bleSlavePar_endTrigger 27 +#define _TYPE_bleSlavePar_endTrigger uint8_t +#define _BITPOS_bleSlavePar_endTrigger_triggerType 0 +#define _NBITS_bleSlavePar_endTrigger_triggerType 4 +#define _BITPOS_bleSlavePar_endTrigger_bEnaCmd 4 +#define _NBITS_bleSlavePar_endTrigger_bEnaCmd 1 +#define _BITPOS_bleSlavePar_endTrigger_triggerNo 5 +#define _NBITS_bleSlavePar_endTrigger_triggerNo 2 +#define _BITPOS_bleSlavePar_endTrigger_pastTrig 7 +#define _NBITS_bleSlavePar_endTrigger_pastTrig 1 +#define _POSITION_bleSlavePar_endTime 28 +#define _TYPE_bleSlavePar_endTime ratmr_t +#define _SIZEOF_bleSlavePar 32 + +#define _POSITION_bleAdvPar_pRxQ 0 +#define _TYPE_bleAdvPar_pRxQ dataQueue_t* +#define _POSITION_bleAdvPar_rxConfig 4 +#define _TYPE_bleAdvPar_rxConfig uint8_t +#define _BITPOS_bleAdvPar_rxConfig_bAutoFlushIgnored 0 +#define _NBITS_bleAdvPar_rxConfig_bAutoFlushIgnored 1 +#define _BITPOS_bleAdvPar_rxConfig_bAutoFlushCrcErr 1 +#define _NBITS_bleAdvPar_rxConfig_bAutoFlushCrcErr 1 +#define _BITPOS_bleAdvPar_rxConfig_bAutoFlushEmpty 2 +#define _NBITS_bleAdvPar_rxConfig_bAutoFlushEmpty 1 +#define _BITPOS_bleAdvPar_rxConfig_bIncludeLenByte 3 +#define _NBITS_bleAdvPar_rxConfig_bIncludeLenByte 1 +#define _BITPOS_bleAdvPar_rxConfig_bIncludeCrc 4 +#define _NBITS_bleAdvPar_rxConfig_bIncludeCrc 1 +#define _BITPOS_bleAdvPar_rxConfig_bAppendRssi 5 +#define _NBITS_bleAdvPar_rxConfig_bAppendRssi 1 +#define _BITPOS_bleAdvPar_rxConfig_bAppendStatus 6 +#define _NBITS_bleAdvPar_rxConfig_bAppendStatus 1 +#define _BITPOS_bleAdvPar_rxConfig_bAppendTimestamp 7 +#define _NBITS_bleAdvPar_rxConfig_bAppendTimestamp 1 +#define _POSITION_bleAdvPar_advConfig 5 +#define _TYPE_bleAdvPar_advConfig uint8_t +#define _BITPOS_bleAdvPar_advConfig_advFilterPolicy 0 +#define _NBITS_bleAdvPar_advConfig_advFilterPolicy 2 +#define _BITPOS_bleAdvPar_advConfig_deviceAddrType 2 +#define _NBITS_bleAdvPar_advConfig_deviceAddrType 1 +#define _BITPOS_bleAdvPar_advConfig_peerAddrType 3 +#define _NBITS_bleAdvPar_advConfig_peerAddrType 1 +#define _BITPOS_bleAdvPar_advConfig_bStrictLenFilter 4 +#define _NBITS_bleAdvPar_advConfig_bStrictLenFilter 1 +#define _POSITION_bleAdvPar_advLen 6 +#define _TYPE_bleAdvPar_advLen uint8_t +#define _POSITION_bleAdvPar_scanRspLen 7 +#define _TYPE_bleAdvPar_scanRspLen uint8_t +#define _POSITION_bleAdvPar_pAdvData 8 +#define _TYPE_bleAdvPar_pAdvData uint8_t* +#define _POSITION_bleAdvPar_pScanRspData 12 +#define _TYPE_bleAdvPar_pScanRspData uint8_t* +#define _POSITION_bleAdvPar_pDeviceAddress 16 +#define _TYPE_bleAdvPar_pDeviceAddress uint16_t* +#define _POSITION_bleAdvPar_pWhiteList 20 +#define _TYPE_bleAdvPar_pWhiteList uint32_t* +#define _POSITION_bleAdvPar_endTrigger 27 +#define _TYPE_bleAdvPar_endTrigger uint8_t +#define _BITPOS_bleAdvPar_endTrigger_triggerType 0 +#define _NBITS_bleAdvPar_endTrigger_triggerType 4 +#define _BITPOS_bleAdvPar_endTrigger_bEnaCmd 4 +#define _NBITS_bleAdvPar_endTrigger_bEnaCmd 1 +#define _BITPOS_bleAdvPar_endTrigger_triggerNo 5 +#define _NBITS_bleAdvPar_endTrigger_triggerNo 2 +#define _BITPOS_bleAdvPar_endTrigger_pastTrig 7 +#define _NBITS_bleAdvPar_endTrigger_pastTrig 1 +#define _POSITION_bleAdvPar_endTime 28 +#define _TYPE_bleAdvPar_endTime ratmr_t +#define _SIZEOF_bleAdvPar 32 + +#define _POSITION_bleScannerPar_pRxQ 0 +#define _TYPE_bleScannerPar_pRxQ dataQueue_t* +#define _POSITION_bleScannerPar_rxConfig 4 +#define _TYPE_bleScannerPar_rxConfig uint8_t +#define _BITPOS_bleScannerPar_rxConfig_bAutoFlushIgnored 0 +#define _NBITS_bleScannerPar_rxConfig_bAutoFlushIgnored 1 +#define _BITPOS_bleScannerPar_rxConfig_bAutoFlushCrcErr 1 +#define _NBITS_bleScannerPar_rxConfig_bAutoFlushCrcErr 1 +#define _BITPOS_bleScannerPar_rxConfig_bAutoFlushEmpty 2 +#define _NBITS_bleScannerPar_rxConfig_bAutoFlushEmpty 1 +#define _BITPOS_bleScannerPar_rxConfig_bIncludeLenByte 3 +#define _NBITS_bleScannerPar_rxConfig_bIncludeLenByte 1 +#define _BITPOS_bleScannerPar_rxConfig_bIncludeCrc 4 +#define _NBITS_bleScannerPar_rxConfig_bIncludeCrc 1 +#define _BITPOS_bleScannerPar_rxConfig_bAppendRssi 5 +#define _NBITS_bleScannerPar_rxConfig_bAppendRssi 1 +#define _BITPOS_bleScannerPar_rxConfig_bAppendStatus 6 +#define _NBITS_bleScannerPar_rxConfig_bAppendStatus 1 +#define _BITPOS_bleScannerPar_rxConfig_bAppendTimestamp 7 +#define _NBITS_bleScannerPar_rxConfig_bAppendTimestamp 1 +#define _POSITION_bleScannerPar_scanConfig 5 +#define _TYPE_bleScannerPar_scanConfig uint8_t +#define _BITPOS_bleScannerPar_scanConfig_scanFilterPolicy 0 +#define _NBITS_bleScannerPar_scanConfig_scanFilterPolicy 1 +#define _BITPOS_bleScannerPar_scanConfig_bActiveScan 1 +#define _NBITS_bleScannerPar_scanConfig_bActiveScan 1 +#define _BITPOS_bleScannerPar_scanConfig_deviceAddrType 2 +#define _NBITS_bleScannerPar_scanConfig_deviceAddrType 1 +#define _BITPOS_bleScannerPar_scanConfig_bStrictLenFilter 4 +#define _NBITS_bleScannerPar_scanConfig_bStrictLenFilter 1 +#define _BITPOS_bleScannerPar_scanConfig_bAutoWlIgnore 5 +#define _NBITS_bleScannerPar_scanConfig_bAutoWlIgnore 1 +#define _BITPOS_bleScannerPar_scanConfig_bEndOnRpt 6 +#define _NBITS_bleScannerPar_scanConfig_bEndOnRpt 1 +#define _POSITION_bleScannerPar_randomState 6 +#define _TYPE_bleScannerPar_randomState uint16_t +#define _POSITION_bleScannerPar_backoffCount 8 +#define _TYPE_bleScannerPar_backoffCount uint16_t +#define _POSITION_bleScannerPar_backoffPar 10 +#define _TYPE_bleScannerPar_backoffPar uint8_t +#define _BITPOS_bleScannerPar_backoffPar_logUpperLimit 0 +#define _NBITS_bleScannerPar_backoffPar_logUpperLimit 4 +#define _BITPOS_bleScannerPar_backoffPar_bLastSucceeded 4 +#define _NBITS_bleScannerPar_backoffPar_bLastSucceeded 1 +#define _BITPOS_bleScannerPar_backoffPar_bLastFailed 5 +#define _NBITS_bleScannerPar_backoffPar_bLastFailed 1 +#define _POSITION_bleScannerPar_scanReqLen 11 +#define _TYPE_bleScannerPar_scanReqLen uint8_t +#define _POSITION_bleScannerPar_pScanReqData 12 +#define _TYPE_bleScannerPar_pScanReqData uint8_t* +#define _POSITION_bleScannerPar_pDeviceAddress 16 +#define _TYPE_bleScannerPar_pDeviceAddress uint16_t* +#define _POSITION_bleScannerPar_pWhiteList 20 +#define _TYPE_bleScannerPar_pWhiteList uint32_t* +#define _POSITION_bleScannerPar_timeoutTrigger 26 +#define _TYPE_bleScannerPar_timeoutTrigger uint8_t +#define _BITPOS_bleScannerPar_timeoutTrigger_triggerType 0 +#define _NBITS_bleScannerPar_timeoutTrigger_triggerType 4 +#define _BITPOS_bleScannerPar_timeoutTrigger_bEnaCmd 4 +#define _NBITS_bleScannerPar_timeoutTrigger_bEnaCmd 1 +#define _BITPOS_bleScannerPar_timeoutTrigger_triggerNo 5 +#define _NBITS_bleScannerPar_timeoutTrigger_triggerNo 2 +#define _BITPOS_bleScannerPar_timeoutTrigger_pastTrig 7 +#define _NBITS_bleScannerPar_timeoutTrigger_pastTrig 1 +#define _POSITION_bleScannerPar_endTrigger 27 +#define _TYPE_bleScannerPar_endTrigger uint8_t +#define _BITPOS_bleScannerPar_endTrigger_triggerType 0 +#define _NBITS_bleScannerPar_endTrigger_triggerType 4 +#define _BITPOS_bleScannerPar_endTrigger_bEnaCmd 4 +#define _NBITS_bleScannerPar_endTrigger_bEnaCmd 1 +#define _BITPOS_bleScannerPar_endTrigger_triggerNo 5 +#define _NBITS_bleScannerPar_endTrigger_triggerNo 2 +#define _BITPOS_bleScannerPar_endTrigger_pastTrig 7 +#define _NBITS_bleScannerPar_endTrigger_pastTrig 1 +#define _POSITION_bleScannerPar_timeoutTime 28 +#define _TYPE_bleScannerPar_timeoutTime ratmr_t +#define _POSITION_bleScannerPar_endTime 32 +#define _TYPE_bleScannerPar_endTime ratmr_t +#define _SIZEOF_bleScannerPar 36 + +#define _POSITION_bleInitiatorPar_pRxQ 0 +#define _TYPE_bleInitiatorPar_pRxQ dataQueue_t* +#define _POSITION_bleInitiatorPar_rxConfig 4 +#define _TYPE_bleInitiatorPar_rxConfig uint8_t +#define _BITPOS_bleInitiatorPar_rxConfig_bAutoFlushIgnored 0 +#define _NBITS_bleInitiatorPar_rxConfig_bAutoFlushIgnored 1 +#define _BITPOS_bleInitiatorPar_rxConfig_bAutoFlushCrcErr 1 +#define _NBITS_bleInitiatorPar_rxConfig_bAutoFlushCrcErr 1 +#define _BITPOS_bleInitiatorPar_rxConfig_bAutoFlushEmpty 2 +#define _NBITS_bleInitiatorPar_rxConfig_bAutoFlushEmpty 1 +#define _BITPOS_bleInitiatorPar_rxConfig_bIncludeLenByte 3 +#define _NBITS_bleInitiatorPar_rxConfig_bIncludeLenByte 1 +#define _BITPOS_bleInitiatorPar_rxConfig_bIncludeCrc 4 +#define _NBITS_bleInitiatorPar_rxConfig_bIncludeCrc 1 +#define _BITPOS_bleInitiatorPar_rxConfig_bAppendRssi 5 +#define _NBITS_bleInitiatorPar_rxConfig_bAppendRssi 1 +#define _BITPOS_bleInitiatorPar_rxConfig_bAppendStatus 6 +#define _NBITS_bleInitiatorPar_rxConfig_bAppendStatus 1 +#define _BITPOS_bleInitiatorPar_rxConfig_bAppendTimestamp 7 +#define _NBITS_bleInitiatorPar_rxConfig_bAppendTimestamp 1 +#define _POSITION_bleInitiatorPar_initConfig 5 +#define _TYPE_bleInitiatorPar_initConfig uint8_t +#define _BITPOS_bleInitiatorPar_initConfig_bUseWhiteList 0 +#define _NBITS_bleInitiatorPar_initConfig_bUseWhiteList 1 +#define _BITPOS_bleInitiatorPar_initConfig_bDynamicWinOffset 1 +#define _NBITS_bleInitiatorPar_initConfig_bDynamicWinOffset 1 +#define _BITPOS_bleInitiatorPar_initConfig_deviceAddrType 2 +#define _NBITS_bleInitiatorPar_initConfig_deviceAddrType 1 +#define _BITPOS_bleInitiatorPar_initConfig_peerAddrType 3 +#define _NBITS_bleInitiatorPar_initConfig_peerAddrType 1 +#define _BITPOS_bleInitiatorPar_initConfig_bStrictLenFilter 4 +#define _NBITS_bleInitiatorPar_initConfig_bStrictLenFilter 1 +#define _POSITION_bleInitiatorPar_connectReqLen 7 +#define _TYPE_bleInitiatorPar_connectReqLen uint8_t +#define _POSITION_bleInitiatorPar_pConnectReqData 8 +#define _TYPE_bleInitiatorPar_pConnectReqData uint8_t* +#define _POSITION_bleInitiatorPar_pDeviceAddress 12 +#define _TYPE_bleInitiatorPar_pDeviceAddress uint16_t* +#define _POSITION_bleInitiatorPar_pWhiteList 16 +#define _TYPE_bleInitiatorPar_pWhiteList uint32_t* +#define _POSITION_bleInitiatorPar_connectTime 20 +#define _TYPE_bleInitiatorPar_connectTime ratmr_t +#define _POSITION_bleInitiatorPar_timeoutTrigger 26 +#define _TYPE_bleInitiatorPar_timeoutTrigger uint8_t +#define _BITPOS_bleInitiatorPar_timeoutTrigger_triggerType 0 +#define _NBITS_bleInitiatorPar_timeoutTrigger_triggerType 4 +#define _BITPOS_bleInitiatorPar_timeoutTrigger_bEnaCmd 4 +#define _NBITS_bleInitiatorPar_timeoutTrigger_bEnaCmd 1 +#define _BITPOS_bleInitiatorPar_timeoutTrigger_triggerNo 5 +#define _NBITS_bleInitiatorPar_timeoutTrigger_triggerNo 2 +#define _BITPOS_bleInitiatorPar_timeoutTrigger_pastTrig 7 +#define _NBITS_bleInitiatorPar_timeoutTrigger_pastTrig 1 +#define _POSITION_bleInitiatorPar_endTrigger 27 +#define _TYPE_bleInitiatorPar_endTrigger uint8_t +#define _BITPOS_bleInitiatorPar_endTrigger_triggerType 0 +#define _NBITS_bleInitiatorPar_endTrigger_triggerType 4 +#define _BITPOS_bleInitiatorPar_endTrigger_bEnaCmd 4 +#define _NBITS_bleInitiatorPar_endTrigger_bEnaCmd 1 +#define _BITPOS_bleInitiatorPar_endTrigger_triggerNo 5 +#define _NBITS_bleInitiatorPar_endTrigger_triggerNo 2 +#define _BITPOS_bleInitiatorPar_endTrigger_pastTrig 7 +#define _NBITS_bleInitiatorPar_endTrigger_pastTrig 1 +#define _POSITION_bleInitiatorPar_timeoutTime 28 +#define _TYPE_bleInitiatorPar_timeoutTime ratmr_t +#define _POSITION_bleInitiatorPar_endTime 32 +#define _TYPE_bleInitiatorPar_endTime ratmr_t +#define _SIZEOF_bleInitiatorPar 36 + +#define _POSITION_bleGenericRxPar_pRxQ 0 +#define _TYPE_bleGenericRxPar_pRxQ dataQueue_t* +#define _POSITION_bleGenericRxPar_rxConfig 4 +#define _TYPE_bleGenericRxPar_rxConfig uint8_t +#define _BITPOS_bleGenericRxPar_rxConfig_bAutoFlushIgnored 0 +#define _NBITS_bleGenericRxPar_rxConfig_bAutoFlushIgnored 1 +#define _BITPOS_bleGenericRxPar_rxConfig_bAutoFlushCrcErr 1 +#define _NBITS_bleGenericRxPar_rxConfig_bAutoFlushCrcErr 1 +#define _BITPOS_bleGenericRxPar_rxConfig_bAutoFlushEmpty 2 +#define _NBITS_bleGenericRxPar_rxConfig_bAutoFlushEmpty 1 +#define _BITPOS_bleGenericRxPar_rxConfig_bIncludeLenByte 3 +#define _NBITS_bleGenericRxPar_rxConfig_bIncludeLenByte 1 +#define _BITPOS_bleGenericRxPar_rxConfig_bIncludeCrc 4 +#define _NBITS_bleGenericRxPar_rxConfig_bIncludeCrc 1 +#define _BITPOS_bleGenericRxPar_rxConfig_bAppendRssi 5 +#define _NBITS_bleGenericRxPar_rxConfig_bAppendRssi 1 +#define _BITPOS_bleGenericRxPar_rxConfig_bAppendStatus 6 +#define _NBITS_bleGenericRxPar_rxConfig_bAppendStatus 1 +#define _BITPOS_bleGenericRxPar_rxConfig_bAppendTimestamp 7 +#define _NBITS_bleGenericRxPar_rxConfig_bAppendTimestamp 1 +#define _POSITION_bleGenericRxPar_bRepeat 5 +#define _TYPE_bleGenericRxPar_bRepeat uint8_t +#define _POSITION_bleGenericRxPar_accessAddress 8 +#define _TYPE_bleGenericRxPar_accessAddress uint32_t +#define _POSITION_bleGenericRxPar_crcInit0 12 +#define _TYPE_bleGenericRxPar_crcInit0 uint8_t +#define _POSITION_bleGenericRxPar_crcInit1 13 +#define _TYPE_bleGenericRxPar_crcInit1 uint8_t +#define _POSITION_bleGenericRxPar_crcInit2 14 +#define _TYPE_bleGenericRxPar_crcInit2 uint8_t +#define _POSITION_bleGenericRxPar_crcInit 12 +#define _TYPE_bleGenericRxPar_crcInit uint32_t +#define _POSITION_bleGenericRxPar_endTrigger 15 +#define _TYPE_bleGenericRxPar_endTrigger uint8_t +#define _BITPOS_bleGenericRxPar_endTrigger_triggerType 0 +#define _NBITS_bleGenericRxPar_endTrigger_triggerType 4 +#define _BITPOS_bleGenericRxPar_endTrigger_bEnaCmd 4 +#define _NBITS_bleGenericRxPar_endTrigger_bEnaCmd 1 +#define _BITPOS_bleGenericRxPar_endTrigger_triggerNo 5 +#define _NBITS_bleGenericRxPar_endTrigger_triggerNo 2 +#define _BITPOS_bleGenericRxPar_endTrigger_pastTrig 7 +#define _NBITS_bleGenericRxPar_endTrigger_pastTrig 1 +#define _POSITION_bleGenericRxPar_endTime 16 +#define _TYPE_bleGenericRxPar_endTime ratmr_t +#define _SIZEOF_bleGenericRxPar 20 + +#define _POSITION_bleTxTestPar_numPackets 0 +#define _TYPE_bleTxTestPar_numPackets uint16_t +#define _POSITION_bleTxTestPar_payloadLength 2 +#define _TYPE_bleTxTestPar_payloadLength uint8_t +#define _POSITION_bleTxTestPar_packetType 3 +#define _TYPE_bleTxTestPar_packetType uint8_t +#define _POSITION_bleTxTestPar_period 4 +#define _TYPE_bleTxTestPar_period ratmr_t +#define _POSITION_bleTxTestPar_config 8 +#define _TYPE_bleTxTestPar_config uint8_t +#define _BITPOS_bleTxTestPar_config_bOverrideDefault 0 +#define _NBITS_bleTxTestPar_config_bOverrideDefault 1 +#define _BITPOS_bleTxTestPar_config_bUsePrbs9 1 +#define _NBITS_bleTxTestPar_config_bUsePrbs9 1 +#define _BITPOS_bleTxTestPar_config_bUsePrbs15 2 +#define _NBITS_bleTxTestPar_config_bUsePrbs15 1 +#define _POSITION_bleTxTestPar_byteVal 9 +#define _TYPE_bleTxTestPar_byteVal uint8_t +#define _POSITION_bleTxTestPar_endTrigger 11 +#define _TYPE_bleTxTestPar_endTrigger uint8_t +#define _BITPOS_bleTxTestPar_endTrigger_triggerType 0 +#define _NBITS_bleTxTestPar_endTrigger_triggerType 4 +#define _BITPOS_bleTxTestPar_endTrigger_bEnaCmd 4 +#define _NBITS_bleTxTestPar_endTrigger_bEnaCmd 1 +#define _BITPOS_bleTxTestPar_endTrigger_triggerNo 5 +#define _NBITS_bleTxTestPar_endTrigger_triggerNo 2 +#define _BITPOS_bleTxTestPar_endTrigger_pastTrig 7 +#define _NBITS_bleTxTestPar_endTrigger_pastTrig 1 +#define _POSITION_bleTxTestPar_endTime 12 +#define _TYPE_bleTxTestPar_endTime ratmr_t +#define _SIZEOF_bleTxTestPar 16 + +#define _POSITION_bleMasterSlaveOutput_nTx 0 +#define _TYPE_bleMasterSlaveOutput_nTx uint8_t +#define _POSITION_bleMasterSlaveOutput_nTxAck 1 +#define _TYPE_bleMasterSlaveOutput_nTxAck uint8_t +#define _POSITION_bleMasterSlaveOutput_nTxCtrl 2 +#define _TYPE_bleMasterSlaveOutput_nTxCtrl uint8_t +#define _POSITION_bleMasterSlaveOutput_nTxCtrlAck 3 +#define _TYPE_bleMasterSlaveOutput_nTxCtrlAck uint8_t +#define _POSITION_bleMasterSlaveOutput_nTxCtrlAckAck 4 +#define _TYPE_bleMasterSlaveOutput_nTxCtrlAckAck uint8_t +#define _POSITION_bleMasterSlaveOutput_nTxRetrans 5 +#define _TYPE_bleMasterSlaveOutput_nTxRetrans uint8_t +#define _POSITION_bleMasterSlaveOutput_nTxEntryDone 6 +#define _TYPE_bleMasterSlaveOutput_nTxEntryDone uint8_t +#define _POSITION_bleMasterSlaveOutput_nRxOk 7 +#define _TYPE_bleMasterSlaveOutput_nRxOk uint8_t +#define _POSITION_bleMasterSlaveOutput_nRxCtrl 8 +#define _TYPE_bleMasterSlaveOutput_nRxCtrl uint8_t +#define _POSITION_bleMasterSlaveOutput_nRxCtrlAck 9 +#define _TYPE_bleMasterSlaveOutput_nRxCtrlAck uint8_t +#define _POSITION_bleMasterSlaveOutput_nRxNok 10 +#define _TYPE_bleMasterSlaveOutput_nRxNok uint8_t +#define _POSITION_bleMasterSlaveOutput_nRxIgnored 11 +#define _TYPE_bleMasterSlaveOutput_nRxIgnored uint8_t +#define _POSITION_bleMasterSlaveOutput_nRxEmpty 12 +#define _TYPE_bleMasterSlaveOutput_nRxEmpty uint8_t +#define _POSITION_bleMasterSlaveOutput_nRxBufFull 13 +#define _TYPE_bleMasterSlaveOutput_nRxBufFull uint8_t +#define _POSITION_bleMasterSlaveOutput_lastRssi 14 +#define _TYPE_bleMasterSlaveOutput_lastRssi int8_t +#define _POSITION_bleMasterSlaveOutput_pktStatus 15 +#define _TYPE_bleMasterSlaveOutput_pktStatus uint8_t +#define _BITPOS_bleMasterSlaveOutput_pktStatus_bTimeStampValid 0 +#define _NBITS_bleMasterSlaveOutput_pktStatus_bTimeStampValid 1 +#define _BITPOS_bleMasterSlaveOutput_pktStatus_bLastCrcErr 1 +#define _NBITS_bleMasterSlaveOutput_pktStatus_bLastCrcErr 1 +#define _BITPOS_bleMasterSlaveOutput_pktStatus_bLastIgnored 2 +#define _NBITS_bleMasterSlaveOutput_pktStatus_bLastIgnored 1 +#define _BITPOS_bleMasterSlaveOutput_pktStatus_bLastEmpty 3 +#define _NBITS_bleMasterSlaveOutput_pktStatus_bLastEmpty 1 +#define _BITPOS_bleMasterSlaveOutput_pktStatus_bLastCtrl 4 +#define _NBITS_bleMasterSlaveOutput_pktStatus_bLastCtrl 1 +#define _BITPOS_bleMasterSlaveOutput_pktStatus_bLastMd 5 +#define _NBITS_bleMasterSlaveOutput_pktStatus_bLastMd 1 +#define _BITPOS_bleMasterSlaveOutput_pktStatus_bLastAck 6 +#define _NBITS_bleMasterSlaveOutput_pktStatus_bLastAck 1 +#define _POSITION_bleMasterSlaveOutput_timeStamp 16 +#define _TYPE_bleMasterSlaveOutput_timeStamp ratmr_t +#define _SIZEOF_bleMasterSlaveOutput 20 + +#define _POSITION_bleAdvOutput_nTxAdvInd 0 +#define _TYPE_bleAdvOutput_nTxAdvInd uint16_t +#define _POSITION_bleAdvOutput_nTxScanRsp 2 +#define _TYPE_bleAdvOutput_nTxScanRsp uint8_t +#define _POSITION_bleAdvOutput_nRxScanReq 3 +#define _TYPE_bleAdvOutput_nRxScanReq uint8_t +#define _POSITION_bleAdvOutput_nRxConnectReq 4 +#define _TYPE_bleAdvOutput_nRxConnectReq uint8_t +#define _POSITION_bleAdvOutput_nRxNok 6 +#define _TYPE_bleAdvOutput_nRxNok uint16_t +#define _POSITION_bleAdvOutput_nRxIgnored 8 +#define _TYPE_bleAdvOutput_nRxIgnored uint16_t +#define _POSITION_bleAdvOutput_nRxBufFull 10 +#define _TYPE_bleAdvOutput_nRxBufFull uint8_t +#define _POSITION_bleAdvOutput_lastRssi 11 +#define _TYPE_bleAdvOutput_lastRssi int8_t +#define _POSITION_bleAdvOutput_timeStamp 12 +#define _TYPE_bleAdvOutput_timeStamp ratmr_t +#define _SIZEOF_bleAdvOutput 16 + +#define _POSITION_bleScannerOutput_nTxScanReq 0 +#define _TYPE_bleScannerOutput_nTxScanReq uint16_t +#define _POSITION_bleScannerOutput_nBackedOffScanReq 2 +#define _TYPE_bleScannerOutput_nBackedOffScanReq uint16_t +#define _POSITION_bleScannerOutput_nRxAdvOk 4 +#define _TYPE_bleScannerOutput_nRxAdvOk uint16_t +#define _POSITION_bleScannerOutput_nRxAdvIgnored 6 +#define _TYPE_bleScannerOutput_nRxAdvIgnored uint16_t +#define _POSITION_bleScannerOutput_nRxAdvNok 8 +#define _TYPE_bleScannerOutput_nRxAdvNok uint16_t +#define _POSITION_bleScannerOutput_nRxScanRspOk 10 +#define _TYPE_bleScannerOutput_nRxScanRspOk uint16_t +#define _POSITION_bleScannerOutput_nRxScanRspIgnored 12 +#define _TYPE_bleScannerOutput_nRxScanRspIgnored uint16_t +#define _POSITION_bleScannerOutput_nRxScanRspNok 14 +#define _TYPE_bleScannerOutput_nRxScanRspNok uint16_t +#define _POSITION_bleScannerOutput_nRxAdvBufFull 16 +#define _TYPE_bleScannerOutput_nRxAdvBufFull uint8_t +#define _POSITION_bleScannerOutput_nRxScanRspBufFull 17 +#define _TYPE_bleScannerOutput_nRxScanRspBufFull uint8_t +#define _POSITION_bleScannerOutput_lastRssi 18 +#define _TYPE_bleScannerOutput_lastRssi int8_t +#define _POSITION_bleScannerOutput_timeStamp 20 +#define _TYPE_bleScannerOutput_timeStamp ratmr_t +#define _SIZEOF_bleScannerOutput 24 + +#define _POSITION_bleInitiatorOutput_nTxConnectReq 0 +#define _TYPE_bleInitiatorOutput_nTxConnectReq uint8_t +#define _POSITION_bleInitiatorOutput_nRxAdvOk 1 +#define _TYPE_bleInitiatorOutput_nRxAdvOk uint8_t +#define _POSITION_bleInitiatorOutput_nRxAdvIgnored 2 +#define _TYPE_bleInitiatorOutput_nRxAdvIgnored uint16_t +#define _POSITION_bleInitiatorOutput_nRxAdvNok 4 +#define _TYPE_bleInitiatorOutput_nRxAdvNok uint16_t +#define _POSITION_bleInitiatorOutput_nRxAdvBufFull 6 +#define _TYPE_bleInitiatorOutput_nRxAdvBufFull uint8_t +#define _POSITION_bleInitiatorOutput_lastRssi 7 +#define _TYPE_bleInitiatorOutput_lastRssi int8_t +#define _POSITION_bleInitiatorOutput_timeStamp 8 +#define _TYPE_bleInitiatorOutput_timeStamp ratmr_t +#define _SIZEOF_bleInitiatorOutput 12 + +#define _POSITION_bleGenericRxOutput_nRxOk 0 +#define _TYPE_bleGenericRxOutput_nRxOk uint16_t +#define _POSITION_bleGenericRxOutput_nRxNok 2 +#define _TYPE_bleGenericRxOutput_nRxNok uint16_t +#define _POSITION_bleGenericRxOutput_nRxBufFull 4 +#define _TYPE_bleGenericRxOutput_nRxBufFull uint16_t +#define _POSITION_bleGenericRxOutput_lastRssi 6 +#define _TYPE_bleGenericRxOutput_lastRssi int8_t +#define _POSITION_bleGenericRxOutput_timeStamp 8 +#define _TYPE_bleGenericRxOutput_timeStamp ratmr_t +#define _SIZEOF_bleGenericRxOutput 12 + +#define _POSITION_bleTxTestOutput_nTx 0 +#define _TYPE_bleTxTestOutput_nTx uint16_t +#define _SIZEOF_bleTxTestOutput 2 + +#define _POSITION_bleWhiteListEntry_size 0 +#define _TYPE_bleWhiteListEntry_size uint8_t +#define _POSITION_bleWhiteListEntry_conf 1 +#define _TYPE_bleWhiteListEntry_conf uint8_t +#define _BITPOS_bleWhiteListEntry_conf_bEnable 0 +#define _NBITS_bleWhiteListEntry_conf_bEnable 1 +#define _BITPOS_bleWhiteListEntry_conf_addrType 1 +#define _NBITS_bleWhiteListEntry_conf_addrType 1 +#define _BITPOS_bleWhiteListEntry_conf_bWlIgn 2 +#define _NBITS_bleWhiteListEntry_conf_bWlIgn 1 +#define _POSITION_bleWhiteListEntry_address 2 +#define _TYPE_bleWhiteListEntry_address uint16_t +#define _POSITION_bleWhiteListEntry_addressHi 4 +#define _TYPE_bleWhiteListEntry_addressHi uint32_t +#define _SIZEOF_bleWhiteListEntry 8 + +#define _POSITION_bleRxStatus_status 0 +#define _TYPE_bleRxStatus_status uint8_t +#define _BITPOS_bleRxStatus_status_channel 0 +#define _NBITS_bleRxStatus_status_channel 6 +#define _BITPOS_bleRxStatus_status_bIgnore 6 +#define _NBITS_bleRxStatus_status_bIgnore 1 +#define _BITPOS_bleRxStatus_status_bCrcErr 7 +#define _NBITS_bleRxStatus_status_bCrcErr 1 +#define _SIZEOF_bleRxStatus 1 + +#endif diff --git a/cpu/cc26xx/dev/rfc-api/ble_mailbox.h b/cpu/cc26xx/dev/rfc-api/ble_mailbox.h new file mode 100644 index 000000000..607a92f5d --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/ble_mailbox.h @@ -0,0 +1,119 @@ +/****************************************************************************** +* Filename: ble_mailbox.h +* Revised: $ $ +* Revision: $ $ +* +* Description: Definitions for BLE interface +* +* Copyright (C) 2015 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 _BLE_MAILBOX_H +#define _BLE_MAILBOX_H + +#include "mailbox.h" + +/// \name CPE interrupt definitions for BLE +/// Interrupt masks for the CPE interrupt in RDBELL. These are new names for interrupts in mailbox.h, +/// used for compartibility with previous versions with separate interrupt numbers. +///@{ +#define IRQN_BLE_TX_DONE IRQN_TX_DONE +#define IRQN_BLE_TX_ACK IRQN_TX_ACK +#define IRQN_BLE_TX_CTRL IRQN_TX_CTRL +#define IRQN_BLE_TX_CTRL_ACK IRQN_TX_CTRL_ACK +#define IRQN_BLE_TX_CTRL_ACK_ACK IRQN_TX_CTRL_ACK_ACK +#define IRQN_BLE_TX_RETRANS IRQN_TX_RETRANS +#define IRQN_BLE_TX_ENTRY_DONE IRQN_TX_ENTRY_DONE +#define IRQN_BLE_TX_BUFFER_CHANGED IRQN_TX_BUFFER_CHANGED +#define IRQN_BLE_RX_OK IRQN_RX_OK +#define IRQN_BLE_RX_NOK IRQN_RX_NOK +#define IRQN_BLE_RX_IGNORED IRQN_RX_IGNORED +#define IRQN_BLE_RX_EMPTY IRQN_RX_EMPTY +#define IRQN_BLE_RX_CTRL IRQN_RX_CTRL +#define IRQN_BLE_RX_CTRL_ACK IRQN_RX_CTRL_ACK +#define IRQN_BLE_RX_BUF_FULL IRQN_RX_BUF_FULL +#define IRQN_BLE_RX_ENTRY_DONE IRQN_RX_ENTRY_DONE + +#define IRQ_BLE_TX_DONE (1U << IRQN_BLE_TX_DONE) +#define IRQ_BLE_TX_ACK (1U << IRQN_BLE_TX_ACK) +#define IRQ_BLE_TX_CTRL (1U << IRQN_BLE_TX_CTRL) +#define IRQ_BLE_TX_CTRL_ACK (1U << IRQN_BLE_TX_CTRL_ACK) +#define IRQ_BLE_TX_CTRL_ACK_ACK (1U << IRQN_BLE_TX_CTRL_ACK_ACK) +#define IRQ_BLE_TX_RETRANS (1U << IRQN_BLE_TX_RETRANS) +#define IRQ_BLE_TX_ENTRY_DONE (1U << IRQN_BLE_TX_ENTRY_DONE) +#define IRQ_BLE_TX_BUFFER_CHANGED (1U << IRQN_BLE_TX_BUFFER_CHANGED) +#define IRQ_BLE_RX_OK (1U << IRQN_BLE_RX_OK) +#define IRQ_BLE_RX_NOK (1U << IRQN_BLE_RX_NOK) +#define IRQ_BLE_RX_IGNORED (1U << IRQN_BLE_RX_IGNORED) +#define IRQ_BLE_RX_EMPTY (1U << IRQN_BLE_RX_EMPTY) +#define IRQ_BLE_RX_CTRL (1U << IRQN_BLE_RX_CTRL) +#define IRQ_BLE_RX_CTRL_ACK (1U << IRQN_BLE_RX_CTRL_ACK) +#define IRQ_BLE_RX_BUF_FULL (1U << IRQN_BLE_RX_BUF_FULL) +#define IRQ_BLE_RX_ENTRY_DONE (1U << IRQN_BLE_RX_ENTRY_DONE) +///@} + + + +/// \name Radio operation status +/// Radio operation status format: +/// Bits 15:12: Protocol +/// 0001: BLE +/// Bits 11:10: Type +/// 00: Not finished +/// 01: Done successfully +/// 10: Done with error +/// Bits 9:0: Identifier + +/// \name Operation finished normally +///@{ +#define BLE_DONE_OK 0x1400 ///< Operation ended normally +#define BLE_DONE_RXTIMEOUT 0x1401 ///< Timeout of first Rx of slave operation or end of scan window +#define BLE_DONE_NOSYNC 0x1402 ///< Timeout of subsequent Rx +#define BLE_DONE_RXERR 0x1403 ///< Operation ended because of receive error (CRC or other) +#define BLE_DONE_CONNECT 0x1404 ///< CONNECT_REQ received or transmitted +#define BLE_DONE_MAXNACK 0x1405 ///< Maximum number of retransmissions exceeded +#define BLE_DONE_ENDED 0x1406 ///< Operation stopped after end trigger +#define BLE_DONE_ABORT 0x1407 ///< Operation aborted by command +#define BLE_DONE_STOPPED 0x1408 ///< Operation stopped after stop command +///@} +/// \name Operation finished with error +///@{ +#define BLE_ERROR_PAR 0x1800 ///< Illegal parameter +#define BLE_ERROR_RXBUF 0x1801 ///< No available Rx buffer (Advertiser, Scanner, Initiator) +#define BLE_ERROR_NO_SETUP 0x1802 ///< Operation using Rx or Tx attemted when not in BLE mode +#define BLE_ERROR_NO_FS 0x1803 ///< Operation using Rx or Tx attemted without frequency synth configured +#define BLE_ERROR_SYNTH_PROG 0x1804 ///< Synthesizer programming failed to complete on time +#define BLE_ERROR_RXOVF 0x1805 ///< Receiver overflowed during operation +#define BLE_ERROR_TXUNF 0x1806 ///< Transmitter underflowed during operation +///@} +///@} + +#endif diff --git a/cpu/cc26xx/dev/rfc-api/common_cmd.h b/cpu/cc26xx/dev/rfc-api/common_cmd.h new file mode 100644 index 000000000..bae0a0200 --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/common_cmd.h @@ -0,0 +1,1043 @@ +/****************************************************************************** +* Filename: common_cmd.h +* Revised: $ $ +* Revision: $ $ +* +* Description: CC26xx API for common/generic commands +* +* Copyright (C) 2015 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_CMD_H +#define __COMMON_CMD_H + +#ifndef __RFC_STRUCT +#ifdef __GNUC__ +#define __RFC_STRUCT __attribute__ ((aligned (4))) +#else +#define __RFC_STRUCT +#endif +#endif + +//! \addtogroup rfc +//! @{ + +//! \addtogroup common_cmd +//! @{ + +#include +#include "mailbox.h" + +typedef struct __RFC_STRUCT rfc_CMD_FG_SCH_IMM_s rfc_CMD_FG_SCH_IMM_t; +typedef struct __RFC_STRUCT rfc_CMD_WRITE_FWPAR_s rfc_CMD_WRITE_FWPAR_t; +typedef struct __RFC_STRUCT rfc_CMD_TX_TEST_s rfc_CMD_TX_TEST_t; +typedef struct __RFC_STRUCT rfc_CMD_RX_TEST_s rfc_CMD_RX_TEST_t; +typedef struct __RFC_STRUCT rfc_CMD_FS_POWERDOWN_s rfc_CMD_FS_POWERDOWN_t; +typedef struct __RFC_STRUCT rfc_CMD_DISARM_RAT_CH_s rfc_CMD_DISARM_RAT_CH_t; +typedef struct __RFC_STRUCT rfc_CMD_SCH_IMM_s rfc_CMD_SCH_IMM_t; +typedef struct __RFC_STRUCT rfc_CMD_ANALOG_POWERDOWN_s rfc_CMD_ANALOG_POWERDOWN_t; +typedef struct __RFC_STRUCT rfc_CMD_SET_RAT_CMP_s rfc_CMD_SET_RAT_CMP_t; +typedef struct __RFC_STRUCT rfc_CMD_FS_OFF_s rfc_CMD_FS_OFF_t; +typedef struct __RFC_STRUCT rfc_CMD_ENABLE_DBG_s rfc_CMD_ENABLE_DBG_t; +typedef struct __RFC_STRUCT rfc_CMD_CLEAR_RX_s rfc_CMD_CLEAR_RX_t; +typedef struct __RFC_STRUCT rfc_CMD_FS_s rfc_CMD_FS_t; +typedef struct __RFC_STRUCT rfc_CMD_FG_COUNT_BRANCH_s rfc_CMD_FG_COUNT_BRANCH_t; +typedef struct __RFC_STRUCT rfc_CMD_GET_FW_INFO_s rfc_CMD_GET_FW_INFO_t; +typedef struct __RFC_STRUCT rfc_CMD_TOPSM_COPY_s rfc_CMD_TOPSM_COPY_t; +typedef struct __RFC_STRUCT rfc_CMD_USER_FUN_s rfc_CMD_USER_FUN_t; +typedef struct __RFC_STRUCT rfc_command_s rfc_command_t; +typedef struct __RFC_STRUCT rfc_CMD_WRITE_ADI0REG_s rfc_CMD_WRITE_ADI0REG_t; +typedef struct __RFC_STRUCT rfc_CMD_FS_POWERUP_s rfc_CMD_FS_POWERUP_t; +typedef struct __RFC_STRUCT rfc_CMD_SYNC_STOP_RAT_s rfc_CMD_SYNC_STOP_RAT_t; +typedef struct __RFC_STRUCT rfc_CMD_ADD_DATA_ENTRY_s rfc_CMD_ADD_DATA_ENTRY_t; +typedef struct __RFC_STRUCT rfc_CMD_FG_COUNT_s rfc_CMD_FG_COUNT_t; +typedef struct __RFC_STRUCT rfc_CMD_MEMCPY_s rfc_CMD_MEMCPY_t; +typedef struct __RFC_STRUCT rfc_CMD_SET_RAT_CPT_s rfc_CMD_SET_RAT_CPT_t; +typedef struct __RFC_STRUCT rfc_CMD_DISABLE_DBG_s rfc_CMD_DISABLE_DBG_t; +typedef struct __RFC_STRUCT rfc_CMD_SYNC_START_RAT_s rfc_CMD_SYNC_START_RAT_t; +typedef struct __RFC_STRUCT rfc_CMD_COUNT_s rfc_CMD_COUNT_t; +typedef struct __RFC_STRUCT rfc_CMD_PATTERN_CHECK_s rfc_CMD_PATTERN_CHECK_t; +typedef struct __RFC_STRUCT rfc_CMD_MODIFY_RFREG_s rfc_CMD_MODIFY_RFREG_t; +typedef struct __RFC_STRUCT rfc_CMD_REMOVE_PENDING_ENTRIES_s rfc_CMD_REMOVE_PENDING_ENTRIES_t; +typedef struct __RFC_STRUCT rfc_CMD_UPDATE_RADIO_SETUP_s rfc_CMD_UPDATE_RADIO_SETUP_t; +typedef struct __RFC_STRUCT rfc_CMD_NOP_s rfc_CMD_NOP_t; +typedef struct __RFC_STRUCT rfc_CMD_STOP_s rfc_CMD_STOP_t; +typedef struct __RFC_STRUCT rfc_CMD_TRIGGER_s rfc_CMD_TRIGGER_t; +typedef struct __RFC_STRUCT rfc_CMD_SET_TRIM_s rfc_CMD_SET_TRIM_t; +typedef struct __RFC_STRUCT rfc_CMD_UPDATE_BAW_FREQ_s rfc_CMD_UPDATE_BAW_FREQ_t; +typedef struct __RFC_STRUCT rfc_CMD_READ_FWPAR_s rfc_CMD_READ_FWPAR_t; +typedef struct __RFC_STRUCT rfc_CMD_REMOVE_DATA_ENTRY_s rfc_CMD_REMOVE_DATA_ENTRY_t; +typedef struct __RFC_STRUCT rfc_CMD_UPDATE_FS_s rfc_CMD_UPDATE_FS_t; +typedef struct __RFC_STRUCT rfc_CMD_ARM_RAT_CH_s rfc_CMD_ARM_RAT_CH_t; +typedef struct __RFC_STRUCT rfc_CMD_WRITE_RFREG_s rfc_CMD_WRITE_RFREG_t; +typedef struct __RFC_STRUCT rfc_CMD_SET_TX_SHAPE_s rfc_CMD_SET_TX_SHAPE_t; +typedef struct __RFC_STRUCT rfc_CMD_READ_ADI1REG_s rfc_CMD_READ_ADI1REG_t; +typedef struct __RFC_STRUCT rfc_CMD_WRITE_ADDR_s rfc_CMD_WRITE_ADDR_t; +typedef struct __RFC_STRUCT rfc_CMD_PING_s rfc_CMD_PING_t; +typedef struct __RFC_STRUCT rfc_CMD_SET_RAT_OUTPUT_s rfc_CMD_SET_RAT_OUTPUT_t; +typedef struct __RFC_STRUCT rfc_CMD_DISABLE_RAT_CH_s rfc_CMD_DISABLE_RAT_CH_t; +typedef struct __RFC_STRUCT rfc_CMD_GET_RSSI_s rfc_CMD_GET_RSSI_t; +typedef struct __RFC_STRUCT rfc_CMD_MEMSET_s rfc_CMD_MEMSET_t; +typedef struct __RFC_STRUCT rfc_CMD_COUNT_BRANCH_s rfc_CMD_COUNT_BRANCH_t; +typedef struct __RFC_STRUCT rfc_CMD_ABORT_s rfc_CMD_ABORT_t; +typedef struct __RFC_STRUCT rfc_CMD_TX_s rfc_CMD_TX_t; +typedef struct __RFC_STRUCT rfc_radioOp_s rfc_radioOp_t; +typedef struct __RFC_STRUCT rfc_CMD_FORCE_CLK_ENA_s rfc_CMD_FORCE_CLK_ENA_t; +typedef struct __RFC_STRUCT rfc_CMD_START_RAT_s rfc_CMD_START_RAT_t; +typedef struct __RFC_STRUCT rfc_CMD_READ_RFREG_s rfc_CMD_READ_RFREG_t; +typedef struct __RFC_STRUCT rfc_CMD_RX_s rfc_CMD_RX_t; +typedef struct __RFC_STRUCT rfc_CMD_FG_PATTERN_CHECK_s rfc_CMD_FG_PATTERN_CHECK_t; +typedef struct __RFC_STRUCT rfc_CMD_SET_TX_POWER_s rfc_CMD_SET_TX_POWER_t; +typedef struct __RFC_STRUCT rfc_CMD_BUS_REQUEST_s rfc_CMD_BUS_REQUEST_t; +typedef struct __RFC_STRUCT rfc_CMD_READ_TRIM_s rfc_CMD_READ_TRIM_t; +typedef struct __RFC_STRUCT rfc_CMD_READ_ADI0REG_s rfc_CMD_READ_ADI0REG_t; +typedef struct __RFC_STRUCT rfc_CMD_WRITE_ADI1REG_s rfc_CMD_WRITE_ADI1REG_t; +typedef struct __RFC_STRUCT rfc_CMD_RADIO_SETUP_s rfc_CMD_RADIO_SETUP_t; +typedef struct __RFC_STRUCT rfc_CMD_READ_FS_CAL_s rfc_CMD_READ_FS_CAL_t; +typedef struct __RFC_STRUCT rfc_CMD_FG_NOP_s rfc_CMD_FG_NOP_t; +typedef struct __RFC_STRUCT rfc_CMD_FLUSH_QUEUE_s rfc_CMD_FLUSH_QUEUE_t; + +//! \addtogroup command +//! @{ +struct __RFC_STRUCT rfc_command_s { + uint16_t commandNo; //!< The command ID number +}; + +//! @} + +//! \addtogroup radioOp +//! @{ +//! Common definition for radio operation commands + +struct __RFC_STRUCT rfc_radioOp_s { + uint16_t commandNo; //!< The command ID number + 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 if the rule involves skipping + } condition; +}; + +//! @} + +//! \addtogroup CMD_NOP +//! @{ +#define CMD_NOP 0x0801 +struct __RFC_STRUCT rfc_CMD_NOP_s { + uint16_t commandNo; //!< The command ID number 0x0801 + 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 if the rule involves skipping + } condition; +}; + +//! @} + +//! \addtogroup CMD_RADIO_SETUP +//! @{ +#define CMD_RADIO_SETUP 0x0802 +struct __RFC_STRUCT rfc_CMD_RADIO_SETUP_s { + uint16_t commandNo; //!< The command ID number 0x0802 + 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 if the rule involves skipping + } condition; + uint8_t mode; //!< \brief The main mode to use
+ //!< 0x00: BLE
+ //!< 0x01: IEEE 802.15.4
+ //!< 0x02: 2 Mbps GFSK
+ //!< 0x05: 5 Mbps coded 8-FSK
+ //!< 0x06: ANT
+ //!< 0xFF: Keep existing mode; update overrides only
+ //!< Others: Reserved + uint8_t __dummy0; + struct { + uint16_t frontEndMode:3; //!< \brief 0x00: Differential mode
+ //!< 0x01: Single-ended mode RFP
+ //!< 0x02: Single-ended mode RFN
+ //!< 0x03: Antenna diversity (start RFP)
+ //!< 0x04: Antenna diversity (start RFN)
+ //!< 0x05 Single-ended mode RFP with external frontend control on RF pins
+ //!< 0x06 Single-ended mode RFN with external frontend control on RF pins
+ //!< Others: Reserved + uint16_t biasMode:1; //!< \brief 0: Internal bias
+ //!< 1: External bias + uint16_t bNoAdi0Setup:1; //!< \brief 0: Program ADI 0 with default values
+ //!< 1: Do not program ADI 0 + uint16_t bNoAdi0Trim:1; //!< \brief 0: Apply trim values to ADI 0
+ //!< 1: Use default values for ADI 0 + uint16_t bNoAdi0Ovr:1; //!< \brief 0: Apply ADI 0 overrides
+ //!< 1: Ignore ADI 0 overrides + uint16_t bNoAdi1Setup:1; //!< \brief 0: Program ADI 1 with default values
+ //!< 1: Do not program ADI 1 + uint16_t bNoAdi1Trim:1; //!< \brief 0: Apply trim values to ADI 1
+ //!< 1: Use default values for ADI 1 + uint16_t bNoAdi1Ovr:1; //!< \brief 0: Apply ADI 1 overrides
+ //!< 1: Ignore ADI 1 overrides + uint16_t bNoFsPowerUp:1; //!< \brief 0: Power up frequency synth
+ //!< 1: Do not power up frequency synth + } config; //!< Configuration options + struct { + uint16_t IB:6; //!< Value to write to the PA power control field at 25 °C + uint16_t GC:2; //!< Value to write to the gain control of the 1st stage of the PA + uint16_t tempCoeff:8; //!< Temperature coefficient for IB. 0: No temperature compensation + } txPower; //!< Transmit power + uint32_t* pRegOverride; //!< \brief Pointer to a list of hardware and configuration registers to override. If NULL, no + //!< override is used. +}; + +//! @} + +//! \addtogroup CMD_FS +//! @{ +#define CMD_FS 0x0803 +struct __RFC_STRUCT rfc_CMD_FS_s { + uint16_t commandNo; //!< The command ID number 0x0803 + 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 if the rule involves skipping + } condition; + uint16_t frequency; //!< The frequency in MHz to tune to + uint16_t fractFreq; //!< Fractional part of the frequency to tune to + struct { + uint8_t bTxMode:1; //!< \brief 0: Start synth in Rx mode
+ //!< 1: Start synth in Tx mode + uint8_t refFreq:6; //!< Reserved + } synthConf; + struct { + uint8_t bOverrideCalib:1; //!< \brief 0: Use standard calibration settings (ignore calibration settings given in command)
+ //!< 1: Override calibration settings + uint8_t bSkipTdcCalib:1; //!< \brief 0: Perform TDC calibration
+ //!< 1: Skip TDC calibration + uint8_t bSkipCoarseCalib:1; //!< \brief 0: Perform coarse calibration
+ //!< 1: Skip coarse calibration + uint8_t bSkipMidCalib:1; //!< \brief 0: Perform mid calibration
+ //!< 1: Skip mid calibration + uint8_t coarsePrecal:4; //!< \brief Coarse pre-calibration value to use when bOverrideCalib and + //!< bSkipCoarseCalib are both 1 + } calibConf; + uint8_t midPrecal; //!< Mid pre-calibration value to use when bOverrideCalib and bSkipCoarseCalib are both 1 + uint8_t ktPrecal; //!< KT pre-calibration value to use when bOverrideCalib and bSkipCoarseCalib are both 1 + uint16_t tdcPrecal; //!< TDC pre-calibration value to use when bOverrideCalib and bSkipCoarseCalib are both 1 +}; + +//! @} + +//! \addtogroup CMD_FS_OFF +//! @{ +#define CMD_FS_OFF 0x0804 +struct __RFC_STRUCT rfc_CMD_FS_OFF_s { + uint16_t commandNo; //!< The command ID number 0x0804 + 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 if the rule involves skipping + } condition; +}; + +//! @} + +//! \addtogroup CMD_RX +//! @{ +#define CMD_RX 0x0805 +struct __RFC_STRUCT rfc_CMD_RX_s { + uint16_t commandNo; //!< The command ID number 0x0805 + 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 if the rule involves skipping + } condition; + struct { + uint16_t endianness:1; //!< \brief 0: Least significant bit first
+ //!< 1: Most significant bit first + uint16_t numHdrBits:6; //!< Number of bits in the header + uint16_t bFsOff:1; //!< \brief 0: Keep frequency synth on after command
+ //!< 1: Turn frequency synth off after command + uint16_t bUseCrc:1; //!< \brief 0: No CRC
+ //!< 1: The last bytes of the packet are a CRC + uint16_t bCrcIncSw:1; //!< \brief 0: Do not include sync word in CRC calculation
+ //!< 1: Include sync word in CRC calculation + uint16_t bCrcIncHdr:1; //!< \brief 0: Do not include header in CRC calculation
+ //!< 1: Include header in CRC calculation + uint16_t bReportCrc:1; //!< \brief 0: Do not write CRC to receive buffer
+ //!< 1: Write received CRC to receive buffer + uint16_t endType:1; //!< \brief 0: Packet is received to the end if end trigger happens after sync is obtained
+ //!< 1: Packet reception is stopped if end trigger happens + uint16_t bDualSw:1; //!< \brief 0: Single sync word
+ //!< 1: Dual sync word. + } pktConfig; + uint32_t syncWord; //!< Sync word to receive + uint32_t syncWord2; //!< Secondary sync word to receive if pktConfig.bDualSw = 1 + struct { + uint16_t numLenBits:4; //!< Number of bits in the length field + uint16_t lenFieldPos:5; //!< Bit position of the first bit in the length field + uint16_t lenOffset:7; //!< Signed number to add to the received length field + } lenConfig; + uint16_t maxLen; //!< Maximum number of bytes in the received packet (including header, excluding CRC) + uint8_t* pRecPkt; //!< Pointer to buffer for received packet. NULL: Do not store the contents. + ratmr_t endTime; //!< Time to end the 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 classifier for ending the operation + int8_t rssi; //!< RSSI of received packet + uint16_t recLen; //!< Number of bytes written to receive buffer + ratmr_t timeStamp; //!< Time stamp of received packet + uint16_t nRxOk; //!< Counter of number of received packets with CRC OK and first sync word + uint16_t nRxNok; //!< Counter of number of received packets with CRC error and first sync word + uint16_t nRx2Ok; //!< \brief Counter of number of received packets with CRC OK and second sync word; may safely be + //!< omitted if pktConfig.bDualSw is 0 + uint16_t nRx2Nok; //!< \brief Counter of number of received packets with CRC error and second sync word; may safely be + //!< omitted if pktConfig.bDualSw is 0 +}; + +//! @} + +//! \addtogroup CMD_TX +//! @{ +#define CMD_TX 0x0806 +struct __RFC_STRUCT rfc_CMD_TX_s { + uint16_t commandNo; //!< The command ID number 0x0806 + 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 if the rule involves skipping + } condition; + struct { + uint16_t endianness:1; //!< \brief 0: Least significant bit first
+ //!< 1: Most significant bit first + uint16_t numHdrBits:6; //!< Number of bits in the header + uint16_t bFsOff:1; //!< \brief 0: Keep frequency synth on after command
+ //!< 1: Turn frequency synth off after command + uint16_t bUseCrc:1; //!< \brief 0: No CRC
+ //!< 1: Append a CRC to the packet + uint16_t bCrcIncSw:1; //!< \brief 0: Do not include sync word in CRC calculation
+ //!< 1: Include sync word in CRC calculation + uint16_t bCrcIncHdr:1; //!< \brief 0: Do not include header in CRC calculation
+ //!< 1: Include header in CRC calculation + } pktConfig; + uint32_t syncWord; //!< Sync word to transmit + uint8_t* pTxPkt; //!< Pointer to buffer for transmitted packet. + uint16_t pktLen; //!< Number of bytes in the transmitted packet +}; + +//! @} + +//! \addtogroup CMD_RX_TEST +//! @{ +#define CMD_RX_TEST 0x0807 +struct __RFC_STRUCT rfc_CMD_RX_TEST_s { + uint16_t commandNo; //!< The command ID number 0x0807 + 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 if the rule involves skipping + } condition; + struct { + uint8_t bEnaFifo:1; //!< \brief 0: Do not enable FIFO in modem, so that received data is not available
+ //!< 1: Enable FIFO in modem – the data must be read out by the application + uint8_t bFsOff:1; //!< \brief 0: Keep frequency synth on after command
+ //!< 1: Turn frequency synth off after command + uint8_t bNoSync:1; //!< \brief 0: Run sync search as normal for the configured mode
+ //!< 1: Write correlation thresholds to the maximum value to avoid getting sync + } config; + 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 classifier for ending the operation + uint32_t syncWord; //!< Sync word to use for receiver + ratmr_t endTime; //!< Time to end the operation +}; + +//! @} + +//! \addtogroup CMD_TX_TEST +//! @{ +#define CMD_TX_TEST 0x0808 +struct __RFC_STRUCT rfc_CMD_TX_TEST_s { + uint16_t commandNo; //!< The command ID number 0x0808 + 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 if the rule involves skipping + } condition; + struct { + uint8_t bUseCw:1; //!< \brief 0: Send modulated signal
+ //!< 1: Send continuous wave + uint8_t bFsOff:1; //!< \brief 0: Keep frequency synth on after command
+ //!< 1: Turn frequency synth off after command + uint8_t whitenMode:2; //!< \brief 0: No whitening
+ //!< 1: Default whitening
+ //!< 2: PRBS-15
+ //!< 3: PRBS-32 + } config; + uint8_t __dummy0; + uint16_t txWord; //!< Value to send to the modem before whitening + uint8_t __dummy1; + 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 classifier for ending the operation + uint32_t syncWord; //!< Sync word to use for transmitter + ratmr_t endTime; //!< Time to end the operation +}; + +//! @} + +//! \addtogroup CMD_SYNC_STOP_RAT +//! @{ +#define CMD_SYNC_STOP_RAT 0x0809 +struct __RFC_STRUCT rfc_CMD_SYNC_STOP_RAT_s { + uint16_t commandNo; //!< The command ID number 0x0809 + 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 if the rule involves skipping + } condition; + uint16_t __dummy0; + ratmr_t rat0; //!< \brief The returned RAT timer value corresponding to the value the RAT would have had when the + //!< RTC was zero +}; + +//! @} + +//! \addtogroup CMD_SYNC_START_RAT +//! @{ +#define CMD_SYNC_START_RAT 0x080A +struct __RFC_STRUCT rfc_CMD_SYNC_START_RAT_s { + uint16_t commandNo; //!< The command ID number 0x080A + 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 if the rule involves skipping + } condition; + uint16_t __dummy0; + ratmr_t rat0; //!< \brief The desired RAT timer value corresponding to the value the RAT would have had when the + //!< RTC was zero. This parameter is returned by CMD_SYNC_STOP_RAT +}; + +//! @} + +//! \addtogroup CMD_COUNT +//! @{ +#define CMD_COUNT 0x080B +struct __RFC_STRUCT rfc_CMD_COUNT_s { + uint16_t commandNo; //!< The command ID number 0x080B + 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 if the rule involves skipping + } condition; + uint16_t counter; //!< \brief Counter. On start, the radio CPU decrements the value, and the end status of the operation + //!< differs if the result is zero +}; + +//! @} + +//! \addtogroup CMD_FS_POWERUP +//! @{ +#define CMD_FS_POWERUP 0x080C +struct __RFC_STRUCT rfc_CMD_FS_POWERUP_s { + uint16_t commandNo; //!< The command ID number 0x080C + 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 if the rule involves skipping + } condition; + uint16_t __dummy0; + uint32_t* pRegOverride; //!< Pointer to a list of hardware and configuration registers to override. If NULL, no override is used. +}; + +//! @} + +//! \addtogroup CMD_FS_POWERDOWN +//! @{ +#define CMD_FS_POWERDOWN 0x080D +struct __RFC_STRUCT rfc_CMD_FS_POWERDOWN_s { + uint16_t commandNo; //!< The command ID number 0x080D + 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 if the rule involves skipping + } condition; +}; + +//! @} + +//! \addtogroup CMD_SCH_IMM +//! @{ +#define CMD_SCH_IMM 0x0810 +struct __RFC_STRUCT rfc_CMD_SCH_IMM_s { + uint16_t commandNo; //!< The command ID number 0x0810 + 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 if the rule involves skipping + } condition; + uint16_t __dummy0; + uint32_t cmdrVal; //!< Value as would be written to CMDR + uint32_t cmdstaVal; //!< Value as would be returned in CMDSTA +}; + +//! @} + +//! \addtogroup CMD_COUNT_BRANCH +//! @{ +#define CMD_COUNT_BRANCH 0x0812 +struct __RFC_STRUCT rfc_CMD_COUNT_BRANCH_s { + uint16_t commandNo; //!< The command ID number 0x0812 + 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 if the rule involves skipping + } condition; + uint16_t counter; //!< \brief Counter. On start, the radio CPU decrements the value, and the end status of the operation + //!< differs if the result is zero + rfc_radioOp_t *pNextOpIfOk; //!< Pointer to next operation if counter did not expire +}; + +//! @} + +//! \addtogroup CMD_PATTERN_CHECK +//! @{ +#define CMD_PATTERN_CHECK 0x0813 +struct __RFC_STRUCT rfc_CMD_PATTERN_CHECK_s { + uint16_t commandNo; //!< The command ID number 0x0813 + 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 if the rule involves skipping + } condition; + struct { + uint16_t operation:2; //!< \brief Operation to perform
+ //!< 0: True if value == compareVal
+ //!< 1: True if value < compareVal
+ //!< 2: True if value > compareVal
+ //!< 3: Reserved + uint16_t bByteRev:1; //!< \brief If 1, interchange the four bytes of the value, so that they are read + //!< most-significant-byte-first. + uint16_t bBitRev:1; //!< If 1, perform bit reversal of the value + uint16_t signExtend:5; //!< \brief 0: Treat value and compareVal as unsigned
+ //!< 1–31: Treat value and compareVal as signed, where the value + //!< gives the number of the most significant bit in the signed number. + uint16_t bRxVal:1; //!< \brief 0: Use pValue as a pointer
+ //!< 1: Use pValue as a signed offset to the start of the last + //!< committed Rx entry element + } patternOpt; //!< Options for comparison + rfc_radioOp_t *pNextOpIfOk; //!< Pointer to next operation if comparison result was true + uint8_t* pValue; //!< Pointer to read from, or offset from last Rx entry if patternOpt.bRxVal == 1 + uint32_t mask; //!< Bit mask to apply before comparison + uint32_t compareVal; //!< Value to compare to +}; + +//! @} + +//! \addtogroup CMD_ABORT +//! @{ +#define CMD_ABORT 0x0401 +struct __RFC_STRUCT rfc_CMD_ABORT_s { + uint16_t commandNo; //!< The command ID number 0x0401 +}; + +//! @} + +//! \addtogroup CMD_STOP +//! @{ +#define CMD_STOP 0x0402 +struct __RFC_STRUCT rfc_CMD_STOP_s { + uint16_t commandNo; //!< The command ID number 0x0402 +}; + +//! @} + +//! \addtogroup CMD_GET_RSSI +//! @{ +#define CMD_GET_RSSI 0x0403 +struct __RFC_STRUCT rfc_CMD_GET_RSSI_s { + uint16_t commandNo; //!< The command ID number 0x0403 +}; + +//! @} + +//! \addtogroup CMD_UPDATE_RADIO_SETUP +//! @{ +#define CMD_UPDATE_RADIO_SETUP 0x0001 +struct __RFC_STRUCT rfc_CMD_UPDATE_RADIO_SETUP_s { + uint16_t commandNo; //!< The command ID number 0x0001 + uint16_t __dummy0; + uint32_t* pRegOverride; //!< Pointer to a list of hardware and configuration registers to override +}; + +//! @} + +//! \addtogroup CMD_TRIGGER +//! @{ +#define CMD_TRIGGER 0x0404 +struct __RFC_STRUCT rfc_CMD_TRIGGER_s { + uint16_t commandNo; //!< The command ID number 0x0404 + uint8_t triggerNo; //!< Command trigger number +}; + +//! @} + +//! \addtogroup CMD_GET_FW_INFO +//! @{ +#define CMD_GET_FW_INFO 0x0002 +struct __RFC_STRUCT rfc_CMD_GET_FW_INFO_s { + uint16_t commandNo; //!< The command ID number 0x0002 + uint16_t versionNo; //!< Firmware version number + uint16_t startOffset; //!< The start of free RAM + uint16_t freeRamSz; //!< The size of free RAM + uint16_t availRatCh; //!< Bitmap of available RAT channels +}; + +//! @} + +//! \addtogroup CMD_START_RAT +//! @{ +#define CMD_START_RAT 0x0405 +struct __RFC_STRUCT rfc_CMD_START_RAT_s { + uint16_t commandNo; //!< The command ID number 0x0405 +}; + +//! @} + +//! \addtogroup CMD_PING +//! @{ +#define CMD_PING 0x0406 +struct __RFC_STRUCT rfc_CMD_PING_s { + uint16_t commandNo; //!< The command ID number 0x0406 +}; + +//! @} + +//! \addtogroup CMD_ADD_DATA_ENTRY +//! @{ +#define CMD_ADD_DATA_ENTRY 0x0005 +struct __RFC_STRUCT rfc_CMD_ADD_DATA_ENTRY_s { + uint16_t commandNo; //!< The command ID number 0x0005 + uint16_t __dummy0; + dataQueue_t* pQueue; //!< Pointer to the queue structure to which the entry will be added + uint8_t* pEntry; //!< Pointer to the entry +}; + +//! @} + +//! \addtogroup CMD_REMOVE_DATA_ENTRY +//! @{ +#define CMD_REMOVE_DATA_ENTRY 0x0006 +struct __RFC_STRUCT rfc_CMD_REMOVE_DATA_ENTRY_s { + uint16_t commandNo; //!< The command ID number 0x0006 + uint16_t __dummy0; + dataQueue_t* pQueue; //!< Pointer to the queue structure from which the entry will be removed + uint8_t* pEntry; //!< Pointer to the entry that was removed +}; + +//! @} + +//! \addtogroup CMD_FLUSH_QUEUE +//! @{ +#define CMD_FLUSH_QUEUE 0x0007 +struct __RFC_STRUCT rfc_CMD_FLUSH_QUEUE_s { + uint16_t commandNo; //!< The command ID number 0x0007 + uint16_t __dummy0; + dataQueue_t* pQueue; //!< Pointer to the queue structure to be flushed + uint8_t* pFirstEntry; //!< Pointer to the first entry that was removed +}; + +//! @} + +//! \addtogroup CMD_CLEAR_RX +//! @{ +#define CMD_CLEAR_RX 0x0008 +struct __RFC_STRUCT rfc_CMD_CLEAR_RX_s { + uint16_t commandNo; //!< The command ID number 0x0008 + uint16_t __dummy0; + dataQueue_t* pQueue; //!< Pointer to the queue structure to be cleared +}; + +//! @} + +//! \addtogroup CMD_REMOVE_PENDING_ENTRIES +//! @{ +#define CMD_REMOVE_PENDING_ENTRIES 0x0009 +struct __RFC_STRUCT rfc_CMD_REMOVE_PENDING_ENTRIES_s { + uint16_t commandNo; //!< The command ID number 0x0009 + uint16_t __dummy0; + dataQueue_t* pQueue; //!< Pointer to the queue structure to be flushed + uint8_t* pFirstEntry; //!< Pointer to the first entry that was removed +}; + +//! @} + +//! \addtogroup CMD_SET_RAT_CMP +//! @{ +#define CMD_SET_RAT_CMP 0x000A +struct __RFC_STRUCT rfc_CMD_SET_RAT_CMP_s { + uint16_t commandNo; //!< The command ID number 0x000A + uint8_t ratCh; //!< The radio timer channel number + uint8_t __dummy0; + ratmr_t compareTime; //!< The time at which the compare occurs +}; + +//! @} + +//! \addtogroup CMD_SET_RAT_CPT +//! @{ +#define CMD_SET_RAT_CPT 0x0603 +struct __RFC_STRUCT rfc_CMD_SET_RAT_CPT_s { + uint16_t commandNo; //!< The command ID number 0x0603 + struct { + uint16_t :3; + uint16_t inputSrc:5; //!< Input source indicator + uint16_t ratCh:4; //!< The radio timer channel number + uint16_t bRepeated:1; //!< \brief 0: Single capture mode
+ //!< 1: Repeated capture mode + uint16_t inputMode:2; //!< \brief Input mode:
+ //!< 0: Capture on rising edge
+ //!< 1: Capture on falling edge
+ //!< 2: Capture on both edges
+ //!< 3: Reserved + } config; +}; + +//! @} + +//! \addtogroup CMD_DISABLE_RAT_CH +//! @{ +#define CMD_DISABLE_RAT_CH 0x0408 +struct __RFC_STRUCT rfc_CMD_DISABLE_RAT_CH_s { + uint16_t commandNo; //!< The command ID number 0x0408 + uint8_t ratCh; //!< The radio timer channel number +}; + +//! @} + +//! \addtogroup CMD_SET_RAT_OUTPUT +//! @{ +#define CMD_SET_RAT_OUTPUT 0x0604 +struct __RFC_STRUCT rfc_CMD_SET_RAT_OUTPUT_s { + uint16_t commandNo; //!< The command ID number 0x0604 + struct { + uint16_t :2; + uint16_t outputSel:3; //!< Output event indicator + uint16_t outputMode:3; //!< \brief 0: Set output line low as default; and pulse on event. Duration of pulse is one RF Core clock period (ca. 41.67 ns).
+ //!< 1: Set output line high on event
+ //!< 2: Set output line low on event
+ //!< 3: Toggle (invert) output line state on event
+ //!< 4: Immediately set output line to low (does not change upon event)
+ //!< 5: Immediately set output line to high (does not change upon event)
+ //!< Others: Reserved + uint16_t ratCh:4; //!< The radio timer channel number + } config; +}; + +//! @} + +//! \addtogroup CMD_ARM_RAT_CH +//! @{ +#define CMD_ARM_RAT_CH 0x0409 +struct __RFC_STRUCT rfc_CMD_ARM_RAT_CH_s { + uint16_t commandNo; //!< The command ID number 0x0409 + uint8_t ratCh; //!< The radio timer channel number +}; + +//! @} + +//! \addtogroup CMD_DISARM_RAT_CH +//! @{ +#define CMD_DISARM_RAT_CH 0x040A +struct __RFC_STRUCT rfc_CMD_DISARM_RAT_CH_s { + uint16_t commandNo; //!< The command ID number 0x040A + uint8_t ratCh; //!< The radio timer channel number +}; + +//! @} + +//! \addtogroup CMD_SET_TX_POWER +//! @{ +#define CMD_SET_TX_POWER 0x0010 +struct __RFC_STRUCT rfc_CMD_SET_TX_POWER_s { + uint16_t commandNo; //!< The command ID number 0x0010 + struct { + uint16_t IB:6; //!< Value to write to the PA power control field at 25 °C + uint16_t GC:2; //!< Value to write to the gain control of the 1st stage of the PA + uint16_t tempCoeff:8; //!< Temperature coefficient for IB. 0: No temperature compensation + } txPower; //!< New Tx power setting +}; + +//! @} + +//! \addtogroup CMD_UPDATE_FS +//! @{ +#define CMD_UPDATE_FS 0x0011 +struct __RFC_STRUCT rfc_CMD_UPDATE_FS_s { + uint16_t commandNo; //!< The command ID number 0x0011 + uint16_t frequency; //!< The frequency in MHz to tune to + uint16_t fractFreq; //!< Fractional part of the frequency to tune to +}; + +//! @} + +//! \addtogroup CMD_BUS_REQUEST +//! @{ +#define CMD_BUS_REQUEST 0x040E +struct __RFC_STRUCT rfc_CMD_BUS_REQUEST_s { + uint16_t commandNo; //!< The command ID number 0x040E + uint8_t bSysBusNeeded; //!< \brief 0: System bus may sleep
+ //!< 1: System bus access needed +}; + +//! @} + +//! @} +//! @} +#endif diff --git a/cpu/cc26xx/dev/rfc-api/common_cmd_field.h b/cpu/cc26xx/dev/rfc-api/common_cmd_field.h new file mode 100755 index 000000000..d9e9431f6 --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/common_cmd_field.h @@ -0,0 +1,448 @@ +/****************************************************************************** +* Filename: common_cmd_field.h +* Revised: $ $ +* Revision: $ $ +* +* Description: CC26xx API for common/generic commands +* +* Copyright (C) 2015 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_CMD_FIELD_H +#define __COMMON_CMD_FIELD_H + +#include +#include "mailbox.h" + +#define _POSITION_command_commandNo 0 +#define _TYPE_command_commandNo uint16_t +#define _SIZEOF_command 2 + +#define _POSITION_radioOp_commandNo 0 +#define _TYPE_radioOp_commandNo uint16_t +#define _POSITION_radioOp_status 2 +#define _TYPE_radioOp_status uint16_t +#define _POSITION_radioOp_pNextOp 4 +#define _TYPE_radioOp_pNextOp uint8_t* +#define _POSITION_radioOp_startTime 8 +#define _TYPE_radioOp_startTime ratmr_t +#define _POSITION_radioOp_startTrigger 12 +#define _TYPE_radioOp_startTrigger uint8_t +#define _BITPOS_radioOp_startTrigger_triggerType 0 +#define _NBITS_radioOp_startTrigger_triggerType 4 +#define _BITPOS_radioOp_startTrigger_bEnaCmd 4 +#define _NBITS_radioOp_startTrigger_bEnaCmd 1 +#define _BITPOS_radioOp_startTrigger_triggerNo 5 +#define _NBITS_radioOp_startTrigger_triggerNo 2 +#define _BITPOS_radioOp_startTrigger_pastTrig 7 +#define _NBITS_radioOp_startTrigger_pastTrig 1 +#define _POSITION_radioOp_condition 13 +#define _TYPE_radioOp_condition uint8_t +#define _BITPOS_radioOp_condition_rule 0 +#define _NBITS_radioOp_condition_rule 4 +#define _BITPOS_radioOp_condition_nSkip 4 +#define _NBITS_radioOp_condition_nSkip 4 +#define _SIZEOF_radioOp 14 + +#define _SIZEOF_CMD_NOP 14 + +#define _POSITION_CMD_RADIO_SETUP_mode 14 +#define _TYPE_CMD_RADIO_SETUP_mode uint8_t +#define _POSITION_CMD_RADIO_SETUP_config 16 +#define _TYPE_CMD_RADIO_SETUP_config uint16_t +#define _BITPOS_CMD_RADIO_SETUP_config_frontEndMode 0 +#define _NBITS_CMD_RADIO_SETUP_config_frontEndMode 3 +#define _BITPOS_CMD_RADIO_SETUP_config_biasMode 3 +#define _NBITS_CMD_RADIO_SETUP_config_biasMode 1 +#define _BITPOS_CMD_RADIO_SETUP_config_bNoAdi0Setup 4 +#define _NBITS_CMD_RADIO_SETUP_config_bNoAdi0Setup 1 +#define _BITPOS_CMD_RADIO_SETUP_config_bNoAdi0Trim 5 +#define _NBITS_CMD_RADIO_SETUP_config_bNoAdi0Trim 1 +#define _BITPOS_CMD_RADIO_SETUP_config_bNoAdi0Ovr 6 +#define _NBITS_CMD_RADIO_SETUP_config_bNoAdi0Ovr 1 +#define _BITPOS_CMD_RADIO_SETUP_config_bNoAdi1Setup 7 +#define _NBITS_CMD_RADIO_SETUP_config_bNoAdi1Setup 1 +#define _BITPOS_CMD_RADIO_SETUP_config_bNoAdi1Trim 8 +#define _NBITS_CMD_RADIO_SETUP_config_bNoAdi1Trim 1 +#define _BITPOS_CMD_RADIO_SETUP_config_bNoAdi1Ovr 9 +#define _NBITS_CMD_RADIO_SETUP_config_bNoAdi1Ovr 1 +#define _BITPOS_CMD_RADIO_SETUP_config_bNoFsPowerUp 10 +#define _NBITS_CMD_RADIO_SETUP_config_bNoFsPowerUp 1 +#define _POSITION_CMD_RADIO_SETUP_txPower 18 +#define _TYPE_CMD_RADIO_SETUP_txPower uint16_t +#define _BITPOS_CMD_RADIO_SETUP_txPower_IB 0 +#define _NBITS_CMD_RADIO_SETUP_txPower_IB 6 +#define _BITPOS_CMD_RADIO_SETUP_txPower_GC 6 +#define _NBITS_CMD_RADIO_SETUP_txPower_GC 2 +#define _BITPOS_CMD_RADIO_SETUP_txPower_tempCoeff 8 +#define _NBITS_CMD_RADIO_SETUP_txPower_tempCoeff 8 +#define _POSITION_CMD_RADIO_SETUP_pRegOverride 20 +#define _TYPE_CMD_RADIO_SETUP_pRegOverride uint32_t* +#define _SIZEOF_CMD_RADIO_SETUP 24 + +#define _POSITION_CMD_FS_frequency 14 +#define _TYPE_CMD_FS_frequency uint16_t +#define _POSITION_CMD_FS_fractFreq 16 +#define _TYPE_CMD_FS_fractFreq uint16_t +#define _POSITION_CMD_FS_synthConf 18 +#define _TYPE_CMD_FS_synthConf uint8_t +#define _BITPOS_CMD_FS_synthConf_bTxMode 0 +#define _NBITS_CMD_FS_synthConf_bTxMode 1 +#define _BITPOS_CMD_FS_synthConf_refFreq 1 +#define _NBITS_CMD_FS_synthConf_refFreq 6 +#define _POSITION_CMD_FS_calibConf 19 +#define _TYPE_CMD_FS_calibConf uint8_t +#define _BITPOS_CMD_FS_calibConf_bOverrideCalib 0 +#define _NBITS_CMD_FS_calibConf_bOverrideCalib 1 +#define _BITPOS_CMD_FS_calibConf_bSkipTdcCalib 1 +#define _NBITS_CMD_FS_calibConf_bSkipTdcCalib 1 +#define _BITPOS_CMD_FS_calibConf_bSkipCoarseCalib 2 +#define _NBITS_CMD_FS_calibConf_bSkipCoarseCalib 1 +#define _BITPOS_CMD_FS_calibConf_bSkipMidCalib 3 +#define _NBITS_CMD_FS_calibConf_bSkipMidCalib 1 +#define _BITPOS_CMD_FS_calibConf_coarsePrecal 4 +#define _NBITS_CMD_FS_calibConf_coarsePrecal 4 +#define _POSITION_CMD_FS_midPrecal 20 +#define _TYPE_CMD_FS_midPrecal uint8_t +#define _POSITION_CMD_FS_ktPrecal 21 +#define _TYPE_CMD_FS_ktPrecal uint8_t +#define _POSITION_CMD_FS_tdcPrecal 22 +#define _TYPE_CMD_FS_tdcPrecal uint16_t +#define _SIZEOF_CMD_FS 24 + +#define _SIZEOF_CMD_FS_OFF 14 + +#define _POSITION_CMD_RX_pktConfig 14 +#define _TYPE_CMD_RX_pktConfig uint16_t +#define _BITPOS_CMD_RX_pktConfig_endianness 0 +#define _NBITS_CMD_RX_pktConfig_endianness 1 +#define _BITPOS_CMD_RX_pktConfig_numHdrBits 1 +#define _NBITS_CMD_RX_pktConfig_numHdrBits 6 +#define _BITPOS_CMD_RX_pktConfig_bFsOff 7 +#define _NBITS_CMD_RX_pktConfig_bFsOff 1 +#define _BITPOS_CMD_RX_pktConfig_bUseCrc 8 +#define _NBITS_CMD_RX_pktConfig_bUseCrc 1 +#define _BITPOS_CMD_RX_pktConfig_bCrcIncSw 9 +#define _NBITS_CMD_RX_pktConfig_bCrcIncSw 1 +#define _BITPOS_CMD_RX_pktConfig_bCrcIncHdr 10 +#define _NBITS_CMD_RX_pktConfig_bCrcIncHdr 1 +#define _BITPOS_CMD_RX_pktConfig_bReportCrc 11 +#define _NBITS_CMD_RX_pktConfig_bReportCrc 1 +#define _BITPOS_CMD_RX_pktConfig_endType 12 +#define _NBITS_CMD_RX_pktConfig_endType 1 +#define _BITPOS_CMD_RX_pktConfig_bDualSw 13 +#define _NBITS_CMD_RX_pktConfig_bDualSw 1 +#define _POSITION_CMD_RX_syncWord 16 +#define _TYPE_CMD_RX_syncWord uint32_t +#define _POSITION_CMD_RX_syncWord2 20 +#define _TYPE_CMD_RX_syncWord2 uint32_t +#define _POSITION_CMD_RX_lenConfig 24 +#define _TYPE_CMD_RX_lenConfig uint16_t +#define _BITPOS_CMD_RX_lenConfig_numLenBits 0 +#define _NBITS_CMD_RX_lenConfig_numLenBits 4 +#define _BITPOS_CMD_RX_lenConfig_lenFieldPos 4 +#define _NBITS_CMD_RX_lenConfig_lenFieldPos 5 +#define _BITPOS_CMD_RX_lenConfig_lenOffset 9 +#define _NBITS_CMD_RX_lenConfig_lenOffset 7 +#define _POSITION_CMD_RX_maxLen 26 +#define _TYPE_CMD_RX_maxLen uint16_t +#define _POSITION_CMD_RX_pRecPkt 28 +#define _TYPE_CMD_RX_pRecPkt uint8_t* +#define _POSITION_CMD_RX_endTime 32 +#define _TYPE_CMD_RX_endTime ratmr_t +#define _POSITION_CMD_RX_endTrigger 36 +#define _TYPE_CMD_RX_endTrigger uint8_t +#define _BITPOS_CMD_RX_endTrigger_triggerType 0 +#define _NBITS_CMD_RX_endTrigger_triggerType 4 +#define _BITPOS_CMD_RX_endTrigger_bEnaCmd 4 +#define _NBITS_CMD_RX_endTrigger_bEnaCmd 1 +#define _BITPOS_CMD_RX_endTrigger_triggerNo 5 +#define _NBITS_CMD_RX_endTrigger_triggerNo 2 +#define _BITPOS_CMD_RX_endTrigger_pastTrig 7 +#define _NBITS_CMD_RX_endTrigger_pastTrig 1 +#define _POSITION_CMD_RX_rssi 37 +#define _TYPE_CMD_RX_rssi int8_t +#define _POSITION_CMD_RX_recLen 38 +#define _TYPE_CMD_RX_recLen uint16_t +#define _POSITION_CMD_RX_timeStamp 40 +#define _TYPE_CMD_RX_timeStamp ratmr_t +#define _POSITION_CMD_RX_nRxOk 44 +#define _TYPE_CMD_RX_nRxOk uint16_t +#define _POSITION_CMD_RX_nRxNok 46 +#define _TYPE_CMD_RX_nRxNok uint16_t +#define _POSITION_CMD_RX_nRx2Ok 48 +#define _TYPE_CMD_RX_nRx2Ok uint16_t +#define _POSITION_CMD_RX_nRx2Nok 50 +#define _TYPE_CMD_RX_nRx2Nok uint16_t +#define _SIZEOF_CMD_RX 52 + +#define _POSITION_CMD_TX_pktConfig 14 +#define _TYPE_CMD_TX_pktConfig uint16_t +#define _BITPOS_CMD_TX_pktConfig_endianness 0 +#define _NBITS_CMD_TX_pktConfig_endianness 1 +#define _BITPOS_CMD_TX_pktConfig_numHdrBits 1 +#define _NBITS_CMD_TX_pktConfig_numHdrBits 6 +#define _BITPOS_CMD_TX_pktConfig_bFsOff 7 +#define _NBITS_CMD_TX_pktConfig_bFsOff 1 +#define _BITPOS_CMD_TX_pktConfig_bUseCrc 8 +#define _NBITS_CMD_TX_pktConfig_bUseCrc 1 +#define _BITPOS_CMD_TX_pktConfig_bCrcIncSw 9 +#define _NBITS_CMD_TX_pktConfig_bCrcIncSw 1 +#define _BITPOS_CMD_TX_pktConfig_bCrcIncHdr 10 +#define _NBITS_CMD_TX_pktConfig_bCrcIncHdr 1 +#define _POSITION_CMD_TX_syncWord 16 +#define _TYPE_CMD_TX_syncWord uint32_t +#define _POSITION_CMD_TX_pTxPkt 20 +#define _TYPE_CMD_TX_pTxPkt uint8_t* +#define _POSITION_CMD_TX_pktLen 24 +#define _TYPE_CMD_TX_pktLen uint16_t +#define _SIZEOF_CMD_TX 26 + +#define _POSITION_CMD_RX_TEST_config 14 +#define _TYPE_CMD_RX_TEST_config uint8_t +#define _BITPOS_CMD_RX_TEST_config_bEnaFifo 0 +#define _NBITS_CMD_RX_TEST_config_bEnaFifo 1 +#define _BITPOS_CMD_RX_TEST_config_bFsOff 1 +#define _NBITS_CMD_RX_TEST_config_bFsOff 1 +#define _BITPOS_CMD_RX_TEST_config_bNoSync 2 +#define _NBITS_CMD_RX_TEST_config_bNoSync 1 +#define _POSITION_CMD_RX_TEST_endTrigger 15 +#define _TYPE_CMD_RX_TEST_endTrigger uint8_t +#define _BITPOS_CMD_RX_TEST_endTrigger_triggerType 0 +#define _NBITS_CMD_RX_TEST_endTrigger_triggerType 4 +#define _BITPOS_CMD_RX_TEST_endTrigger_bEnaCmd 4 +#define _NBITS_CMD_RX_TEST_endTrigger_bEnaCmd 1 +#define _BITPOS_CMD_RX_TEST_endTrigger_triggerNo 5 +#define _NBITS_CMD_RX_TEST_endTrigger_triggerNo 2 +#define _BITPOS_CMD_RX_TEST_endTrigger_pastTrig 7 +#define _NBITS_CMD_RX_TEST_endTrigger_pastTrig 1 +#define _POSITION_CMD_RX_TEST_syncWord 16 +#define _TYPE_CMD_RX_TEST_syncWord uint32_t +#define _POSITION_CMD_RX_TEST_endTime 20 +#define _TYPE_CMD_RX_TEST_endTime ratmr_t +#define _SIZEOF_CMD_RX_TEST 24 + +#define _POSITION_CMD_TX_TEST_config 14 +#define _TYPE_CMD_TX_TEST_config uint8_t +#define _BITPOS_CMD_TX_TEST_config_bUseCw 0 +#define _NBITS_CMD_TX_TEST_config_bUseCw 1 +#define _BITPOS_CMD_TX_TEST_config_bFsOff 1 +#define _NBITS_CMD_TX_TEST_config_bFsOff 1 +#define _BITPOS_CMD_TX_TEST_config_whitenMode 2 +#define _NBITS_CMD_TX_TEST_config_whitenMode 2 +#define _POSITION_CMD_TX_TEST_txWord 16 +#define _TYPE_CMD_TX_TEST_txWord uint16_t +#define _POSITION_CMD_TX_TEST_endTrigger 19 +#define _TYPE_CMD_TX_TEST_endTrigger uint8_t +#define _BITPOS_CMD_TX_TEST_endTrigger_triggerType 0 +#define _NBITS_CMD_TX_TEST_endTrigger_triggerType 4 +#define _BITPOS_CMD_TX_TEST_endTrigger_bEnaCmd 4 +#define _NBITS_CMD_TX_TEST_endTrigger_bEnaCmd 1 +#define _BITPOS_CMD_TX_TEST_endTrigger_triggerNo 5 +#define _NBITS_CMD_TX_TEST_endTrigger_triggerNo 2 +#define _BITPOS_CMD_TX_TEST_endTrigger_pastTrig 7 +#define _NBITS_CMD_TX_TEST_endTrigger_pastTrig 1 +#define _POSITION_CMD_TX_TEST_syncWord 20 +#define _TYPE_CMD_TX_TEST_syncWord uint32_t +#define _POSITION_CMD_TX_TEST_endTime 24 +#define _TYPE_CMD_TX_TEST_endTime ratmr_t +#define _SIZEOF_CMD_TX_TEST 28 + +#define _POSITION_CMD_SYNC_STOP_RAT_rat0 16 +#define _TYPE_CMD_SYNC_STOP_RAT_rat0 ratmr_t +#define _SIZEOF_CMD_SYNC_STOP_RAT 20 + +#define _POSITION_CMD_SYNC_START_RAT_rat0 16 +#define _TYPE_CMD_SYNC_START_RAT_rat0 ratmr_t +#define _SIZEOF_CMD_SYNC_START_RAT 20 + +#define _POSITION_CMD_COUNT_counter 14 +#define _TYPE_CMD_COUNT_counter uint16_t +#define _SIZEOF_CMD_COUNT 16 + +#define _POSITION_CMD_FS_POWERUP_pRegOverride 16 +#define _TYPE_CMD_FS_POWERUP_pRegOverride uint32_t* +#define _SIZEOF_CMD_FS_POWERUP 20 + +#define _SIZEOF_CMD_FS_POWERDOWN 14 + +#define _POSITION_CMD_SCH_IMM_cmdrVal 16 +#define _TYPE_CMD_SCH_IMM_cmdrVal uint32_t +#define _POSITION_CMD_SCH_IMM_cmdstaVal 20 +#define _TYPE_CMD_SCH_IMM_cmdstaVal uint32_t +#define _SIZEOF_CMD_SCH_IMM 24 + +#define _POSITION_CMD_COUNT_BRANCH_counter 14 +#define _TYPE_CMD_COUNT_BRANCH_counter uint16_t +#define _POSITION_CMD_COUNT_BRANCH_pNextOpIfOk 16 +#define _TYPE_CMD_COUNT_BRANCH_pNextOpIfOk uint8_t* +#define _SIZEOF_CMD_COUNT_BRANCH 20 + +#define _POSITION_CMD_PATTERN_CHECK_patternOpt 14 +#define _TYPE_CMD_PATTERN_CHECK_patternOpt uint16_t +#define _BITPOS_CMD_PATTERN_CHECK_patternOpt_operation 0 +#define _NBITS_CMD_PATTERN_CHECK_patternOpt_operation 2 +#define _BITPOS_CMD_PATTERN_CHECK_patternOpt_bByteRev 2 +#define _NBITS_CMD_PATTERN_CHECK_patternOpt_bByteRev 1 +#define _BITPOS_CMD_PATTERN_CHECK_patternOpt_bBitRev 3 +#define _NBITS_CMD_PATTERN_CHECK_patternOpt_bBitRev 1 +#define _BITPOS_CMD_PATTERN_CHECK_patternOpt_signExtend 4 +#define _NBITS_CMD_PATTERN_CHECK_patternOpt_signExtend 5 +#define _BITPOS_CMD_PATTERN_CHECK_patternOpt_bRxVal 9 +#define _NBITS_CMD_PATTERN_CHECK_patternOpt_bRxVal 1 +#define _POSITION_CMD_PATTERN_CHECK_pNextOpIfOk 16 +#define _TYPE_CMD_PATTERN_CHECK_pNextOpIfOk uint8_t* +#define _POSITION_CMD_PATTERN_CHECK_pValue 20 +#define _TYPE_CMD_PATTERN_CHECK_pValue uint8_t* +#define _POSITION_CMD_PATTERN_CHECK_mask 24 +#define _TYPE_CMD_PATTERN_CHECK_mask uint32_t +#define _POSITION_CMD_PATTERN_CHECK_compareVal 28 +#define _TYPE_CMD_PATTERN_CHECK_compareVal uint32_t +#define _SIZEOF_CMD_PATTERN_CHECK 32 + +#define _SIZEOF_CMD_ABORT 2 + +#define _SIZEOF_CMD_STOP 2 + +#define _SIZEOF_CMD_GET_RSSI 2 + +#define _POSITION_CMD_UPDATE_RADIO_SETUP_pRegOverride 4 +#define _TYPE_CMD_UPDATE_RADIO_SETUP_pRegOverride uint32_t* +#define _SIZEOF_CMD_UPDATE_RADIO_SETUP 8 + +#define _POSITION_CMD_TRIGGER_triggerNo 2 +#define _TYPE_CMD_TRIGGER_triggerNo uint8_t +#define _SIZEOF_CMD_TRIGGER 3 + +#define _POSITION_CMD_GET_FW_INFO_versionNo 2 +#define _TYPE_CMD_GET_FW_INFO_versionNo uint16_t +#define _POSITION_CMD_GET_FW_INFO_startOffset 4 +#define _TYPE_CMD_GET_FW_INFO_startOffset uint16_t +#define _POSITION_CMD_GET_FW_INFO_freeRamSz 6 +#define _TYPE_CMD_GET_FW_INFO_freeRamSz uint16_t +#define _POSITION_CMD_GET_FW_INFO_availRatCh 8 +#define _TYPE_CMD_GET_FW_INFO_availRatCh uint16_t +#define _SIZEOF_CMD_GET_FW_INFO 10 + +#define _SIZEOF_CMD_START_RAT 2 + +#define _SIZEOF_CMD_PING 2 + +#define _POSITION_CMD_ADD_DATA_ENTRY_pQueue 4 +#define _TYPE_CMD_ADD_DATA_ENTRY_pQueue dataQueue_t* +#define _POSITION_CMD_ADD_DATA_ENTRY_pEntry 8 +#define _TYPE_CMD_ADD_DATA_ENTRY_pEntry uint8_t* +#define _SIZEOF_CMD_ADD_DATA_ENTRY 12 + +#define _POSITION_CMD_REMOVE_DATA_ENTRY_pQueue 4 +#define _TYPE_CMD_REMOVE_DATA_ENTRY_pQueue dataQueue_t* +#define _POSITION_CMD_REMOVE_DATA_ENTRY_pEntry 8 +#define _TYPE_CMD_REMOVE_DATA_ENTRY_pEntry uint8_t* +#define _SIZEOF_CMD_REMOVE_DATA_ENTRY 12 + +#define _POSITION_CMD_FLUSH_QUEUE_pQueue 4 +#define _TYPE_CMD_FLUSH_QUEUE_pQueue dataQueue_t* +#define _POSITION_CMD_FLUSH_QUEUE_pFirstEntry 8 +#define _TYPE_CMD_FLUSH_QUEUE_pFirstEntry uint8_t* +#define _SIZEOF_CMD_FLUSH_QUEUE 12 + +#define _POSITION_CMD_CLEAR_RX_pQueue 4 +#define _TYPE_CMD_CLEAR_RX_pQueue dataQueue_t* +#define _SIZEOF_CMD_CLEAR_RX 8 + +#define _POSITION_CMD_REMOVE_PENDING_ENTRIES_pQueue 4 +#define _TYPE_CMD_REMOVE_PENDING_ENTRIES_pQueue dataQueue_t* +#define _POSITION_CMD_REMOVE_PENDING_ENTRIES_pFirstEntry 8 +#define _TYPE_CMD_REMOVE_PENDING_ENTRIES_pFirstEntry uint8_t* +#define _SIZEOF_CMD_REMOVE_PENDING_ENTRIES 12 + +#define _POSITION_CMD_SET_RAT_CMP_ratCh 2 +#define _TYPE_CMD_SET_RAT_CMP_ratCh uint8_t +#define _POSITION_CMD_SET_RAT_CMP_compareTime 4 +#define _TYPE_CMD_SET_RAT_CMP_compareTime ratmr_t +#define _SIZEOF_CMD_SET_RAT_CMP 8 + +#define _POSITION_CMD_SET_RAT_CPT_config 2 +#define _TYPE_CMD_SET_RAT_CPT_config uint16_t +#define _BITPOS_CMD_SET_RAT_CPT_config_inputSrc 3 +#define _NBITS_CMD_SET_RAT_CPT_config_inputSrc 5 +#define _BITPOS_CMD_SET_RAT_CPT_config_ratCh 8 +#define _NBITS_CMD_SET_RAT_CPT_config_ratCh 4 +#define _BITPOS_CMD_SET_RAT_CPT_config_bRepeated 12 +#define _NBITS_CMD_SET_RAT_CPT_config_bRepeated 1 +#define _BITPOS_CMD_SET_RAT_CPT_config_inputMode 13 +#define _NBITS_CMD_SET_RAT_CPT_config_inputMode 2 +#define _SIZEOF_CMD_SET_RAT_CPT 4 + +#define _POSITION_CMD_DISABLE_RAT_CH_ratCh 2 +#define _TYPE_CMD_DISABLE_RAT_CH_ratCh uint8_t +#define _SIZEOF_CMD_DISABLE_RAT_CH 3 + +#define _POSITION_CMD_SET_RAT_OUTPUT_config 2 +#define _TYPE_CMD_SET_RAT_OUTPUT_config uint16_t +#define _BITPOS_CMD_SET_RAT_OUTPUT_config_outputSel 2 +#define _NBITS_CMD_SET_RAT_OUTPUT_config_outputSel 3 +#define _BITPOS_CMD_SET_RAT_OUTPUT_config_outputMode 5 +#define _NBITS_CMD_SET_RAT_OUTPUT_config_outputMode 3 +#define _BITPOS_CMD_SET_RAT_OUTPUT_config_ratCh 8 +#define _NBITS_CMD_SET_RAT_OUTPUT_config_ratCh 4 +#define _SIZEOF_CMD_SET_RAT_OUTPUT 4 + +#define _POSITION_CMD_ARM_RAT_CH_ratCh 2 +#define _TYPE_CMD_ARM_RAT_CH_ratCh uint8_t +#define _SIZEOF_CMD_ARM_RAT_CH 3 + +#define _POSITION_CMD_DISARM_RAT_CH_ratCh 2 +#define _TYPE_CMD_DISARM_RAT_CH_ratCh uint8_t +#define _SIZEOF_CMD_DISARM_RAT_CH 3 + +#define _POSITION_CMD_SET_TX_POWER_txPower 2 +#define _TYPE_CMD_SET_TX_POWER_txPower uint16_t +#define _BITPOS_CMD_SET_TX_POWER_txPower_IB 0 +#define _NBITS_CMD_SET_TX_POWER_txPower_IB 6 +#define _BITPOS_CMD_SET_TX_POWER_txPower_GC 6 +#define _NBITS_CMD_SET_TX_POWER_txPower_GC 2 +#define _BITPOS_CMD_SET_TX_POWER_txPower_tempCoeff 8 +#define _NBITS_CMD_SET_TX_POWER_txPower_tempCoeff 8 +#define _SIZEOF_CMD_SET_TX_POWER 4 + +#define _POSITION_CMD_UPDATE_FS_frequency 2 +#define _TYPE_CMD_UPDATE_FS_frequency uint16_t +#define _POSITION_CMD_UPDATE_FS_fractFreq 4 +#define _TYPE_CMD_UPDATE_FS_fractFreq uint16_t +#define _SIZEOF_CMD_UPDATE_FS 6 + +#define _POSITION_CMD_BUS_REQUEST_bSysBusNeeded 2 +#define _TYPE_CMD_BUS_REQUEST_bSysBusNeeded uint8_t +#define _SIZEOF_CMD_BUS_REQUEST 3 + +#endif diff --git a/cpu/cc26xx/dev/rfc-api/data_entry.h b/cpu/cc26xx/dev/rfc-api/data_entry.h new file mode 100644 index 000000000..91c1826de --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/data_entry.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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 __DATA_ENTRY_H +#define __DATA_ENTRY_H + +#include +#include "mailbox.h" + +typedef struct rfc_dataEntry_s rfc_dataEntry_t; + +#define _POSITION_dataEntry_pNextEntry 0 +#define _TYPE_dataEntry_pNextEntry uint8_t* +#define _POSITION_dataEntry_status 4 +#define _TYPE_dataEntry_status uint8_t +#define _POSITION_dataEntry_config 5 +#define _TYPE_dataEntry_config uint8_t +#define _BITPOS_dataEntry_config_type 0 +#define _NBITS_dataEntry_config_type 2 +#define _BITPOS_dataEntry_config_lenSz 2 +#define _NBITS_dataEntry_config_lenSz 2 +#define _BITPOS_dataEntry_config_irqIntv 4 +#define _NBITS_dataEntry_config_irqIntv 4 +#define _POSITION_dataEntry_length 6 +#define _TYPE_dataEntry_length uint16_t +#define _POSITION_dataEntry_data 8 +#define _TYPE_dataEntry_data uint8_t +#define _POSITION_dataEntry_pData 8 +#define _TYPE_dataEntry_pData uint8_t* +#define _POSITION_dataEntry_numElements 8 +#define _TYPE_dataEntry_numElements uint16_t +#define _POSITION_dataEntry_pktStatus 8 +#define _TYPE_dataEntry_pktStatus uint16_t +#define _BITPOS_dataEntry_pktStatus_numElements 0 +#define _NBITS_dataEntry_pktStatus_numElements 13 +#define _BITPOS_dataEntry_pktStatus_bEntryOpen 13 +#define _NBITS_dataEntry_pktStatus_bEntryOpen 1 +#define _BITPOS_dataEntry_pktStatus_bFirstCont 14 +#define _NBITS_dataEntry_pktStatus_bFirstCont 1 +#define _BITPOS_dataEntry_pktStatus_bLastCont 15 +#define _NBITS_dataEntry_pktStatus_bLastCont 1 +#define _POSITION_dataEntry_nextIndex 10 +#define _TYPE_dataEntry_nextIndex uint16_t +#define _POSITION_dataEntry_rxData 12 +#define _TYPE_dataEntry_rxData uint8_t +#define _LAST_POSITION_dataEntry 12 +#define _LAST_TYPE_dataEntry uint8_t + +struct rfc_dataEntry_s { + uint8_t* pNextEntry; + uint8_t status; + struct { + uint8_t type:2; + uint8_t lenSz:2; + uint8_t irqIntv:4; + } config; + uint16_t length; + uint8_t data; + uint8_t __dummy0; + uint16_t nextIndex; + uint8_t rxData; +}; + +#endif diff --git a/cpu/cc26xx/dev/rfc-api/ieee_cmd.h b/cpu/cc26xx/dev/rfc-api/ieee_cmd.h new file mode 100644 index 000000000..e06207053 --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/ieee_cmd.h @@ -0,0 +1,614 @@ +/****************************************************************************** +* Filename: ieee_cmd.h +* Revised: $ $ +* Revision: $ $ +* +* Description: CC26xx/CC13xx API for IEEE 802.15.4 commands +* +* Copyright (C) 2015 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 __IEEE_CMD_H +#define __IEEE_CMD_H + +#ifndef __RFC_STRUCT +#ifdef __GNUC__ +#define __RFC_STRUCT __attribute__ ((aligned (4))) +#else +#define __RFC_STRUCT +#endif +#endif + +//! \addtogroup rfc +//! @{ + +//! \addtogroup ieee_cmd +//! @{ + +#include +#include "mailbox.h" +#include "common_cmd.h" + +typedef struct __RFC_STRUCT rfc_CMD_IEEE_RX_s rfc_CMD_IEEE_RX_t; +typedef struct __RFC_STRUCT rfc_shortAddrEntry_s rfc_shortAddrEntry_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_CSMA_s rfc_CMD_IEEE_CSMA_t; +typedef struct __RFC_STRUCT rfc_ieeeAuxSecCtrl_s rfc_ieeeAuxSecCtrl_t; +typedef struct __RFC_STRUCT rfc_ieeeRxCorrCrc_s rfc_ieeeRxCorrCrc_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_ED_SCAN_s rfc_CMD_IEEE_ED_SCAN_t; +typedef struct __RFC_STRUCT rfc_ieeeMacHdr_s rfc_ieeeMacHdr_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_ABORT_FG_s rfc_CMD_IEEE_ABORT_FG_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_CCA_REQ_s rfc_CMD_IEEE_CCA_REQ_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_SETUP_s rfc_CMD_IEEE_SETUP_t; +typedef struct __RFC_STRUCT rfc_ieeeRxOutput_s rfc_ieeeRxOutput_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_STOP_FG_s rfc_CMD_IEEE_STOP_FG_t; +typedef struct __RFC_STRUCT rfc_CMD_IEEE_TX_s rfc_CMD_IEEE_TX_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_CCA_s rfc_CMD_IEEE_MOD_CCA_t; + +//! \addtogroup CMD_IEEE_RX +//! @{ +#define CMD_IEEE_RX 0x2801 +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 if the rule involves skipping + } 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 + rfc_shortAddrEntry_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 +}; + +//! @} + +//! \addtogroup CMD_IEEE_ED_SCAN +//! @{ +#define CMD_IEEE_ED_SCAN 0x2802 +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 if the rule involves skipping + } 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 +}; + +//! @} + +//! \addtogroup CMD_IEEE_TX +//! @{ +#define CMD_IEEE_TX 0x2C01 +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 if the rule involves skipping + } 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 +}; + +//! @} + +//! \addtogroup CMD_IEEE_CSMA +//! @{ +#define CMD_IEEE_CSMA 0x2C02 +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 if the rule involves skipping + } 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 +}; + +//! @} + +//! \addtogroup CMD_IEEE_RX_ACK +//! @{ +#define CMD_IEEE_RX_ACK 0x2C03 +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 if the rule involves skipping + } 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 +}; + +//! @} + +//! \addtogroup CMD_IEEE_ABORT_BG +//! @{ +#define CMD_IEEE_ABORT_BG 0x2C04 +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 if the rule involves skipping + } condition; +}; + +//! @} + +//! \addtogroup CMD_IEEE_MOD_CCA +//! @{ +#define CMD_IEEE_MOD_CCA 0x2001 +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 +}; + +//! @} + +//! \addtogroup CMD_IEEE_MOD_FILT +//! @{ +#define CMD_IEEE_MOD_FILT 0x2002 +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 +}; + +//! @} + +//! \addtogroup CMD_IEEE_MOD_SRC_MATCH +//! @{ +#define CMD_IEEE_MOD_SRC_MATCH 0x2003 +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: Extended address
+ //!< 1: Short address + } options; + uint8_t entryNo; //!< Index of entry to enable or disable +}; + +//! @} + +//! \addtogroup CMD_IEEE_ABORT_FG +//! @{ +#define CMD_IEEE_ABORT_FG 0x2401 +struct __RFC_STRUCT rfc_CMD_IEEE_ABORT_FG_s { + uint16_t commandNo; //!< The command ID number 0x2401 +}; + +//! @} + +//! \addtogroup CMD_IEEE_STOP_FG +//! @{ +#define CMD_IEEE_STOP_FG 0x2402 +struct __RFC_STRUCT rfc_CMD_IEEE_STOP_FG_s { + uint16_t commandNo; //!< The command ID number 0x2402 +}; + +//! @} + +//! \addtogroup CMD_IEEE_CCA_REQ +//! @{ +#define CMD_IEEE_CCA_REQ 0x2403 +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; +}; + +//! @} + +//! \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 +}; + +//! @} + +//! \addtogroup shortAddrEntry +//! @{ +//! Structure for short address entries + +struct __RFC_STRUCT rfc_shortAddrEntry_s { + uint16_t shortAddr; //!< Short address + uint16_t panId; //!< PAN ID +}; + +//! @} + +//! \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; +}; + +//! @} + +//! @} +//! @} +#endif diff --git a/cpu/cc26xx/dev/rfc-api/ieee_cmd_field.h b/cpu/cc26xx/dev/rfc-api/ieee_cmd_field.h new file mode 100755 index 000000000..149940f79 --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/ieee_cmd_field.h @@ -0,0 +1,403 @@ +/****************************************************************************** +* Filename: ieee_cmd_field.h +* Revised: $ $ +* Revision: $ $ +* +* Description: CC26xx/CC13xx API for IEEE 802.15.4 commands +* +* Copyright (C) 2015 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 __IEEE_CMD_FIELD_H +#define __IEEE_CMD_FIELD_H + +#include +#include "mailbox.h" +#include "common_cmd.h" + +#define _POSITION_CMD_IEEE_RX_channel 14 +#define _TYPE_CMD_IEEE_RX_channel uint8_t +#define _POSITION_CMD_IEEE_RX_rxConfig 15 +#define _TYPE_CMD_IEEE_RX_rxConfig uint8_t +#define _BITPOS_CMD_IEEE_RX_rxConfig_bAutoFlushCrc 0 +#define _NBITS_CMD_IEEE_RX_rxConfig_bAutoFlushCrc 1 +#define _BITPOS_CMD_IEEE_RX_rxConfig_bAutoFlushIgn 1 +#define _NBITS_CMD_IEEE_RX_rxConfig_bAutoFlushIgn 1 +#define _BITPOS_CMD_IEEE_RX_rxConfig_bIncludePhyHdr 2 +#define _NBITS_CMD_IEEE_RX_rxConfig_bIncludePhyHdr 1 +#define _BITPOS_CMD_IEEE_RX_rxConfig_bIncludeCrc 3 +#define _NBITS_CMD_IEEE_RX_rxConfig_bIncludeCrc 1 +#define _BITPOS_CMD_IEEE_RX_rxConfig_bAppendRssi 4 +#define _NBITS_CMD_IEEE_RX_rxConfig_bAppendRssi 1 +#define _BITPOS_CMD_IEEE_RX_rxConfig_bAppendCorrCrc 5 +#define _NBITS_CMD_IEEE_RX_rxConfig_bAppendCorrCrc 1 +#define _BITPOS_CMD_IEEE_RX_rxConfig_bAppendSrcInd 6 +#define _NBITS_CMD_IEEE_RX_rxConfig_bAppendSrcInd 1 +#define _BITPOS_CMD_IEEE_RX_rxConfig_bAppendTimestamp 7 +#define _NBITS_CMD_IEEE_RX_rxConfig_bAppendTimestamp 1 +#define _POSITION_CMD_IEEE_RX_pRxQ 16 +#define _TYPE_CMD_IEEE_RX_pRxQ dataQueue_t* +#define _POSITION_CMD_IEEE_RX_pOutput 20 +#define _TYPE_CMD_IEEE_RX_pOutput uint8_t* +#define _POSITION_CMD_IEEE_RX_frameFiltOpt 24 +#define _TYPE_CMD_IEEE_RX_frameFiltOpt uint16_t +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_frameFiltEn 0 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_frameFiltEn 1 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_frameFiltStop 1 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_frameFiltStop 1 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_autoAckEn 2 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_autoAckEn 1 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_slottedAckEn 3 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_slottedAckEn 1 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_autoPendEn 4 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_autoPendEn 1 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_defaultPend 5 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_defaultPend 1 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_bPendDataReqOnly 6 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_bPendDataReqOnly 1 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_bPanCoord 7 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_bPanCoord 1 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_maxFrameVersion 8 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_maxFrameVersion 2 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_fcfReservedMask 10 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_fcfReservedMask 3 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_modifyFtFilter 13 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_modifyFtFilter 2 +#define _BITPOS_CMD_IEEE_RX_frameFiltOpt_bStrictLenFilter 15 +#define _NBITS_CMD_IEEE_RX_frameFiltOpt_bStrictLenFilter 1 +#define _POSITION_CMD_IEEE_RX_frameTypes 26 +#define _TYPE_CMD_IEEE_RX_frameTypes uint8_t +#define _BITPOS_CMD_IEEE_RX_frameTypes_bAcceptFt0Beacon 0 +#define _NBITS_CMD_IEEE_RX_frameTypes_bAcceptFt0Beacon 1 +#define _BITPOS_CMD_IEEE_RX_frameTypes_bAcceptFt1Data 1 +#define _NBITS_CMD_IEEE_RX_frameTypes_bAcceptFt1Data 1 +#define _BITPOS_CMD_IEEE_RX_frameTypes_bAcceptFt2Ack 2 +#define _NBITS_CMD_IEEE_RX_frameTypes_bAcceptFt2Ack 1 +#define _BITPOS_CMD_IEEE_RX_frameTypes_bAcceptFt3MacCmd 3 +#define _NBITS_CMD_IEEE_RX_frameTypes_bAcceptFt3MacCmd 1 +#define _BITPOS_CMD_IEEE_RX_frameTypes_bAcceptFt4Reserved 4 +#define _NBITS_CMD_IEEE_RX_frameTypes_bAcceptFt4Reserved 1 +#define _BITPOS_CMD_IEEE_RX_frameTypes_bAcceptFt5Reserved 5 +#define _NBITS_CMD_IEEE_RX_frameTypes_bAcceptFt5Reserved 1 +#define _BITPOS_CMD_IEEE_RX_frameTypes_bAcceptFt6Reserved 6 +#define _NBITS_CMD_IEEE_RX_frameTypes_bAcceptFt6Reserved 1 +#define _BITPOS_CMD_IEEE_RX_frameTypes_bAcceptFt7Reserved 7 +#define _NBITS_CMD_IEEE_RX_frameTypes_bAcceptFt7Reserved 1 +#define _POSITION_CMD_IEEE_RX_ccaOpt 27 +#define _TYPE_CMD_IEEE_RX_ccaOpt uint8_t +#define _BITPOS_CMD_IEEE_RX_ccaOpt_ccaEnEnergy 0 +#define _NBITS_CMD_IEEE_RX_ccaOpt_ccaEnEnergy 1 +#define _BITPOS_CMD_IEEE_RX_ccaOpt_ccaEnCorr 1 +#define _NBITS_CMD_IEEE_RX_ccaOpt_ccaEnCorr 1 +#define _BITPOS_CMD_IEEE_RX_ccaOpt_ccaEnSync 2 +#define _NBITS_CMD_IEEE_RX_ccaOpt_ccaEnSync 1 +#define _BITPOS_CMD_IEEE_RX_ccaOpt_ccaCorrOp 3 +#define _NBITS_CMD_IEEE_RX_ccaOpt_ccaCorrOp 1 +#define _BITPOS_CMD_IEEE_RX_ccaOpt_ccaSyncOp 4 +#define _NBITS_CMD_IEEE_RX_ccaOpt_ccaSyncOp 1 +#define _BITPOS_CMD_IEEE_RX_ccaOpt_ccaCorrThr 5 +#define _NBITS_CMD_IEEE_RX_ccaOpt_ccaCorrThr 2 +#define _POSITION_CMD_IEEE_RX_ccaRssiThr 28 +#define _TYPE_CMD_IEEE_RX_ccaRssiThr int8_t +#define _POSITION_CMD_IEEE_RX_numExtEntries 30 +#define _TYPE_CMD_IEEE_RX_numExtEntries uint8_t +#define _POSITION_CMD_IEEE_RX_numShortEntries 31 +#define _TYPE_CMD_IEEE_RX_numShortEntries uint8_t +#define _POSITION_CMD_IEEE_RX_pExtEntryList 32 +#define _TYPE_CMD_IEEE_RX_pExtEntryList uint32_t* +#define _POSITION_CMD_IEEE_RX_pShortEntryList 36 +#define _TYPE_CMD_IEEE_RX_pShortEntryList uint32_t* +#define _POSITION_CMD_IEEE_RX_localExtAddr 40 +#define _TYPE_CMD_IEEE_RX_localExtAddr uint64_t +#define _POSITION_CMD_IEEE_RX_localShortAddr 48 +#define _TYPE_CMD_IEEE_RX_localShortAddr uint16_t +#define _POSITION_CMD_IEEE_RX_localPanID 50 +#define _TYPE_CMD_IEEE_RX_localPanID uint16_t +#define _POSITION_CMD_IEEE_RX_endTrigger 55 +#define _TYPE_CMD_IEEE_RX_endTrigger uint8_t +#define _BITPOS_CMD_IEEE_RX_endTrigger_triggerType 0 +#define _NBITS_CMD_IEEE_RX_endTrigger_triggerType 4 +#define _BITPOS_CMD_IEEE_RX_endTrigger_bEnaCmd 4 +#define _NBITS_CMD_IEEE_RX_endTrigger_bEnaCmd 1 +#define _BITPOS_CMD_IEEE_RX_endTrigger_triggerNo 5 +#define _NBITS_CMD_IEEE_RX_endTrigger_triggerNo 2 +#define _BITPOS_CMD_IEEE_RX_endTrigger_pastTrig 7 +#define _NBITS_CMD_IEEE_RX_endTrigger_pastTrig 1 +#define _POSITION_CMD_IEEE_RX_endTime 56 +#define _TYPE_CMD_IEEE_RX_endTime ratmr_t +#define _SIZEOF_CMD_IEEE_RX 60 + +#define _POSITION_CMD_IEEE_ED_SCAN_channel 14 +#define _TYPE_CMD_IEEE_ED_SCAN_channel uint8_t +#define _POSITION_CMD_IEEE_ED_SCAN_ccaOpt 15 +#define _TYPE_CMD_IEEE_ED_SCAN_ccaOpt uint8_t +#define _BITPOS_CMD_IEEE_ED_SCAN_ccaOpt_ccaEnEnergy 0 +#define _NBITS_CMD_IEEE_ED_SCAN_ccaOpt_ccaEnEnergy 1 +#define _BITPOS_CMD_IEEE_ED_SCAN_ccaOpt_ccaEnCorr 1 +#define _NBITS_CMD_IEEE_ED_SCAN_ccaOpt_ccaEnCorr 1 +#define _BITPOS_CMD_IEEE_ED_SCAN_ccaOpt_ccaEnSync 2 +#define _NBITS_CMD_IEEE_ED_SCAN_ccaOpt_ccaEnSync 1 +#define _BITPOS_CMD_IEEE_ED_SCAN_ccaOpt_ccaCorrOp 3 +#define _NBITS_CMD_IEEE_ED_SCAN_ccaOpt_ccaCorrOp 1 +#define _BITPOS_CMD_IEEE_ED_SCAN_ccaOpt_ccaSyncOp 4 +#define _NBITS_CMD_IEEE_ED_SCAN_ccaOpt_ccaSyncOp 1 +#define _BITPOS_CMD_IEEE_ED_SCAN_ccaOpt_ccaCorrThr 5 +#define _NBITS_CMD_IEEE_ED_SCAN_ccaOpt_ccaCorrThr 2 +#define _POSITION_CMD_IEEE_ED_SCAN_ccaRssiThr 16 +#define _TYPE_CMD_IEEE_ED_SCAN_ccaRssiThr int8_t +#define _POSITION_CMD_IEEE_ED_SCAN_maxRssi 18 +#define _TYPE_CMD_IEEE_ED_SCAN_maxRssi int8_t +#define _POSITION_CMD_IEEE_ED_SCAN_endTrigger 19 +#define _TYPE_CMD_IEEE_ED_SCAN_endTrigger uint8_t +#define _BITPOS_CMD_IEEE_ED_SCAN_endTrigger_triggerType 0 +#define _NBITS_CMD_IEEE_ED_SCAN_endTrigger_triggerType 4 +#define _BITPOS_CMD_IEEE_ED_SCAN_endTrigger_bEnaCmd 4 +#define _NBITS_CMD_IEEE_ED_SCAN_endTrigger_bEnaCmd 1 +#define _BITPOS_CMD_IEEE_ED_SCAN_endTrigger_triggerNo 5 +#define _NBITS_CMD_IEEE_ED_SCAN_endTrigger_triggerNo 2 +#define _BITPOS_CMD_IEEE_ED_SCAN_endTrigger_pastTrig 7 +#define _NBITS_CMD_IEEE_ED_SCAN_endTrigger_pastTrig 1 +#define _POSITION_CMD_IEEE_ED_SCAN_endTime 20 +#define _TYPE_CMD_IEEE_ED_SCAN_endTime ratmr_t +#define _SIZEOF_CMD_IEEE_ED_SCAN 24 + +#define _POSITION_CMD_IEEE_TX_txOpt 14 +#define _TYPE_CMD_IEEE_TX_txOpt uint8_t +#define _BITPOS_CMD_IEEE_TX_txOpt_bIncludePhyHdr 0 +#define _NBITS_CMD_IEEE_TX_txOpt_bIncludePhyHdr 1 +#define _BITPOS_CMD_IEEE_TX_txOpt_bIncludeCrc 1 +#define _NBITS_CMD_IEEE_TX_txOpt_bIncludeCrc 1 +#define _BITPOS_CMD_IEEE_TX_txOpt_payloadLenMsb 3 +#define _NBITS_CMD_IEEE_TX_txOpt_payloadLenMsb 5 +#define _POSITION_CMD_IEEE_TX_payloadLen 15 +#define _TYPE_CMD_IEEE_TX_payloadLen uint8_t +#define _POSITION_CMD_IEEE_TX_pPayload 16 +#define _TYPE_CMD_IEEE_TX_pPayload uint8_t* +#define _POSITION_CMD_IEEE_TX_timeStamp 20 +#define _TYPE_CMD_IEEE_TX_timeStamp ratmr_t +#define _SIZEOF_CMD_IEEE_TX 24 + +#define _POSITION_CMD_IEEE_CSMA_randomState 14 +#define _TYPE_CMD_IEEE_CSMA_randomState uint16_t +#define _POSITION_CMD_IEEE_CSMA_macMaxBE 16 +#define _TYPE_CMD_IEEE_CSMA_macMaxBE uint8_t +#define _POSITION_CMD_IEEE_CSMA_macMaxCSMABackoffs 17 +#define _TYPE_CMD_IEEE_CSMA_macMaxCSMABackoffs uint8_t +#define _POSITION_CMD_IEEE_CSMA_csmaConfig 18 +#define _TYPE_CMD_IEEE_CSMA_csmaConfig uint8_t +#define _BITPOS_CMD_IEEE_CSMA_csmaConfig_initCW 0 +#define _NBITS_CMD_IEEE_CSMA_csmaConfig_initCW 5 +#define _BITPOS_CMD_IEEE_CSMA_csmaConfig_bSlotted 5 +#define _NBITS_CMD_IEEE_CSMA_csmaConfig_bSlotted 1 +#define _BITPOS_CMD_IEEE_CSMA_csmaConfig_rxOffMode 6 +#define _NBITS_CMD_IEEE_CSMA_csmaConfig_rxOffMode 2 +#define _POSITION_CMD_IEEE_CSMA_NB 19 +#define _TYPE_CMD_IEEE_CSMA_NB uint8_t +#define _POSITION_CMD_IEEE_CSMA_BE 20 +#define _TYPE_CMD_IEEE_CSMA_BE uint8_t +#define _POSITION_CMD_IEEE_CSMA_remainingPeriods 21 +#define _TYPE_CMD_IEEE_CSMA_remainingPeriods uint8_t +#define _POSITION_CMD_IEEE_CSMA_lastRssi 22 +#define _TYPE_CMD_IEEE_CSMA_lastRssi int8_t +#define _POSITION_CMD_IEEE_CSMA_endTrigger 23 +#define _TYPE_CMD_IEEE_CSMA_endTrigger uint8_t +#define _BITPOS_CMD_IEEE_CSMA_endTrigger_triggerType 0 +#define _NBITS_CMD_IEEE_CSMA_endTrigger_triggerType 4 +#define _BITPOS_CMD_IEEE_CSMA_endTrigger_bEnaCmd 4 +#define _NBITS_CMD_IEEE_CSMA_endTrigger_bEnaCmd 1 +#define _BITPOS_CMD_IEEE_CSMA_endTrigger_triggerNo 5 +#define _NBITS_CMD_IEEE_CSMA_endTrigger_triggerNo 2 +#define _BITPOS_CMD_IEEE_CSMA_endTrigger_pastTrig 7 +#define _NBITS_CMD_IEEE_CSMA_endTrigger_pastTrig 1 +#define _POSITION_CMD_IEEE_CSMA_lastTimeStamp 24 +#define _TYPE_CMD_IEEE_CSMA_lastTimeStamp ratmr_t +#define _POSITION_CMD_IEEE_CSMA_endTime 28 +#define _TYPE_CMD_IEEE_CSMA_endTime ratmr_t +#define _SIZEOF_CMD_IEEE_CSMA 32 + +#define _POSITION_CMD_IEEE_RX_ACK_seqNo 14 +#define _TYPE_CMD_IEEE_RX_ACK_seqNo uint8_t +#define _POSITION_CMD_IEEE_RX_ACK_endTrigger 15 +#define _TYPE_CMD_IEEE_RX_ACK_endTrigger uint8_t +#define _BITPOS_CMD_IEEE_RX_ACK_endTrigger_triggerType 0 +#define _NBITS_CMD_IEEE_RX_ACK_endTrigger_triggerType 4 +#define _BITPOS_CMD_IEEE_RX_ACK_endTrigger_bEnaCmd 4 +#define _NBITS_CMD_IEEE_RX_ACK_endTrigger_bEnaCmd 1 +#define _BITPOS_CMD_IEEE_RX_ACK_endTrigger_triggerNo 5 +#define _NBITS_CMD_IEEE_RX_ACK_endTrigger_triggerNo 2 +#define _BITPOS_CMD_IEEE_RX_ACK_endTrigger_pastTrig 7 +#define _NBITS_CMD_IEEE_RX_ACK_endTrigger_pastTrig 1 +#define _POSITION_CMD_IEEE_RX_ACK_endTime 16 +#define _TYPE_CMD_IEEE_RX_ACK_endTime ratmr_t +#define _SIZEOF_CMD_IEEE_RX_ACK 20 + +#define _SIZEOF_CMD_IEEE_ABORT_BG 14 + +#define _POSITION_CMD_IEEE_MOD_CCA_newCcaOpt 2 +#define _TYPE_CMD_IEEE_MOD_CCA_newCcaOpt uint8_t +#define _BITPOS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaEnEnergy 0 +#define _NBITS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaEnEnergy 1 +#define _BITPOS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaEnCorr 1 +#define _NBITS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaEnCorr 1 +#define _BITPOS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaEnSync 2 +#define _NBITS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaEnSync 1 +#define _BITPOS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaCorrOp 3 +#define _NBITS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaCorrOp 1 +#define _BITPOS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaSyncOp 4 +#define _NBITS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaSyncOp 1 +#define _BITPOS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaCorrThr 5 +#define _NBITS_CMD_IEEE_MOD_CCA_newCcaOpt_ccaCorrThr 2 +#define _POSITION_CMD_IEEE_MOD_CCA_newCcaRssiThr 3 +#define _TYPE_CMD_IEEE_MOD_CCA_newCcaRssiThr int8_t +#define _SIZEOF_CMD_IEEE_MOD_CCA 4 + +#define _POSITION_CMD_IEEE_MOD_FILT_newFrameFiltOpt 2 +#define _TYPE_CMD_IEEE_MOD_FILT_newFrameFiltOpt uint16_t +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_frameFiltEn 0 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_frameFiltEn 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_frameFiltStop 1 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_frameFiltStop 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_autoAckEn 2 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_autoAckEn 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_slottedAckEn 3 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_slottedAckEn 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_autoPendEn 4 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_autoPendEn 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_defaultPend 5 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_defaultPend 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_bPendDataReqOnly 6 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_bPendDataReqOnly 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_bPanCoord 7 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_bPanCoord 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_maxFrameVersion 8 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_maxFrameVersion 2 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_fcfReservedMask 10 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_fcfReservedMask 3 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_modifyFtFilter 13 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_modifyFtFilter 2 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_bStrictLenFilter 15 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameFiltOpt_bStrictLenFilter 1 +#define _POSITION_CMD_IEEE_MOD_FILT_newFrameTypes 4 +#define _TYPE_CMD_IEEE_MOD_FILT_newFrameTypes uint8_t +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt0Beacon 0 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt0Beacon 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt1Data 1 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt1Data 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt2Ack 2 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt2Ack 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt3MacCmd 3 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt3MacCmd 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt4Reserved 4 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt4Reserved 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt5Reserved 5 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt5Reserved 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt6Reserved 6 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt6Reserved 1 +#define _BITPOS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt7Reserved 7 +#define _NBITS_CMD_IEEE_MOD_FILT_newFrameTypes_bAcceptFt7Reserved 1 +#define _SIZEOF_CMD_IEEE_MOD_FILT 5 + +#define _POSITION_CMD_IEEE_MOD_SRC_MATCH_options 2 +#define _TYPE_CMD_IEEE_MOD_SRC_MATCH_options uint8_t +#define _BITPOS_CMD_IEEE_MOD_SRC_MATCH_options_bEnable 0 +#define _NBITS_CMD_IEEE_MOD_SRC_MATCH_options_bEnable 1 +#define _BITPOS_CMD_IEEE_MOD_SRC_MATCH_options_srcPend 1 +#define _NBITS_CMD_IEEE_MOD_SRC_MATCH_options_srcPend 1 +#define _BITPOS_CMD_IEEE_MOD_SRC_MATCH_options_entryType 2 +#define _NBITS_CMD_IEEE_MOD_SRC_MATCH_options_entryType 1 +#define _POSITION_CMD_IEEE_MOD_SRC_MATCH_entryNo 3 +#define _TYPE_CMD_IEEE_MOD_SRC_MATCH_entryNo uint8_t +#define _SIZEOF_CMD_IEEE_MOD_SRC_MATCH 4 + +#define _SIZEOF_CMD_IEEE_ABORT_FG 2 + +#define _SIZEOF_CMD_IEEE_STOP_FG 2 + +#define _POSITION_CMD_IEEE_CCA_REQ_currentRssi 2 +#define _TYPE_CMD_IEEE_CCA_REQ_currentRssi int8_t +#define _POSITION_CMD_IEEE_CCA_REQ_maxRssi 3 +#define _TYPE_CMD_IEEE_CCA_REQ_maxRssi int8_t +#define _POSITION_CMD_IEEE_CCA_REQ_ccaInfo 4 +#define _TYPE_CMD_IEEE_CCA_REQ_ccaInfo uint8_t +#define _BITPOS_CMD_IEEE_CCA_REQ_ccaInfo_ccaState 0 +#define _NBITS_CMD_IEEE_CCA_REQ_ccaInfo_ccaState 2 +#define _BITPOS_CMD_IEEE_CCA_REQ_ccaInfo_ccaEnergy 2 +#define _NBITS_CMD_IEEE_CCA_REQ_ccaInfo_ccaEnergy 2 +#define _BITPOS_CMD_IEEE_CCA_REQ_ccaInfo_ccaCorr 4 +#define _NBITS_CMD_IEEE_CCA_REQ_ccaInfo_ccaCorr 2 +#define _BITPOS_CMD_IEEE_CCA_REQ_ccaInfo_ccaSync 6 +#define _NBITS_CMD_IEEE_CCA_REQ_ccaInfo_ccaSync 1 +#define _SIZEOF_CMD_IEEE_CCA_REQ 5 + +#define _POSITION_ieeeRxOutput_nTxAck 0 +#define _TYPE_ieeeRxOutput_nTxAck uint8_t +#define _POSITION_ieeeRxOutput_nRxBeacon 1 +#define _TYPE_ieeeRxOutput_nRxBeacon uint8_t +#define _POSITION_ieeeRxOutput_nRxData 2 +#define _TYPE_ieeeRxOutput_nRxData uint8_t +#define _POSITION_ieeeRxOutput_nRxAck 3 +#define _TYPE_ieeeRxOutput_nRxAck uint8_t +#define _POSITION_ieeeRxOutput_nRxMacCmd 4 +#define _TYPE_ieeeRxOutput_nRxMacCmd uint8_t +#define _POSITION_ieeeRxOutput_nRxReserved 5 +#define _TYPE_ieeeRxOutput_nRxReserved uint8_t +#define _POSITION_ieeeRxOutput_nRxNok 6 +#define _TYPE_ieeeRxOutput_nRxNok uint8_t +#define _POSITION_ieeeRxOutput_nRxIgnored 7 +#define _TYPE_ieeeRxOutput_nRxIgnored uint8_t +#define _POSITION_ieeeRxOutput_nRxBufFull 8 +#define _TYPE_ieeeRxOutput_nRxBufFull uint8_t +#define _POSITION_ieeeRxOutput_lastRssi 9 +#define _TYPE_ieeeRxOutput_lastRssi int8_t +#define _POSITION_ieeeRxOutput_maxRssi 10 +#define _TYPE_ieeeRxOutput_maxRssi int8_t +#define _POSITION_ieeeRxOutput_beaconTimeStamp 12 +#define _TYPE_ieeeRxOutput_beaconTimeStamp ratmr_t +#define _SIZEOF_ieeeRxOutput 16 + +#define _POSITION_shortAddrEntry_shortAddr 0 +#define _TYPE_shortAddrEntry_shortAddr uint16_t +#define _POSITION_shortAddrEntry_panId 2 +#define _TYPE_shortAddrEntry_panId uint16_t +#define _SIZEOF_shortAddrEntry 4 + +#define _POSITION_ieeeRxCorrCrc_status 0 +#define _TYPE_ieeeRxCorrCrc_status uint8_t +#define _BITPOS_ieeeRxCorrCrc_status_corr 0 +#define _NBITS_ieeeRxCorrCrc_status_corr 6 +#define _BITPOS_ieeeRxCorrCrc_status_bIgnore 6 +#define _NBITS_ieeeRxCorrCrc_status_bIgnore 1 +#define _BITPOS_ieeeRxCorrCrc_status_bCrcErr 7 +#define _NBITS_ieeeRxCorrCrc_status_bCrcErr 1 +#define _SIZEOF_ieeeRxCorrCrc 1 + +#endif diff --git a/cpu/cc26xx/dev/rfc-api/ieee_mailbox.h b/cpu/cc26xx/dev/rfc-api/ieee_mailbox.h new file mode 100644 index 000000000..772c4c219 --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/ieee_mailbox.h @@ -0,0 +1,107 @@ +/****************************************************************************** +* Filename: ieee_mailbox.h +* Revised: $ $ +* Revision: $ $ +* +* Description: Definitions for IEEE 802.15.4 interface +* +* Copyright (C) 2015 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 _IEEE_MAILBOX_H +#define _IEEE_MAILBOX_H + +#include "mailbox.h" + +/// \name CPE interrupt definitions for IEEE 802.15.4 +/// Interrupt masks for the CPE interrupt in RDBELL. These are new names for interrupts in mailbox.h, +/// used for compartibility with previous versions with separate interrupt numbers. +///@{ +#define IRQN_IEEE_BG_COMMAND_SUSPENDED IRQN_BG_COMMAND_SUSPENDED +#define IRQN_IEEE_TX_FRAME IRQN_TX_DONE +#define IRQN_IEEE_TX_ACK IRQN_TX_ACK + +#define IRQN_IEEE_RX_FRAME IRQN_RX_OK +#define IRQN_IEEE_RX_NOK IRQN_RX_NOK +#define IRQN_IEEE_RX_IGNORED IRQN_RX_IGNORED +#define IRQN_IEEE_RX_BUF_FULL IRQN_RX_BUF_FULL +#define IRQN_IEEE_RX_ENTRY_DONE IRQN_RX_ENTRY_DONE + +#define IRQ_IEEE_BG_COMMAND_SUSPENDED (1U << IRQN_IEEE_BG_COMMAND_SUSPENDED) +#define IRQ_IEEE_TX_FRAME (1U << IRQN_IEEE_TX_FRAME) +#define IRQ_IEEE_TX_ACK (1U << IRQN_IEEE_TX_ACK) +#define IRQ_IEEE_RX_FRAME (1U << IRQN_IEEE_RX_FRAME) +#define IRQ_IEEE_RX_NOK (1U << IRQN_IEEE_RX_NOK) +#define IRQ_IEEE_RX_IGNORED (1U << IRQN_IEEE_RX_IGNORED) +#define IRQ_IEEE_RX_BUF_FULL (1U << IRQN_IEEE_RX_BUF_FULL) +#define IRQ_IEEE_RX_ENTRY_DONE (1U << IRQN_IEEE_RX_ENTRY_DONE) +///@} + + + +/// \name Radio operation status +/// Radio operation status format: +/// Bits 15:12: Protocol +/// 0010: IEEE 802.15.4 +/// Bits 11:10: Type +/// 00: Not finished +/// 01: Done successfully +/// 10: Done with error +/// Bits 9:0: Identifier + +/// \name Operation not finished +///@{ +#define IEEE_SUSPENDED 0x2001 ///< Operation suspended +///@} +/// \name Operation finished normally +///@{ +#define IEEE_DONE_OK 0x2400 ///< Operation ended normally +#define IEEE_DONE_BUSY 0x2401 ///< CSMA-CA operation ended with failure +#define IEEE_DONE_STOPPED 0x2402 ///< Operation stopped after stop command +#define IEEE_DONE_ACK 0x2403 ///< ACK packet received with pending data bit cleared +#define IEEE_DONE_ACKPEND 0x2404 ///< ACK packet received with pending data bit set +#define IEEE_DONE_TIMEOUT 0x2405 ///< Operation ended due to timeout +#define IEEE_DONE_BGEND 0x2406 ///< FG operation ended because necessary background level + ///< operation ended +#define IEEE_DONE_ABORT 0x2407 ///< Operation aborted by command +///@} +/// \name Operation finished with error +///@{ +#define IEEE_ERROR_PAR 0x2800 ///< Illegal parameter +#define IEEE_ERROR_NO_SETUP 0x2801 ///< Operation using Rx or Tx attemted when not in 15.4 mode +#define IEEE_ERROR_NO_FS 0x2802 ///< Operation using Rx or Tx attemted without frequency synth configured +#define IEEE_ERROR_SYNTH_PROG 0x2803 ///< Synthesizer programming failed to complete on time +#define IEEE_ERROR_RXOVF 0x2804 ///< Receiver overflowed during operation +#define IEEE_ERROR_TXUNF 0x2805 ///< Transmitter underflowed during operation +///@} +///@} + +#endif diff --git a/cpu/cc26xx/dev/rfc-api/mailbox.h b/cpu/cc26xx/dev/rfc-api/mailbox.h new file mode 100644 index 000000000..b11d72f08 --- /dev/null +++ b/cpu/cc26xx/dev/rfc-api/mailbox.h @@ -0,0 +1,580 @@ +/****************************************************************************** +* Filename: mailbox.h +* Revised: $ $ +* Revision: $ $ +* +* Description: Definitions for interface between system and radio CPU +* +* Copyright (C) 2015 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 _MAILBOX_H +#define _MAILBOX_H + +#include +#include + +/// Type definition for RAT +typedef uint32_t ratmr_t; + + + +/// Type definition for a data queue +typedef struct { + uint8_t *pCurrEntry; ///< Pointer to the data queue entry to be used, NULL for an empty queue + uint8_t *pLastEntry; ///< Pointer to the last entry in the queue, NULL for a circular queue +} dataQueue_t; + + + +/// \name CPE interrupt definitions +/// Interrupt masks for the CPE interrupt in RDBELL. +///@{ +#define IRQN_COMMAND_DONE 0 ///< Radio operation command finished +#define IRQN_LAST_COMMAND_DONE 1 ///< Last radio operation command in a chain finished +#define IRQN_FG_COMMAND_DONE 2 ///< FG level Radio operation command finished +#define IRQN_LAST_FG_COMMAND_DONE 3 ///< Last FG level radio operation command in a chain finished +#define IRQN_TX_DONE 4 ///< Packet transmitted +#define IRQN_TX_ACK 5 ///< ACK packet transmitted +#define IRQN_TX_CTRL 6 ///< Control packet transmitted +#define IRQN_TX_CTRL_ACK 7 ///< Acknowledgement received on a transmitted control packet +#define IRQN_TX_CTRL_ACK_ACK 8 ///< Acknowledgement received on a transmitted control packet, and acknowledgement transmitted for that packet +#define IRQN_TX_RETRANS 9 ///< Packet retransmitted +#define IRQN_TX_ENTRY_DONE 10 ///< Tx queue data entry state changed to Finished +#define IRQN_TX_BUFFER_CHANGED 11 ///< A buffer change is complete +#define IRQN_BG_COMMAND_SUSPENDED 12 ///< A background level radio operation command has been suspended +#define IRQN_RX_OK 16 ///< Packet received with CRC OK, payload, and not to be ignored +#define IRQN_RX_NOK 17 ///< Packet received with CRC error +#define IRQN_RX_IGNORED 18 ///< Packet received with CRC OK, but to be ignored +#define IRQN_RX_EMPTY 19 ///< Packet received with CRC OK, not to be ignored, no payload +#define IRQN_RX_CTRL 20 ///< Control packet received with CRC OK, not to be ignored +#define IRQN_RX_CTRL_ACK 21 ///< Control packet received with CRC OK, not to be ignored, then ACK sent +#define IRQN_RX_BUF_FULL 22 ///< Packet received that did not fit in the Rx queue +#define IRQN_RX_ENTRY_DONE 23 ///< Rx queue data entry changing state to Finished +#define IRQN_RX_DATA_WRITTEN 24 ///< Data written to partial read Rx buffer +#define IRQN_RX_N_DATA_WRITTEN 25 ///< Specified number of bytes written to partial read Rx buffer +#define IRQN_RX_ABORTED 26 ///< Packet reception stopped before packet was done +#define IRQN_SYNTH_NO_LOCK 28 ///< The synth has gone out of lock after calibration +#define IRQN_MODULES_UNLOCKED 29 ///< As part of the boot process, the CM0 has opened access to RF core modules and memories +#define IRQN_BOOT_DONE 30 ///< The RF core CPU boot is finished + +#define IRQN_INTERNAL_ERROR 31 ///< Internal error observed + +#define IRQ_COMMAND_DONE (1U << IRQN_COMMAND_DONE) +#define IRQ_LAST_COMMAND_DONE (1U << IRQN_LAST_COMMAND_DONE) +#define IRQ_FG_COMMAND_DONE (1U << IRQN_FG_COMMAND_DONE) +#define IRQ_LAST_FG_COMMAND_DONE (1U << IRQN_LAST_FG_COMMAND_DONE) + +#define IRQ_TX_DONE (1U << IRQN_TX_DONE) +#define IRQ_TX_ACK (1U << IRQN_TX_ACK) +#define IRQ_TX_CTRL (1U << IRQN_TX_CTRL) +#define IRQ_TX_CTRL_ACK (1U << IRQN_TX_CTRL_ACK) +#define IRQ_TX_CTRL_ACK_ACK (1U << IRQN_TX_CTRL_ACK_ACK) +#define IRQ_TX_RETRANS (1U << IRQN_TX_RETRANS) + +#define IRQ_TX_ENTRY_DONE (1U << IRQN_TX_ENTRY_DONE) +#define IRQ_TX_BUFFER_CHANGED (1U << IRQN_TX_BUFFER_CHANGED) + +#define IRQ_BG_COMMAND_SUSPENDED (1U << IRQN_BG_COMMAND_SUSPENDED) + +#define IRQ_RX_OK (1U << IRQN_RX_OK) +#define IRQ_RX_NOK (1U << IRQN_RX_NOK) +#define IRQ_RX_IGNORED (1U << IRQN_RX_IGNORED) +#define IRQ_RX_EMPTY (1U << IRQN_RX_EMPTY) +#define IRQ_RX_CTRL (1U << IRQN_RX_CTRL) +#define IRQ_RX_CTRL_ACK (1U << IRQN_RX_CTRL_ACK) +#define IRQ_RX_BUF_FULL (1U << IRQN_RX_BUF_FULL) +#define IRQ_RX_ENTRY_DONE (1U << IRQN_RX_ENTRY_DONE) +#define IRQ_RX_DATA_WRITTEN (1U << IRQN_RX_DATA_WRITTEN) +#define IRQ_RX_N_DATA_WRITTEN (1U << IRQN_RX_N_DATA_WRITTEN) +#define IRQ_RX_ABORTED (1U << IRQN_RX_ABORTED) + +#define IRQ_SYNTH_NO_LOCK (1U << IRQN_SYNTH_NO_LOCK) +#define IRQ_MODULES_UNLOCKED (1U << IRQN_MODULES_UNLOCKED) +#define IRQ_BOOT_DONE (1U << IRQN_BOOT_DONE) +#define IRQ_INTERNAL_ERROR (1U << IRQN_INTERNAL_ERROR) +///@} + + + +/// \name CMDSTA values +/// Values returned in result byte of CMDSTA +///@{ +#define CMDSTA_Pending 0x00 ///< The command has not yet been parsed +#define CMDSTA_Done 0x01 ///< Command successfully parsed + +#define CMDSTA_IllegalPointer 0x81 ///< The pointer signaled in CMDR is not valid +#define CMDSTA_UnknownCommand 0x82 ///< The command number in the command structure is unknown +#define CMDSTA_UnknownDirCommand 0x83 ///< The command number for a direct command is unknown, or the + ///< command is not a direct command +#define CMDSTA_ContextError 0x85 ///< An immediate or direct command was issued in a context + ///< where it is not supported +#define CMDSTA_SchedulingError 0x86 ///< A radio operation command was attempted to be scheduled + ///< while another operation was already running in the RF core +#define CMDSTA_ParError 0x87 ///< There were errors in the command parameters that are parsed + ///< on submission. +#define CMDSTA_QueueError 0x88 ///< An operation on a data entry queue was attempted that was + ///< not supported by the queue in its current state +#define CMDSTA_QueueBusy 0x89 ///< An operation on a data entry was attempted while that entry + ///< was busy +///@} + +/// \name Macros for use with command definition files +/// The script create_command.pl generates header files from command and structure definitions in the +/// *_def.txt files. These are the macros to access the definitions +///@{ + +/// Get a field from a structure +// +/// Gets a field from a structure defined in a _def.txt file. This may be used both in assignments and +/// references (e.g. GET_FIELD(pCmd1, CMD_TEST, testParam) = GET_FIELD(pCmd2, CMD_DUMMY, dummyParam);) +/// \param[in] ptr +/// Pointer to the structure +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the accessed field as defined in the _def.txt file +/// +#define GET_FIELD(ptr, cmd, field) \ +(*((_TYPE_##cmd##_##field *) (((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field)))) + +/// Get a field from a structure, reading as volatile +// +/// Gets a field from a structure defined in a _def.txt file, reading it as a volatile parameter, which +/// takes into account that it may be changed by the other side. This may be used both in assignments and +/// references (e.g. GET_FIELD(pCmd1, CMD_TEST, testParam) = GET_FIELD(pCmd2, CMD_DUMMY, dummyParam);) +/// \param[in] ptr +/// Pointer to the structure +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the accessed field as defined in the _def.txt file +/// +#define GET_FIELD_V(ptr, cmd, field) \ +(*((volatile _TYPE_##cmd##_##field *) (((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field)))) + +/// Get the pointer to a field from a structure +// +/// Gets the pointer to a field from a structure defined in a _def.txt file. +/// \param[in] ptr +/// Pointer to the structure +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the accessed field as defined in the _def.txt file +/// +#define GET_FIELD_PTR(ptr, cmd, field) \ +((_TYPE_##cmd##_##field *) (((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field))) + +/// Get the volatile pointer to a field from a structure +// +/// Gets the volatile pointer to a field from a structure defined in a _def.txt file. +/// \param[in] ptr +/// Pointer to the structure +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the accessed field as defined in the _def.txt file +/// +#define GET_FIELD_VPTR(ptr, cmd, field) \ +((volatile _TYPE_##cmd##_##field *) (((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field))) + +/// Get bits from a bit field +// +/// Returns bits from a bit field defined in a _def.txt file. +/// \param[in] value +/// The value of the entire field that contains the bit field +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the field that contains the bit field definition as defined in the _def.txt file +/// \param[in] bitfield +/// Name of the bitfield as defined in the _def.txt file +/// +#define GET_BITS(value, cmd, field, bitfield) \ +(((value) >> (_BITPOS_##cmd##_##field##_##bitfield)) & \ + ((1U << (_NBITS_##cmd##_##field##_##bitfield)) - 1)) + +/// Get bits from a bit field as a signed value +// +/// Returns sign extended bits from a bit field defined in a _def.txt file. +/// \param[in] value +/// The value of the entire field that contains the bit field +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the field that contains the bit field definition as defined in the _def.txt file +/// \param[in] bitfield +/// Name of the bitfield as defined in the _def.txt file +/// +#define GET_BITS_S(value, cmd, field, bitfield) \ +(((int)(value) << (32 - ((_BITPOS_##cmd##_##field##_##bitfield) + (_NBITS_##cmd##_##field##_##bitfield)))) >> \ + (32 - (_NBITS_##cmd##_##field##_##bitfield))) + +/// Set bits in a bit field +// +/// Modifies a bit field defined in a _def.txt file. +/// \param[in,out] value +/// The value of the entire field that contains the bit field +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the field that contains the bit field definition as defined in the _def.txt file +/// \param[in] bitfield +/// Name of the bitfield as defined in the _def.txt file +/// \param[in] bvalue +/// The value to set in the bitfield +/// +#define SET_BITS(value, cmd, field, bitfield, bvalue) \ +(((value) = ((value) & \ + (~(((1U << (_NBITS_##cmd##_##field##_##bitfield)) - 1) << (_BITPOS_##cmd##_##field##_##bitfield))) | \ + ((bvalue) << (_BITPOS_##cmd##_##field##_##bitfield))))) + + +/// Get bits from a bit field in a structure +// +/// Returns bits from a bit field in a structure defined in a _def.txt file. +/// \param[in] ptr +/// Pointer to the structure +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the field that contains the bit field definition as defined in the _def.txt file +/// \param[in] bitfield +/// Name of the bitfield as defined in the _def.txt file +/// +#define GET_BITFIELD(ptr, cmd, field, bitfield) \ +((*((_TYPE_##cmd##_##field *) ((((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field)))) >> \ + ((_BITPOS_##cmd##_##field##_##bitfield))) & \ + ((1U << (_NBITS_##cmd##_##field##_##bitfield)) - 1)) + +/// Get bits from a bit field in a structure, reading as volatile +// +/// Returns bits from a bit field in a structure defined in a _def.txt file, reading it as a +/// volatile parameter. +/// \param[in] ptr +/// Pointer to the structure +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the field that contains the bit field definition as defined in the _def.txt file +/// \param[in] bitfield +/// Name of the bitfield as defined in the _def.txt file +/// +#define GET_BITFIELD_V(ptr, cmd, field, bitfield) \ +((*((volatile _TYPE_##cmd##_##field *) ((((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field)))) >> \ + ((_BITPOS_##cmd##_##field##_##bitfield))) & \ + ((1U << (_NBITS_##cmd##_##field##_##bitfield)) - 1)) + + +/// Set bits in a bit field in a structure +// +/// Modifies a bit field in a field in a structure defined in a _def.txt file. +/// \param[in] ptr +/// Pointer to the structure +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the field that contains the bit field definition as defined in the _def.txt file +/// \param[in] bitfield +/// Name of the bitfield as defined in the _def.txt file +/// \param[in] value +/// The value to set in the bitfield +/// +#define SET_BITFIELD(ptr, cmd, field, bitfield, value) \ +((*((_TYPE_##cmd##_##field *) (((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field)))) = \ + ((*((_TYPE_##cmd##_##field *) (((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field)))) & \ + (~(((1U << (_NBITS_##cmd##_##field##_##bitfield)) - 1) << (_BITPOS_##cmd##_##field##_##bitfield))) | \ + (((uint32_t)(value)) << (_BITPOS_##cmd##_##field##_##bitfield)))) + +/// Set bits in a bit field in a structure, reading and writing as volatile +// +/// Modifies a bit field in a field in a structure defined in a _def.txt file, accessing it as a volatile +/// parameter. +/// \param[in] ptr +/// Pointer to the structure +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the field that contains the bit field definition as defined in the _def.txt file +/// \param[in] bitfield +/// Name of the bitfield as defined in the _def.txt file +/// \param[in] value +/// The value to set in the bitfield +/// +#define SET_BITFIELD_V(ptr, cmd, field, bitfield, value) \ +((*((volatile _TYPE_##cmd##_##field *) (((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field)))) = \ + ((*((volatile _TYPE_##cmd##_##field *) (((uint8_t *)(ptr)) + (_POSITION_##cmd##_##field)))) & \ + (~(((1U << (_NBITS_##cmd##_##field##_##bitfield)) - 1) << (_BITPOS_##cmd##_##field##_##bitfield))) | \ + (((uint32_t)(value)) << (_BITPOS_##cmd##_##field##_##bitfield)))) + +/// Get the value of specific bifield in a field with the remaining bits set to 0 +// +/// Returns a bitfield so that the value of the full field can be obtained by bitwise +/// OR between these +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] field +/// Name of the field that contains the bit field definition as defined in the _def.txt file +/// \param[in] bitfield +/// Name of the bitfield as defined in the _def.txt file +/// \param[in] value +/// The value to use in the bitfield +/// +#define BITVALUE(cmd, field, bitfield, value) \ +((((uint32_t)(value)) & ((1U << (_NBITS_##cmd##_##field##_##bitfield)) - 1)) << \ + (_BITPOS_##cmd##_##field##_##bitfield)) + +/// Get the size of a structure +// +/// Gets the size of a structure defined in a _def.txt file. +/// +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// +#define SIZEOF_STRUCT(cmd) \ +(_SIZEOF_##cmd) + +/// Get the size of a radio operation command structure +// +/// Gets the size of a radio operation command structure defined in a _def.txt file. The difference from +/// SIZEOF_STRUCT is for legacy reasons only. +/// +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// +#define SIZEOF_RADIO_OP(cmd) \ +(_SIZEOF_##cmd) + +/// Initializes a structure to an initialization set +// +/// Sets the value of a structure to its given initialization values +/// \param[in] ptr +/// Pointer to the structure, must be word aligned +/// \param[in] cmd +/// Name of the command or structure as defined in the _def.txt file +/// \param[in] set +/// Index of the set of initializations to use +/// +#define INIT_STRUCT(ptr, cmd, set) \ +(memcpy(((uint32_t *)(ptr)) + (_START_INIT_WIDX_##cmd), (__init_##cmd[(set)]), \ + (_N_INIT_WORDS_##cmd) * sizeof(uint32_t))) + +///@} + + +/// \name Macros for sending direct commands +///@{ +/// Direct command with no parameter +#define CMDR_DIR_CMD(cmdId) (((cmdId) << 16) | 1) + +/// Direct command with 1-byte parameter +#define CMDR_DIR_CMD_1BYTE(cmdId, par) (((cmdId) << 16) | ((par) << 8) | 1) + +/// Direct command with 2-byte parameter +#define CMDR_DIR_CMD_2BYTE(cmdId, par) (((cmdId) << 16) | ((par) & 0xFFFC) | 1) + +///@} + + + +/// \name Definitions for trigger types +///@{ +#define TRIG_NOW 0 ///< Triggers immediately +#define TRIG_NEVER 1 ///< Never trigs +#define TRIG_ABSTIME 2 ///< Trigs at an absolute time +#define TRIG_REL_SUBMIT 3 ///< Trigs at a time relative to the command was submitted +#define TRIG_REL_START 4 ///< Trigs at a time relative to the command started +#define TRIG_REL_PREVSTART 5 ///< Trigs at a time relative to the previous command in the chain started +#define TRIG_REL_FIRSTSTART 6 ///< Trigs at a time relative to the first command in the chain started +#define TRIG_REL_PREVEND 7 ///< Trigs at a time relative to the previous command in the chain ended +#define TRIG_REL_EVT1 8 ///< Trigs at a time relative to the context defined "Event 1" +#define TRIG_REL_EVT2 9 ///< Trigs at a time relative to the context defined "Event 2" +#define TRIG_EXTERNAL 10 ///< Trigs at an external event to the radio timer +#define TRIG_PAST_BM 0x80 ///< Bitmask for setting pastTrig bit in order to trig immediately if + ///< trigger happened in the past +///@} + + +/// \name Definitions for conditional execution +///@{ +#define COND_ALWAYS 0 ///< Always run next command (except in case of Abort) +#define COND_NEVER 1 ///< Never run next command +#define COND_STOP_ON_FALSE 2 ///< Run next command if this command returned True, stop if it returned + ///< False +#define COND_STOP_ON_TRUE 3 ///< Stop if this command returned True, run next command if it returned + ///< False +#define COND_SKIP_ON_FALSE 4 ///< Run next command if this command returned True, skip a number of + ///< commands if it returned False +#define COND_SKIP_ON_TRUE 5 ///< Skip a number of commands if this command returned True, run next + ///< command if it returned False +///@} + + + +/// \name Radio operation status +/// Radio operation status format: +///@{ +/// \name Operation not finished +///@{ +#define IDLE 0x0000 ///< Operation not started +#define PENDING 0x0001 ///< Start of command is pending +#define ACTIVE 0x0002 ///< Running +#define SKIPPED 0x0003 ///< Operation skipped due to condition in another command +///@} +/// \name Operation finished normally +///@{ +#define DONE_OK 0x0400 ///< Operation ended normally +#define DONE_COUNTDOWN 0x0401 ///< Counter reached zero +#define DONE_RXERR 0x0402 ///< Operation ended with CRC error +#define DONE_TIMEOUT 0x0403 ///< Operation ended with timeout +#define DONE_STOPPED 0x0404 ///< Operation stopped after CMD_STOP command +#define DONE_ABORT 0x0405 ///< Operation aborted by CMD_ABORT command +#define DONE_FAILED 0x0406 ///< Scheduled immediate command failed +///@} +/// \name Operation finished with error +///@{ +#define ERROR_PAST_START 0x0800 ///< The start trigger occurred in the past +#define ERROR_START_TRIG 0x0801 ///< Illegal start trigger parameter +#define ERROR_CONDITION 0x0802 ///< Illegal condition for next operation +#define ERROR_PAR 0x0803 ///< Error in a command specific parameter +#define ERROR_POINTER 0x0804 ///< Invalid pointer to next operation +#define ERROR_CMDID 0x0805 ///< Next operation has a command ID that is undefined or not a radio + ///< operation command +#define ERROR_WRONG_BG 0x0806 ///< FG level command not compatible with running BG level command +#define ERROR_NO_SETUP 0x0807 ///< Operation using Rx or Tx attemted without CMD_RADIO_SETUP +#define ERROR_NO_FS 0x0808 ///< Operation using Rx or Tx attemted without frequency synth configured +#define ERROR_SYNTH_PROG 0x0809 ///< Synthesizer calibration failed +#define ERROR_TXUNF 0x080A ///< Tx underflow observed +#define ERROR_RXOVF 0x080B ///< Rx overflow observed +#define ERROR_NO_RX 0x080C ///< Attempted to access data from Rx when no such data was yet received +#define ERROR_PENDING 0x080D ///< Command submitted in the future with another command at different level pending +///@} +///@} + + +/// \name Data entry types +///@{ +#define DATA_ENTRY_TYPE_GEN 0 ///< General type: Tx entry or single element Rx entry +#define DATA_ENTRY_TYPE_MULTI 1 ///< Multi-element Rx entry type +#define DATA_ENTRY_TYPE_PTR 2 ///< Pointer entry type +#define DATA_ENTRY_TYPE_PARTIAL 3 ///< Partial read entry type +///@ + + +/// \name Data entry statuses +///@{ +#define DATA_ENTRY_PENDING 0 ///< Entry not yet used +#define DATA_ENTRY_ACTIVE 1 ///< Entry in use by radio CPU +#define DATA_ENTRY_BUSY 2 ///< Entry being updated +#define DATA_ENTRY_FINISHED 3 ///< Radio CPU is finished accessing the entry +#define DATA_ENTRY_UNFINISHED 4 ///< Radio CPU is finished accessing the entry, but packet could not be finished +///@} + +/// Difference between length and size of rxData field in multi-element Rx entry +#define DATA_ENTRY_MULTI_LEN_OFFSET (_POSITION_dataEntry_rxData - _POSITION_dataEntry_data) + + +/// \name Macros for RF register override +///@{ +/// Macro for ADI half-size value-mask combination +#define ADI_VAL_MASK(addr, mask, value) \ +(((addr) & 1) ? (((mask) & 0x0F) | (((value) & 0x0F) << 4)) : \ + ((((mask) & 0x0F) << 4) | ((value) & 0x0F))) +/// 32-bit write of 16-bit value +#define HW_REG_OVERRIDE(addr, val) ((((uintptr_t) (addr)) & 0xFFFC) | ((uint32_t)(val) << 16)) +/// ADI register, full-size write +#define ADI_REG_OVERRIDE(adiNo, addr, val) (2 | ((uint32_t)(val) << 16) | \ +(((addr) & 0x3F) << 24) | (((adiNo) ? 1U : 0) << 31)) +/// 2 ADI registers, full-size write +#define ADI_2REG_OVERRIDE(adiNo, addr, val, addr2, val2) \ +(2 | ((uint32_t)(val2) << 2) | (((addr2) & 0x3F) << 10) | ((uint32_t)(val) << 16) | \ +(((addr) & 0x3F) << 24) | (((adiNo) ? 1U : 0) << 31)) +/// ADI register, half-size read-modify-write +#define ADI_HALFREG_OVERRIDE(adiNo, addr, mask, val) (2 | (ADI_VAL_MASK(addr, mask, val) << 16) | \ +(((addr) & 0x3F) << 24) | (1U << 30) | (((adiNo) ? 1U : 0) << 31)) +/// 2 ADI registers, half-size read-modify-write +#define ADI_2HALFREG_OVERRIDE(adiNo, addr, mask, val, addr2, mask2, val2) \ +(2 | (ADI_VAL_MASK(addr2, mask2, val2) << 2) | (((addr2) & 0x3F) << 10) | \ +(ADI_VAL_MASK(addr, mask, val) << 16) | (((addr) & 0x3F) << 24) | (1U << 30) | (((adiNo) ? 1U : 0) << 31)) + +/// 16-bit SW register as defined in radio_par_def.txt +#define SW_REG_OVERRIDE(cmd, field, val) (3 | ((_POSITION_##cmd##_##field) << 4) | ((uint32_t)(val) << 16)) +/// SW register as defined in radio_par_def.txt with added index (for use with registers > 16 bits). +#define SW_REG_IND_OVERRIDE(cmd, field, offset, val) (3 | \ +(((_POSITION_##cmd##_##field) + ((offset) << 1)) << 4) | ((uint32_t)(val) << 16)) +/// 8-bit SW register as defined in radio_par_def.txt +#define SW_REG_BYTE_OVERRIDE(cmd, field, val) (0x8003 | ((_POSITION_##cmd##_##field) << 4) | \ +((uint32_t)(val) << 16)) +/// Two 8-bit SW registers as defined in radio_par_def.txt; the one given by field and the next byte. +#define SW_REG_2BYTE_OVERRIDE(cmd, field, val0, val1) (3 | (((_POSITION_##cmd##_##field) & 0xFFFE) << 4) | \ + (((uint32_t)(val0) << 16) & 0x00FF0000) | ((uint32_t)(val1) << 24)) +#define HW16_ARRAY_OVERRIDE(addr, length) (1 | (((uintptr_t) (addr)) & 0xFFFC) | ((uint32_t)(length) << 16)) +#define HW32_ARRAY_OVERRIDE(addr, length) (1 | (((uintptr_t) (addr)) & 0xFFFC) | \ +((uint32_t)(length) << 16) | (1U << 30)) +#define ADI_ARRAY_OVERRIDE(adiNo, addr, bHalfSize, length) (1 | ((((addr) & 0x3F) << 2)) | \ +((!!(bHalfSize)) << 8) | ((!!(adiNo)) << 9) | ((uint32_t)(length) << 16) | (2U << 30)) +#define SW_ARRAY_OVERRIDE(cmd, firstfield, length) (1 | (((_POSITION_##cmd##_##firstfield)) << 2) | \ +((uint32_t)(length) << 16) | (3U << 30)) +#define MCE_RFE_OVERRIDE(bMceRam, mceRomBank, mceMode, bRfeRam, rfeRomBank, rfeMode) \ + (7 | ((!!(bMceRam)) << 8) | (((mceRomBank) & 0x07) << 9) | ((!!(bRfeRam)) << 12) | (((rfeRomBank) & 0x07) << 13) | \ + (((mceMode) & 0x00FF) << 16) | (((rfeMode) & 0x00FF) << 24)) +#define BAW_OVERRIDE(freqOffset) (0x000B | ((freqOffset) << 16)) +#define NEW_OVERRIDE_SEGMENT(address) (((((uintptr_t)(address)) & 0x03FFFFFC) << 6) | 0x000F | \ + (((((uintptr_t)(address) >> 24) == 0x20) ? 0x01 : \ + (((uintptr_t)(address) >> 24) == 0x21) ? 0x02 : \ + (((uintptr_t)(address) >> 24) == 0xA0) ? 0x03 : \ + (((uintptr_t)(address) >> 24) == 0x00) ? 0x04 : \ + (((uintptr_t)(address) >> 24) == 0x10) ? 0x05 : \ + (((uintptr_t)(address) >> 24) == 0x11) ? 0x06 : \ + (((uintptr_t)(address) >> 24) == 0x40) ? 0x07 : \ + (((uintptr_t)(address) >> 24) == 0x50) ? 0x08 : \ + 0x09) << 4)) // Use illegal value for illegal address range +/// End of string for override register +#define END_OVERRIDE 0xFFFFFFFF + + +#define FWPAR_8BIT_ADDR(cmd, field) (0x1800 | (_POSITION_##cmd##_##field)) +#define FWPAR_16BIT_ADDR(cmd, field) (0x1000 | (_POSITION_##cmd##_##field)) +#define FWPAR_32BIT_ADDR(cmd, field) (0x0000 | (_POSITION_##cmd##_##field)) + +/// ADI address-value pair +#define ADI_ADDR_VAL(addr, value) ((((addr) & 0x7F) << 8) | ((value) & 0xFF)) +#define ADI_ADDR_VAL_MASK(addr, mask, value) ((((addr) & 0x7F) << 8) | ADI_VAL_MASK(addr, mask, value)) + +/// Low half-word +#define LOWORD(value) ((value) & 0xFFFF) +/// High half-word +#define HIWORD(value) ((value) >> 16) +///@} + + +#endif diff --git a/cpu/cc26xx/dev/uart1.h b/cpu/cc26xx/dev/uart1.h new file mode 100644 index 000000000..76dcdcdbe --- /dev/null +++ b/cpu/cc26xx/dev/uart1.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-uart + * @{ + * + * \file + * This file really only exists because some examples rely on it. + * + * For instance, some examples do uart1_set_input(f). We re-write this to + * uart_set_input + */ +#ifndef UART1_H_ +#define UART1_H_ + +#include "dev/cc26xx-uart.h" + +#define BAUD2UBR(x) x +#define uart1_set_input(f) cc26xx_uart_set_input(f) + +#endif /* UART1_H_ */ + +/** @} */ diff --git a/cpu/cc26xx/fault-handlers.c b/cpu/cc26xx/fault-handlers.c new file mode 100644 index 000000000..48dd9f204 --- /dev/null +++ b/cpu/cc26xx/fault-handlers.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +#include "inc/hw_types.h" +#include "inc/hw_memmap.h" +#include "inc/hw_cpu_scs.h" +/*---------------------------------------------------------------------------*/ +#define fault_handlers_hard_fault_isr FaultISR +/*---------------------------------------------------------------------------*/ +void +fault_handlers_hard_fault_isr(void) +{ + /* + * Workaround for (Im)precise Bus Faults caused under unknown circumstances, + * likely by access to RFC registers while the RF PD is off (which is + * something that should never happen because we do in fact check before + * accessing) + */ + if((HWREG(CPU_SCS_BASE + CPU_SCS_O_CFSR) == CPU_SCS_CFSR_IMPRECISERR) || + (HWREG(CPU_SCS_BASE + CPU_SCS_O_CFSR) & CPU_SCS_CFSR_PRECISERR)){ + /* ToDo: Check BFARVALID and then BFAR to filter down even further */ + return; + } + + while(1); +} +/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc26xx/ieee-addr.c b/cpu/cc26xx/ieee-addr.c new file mode 100644 index 000000000..1d70aff18 --- /dev/null +++ b/cpu/cc26xx/ieee-addr.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-ieee-addr + * @{ + * + * \file + * Driver for the CC26xx IEEE addresses + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "net/linkaddr.h" +#include "ieee-addr.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +void +ieee_addr_cpy_to(uint8_t *dst, uint8_t len) +{ + if(IEEE_ADDR_CONF_HARDCODED) { + uint8_t ieee_addr_hc[8] = IEEE_ADDR_CONF_ADDRESS; + + memcpy(dst, &ieee_addr_hc[8 - len], len); + } else { + int i; + + /* Reading from primary location... */ + uint8_t *location = (uint8_t *)IEEE_ADDR_LOCATION_PRIMARY; + + /* + * ...unless we can find a byte != 0xFF in secondary + * + * Intentionally checking all 8 bytes here instead of len, because we + * are checking validity of the entire IEEE address irrespective of the + * actual number of bytes the caller wants to copy over. + */ + for(i = 0; i < 8; i++) { + if(((uint8_t *)IEEE_ADDR_LOCATION_SECONDARY)[i] != 0xFF) { + /* A byte in the secondary location is not 0xFF. Use the secondary */ + location = (uint8_t *)IEEE_ADDR_LOCATION_SECONDARY; + break; + } + } + + /* + * We have chosen what address to read the IEEE address from. Do so, + * inverting byte order + */ + for(i = 0; i < len; i++) { + dst[i] = location[len - 1 - i]; + } + } + +#if IEEE_ADDR_NODE_ID + dst[len - 1] = IEEE_ADDR_NODE_ID & 0xFF; + dst[len - 2] = IEEE_ADDR_NODE_ID >> 8; +#endif +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/cpu/cc26xx/ieee-addr.h b/cpu/cc26xx/ieee-addr.h new file mode 100644 index 000000000..3cd97d72a --- /dev/null +++ b/cpu/cc26xx/ieee-addr.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-ieee-addr CC26xx IEEE Address Control + * + * Driver for the retrieval of an IEEE address from flash + * + * The user can specify a hardcoded IEEE address through the + * IEEE_ADDR_CONF_HARDCODED configuration macro. + * + * If the user does not hard-code an address, then one will be read from either + * the primary location (InfoPage) or from the secondary location (on flash). + * + * In order to allow the user to easily program nodes with addresses, the + * secondary location is given priority: If it contains a valid address then + * it will be chosen in favour of the one on InfoPage. + * + * In this context, an address is valid if at least one of the 8 bytes does not + * equal 0xFF. If all 8 bytes are 0xFF, then the primary location will be used. + * + * In all cases, the address is assumed to be written little-endian. + * + * Lastly, it is possible to override the 2 LSB's of the address by using the + * NODE_ID make variable. + * @{ + * + * \file + * Header file with register and macro declarations for the cc26xx IEEE address + * driver + */ +/*---------------------------------------------------------------------------*/ +#ifndef IEEE_ADDR_H_ +#define IEEE_ADDR_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" + +#include +/*---------------------------------------------------------------------------*/ +/** + * \name IEEE address locations + * + * The address of the secondary location can be configured by the platform + * or example + * + * @{ + */ +#define IEEE_ADDR_LOCATION_PRIMARY 0x500012F0 /**< Primary IEEE address location */ + +#ifdef IEEE_ADDR_CONF_LOCATION_SECONDARY +#define IEEE_ADDR_LOCATION_SECONDARY IEEE_ADDR_CONF_LOCATION_SECONDARY +#else +#define IEEE_ADDR_LOCATION_SECONDARY 0x0001FFC8 /**< Secondary IEEE address location */ +#endif +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \brief Copy the node's IEEE address to a destination memory area + * \param dst A pointer to the destination area where the IEEE address is to be + * written + * \param len The number of bytes to write to destination area + * + * This function will copy \e len LS bytes and it will invert byte order in + * the process. The factory address on devices is normally little-endian, + * therefore you should expect dst to store the address in a big-endian order. + */ +void ieee_addr_cpy_to(uint8_t *dst, uint8_t len); +/*---------------------------------------------------------------------------*/ +#endif /* IEEE_ADDR_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/lpm.c b/cpu/cc26xx/lpm.c new file mode 100644 index 000000000..0322f940c --- /dev/null +++ b/cpu/cc26xx/lpm.c @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-lpm + * @{ + * + * Implementation of CC26xx low-power operation functionality + * + * @{ + * + * \file + * Driver for CC26xx's low-power operation + */ +/*---------------------------------------------------------------------------*/ +#include "prcm.h" +#include "contiki-conf.h" +#include "ti-lib.h" +#include "lpm.h" +#include "sys/energest.h" +#include "lib/list.h" +#include "dev/leds.h" +#include "dev/watchdog.h" +#include "dev/cc26xx-rtc.h" +/*---------------------------------------------------------------------------*/ +#if ENERGEST_CONF_ON +static unsigned long irq_energest = 0; + +#define ENERGEST_IRQ_SAVE(a) do { \ + a = energest_type_time(ENERGEST_TYPE_IRQ); } while(0) +#define ENERGEST_IRQ_RESTORE(a) do { \ + energest_type_set(ENERGEST_TYPE_IRQ, a); } while(0) +#else +#define ENERGEST_IRQ_SAVE(a) do {} while(0) +#define ENERGEST_IRQ_RESTORE(a) do {} while(0) +#endif +/*---------------------------------------------------------------------------*/ +LIST(modules_list); +/*---------------------------------------------------------------------------*/ +/* Control what power domains we are allow to run under what mode */ +LIST(power_domain_locks_list); + +/* PDs that may stay on in deep sleep */ +#define LOCKABLE_DOMAINS ((uint32_t)(PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH)) +/*---------------------------------------------------------------------------*/ +/* + * Don't consider standby mode if the next AON RTC event is scheduled to fire + * in less than STANDBY_MIN_DURATION rtimer ticks + */ +#define STANDBY_MIN_DURATION (RTIMER_SECOND >> 8) +/*---------------------------------------------------------------------------*/ +/* Variables used by the power on/off (Power mode: SHUTDOWN) functionality */ +static uint8_t shutdown_requested; +static uint32_t pin; +/*---------------------------------------------------------------------------*/ +void +lpm_pd_lock_obtain(lpm_power_domain_lock_t *lock, uint32_t domains) +{ + /* We only accept locks for specific PDs */ + domains &= LOCKABLE_DOMAINS; + + if(domains == 0) { + return; + } + + lock->domains = domains; + + list_add(power_domain_locks_list, lock); +} +/*---------------------------------------------------------------------------*/ +void +lpm_pd_lock_release(lpm_power_domain_lock_t *lock) +{ + lock->domains = 0; + + list_remove(power_domain_locks_list, lock); +} +/*---------------------------------------------------------------------------*/ +void +lpm_shutdown(uint32_t wakeup_pin) +{ + shutdown_requested = 1; + pin = wakeup_pin; +} +/*---------------------------------------------------------------------------*/ +static void +shutdown_now(void) +{ + lpm_registered_module_t *module; + int i; + rtimer_clock_t t0; + uint32_t io_cfg = (IOC_STD_INPUT & ~IOC_IOPULL_M) | IOC_IOPULL_UP | + IOC_WAKE_ON_LOW; + + for(module = list_head(modules_list); module != NULL; + module = module->next) { + if(module->shutdown) { + module->shutdown(LPM_MODE_SHUTDOWN); + } + } + + leds_off(LEDS_ALL); + + for(i = 0; i < 5; i++) { + t0 = RTIMER_NOW(); + leds_toggle(LEDS_ALL); + while(RTIMER_CLOCK_LT(RTIMER_NOW(), (t0 + (RTIMER_SECOND >> 3)))); + } + + leds_off(LEDS_ALL); + + ti_lib_gpio_dir_mode_set((1 << pin), GPIO_DIR_MODE_IN); + ti_lib_ioc_port_configure_set(pin, IOC_PORT_GPIO, io_cfg); + + ti_lib_pwr_ctrl_state_set(LPM_MODE_SHUTDOWN); +} +/*---------------------------------------------------------------------------*/ +/* + * We'll get called on three occasions: + * - While running + * - While sleeping + * - While deep sleeping + * + * For the former two, we don't need to do anything. For the latter, we + * notify all modules that we're back on and rely on them to restore clocks + * and power domains as required. + */ +void +lpm_wake_up() +{ + lpm_registered_module_t *module; + + /* Remember IRQ energest for next pass */ + ENERGEST_IRQ_SAVE(irq_energest); + ENERGEST_ON(ENERGEST_TYPE_CPU); + ENERGEST_OFF(ENERGEST_TYPE_LPM); + + /* Sync so that we get the latest values before adjusting recharge settings */ + ti_lib_sys_ctrl_aon_sync(); + + /* Adjust recharge settings */ + ti_lib_sys_ctrl_adjust_recharge_after_power_down(); + + /* + * Release the request to the uLDO + * This is likely not required, since the switch to GLDO/DCDC is automatic + * when coming back from deep sleep + */ + ti_lib_prcm_mcu_uldo_configure(false); + + /* Turn on cache again */ + ti_lib_vims_mode_set(VIMS_BASE, VIMS_MODE_ENABLED); + ti_lib_prcm_retention_enable(PRCM_DOMAIN_VIMS); + + ti_lib_aon_ioc_freeze_disable(); + ti_lib_sys_ctrl_aon_sync(); + + /* Power up AUX and allow it to go to sleep */ + ti_lib_aon_wuc_aux_wakeup_event(AONWUC_AUX_ALLOW_SLEEP); + + /* Notify all registered modules that we've just woken up */ + for(module = list_head(modules_list); module != NULL; + module = module->next) { + if(module->wakeup) { + module->wakeup(); + } + } +} +/*---------------------------------------------------------------------------*/ +void +lpm_drop() +{ + lpm_registered_module_t *module; + lpm_power_domain_lock_t *lock; + uint8_t max_pm = LPM_MODE_MAX_SUPPORTED; + uint8_t module_pm; + + uint32_t domains = LOCKABLE_DOMAINS; + + if(shutdown_requested) { + shutdown_now(); + } + + if(RTIMER_CLOCK_LT(cc26xx_rtc_get_next_trigger(), + RTIMER_NOW() + STANDBY_MIN_DURATION)) { + lpm_sleep(); + return; + } + + /* Collect max allowed PM permission from interested modules */ + for(module = list_head(modules_list); module != NULL; + module = module->next) { + if(module->request_max_pm) { + module_pm = module->request_max_pm(); + if(module_pm < max_pm) { + max_pm = module_pm; + } + } + } + + /* Check if any events fired during this process. Last chance to abort */ + if(process_nevents()) { + return; + } + + /* Drop */ + if(max_pm == LPM_MODE_SLEEP) { + lpm_sleep(); + } else { + /* Critical. Don't get interrupted! */ + ti_lib_int_master_disable(); + + /* + * Notify all registered modules that we are dropping to mode X. We do not + * need to do this for simple sleep. + * + * This is a chance for modules to delay us a little bit until an ongoing + * operation has finished (e.g. uart TX) or to configure themselves for + * deep sleep. + */ + for(module = list_head(modules_list); module != NULL; + module = module->next) { + if(module->shutdown) { + module->shutdown(max_pm); + } + } + + /* + * Iterate PD locks to see what we can and cannot turn off. + * + * The argument to PRCMPowerDomainOff() is a bitwise OR, so every time + * we encounter a lock we just clear the respective bits in the 'domains' + * variable as required by the lock. In the end the domains variable will + * just hold whatever has not been cleared + */ + for(lock = list_head(power_domain_locks_list); lock != NULL; + lock = lock->next) { + /* Clear the bits specified in the lock */ + domains &= ~lock->domains; + } + + /* Pat the dog: We don't want it to shout right after we wake up */ + watchdog_periodic(); + + /* Clear unacceptable bits, just in case a lock provided a bad value */ + domains &= LOCKABLE_DOMAINS; + + /* + * Freeze the IOs on the boundary between MCU and AON. We only do this if + * PERIPH is not needed + */ + if(domains & PRCM_DOMAIN_PERIPH) { + ti_lib_aon_ioc_freeze_enable(); + } + + /* + * Among LOCKABLE_DOMAINS, turn off those that are not locked + * + * If domains is != 0, pass it as-is + */ + if(domains) { + ti_lib_prcm_power_domain_off(domains); + } + + /* Configure clock sources for MCU and AUX: No clock */ + ti_lib_aon_wuc_mcu_power_down_config(AONWUC_NO_CLOCK); + ti_lib_aon_wuc_aux_power_down_config(AONWUC_NO_CLOCK); + + /* Full RAM retention. */ + ti_lib_aon_wuc_mcu_sram_config(MCU_RAM0_RETENTION | MCU_RAM1_RETENTION | + MCU_RAM2_RETENTION | MCU_RAM3_RETENTION); + + /* Enable retention on the CPU domain */ + ti_lib_prcm_retention_enable(PRCM_DOMAIN_CPU); + + /* Disable retention of AUX RAM */ + ti_lib_aon_wuc_aux_sram_config(false); + + /* Disable retention in the RFCORE RAM */ + HWREG(PRCM_BASE + PRCM_O_RAMRETEN) &= ~PRCM_RAMRETEN_RFC; + + /* Disable retention of VIMS RAM (TRAM and CRAM) */ + //TODO: This can probably be removed, we are calling ti_lib_prcm_retention_disable(PRCM_DOMAIN_VIMS); further down + HWREG(PRCM_BASE + PRCM_O_RAMRETEN) &= ~PRCM_RAMRETEN_VIMS_M; + + /* + * Always turn off RFCORE, CPU, SYSBUS and VIMS. RFCORE should be off + * already + */ + ti_lib_prcm_power_domain_off(PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_CPU | + PRCM_DOMAIN_VIMS | PRCM_DOMAIN_SYSBUS); + + /* Request JTAG domain power off */ + ti_lib_aon_wuc_jtag_power_off(); + + /* Turn off AUX */ + ti_lib_aux_wuc_power_ctrl(AUX_WUC_POWER_OFF); + ti_lib_aon_wuc_domain_power_down_enable(); + while(ti_lib_aon_wuc_power_status() & AONWUC_AUX_POWER_ON); + + /* Configure the recharge controller */ + ti_lib_sys_ctrl_set_recharge_before_power_down(false); + + /* + * If both PERIPH and SERIAL PDs are off, request the uLDO for as the power + * source while in deep sleep. + */ + if(domains == LOCKABLE_DOMAINS) { + ti_lib_pwr_ctrl_source_set(PWRCTRL_PWRSRC_ULDO); + } + + /* We are only interested in IRQ energest while idle or in LPM */ + ENERGEST_IRQ_RESTORE(irq_energest); + ENERGEST_OFF(ENERGEST_TYPE_CPU); + ENERGEST_ON(ENERGEST_TYPE_LPM); + + /* Sync the AON interface to ensure all writes have gone through. */ + ti_lib_sys_ctrl_aon_sync(); + + /* + * Explicitly turn off VIMS cache, CRAM and TRAM. Needed because of + * retention mismatch between VIMS logic and cache. We wait to do this + * until right before deep sleep to be able to use the cache for as long + * as possible. + */ + ti_lib_prcm_retention_disable(PRCM_DOMAIN_VIMS); + ti_lib_vims_mode_set(VIMS_BASE, VIMS_MODE_OFF); + + /* Deep Sleep */ + ti_lib_prcm_deep_sleep(); + + /* + * When we reach here, some interrupt woke us up. The global interrupt + * flag is off, hence we have a chance to run things here. We will wake up + * the chip properly, and then we will enable the global interrupt without + * unpending events so the handlers can fire + */ + lpm_wake_up(); + + ti_lib_int_master_enable(); + } +} +/*---------------------------------------------------------------------------*/ +void +lpm_sleep(void) +{ + ENERGEST_OFF(ENERGEST_TYPE_CPU); + ENERGEST_ON(ENERGEST_TYPE_LPM); + + /* We are only interested in IRQ energest while idle or in LPM */ + ENERGEST_IRQ_RESTORE(irq_energest); + + /* Just to be on the safe side, explicitly disable Deep Sleep */ + HWREG(NVIC_SYS_CTRL) &= ~(NVIC_SYS_CTRL_SLEEPDEEP); + + ti_lib_prcm_sleep(); + + /* Remember IRQ energest for next pass */ + ENERGEST_IRQ_SAVE(irq_energest); + + ENERGEST_ON(ENERGEST_TYPE_CPU); + ENERGEST_OFF(ENERGEST_TYPE_LPM); +} +/*---------------------------------------------------------------------------*/ +void +lpm_register_module(lpm_registered_module_t *module) +{ + list_add(modules_list, module); +} +/*---------------------------------------------------------------------------*/ +void +lpm_init() +{ + list_init(modules_list); + list_init(power_domain_locks_list); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/lpm.h b/cpu/cc26xx/lpm.h new file mode 100644 index 000000000..85c20e77b --- /dev/null +++ b/cpu/cc26xx/lpm.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-lpm CC26xx Low-Power management + * + * CC26xx low-power operation + * + * @{ + * + * \file + * Header file for the management of CC26xx low-power operation + */ +/*---------------------------------------------------------------------------*/ +#ifndef LPM_H_ +#define LPM_H_ +/*---------------------------------------------------------------------------*/ +#include "pwr_ctrl.h" + +#include +/*---------------------------------------------------------------------------*/ +#define LPM_MODE_SLEEP PWRCTRL_ACTIVE +#define LPM_MODE_DEEP_SLEEP PWRCTRL_POWER_DOWN +#define LPM_MODE_SHUTDOWN PWRCTRL_SHUTDOWN + +#define LPM_MODE_MAX_SUPPORTED LPM_MODE_DEEP_SLEEP +/*---------------------------------------------------------------------------*/ +typedef struct lpm_registered_module { + struct lpm_registered_module *next; + uint8_t (*request_max_pm)(void); + void (*shutdown)(uint8_t mode); + void (*wakeup)(void); +} lpm_registered_module_t; +/*---------------------------------------------------------------------------*/ +/** + * \brief Declare a variable to be used in order to get notifications from LPM + * \param n the variable name to be declared + * \param m A pointer to a function which will tell the LPM module the max + * PM this module is willing to handle. This function will return + * LPM_MODE_SLEEP, LPM_MODE_DEEP_SLEEP etc. The LPM module will ask all + * registered modules and will trigger the highest LPM permitted + * \param s A pointer to a function which will receive a notification just + * before entering the low power mode. The callee can prepare for the + * imminent LPM state. The argument to this function will be the + * upcoming low power mode. This function can e.g. turn off a + * peripheral before the LPM module shuts down the power domain. + * \param w A pointer to a function which will be called just after we have + * woken up. This can be used to e.g. turn a peripheral back on. This + * function is in charge of turning power domains back on. This + * function will normally be called within an interrupt context. + */ +#define LPM_MODULE(n, m, s, w) static lpm_registered_module_t n = \ + { NULL, m, s, w } +/*---------------------------------------------------------------------------*/ +/** + * + * \brief Data type used to control whether a PD will get shut down when the + * CM3 drops to deep sleep + * + * Modules using these facilities must allocate a variable of this type, but + * they must not try to manipulate it directly. Instead, the respective + * functions must be used + * + * \sa lpm_pd_lock_obtain(), lpm_pd_lock_release() + */ +typedef struct lpm_power_domain_lock { + struct lpm_power_domain_lock *next; + uint32_t domains; +} lpm_power_domain_lock_t; +/*---------------------------------------------------------------------------*/ +/** + * \brief Prohibit a PD from turning off in standby mode + * \param lock A pointer to a lpm_power_domain_lock_t + * \param domains Bitwise OR from PRCM_DOMAIN_PERIPH, PRCM_DOMAIN_SERIAL + * + * The caller is responsible for allocating lpm_power_domain_lock_t + * + * Only the domains listed above can be locked / released, but a single lock + * can be used for multiple domains + */ +void lpm_pd_lock_obtain(lpm_power_domain_lock_t *lock, uint32_t domains); + +/** + * \brief Permit a PD to turn off in standby mode + * \param pd A pointer to a previously used lock + * + * \sa lpm_pd_lock_obtain() + */ +void lpm_pd_lock_release(lpm_power_domain_lock_t *pd); + +/** + * \brief Drop the cortex to sleep / deep sleep and shut down peripherals + * + * Whether the cortex will drop to sleep or deep sleep is configurable. The + * exact peripherals which will be shut down is also configurable + */ +void lpm_drop(void); + +/** + * \brief Enter sleep mode + */ +void lpm_sleep(void); + +/** + * \brief Put the chip in shutdown power mode + * \param wakeup_pin The GPIO pin which will wake us up. Must be IOID_0 etc... + */ +void lpm_shutdown(uint32_t wakeup_pin); + +/** + * \brief Wake up from sleep mode + * + * This function must be called at the start of any interrupt context which + * may bring us out of sleep. Current interrupts do this already, but make sure + * to do the same when adding new ISRs + */ +void lpm_wake_up(void); + +/** + * \brief Register a module for LPM notifications. + * \param module A pointer to the data structure with the module definition + * + * When the LPM module is about to drop to some low power mode, it will first + * notify all modules about this. + * + * This function must not be called before the module has been initialised + * with lpm_init(). The code does not perform checks: This is the caller's + * responsibility. + */ +void lpm_register_module(lpm_registered_module_t *module); + +/** + * \brief Initialise the low-power mode management module + */ +void lpm_init(void); +/*---------------------------------------------------------------------------*/ +#endif /* LPM_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/mtarch.h b/cpu/cc26xx/mtarch.h new file mode 100644 index 000000000..4f696669d --- /dev/null +++ b/cpu/cc26xx/mtarch.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * 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. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 + * Stub header file for multi-threading. It doesn't do anything, it + * just exists so that mt.c can compile cleanly. + * + * This is based on the original mtarch.h for z80 by Takahide Matsutsuka + * + * \author + * George Oikonomou - + */ +#ifndef __MTARCH_H__ +#define __MTARCH_H__ + +struct mtarch_thread { + unsigned char *sp; +}; + +#endif /* __MTARCH_H__ */ diff --git a/cpu/cc26xx/putchar.c b/cpu/cc26xx/putchar.c new file mode 100644 index 000000000..0f91deea5 --- /dev/null +++ b/cpu/cc26xx/putchar.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +#include "cc26xx-uart.h" + +#include +/*---------------------------------------------------------------------------*/ +int +putchar(int c) +{ + cc26xx_uart_write_byte(c); + return c; +} +/*---------------------------------------------------------------------------*/ +int +puts(const char *str) +{ + int i; + if(str == NULL) { + return 0; + } + for(i = 0; i < strlen(str); i++) { + putchar(str[i]); + } + putchar('\n'); + return i; +} +/*---------------------------------------------------------------------------*/ +unsigned int +dbg_send_bytes(const unsigned char *s, unsigned int len) +{ + unsigned int i = 0; + + while(s && *s != 0) { + if(i >= len) { + break; + } + putchar(*s++); + i++; + } + return i; +} +/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc26xx/rtimer-arch.c b/cpu/cc26xx/rtimer-arch.c new file mode 100644 index 000000000..37077f1b3 --- /dev/null +++ b/cpu/cc26xx/rtimer-arch.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-rtimer + * @{ + * + * \file + * Implementation of the arch-specific rtimer functions for the cc26xx + * + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "sys/energest.h" +#include "sys/rtimer.h" +#include "cpu.h" +#include "dev/cc26xx-rtc.h" + +#include "ti-lib.h" + +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief We don't need to do anything special here. The RTC is initialised + * elsewhere + */ +void +rtimer_arch_init(void) +{ + return; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Schedules an rtimer task to be triggered at time t + * \param t The time when the task will need executed. + * + * \e t is an absolute time, in other words the task will be executed AT + * time \e t, not IN \e t rtimer ticks. + * + * This function schedules a one-shot event with the AON RTC. + * + * This functions converts \e to a value suitable for the AON RTC. + */ +void +rtimer_arch_schedule(rtimer_clock_t t) +{ + /* Convert the rtimer tick value to a value suitable for the AON RTC */ + cc26xx_rtc_schedule_one_shot(t); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the current real-time clock time + * \return The current rtimer time in ticks + * + * The value is read from the AON RTC counter and converted to a number of + * rtimer ticks + * + */ +rtimer_clock_t +rtimer_arch_now() +{ + return ti_lib_aon_rtc_current_compare_value_get(); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/cpu/cc26xx/rtimer-arch.h b/cpu/cc26xx/rtimer-arch.h new file mode 100644 index 000000000..1a4a50c09 --- /dev/null +++ b/cpu/cc26xx/rtimer-arch.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-clocks + * @{ + * + * \defgroup cc26xx-rtimer CC26xx rtimer + * + * Implementation of the rtimer module for the CC26xx + * + * The rtimer runs on the AON RTC. We set the RTC's channel 2 to continuous + * compare mode, instead of scheduling the next tick interrupt by software. + * This gives us completely equidistant events. + * + * The RTC runs in all power modes (except shutdown) + * @{ + */ +/** + * \file + * Header file for the CC26xx rtimer driver + */ +/*---------------------------------------------------------------------------*/ +#ifndef RTIMER_ARCH_H_ +#define RTIMER_ARCH_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +/*---------------------------------------------------------------------------*/ +#define RTIMER_ARCH_SECOND 65536 +/*---------------------------------------------------------------------------*/ +rtimer_clock_t rtimer_arch_now(void); +/*---------------------------------------------------------------------------*/ +#endif /* RTIMER_ARCH_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/cpu/cc26xx/slip-arch.c b/cpu/cc26xx/slip-arch.c new file mode 100644 index 000000000..268c84548 --- /dev/null +++ b/cpu/cc26xx/slip-arch.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-char-io + * @{ + * + * \file + * Arch-specific SLIP functions for the cc26xx + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "dev/cc26xx-uart.h" +#include "dev/slip.h" +/*---------------------------------------------------------------------------*/ +/** + * \brief Write a byte over SLIP + * \param c the byte + */ +void +slip_arch_writeb(unsigned char c) +{ + cc26xx_uart_write_byte(c); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise the arch-specific SLIP driver + * \param ubr Ignored for the cc26xx + */ +void +slip_arch_init(unsigned long ubr) +{ + cc26xx_uart_set_input(slip_input_byte); +} +/*---------------------------------------------------------------------------*/ + +/** @} */ diff --git a/cpu/cc26xx/ti-lib.h b/cpu/cc26xx/ti-lib.h new file mode 100644 index 000000000..df5e2cf39 --- /dev/null +++ b/cpu/cc26xx/ti-lib.h @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx + * @{ + * + * \defgroup cc26xx-ti-lib TI CC26xxware Glue + * + * Glue file which renames TI CC26xxware functions. Thus, for example, + * PowerCtrlIOFreezeDisable() becomes power_ctrl_io_freeze_disable() + * + * This is not strictly required and a call to the former will work perfectly + * correctly. However, in using those macros, we make the core of the Contiki + * port match the naming convention. + * + * Since all functions are prefixed with ti_lib, it also becomes clear to the + * reader that this is a call to TI driverlib's sources and not a call to a + * function inside Contiki + * + * @{ + * + * \file + * Header file with macros which rename TI CC26xxware functions. + */ +#ifndef TI_LIB_H_ +#define TI_LIB_H_ +/*---------------------------------------------------------------------------*/ +/* aon_batmon.h */ +#include "driverlib/aon_batmon.h" + +#define ti_lib_aon_batmon_enable(...) AONBatMonEnable(__VA_ARGS__) +#define ti_lib_aon_batmon_disable(...) AONBatMonDisable(__VA_ARGS__) +#define ti_lib_aon_batmon_measurement_cycle_set(...) AONBatMonMeasurementCycleSet(__VA_ARGS__) +#define ti_lib_aon_batmon_measurement_cycle_get(...) AONBatMonMeasurementCycleGet(__VA_ARGS__) +#define ti_lib_aon_batmon_battery_trim_set(...) AONBatMonBatteryTrimSet(__VA_ARGS__) +#define ti_lib_aon_batmon_temperature_trim_set(...) AONBatMonTemperatureTrimSet(__VA_ARGS__) +#define ti_lib_aon_batmon_temperature_get(...) AONBatMonTemperatureGet(__VA_ARGS__) +#define ti_lib_aon_batmon_temp_get_deg(...) AON_BatmonTempGetDegC(__VA_ARGS__) +#define ti_lib_aon_batmon_battery_voltage_get(...) AONBatMonBatteryVoltageGet(__VA_ARGS__) +#define ti_lib_aon_batmon_new_battery_measure_ready(...) AONBatMonNewBatteryMeasureReady(__VA_ARGS__) +#define ti_lib_aon_batmon_new_temp_measure_ready(...) AONBatMonNewTempMeasureReady(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* aon_event.h */ +#include "driverlib/aon_event.h" + +#define ti_lib_aon_event_mcu_wake_up_set(...) AONEventMcuWakeUpSet(__VA_ARGS__) +#define ti_lib_aon_event_mcu_wake_up_get(...) AONEventMcuWakeUpGet(__VA_ARGS__) +#define ti_lib_aon_event_aux_wake_up_set(...) AONEventAuxWakeUpSet(__VA_ARGS__) +#define ti_lib_aon_event_aux_wake_up_get(...) AONEventAuxWakeUpGet(__VA_ARGS__) +#define ti_lib_aon_event_mcu_set(...) AONEventMcuSet(__VA_ARGS__) +#define ti_lib_aon_event_mcu_get(...) AONEventMcuGet(__VA_ARGS__) +#define ti_lib_aon_event_rtc_set(...) AONEventRtcSet(__VA_ARGS__) +#define ti_lib_aon_event_rtc_get(...) AONEventRtcGet(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* aon_ioc.h */ +#include "driverlib/aon_ioc.h" + +#define ti_lib_aon_ioc_drive_strength_set(...) AONIOCDriveStrengthSet(__VA_ARGS__) +#define ti_lib_aon_ioc_drive_strength_get(...) AONIOCDriveStrengthGet(__VA_ARGS__) +#define ti_lib_aon_ioc_freeze_enable(...) AONIOCFreezeEnable(__VA_ARGS__) +#define ti_lib_aon_ioc_freeze_disable(...) AONIOCFreezeDisable(__VA_ARGS__) +#define ti_lib_aon_ioc_32_khz_output_disable(...) AONIOC32kHzOutputDisable(__VA_ARGS__) +#define ti_lib_aon_ioc_32_khz_output_enable(...) AONIOC32kHzOutputEnable(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* aon_rtc.h */ +#include "driverlib/aon_rtc.h" + +#define ti_lib_aon_rtc_enable(...) AONRTCEnable(__VA_ARGS__) +#define ti_lib_aon_rtc_disable(...) AONRTCDisable(__VA_ARGS__) +#define ti_lib_aon_rtc_status(...) AONRTCStatus(__VA_ARGS__) +#define ti_lib_aon_rtc_reset(...) AONRTCReset(__VA_ARGS__) +#define ti_lib_aon_rtc_delay_config(...) AONRTCDelayConfig(__VA_ARGS__) +#define ti_lib_aon_rtc_combined_event_config(...) AONRTCCombinedEventConfig(__VA_ARGS__) +#define ti_lib_aon_rtc_event_clear(...) AONRTCEventClear(__VA_ARGS__) +#define ti_lib_aon_rtc_event_get(...) AONRTCEventGet(__VA_ARGS__) +#define ti_lib_aon_rtc_sec_get(...) AONRTCSecGet(__VA_ARGS__) +#define ti_lib_aon_rtc_fraction_get(...) AONRTCFractionGet(__VA_ARGS__) +#define ti_lib_aon_rtc_sub_sec_incr_get(...) AONRTCSubSecIncrGet(__VA_ARGS__) +#define ti_lib_aon_rtc_mode_ch1_set(...) AONRTCModeCh1Set(__VA_ARGS__) +#define ti_lib_aon_rtc_mode_ch1_get(...) AONRTCModeCh1Get(__VA_ARGS__) +#define ti_lib_aon_rtc_mode_ch2_set(...) AONRTCModeCh2Set(__VA_ARGS__) +#define ti_lib_aon_rtc_mode_ch2_get(...) AONRTCModeCh2Get(__VA_ARGS__) +#define ti_lib_aon_rtc_channel_enable(...) AONRTCChannelEnable(__VA_ARGS__) +#define ti_lib_aon_rtc_channel_disable(...) AONRTCChannelDisable(__VA_ARGS__) +#define ti_lib_aon_rtc_compare_value_set(...) AONRTCCompareValueSet(__VA_ARGS__) +#define ti_lib_aon_rtc_compare_value_get(...) AONRTCCompareValueGet(__VA_ARGS__) +#define ti_lib_aon_rtc_current_compare_value_get(...) AONRTCCurrentCompareValueGet(__VA_ARGS__) +#define ti_lib_aon_rtc_inc_value_ch2_set(...) AONRTCIncValueCh2Set(__VA_ARGS__) +#define ti_lib_aon_rtc_inc_value_ch2_get(...) AONRTCIncValueCh2Get(__VA_ARGS__) +#define ti_lib_aon_rtc_capture_value_ch1_get(...) AONRTCCaptureValueCh1Get(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* aon_wuc.h */ +#include "driverlib/aon_wuc.h" + +#define ti_lib_aon_wuc_mcu_wake_up_config(...) AONWUCMcuWakeUpConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_mcu_power_down_config(...) AONWUCMcuPowerDownConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_mcu_power_off_config(...) AONWUCMcuPowerOffConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_mcu_sram_config(...) AONWUCMcuSRamConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_clock_config_set(...) AONWUCAuxClockConfigSet(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_clock_config_get(...) AONWUCAuxClockConfigGet(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_power_down_config(...) AONWUCAuxPowerDownConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_power_off_config(...) AONWUCAuxPowerOffConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_wake_up_config(...) AONWUCAuxWakeUpConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_sram_config(...) AONWUCAuxSRamConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_wakeup_event(...) AONWUCAuxWakeupEvent(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_image_valid(...) AONWUCAuxImageValid(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_image_invalid(...) AONWUCAuxImageInvalid(__VA_ARGS__) +#define ti_lib_aon_wuc_aux_reset(...) AONWUCAuxReset(__VA_ARGS__) +#define ti_lib_aon_wuc_power_status(...) AONWUCPowerStatus(__VA_ARGS__) +#define ti_lib_aon_wuc_shut_down_enable(...) AONWUCShutDownEnable(__VA_ARGS__) +#define ti_lib_aon_wuc_domain_power_down_enable(...) AONWUCDomainPowerDownEnable(__VA_ARGS__) +#define ti_lib_aon_wuc_domain_power_down_disable(...) AONWUCDomainPowerDownDisable(__VA_ARGS__) +#define ti_lib_aon_wuc_mcu_reset_status(...) AONWUCMcuResetStatus(__VA_ARGS__) +#define ti_lib_aon_wuc_mcu_reset_clear(...) AONWUCMcuResetClear(__VA_ARGS__) +#define ti_lib_aon_wuc_recharge_ctrl_config_set(...) AONWUCRechargeCtrlConfigSet(__VA_ARGS__) +#define ti_lib_aon_wuc_recharge_ctrl_config_get(...) AONWUCRechargeCtrlConfigGet(__VA_ARGS__) +#define ti_lib_aon_wuc_osc_config(...) AONWUCOscConfig(__VA_ARGS__) +#define ti_lib_aon_wuc_jtag_power_off(...) AONWUCJtagPowerOff(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* aux_wuc.h */ +#include "driverlib/aux_wuc.h" + +#define ti_lib_aux_wuc_clock_enable(...) AUXWUCClockEnable(__VA_ARGS__) +#define ti_lib_aux_wuc_clock_disble(...) AUXWUCClockDisable(__VA_ARGS__) +#define ti_lib_aux_wuc_clock_status(...) AUXWUCClockStatus(__VA_ARGS__) +#define ti_lib_aux_wuc_clock_freq_req(...) AUXWUCClockFreqReq(__VA_ARGS__) +#define ti_lib_aux_wuc_power_ctrl(...) AUXWUCPowerCtrl(__VA_ARGS__) +#define ti_lib_aux_wuc_freeze_enable(...) AUXWUCFreezeEnable(__VA_ARGS__) +#define ti_lib_aux_wuc_freeze_disable(...) AUXWUCFreezeDisable(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* cpu.h */ +#include "driverlib/cpu.h" + +#define ti_lib_cpu_cpsid(...) CPUcpsid(__VA_ARGS__) +#define ti_lib_cpu_cpsie(...) CPUcpsie(__VA_ARGS__) +#define ti_lib_cpu_primask(...) CPUprimask(__VA_ARGS__) +#define ti_lib_cpu_wfi(...) CPUwfi(__VA_ARGS__) +#define ti_lib_cpu_wfe(...) CPUwfe(__VA_ARGS__) +#define ti_lib_cpu_sev(...) CPUsev(__VA_ARGS__) +#define ti_lib_cpu_base_pri_get(...) CPUbasepriGet(__VA_ARGS__) +#define ti_lib_cpu_base_pri_set(...) CPUbasepriSet(__VA_ARGS__) +#define ti_lib_cpu_delay(...) CPUdelay(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* ddi.h */ +#include "driverlib/ddi.h" + +#define ti_lib_aux_adi_ddi_safe_write(...) AuxAdiDdiSafeWrite(__VA_ARGS__) +#define ti_lib_aux_adi_ddi_safe_read(...) AuxAdiDdiSafeRead(__VA_ARGS__) +#define ti_lib_ddi_status_get(...) DDIStatusGet(__VA_ARGS__) +#define ti_lib_ddi_config_set(...) DDIConfigSet(__VA_ARGS__) +#define ti_lib_ddi_sync(...) DDISync(__VA_ARGS__) +#define ti_lib_ddi_protect(...) DDIProtect(__VA_ARGS__) +#define ti_lib_ddi_32_reg_write(...) DDI32RegWrite(__VA_ARGS__) +#define ti_lib_ddi_32_reg_read(...) DDI32RegRead(__VA_ARGS__) +#define ti_lib_ddi_32_bits_set(...) DDI32BitsSet(__VA_ARGS__) +#define ti_lib_ddi_32_bits_clear(...) DDI32BitsClear(__VA_ARGS__) +#define ti_lib_ddi_8_set_val_bit(...) DDI8SetValBit(__VA_ARGS__) +#define ti_lib_ddi_16_set_val_bit(...) DDI16SetValBit(__VA_ARGS__) +#define ti_lib_ddi_16_bit_write(...) DDI16BitWrite(__VA_ARGS__) +#define ti_lib_ddi_16_bit_field_write(...) DDI16BitfieldWrite(__VA_ARGS__) +#define ti_lib_ddi_16_bit_read(...) DDI16BitRead(__VA_ARGS__) +#define ti_lib_ddi_16_bitfield_read(...) DDI16BitfieldRead(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* gpio.h */ +#include "driverlib/gpio.h" + +#define ti_lib_gpio_dir_mode_set(...) GPIODirModeSet(__VA_ARGS__) +#define ti_lib_gpio_dir_mode_get(...) GPIODirModeGet(__VA_ARGS__) +#define ti_lib_gpio_pin_write(...) GPIOPinWrite(__VA_ARGS__) +#define ti_lib_gpio_pin_read(...) GPIOPinRead(__VA_ARGS__) +#define ti_lib_gpio_pin_clear(...) GPIOPinClear(__VA_ARGS__) +#define ti_lib_gpio_pin_toggle(...) GPIOPinToggle(__VA_ARGS__) +#define ti_lib_gpio_event_get(...) GPIOEventGet(__VA_ARGS__) +#define ti_lib_gpio_event_clear(...) GPIOEventClear(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* i2c.h */ +#include "driverlib/i2c.h" + +#define ti_lib_i2c_int_register(...) I2CIntRegister(__VA_ARGS__) +#define ti_lib_i2c_int_unregister(...) I2CIntUnregister(__VA_ARGS__) +#define ti_lib_i2c_master_bus_busy(...) I2CMasterBusBusy(__VA_ARGS__) +#define ti_lib_i2c_master_busy(...) I2CMasterBusy(__VA_ARGS__) +#define ti_lib_i2c_master_control(...) I2CMasterControl(__VA_ARGS__) +#define ti_lib_i2c_master_data_get(...) I2CMasterDataGet(__VA_ARGS__) +#define ti_lib_i2c_master_data_put(...) I2CMasterDataPut(__VA_ARGS__) +#define ti_lib_i2c_master_disable(...) I2CMasterDisable(__VA_ARGS__) +#define ti_lib_i2c_master_enable(...) I2CMasterEnable(__VA_ARGS__) +#define ti_lib_i2c_master_err(...) I2CMasterErr(__VA_ARGS__) +#define ti_lib_i2c_master_init_exp_clk(...) I2CMasterInitExpClk(__VA_ARGS__) +#define ti_lib_i2c_master_int_clear(...) I2CMasterIntClear(__VA_ARGS__) +#define ti_lib_i2c_master_int_disable(...) I2CMasterIntDisable(__VA_ARGS__) +#define ti_lib_i2c_master_int_enable(...) I2CMasterIntEnable(__VA_ARGS__) +#define ti_lib_i2c_master_int_status(...) I2CMasterIntStatus(__VA_ARGS__) +#define ti_lib_i2c_master_slave_addr_set(...) I2CMasterSlaveAddrSet(__VA_ARGS__) +#define ti_lib_i2c_slave_data_get(...) I2CSlaveDataGet(__VA_ARGS__) +#define ti_lib_i2c_slave_data_put(...) I2CSlaveDataPut(__VA_ARGS__) +#define ti_lib_i2c_slave_disable(...) I2CSlaveDisable(__VA_ARGS__) +#define ti_lib_i2c_slave_enable(...) I2CSlaveEnable(__VA_ARGS__) +#define ti_lib_i2c_slave_init(...) I2CSlaveInit(__VA_ARGS__) +#define ti_lib_i2c_slave_address_set(...) I2CSlaveAddressSet(__VA_ARGS__) +#define ti_lib_i2c_slave_int_clear(...) I2CSlaveIntClear(__VA_ARGS__) +#define ti_lib_i2c_slave_int_disable(...) I2CSlaveIntDisable(__VA_ARGS__) +#define ti_lib_i2c_slave_int_enable(...) I2CSlaveIntEnable(__VA_ARGS__) +#define ti_lib_i2c_slave_int_status(...) I2CSlaveIntStatus(__VA_ARGS__) +#define ti_lib_i2c_slave_status(...) I2CSlaveStatus(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* interrupt.h */ +#include "driverlib/interrupt.h" + +#define ti_lib_int_master_enable(...) IntMasterEnable(__VA_ARGS__) +#define ti_lib_int_master_disable(...) IntMasterDisable(__VA_ARGS__) +#define ti_lib_int_register(...) IntRegister(__VA_ARGS__); +#define ti_lib_int_unregsiter(...) IntUnregister(__VA_ARGS__) +#define ti_lib_int_priority_grouping_set(...) IntPriorityGroupingSet(__VA_ARGS__) +#define ti_lib_int_priority_grouping_get(...) IntPriorityGroupingGet(__VA_ARGS__) +#define ti_lib_int_priority_set(...) IntPrioritySet(__VA_ARGS__) +#define ti_lib_int_priority_get(...) IntPriorityGet(__VA_ARGS__) +#define ti_lib_int_enable(...) IntEnable(__VA_ARGS__) +#define ti_lib_int_disable(...) IntDisable(__VA_ARGS__) +#define ti_lib_int_pend_set(...) IntPendSet(__VA_ARGS__) +#define ti_lib_int_pend_get(...) IntPendGet(__VA_ARGS__) +#define ti_lib_int_pend_clear(...) IntPendClear(__VA_ARGS__) +#define ti_lib_int_mask_set(...) IntPriorityMaskSet(__VA_ARGS__) +#define ti_lib_int_mask_get(...) IntPriorityMaskGet(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* ioc.h */ +#include "driverlib/ioc.h" + +#define ti_lib_ioc_port_configure_set(...) IOCPortConfigureSet(__VA_ARGS__) +#define ti_lib_ioc_port_configure_get(...) IOCPortConfigureGet(__VA_ARGS__) +#define ti_lib_ioc_io_shutdown_set(...) IOCIOShutdownSet(__VA_ARGS__) +#define ti_lib_ioc_io_jtag_set(...) IOCIOJTagSet(__VA_ARGS__) +#define ti_lib_ioc_io_mode_set(...) IOCIOModeSet(__VA_ARGS__) +#define ti_lib_ioc_io_port_pull_set(...) IOCIOPortPullSet(__VA_ARGS__) +#define ti_lib_ioc_io_hyst_set(...) IOCIOHystSet(__VA_ARGS__) +#define ti_lib_ioc_io_input_set(...) IOCIOInputSet(__VA_ARGS__) +#define ti_lib_ioc_io_slew_ctrl_set(...) IOCIOSlewCtrlSet(__VA_ARGS__) +#define ti_lib_ioc_io_drv_strength_set(...) IOCIODrvStrengthSet(__VA_ARGS__) +#define ti_lib_ioc_io_port_id_set(...) IOCIOPortIdSet(__VA_ARGS__) +#define ti_lib_ioc_io_int_set(...) IOCIOIntSet(__VA_ARGS__) +#define ti_lib_ioc_int_register(...) IOCIntRegister(__VA_ARGS__); +#define ti_lib_ioc_int_unregister(...) IOCIntUnregister(__VA_ARGS__) +#define ti_lib_ioc_int_enable(...) IOCIntEnable(__VA_ARGS__) +#define ti_lib_ioc_int_disable(...) IOCIntDisable(__VA_ARGS__) +#define ti_lib_ioc_int_clear(...) IOCIntClear(__VA_ARGS__) +#define ti_lib_ioc_int_status(...) IOCIntStatus(__VA_ARGS__) +#define ti_lib_ioc_pin_type_gpio_input(...) IOCPinTypeGpioInput(__VA_ARGS__) +#define ti_lib_ioc_pin_type_gpio_output(...) IOCPinTypeGpioOutput(__VA_ARGS__) +#define ti_lib_ioc_pin_type_uart(...) IOCPinTypeUart(__VA_ARGS__) +#define ti_lib_ioc_pin_type_ssi_master(...) IOCPinTypeSsiMaster(__VA_ARGS__) +#define ti_lib_ioc_pin_type_ssi_slave(...) IOCPinTypeSsiSlave(__VA_ARGS__) +#define ti_lib_ioc_pin_type_i2c(...) IOCPinTypeI2c(__VA_ARGS__) +#define ti_lib_ioc_pin_type_aux(...) IOCPinTypeAux(__VA_ARGS__) +#define ti_lib_ioc_pin_type_spis(...) IOCPinTypeSpis(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* osc.h */ +#include "driverlib/osc.h" + +#define ti_lib_osc_xhf_power_mode_set(...) OSCXHfPowerModeSet(__VA_ARGS__) +#define ti_lib_osc_clock_source_set(...) OSCClockSourceSet(__VA_ARGS__) +#define ti_lib_osc_clock_source_get(...) OSCClockSourceGet(__VA_ARGS__) +#define ti_lib_osc_hf_source_ready(...) OSCHfSourceReady(__VA_ARGS__) +#define ti_lib_osc_hf_source_switch(...) OSCHfSourceSwitch(__VA_ARGS__) +#define ti_lib_osc_interface_enable(...) OSCInterfaceEnable(__VA_ARGS__) +#define ti_lib_osc_interface_disable(...) OSCInterfaceDisable(__VA_ARGS__) +#define ti_lib_osc_hf_get_startup_time(...) OSCHF_GetStartupTime(__VA_ARGS__) +#define ti_lib_osc_hf_turn_on_xosc(...) OSCHF_TurnOnXosc(__VA_ARGS__) +#define ti_lib_osc_hf_attempt_to_switch_to_xosc(...) OSCHF_AttemptToSwitchToXosc(__VA_ARGS__) +#define ti_lib_osc_hf_switch_to_rc_osc_turn_off_xosc(...) OSCHF_SwitchToRcOscTurnOffXosc(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* prcm.h */ +#include "driverlib/prcm.h" + +#define ti_lib_prcm_inf_clock_configure_set(...) PRCMInfClockConfigureSet(__VA_ARGS__) +#define ti_lib_prcm_inf_clock_configure_get(...) PRCMInfClockConfigureGet(__VA_ARGS__) +#define ti_lib_prcm_mcu_power_off(...) PRCMMcuPowerOff(__VA_ARGS__) +#define ti_lib_prcm_mcu_power_off_cancel(...) PRCMMcuPowerOffCancel(__VA_ARGS__) +#define ti_lib_prcm_mcu_uldo_configure(...) PRCMMcuUldoConfigure(__VA_ARGS__) +#define ti_lib_prcm_clock_configure_set(...) PRCMClockConfigureSet(__VA_ARGS__) +#define ti_lib_prcm_clock_configure_get(...) PRCMClockConfigureGet(__VA_ARGS__) +#define ti_lib_prcm_audio_clock_enable(...) PRCMAudioClockEnable(__VA_ARGS__) +#define ti_lib_prcm_audio_clock_disable(...) PRCMAudioClockDisable(__VA_ARGS__) +#define ti_lib_prcm_audio_clock_config_set(...) PRCMAudioClockConfigSet(__VA_ARGS__) +#define ti_lib_prcm_load_set(...) PRCMLoadSet(__VA_ARGS__) +#define ti_lib_prcm_load_get(...) PRCMLoadGet(__VA_ARGS__) +#define ti_lib_prcm_domain_enable(...) PRCMDomainEnable(__VA_ARGS__) +#define ti_lib_prcm_domain_disable(...) PRCMDomainDisable(__VA_ARGS__) +#define ti_lib_prcm_power_domain_on(...) PRCMPowerDomainOn(__VA_ARGS__) +#define ti_lib_prcm_power_domain_off(...) PRCMPowerDomainOff(__VA_ARGS__) +#define ti_lib_prcm_rf_power_down_when_idle(...) PRCMRfPowerDownWhenIdle(__VA_ARGS__) +#define ti_lib_prcm_peripheral_run_enable(...) PRCMPeripheralRunEnable(__VA_ARGS__) +#define ti_lib_prcm_peripheral_run_disable(...) PRCMPeripheralRunDisable(__VA_ARGS__) +#define ti_lib_prcm_peripheral_sleep_enable(...) PRCMPeripheralSleepEnable(__VA_ARGS__) +#define ti_lib_prcm_peripheral_sleep_disable(...) PRCMPeripheralSleepDisable(__VA_ARGS__) +#define ti_lib_prcm_peripheral_deep_sleep_enable(...) PRCMPeripheralDeepSleepEnable(__VA_ARGS__) +#define ti_lib_prcm_peripheral_deep_sleep_disable(...) PRCMPeripheralDeepSleepDisable(__VA_ARGS__) +#define ti_lib_prcm_power_domain_status(...) PRCMPowerDomainStatus(__VA_ARGS__) +#define ti_lib_prcm_rf_ready(...) PRCMRfReady(__VA_ARGS__) +#define ti_lib_prcm_wdt_reset_status(...) PRCMWdtResetStatus(__VA_ARGS__) +#define ti_lib_prcm_sleep(...) PRCMSleep(__VA_ARGS__) +#define ti_lib_prcm_deep_sleep(...) PRCMDeepSleep(__VA_ARGS__) +#define ti_lib_prcm_retention_enable(...) PRCMRetentionEnable(__VA_ARGS__) +#define ti_lib_prcm_retention_disable(...) PRCMRetentionDisable(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* sys_ctrl.h */ +#include "driverlib/pwr_ctrl.h" + +#define ti_lib_pwr_ctrl_state_set(...) PowerCtrlStateSet(__VA_ARGS__) +#define ti_lib_pwr_ctrl_source_set(...) PowerCtrlSourceSet(__VA_ARGS__) +#define ti_lib_pwr_ctrl_source_get(...) PowerCtrlSourceGet(__VA_ARGS__) +#define ti_lib_pwr_ctrl_io_config_set(...) PowerCtrlIoConfigSet(__VA_ARGS__) +#define ti_lib_pwr_ctrl_reset_source_get(...) PowerCtrlResetSourceGet(__VA_ARGS__) +#define ti_lib_pwr_ctrl_reset_source_clear(...) PowerCtrlResetSourceClear(__VA_ARGS__) +#define ti_lib_pwr_ctrl_io_freeze_enable(...) PowerCtrlIOFreezeEnable(__VA_ARGS__) +#define ti_lib_pwr_ctrl_io_freeze_disable(...) PowerCtrlIOFreezeDisable(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* rom.h */ +#include "driverlib/rom.h" + +/* AON API */ +#define ti_lib_rom_aon_event_mcu_wake_up_set ROM_AONEventMcuWakeUpSet +#define ti_lib_rom_aon_event_mcu_wake_up_get ROM_AONEventMcuWakeUpGet +#define ti_lib_rom_aon_event_aux_wake_up_set ROM_AONEventAuxWakeUpSet +#define ti_lib_rom_aon_event_aux_wake_up_get ROM_AONEventAuxWakeUpGet +#define ti_lib_rom_aon_event_mcu_set ROM_AONEventMcuSet +#define ti_lib_rom_aon_event_mcu_get ROM_AONEventMcuGet + +/* AON_IOC API */ +#define ti_lib_rom_aon_ioc_drive_strength_set ROM_AONIOCDriveStrengthSet +#define ti_lib_rom_aon_ioc_drive_strength_get ROM_AONIOCDriveStrengthGet + +/* AON_RTC API */ +#define ti_lib_rom_aon_rtc_status ROM_AONRTCStatus +#define ti_lib_rom_aon_rtc_event_clear ROM_AONRTCEventClear +#define ti_lib_rom_aon_rtc_event_get ROM_AONRTCEventGet +#define ti_lib_rom_aon_rtc_mode_ch1_set ROM_AONRTCModeCh1Set +#define ti_lib_rom_aon_rtc_mode_ch1_get ROM_AONRTCModeCh1Get +#define ti_lib_rom_aon_rtc_mode_ch2_set ROM_AONRTCModeCh2Set +#define ti_lib_rom_aon_rtc_mode_ch2_get ROM_AONRTCModeCh2Get +#define ti_lib_rom_aon_rtc_channel_enable ROM_AONRTCChannelEnable +#define ti_lib_rom_aon_rtc_channel_disable ROM_AONRTCChannelDisable +#define ti_lib_rom_aon_rtc_compare_value_set ROM_AONRTCCompareValueSet +#define ti_lib_rom_aon_rtc_compare_value_get ROM_AONRTCCompareValueGet +#define ti_lib_rom_aon_rtc_current_compare_value_get ROM_AONRTCCurrentCompareValueGet + +/* AON_WUC API */ +#define ti_lib_rom_aon_wuc_aux_clock_config_set ROM_AONWUCAuxClockConfigSet +#define ti_lib_rom_aon_wuc_aux_s_ram_config ROM_AONWUCAuxSRamConfig +#define ti_lib_rom_aon_wuc_aux_wakeup_event ROM_AONWUCAuxWakeupEvent +#define ti_lib_rom_aon_wuc_aux_reset ROM_AONWUCAuxReset +#define ti_lib_rom_aon_wuc_recharge_ctrl_config_set ROM_AONWUCRechargeCtrlConfigSet +#define ti_lib_rom_aon_wuc_osc_config ROM_AONWUCOscConfig + +/* AUX_TDC API */ +#define ti_lib_rom_aux_tdc_config_set ROM_AUXTDCConfigSet +#define ti_lib_rom_aux_tdc_measurement_done ROM_AUXTDCMeasurementDone + +/* AUX_TIMER API */ +#define ti_lib_rom_aux_timer_configure ROM_AUXTimerConfigure +#define ti_lib_rom_aux_timer_start ROM_AUXTimerStart +#define ti_lib_rom_aux_timer_stop ROM_AUXTimerStop +#define ti_lib_rom_aux_timer_prescale_set ROM_AUXTimerPrescaleSet +#define ti_lib_rom_aux_timer_prescale_get ROM_AUXTimerPrescaleGet + +/* AUX_WUC API */ +#define ti_lib_rom_aux_wuc_clock_enable ROM_AUXWUCClockEnable +#define ti_lib_rom_aux_wuc_clock_disable ROM_AUXWUCClockDisable +#define ti_lib_rom_aux_wuc_clock_status ROM_AUXWUCClockStatus +#define ti_lib_rom_aux_wuc_power_ctrl ROM_AUXWUCPowerCtrl + +/* FLASH API */ +#define ti_lib_rom_flash_power_mode_get ROM_FlashPowerModeGet +#define ti_lib_rom_flash_protection_set ROM_FlashProtectionSet +#define ti_lib_rom_flash_protection_get ROM_FlashProtectionGet +#define ti_lib_rom_flash_protection_save ROM_FlashProtectionSave +#define ti_lib_rom_flash_sector_erase ROM_FlashSectorErase +#define ti_lib_rom_flash_program ROM_FlashProgram +#define ti_lib_rom_flash_program_nowait ROM_FlashProgramNowait + +#define ti_lib_rom_flash_efuse_read_row ROM_FlashEfuseReadRow +#define ti_lib_rom_flash_disable_sectors_for_write ROM_FlashDisableSectorsForWrite + +/* I2C API */ +#define ti_lib_rom_i2c_master_init_exp_clk ROM_I2CMasterInitExpClk +#define ti_lib_rom_i2c_master_err ROM_I2CMasterErr + +/* INTERRUPT API */ +#define ti_lib_rom_int_priority_grouping_set ROM_IntPriorityGroupingSet +#define ti_lib_rom_int_priority_grouping_get ROM_IntPriorityGroupingGet +#define ti_lib_rom_int_priority_set ROM_IntPrioritySet +#define ti_lib_rom_int_priority_get ROM_IntPriorityGet +#define ti_lib_rom_int_enable ROM_IntEnable +#define ti_lib_rom_int_disable ROM_IntDisable +#define ti_lib_rom_int_pend_set ROM_IntPendSet +#define ti_lib_rom_int_pend_get ROM_IntPendGet +#define ti_lib_rom_int_pend_clear ROM_IntPendClear + +/* IOC API */ +#define ti_lib_rom_ioc_port_configure_set ROM_IOCPortConfigureSet +#define ti_lib_rom_ioc_port_configure_get ROM_IOCPortConfigureGet +#define ti_lib_rom_ioc_io_shutdown_set ROM_IOCIOShutdownSet +#define ti_lib_rom_ioc_io_jtag_set ROM_IOCIOJTagSet +#define ti_lib_rom_ioc_io_mode_set ROM_IOCIOModeSet +#define ti_lib_rom_ioc_io_int_set ROM_IOCIOIntSet +#define ti_lib_rom_ioc_io_port_pull_set ROM_IOCIOPortPullSet +#define ti_lib_rom_ioc_io_hyst_set ROM_IOCIOHystSet +#define ti_lib_rom_ioc_io_input_set ROM_IOCIOInputSet +#define ti_lib_rom_ioc_io_slew_ctrl_set ROM_IOCIOSlewCtrlSet +#define ti_lib_rom_ioc_io_drv_strength_set ROM_IOCIODrvStrengthSet +#define ti_lib_rom_ioc_io_port_id_set ROM_IOCIOPortIdSet +#define ti_lib_rom_ioc_int_enable ROM_IOCIntEnable +#define ti_lib_rom_ioc_int_disable ROM_IOCIntDisable +#define ti_lib_rom_ioc_pin_type_gpio_input ROM_IOCPinTypeGpioInput +#define ti_lib_rom_ioc_pin_type_gpio_output ROM_IOCPinTypeGpioOutput +#define ti_lib_rom_ioc_pin_type_uart ROM_IOCPinTypeUart +#define ti_lib_rom_ioc_pin_type_ssi_master ROM_IOCPinTypeSsiMaster +#define ti_lib_rom_ioc_pin_type_ssi_slave ROM_IOCPinTypeSsiSlave +#define ti_lib_rom_ioc_pin_type_i2c ROM_IOCPinTypeI2c +#define ti_lib_rom_ioc_pin_type_spis ROM_IOCPinTypeSpis +#define ti_lib_rom_ioc_pin_type_aux ROM_IOCPinTypeAux + +/* PRCM API */ +#define ti_lib_rom_prcm_inf_clock_configure_set ROM_PRCMInfClockConfigureSet +#define ti_lib_rom_prcm_inf_clock_configure_get ROM_PRCMInfClockConfigureGet +#define ti_lib_rom_prcm_clock_configure_set ROM_PRCMClockConfigureSet +#define ti_lib_rom_prcm_clock_configure_get ROM_PRCMClockConfigureGet +#define ti_lib_rom_prcm_audio_clock_config_set ROM_PRCMAudioClockConfigSet +#define ti_lib_rom_prcm_power_domain_on ROM_PRCMPowerDomainOn +#define ti_lib_rom_prcm_power_domain_off ROM_PRCMPowerDomainOff +#define ti_lib_rom_prcm_peripheral_run_enable ROM_PRCMPeripheralRunEnable +#define ti_lib_rom_prcm_peripheral_run_disable ROM_PRCMPeripheralRunDisable +#define ti_lib_rom_prcm_peripheral_sleep_enable ROM_PRCMPeripheralSleepEnable +#define ti_lib_rom_prcm_peripheral_sleep_disable ROM_PRCMPeripheralSleepDisable +#define ti_lib_rom_prcm_peripheral_deep_sleep_enable ROM_PRCMPeripheralDeepSleepEnable +#define ti_lib_rom_prcm_peripheral_deep_sleep_disable ROM_PRCMPeripheralDeepSleepDisable +#define ti_lib_rom_prcm_power_domain_status ROM_PRCMPowerDomainStatus +#define ti_lib_rom_prcm_deep_sleep ROM_PRCMDeepSleep +#define ti_lib_rom_prcm_retention_enable ROM_PRCMRetentionEnable +#define ti_lib_rom_prcm_retention_disable ROM_PRCMRetentionDisable + +/* SMPH API */ +#define ti_lib_rom_smph_acquire ROM_SMPHAcquire + +/* SPIS API */ +#define ti_lib_rom_spis_data_put ROM_SPISDataPut +#define ti_lib_rom_spis_tx_get_value ROM_SPISTxGetValue +#define ti_lib_rom_spis_data_get ROM_SPISDataGet +#define ti_lib_rom_spis_rx_get_value ROM_SPISRxGetValue +#define ti_lib_rom_spis_int_status ROM_SPISIntStatus + +/* SSI API */ +#define ti_lib_rom_ssi_config_set_exp_clk ROM_SSIConfigSetExpClk +#define ti_lib_rom_ssi_data_put ROM_SSIDataPut +#define ti_lib_rom_ssi_data_put_non_blocking ROM_SSIDataPutNonBlocking +#define ti_lib_rom_ssi_data_get ROM_SSIDataGet +#define ti_lib_rom_ssi_data_get_non_blocking ROM_SSIDataGetNonBlocking + +/* TIMER API */ +#define ti_lib_rom_timer_configure ROM_TimerConfigure +#define ti_lib_rom_timer_level_control ROM_TimerLevelControl +#define ti_lib_rom_timer_trigger_control ROM_TimerTriggerControl +#define ti_lib_rom_timer_stall_control ROM_TimerStallControl +#define ti_lib_rom_timer_wait_on_trigger_control ROM_TimerWaitOnTriggerControl + +/* TRNG API */ +#define ti_lib_rom_trng_configure ROM_TRNGConfigure +#define ti_lib_rom_trng_number_get ROM_TRNGNumberGet + +/* UART API */ +#define ti_lib_rom_uart_fifo_level_get ROM_UARTFIFOLevelGet +#define ti_lib_rom_uart_config_set_exp_clk ROM_UARTConfigSetExpClk +#define ti_lib_rom_uart_config_get_exp_clk ROM_UARTConfigGetExpClk +#define ti_lib_rom_uart_disable ROM_UARTDisable +#define ti_lib_rom_uart_char_get_non_blocking ROM_UARTCharGetNonBlocking +#define ti_lib_rom_uart_char_get ROM_UARTCharGet +#define ti_lib_rom_uart_char_put_non_blocking ROM_UARTCharPutNonBlocking +#define ti_lib_rom_uart_char_put ROM_UARTCharPut + +/* UDMA API */ +#define ti_lib_rom_udma_channel_attribute_enable ROM_uDMAChannelAttributeEnable +#define ti_lib_rom_udma_channel_attribute_disable ROM_uDMAChannelAttributeDisable +#define ti_lib_rom_udma_channel_attribute_get ROM_uDMAChannelAttributeGet +#define ti_lib_rom_udma_channel_control_set ROM_uDMAChannelControlSet +#define ti_lib_rom_udma_channel_transfer_set ROM_uDMAChannelTransferSet +#define ti_lib_rom_udma_channel_scatter_gather_set ROM_uDMAChannelScatterGatherSet +#define ti_lib_rom_udma_channel_size_get ROM_uDMAChannelSizeGet +#define ti_lib_rom_udma_channel_mode_get ROM_uDMAChannelModeGet + +/* VIMS API */ +#define ti_lib_rom_vims_configure ROM_VIMSConfigure +#define ti_lib_rom_vims_mode_set ROM_VIMSModeSet +#define ti_lib_rom_vims_mode_get ROM_VIMSModeGet +/*---------------------------------------------------------------------------*/ +/* sys_ctrl.h */ +#include "driverlib/sys_ctrl.h" + +#define ti_lib_sys_ctrl_power_everything(...) SysCtrlPowerEverything(__VA_ARGS__) +#define ti_lib_sys_ctrl_powerdown(...) SysCtrlPowerdown(__VA_ARGS__) +#define ti_lib_sys_ctrl_standby(...) SysCtrlStandby(__VA_ARGS__) +#define ti_lib_sys_ctrl_shutdown(...) SysCtrlShutdown(__VA_ARGS__) +#define ti_lib_sys_ctrl_clock_get(...) SysCtrlClockGet(__VA_ARGS__) +#define ti_lib_sys_ctrl_peripheral_clock_get(...) SysCtrlPeripheralClockGet(__VA_ARGS__) +#define ti_lib_sys_ctrl_aon_sync(...) SysCtrlAonSync(__VA_ARGS__) +#define ti_lib_sys_ctrl_aon_update(...) SysCtrlAonUpdate(__VA_ARGS__) +#define ti_lib_sys_ctrl_set_recharge_before_power_down(...) SysCtrlSetRechargeBeforePowerDown(__VA_ARGS__) +#define ti_lib_sys_ctrl_adjust_recharge_after_power_down(...) SysCtrlAdjustRechargeAfterPowerDown(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* ssi.h */ +#include "driverlib/ssi.h" + +#define ti_lib_ssi_config_set_exp_clk(...) SSIConfigSetExpClk(__VA_ARGS__) +#define ti_lib_ssi_enable(...) SSIEnable(__VA_ARGS__) +#define ti_lib_ssi_disable(...) SSIDisable(__VA_ARGS__) +#define ti_lib_ssi_data_put(...) SSIDataPut(__VA_ARGS__) +#define ti_lib_ssi_data_put_non_blocking(...) SSIDataPutNonBlocking(__VA_ARGS__) +#define ti_lib_ssi_data_get(...) SSIDataGet(__VA_ARGS__) +#define ti_lib_ssi_data_get_non_blocking(...) SSIDataGetNonBlocking(__VA_ARGS__) +#define ti_lib_ssi_busy(...) SSIBusy(__VA_ARGS__) +#define ti_lib_ssi_status(...) SSIStatus(__VA_ARGS__) +#define ti_lib_ssi_int_register(...) SSIIntRegister(__VA_ARGS__) +#define ti_lib_ssi_int_unregister(...) SSIIntUnregister(__VA_ARGS__) +#define ti_lib_ssi_int_enable(...) SSIIntEnable(__VA_ARGS__) +#define ti_lib_ssi_int_disable(...) SSIIntDisable(__VA_ARGS__) +#define ti_lib_ssi_int_clear(...) SSIIntClear(__VA_ARGS__) +#define ti_lib_ssi_int_status(...) SSIIntStatus(__VA_ARGS__) +#define ti_lib_ssi_dma_enable(...) SSIDMAEnable(__VA_ARGS__) +#define ti_lib_ssi_dma_disable(...) SSIDMADisable(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* systick.h */ +#include "driverlib/systick.h" + +#define ti_lib_systick_enable(...) SysTickEnable(__VA_ARGS__) +#define ti_lib_systick_disable(...) SysTickDisable(__VA_ARGS__) +#define ti_lib_systick_int_register(...) SysTickIntRegister(__VA_ARGS__) +#define ti_lib_systick_int_unregister(...) SysTickIntUnregister(__VA_ARGS__) +#define ti_lib_systick_int_enable(...) SysTickIntEnable(__VA_ARGS__) +#define ti_lib_systick_int_disable(...) SysTickIntDisable(__VA_ARGS__) +#define ti_lib_systick_period_set(...) SysTickPeriodSet(__VA_ARGS__) +#define ti_lib_systick_period_get(...) SysTickPeriodGet(__VA_ARGS__) +#define ti_lib_systick_value_get(...) SysTickValueGet(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* timer.h */ +#include "driverlib/timer.h" + +#define ti_lib_timer_enable(...) TimerEnable(__VA_ARGS__) +#define ti_lib_timer_disable(...) TimerDisable(__VA_ARGS__) +#define ti_lib_timer_configure(...) TimerConfigure(__VA_ARGS__) +#define ti_lib_timer_level_control(...) TimerLevelControl(__VA_ARGS__) +#define ti_lib_timer_trigger_control(...) TimerTriggerControl(__VA_ARGS__) +#define ti_lib_timer_event_control(...) TimerEventControl(__VA_ARGS__) +#define ti_lib_timer_stall_control(...) TimerStallControl(__VA_ARGS__) +#define ti_lib_timer_wait_on_trigger_control(...) TimerWaitOnTriggerControl(__VA_ARGS__) +#define ti_lib_timer_rtc_enable(...) TimerRtcEnable(__VA_ARGS__) +#define ti_lib_timer_rtc_disable(...) TimerRtcDisable(__VA_ARGS__) +#define ti_lib_timer_prescale_set(...) TimerPrescaleSet(__VA_ARGS__) +#define ti_lib_timer_prescale_get(...) TimerPrescaleGet(__VA_ARGS__) +#define ti_lib_timer_prescale_match_set(...) TimerPrescaleMatchSet(__VA_ARGS__) +#define ti_lib_timer_prescale_match_get(...) TimerPrescaleMatchGet(__VA_ARGS__) +#define ti_lib_timer_load_set(...) TimerLoadSet(__VA_ARGS__) +#define ti_lib_timer_load_get(...) TimerLoadGet(__VA_ARGS__) +#define ti_lib_timer_value_get(...) TimerValueGet(__VA_ARGS__) +#define ti_lib_timer_match_set(...) TimerMatchSet(__VA_ARGS__) +#define ti_lib_timer_match_get(...) TimerMatchGet(__VA_ARGS__) +#define ti_lib_timer_int_register(...) TimerIntRegister(__VA_ARGS__) +#define ti_lib_timer_int_unregister(...) TimerIntUnregister(__VA_ARGS__) +#define ti_lib_timer_int_enable(...) TimerIntEnable(__VA_ARGS__) +#define ti_lib_timer_int_disable(...) TimerIntDisable(__VA_ARGS__) +#define ti_lib_timer_int_status(...) TimerIntStatus(__VA_ARGS__) +#define ti_lib_timer_int_clear(...) TimerIntClear(__VA_ARGS__) +#define ti_lib_timer_synchronize(...) TimerSynchronize(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* uart.h */ +#include "driverlib/uart.h" + +#define ti_lib_uart_parity_mode_set(...) UARTParityModeSet(__VA_ARGS__) +#define ti_lib_uart_parity_mode_get(...) UARTParityModeGet(__VA_ARGS__) +#define ti_lib_uart_fifo_level_set(...) UARTFIFOLevelSet(__VA_ARGS__) +#define ti_lib_uart_fifo_level_get(...) UARTFIFOLevelGet(__VA_ARGS__) +#define ti_lib_uart_config_set_exp_clk(...) UARTConfigSetExpClk(__VA_ARGS__) +#define ti_lib_uart_config_get_exp_clk(...) UARTConfigGetExpClk(__VA_ARGS__) +#define ti_lib_uart_enable(...) UARTEnable(__VA_ARGS__) +#define ti_lib_uart_disable(...) UARTDisable(__VA_ARGS__) +#define ti_lib_uart_fifo_enable(...) UARTFIFOEnable(__VA_ARGS__) +#define ti_lib_uart_fifo_disable(...) UARTFIFODisable(__VA_ARGS__) +#define ti_lib_uart_chars_avail(...) UARTCharsAvail(__VA_ARGS__) +#define ti_lib_uart_space_avail(...) UARTSpaceAvail(__VA_ARGS__) +#define ti_lib_uart_char_get_non_blocking(...) UARTCharGetNonBlocking(__VA_ARGS__) +#define ti_lib_uart_char_get(...) UARTCharGet(__VA_ARGS__) +#define ti_lib_uart_char_put_non_blocking(...) UARTCharPutNonBlocking(__VA_ARGS__) +#define ti_lib_uart_char_put(...) UARTCharPut(__VA_ARGS__) +#define ti_lib_uart_break_ctl(...) UARTBreakCtl(__VA_ARGS__) +#define ti_lib_uart_busy(...) UARTBusy(__VA_ARGS__) +#define ti_lib_uart_int_register(...) UARTIntRegister(__VA_ARGS__) +#define ti_lib_uart_int_unregister(...) UARTIntUnregister(__VA_ARGS__) +#define ti_lib_uart_int_enable(...) UARTIntEnable(__VA_ARGS__) +#define ti_lib_uart_int_disable(...) UARTIntDisable(__VA_ARGS__) +#define ti_lib_uart_int_status(...) UARTIntStatus(__VA_ARGS__) +#define ti_lib_uart_int_clear(...) UARTIntClear(__VA_ARGS__) +#define ti_lib_uart_dma_enable(...) UARTDMAEnable(__VA_ARGS__) +#define ti_lib_uart_dma_disable(...) UARTDMADisable(__VA_ARGS__) +#define ti_lib_uart_rx_error_get(...) UARTRxErrorGet(__VA_ARGS__) +#define ti_lib_uart_rx_error_clear(...) UARTRxErrorClear(__VA_ARGS__) +#define ti_lib_uart_tx_int_mode_set(...) UARTTxIntModeSet(__VA_ARGS__) +#define ti_lib_uart_tx_int_mode_get(...) UARTTxIntModeGet(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* vims.h */ +#include "driverlib/vims.h" + +#define ti_lib_vims_configure(...) VIMSConfigure(__VA_ARGS__) +#define ti_lib_vims_mode_set(...) VIMSModeSet(__VA_ARGS__) +#define ti_lib_vims_mode_get(...) VIMSModeGet(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +/* watchdog.h */ +#include "driverlib/watchdog.h" + +#define ti_lib_wathdog_running(...) WatchdogRunning(__VA_ARGS__) +#define ti_lib_wathdog_enable(...) WatchdogEnable(__VA_ARGS__) +#define ti_lib_wathdog_reset_enable(...) WatchdogResetEnable(__VA_ARGS__) +#define ti_lib_wathdog_reset_disable(...) WatchdogResetDisable(__VA_ARGS__) +#define ti_lib_wathdog_lock(...) WatchdogLock(__VA_ARGS__) +#define ti_lib_wathdog_unlock(...) WatchdogUnlock(__VA_ARGS__) +#define ti_lib_wathdog_lock_state(...) WatchdogLockState(__VA_ARGS__) +#define ti_lib_wathdog_reload_set(...) WatchdogReloadSet(__VA_ARGS__) +#define ti_lib_wathdog_reload_get(...) WatchdogReloadGet(__VA_ARGS__) +#define ti_lib_wathdog_value_get(...) WatchdogValueGet(__VA_ARGS__) +#define ti_lib_wathdog_int_register(...) WatchdogIntRegister(__VA_ARGS__) +#define ti_lib_wathdog_int_unregister(...) WatchdogIntUnregister(__VA_ARGS__) +#define ti_lib_wathdog_int_enable(...) WatchdogIntEnable(__VA_ARGS__) +#define ti_lib_wathdog_int_status(...) WatchdogIntStatus(__VA_ARGS__) +#define ti_lib_wathdog_int_clear(...) WatchdogIntClear(__VA_ARGS__) +#define ti_lib_wathdog_int_type_set(...) WatchdogIntTypeSet(__VA_ARGS__) +#define ti_lib_wathdog_stall_enable(...) WatchdogStallEnable(__VA_ARGS__) +#define ti_lib_wathdog_stall_disable(...) WatchdogStallDisable(__VA_ARGS__) +/*---------------------------------------------------------------------------*/ +#endif /* TI_LIB_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ From c7d85c3ea55f41bc8aafc4f99c1d7e3202b4b48e Mon Sep 17 00:00:00 2001 From: Jonas Olsson Date: Wed, 25 Feb 2015 13:10:26 +0100 Subject: [PATCH 2/7] Add Srf06EB + CC26xx EM files --- platform/srf06-cc26xx/srf06/Makefile.srf06 | 5 + .../srf06-cc26xx/srf06/board-peripherals.h | 49 ++ platform/srf06-cc26xx/srf06/board.c | 143 +++++ platform/srf06-cc26xx/srf06/board.h | 249 +++++++++ platform/srf06-cc26xx/srf06/button-sensor.c | 492 ++++++++++++++++++ platform/srf06-cc26xx/srf06/button-sensor.h | 61 +++ platform/srf06-cc26xx/srf06/leds-arch.c | 90 ++++ platform/srf06-cc26xx/srf06/srf06-sensors.c | 49 ++ 8 files changed, 1138 insertions(+) create mode 100644 platform/srf06-cc26xx/srf06/Makefile.srf06 create mode 100644 platform/srf06-cc26xx/srf06/board-peripherals.h create mode 100644 platform/srf06-cc26xx/srf06/board.c create mode 100644 platform/srf06-cc26xx/srf06/board.h create mode 100644 platform/srf06-cc26xx/srf06/button-sensor.c create mode 100644 platform/srf06-cc26xx/srf06/button-sensor.h create mode 100644 platform/srf06-cc26xx/srf06/leds-arch.c create mode 100644 platform/srf06-cc26xx/srf06/srf06-sensors.c diff --git a/platform/srf06-cc26xx/srf06/Makefile.srf06 b/platform/srf06-cc26xx/srf06/Makefile.srf06 new file mode 100644 index 000000000..7d64aac94 --- /dev/null +++ b/platform/srf06-cc26xx/srf06/Makefile.srf06 @@ -0,0 +1,5 @@ +CFLAGS += -DBOARD_SMARTRF06EB=1 + +BOARD_SOURCEFILES += leds-arch.c srf06-sensors.c button-sensor.c board.c + +CONTIKI_TARGET_DIRS += $(BOARD)/bsp diff --git a/platform/srf06-cc26xx/srf06/board-peripherals.h b/platform/srf06-cc26xx/srf06/board-peripherals.h new file mode 100644 index 000000000..def762b56 --- /dev/null +++ b/platform/srf06-cc26xx/srf06/board-peripherals.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** \addtogroup srf06-cc26xx-peripherals + * @{ + * + * \file + * Header file with definitions related to the Srf06EB peripherals + * + * \note Do not include this file directly. + */ +/*---------------------------------------------------------------------------*/ +#ifndef BOARD_PERIPHERALS_H_ +#define BOARD_PERIPHERALS_H_ +/*---------------------------------------------------------------------------*/ +/* ToDo: Include things here */ +/*---------------------------------------------------------------------------*/ +#endif /* BOARD_PERIPHERALS_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/platform/srf06-cc26xx/srf06/board.c b/platform/srf06-cc26xx/srf06/board.c new file mode 100644 index 000000000..b62f3fc24 --- /dev/null +++ b/platform/srf06-cc26xx/srf06/board.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \file + * Board-initialisation for the Srf06EB with a CC26xx EM. + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "ti-lib.h" +#include "lpm.h" +#include "prcm.h" +#include "hw_sysctl.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +#define PRCM_DOMAINS (PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_SERIAL | \ + PRCM_DOMAIN_PERIPH | PRCM_DOMAIN_CPU | \ + PRCM_DOMAIN_SYSBUS | PRCM_DOMAIN_VIMS) +/*---------------------------------------------------------------------------*/ +#define LPM_DOMAINS (PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH) +/*---------------------------------------------------------------------------*/ +static void +power_domains_on(void) +{ + /* Turn on relevant power domains */ + ti_lib_prcm_power_domain_on(LPM_DOMAINS); + + /* Wait for domains to power on */ + while((ti_lib_prcm_power_domain_status(LPM_DOMAINS) + != PRCM_DOMAIN_POWER_ON)); +} +/*---------------------------------------------------------------------------*/ +static void +lpm_wakeup_handler(void) +{ + power_domains_on(); +} +/*---------------------------------------------------------------------------*/ +/* + * Declare a data structure to register with LPM. + * We don't care about what power mode we'll drop to, we don't care about + * getting notified before deep sleep. All we need is to be notified when we + * wake up so we can turn power domains back on + */ +LPM_MODULE(srf_module, NULL, NULL, lpm_wakeup_handler); +/*---------------------------------------------------------------------------*/ +void +board_init() +{ + uint8_t int_disabled = ti_lib_int_master_disable(); + + /* Turn on all power domains */ + ti_lib_prcm_power_domain_on(PRCM_DOMAINS); + + /* Wait for power on domains */ + while((ti_lib_prcm_power_domain_status(PRCM_DOMAINS) + != PRCM_DOMAIN_POWER_ON)); + + /* Configure all clock domains to run at full speed */ + ti_lib_prcm_clock_configure_set(PRCM_DOMAIN_SYSBUS | PRCM_DOMAIN_CPU | + PRCM_DOMAIN_CPU | PRCM_DOMAIN_TIMER | + PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH, + PRCM_CLOCK_DIV_1); + + /* Enable GPIO peripheral */ + ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_GPIO); + + /* Apply settings and wait for them to take effect */ + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()) ; + + /* Keys (input pullup) */ + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_UP); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_DOWN); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_LEFT); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_RIGHT); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_KEY_SELECT); + + /* Turn off 3.3V domain (Powers the LCD and SD card reader): Output, low */ + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_3V3_EN); + ti_lib_gpio_pin_write(BOARD_3V3_EN, 0); + + /* LCD CSn (output high) */ + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_LCD_CS); + ti_lib_gpio_pin_write(BOARD_LCD_CS, 1); + + /* SD Card reader CSn (output high) */ + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_SDCARD_CS); + ti_lib_gpio_pin_write(BOARD_SDCARD_CS, 1); + + /* Accelerometer (PWR output low, CSn output high) */ + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ACC_PWR); + ti_lib_gpio_pin_write(BOARD_ACC_PWR, 0); + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ACC_CS); + ti_lib_gpio_pin_write(BOARD_IOID_ACC_CS, 1); + + /* Ambient light sensor (off, output low) */ + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_ALS_PWR); + ti_lib_gpio_pin_write(BOARD_ALS_PWR, 0); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_ALS_OUT); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_ALS_OUT, IOC_NO_IOPULL); + + lpm_register_module(&srf_module); + + /* Re-enable interrupt if initially enabled. */ + if(!int_disabled) { + ti_lib_int_master_enable(); + } +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/srf06/board.h b/platform/srf06-cc26xx/srf06/board.h new file mode 100644 index 000000000..42c060b9b --- /dev/null +++ b/platform/srf06-cc26xx/srf06/board.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** \addtogroup cc26xx-srf-tag + * @{ + * + * \defgroup srf06-cc26xx-peripherals Peripherals for the SmartRF06EB + CC26xxEM + * + * Defines related to the SmartRF06 Evaluation Board with a CC26xxEM + * + * This file provides connectivity information on LEDs, Buttons, UART and + * other peripherals + * + * This file can be used as the basis to configure other boards using the + * CC26xx code as their basis. + * + * This file is not meant to be modified by the user. + * @{ + * + * \file + * Header file with definitions related to the I/O connections on the TI + * SmartRF06 Evaluation Board with a CC26xxEM + * + * \note Do not include this file directly. It gets included by contiki-conf + * after all relevant directives have been set. + */ +/*---------------------------------------------------------------------------*/ +#ifndef BOARD_H_ +#define BOARD_H_ +/*---------------------------------------------------------------------------*/ +#include "ioc.h" +/*---------------------------------------------------------------------------*/ +/** + * \name LED configurations + * + * Those values are not meant to be modified by the user + * @{ + */ +/* Some files include leds.h before us, so we need to get rid of defaults in + * leds.h before we provide correct definitions */ +#undef LEDS_GREEN +#undef LEDS_YELLOW +#undef LEDS_RED +#undef LEDS_CONF_ALL + +#define LEDS_RED 1 /**< LED1 (Red) */ +#define LEDS_YELLOW 2 /**< LED2 (Yellow) */ +#define LEDS_GREEN 4 /**< LED3 (Green) */ +#define LEDS_ORANGE 8 /**< LED4 (Orange) */ + +#define LEDS_CONF_ALL 15 + +/* Notify various examples that we have LEDs */ +#define PLATFORM_HAS_LEDS 1 +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name LED IOID mappings + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_LED_1 IOID_25 +#define BOARD_IOID_LED_2 IOID_27 +#define BOARD_IOID_LED_3 IOID_7 +#define BOARD_IOID_LED_4 IOID_6 +#define BOARD_LED_1 (1 << BOARD_IOID_LED_1) +#define BOARD_LED_2 (1 << BOARD_IOID_LED_2) +#define BOARD_LED_3 (1 << BOARD_IOID_LED_3) +#define BOARD_LED_4 (1 << BOARD_IOID_LED_4) +#define BOARD_LED_ALL (BOARD_LED_1 | BOARD_LED_2 | BOARD_LED_3 | \ + BOARD_LED_4) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name UART IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_UART_RX IOID_2 +#define BOARD_IOID_UART_TX IOID_3 +#define BOARD_IOID_UART_CTS IOID_0 +#define BOARD_IOID_UART_RTS IOID_21 +#define BOARD_UART_RX (1 << BOARD_IOID_UART_RX) +#define BOARD_UART_TX (1 << BOARD_IOID_UART_TX) +#define BOARD_UART_CTS (1 << BOARD_IOID_UART_CTS) +#define BOARD_UART_RTS (1 << BOARD_IOID_UART_RTS) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Button IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_KEY_LEFT IOID_15 +#define BOARD_IOID_KEY_RIGHT IOID_18 +#define BOARD_IOID_KEY_UP IOID_19 +#define BOARD_IOID_KEY_DOWN IOID_12 +#define BOARD_IOID_KEY_SELECT IOID_11 +#define BOARD_KEY_LEFT (1 << BOARD_IOID_KEY_LEFT) +#define BOARD_KEY_RIGHT (1 << BOARD_IOID_KEY_RIGHT) +#define BOARD_KEY_UP (1 << BOARD_IOID_KEY_UP) +#define BOARD_KEY_DOWN (1 << BOARD_IOID_KEY_DOWN) +#define BOARD_KEY_SELECT (1 << BOARD_IOID_KEY_SELECT) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name 3.3V domain IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_3V3_EN IOID_13 +#define BOARD_3V3_EN (1 << BOARD_IOID_3V3_EN) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name SPI IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_SPI_SCK IOID_10 +#define BOARD_IOID_SPI_MOSI IOID_9 +#define BOARD_IOID_SPI_MISO IOID_8 +#define BOARD_SPI_SCK (1 << BOARD_IOID_SPI_SCK) +#define BOARD_SPI_MOSI (1 << BOARD_IOID_SPI_MOSI) +#define BOARD_SPI_MISO (1 << BOARD_IOID_SPI_MISO) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name LCD IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_LCD_MODE IOID_4 +#define BOARD_IOID_LCD_RST IOID_5 +#define BOARD_IOID_LCD_CS IOID_14 +#define BOARD_IOID_LCD_SCK BOARD_IOID_SPI_SCK +#define BOARD_IOID_LCD_MOSI BOARD_IOID_SPI_MOSI +#define BOARD_LCD_MODE (1 << BOARD_IOID_LCD_MODE) +#define BOARD_LCD_RST (1 << BOARD_IOID_LCD_RST) +#define BOARD_LCD_CS (1 << BOARD_IOID_LCD_CS) +#define BOARD_LCD_SCK BOARD_SPI_SCK +#define BOARD_LCD_MOSI BOARD_SPI_MOSI +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name SD Card IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_SDCARD_CS IOID_30 +#define BOARD_SDCARD_CS (1 << BOARD_IOID_SDCARD_CS) +#define BOARD_IOID_SDCARD_SCK BOARD_IOID_SPI_SCK +#define BOARD_SDCARD_SCK BOARD_SPI_SCK +#define BOARD_IOID_SDCARD_MOSI BOARD_IOID_SPI_MOSI +#define BOARD_SDCARD_MOSI BOARD_SPI_MOSI +#define BOARD_IOID_SDCARD_MISO BOARD_IOID_SPI_MISO +#define BOARD_SDCARD_MISO BOARD_SPI_MISO +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name ALS IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_ALS_PWR IOID_26 +#define BOARD_IOID_ALS_OUT IOID_23 +#define BOARD_ALS_PWR (1 << BOARD_IOID_ALS_PWR) +#define BOARD_ALS_OUT (1 << BOARD_IOID_ALS_OUT) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name ACC IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_ACC_PWR IOID_20 +#define BOARD_IOID_ACC_INT IOID_28 +#define BOARD_IOID_ACC_INT1 IOID_28 +#define BOARD_IOID_ACC_INT2 IOID_29 +#define BOARD_IOID_ACC_CS IOID_24 +#define BOARD_ACC_PWR (1 << BOARD_IOID_ACC_PWR) +#define BOARD_ACC_INT (1 << BOARD_IOID_ACC_INT) +#define BOARD_ACC_INT1 (1 << BOARD_IOID_ACC_INT1) +#define BOARD_ACC_INT2 (1 << BOARD_IOID_ACC_INT2) +#define BOARD_ACC_CS (1 << BOARD_IOID_ACC_CS) +#define BOARD_IOID_ACC_SCK BOARD_IOID_SPI_SCK +#define BOARD_ACC_SCK BOARD_SPI_SCK +#define BOARD_IOID_ACC_MOSI BOARD_IOID_SPI_MOSI +#define BOARD_ACC_MOSI BOARD_SPI_MOSI +#define BOARD_IOID_ACC_MISO BOARD_IOID_SPI_MISO +#define BOARD_ACC_MISO BOARD_SPI_MISO +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Device string used on startup + * @{ + */ +#define BOARD_STRING "TI SmartRF06EB+CC26xx EM" +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \brief Board specific iniatialisation + * @{ + */ +void board_init(void); +/** @} */ +/*---------------------------------------------------------------------------*/ +#endif /* BOARD_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/srf06/button-sensor.c b/platform/srf06-cc26xx/srf06/button-sensor.c new file mode 100644 index 000000000..5c6dae6f1 --- /dev/null +++ b/platform/srf06-cc26xx/srf06/button-sensor.c @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup srf06-cc26xx-peripherals + * @{ + * + * \file + * Driver for the SmartRF06EB buttons when a CC26xxEM is mounted on the board + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/sensors.h" +#include "srf06/button-sensor.h" +#include "gpio-interrupt.h" +#include "sys/timer.h" +#include "lpm.h" + +#include "ti-lib.h" + +#include +/*---------------------------------------------------------------------------*/ +#ifdef BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN +#define BUTTON_SENSOR_ENABLE_SHUTDOWN BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN +#else +#define BUTTON_SENSOR_ENABLE_SHUTDOWN 1 +#endif +/*---------------------------------------------------------------------------*/ +#define BUTTON_GPIO_CFG (IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | \ + IOC_IOPULL_UP | IOC_SLEW_DISABLE | \ + IOC_HYST_DISABLE | IOC_BOTH_EDGES | \ + IOC_INT_ENABLE | IOC_IOMODE_NORMAL | \ + IOC_NO_WAKE_UP | IOC_INPUT_ENABLE | \ + IOC_JTAG_DISABLE) +/*---------------------------------------------------------------------------*/ +#define DEBOUNCE_DURATION (CLOCK_SECOND >> 5) + +struct btn_timer { + struct timer debounce; + clock_time_t start; + clock_time_t duration; +}; + +static struct btn_timer sel_timer, left_timer, right_timer, up_timer, + down_timer; +/*---------------------------------------------------------------------------*/ +/** + * \brief Handler for SmartRF button presses + */ +static void +button_press_handler(uint8_t ioid) +{ + if(ioid == BOARD_IOID_KEY_SELECT) { + if(!timer_expired(&sel_timer.debounce)) { + return; + } + + timer_set(&sel_timer.debounce, DEBOUNCE_DURATION); + + /* + * Start press duration counter on press (falling), notify on release + * (rising) + */ + if(ti_lib_gpio_pin_read(BOARD_KEY_SELECT) == 0) { + sel_timer.start = clock_time(); + sel_timer.duration = 0; + } else { + sel_timer.duration = clock_time() - sel_timer.start; + sensors_changed(&button_select_sensor); + } + } + + if(ioid == BOARD_IOID_KEY_LEFT) { + if(!timer_expired(&left_timer.debounce)) { + return; + } + + timer_set(&left_timer.debounce, DEBOUNCE_DURATION); + + /* + * Start press duration counter on press (falling), notify on release + * (rising) + */ + if(ti_lib_gpio_pin_read(BOARD_KEY_LEFT) == 0) { + left_timer.start = clock_time(); + left_timer.duration = 0; + } else { + left_timer.duration = clock_time() - left_timer.start; + sensors_changed(&button_left_sensor); + } + } + + if(ioid == BOARD_IOID_KEY_RIGHT) { + if(BUTTON_SENSOR_ENABLE_SHUTDOWN == 0) { + if(!timer_expired(&right_timer.debounce)) { + return; + } + + timer_set(&right_timer.debounce, DEBOUNCE_DURATION); + + /* + * Start press duration counter on press (falling), notify on release + * (rising) + */ + if(ti_lib_gpio_pin_read(BOARD_KEY_RIGHT) == 0) { + right_timer.start = clock_time(); + right_timer.duration = 0; + } else { + right_timer.duration = clock_time() - right_timer.start; + sensors_changed(&button_right_sensor); + } + } else { + lpm_shutdown(BOARD_IOID_KEY_RIGHT); + } + } + + if(ioid == BOARD_IOID_KEY_UP) { + if(!timer_expired(&up_timer.debounce)) { + return; + } + + timer_set(&up_timer.debounce, DEBOUNCE_DURATION); + + /* + * Start press duration counter on press (falling), notify on release + * (rising) + */ + if(ti_lib_gpio_pin_read(BOARD_KEY_UP) == 0) { + up_timer.start = clock_time(); + up_timer.duration = 0; + } else { + up_timer.duration = clock_time() - up_timer.start; + sensors_changed(&button_up_sensor); + } + } + + if(ioid == BOARD_IOID_KEY_DOWN) { + if(!timer_expired(&down_timer.debounce)) { + return; + } + + timer_set(&down_timer.debounce, DEBOUNCE_DURATION); + + /* + * Start press duration counter on press (falling), notify on release + * (rising) + */ + if(ti_lib_gpio_pin_read(BOARD_KEY_DOWN) == 0) { + down_timer.start = clock_time(); + down_timer.duration = 0; + } else { + down_timer.duration = clock_time() - down_timer.start; + sensors_changed(&button_down_sensor); + } + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the button sensor for all buttons. + * + * \param type This function does nothing unless type == SENSORS_ACTIVE + * \param c 0: disable the button, non-zero: enable + * \param key: One of BOARD_KEY_LEFT, BOARD_KEY_RIGHT etc + */ +static void +config_buttons(int type, int c, uint32_t key) +{ + switch(type) { + case SENSORS_HW_INIT: + ti_lib_gpio_event_clear(1 << key); + ti_lib_ioc_port_configure_set(key, IOC_PORT_GPIO, BUTTON_GPIO_CFG); + ti_lib_gpio_dir_mode_set((1 << key), GPIO_DIR_MODE_IN); + gpio_interrupt_register_handler(key, button_press_handler); + break; + case SENSORS_ACTIVE: + if(c) { + ti_lib_gpio_event_clear(1 << key); + ti_lib_ioc_port_configure_set(key, IOC_PORT_GPIO, BUTTON_GPIO_CFG); + ti_lib_gpio_dir_mode_set((1 << key), GPIO_DIR_MODE_IN); + ti_lib_ioc_int_enable(key); + } else { + ti_lib_ioc_int_disable(key); + } + break; + default: + break; + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the select button. + * + * Parameters are passed onto config_buttons, which does the actual + * configuration + * Parameters are ignored. They have been included because the prototype is + * dictated by the core sensor api. The return value is also required by + * the API but otherwise ignored. + * + * \param type passed to config_buttons as-is + * \param value passed to config_buttons as-is + * + * \return ignored + */ +static int +config_select(int type, int value) +{ + config_buttons(type, value, BOARD_IOID_KEY_SELECT); + + return 1; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the left button. + * + * Parameters are passed onto config_buttons, which does the actual + * configuration + * Parameters are ignored. They have been included because the prototype is + * dictated by the core sensor api. The return value is also required by + * the API but otherwise ignored. + * + * \param type passed to config_buttons as-is + * \param value passed to config_buttons as-is + * + * \return ignored + */ +static int +config_left(int type, int value) +{ + config_buttons(type, value, BOARD_IOID_KEY_LEFT); + + return 1; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the right button. + * + * Parameters are passed onto config_buttons, which does the actual + * configuration + * Parameters are ignored. They have been included because the prototype is + * dictated by the core sensor api. The return value is also required by + * the API but otherwise ignored. + * + * \param type passed to config_buttons as-is + * \param value passed to config_buttons as-is + * + * \return ignored + */ +static int +config_right(int type, int value) +{ + config_buttons(type, value, BOARD_IOID_KEY_RIGHT); + + return 1; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the up button. + * + * Parameters are passed onto config_buttons, which does the actual + * configuration + * Parameters are ignored. They have been included because the prototype is + * dictated by the core sensor api. The return value is also required by + * the API but otherwise ignored. + * + * \param type passed to config_buttons as-is + * \param value passed to config_buttons as-is + * + * \return ignored + */ +static int +config_up(int type, int value) +{ + config_buttons(type, value, BOARD_IOID_KEY_UP); + + return 1; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the down button. + * + * Parameters are passed onto config_buttons, which does the actual + * configuration + * Parameters are ignored. They have been included because the prototype is + * dictated by the core sensor api. The return value is also required by + * the API but otherwise ignored. + * + * \param type passed to config_buttons as-is + * \param value passed to config_buttons as-is + * + * \return ignored + */ +static int +config_down(int type, int value) +{ + config_buttons(type, value, BOARD_IOID_KEY_DOWN); + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +value_select(int type) +{ + if(type == BUTTON_SENSOR_VALUE_STATE) { + return ti_lib_gpio_pin_read(BOARD_KEY_SELECT) == 0 ? + BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED; + } else if(type == BUTTON_SENSOR_VALUE_DURATION) { + return (int)sel_timer.duration; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +value_left(int type) +{ + if(type == BUTTON_SENSOR_VALUE_STATE) { + return ti_lib_gpio_pin_read(BOARD_KEY_LEFT) == 0 ? + BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED; + } else if(type == BUTTON_SENSOR_VALUE_DURATION) { + return (int)left_timer.duration; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +value_right(int type) +{ + if(type == BUTTON_SENSOR_VALUE_STATE) { + return ti_lib_gpio_pin_read(BOARD_KEY_RIGHT) == 0 ? + BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED; + } else if(type == BUTTON_SENSOR_VALUE_DURATION) { + return (int)right_timer.duration; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +value_up(int type) +{ + if(type == BUTTON_SENSOR_VALUE_STATE) { + return ti_lib_gpio_pin_read(BOARD_KEY_UP) == 0 ? + BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED; + } else if(type == BUTTON_SENSOR_VALUE_DURATION) { + return (int)up_timer.duration; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +value_down(int type) +{ + if(type == BUTTON_SENSOR_VALUE_STATE) { + return ti_lib_gpio_pin_read(BOARD_KEY_DOWN) == 0 ? + BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED; + } else if(type == BUTTON_SENSOR_VALUE_DURATION) { + return (int)down_timer.duration; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for all buttons + * \param type SENSORS_ACTIVE or SENSORS_READY + * \param key_io_id BOARD_IOID_KEY_LEFT, BOARD_IOID_KEY_RIGHT etc + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will only be called by status_left, status_right and the + * called will pass the correct key_io_id + */ +static int +status(int type, uint32_t key_io_id) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + if(ti_lib_ioc_port_configure_get(key_io_id) & IOC_INT_ENABLE) { + return 1; + } + break; + default: + break; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for the select button. + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will call status. It will pass type verbatim and it will also + * pass the correct key_io_id + */ +static int +status_select(int type) +{ + return status(type, BOARD_IOID_KEY_SELECT); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for the left button. + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will call status. It will pass type verbatim and it will also + * pass the correct key_io_id + */ +static int +status_left(int type) +{ + return status(type, BOARD_IOID_KEY_LEFT); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for the right button. + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will call status. It will pass type verbatim and it will also + * pass the correct key_io_id + */ +static int +status_right(int type) +{ + return status(type, BOARD_IOID_KEY_RIGHT); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for the up button. + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will call status. It will pass type verbatim and it will also + * pass the correct key_io_id + */ +static int +status_up(int type) +{ + return status(type, BOARD_IOID_KEY_UP); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for the down button. + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will call status. It will pass type verbatim and it will also + * pass the correct key_io_id + */ +static int +status_down(int type) +{ + return status(type, BOARD_IOID_KEY_DOWN); +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(button_select_sensor, BUTTON_SENSOR, value_select, + config_select, status_select); +SENSORS_SENSOR(button_left_sensor, BUTTON_SENSOR, value_left, config_left, + status_left); +SENSORS_SENSOR(button_right_sensor, BUTTON_SENSOR, value_right, config_right, + status_right); +SENSORS_SENSOR(button_up_sensor, BUTTON_SENSOR, value_up, config_up, status_up); +SENSORS_SENSOR(button_down_sensor, BUTTON_SENSOR, value_down, config_down, + status_down); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/srf06/button-sensor.h b/platform/srf06-cc26xx/srf06/button-sensor.h new file mode 100644 index 000000000..1f5752059 --- /dev/null +++ b/platform/srf06-cc26xx/srf06/button-sensor.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup srf06-cc26xx-peripherals + * @{ + * + * \file + * Header file for the SmartRF06EB + CC26xxEM Button Driver + */ +/*---------------------------------------------------------------------------*/ +#ifndef BUTTON_SENSOR_H_ +#define BUTTON_SENSOR_H_ +/*---------------------------------------------------------------------------*/ +#include "lib/sensors.h" +/*---------------------------------------------------------------------------*/ +#define BUTTON_SENSOR "Button" +/*---------------------------------------------------------------------------*/ +#define BUTTON_SENSOR_VALUE_STATE 0 +#define BUTTON_SENSOR_VALUE_DURATION 1 + +#define BUTTON_SENSOR_VALUE_RELEASED 0 +#define BUTTON_SENSOR_VALUE_PRESSED 1 +/*---------------------------------------------------------------------------*/ +#define button_sensor button_select_sensor +extern const struct sensors_sensor button_select_sensor; +extern const struct sensors_sensor button_left_sensor; +extern const struct sensors_sensor button_right_sensor; +extern const struct sensors_sensor button_up_sensor; +extern const struct sensors_sensor button_down_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* BUTTON_SENSOR_H_ */ +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/srf06/leds-arch.c b/platform/srf06-cc26xx/srf06/leds-arch.c new file mode 100644 index 000000000..ce1c29c50 --- /dev/null +++ b/platform/srf06-cc26xx/srf06/leds-arch.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup srf06-cc26xx-peripherals + * @{ + * + * \file + * Driver for the SmartRF06EB LEDs when a CC26xx is mounted on the board + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/leds.h" +#include "ti-lib.h" +/*---------------------------------------------------------------------------*/ +static unsigned char c; +static int inited = 0; +/*---------------------------------------------------------------------------*/ +void +leds_arch_init(void) +{ + if(inited) { + return; + } + inited = 1; + + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_LED_1); + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_LED_2); + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_LED_3); + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_LED_4); + + ti_lib_gpio_pin_write(BOARD_LED_ALL, 0); +} +/*---------------------------------------------------------------------------*/ +unsigned char +leds_arch_get(void) +{ + return c; +} +/*---------------------------------------------------------------------------*/ +void +leds_arch_set(unsigned char leds) +{ + c = leds; + + /* Clear everything */ + ti_lib_gpio_pin_write(BOARD_LED_ALL, 0); + + if((leds & LEDS_RED) == LEDS_RED) { + ti_lib_gpio_pin_write(BOARD_LED_1, 1); + } + if((leds & LEDS_YELLOW) == LEDS_YELLOW) { + ti_lib_gpio_pin_write(BOARD_LED_2, 1); + } + if((leds & LEDS_GREEN) == LEDS_GREEN) { + ti_lib_gpio_pin_write(BOARD_LED_3, 1); + } + if((leds & LEDS_ORANGE) == LEDS_ORANGE) { + ti_lib_gpio_pin_write(BOARD_LED_4, 1); + } +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/srf06/srf06-sensors.c b/platform/srf06-cc26xx/srf06/srf06-sensors.c new file mode 100644 index 000000000..c69b4c22f --- /dev/null +++ b/platform/srf06-cc26xx/srf06/srf06-sensors.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup srf06-cc26xx-peripherals + * @{ + * + * \file + * Generic module controlling sensors on the SmartRF06EB when a CC26xx is + * mounted on the board + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "srf06/button-sensor.h" + +#include +/*---------------------------------------------------------------------------*/ +/** \brief Exports a global symbol to be used by the sensor API */ +SENSORS(&button_select_sensor, &button_left_sensor, &button_right_sensor, + &button_up_sensor, &button_down_sensor); +/*---------------------------------------------------------------------------*/ +/** @} */ From b385933fa811443310b978370d1c2947cbd8688b Mon Sep 17 00:00:00 2001 From: Jonas Olsson Date: Wed, 25 Feb 2015 13:11:09 +0100 Subject: [PATCH 3/7] Add SensorTag CC2650 files --- .../srf06-cc26xx/sensortag/Makefile.sensortag | 8 + .../srf06-cc26xx/sensortag/bmp-280-sensor.c | 391 ++++++++++ .../srf06-cc26xx/sensortag/bmp-280-sensor.h | 70 ++ platform/srf06-cc26xx/sensortag/board-i2c.c | 269 +++++++ platform/srf06-cc26xx/sensortag/board-i2c.h | 108 +++ .../sensortag/board-peripherals.h | 56 ++ platform/srf06-cc26xx/sensortag/board-spi.c | 118 +++ platform/srf06-cc26xx/sensortag/board-spi.h | 90 +++ platform/srf06-cc26xx/sensortag/board.c | 152 ++++ platform/srf06-cc26xx/sensortag/board.h | 206 ++++++ .../srf06-cc26xx/sensortag/button-sensor.c | 282 +++++++ .../srf06-cc26xx/sensortag/button-sensor.h | 66 ++ platform/srf06-cc26xx/sensortag/buzzer.c | 124 ++++ platform/srf06-cc26xx/sensortag/buzzer.h | 74 ++ platform/srf06-cc26xx/sensortag/ext-flash.c | 409 +++++++++++ platform/srf06-cc26xx/sensortag/ext-flash.h | 103 +++ platform/srf06-cc26xx/sensortag/leds-arch.c | 81 +++ .../srf06-cc26xx/sensortag/mpu-9250-sensor.c | 685 ++++++++++++++++++ .../srf06-cc26xx/sensortag/mpu-9250-sensor.h | 112 +++ .../srf06-cc26xx/sensortag/opt-3001-sensor.c | 273 +++++++ .../srf06-cc26xx/sensortag/opt-3001-sensor.h | 67 ++ platform/srf06-cc26xx/sensortag/reed-relay.c | 140 ++++ platform/srf06-cc26xx/sensortag/reed-relay.h | 59 ++ .../srf06-cc26xx/sensortag/sensor-common.c | 79 ++ .../srf06-cc26xx/sensortag/sensor-common.h | 85 +++ .../sensortag/sensortag-sensors.c | 55 ++ .../srf06-cc26xx/sensortag/sht-21-sensor.c | 386 ++++++++++ .../srf06-cc26xx/sensortag/sht-21-sensor.h | 85 +++ .../srf06-cc26xx/sensortag/tmp-007-sensor.c | 315 ++++++++ .../srf06-cc26xx/sensortag/tmp-007-sensor.h | 76 ++ 30 files changed, 5024 insertions(+) create mode 100644 platform/srf06-cc26xx/sensortag/Makefile.sensortag create mode 100644 platform/srf06-cc26xx/sensortag/bmp-280-sensor.c create mode 100644 platform/srf06-cc26xx/sensortag/bmp-280-sensor.h create mode 100644 platform/srf06-cc26xx/sensortag/board-i2c.c create mode 100644 platform/srf06-cc26xx/sensortag/board-i2c.h create mode 100644 platform/srf06-cc26xx/sensortag/board-peripherals.h create mode 100644 platform/srf06-cc26xx/sensortag/board-spi.c create mode 100644 platform/srf06-cc26xx/sensortag/board-spi.h create mode 100644 platform/srf06-cc26xx/sensortag/board.c create mode 100644 platform/srf06-cc26xx/sensortag/board.h create mode 100644 platform/srf06-cc26xx/sensortag/button-sensor.c create mode 100644 platform/srf06-cc26xx/sensortag/button-sensor.h create mode 100644 platform/srf06-cc26xx/sensortag/buzzer.c create mode 100644 platform/srf06-cc26xx/sensortag/buzzer.h create mode 100644 platform/srf06-cc26xx/sensortag/ext-flash.c create mode 100644 platform/srf06-cc26xx/sensortag/ext-flash.h create mode 100644 platform/srf06-cc26xx/sensortag/leds-arch.c create mode 100644 platform/srf06-cc26xx/sensortag/mpu-9250-sensor.c create mode 100644 platform/srf06-cc26xx/sensortag/mpu-9250-sensor.h create mode 100644 platform/srf06-cc26xx/sensortag/opt-3001-sensor.c create mode 100644 platform/srf06-cc26xx/sensortag/opt-3001-sensor.h create mode 100644 platform/srf06-cc26xx/sensortag/reed-relay.c create mode 100644 platform/srf06-cc26xx/sensortag/reed-relay.h create mode 100644 platform/srf06-cc26xx/sensortag/sensor-common.c create mode 100644 platform/srf06-cc26xx/sensortag/sensor-common.h create mode 100644 platform/srf06-cc26xx/sensortag/sensortag-sensors.c create mode 100644 platform/srf06-cc26xx/sensortag/sht-21-sensor.c create mode 100644 platform/srf06-cc26xx/sensortag/sht-21-sensor.h create mode 100644 platform/srf06-cc26xx/sensortag/tmp-007-sensor.c create mode 100644 platform/srf06-cc26xx/sensortag/tmp-007-sensor.h diff --git a/platform/srf06-cc26xx/sensortag/Makefile.sensortag b/platform/srf06-cc26xx/sensortag/Makefile.sensortag new file mode 100644 index 000000000..98fad7a68 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/Makefile.sensortag @@ -0,0 +1,8 @@ +CFLAGS += -DBOARD_SENSORTAG=1 -DTI_BSP_BOARD_HDR=\"ti-bsp-st.h\" +CFLAGS += -DBACKDOOR_IOID=0x00000000 + +BOARD_SOURCEFILES += leds-arch.c sensortag-sensors.c sensor-common.c +BOARD_SOURCEFILES += bmp-280-sensor.c tmp-007-sensor.c opt-3001-sensor.c +BOARD_SOURCEFILES += sht-21-sensor.c mpu-9250-sensor.c button-sensor.c +BOARD_SOURCEFILES += reed-relay.c ext-flash.c buzzer.c +BOARD_SOURCEFILES += board.c board-spi.c board-i2c.c diff --git a/platform/srf06-cc26xx/sensortag/bmp-280-sensor.c b/platform/srf06-cc26xx/sensortag/bmp-280-sensor.c new file mode 100644 index 000000000..a3b85ccc2 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/bmp-280-sensor.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-bmp-sensor + * @{ + * + * \file + * Driver for the Sensortag-CC26XX BMP280 Altimeter / Pressure Sensor + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "lib/sensors.h" +#include "bmp-280-sensor.h" +#include "sys/ctimer.h" +#include "sensor-common.h" +#include "board-i2c.h" +#include "ti-lib.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +#define BMP280_I2C_ADDRESS 0x77 +/*---------------------------------------------------------------------------*/ +/* Registers */ +#define ADDR_CALIB 0x88 +#define ADDR_PROD_ID 0xD0 +#define ADDR_RESET 0xE0 +#define ADDR_STATUS 0xF3 +#define ADDR_CTRL_MEAS 0xF4 +#define ADDR_CONFIG 0xF5 +#define ADDR_PRESS_MSB 0xF7 +#define ADDR_PRESS_LSB 0xF8 +#define ADDR_PRESS_XLSB 0xF9 +#define ADDR_TEMP_MSB 0xFA +#define ADDR_TEMP_LSB 0xFB +#define ADDR_TEMP_XLSB 0xFC +/*---------------------------------------------------------------------------*/ +/* Reset values */ +#define VAL_PROD_ID 0x58 +#define VAL_RESET 0x00 +#define VAL_STATUS 0x00 +#define VAL_CTRL_MEAS 0x00 +#define VAL_CONFIG 0x00 +#define VAL_PRESS_MSB 0x80 +#define VAL_PRESS_LSB 0x00 +#define VAL_TEMP_MSB 0x80 +#define VAL_TEMP_LSB 0x00 +/*---------------------------------------------------------------------------*/ +/* Test values */ +#define VAL_RESET_EXECUTE 0xB6 +#define VAL_CTRL_MEAS_TEST 0x55 +/*---------------------------------------------------------------------------*/ +/* Misc. */ +#define MEAS_DATA_SIZE 6 +#define CALIB_DATA_SIZE 24 +/*---------------------------------------------------------------------------*/ +#define RES_OFF 0 +#define RES_ULTRA_LOW_POWER 1 +#define RES_LOW_POWER 2 +#define RES_STANDARD 3 +#define RES_HIGH 5 +#define RES_ULTRA_HIGH 6 +/*---------------------------------------------------------------------------*/ +/* Bit fields in CTRL_MEAS register */ +#define PM_OFF 0 +#define PM_FORCED 1 +#define PM_NORMAL 3 +/*---------------------------------------------------------------------------*/ +#define OSRST(v) ((v) << 5) +#define OSRSP(v) ((v) << 2) +/*---------------------------------------------------------------------------*/ +typedef struct bmp_280_calibration { + uint16_t dig_t1; + int16_t dig_t2; + int16_t dig_t3; + uint16_t dig_p1; + int16_t dig_p2; + int16_t dig_p3; + int16_t dig_p4; + int16_t dig_p5; + int16_t dig_p6; + int16_t dig_p7; + int16_t dig_p8; + int16_t dig_p9; + int32_t t_fine; +} bmp_280_calibration_t; +/*---------------------------------------------------------------------------*/ +static uint8_t calibration_data[CALIB_DATA_SIZE]; +/*---------------------------------------------------------------------------*/ +#define SENSOR_STATUS_DISABLED 0 +#define SENSOR_STATUS_INITIALISED 1 +#define SENSOR_STATUS_NOT_READY 2 +#define SENSOR_STATUS_READY 3 + +static int enabled = SENSOR_STATUS_DISABLED; +/*---------------------------------------------------------------------------*/ +/* A buffer for the raw reading from the sensor */ +#define SENSOR_DATA_BUF_SIZE 6 + +static uint8_t sensor_value[SENSOR_DATA_BUF_SIZE]; +/*---------------------------------------------------------------------------*/ +/* Wait SENSOR_STARTUP_DELAY clock ticks for the sensor to be ready - ~80ms */ +#define SENSOR_STARTUP_DELAY 11 + +static struct ctimer startup_timer; +/*---------------------------------------------------------------------------*/ +static void +notify_ready(void *not_used) +{ + enabled = SENSOR_STATUS_READY; + sensors_changed(&bmp_280_sensor); +} +/*---------------------------------------------------------------------------*/ +static void +select(void) +{ + /* Set up I2C */ + board_i2c_select(BOARD_I2C_INTERFACE_0, BMP280_I2C_ADDRESS); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Initalise the sensor + */ +static void +init(void) +{ + uint8_t val; + + select(); + + /* Read and store calibration data */ + sensor_common_read_reg(ADDR_CALIB, calibration_data, CALIB_DATA_SIZE); + + /* Reset the sensor */ + val = VAL_RESET_EXECUTE; + sensor_common_write_reg(ADDR_RESET, &val, sizeof(val)); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Enable/disable measurements + * \param enable 0: disable, enable otherwise + * + * @return none + */ +static void +enable_sensor(bool enable) +{ + uint8_t val; + + select(); + + if(enable) { + /* Enable forced mode */ + val = PM_NORMAL | OSRSP(1) | OSRST(1); + } else { + val = PM_OFF; + } + sensor_common_write_reg(ADDR_CTRL_MEAS, &val, sizeof(val)); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Read temperature and pressure data + * \param data Pointer to a buffer where temperature and pressure will be + * written (6 bytes) + * \return True if valid data could be retrieved + */ +static bool +read_data(uint8_t *data) +{ + bool success; + + select(); + + success = sensor_common_read_reg(ADDR_PRESS_MSB, data, MEAS_DATA_SIZE); + if(!success) { + sensor_common_set_error_data(data, MEAS_DATA_SIZE); + } + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Convert raw data to values in degrees C (temp) and Pascal (pressure) + * \param data Pointer to a buffer that holds raw sensor data + * \param temp Pointer to a variable where the converted temperature will be + * written + * \param press Pointer to a variable where the converted pressure will be + * written + */ +static void +convert(uint8_t *data, int32_t *temp, uint32_t *press) +{ + int32_t utemp, upress; + bmp_280_calibration_t *p = (bmp_280_calibration_t *)calibration_data; + int32_t v_x1_u32r; + int32_t v_x2_u32r; + int32_t temperature; + uint32_t pressure; + + /* Pressure */ + upress = (int32_t)((((uint32_t)(data[0])) << 12) + | (((uint32_t)(data[1])) << 4) | ((uint32_t)data[2] >> 4)); + + /* Temperature */ + utemp = (int32_t)((((uint32_t)(data[3])) << 12) | (((uint32_t)(data[4])) << 4) + | ((uint32_t)data[5] >> 4)); + + /* Compensate temperature */ + v_x1_u32r = ((((utemp >> 3) - ((int32_t)p->dig_t1 << 1))) + * ((int32_t)p->dig_t2)) >> 11; + v_x2_u32r = (((((utemp >> 4) - ((int32_t)p->dig_t1)) + * ((utemp >> 4) - ((int32_t)p->dig_t1))) >> 12) + * ((int32_t)p->dig_t3)) + >> 14; + p->t_fine = v_x1_u32r + v_x2_u32r; + temperature = (p->t_fine * 5 + 128) >> 8; + *temp = temperature; + + /* Compensate pressure */ + v_x1_u32r = (((int32_t)p->t_fine) >> 1) - (int32_t)64000; + v_x2_u32r = (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 11) + * ((int32_t)p->dig_p6); + v_x2_u32r = v_x2_u32r + ((v_x1_u32r * ((int32_t)p->dig_p5)) << 1); + v_x2_u32r = (v_x2_u32r >> 2) + (((int32_t)p->dig_p4) << 16); + v_x1_u32r = + (((p->dig_p3 * (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 13)) >> 3) + + ((((int32_t)p->dig_p2) * v_x1_u32r) >> 1)) >> 18; + v_x1_u32r = ((((32768 + v_x1_u32r)) * ((int32_t)p->dig_p1)) >> 15); + + if(v_x1_u32r == 0) { + return; /* Avoid exception caused by division by zero */ + } + + pressure = (((uint32_t)(((int32_t)1048576) - upress) - (v_x2_u32r >> 12))) + * 3125; + if(pressure < 0x80000000) { + pressure = (pressure << 1) / ((uint32_t)v_x1_u32r); + } else { + pressure = (pressure / (uint32_t)v_x1_u32r) * 2; + } + + v_x1_u32r = (((int32_t)p->dig_p9) + * ((int32_t)(((pressure >> 3) * (pressure >> 3)) >> 13))) >> 12; + v_x2_u32r = (((int32_t)(pressure >> 2)) * ((int32_t)p->dig_p8)) >> 13; + pressure = (uint32_t)((int32_t)pressure + + ((v_x1_u32r + v_x2_u32r + p->dig_p7) >> 4)); + + *press = pressure; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns a reading from the sensor + * \param BMP_280_SENSOR_TYPE_TEMP or BMP_280_SENSOR_TYPE_PRESS + * \return Temperature (centi degrees C) or Pressure (Pascal). + */ +static int +value(int type) +{ + int rv; + int32_t temp = 0; + uint32_t pres = 0; + + if(enabled != SENSOR_STATUS_READY) { + PRINTF("Sensor disabled or starting up (%d)\n", enabled); + return CC26XX_SENSOR_READING_ERROR; + } + + if((type != BMP_280_SENSOR_TYPE_TEMP) && type != BMP_280_SENSOR_TYPE_PRESS) { + PRINTF("Invalid type\n"); + return CC26XX_SENSOR_READING_ERROR; + } else { + memset(sensor_value, 0, SENSOR_DATA_BUF_SIZE); + + rv = read_data(sensor_value); + + if(rv == 0) { + return CC26XX_SENSOR_READING_ERROR; + } + + PRINTF("val: %02x%02x%02x %02x%02x%02x\n", + sensor_value[0], sensor_value[1], sensor_value[2], + sensor_value[3], sensor_value[4], sensor_value[5]); + + convert(sensor_value, &temp, &pres); + + if(type == BMP_280_SENSOR_TYPE_TEMP) { + rv = (int)temp; + } else if(type == BMP_280_SENSOR_TYPE_PRESS) { + rv = (int)pres; + } + } + return rv; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the BMP280 sensor. + * + * \param type Activate, enable or disable the sensor. See below + * \param enable + * + * When type == SENSORS_HW_INIT we turn on the hardware + * When type == SENSORS_ACTIVE and enable==1 we enable the sensor + * When type == SENSORS_ACTIVE and enable==0 we disable the sensor + */ +static int +configure(int type, int enable) +{ + switch(type) { + case SENSORS_HW_INIT: + enabled = SENSOR_STATUS_INITIALISED; + init(); + break; + case SENSORS_ACTIVE: + /* Must be initialised first */ + if(enabled == SENSOR_STATUS_DISABLED) { + return SENSOR_STATUS_DISABLED; + } + if(enable) { + enable_sensor(1); + ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL); + enabled = SENSOR_STATUS_NOT_READY; + } else { + ctimer_stop(&startup_timer); + enable_sensor(0); + enabled = SENSOR_STATUS_INITIALISED; + } + break; + default: + break; + } + return enabled; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the status of the sensor + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the sensor is enabled + */ +static int +status(int type) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return enabled; + break; + default: + break; + } + return SENSOR_STATUS_DISABLED; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(bmp_280_sensor, "BMP280", value, configure, status); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/bmp-280-sensor.h b/platform/srf06-cc26xx/sensortag/bmp-280-sensor.h new file mode 100644 index 000000000..e27013c40 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/bmp-280-sensor.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-bmp-sensor SensorTag 2.0 Pressure Sensor + * + * Due to the time required for the sensor to startup, this driver is meant to + * be used in an asynchronous fashion. The caller must first activate the + * sensor by calling SENSORS_ACTIVATE(). This will trigger the sensor's startup + * sequence, but the call will not wait for it to complete so that the CPU can + * perform other tasks or drop to a low power mode. + * + * Once the sensor is stable, the driver will generate a sensors_changed event. + * + * Once a reading has been taken, the caller has two options: + * - Turn the sensor off by calling SENSORS_DEACTIVATE, but in order to take + * subsequent readings SENSORS_ACTIVATE must be called again + * - Leave the sensor on. In this scenario, the caller can simply keep calling + * value() for subsequent readings, but having the sensor on will consume + * energy + * @{ + * + * \file + * Header file for the Sensortag-CC26xx BMP280 Altimeter / Pressure Sensor + */ +/*---------------------------------------------------------------------------*/ +#ifndef BMP_280_SENSOR_H_ +#define BMP_280_SENSOR_H_ +/*---------------------------------------------------------------------------*/ +#define BMP_280_SENSOR_TYPE_TEMP 1 +#define BMP_280_SENSOR_TYPE_PRESS 2 +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor bmp_280_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* BMP_280_SENSOR_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/board-i2c.c b/platform/srf06-cc26xx/sensortag/board-i2c.c new file mode 100644 index 000000000..0e1f544a6 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/board-i2c.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-i2c + * @{ + * + * \file + * Board-specific I2C driver for the Sensortag-CC26xx + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "ti-lib.h" +#include "board-i2c.h" +/*---------------------------------------------------------------------------*/ +static uint8_t slave_addr = 0x00; +static uint8_t interface = 0xFF; +/*---------------------------------------------------------------------------*/ +static bool +i2c_status() +{ + uint32_t status; + + status = ti_lib_i2c_master_err(I2C0_BASE); + if(status & (I2C_MSTAT_DATACK_N_M | I2C_MSTAT_ADRACK_N_M)) { + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP); + } + + return status == I2C_MASTER_ERR_NONE; +} +/*---------------------------------------------------------------------------*/ +void +board_i2c_init() +{ + /* The I2C peripheral must be enabled */ + ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_I2C0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* Reset the I2C controller */ + HWREG(PRCM_BASE + PRCM_O_RESETI2C) = PRCM_RESETI2C_I2C; + + /* Enable and initialize the I2C master module */ + ti_lib_i2c_master_init_exp_clk(I2C0_BASE, + ti_lib_sys_ctrl_peripheral_clock_get( + PRCM_PERIPH_I2C0, SYSCTRL_SYSBUS_ON), + true); +} +/*---------------------------------------------------------------------------*/ +bool +board_i2c_write(uint8_t *data, uint8_t len) +{ + uint32_t i; + bool success; + + /* Write slave address */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false); + + /* Write first byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, data[0]); + + /* Check if another master has access */ + while(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + + /* Assert RUN + START */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START); + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + + for(i = 1; i < len && success; i++) { + /* Write next byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, data[i]); + if(i < len - 1) { + /* Clear START */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT); + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + } + } + + /* Assert stop */ + if(success) { + /* Assert STOP */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH); + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + while(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + } + + return success; +} +/*---------------------------------------------------------------------------*/ +bool +board_i2c_write_single(uint8_t data) +{ + bool success; + + /* Write slave address */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false); + + /* Write first byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, data); + + /* Check if another master has access */ + while(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + + /* Assert RUN + START + STOP */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND); + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + + return success; +} +/*---------------------------------------------------------------------------*/ +bool +board_i2c_read(uint8_t *data, uint8_t len) +{ + uint8_t i; + bool success; + + /* Set slave address */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, true); + + /* Check if another master has access */ + while(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + + /* Assert RUN + START + ACK */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START); + + i = 0; + success = true; + while(i < (len - 1) && success) { + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + if(success) { + data[i] = ti_lib_i2c_master_data_get(I2C0_BASE); + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT); + i++; + } + } + + if(success) { + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + if(success) { + data[len - 1] = ti_lib_i2c_master_data_get(I2C0_BASE); + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + while(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + } + } + + return success; +} +/*---------------------------------------------------------------------------*/ +bool +board_i2c_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata, uint8_t rlen) +{ + uint32_t i; + bool success; + + /* Set slave address for write */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false); + + /* Write first byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, wdata[0]); + + /* Check if another master has access */ + while(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + + /* Assert RUN + START */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START); + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + + for(i = 1; i < wlen && success; i++) { + /* Write next byte */ + ti_lib_i2c_master_data_put(I2C0_BASE, wdata[i]); + if(i < wlen - 1) { + /* Clear START */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT); + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + } + } + if(!success) { + return false; + } + + /* Set slave address for read */ + ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, true); + + /* Assert ACK */ + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START); + + i = 0; + while(i < (rlen - 1) && success) { + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + if(success) { + rdata[i] = ti_lib_i2c_master_data_get(I2C0_BASE); + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT); + i++; + } + } + + if(success) { + while(ti_lib_i2c_master_busy(I2C0_BASE)); + success = i2c_status(); + if(success) { + rdata[rlen - 1] = ti_lib_i2c_master_data_get(I2C0_BASE); + ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + while(ti_lib_i2c_master_bus_busy(I2C0_BASE)); + } + } + + return success; +} +/*---------------------------------------------------------------------------*/ +void +board_i2c_select(uint8_t new_interface, uint8_t address) +{ + slave_addr = address; + + if(new_interface != interface) { + interface = new_interface; + if(interface == BOARD_I2C_INTERFACE_0) { + ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_NO_IOPULL); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_NO_IOPULL); + ti_lib_ioc_pin_type_i2c(I2C0_BASE, BOARD_IOID_SDA, BOARD_IOID_SCL); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA_HP); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL_HP); + } else if(interface == BOARD_I2C_INTERFACE_1) { + ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA_HP, IOC_NO_IOPULL); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL_HP, IOC_NO_IOPULL); + ti_lib_ioc_pin_type_i2c(I2C0_BASE, BOARD_IOID_SDA_HP, BOARD_IOID_SCL_HP); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA); + ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL); + } + } +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/board-i2c.h b/platform/srf06-cc26xx/sensortag/board-i2c.h new file mode 100644 index 000000000..bed78cf46 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/board-i2c.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-i2c SensorTag 2.0 I2C functions + * @{ + * + * \file + * Header file for the Sensortag-CC26xx I2C Driver + */ +/*---------------------------------------------------------------------------*/ +#ifndef BOARD_I2C_H_ +#define BOARD_I2C_H_ +/*---------------------------------------------------------------------------*/ +#include +#include +/*---------------------------------------------------------------------------*/ +#define BOARD_I2C_INTERFACE_0 0 +#define BOARD_I2C_INTERFACE_1 1 +/*---------------------------------------------------------------------------*/ +#define board_i2c_deselect(...) +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise the I2C controller with defaults for the sensortag + */ +void board_i2c_init(void); + +/** + * \brief Select an I2C slave + * \param interface The I2C interface to be used (BOARD_I2C_INTERFACE_0 or _1) + * \param slave_addr The slave's address + * + * The various sensors on the sensortag are connected either on interface 0 or + * 1. All sensors are connected to interface 0, with the exception of the MPU + * that is connected to 1. + */ +void board_i2c_select(uint8_t interface, uint8_t slave_addr); + +/** + * \brief Burst read from an I2C device + * \param buf Pointer to a buffer where the read data will be stored + * \param len Number of bytes to read + * \return True on success + */ +bool board_i2c_read(uint8_t *buf, uint8_t len); + +/** + * \brief Burst write to an I2C device + * \param buf Pointer to the buffer to be written + * \param len Number of bytes to write + * \return True on success + */ +bool board_i2c_write(uint8_t *buf, uint8_t len); + +/** + * \brief Single write to an I2C device + * \param data The byte to write + * \return True on success + */ +bool board_i2c_write_single(uint8_t data); + +/** + * \brief Write and read in one operation + * \param wdata Pointer to the buffer to be written + * \param wlen Number of bytes to write + * \param rdata Pointer to a buffer where the read data will be stored + * \param rlen Number of bytes to read + * \return True on success + */ +bool board_i2c_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata, + uint8_t rlen); +/*---------------------------------------------------------------------------*/ +#endif /* BOARD_I2C_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/board-peripherals.h b/platform/srf06-cc26xx/sensortag/board-peripherals.h new file mode 100644 index 000000000..aa61ab333 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/board-peripherals.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** \addtogroup cc26xx-srf-tag + * @{ + * + * \file + * Header file with definitions related to the sensors on the Sensortag-CC26xx + * + * \note Do not include this file directly. + */ +/*---------------------------------------------------------------------------*/ +#ifndef BOARD_PERIPHERALS_H_ +#define BOARD_PERIPHERALS_H_ +/*---------------------------------------------------------------------------*/ +#include "bmp-280-sensor.h" +#include "tmp-007-sensor.h" +#include "opt-3001-sensor.h" +#include "sht-21-sensor.h" +#include "mpu-9250-sensor.h" +#include "reed-relay.h" +#include "buzzer.h" +#include "ext-flash.h" +/*---------------------------------------------------------------------------*/ +#endif /* BOARD_PERIPHERALS_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/board-spi.c b/platform/srf06-cc26xx/sensortag/board-spi.c new file mode 100644 index 000000000..01dd65857 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/board-spi.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-spi + * @{ + * + * \file + * Board-specific SPI driver for the Sensortag-CC26xx + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "ti-lib.h" +#include "board-spi.h" +#include "board.h" +/*---------------------------------------------------------------------------*/ +#define CPU_FREQ 48000000ul +/*---------------------------------------------------------------------------*/ +int +board_spi_write(const uint8_t *buf, size_t len) +{ + while(len > 0) { + uint32_t ul; + + ti_lib_ssi_data_put(SSI0_BASE, *buf); + ti_lib_rom_ssi_data_get(SSI0_BASE, &ul); + len--; + buf++; + } + + return 0; +} +/*---------------------------------------------------------------------------*/ +int +board_spi_read(uint8_t *buf, size_t len) +{ + while(len > 0) { + uint32_t ul; + + if(!ti_lib_rom_ssi_data_put_non_blocking(SSI0_BASE, 0)) { + /* Error */ + return -1; + } + ti_lib_rom_ssi_data_get(SSI0_BASE, &ul); + *buf = (uint8_t)ul; + len--; + buf++; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +void +board_spi_flush() +{ + uint32_t ul; + while(ti_lib_rom_ssi_data_get_non_blocking(SSI0_BASE, &ul)); +} +/*---------------------------------------------------------------------------*/ +void +board_spi_open(uint32_t bit_rate, uint32_t clk_pin) +{ + uint32_t buf; + + /* SPI power */ + ti_lib_rom_prcm_peripheral_run_enable(PRCM_PERIPH_SSI0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* SPI configuration */ + ti_lib_ssi_int_disable(SSI0_BASE, SSI_RXOR | SSI_RXFF | SSI_RXTO | SSI_TXFF); + ti_lib_ssi_int_clear(SSI0_BASE, SSI_RXOR | SSI_RXTO); + ti_lib_rom_ssi_config_set_exp_clk(SSI0_BASE, CPU_FREQ, SSI_FRF_MOTO_MODE_0, + SSI_MODE_MASTER, bit_rate, 8); + ti_lib_rom_ioc_pin_type_ssi_master(SSI0_BASE, BOARD_IOID_SPI_MISO, + BOARD_IOID_SPI_MOSI, IOID_UNUSED, clk_pin); + ti_lib_ssi_enable(SSI0_BASE); + + /* Get rid of residual data from SSI port */ + while(ti_lib_ssi_data_get_non_blocking(SSI0_BASE, &buf)); +} +/*---------------------------------------------------------------------------*/ +void +board_spi_close() +{ + /* Power down SSI0 */ + ti_lib_rom_prcm_peripheral_run_disable(PRCM_PERIPH_SSI0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/board-spi.h b/platform/srf06-cc26xx/sensortag/board-spi.h new file mode 100644 index 000000000..0c1e49d1f --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/board-spi.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-spi SensorTag 2.0 SPI functions + * @{ + * + * \file + * Header file for the Sensortag-CC26xx SPI Driver + */ +/*---------------------------------------------------------------------------*/ +#ifndef BOARD_SPI_H_ +#define BOARD_SPI_H_ +/*---------------------------------------------------------------------------*/ +#include +#include +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialize the SPI interface + * \param bit_rate The bit rate to use + * \param clk_pin The IOID for the clock pin. This can be IOID_0 etc + * \return none + */ +void board_spi_open(uint32_t bit_rate, uint32_t clk_pin); + +/** + * \brief Close the SPI interface + * \return True when successful. + */ +void board_spi_close(void); + +/** + * \brief Clear data from the SPI interface + * \return none + */ +void board_spi_flush(void); + +/** + * \brief Read from an SPI device + * \param buf The buffer to store data + * \param length The number of bytes to read + * \return True when successful. + */ +int board_spi_read(uint8_t *buf, size_t length); + +/** + * \brief Write to an SPI device + * \param buf The buffer with the data to write + * \param length The number of bytes to write + * \return True when successful. + */ +int board_spi_write(const uint8_t *buf, size_t length); +/*---------------------------------------------------------------------------*/ +#endif /* BOARD_SPI_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/board.c b/platform/srf06-cc26xx/sensortag/board.c new file mode 100644 index 000000000..5246a85ef --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/board.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \file + * Sensortag-specific board initialisation driver + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "lib/sensors.h" +#include "buzzer.h" +#include "lpm.h" +#include "ti-lib.h" +#include "board-peripherals.h" +#include "board-i2c.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +#define PRCM_DOMAINS (PRCM_DOMAIN_SERIAL | PRCM_DOMAIN_PERIPH) +/*---------------------------------------------------------------------------*/ +static void +power_domains_on(void) +{ + /* Turn on relevant power domains */ + ti_lib_prcm_power_domain_on(PRCM_DOMAINS); + + /* Wait for domains to power on */ + while((ti_lib_prcm_power_domain_status(PRCM_DOMAINS) + != PRCM_DOMAIN_POWER_ON)); +} +/*---------------------------------------------------------------------------*/ +static void +lpm_wakeup_handler(void) +{ + power_domains_on(); + + board_i2c_init(); +} +/*---------------------------------------------------------------------------*/ +static void +shutdown_handler(uint8_t mode) +{ + if(mode == LPM_MODE_SHUTDOWN) { + buzzer_stop(); + SENSORS_DEACTIVATE(bmp_280_sensor); + SENSORS_DEACTIVATE(opt_3001_sensor); + SENSORS_DEACTIVATE(tmp_007_sensor); + SENSORS_DEACTIVATE(sht_21_sensor); + mpu_9250_sensor.configure(MPU_9250_SENSOR_SHUTDOWN, 0); + } +} +/*---------------------------------------------------------------------------*/ +/* + * Declare a data structure to register with LPM. + * We don't care about what power mode we'll drop to, we don't care about + * getting notified before deep sleep. All we need is to be notified when we + * wake up so we can turn power domains back on for I2C and SSI, and to make + * sure everything on the board is off before CM3 shutdown. + */ +LPM_MODULE(sensortag_module, NULL, shutdown_handler, lpm_wakeup_handler); +/*---------------------------------------------------------------------------*/ +void +board_init() +{ + /* Disable global interrupts */ + uint8_t int_disabled = ti_lib_int_master_disable(); + + power_domains_on(); + + /* Configure all clock domains to run at full speed */ + ti_lib_prcm_clock_configure_set(PRCM_DOMAIN_SYSBUS, PRCM_CLOCK_DIV_1); + ti_lib_prcm_clock_configure_set(PRCM_DOMAIN_CPU, PRCM_CLOCK_DIV_1); + ti_lib_prcm_clock_configure_set(PRCM_DOMAIN_TIMER, PRCM_CLOCK_DIV_1); + ti_lib_prcm_clock_configure_set(PRCM_DOMAIN_SERIAL, PRCM_CLOCK_DIV_1); + ti_lib_prcm_clock_configure_set(PRCM_DOMAIN_PERIPH, PRCM_CLOCK_DIV_1); + + /* Enable GPIO peripheral */ + ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_GPIO); + + /* Apply settings and wait for them to take effect */ + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* Enable GPT0 module - Wait for the clock to be enabled */ + ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_TIMER0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); + + /* Keys (input pullup) */ + ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_KEY_LEFT); + ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_KEY_RIGHT); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_KEY_LEFT, IOC_IOPULL_UP); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_KEY_RIGHT, IOC_IOPULL_UP); + + /* I2C controller */ + board_i2c_init(); + + /* Sensor interface */ + ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_MPU_INT); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_MPU_INT, IOC_IOPULL_DOWN); + + ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_REED_RELAY); + ti_lib_ioc_io_port_pull_set(BOARD_IOID_REED_RELAY, IOC_IOPULL_DOWN); + + ti_lib_rom_ioc_pin_type_gpio_output(BOARD_IOID_MPU_POWER); + + /* Flash interface */ + ti_lib_rom_ioc_pin_type_gpio_output(BOARD_IOID_FLASH_CS); + ti_lib_gpio_pin_write(BOARD_FLASH_CS, 1); + + buzzer_init(); + + lpm_register_module(&sensortag_module); + + /* Re-enable interrupt if initially enabled. */ + if(!int_disabled) { + ti_lib_int_master_enable(); + } +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/board.h b/platform/srf06-cc26xx/sensortag/board.h new file mode 100644 index 000000000..00afb98f2 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/board.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** \addtogroup cc26xx-srf-tag + * @{ + * + * \defgroup sensortag-cc26xx-peripherals Sensortag Peripherals + * + * Defines related to the Sensortag-CC26XX + * + * This file provides connectivity information on LEDs, Buttons, UART and + * other peripherals + * + * This file can be used as the basis to configure other boards using the + * CC26XX code as their basis. + * + * This file is not meant to be modified by the user. + * @{ + * + * \file + * Header file with definitions related to the I/O connections on the TI + * Sensortag + * + * \note Do not include this file directly. It gets included by contiki-conf + * after all relevant directives have been set. + */ +/*---------------------------------------------------------------------------*/ +#ifndef BOARD_H_ +#define BOARD_H_ +/*---------------------------------------------------------------------------*/ +#include "ioc.h" +/*---------------------------------------------------------------------------*/ +/** + * \name LED configurations + * + * Those values are not meant to be modified by the user + * @{ + */ +/* Some files include leds.h before us, so we need to get rid of defaults in + * leds.h before we provide correct definitions */ +#undef LEDS_GREEN +#undef LEDS_YELLOW +#undef LEDS_RED +#undef LEDS_CONF_ALL + +#define LEDS_RED 1 +#define LEDS_GREEN 2 +#define LEDS_YELLOW LEDS_GREEN +#define LEDS_ORANGE LEDS_RED + +#define LEDS_CONF_ALL 3 + +/* Notify various examples that we have LEDs */ +#define PLATFORM_HAS_LEDS 1 +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name LED IOID mappings + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_LED_1 IOID_10 +#define BOARD_IOID_LED_2 IOID_15 +#define BOARD_LED_1 (1 << BOARD_IOID_LED_1) +#define BOARD_LED_2 (1 << BOARD_IOID_LED_2) +#define BOARD_LED_ALL (BOARD_LED_1 | BOARD_LED_2) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name UART IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_UART_RX IOID_17 +#define BOARD_IOID_UART_TX IOID_16 +#define BOARD_IOID_UART_CTS IOID_UNUSED +#define BOARD_IOID_UART_RTS IOID_UNUSED +#define BOARD_UART_RXD (1 << BOARD_IOID_UART_RXD) +#define BOARD_UART_TXD (1 << BOARD_IOID_UART_TXD) +#define BOARD_UART_CTS (1 << BOARD_IOID_UART_CTS) +#define BOARD_UART_RTS (1 << BOARD_IOID_UART_RTS) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Button IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_KEY_LEFT IOID_0 +#define BOARD_IOID_KEY_RIGHT IOID_4 +#define BOARD_KEY_LEFT (1 << BOARD_IOID_KEY_LEFT) +#define BOARD_KEY_RIGHT (1 << BOARD_IOID_KEY_RIGHT) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \brief SPI IOID mappings + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_SPI_MOSI IOID_19 +#define BOARD_IOID_SPI_MISO IOID_18 +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Buzzer configuration + * @{ + */ +#define BOARD_IOID_BUZZER IOID_21 /**< Buzzer Pin */ +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Reed Relay IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_REED_RELAY IOID_3 +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name External flash IOID mapping + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_FLASH_CS IOID_14 +#define BOARD_FLASH_CS (1 << BOARD_IOID_FLASH_CS) +#define BOARD_SPI_CLK_FLASH IOID_11 +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \brief I2C IOID mappings + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_SDA IOID_5 /**< Interface 0 SDA: All sensors bar MPU */ +#define BOARD_IOID_SCL IOID_6 /**< Interface 0 SCL: All sensors bar MPU */ +#define BOARD_IOID_SDA_HP IOID_8 /**< Interface 1 SDA: MPU */ +#define BOARD_IOID_SCL_HP IOID_9 /**< Interface 1 SCL: MPU */ +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \brief MPU IOID mappings + * + * Those values are not meant to be modified by the user + * @{ + */ +#define BOARD_IOID_MPU_INT IOID_7 +#define BOARD_IOID_MPU_POWER IOID_12 +#define BOARD_MPU_INT (1 << BOARD_IOID_MPU_INT) +#define BOARD_MPU_POWER (1 << BOARD_IOID_MPU_POWER) +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Device string used on startup + * @{ + */ +#define BOARD_STRING "TI CC2650 SensorTag" + +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \brief Board specific iniatialisation + * @{ + */ +void board_init(void); +/** @} */ +/*---------------------------------------------------------------------------*/ +#endif /* BOARD_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/button-sensor.c b/platform/srf06-cc26xx/sensortag/button-sensor.c new file mode 100644 index 000000000..089ea7332 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/button-sensor.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-button-sensor + * @{ + * + * \file + * Driver for the Sensortag-CC26xx buttons + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/sensors.h" +#include "sensortag/button-sensor.h" +#include "gpio-interrupt.h" +#include "sys/timer.h" +#include "lpm.h" + +#include "ti-lib.h" + +#include +/*---------------------------------------------------------------------------*/ +#ifdef BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN +#define BUTTON_SENSOR_ENABLE_SHUTDOWN BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN +#else +#define BUTTON_SENSOR_ENABLE_SHUTDOWN 1 +#endif +/*---------------------------------------------------------------------------*/ +#define BUTTON_GPIO_CFG (IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | \ + IOC_IOPULL_UP | IOC_SLEW_DISABLE | \ + IOC_HYST_DISABLE | IOC_BOTH_EDGES | \ + IOC_INT_ENABLE | IOC_IOMODE_NORMAL | \ + IOC_NO_WAKE_UP | IOC_INPUT_ENABLE | \ + IOC_JTAG_DISABLE) +/*---------------------------------------------------------------------------*/ +#define DEBOUNCE_DURATION (CLOCK_SECOND >> 5) + +struct btn_timer { + struct timer debounce; + clock_time_t start; + clock_time_t duration; +}; + +static struct btn_timer left_timer, right_timer; +/*---------------------------------------------------------------------------*/ +/** + * \brief Handler for Sensortag-CC26XX button presses + */ +static void +button_press_handler(uint8_t ioid) +{ + if(ioid == BOARD_IOID_KEY_LEFT) { + if(!timer_expired(&left_timer.debounce)) { + return; + } + + timer_set(&left_timer.debounce, DEBOUNCE_DURATION); + + /* + * Start press duration counter on press (falling), notify on release + * (rising) + */ + if(ti_lib_gpio_pin_read(BOARD_KEY_LEFT) == 0) { + left_timer.start = clock_time(); + left_timer.duration = 0; + } else { + left_timer.duration = clock_time() - left_timer.start; + sensors_changed(&button_left_sensor); + } + } + + if(ioid == BOARD_IOID_KEY_RIGHT) { + if(BUTTON_SENSOR_ENABLE_SHUTDOWN == 0) { + if(!timer_expired(&right_timer.debounce)) { + return; + } + + timer_set(&right_timer.debounce, DEBOUNCE_DURATION); + + /* + * Start press duration counter on press (falling), notify on release + * (rising) + */ + if(ti_lib_gpio_pin_read(BOARD_KEY_RIGHT) == 0) { + right_timer.start = clock_time(); + right_timer.duration = 0; + } else { + right_timer.duration = clock_time() - right_timer.start; + sensors_changed(&button_right_sensor); + } + } else { + lpm_shutdown(BOARD_IOID_KEY_RIGHT); + } + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the button sensor for all buttons. + * + * \param type This function does nothing unless type == SENSORS_ACTIVE + * \param c 0: disable the button, non-zero: enable + * \param key: One of BOARD_KEY_LEFT, BOARD_KEY_RIGHT etc + */ +static void +config_buttons(int type, int c, uint32_t key) +{ + switch(type) { + case SENSORS_HW_INIT: + ti_lib_gpio_event_clear(1 << key); + ti_lib_ioc_port_configure_set(key, IOC_PORT_GPIO, BUTTON_GPIO_CFG); + ti_lib_gpio_dir_mode_set((1 << key), GPIO_DIR_MODE_IN); + gpio_interrupt_register_handler(key, button_press_handler); + break; + case SENSORS_ACTIVE: + if(c) { + ti_lib_gpio_event_clear(1 << key); + ti_lib_ioc_port_configure_set(key, IOC_PORT_GPIO, BUTTON_GPIO_CFG); + ti_lib_gpio_dir_mode_set((1 << key), GPIO_DIR_MODE_IN); + ti_lib_ioc_int_enable(key); + } else { + ti_lib_ioc_int_disable(key); + } + break; + default: + break; + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the left button. + * + * Parameters are passed onto config_buttons, which does the actual + * configuration + * Parameters are ignored. They have been included because the prototype is + * dictated by the core sensor API. The return value is also required by + * the API but otherwise ignored. + * + * \param type passed to config_buttons as-is + * \param value passed to config_buttons as-is + * + * \return ignored + */ +static int +config_left(int type, int value) +{ + config_buttons(type, value, BOARD_IOID_KEY_LEFT); + + return 1; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the right button. + * + * Parameters are passed onto config_buttons, which does the actual + * configuration + * Parameters are ignored. They have been included because the prototype is + * dictated by the core sensor api. The return value is also required by + * the API but otherwise ignored. + * + * \param type passed to config_buttons as-is + * \param value passed to config_buttons as-is + * + * \return ignored + */ +static int +config_right(int type, int value) +{ + config_buttons(type, value, BOARD_IOID_KEY_RIGHT); + + return 1; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for all buttons + * \param type SENSORS_ACTIVE or SENSORS_READY + * \param key_io_id BOARD_IOID_KEY_LEFT, BOARD_IOID_KEY_RIGHT etc + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will only be called by status_left, status_right and the + * called will pass the correct key_io_id + */ +static int +status(int type, uint32_t key_io_id) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + if(ti_lib_ioc_port_configure_get(key_io_id) & IOC_INT_ENABLE) { + return 1; + } + break; + default: + break; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +value_left(int type) +{ + if(type == BUTTON_SENSOR_VALUE_STATE) { + return ti_lib_gpio_pin_read(BOARD_KEY_LEFT) == 0 ? + BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED; + } else if(type == BUTTON_SENSOR_VALUE_DURATION) { + return (int)left_timer.duration; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +value_right(int type) +{ + if(type == BUTTON_SENSOR_VALUE_STATE) { + return ti_lib_gpio_pin_read(BOARD_KEY_RIGHT) == 0 ? + BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED; + } else if(type == BUTTON_SENSOR_VALUE_DURATION) { + return (int)right_timer.duration; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for the left button. + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will call status. It will pass type verbatim and it will also + * pass the correct key_io_id + */ +static int +status_left(int type) +{ + return status(type, BOARD_IOID_KEY_LEFT); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for the right button. + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the button's port interrupt is enabled (edge detect) + * + * This function will call status. It will pass type verbatim and it will also + * pass the correct key_io_id + */ +static int +status_right(int type) +{ + return status(type, BOARD_IOID_KEY_RIGHT); +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(button_left_sensor, BUTTON_SENSOR, value_left, config_left, + status_left); +SENSORS_SENSOR(button_right_sensor, BUTTON_SENSOR, value_right, config_right, + status_right); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/button-sensor.h b/platform/srf06-cc26xx/sensortag/button-sensor.h new file mode 100644 index 000000000..6e15b4d4c --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/button-sensor.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-button-sensor SensorTag 2.0 Button Sensor + * + * One of the buttons can be configured as general purpose or as an on/off key + * @{ + * + * \file + * Header file for the Sensortag-CC26xx Button Driver + */ +/*---------------------------------------------------------------------------*/ +#ifndef BUTTON_SENSOR_H_ +#define BUTTON_SENSOR_H_ +/*---------------------------------------------------------------------------*/ +#include "lib/sensors.h" +/*---------------------------------------------------------------------------*/ +#define BUTTON_SENSOR "Button" +/*---------------------------------------------------------------------------*/ +#define BUTTON_SENSOR_VALUE_STATE 0 +#define BUTTON_SENSOR_VALUE_DURATION 1 + +#define BUTTON_SENSOR_VALUE_RELEASED 0 +#define BUTTON_SENSOR_VALUE_PRESSED 1 +/*---------------------------------------------------------------------------*/ +#define button_sensor button_left_sensor +extern const struct sensors_sensor button_left_sensor; +extern const struct sensors_sensor button_right_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* BUTTON_SENSOR_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/buzzer.c b/platform/srf06-cc26xx/sensortag/buzzer.c new file mode 100644 index 000000000..57d063897 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/buzzer.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-buzzer + * @{ + * + * \file + * Driver for the Sensortag-CC26XX Buzzer + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "buzzer.h" +#include "ti-lib.h" +#include "lpm.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +static uint8_t buzzer_on; +static lpm_power_domain_lock_t lock; +/*---------------------------------------------------------------------------*/ +void +buzzer_init() +{ + buzzer_on = 0; + + /* Drive the I/O ID with GPT0 / Timer A */ + ti_lib_ioc_port_configure_set(BOARD_IOID_BUZZER, IOC_PORT_MCU_PORT_EVENT0, + IOC_STD_OUTPUT); + + /* GPT0 / Timer A: PWM, Interrupt Enable */ + HWREG(GPT0_BASE + GPT_O_TAMR) = (TIMER_CFG_A_PWM & 0xFF) | GPT_TAMR_TAPWMIE; +} +/*---------------------------------------------------------------------------*/ +uint8_t +buzzer_state() +{ + return buzzer_on; +} +/*---------------------------------------------------------------------------*/ +void +buzzer_start(int freq) +{ + uint32_t load; + + buzzer_on = 1; + + lpm_pd_lock_obtain(&lock, PRCM_DOMAIN_PERIPH); + + /* Stop the timer */ + ti_lib_timer_disable(GPT0_BASE, TIMER_A); + + if(freq > 0) { + load = (GET_MCU_CLOCK / freq); + + ti_lib_timer_load_set(GPT0_BASE, TIMER_A, load); + ti_lib_timer_match_set(GPT0_BASE, TIMER_A, load / 2); + + /* Start */ + ti_lib_timer_enable(GPT0_BASE, TIMER_A); + } + + /* Run in sleep mode */ + ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_TIMER0); + ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_TIMER0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); +} +/*---------------------------------------------------------------------------*/ +void +buzzer_stop() +{ + buzzer_on = 0; + + lpm_pd_lock_release(&lock); + + /* Stop the timer */ + ti_lib_timer_disable(GPT0_BASE, TIMER_A); + + /* + * Stop running in sleep mode. + * ToDo: Currently GPT0 is in use by clock_delay_usec (GPT0/TB) and by this + * module here (GPT0/TA). clock_delay_usec will never need GPT0/TB in sleep + * mode and we control TA here. Thus, with the current setup, it's OK to + * control whether GPT0 runs in sleep mode in this module here. However, if + * some other module at some point starts using GPT0, we should change this + * to happen through an umbrella module + */ + ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_TIMER0); + ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_TIMER0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/buzzer.h b/platform/srf06-cc26xx/sensortag/buzzer.h new file mode 100644 index 000000000..653b46f88 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/buzzer.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-buzzer SensorTag 2.0 Buzzer + * @{ + * + * \file + * Header file for the Sensortag-CC26xx Buzzer + */ +/*---------------------------------------------------------------------------*/ +#ifndef BUZZER_H_ +#define BUZZER_H_ +/*---------------------------------------------------------------------------*/ +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise the buzzer + */ +void buzzer_init(void); + +/** + * \brief Start the buzzer + * \param freq The buzzer frequency + */ +void buzzer_start(int freq); + +/** + * \brief Stop the buzzer + */ +void buzzer_stop(void); + +/** + * \brief Retrieve the buzzer state + * \return 1: on, 0: off + */ +uint8_t buzzer_state(void); +/*---------------------------------------------------------------------------*/ +#endif /* BUZZER_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/ext-flash.c b/platform/srf06-cc26xx/sensortag/ext-flash.c new file mode 100644 index 000000000..83d2758a4 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/ext-flash.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-ext-flash + * @{ + * + * \file + * Driver for the Sensortag-CC26xx WinBond W25X20CL Flash + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "ext-flash.h" +#include "ti-lib.h" +#include "board-spi.h" +/*---------------------------------------------------------------------------*/ +/* Instruction codes */ + +#define BLS_CODE_PROGRAM 0x02 /**< Page Program */ +#define BLS_CODE_READ 0x03 /**< Read Data */ +#define BLS_CODE_READ_STATUS 0x05 /**< Read Status Register */ +#define BLS_CODE_WRITE_ENABLE 0x06 /**< Write Enable */ +#define BLS_CODE_SECTOR_ERASE 0x20 /**< Sector Erase */ +#define BLS_CODE_MDID 0x90 /**< Manufacturer Device ID */ + +#define BLS_CODE_DP 0xB9 /**< Power down */ +#define BLS_CODE_RDP 0xAB /**< Power standby */ +/*---------------------------------------------------------------------------*/ +/* Erase instructions */ + +#define BLS_CODE_ERASE_4K 0x20 /**< Sector Erase */ +#define BLS_CODE_ERASE_32K 0x52 +#define BLS_CODE_ERASE_64K 0xD8 +#define BLS_CODE_ERASE_ALL 0xC7 /**< Mass Erase */ +/*---------------------------------------------------------------------------*/ +/* Bitmasks of the status register */ + +#define BLS_STATUS_SRWD_BM 0x80 +#define BLS_STATUS_BP_BM 0x0C +#define BLS_STATUS_WEL_BM 0x02 +#define BLS_STATUS_WIP_BM 0x01 + +#define BLS_STATUS_BIT_BUSY 0x01 /**< Busy bit of the status register */ +/*---------------------------------------------------------------------------*/ +/* Part specific constants */ + +#define BLS_MANUFACTURER_ID 0xEF +#define BLS_DEVICE_ID 0x11 + +#define BLS_PROGRAM_PAGE_SIZE 256 +#define BLS_ERASE_SECTOR_SIZE 4096 +/*---------------------------------------------------------------------------*/ +/** + * Clear external flash CSN line + */ +static void +select(void) +{ + ti_lib_gpio_pin_write(BOARD_FLASH_CS, 0); +} +/*---------------------------------------------------------------------------*/ +/** + * Set external flash CSN line + */ +static void +deselect(void) +{ + ti_lib_gpio_pin_write(BOARD_FLASH_CS, 1); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Wait till previous erase/program operation completes. + * \return Zero when successful. + */ +static int +wait_ready(void) +{ + const uint8_t wbuf[1] = { BLS_CODE_READ_STATUS }; + + select(); + + /* Throw away all garbages */ + board_spi_flush(); + + int ret = board_spi_write(wbuf, sizeof(wbuf)); + + if(ret) { + deselect(); + return -2; + } + + for(;;) { + uint8_t buf; + /* Note that this temporary implementation is not + * energy efficient. + * Thread could have yielded while waiting for flash + * erase/program to complete. + */ + ret = board_spi_read(&buf, sizeof(buf)); + + if(ret) { + /* Error */ + deselect(); + return -2; + } + if(!(buf & BLS_STATUS_BIT_BUSY)) { + /* Now ready */ + break; + } + } + deselect(); + return 0; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Put the device in power save mode. No access to data; only + * the status register is accessible. + * \return True when SPI transactions succeed + */ +static bool +power_down(void) +{ + uint8_t cmd; + bool success; + + cmd = BLS_CODE_DP; + select(); + success = board_spi_write(&cmd, sizeof(cmd)); + deselect(); + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Take device out of power save mode and prepare it for normal operation + * \return True if the command was written successfully + */ +static bool +power_standby(void) +{ + uint8_t cmd; + bool success; + + cmd = BLS_CODE_RDP; + select(); + success = board_spi_write(&cmd, sizeof(cmd)); + + if(success) { + success = wait_ready() == 0; + } + + deselect(); + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * Verify the flash part. + * @return True when successful. + */ +static bool +verify_part(void) +{ + const uint8_t wbuf[] = { BLS_CODE_MDID, 0xFF, 0xFF, 0x00 }; + uint8_t rbuf[2]; + int ret; + + select(); + + ret = board_spi_write(wbuf, sizeof(wbuf)); + + if(ret) { + deselect(); + return false; + } + + ret = board_spi_read(rbuf, sizeof(rbuf)); + deselect(); + + if(ret || rbuf[0] != BLS_MANUFACTURER_ID || rbuf[1] != BLS_DEVICE_ID) { + return false; + } + return true; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Enable write. + * \return Zero when successful. + */ +static int +write_enable(void) +{ + const uint8_t wbuf[] = { BLS_CODE_WRITE_ENABLE }; + + select(); + int ret = board_spi_write(wbuf, sizeof(wbuf)); + deselect(); + + if(ret) { + return -3; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +bool +ext_flash_open() +{ + board_spi_open(4000000, BOARD_SPI_CLK_FLASH); + + /* GPIO pin configuration */ + ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_FLASH_CS); + + /* Default output to clear chip select */ + deselect(); + + /* Put the part is standby mode */ + power_standby(); + + return verify_part(); +} +/*---------------------------------------------------------------------------*/ +void +ext_flash_close() +{ + /* Put the part in low power mode */ + power_down(); + + board_spi_close(); +} +/*---------------------------------------------------------------------------*/ +bool +ext_flash_read(size_t offset, size_t length, uint8_t *buf) +{ + uint8_t wbuf[4]; + + /* Wait till previous erase/program operation completes */ + int ret = wait_ready(); + if(ret) { + return false; + } + + /* + * SPI is driven with very low frequency (1MHz < 33MHz fR spec) + * in this implementation, hence it is not necessary to use fast read. + */ + wbuf[0] = BLS_CODE_READ; + wbuf[1] = (offset >> 16) & 0xff; + wbuf[2] = (offset >> 8) & 0xff; + wbuf[3] = offset & 0xff; + + select(); + + if(board_spi_write(wbuf, sizeof(wbuf))) { + /* failure */ + deselect(); + return false; + } + + ret = board_spi_read(buf, length); + + deselect(); + + return ret == 0; +} +/*---------------------------------------------------------------------------*/ +bool +ext_flash_write(size_t offset, size_t length, const uint8_t *buf) +{ + uint8_t wbuf[4]; + int ret; + size_t ilen; /* interim length per instruction */ + + while(length > 0) { + /* Wait till previous erase/program operation completes */ + ret = wait_ready(); + if(ret) { + return false; + } + + ret = write_enable(); + if(ret) { + return false; + } + + ilen = BLS_PROGRAM_PAGE_SIZE - (offset % BLS_PROGRAM_PAGE_SIZE); + if(length < ilen) { + ilen = length; + } + + wbuf[0] = BLS_CODE_PROGRAM; + wbuf[1] = (offset >> 16) & 0xff; + wbuf[2] = (offset >> 8) & 0xff; + wbuf[3] = offset & 0xff; + + offset += ilen; + length -= ilen; + + /* Upto 100ns CS hold time (which is not clear + * whether it's application only inbetween reads) + * is not imposed here since above instructions + * should be enough to delay + * as much. */ + select(); + + if(board_spi_write(wbuf, sizeof(wbuf))) { + /* failure */ + deselect(); + return false; + } + + if(board_spi_write(buf, ilen)) { + /* failure */ + deselect(); + return false; + } + buf += ilen; + deselect(); + } + + return true; +} +/*---------------------------------------------------------------------------*/ +bool +ext_flash_erase(size_t offset, size_t length) +{ + /* + * Note that Block erase might be more efficient when the floor map + * is well planned for OTA, but to simplify this implementation, + * sector erase is used blindly. + */ + uint8_t wbuf[4]; + size_t i, numsectors; + size_t endoffset = offset + length - 1; + + offset = (offset / BLS_ERASE_SECTOR_SIZE) * BLS_ERASE_SECTOR_SIZE; + numsectors = (endoffset - offset + BLS_ERASE_SECTOR_SIZE - 1) / BLS_ERASE_SECTOR_SIZE; + + wbuf[0] = BLS_CODE_SECTOR_ERASE; + + for(i = 0; i < numsectors; i++) { + /* Wait till previous erase/program operation completes */ + int ret = wait_ready(); + if(ret) { + return false; + } + + ret = write_enable(); + if(ret) { + return false; + } + + wbuf[1] = (offset >> 16) & 0xff; + wbuf[2] = (offset >> 8) & 0xff; + wbuf[3] = offset & 0xff; + + select(); + + if(board_spi_write(wbuf, sizeof(wbuf))) { + /* failure */ + deselect(); + return false; + } + deselect(); + + offset += BLS_ERASE_SECTOR_SIZE; + } + + return true; +} +/*---------------------------------------------------------------------------*/ +bool +ext_flash_test(void) +{ + bool ret; + + ret = ext_flash_open(); + ext_flash_close(); + + return ret; +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/ext-flash.h b/platform/srf06-cc26xx/sensortag/ext-flash.h new file mode 100644 index 000000000..ca2ef80ea --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/ext-flash.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-ext-flash SensorTag 2.0 External Flash + * @{ + * + * \file + * Header file for the Sensortag-CC26xx External Flash Driver + */ +/*---------------------------------------------------------------------------*/ +#ifndef EXT_FLASH_H_ +#define EXT_FLASH_H_ +/*---------------------------------------------------------------------------*/ +#include +#include +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialize storage driver. + * \return True when successful. + */ +bool ext_flash_open(void); + +/** + * \brief Close the storage driver + */ +void ext_flash_close(void); + +/** + * \brief Read storage content + * \param offset Address to read from + * \param length Number of bytes to read + * \param buf Buffer where to store the read bytes + * \return True when successful. + * + * buf must be allocated by the caller + */ +bool ext_flash_read(size_t offset, size_t length, uint8_t *buf); + +/** + * \brief Erase storage sectors corresponding to the range. + * \param offset Address to start erasing + * \param length Number of bytes to erase + * \return True when successful. + * + * The erase operation will be sector-wise, therefore a call to this function + * will generally start the erase procedure at an address lower than offset + */ +bool ext_flash_erase(size_t offset, size_t length); + +/** + * \brief Write to storage sectors. + * \param offset Address to write to + * \param length Number of bytes to write + * \param buf Buffer holding the bytes to be written + * + * \return True when successful. + */ +bool ext_flash_write(size_t offset, size_t length, const uint8_t *buf); + +/** + * \brief Test the flash (power on self-test) + * \return True when successful. + */ +bool ext_flash_test(void); +/*---------------------------------------------------------------------------*/ +#endif /* EXT_FLASH_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/leds-arch.c b/platform/srf06-cc26xx/sensortag/leds-arch.c new file mode 100644 index 000000000..e415dca55 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/leds-arch.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \file + * Driver for the Sensortag-CC26XX LEDs + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/leds.h" + +#include "ti-lib.h" +/*---------------------------------------------------------------------------*/ +static unsigned char c; +static int inited = 0; +/*---------------------------------------------------------------------------*/ +void +leds_arch_init(void) +{ + if(inited) { + return; + } + inited = 1; + + ti_lib_rom_ioc_pin_type_gpio_output(BOARD_IOID_LED_1); + ti_lib_rom_ioc_pin_type_gpio_output(BOARD_IOID_LED_2); + + ti_lib_gpio_pin_write(BOARD_LED_ALL, 0); +} +/*---------------------------------------------------------------------------*/ +unsigned char +leds_arch_get(void) +{ + return c; +} +/*---------------------------------------------------------------------------*/ +void +leds_arch_set(unsigned char leds) +{ + c = leds; + ti_lib_gpio_pin_write(BOARD_LED_ALL, 0); + + if((leds & LEDS_RED) == LEDS_RED) { + ti_lib_gpio_pin_write(BOARD_LED_1, 1); + } + if((leds & LEDS_YELLOW) == LEDS_YELLOW) { + ti_lib_gpio_pin_write(BOARD_LED_2, 1); + } +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/mpu-9250-sensor.c b/platform/srf06-cc26xx/sensortag/mpu-9250-sensor.c new file mode 100644 index 000000000..fd7bff688 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/mpu-9250-sensor.c @@ -0,0 +1,685 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-mpu + * @{ + * + * \file + * Driver for the Sensortag-CC26XX Invensense MPU9250 motion processing unit + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "lib/sensors.h" +#include "mpu-9250-sensor.h" +#include "sys/rtimer.h" +#include "sensor-common.h" +#include "board-i2c.h" + +#include "ti-lib.h" + +#include +#include +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +/* Sensor I2C address */ +#define SENSOR_I2C_ADDRESS 0x68 +#define SENSOR_MAG_I2_ADDRESS 0x0C +/*---------------------------------------------------------------------------*/ +/* Registers */ +#define SELF_TEST_X_GYRO 0x00 /* R/W */ +#define SELF_TEST_Y_GYRO 0x01 /* R/W */ +#define SELF_TEST_Z_GYRO 0x02 /* R/W */ +#define SELF_TEST_X_ACCEL 0x0D /* R/W */ +#define SELF_TEST_Z_ACCEL 0x0E /* R/W */ +#define SELF_TEST_Y_ACCEL 0x0F /* R/W */ +/*---------------------------------------------------------------------------*/ +#define XG_OFFSET_H 0x13 /* R/W */ +#define XG_OFFSET_L 0x14 /* R/W */ +#define YG_OFFSET_H 0x15 /* R/W */ +#define YG_OFFSET_L 0x16 /* R/W */ +#define ZG_OFFSET_H 0x17 /* R/W */ +#define ZG_OFFSET_L 0x18 /* R/W */ +/*---------------------------------------------------------------------------*/ +#define SMPLRT_DIV 0x19 /* R/W */ +#define CONFIG 0x1A /* R/W */ +#define GYRO_CONFIG 0x1B /* R/W */ +#define ACCEL_CONFIG 0x1C /* R/W */ +#define ACCEL_CONFIG_2 0x1D /* R/W */ +#define LP_ACCEL_ODR 0x1E /* R/W */ +#define WOM_THR 0x1F /* R/W */ +#define FIFO_EN 0x23 /* R/W */ +/*---------------------------------------------------------------------------*/ +/* + * Registers 0x24 - 0x36 are not applicable to the SensorTag HW configuration + * (IC2 Master) + */ +#define INT_PIN_CFG 0x37 /* R/W */ +#define INT_ENABLE 0x38 /* R/W */ +#define INT_STATUS 0x3A /* R */ +#define ACCEL_XOUT_H 0x3B /* R */ +#define ACCEL_XOUT_L 0x3C /* R */ +#define ACCEL_YOUT_H 0x3D /* R */ +#define ACCEL_YOUT_L 0x3E /* R */ +#define ACCEL_ZOUT_H 0x3F /* R */ +#define ACCEL_ZOUT_L 0x40 /* R */ +#define TEMP_OUT_H 0x41 /* R */ +#define TEMP_OUT_L 0x42 /* R */ +#define GYRO_XOUT_H 0x43 /* R */ +#define GYRO_XOUT_L 0x44 /* R */ +#define GYRO_YOUT_H 0x45 /* R */ +#define GYRO_YOUT_L 0x46 /* R */ +#define GYRO_ZOUT_H 0x47 /* R */ +#define GYRO_ZOUT_L 0x48 /* R */ +/*---------------------------------------------------------------------------*/ +/* + * Registers 0x49 - 0x60 are not applicable to the SensorTag HW configuration + * (external sensor data) + * + * Registers 0x63 - 0x67 are not applicable to the SensorTag HW configuration + * (I2C master) + */ +#define SIGNAL_PATH_RESET 0x68 /* R/W */ +#define ACCEL_INTEL_CTRL 0x69 /* R/W */ +#define USER_CTRL 0x6A /* R/W */ +#define PWR_MGMT_1 0x6B /* R/W */ +#define PWR_MGMT_2 0x6C /* R/W */ +#define FIFO_COUNT_H 0x72 /* R/W */ +#define FIFO_COUNT_L 0x73 /* R/W */ +#define FIFO_R_W 0x74 /* R/W */ +#define WHO_AM_I 0x75 /* R/W */ +/*---------------------------------------------------------------------------*/ +/* Masks is mpuConfig valiable */ +#define ACC_CONFIG_MASK 0x38 +#define GYRO_CONFIG_MASK 0x07 +/*---------------------------------------------------------------------------*/ +/* Values PWR_MGMT_1 */ +#define MPU_SLEEP 0x4F /* Sleep + stop all clocks */ +#define MPU_WAKE_UP 0x09 /* Disable temp. + intern osc */ +/*---------------------------------------------------------------------------*/ +/* Values PWR_MGMT_2 */ +#define ALL_AXES 0x3F +#define GYRO_AXES 0x07 +#define ACC_AXES 0x38 +/*---------------------------------------------------------------------------*/ +/* Data sizes */ +#define DATA_SIZE 6 +/*---------------------------------------------------------------------------*/ +/* Output data rates */ +#define INV_LPA_0_3125HZ 0 +#define INV_LPA_0_625HZ 1 +#define INV_LPA_1_25HZ 2 +#define INV_LPA_2_5HZ 3 +#define INV_LPA_5HZ 4 +#define INV_LPA_10HZ 5 +#define INV_LPA_20HZ 6 +#define INV_LPA_40HZ 7 +#define INV_LPA_80HZ 8 +#define INV_LPA_160HZ 9 +#define INV_LPA_320HZ 10 +#define INV_LPA_640HZ 11 +#define INV_LPA_STOPPED 255 +/*---------------------------------------------------------------------------*/ +/* Bit values */ +#define BIT_ANY_RD_CLR 0x10 +#define BIT_RAW_RDY_EN 0x01 +#define BIT_WOM_EN 0x40 +#define BIT_LPA_CYCLE 0x20 +#define BIT_STBY_XA 0x20 +#define BIT_STBY_YA 0x10 +#define BIT_STBY_ZA 0x08 +#define BIT_STBY_XG 0x04 +#define BIT_STBY_YG 0x02 +#define BIT_STBY_ZG 0x01 +#define BIT_STBY_XYZA (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA) +#define BIT_STBY_XYZG (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG) +/*---------------------------------------------------------------------------*/ +/* User control register */ +#define BIT_ACTL 0x80 +#define BIT_LATCH_EN 0x20 +/*---------------------------------------------------------------------------*/ +/* INT Pin / Bypass Enable Configuration */ +#define BIT_AUX_IF_EN 0x20 /* I2C_MST_EN */ +#define BIT_BYPASS_EN 0x02 +/*---------------------------------------------------------------------------*/ +#define ACC_RANGE_INVALID -1 + +#define ACC_RANGE_2G 0 +#define ACC_RANGE_4G 1 +#define ACC_RANGE_8G 2 +#define ACC_RANGE_16G 3 + +#define MPU_AX_GYR_X 2 +#define MPU_AX_GYR_Y 1 +#define MPU_AX_GYR_Z 0 +#define MPU_AX_GYR 0x07 + +#define MPU_AX_ACC_X 5 +#define MPU_AX_ACC_Y 4 +#define MPU_AX_ACC_Z 3 +#define MPU_AX_ACC 0x38 + +#define MPU_AX_MAG 6 +/*---------------------------------------------------------------------------*/ +#define MPU_DATA_READY 0x01 +#define MPU_MOVEMENT 0x40 +/*---------------------------------------------------------------------------*/ +/* Sensor selection/deselection */ +#define SENSOR_SELECT() board_i2c_select(BOARD_I2C_INTERFACE_1, SENSOR_I2C_ADDRESS) +#define SENSOR_DESELECT() board_i2c_deselect() +/*---------------------------------------------------------------------------*/ +/* Delay */ +#define delay_ms(i) (ti_lib_cpu_delay(8000 * (i))) +/*---------------------------------------------------------------------------*/ +static uint8_t mpu_config; +static uint8_t acc_range; +static uint8_t acc_range_reg; +static uint8_t val; +static uint8_t interrupt_status; +/*---------------------------------------------------------------------------*/ +#define SENSOR_STATUS_DISABLED 0 +#define SENSOR_STATUS_BOOTING 1 +#define SENSOR_STATUS_ENABLED 2 + +static int enabled = SENSOR_STATUS_DISABLED; +static int elements; +/*---------------------------------------------------------------------------*/ +/* 3 16-byte words for all sensor readings */ +#define SENSOR_DATA_BUF_SIZE 3 + +static uint16_t sensor_value[SENSOR_DATA_BUF_SIZE]; +/*---------------------------------------------------------------------------*/ +/* + * Wait SENSOR_BOOT_DELAY ticks for the sensor to boot and + * SENSOR_STARTUP_DELAY for readings to be ready + * Gyro is a little slower than Acc + */ +#define SENSOR_BOOT_DELAY 8 +#define SENSOR_STARTUP_DELAY 5 + +static struct ctimer startup_timer; +/*---------------------------------------------------------------------------*/ +/* Wait for the MPU to have data ready */ +rtimer_clock_t t0; + +/* + * Wait timeout in rtimer ticks. This is just a random low number, since the + * first time we read the sensor status, it should be ready to return data + */ +#define READING_WAIT_TIMEOUT 10 +/*---------------------------------------------------------------------------*/ +/** + * \brief Place the MPU in low power mode + */ +static void +sensor_sleep(void) +{ + SENSOR_SELECT(); + + val = ALL_AXES; + sensor_common_write_reg(PWR_MGMT_2, &val, 1); + + val = MPU_SLEEP; + sensor_common_write_reg(PWR_MGMT_1, &val, 1); + SENSOR_DESELECT(); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Exit low power mode + */ +static void +sensor_wakeup(void) +{ + SENSOR_SELECT(); + val = MPU_WAKE_UP; + sensor_common_write_reg(PWR_MGMT_1, &val, 1); + + /* All axis initially disabled */ + val = ALL_AXES; + sensor_common_write_reg(PWR_MGMT_2, &val, 1); + mpu_config = 0; + + /* Restore the range */ + sensor_common_write_reg(ACCEL_CONFIG, &acc_range_reg, 1); + + /* Clear interrupts */ + sensor_common_read_reg(INT_STATUS, &val, 1); + SENSOR_DESELECT(); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Select gyro and accelerometer axes + */ +static void +select_axes(void) +{ + val = ~mpu_config; + sensor_common_write_reg(PWR_MGMT_2, &val, 1); +} +/*---------------------------------------------------------------------------*/ +static void +convert_to_le(uint8_t *data, uint8_t len) +{ + int i; + for(i = 0; i < len; i += 2) { + uint8_t tmp; + tmp = data[i]; + data[i] = data[i + 1]; + data[i + 1] = tmp; + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Set the range of the accelerometer + * \param new_range: ACC_RANGE_2G, ACC_RANGE_4G, ACC_RANGE_8G, ACC_RANGE_16G + * \return true if the write to the sensor succeeded + */ +static bool +acc_set_range(uint8_t new_range) +{ + bool success; + + if(new_range == acc_range) { + return true; + } + + success = false; + + acc_range_reg = (new_range << 3); + + /* Apply the range */ + SENSOR_SELECT(); + success = sensor_common_write_reg(ACCEL_CONFIG, &acc_range_reg, 1); + SENSOR_DESELECT(); + + if(success) { + acc_range = new_range; + } + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise the MPU + * \return True if success + */ +static bool +init_sensor(void) +{ + bool ret; + + interrupt_status = false; + acc_range = ACC_RANGE_INVALID; + mpu_config = 0; /* All axes off */ + + /* Device reset */ + val = 0x80; + SENSOR_SELECT(); + ret = sensor_common_write_reg(PWR_MGMT_1, &val, 1); + SENSOR_DESELECT(); + + if(ret) { + delay_ms(200); + + /* Initial configuration */ + acc_set_range(ACC_RANGE_8G); + /* Power save */ + sensor_sleep(); + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Check whether a data or wake on motion interrupt has occurred + * \return Return the interrupt status + * + * This driver does not use interrupts, however this function allows us to + * determine whether a new sensor reading is available + */ +static uint8_t +int_status(void) +{ + SENSOR_SELECT(); + sensor_common_read_reg(INT_STATUS, &interrupt_status, 1); + SENSOR_DESELECT(); + + return interrupt_status; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Enable the MPU + * \param axes: Gyro bitmap [0..2], X = 1, Y = 2, Z = 4. 0 = gyro off + * Acc bitmap [3..5], X = 8, Y = 16, Z = 32. 0 = accelerometer off + */ +static void +enable_sensor(uint16_t axes) +{ + if(mpu_config == 0 && axes != 0) { + /* Wake up the sensor if it was off */ + sensor_wakeup(); + } + + mpu_config = axes; + + if(mpu_config != 0) { + /* Enable gyro + accelerometer readout */ + select_axes(); + delay_ms(10); + } else if(mpu_config == 0) { + sensor_sleep(); + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Read data from the accelerometer - X, Y, Z - 3 words + * \return True if a valid reading could be taken, false otherwise + */ +static bool +acc_read(uint16_t *data) +{ + bool success; + + if(interrupt_status & BIT_RAW_RDY_EN) { + /* Burst read of all accelerometer values */ + SENSOR_SELECT(); + success = sensor_common_read_reg(ACCEL_XOUT_H, (uint8_t *)data, DATA_SIZE); + SENSOR_DESELECT(); + + if(success) { + convert_to_le((uint8_t *)data, DATA_SIZE); + } else { + sensor_common_set_error_data((uint8_t *)data, DATA_SIZE); + } + } else { + /* Data not ready */ + success = false; + } + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Read data from the gyroscope - X, Y, Z - 3 words + * \return True if a valid reading could be taken, false otherwise + */ +static bool +gyro_read(uint16_t *data) +{ + bool success; + + if(interrupt_status & BIT_RAW_RDY_EN) { + /* Select this sensor */ + SENSOR_SELECT(); + + /* Burst read of all gyroscope values */ + success = sensor_common_read_reg(GYRO_XOUT_H, (uint8_t *)data, DATA_SIZE); + + SENSOR_DESELECT(); + + if(success) { + convert_to_le((uint8_t *)data, DATA_SIZE); + } else { + sensor_common_set_error_data((uint8_t *)data, DATA_SIZE); + } + } else { + success = false; + } + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Convert accelerometer raw reading to a value in G + * \param raw_data The raw accelerometer reading + * \return The converted value + */ +static float +acc_convert(int16_t raw_data) +{ + float v = 0; + + switch(acc_range) { + case ACC_RANGE_2G: + /* Calculate acceleration, unit G, range -2, +2 */ + v = (raw_data * 1.0) / (32768 / 2); + break; + case ACC_RANGE_4G: + /* Calculate acceleration, unit G, range -4, +4 */ + v = (raw_data * 1.0) / (32768 / 4); + break; + case ACC_RANGE_8G: + /* Calculate acceleration, unit G, range -8, +8 */ + v = (raw_data * 1.0) / (32768 / 8); + break; + case ACC_RANGE_16G: + /* Calculate acceleration, unit G, range -16, +16 */ + v = (raw_data * 1.0) / (32768 / 16); + break; + default: + v = 0; + break; + } + + return v; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Convert gyro raw reading to a value in deg/sec + * \param raw_data The raw accelerometer reading + * \return The converted value + */ +static float +gyro_convert(int16_t raw_data) +{ + /* calculate rotation, unit deg/s, range -250, +250 */ + return (raw_data * 1.0) / (65536 / 500); +} +/*---------------------------------------------------------------------------*/ +static void +notify_ready(void *not_used) +{ + enabled = SENSOR_STATUS_ENABLED; + sensors_changed(&mpu_9250_sensor); +} +/*---------------------------------------------------------------------------*/ +static void +initialise(void *not_used) +{ + init_sensor(); + + /* Configure the accelerometer range */ + if((elements & MPU_9250_SENSOR_TYPE_ACC) != 0) { + acc_set_range(MPU_9250_SENSOR_ACC_RANGE); + } + + enable_sensor(elements & MPU_9250_SENSOR_TYPE_ALL); + + ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +power_up(void) +{ + ti_lib_gpio_pin_write(BOARD_MPU_POWER, 1); + enabled = SENSOR_STATUS_BOOTING; + + ctimer_set(&startup_timer, SENSOR_BOOT_DELAY, initialise, NULL); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns a reading from the sensor + * \param type MPU_9250_SENSOR_TYPE_ACC_[XYZ] or MPU_9250_SENSOR_TYPE_GYRO_[XYZ] + * \return centi-G (ACC) or centi-Deg/Sec (Gyro) + */ +static int +value(int type) +{ + int rv; + float converted_val = 0; + + if(enabled == SENSOR_STATUS_DISABLED) { + PRINTF("MPU: Sensor Disabled\n"); + return CC26XX_SENSOR_READING_ERROR; + } + + memset(sensor_value, 0, sizeof(sensor_value)); + + if((type & MPU_9250_SENSOR_TYPE_ACC) != 0) { + t0 = RTIMER_NOW(); + + while(!int_status() && + (RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + READING_WAIT_TIMEOUT))); + + rv = acc_read(sensor_value); + + if(rv == 0) { + return CC26XX_SENSOR_READING_ERROR; + } + + PRINTF("MPU: ACC = 0x%04x 0x%04x 0x%04x = ", + sensor_value[0], sensor_value[1], sensor_value[2]); + + /* Convert */ + if(type == MPU_9250_SENSOR_TYPE_ACC_X) { + converted_val = acc_convert(sensor_value[0]); + } else if(type == MPU_9250_SENSOR_TYPE_ACC_Y) { + converted_val = acc_convert(sensor_value[1]); + } else if(type == MPU_9250_SENSOR_TYPE_ACC_Z) { + converted_val = acc_convert(sensor_value[2]); + } + rv = (int)(converted_val * 100); + } else if((type & MPU_9250_SENSOR_TYPE_GYRO) != 0) { + t0 = RTIMER_NOW(); + + while(!int_status() && + (RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + READING_WAIT_TIMEOUT))); + + rv = gyro_read(sensor_value); + + if(rv == 0) { + return CC26XX_SENSOR_READING_ERROR; + } + + PRINTF("MPU: Gyro = 0x%04x 0x%04x 0x%04x = ", + sensor_value[0], sensor_value[1], sensor_value[2]); + + if(type == MPU_9250_SENSOR_TYPE_GYRO_X) { + converted_val = gyro_convert(sensor_value[0]); + } else if(type == MPU_9250_SENSOR_TYPE_GYRO_Y) { + converted_val = gyro_convert(sensor_value[1]); + } else if(type == MPU_9250_SENSOR_TYPE_GYRO_Z) { + converted_val = gyro_convert(sensor_value[2]); + } + rv = (int)(converted_val * 100); + } else { + PRINTF("MPU: Invalid type\n"); + rv = CC26XX_SENSOR_READING_ERROR; + } + + PRINTF("%ld\n", (long int)(converted_val * 100)); + + return rv; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the MPU9250 sensor. + * + * \param type Activate, enable or disable the sensor. See below + * \param enable + * + * When type == SENSORS_HW_INIT we turn on the hardware + * When type == SENSORS_ACTIVE and enable==1 we enable the sensor + * When type == SENSORS_ACTIVE and enable==0 we disable the sensor + */ +static int +configure(int type, int enable) +{ + switch(type) { + case SENSORS_HW_INIT: + elements = MPU_9250_SENSOR_TYPE_NONE; + break; + case SENSORS_ACTIVE: + if((enable & MPU_9250_SENSOR_TYPE_ACC) != 0 || + (enable & MPU_9250_SENSOR_TYPE_GYRO) != 0) { + PRINTF("MPU: Enabling\n"); + elements = enable & MPU_9250_SENSOR_TYPE_ALL; + + power_up(); + + enabled = SENSOR_STATUS_BOOTING; + } else { + PRINTF("MPU: Disabling\n"); + ctimer_stop(&startup_timer); + elements = MPU_9250_SENSOR_TYPE_NONE; + enable_sensor(0); + while(ti_lib_i2c_master_busy(I2C0_BASE)); + enabled = SENSOR_STATUS_DISABLED; + } + break; + case MPU_9250_SENSOR_SHUTDOWN: + ti_lib_gpio_pin_write(BOARD_MPU_POWER, 0); + break; + default: + break; + } + return enabled; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the status of the sensor + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the sensor is enabled + */ +static int +status(int type) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return enabled; + break; + default: + break; + } + return SENSOR_STATUS_DISABLED; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(mpu_9250_sensor, "MPU9250", value, configure, status); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/mpu-9250-sensor.h b/platform/srf06-cc26xx/sensortag/mpu-9250-sensor.h new file mode 100644 index 000000000..46836a538 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/mpu-9250-sensor.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-mpu SensorTag 2.0 Motion Processing Unit + * + * Driver for the Invensense MPU9250 Motion Processing Unit. + * + * Due to the time required between triggering a reading and the reading + * becoming available, this driver is meant to be used in an asynchronous + * fashion. The caller must first activate the sensor by calling + * mpu_9250_sensor.configure(SENSORS_ACTIVE, xyz); + * The value for the xyz arguments depends on the required readings. If the + * caller intends to read both the accelerometer as well as the gyro then + * xyz should be MPU_9250_SENSOR_TYPE_ALL. If the caller only needs to take a + * reading from one of the two elements, xyz should be one of + * MPU_9250_SENSOR_TYPE_ACC or MPU_9250_SENSOR_TYPE_GYRO + * + * Calling .configure() will power up the sensor and initialise it. When the + * sensor is ready to provide readings, the driver will generate a + * sensors_changed event. + * + * Calls to .status() will return the driver's state which could indicate that + * the sensor is off, booting or on. + * + * Once a reading has been taken, the caller has two options: + * - Turn the sensor off by calling SENSORS_DEACTIVATE, but in order to take + * subsequent readings the sensor must be started up all over + * - Leave the sensor on. In this scenario, the caller can simply keep calling + * value() for subsequent readings, but having the sensor on will consume + * more energy, especially if both accelerometer and the gyro are on. + * @{ + * + * \file + * Header file for the Sensortag-CC26XX Invensense MPU9250 motion processing unit + */ +/*---------------------------------------------------------------------------*/ +#ifndef MPU_9250_SENSOR_H_ +#define MPU_9250_SENSOR_H_ +/*---------------------------------------------------------------------------*/ +/* ACC / Gyro Axes */ +#define MPU_9250_SENSOR_TYPE_GYRO_Z 0x01 +#define MPU_9250_SENSOR_TYPE_GYRO_Y 0x02 +#define MPU_9250_SENSOR_TYPE_GYRO_X 0x04 +#define MPU_9250_SENSOR_TYPE_GYRO_ALL 0x07 + +#define MPU_9250_SENSOR_TYPE_ACC_Z 0x08 +#define MPU_9250_SENSOR_TYPE_ACC_Y 0x10 +#define MPU_9250_SENSOR_TYPE_ACC_X 0x20 +#define MPU_9250_SENSOR_TYPE_ACC_ALL 0x38 + +#define MPU_9250_SENSOR_TYPE_MASK 0x3F +#define MPU_9250_SENSOR_TYPE_ACC 0x38 +#define MPU_9250_SENSOR_TYPE_GYRO 0x07 + +#define MPU_9250_SENSOR_TYPE_NONE 0 +#define MPU_9250_SENSOR_TYPE_ALL (MPU_9250_SENSOR_TYPE_ACC | \ + MPU_9250_SENSOR_TYPE_GYRO) + +#define MPU_9250_SENSOR_SHUTDOWN 0xFF +/*---------------------------------------------------------------------------*/ +/* Accelerometer range */ +#define MPU_9250_SENSOR_ACC_RANGE_2G 0 +#define MPU_9250_SENSOR_ACC_RANGE_4G 1 +#define MPU_9250_SENSOR_ACC_RANGE_8G 2 +#define MPU_9250_SENSOR_ACC_RANGE_16G 3 +/*---------------------------------------------------------------------------*/ +/* Accelerometer range configuration */ +#ifdef MPU_9250_SENSOR_CONF_ACC_RANGE +#define MPU_9250_SENSOR_ACC_RANGE MPU_9250_SENSOR_CONF_ACC_RANGE +#else +#define MPU_9250_SENSOR_ACC_RANGE MPU_9250_SENSOR_ACC_RANGE_2G +#endif +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor mpu_9250_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* MPU_9250_SENSOR_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/opt-3001-sensor.c b/platform/srf06-cc26xx/sensortag/opt-3001-sensor.c new file mode 100644 index 000000000..7cb41bf6d --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/opt-3001-sensor.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-opt-sensor + * @{ + * + * \file + * Driver for the Sensortag-CC26xx Opt3001 light sensor + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "lib/sensors.h" +#include "opt-3001-sensor.h" +#include "sys/ctimer.h" +#include "ti-lib.h" +#include "board-i2c.h" +#include "sensor-common.h" + +#include +#include +#include +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +/* Slave address */ +#define OPT3001_I2C_ADDRESS 0x45 +/*---------------------------------------------------------------------------*/ +/* Register addresses */ +#define REG_RESULT 0x00 +#define REG_CONFIGURATION 0x01 +#define REG_LOW_LIMIT 0x02 +#define REG_HIGH_LIMIT 0x03 + +#define REG_MANUFACTURER_ID 0x7E +#define REG_DEVICE_ID 0x7F +/*---------------------------------------------------------------------------*/ +/* Register values */ +#define MANUFACTURER_ID 0x5449 /* TI */ +#define DEVICE_ID 0x3001 /* Opt 3001 */ +#define CONFIG_RESET 0xC810 +#define CONFIG_TEST 0xCC10 +#define CONFIG_ENABLE 0x10CC /* 0xCC10 */ +#define CONFIG_DISABLE 0x108C /* 0xC810 */ +/*---------------------------------------------------------------------------*/ +/* Bit values */ +#define DATA_RDY_BIT 0x0080 /* Data ready */ +/*---------------------------------------------------------------------------*/ +/* Register length */ +#define REGISTER_LENGTH 2 +/*---------------------------------------------------------------------------*/ +/* Sensor data size */ +#define DATA_LENGTH 2 +/*---------------------------------------------------------------------------*/ +#define SENSOR_STATUS_DISABLED 0 +#define SENSOR_STATUS_NOT_READY 1 +#define SENSOR_STATUS_ENABLED 2 + +static int enabled = SENSOR_STATUS_DISABLED; +/*---------------------------------------------------------------------------*/ +/* Wait SENSOR_STARTUP_DELAY for the sensor to be ready - 125ms */ +#define SENSOR_STARTUP_DELAY (CLOCK_SECOND >> 3) + +static struct ctimer startup_timer; +/*---------------------------------------------------------------------------*/ +static void +notify_ready(void *not_used) +{ + enabled = SENSOR_STATUS_ENABLED; + sensors_changed(&opt_3001_sensor); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Select the sensor on the I2C bus + */ +static void +select(void) +{ + /* Select slave and set clock rate */ + board_i2c_select(BOARD_I2C_INTERFACE_0, OPT3001_I2C_ADDRESS); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Turn the sensor on/off + * \param enable TRUE: on, FALSE: off + */ +static void +enable_sensor(bool enable) +{ + uint16_t val; + + select(); + + if(enable) { + val = CONFIG_ENABLE; + } else { + val = CONFIG_DISABLE; + } + + sensor_common_write_reg(REG_CONFIGURATION, (uint8_t *)&val, REGISTER_LENGTH); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Read the result register + * \param raw_data Pointer to a buffer to store the reading + * \return TRUE if valid data + */ +static bool +read_data(uint16_t *raw_data) +{ + bool success; + uint16_t val; + + select(); + + success = sensor_common_read_reg(REG_CONFIGURATION, (uint8_t *)&val, + REGISTER_LENGTH); + + if(success) { + success = (val & DATA_RDY_BIT) == DATA_RDY_BIT; + } + + if(success) { + success = sensor_common_read_reg(REG_RESULT, (uint8_t *)&val, DATA_LENGTH); + } + + if(success) { + /* Swap bytes */ + *raw_data = (val << 8) | (val >> 8 & 0xFF); + } else { + sensor_common_set_error_data((uint8_t *)raw_data, DATA_LENGTH); + } + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Convert raw data to a value in lux + * \param data Pointer to a buffer with a raw sensor reading + * \return Converted value (lux) + */ +static float +convert(uint16_t raw_data) +{ + uint16_t e, m; + + m = raw_data & 0x0FFF; + e = (raw_data & 0xF000) >> 12; + + return m * (0.01 * exp2(e)); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns a reading from the sensor + * \param type Ignored + * \return Illuminance in centilux + */ +static int +value(int type) +{ + int rv; + uint16_t raw_val; + float converted_val; + + if(enabled != SENSOR_STATUS_ENABLED) { + PRINTF("Sensor disabled or starting up (%d)\n", enabled); + return CC26XX_SENSOR_READING_ERROR; + } + + rv = read_data(&raw_val); + + if(rv == 0) { + return CC26XX_SENSOR_READING_ERROR; + } + + converted_val = convert(raw_val); + PRINTF("OPT: %04X r=%d (centilux)\n", raw_val, + (int)(converted_val * 100)); + + rv = (int)(converted_val * 100); + + return rv; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the OPT3001 sensor. + * + * \param type Activate, enable or disable the sensor. See below + * \param enable + * + * When type == SENSORS_HW_INIT we turn on the hardware + * When type == SENSORS_ACTIVE and enable==1 we enable the sensor + * When type == SENSORS_ACTIVE and enable==0 we disable the sensor + */ +static int +configure(int type, int enable) +{ + switch(type) { + case SENSORS_HW_INIT: + break; + case SENSORS_ACTIVE: + if(enable) { + enable_sensor(1); + ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL); + enabled = SENSOR_STATUS_NOT_READY; + } else { + ctimer_stop(&startup_timer); + enable_sensor(0); + enabled = SENSOR_STATUS_DISABLED; + } + break; + default: + break; + } + return enabled; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the status of the sensor + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the sensor is enabled + */ +static int +status(int type) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return enabled; + break; + default: + break; + } + return SENSOR_STATUS_DISABLED; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(opt_3001_sensor, "OPT3001", value, configure, status); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/opt-3001-sensor.h b/platform/srf06-cc26xx/sensortag/opt-3001-sensor.h new file mode 100644 index 000000000..205ab00ff --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/opt-3001-sensor.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-opt-sensor SensorTag 2.0 Light Sensor + * + * Due to the time required for the sensor to startup, this driver is meant to + * be used in an asynchronous fashion. The caller must first activate the + * sensor by calling SENSORS_ACTIVATE(). This will trigger the sensor's startup + * sequence, but the call will not wait for it to complete so that the CPU can + * perform other tasks or drop to a low power mode. + * + * Once the sensor is stable, the driver will generate a sensors_changed event. + * + * Once a reading has been taken, the caller has two options: + * - Turn the sensor off by calling SENSORS_DEACTIVATE, but in order to take + * subsequent readings SENSORS_ACTIVATE must be called again + * - Leave the sensor on. In this scenario, the caller can simply keep calling + * value() for subsequent readings, but having the sensor on will consume + * energy + * @{ + * + * \file + * Header file for the Sensortag-CC26xx Opt3001 light sensor + */ +/*---------------------------------------------------------------------------*/ +#ifndef OPT_3001_SENSOR_H_ +#define OPT_3001_SENSOR_H_ +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor opt_3001_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* OPT_3001_SENSOR_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/reed-relay.c b/platform/srf06-cc26xx/sensortag/reed-relay.c new file mode 100644 index 000000000..b5956f6f8 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/reed-relay.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-reed-relay + * @{ + * + * \file + * Driver for the Sensortag-CC26XX Reed Relay + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "sys/clock.h" +#include "sys/timer.h" +#include "lib/sensors.h" +#include "sensortag/reed-relay.h" +#include "gpio-interrupt.h" +#include "sys/timer.h" + +#include "ti-lib.h" + +#include +/*---------------------------------------------------------------------------*/ +static struct timer debouncetimer; +/*---------------------------------------------------------------------------*/ +#define REED_ISR_CFG (IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | \ + IOC_IOPULL_DOWN | IOC_SLEW_DISABLE | \ + IOC_HYST_DISABLE | IOC_BOTH_EDGES | \ + IOC_INT_DISABLE | IOC_IOMODE_NORMAL | \ + IOC_NO_WAKE_UP | IOC_INPUT_ENABLE | \ + IOC_JTAG_DISABLE) +/*---------------------------------------------------------------------------*/ +/** + * \brief Handler for Sensortag-CC26XX reed interrupts + */ +static void +reed_interrupt_handler(uint8_t ioid) +{ + if(!timer_expired(&debouncetimer)) { + ENERGEST_OFF(ENERGEST_TYPE_IRQ); + return; + } + + sensors_changed(&reed_relay_sensor); + timer_set(&debouncetimer, CLOCK_SECOND / 2); +} +/*---------------------------------------------------------------------------*/ +static int +value(int type) +{ + return (int)ti_lib_gpio_pin_read(1 << BOARD_IOID_REED_RELAY); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the button sensor for all buttons. + * + * \param type SENSORS_HW_INIT: Initialise. SENSORS_ACTIVE: Enables/Disables + * depending on 'value' + * \param value 0: disable, non-zero: enable + * \return Always returns 1 + */ +static int +configure(int type, int value) +{ + switch(type) { + case SENSORS_HW_INIT: + ti_lib_ioc_int_disable(BOARD_IOID_REED_RELAY); + ti_lib_gpio_event_clear(1 << BOARD_IOID_REED_RELAY); + + /* Enabled the GPIO clock when the CM3 is running */ + ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_GPIO); + ti_lib_ioc_port_configure_set(BOARD_IOID_REED_RELAY, IOC_PORT_GPIO, + REED_ISR_CFG); + + gpio_interrupt_register_handler(BOARD_IOID_REED_RELAY, + reed_interrupt_handler); + break; + case SENSORS_ACTIVE: + if(value) { + ti_lib_ioc_int_enable(BOARD_IOID_REED_RELAY); + } else { + ti_lib_ioc_int_disable(BOARD_IOID_REED_RELAY); + } + break; + default: + break; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Status function for the reed + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 Interrupt enabled, 0: Disabled + */ +static int +status(int type) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return (ti_lib_ioc_port_configure_get(BOARD_IOID_REED_RELAY) + & IOC_INT_ENABLE) == IOC_INT_ENABLE; + break; + default: + break; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(reed_relay_sensor, "REED", value, configure, status); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/reed-relay.h b/platform/srf06-cc26xx/sensortag/reed-relay.h new file mode 100644 index 000000000..3ec712719 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/reed-relay.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-reed-relay SensorTag 2.0 Reed Relay + * + * The reed relay acts like a button without a button. To trigger the reed, + * approach a magnet to the sensortag and a sensors_changed event will be + * generated, in a fashion similar to as if a button had been pressed + * + * @{ + * + * \file + * Header file for the Sensortag-CC26XX Reed Relay + */ +/*---------------------------------------------------------------------------*/ +#ifndef REED_RELAY_H +#define REED_RELAY_H +/*---------------------------------------------------------------------------*/ +#include "lib/sensors.h" +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor reed_relay_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* REED_RELAY_H */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/sensor-common.c b/platform/srf06-cc26xx/sensortag/sensor-common.c new file mode 100644 index 000000000..0c09bf630 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/sensor-common.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-sensor-common + * @{ + * + * \file + * Utilities common among SensorTag sensors + */ +/*---------------------------------------------------------------------------*/ +#include "sensor-common.h" +#include "board-i2c.h" +/*---------------------------------------------------------------------------*/ +/* Data to use when an error occurs */ +#define ERROR_DATA 0xCC +/*---------------------------------------------------------------------------*/ +static uint8_t buffer[32]; +/*---------------------------------------------------------------------------*/ +bool +sensor_common_read_reg(uint8_t addr, uint8_t *buf, uint8_t len) +{ + return board_i2c_write_read(&addr, 1, buf, len); +} +/*---------------------------------------------------------------------------*/ +bool +sensor_common_write_reg(uint8_t addr, uint8_t *buf, uint8_t len) +{ + uint8_t i; + uint8_t *p = buffer; + + /* Copy address and data to local buffer for burst write */ + *p++ = addr; + for(i = 0; i < len; i++) { + *p++ = *buf++; + } + len++; + + /* Send data */ + return board_i2c_write(buffer, len); +} +/*---------------------------------------------------------------------------*/ +void +sensor_common_set_error_data(uint8_t *buf, uint8_t len) +{ + while(len > 0) { + len--; + buf[len] = ERROR_DATA; + } +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/sensor-common.h b/platform/srf06-cc26xx/sensortag/sensor-common.h new file mode 100644 index 000000000..977650d5e --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/sensor-common.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-sensor-common SensorTag 2.0 Sensors + * @{ + * + * \file + * Header file for the Sensortag-CC26xx Common sensor utilities + */ +/*---------------------------------------------------------------------------*/ +#ifndef SENSOR_H +#define SENSOR_H +/*---------------------------------------------------------------------------*/ +#include "board-i2c.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief Reads a sensor's register over I2C + * \param addr The address of the register to read + * \param buf Pointer to buffer to place data + * \param len Number of bytes to read + * \return TRUE if the required number of bytes are received + * + * The sensor must be selected before this routine is called. + */ +bool sensor_common_read_reg(uint8_t addr, uint8_t *buf, uint8_t len); + +/** + * \brief Write to a sensor's register over I2C + * \param addr The address of the register to read + * \param buf Pointer to buffer containing data to be written + * \param len Number of bytes to write + * \return TRUE if successful write + * + * The sensor must be selected before this routine is called. + */ +bool sensor_common_write_reg(uint8_t addr, uint8_t *buf, uint8_t len); + +/** + * \brief Fill a result buffer with dummy error data + * \param buf Pointer to the buffer where to write the data + * \param len Number of bytes to fill + * \return bitmask of error flags + */ +void sensor_common_set_error_data(uint8_t *buf, uint8_t len); +/*---------------------------------------------------------------------------*/ +#endif /* SENSOR_H */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/sensortag-sensors.c b/platform/srf06-cc26xx/sensortag/sensortag-sensors.c new file mode 100644 index 000000000..a8f26aefe --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/sensortag-sensors.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \file + * Generic module controlling sensors on CC26XX Sensortag + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "sensortag/button-sensor.h" +#include "sensortag/bmp-280-sensor.h" +#include "sensortag/tmp-007-sensor.h" +#include "sensortag/opt-3001-sensor.h" +#include "sensortag/sht-21-sensor.h" +#include "sensortag/mpu-9250-sensor.h" +#include "sensortag/reed-relay.h" + +#include +/*---------------------------------------------------------------------------*/ +/** \brief Exports a global symbol to be used by the sensor API */ +SENSORS(&button_left_sensor, &button_right_sensor, + &bmp_280_sensor, &tmp_007_sensor, &opt_3001_sensor, &sht_21_sensor, + &mpu_9250_sensor, &reed_relay_sensor); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/sht-21-sensor.c b/platform/srf06-cc26xx/sensortag/sht-21-sensor.c new file mode 100644 index 000000000..f90e12f55 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/sht-21-sensor.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-sht-sensor + * @{ + * + * \file + * Driver for the Sensortag-CC26xx Sensirion SHT21 Humidity sensor + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "sys/ctimer.h" +#include "lib/sensors.h" +#include "sht-21-sensor.h" +#include "sensor-common.h" +#include "board-i2c.h" + +#include "ti-lib.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +/* Sensor I2C address */ +#define HAL_SHT21_I2C_ADDRESS 0x40 +/*---------------------------------------------------------------------------*/ +#define S_REG_LEN 2 +#define DATA_LEN 3 +/*---------------------------------------------------------------------------*/ +/* Internal commands */ +#define SHT21_CMD_TEMP_T_NH 0xF3 /* command trig. temp meas. no hold master */ +#define SHT21_CMD_HUMI_T_NH 0xF5 /* command trig. humidity meas. no hold master */ +#define SHT21_CMD_WRITE_U_R 0xE6 /* command write user register */ +#define SHT21_CMD_READ_U_R 0xE7 /* command read user register */ +#define SHT21_CMD_SOFT_RST 0xFE /* command soft reset */ +/*---------------------------------------------------------------------------*/ +#define HUMIDITY 0x00 +#define TEMPERATURE 0x01 +/*---------------------------------------------------------------------------*/ +#define USR_REG_MASK 0x38 /* Mask off reserved bits (3,4,5) */ +#define USR_REG_DEFAULT 0x02 /* Disable OTP reload */ +#define USR_REG_RES_MASK 0x7E /* Only change bits 0 and 7 (meas. res.) */ +#define USR_REG_11BITRES 0x81 /* 11-bit resolution */ +/*---------------------------------------------------------------------------*/ +#define USR_REG_TEST_VAL 0x83 +/*---------------------------------------------------------------------------*/ +#define DATA_SIZE 6 +/*---------------------------------------------------------------------------*/ +static uint8_t usr; /* User register value */ +static uint8_t buf[DATA_SIZE]; /* Data buffer */ +static bool success; +/*---------------------------------------------------------------------------*/ +static int enabled = SHT_21_SENSOR_STATUS_DISABLED; +/*---------------------------------------------------------------------------*/ +/* + * Maximum measurement durations in clock ticks. We use 11bit resolution, thus: + * - Tmp: 11ms + * - RH: 15ms + */ +#define MEASUREMENT_DURATION 3 + +/* + * Wait SENSOR_STARTUP_DELAY clock ticks between activation and triggering a + * reading + */ +#define SENSOR_STARTUP_DELAY 4 + +static struct ctimer startup_timer; +/*---------------------------------------------------------------------------*/ +/** + * \brief Select the SHT sensor on the I2C-bus + */ +static void +select(void) +{ + /* Select the SHT21 address */ + board_i2c_select(BOARD_I2C_INTERFACE_0, HAL_SHT21_I2C_ADDRESS); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Write a command over I2C + * \param cmd The command to send + * \return TRUE if the command was sent successfully + */ +static bool +write_cmd(uint8_t cmd) +{ + /* Send command */ + return board_i2c_write_single(cmd); +} +/*---------------------------------------------------------------------------*/ +/* + * \brief Read data from the SHT over I2C + * \param buf Pointer to buffer where data will be stored + * \param len Number of bytes to read + * \return TRUE if the required number of bytes were received + */ +static bool +read_data(uint8_t *buf, uint8_t len) +{ + /* Read data */ + return board_i2c_read(buf, len); +} +/*---------------------------------------------------------------------------*/ +/** + * @brief Initialise the SHT21 sensor + */ +static void +sensor_init(void) +{ + select(); + + /* Set 11 bit resolution */ + sensor_common_read_reg(SHT21_CMD_READ_U_R, &usr, 1); + usr &= USR_REG_RES_MASK; + usr |= USR_REG_11BITRES; + sensor_common_write_reg(SHT21_CMD_WRITE_U_R, &usr, 1); + success = true; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Start a temperature measurement + */ +static void +start_temp(void) +{ + if(success) { + select(); + success = write_cmd(SHT21_CMD_TEMP_T_NH); + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Latch the last temperature measurement + */ +static void +latch_temp(void) +{ + if(success) { + select(); + success = read_data(buf, DATA_LEN); + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Start a humidity measurement + */ +static void +start_humidity(void) +{ + if(success) { + select(); + success = write_cmd(SHT21_CMD_HUMI_T_NH); + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Latch the last humidity measurement + */ +static void +latch_humidity(void) +{ + if(success) { + select(); + success = read_data(buf + DATA_LEN, DATA_LEN); + } +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Retrieve latched raw readings from buffer and store in variables + * \param raw_temp Pointer to a buffer where the temperature reading will be + * stored + * \param raw_hum Pointer to a buffer where the humidity reading will be + * stored + * \return TRUE on success + */ +static bool +get_readings(uint16_t *raw_temp, uint16_t *raw_hum) +{ + bool valid; + + valid = success; + if(!success) { + sensor_common_set_error_data(buf, DATA_SIZE); + } + + /* Store temperature */ + *raw_temp = buf[0] << 8 | buf[1]; + + /* [2] We ignore the CRC */ + + /* Store humidity */ + *raw_hum = buf[3] << 8 | buf[4]; + + /* [5] We ignore the CRC */ + + success = true; + + return valid; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Convert raw data to temperature (degrees C) and rel. humidity (%RH) + * \param raw_temp Raw temperature data (little endian) + * \param raw_hum Raw humidity data (little endian) + * \param temp Converted temperature value + * \param hum Converted humidity value + */ +static void +convert(uint16_t raw_temp, uint16_t raw_hum, float *temp, float *hum) +{ + /* Convert temperature to degrees C */ + raw_temp &= ~0x0003; /* clear bits [1..0] (status bits) */ + *temp = -46.85 + 175.72 / 65536 * (double)(int16_t)raw_temp; + + /* Convert relative humidity to a %RH value */ + raw_hum &= ~0x0003; /* clear bits [1..0] (status bits) */ + + /* RH= -6 + 125 * SRH / 2^16 */ + *hum = -6.0 + 125.0 / 65536 * (double)raw_hum; +} +/*---------------------------------------------------------------------------*/ +static void +state_machine(void *not_used) +{ + switch(enabled) { + case SHT_21_SENSOR_STATUS_INITIALISED: + start_temp(); + enabled = SHT_21_SENSOR_STATUS_READING_TEMP; + break; + case SHT_21_SENSOR_STATUS_READING_TEMP: + latch_temp(); + start_humidity(); + enabled = SHT_21_SENSOR_STATUS_READING_HUMIDITY; + break; + case SHT_21_SENSOR_STATUS_READING_HUMIDITY: + latch_humidity(); + enabled = SHT_21_SENSOR_STATUS_READINGS_READY; + sensors_changed(&sht_21_sensor); + return; + case SHT_21_SENSOR_STATUS_READINGS_READY: + case SHT_21_SENSOR_STATUS_DISABLED: + default: + ctimer_stop(&startup_timer); + return; + } + + ctimer_set(&startup_timer, MEASUREMENT_DURATION, state_machine, NULL); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns a reading from the sensor + * \param type SHT_21_SENSOR_TYPE_TEMP or SHT_21_SENSOR_TYPE_HUMIDITY + * \return Temperature (centi degrees C) or Humidity (centi %RH) + */ +static int +value(int type) +{ + int rv; + uint16_t raw_temp; + uint16_t raw_hum; + float temp; + float hum; + + if(enabled != SHT_21_SENSOR_STATUS_READINGS_READY) { + PRINTF("Sensor disabled or starting up (%d)\n", enabled); + return CC26XX_SENSOR_READING_ERROR; + } + + if((type != SHT_21_SENSOR_TYPE_TEMP) && type != SHT_21_SENSOR_TYPE_HUMIDITY) { + PRINTF("Invalid type\n"); + return CC26XX_SENSOR_READING_ERROR; + } else { + rv = get_readings(&raw_temp, &raw_hum); + + if(rv == 0) { + return CC26XX_SENSOR_READING_ERROR; + } + + convert(raw_temp, raw_hum, &temp, &hum); + PRINTF("SHT: %04X %04X t=%d h=%d\n", raw_temp, raw_hum, + (int)(temp * 100), (int)(hum * 100)); + + if(type == SHT_21_SENSOR_TYPE_TEMP) { + rv = (int)(temp * 100); + } else if(type == SHT_21_SENSOR_TYPE_HUMIDITY) { + rv = (int)(hum * 100); + } + } + return rv; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the SHT21 sensor. + * + * \param type Activate, enable or disable the sensor. See below + * \param enable + * + * When type == SENSORS_HW_INIT we turn on the hardware + * When type == SENSORS_ACTIVE and enable==1 we enable the sensor + * When type == SENSORS_ACTIVE and enable==0 we disable the sensor + */ +static int +configure(int type, int enable) +{ + switch(type) { + case SENSORS_HW_INIT: + sensor_init(); + enabled = SHT_21_SENSOR_STATUS_INITIALISED; + break; + case SENSORS_ACTIVE: + /* Must be initialised first */ + if(enabled == SHT_21_SENSOR_STATUS_DISABLED) { + return SHT_21_SENSOR_STATUS_DISABLED; + } + if(enable) { + enabled = SHT_21_SENSOR_STATUS_INITIALISED; + ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, state_machine, NULL); + } else { + ctimer_stop(&startup_timer); + enabled = SHT_21_SENSOR_STATUS_INITIALISED; + } + break; + default: + break; + } + return enabled; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the status of the sensor + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return One of the SENSOR_STATUS_xyz defines + */ +static int +status(int type) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return enabled; + break; + default: + break; + } + return SHT_21_SENSOR_STATUS_DISABLED; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(sht_21_sensor, "SHT21", value, configure, status); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/sht-21-sensor.h b/platform/srf06-cc26xx/sensortag/sht-21-sensor.h new file mode 100644 index 000000000..af2f5d401 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/sht-21-sensor.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-sht-sensor SensorTag 2.0 Humidity Sensor + * + * Due to the time required between triggering a reading and the reading + * becoming available, this driver is meant to be used in an asynchronous + * fashion. The caller must first activate the sensor by calling + * SENSORS_ACTIVATE(). This will trigger a cycle which will read both + * temperature and humidity, but the call will not wait for the cycle to + * complete so that the CPU can perform other tasks or drop to a low power + * mode. + * + * Once readings are available, the driver will generate a sensors_changed + * event. + * + * Calls to .status() will return the driver's state which could indicate that + * a measurement is in progress (different return values for each) or that + * readings are ready. + * + * Multiple calls to value() will simply return the reading that was latched + * after the last cycle. In order to obtain fresh readings, a new cycle must be + * started by a new call to SENSORS_ACTIVATE. + * @{ + * + * \file + * Header file for the Sensortag-CC26ss Sensirion SHT21 Humidity sensor + */ +/*---------------------------------------------------------------------------*/ +#ifndef SHT_21_SENSOR_H_ +#define SHT_21_SENSOR_H_ +/*---------------------------------------------------------------------------*/ +#define SHT_21_SENSOR_TYPE_TEMP 1 +#define SHT_21_SENSOR_TYPE_HUMIDITY 2 +/*---------------------------------------------------------------------------*/ +/** + * \name SHT21 driver states + * @{ + */ +#define SHT_21_SENSOR_STATUS_DISABLED 0 /**< Not initialised */ +#define SHT_21_SENSOR_STATUS_INITIALISED 1 /**< Initialised but idle */ +#define SHT_21_SENSOR_STATUS_READING_TEMP 2 /**< Temp reading in progress */ +#define SHT_21_SENSOR_STATUS_READING_HUMIDITY 3 /**< Humidity reading in progress */ +#define SHT_21_SENSOR_STATUS_READINGS_READY 4 /**< Both readings ready */ +/** @} */ +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor sht_21_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* SHT_21_SENSOR_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/srf06-cc26xx/sensortag/tmp-007-sensor.c b/platform/srf06-cc26xx/sensortag/tmp-007-sensor.c new file mode 100644 index 000000000..8f880cb54 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/tmp-007-sensor.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-tmp-sensor + * @{ + * + * \file + * Driver for the Sensortag-CC26xx TI TMP007 infrared thermophile sensor + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "lib/sensors.h" +#include "tmp-007-sensor.h" +#include "sys/ctimer.h" +#include "board-i2c.h" +#include "sensor-common.h" +#include "ti-lib.h" + +#include +#include +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +/* Slave address */ +#define SENSOR_I2C_ADDRESS 0x44 +/*---------------------------------------------------------------------------*/ +/* TMP007 register addresses */ +#define TMP007_REG_ADDR_VOLTAGE 0x00 +#define TMP007_REG_ADDR_LOCAL_TEMP 0x01 +#define TMP007_REG_ADDR_CONFIG 0x02 +#define TMP007_REG_ADDR_OBJ_TEMP 0x03 +#define TMP007_REG_ADDR_STATUS 0x04 +#define TMP007_REG_PROD_ID 0x1F +/*---------------------------------------------------------------------------*/ +/* TMP007 register values */ +#define TMP007_VAL_CONFIG_ON 0x1000 /* Sensor on state */ +#define TMP007_VAL_CONFIG_OFF 0x0000 /* Sensor off state */ +#define TMP007_VAL_CONFIG_RESET 0x8000 +#define TMP007_VAL_PROD_ID 0x0078 /* Product ID */ +/*---------------------------------------------------------------------------*/ +/* Conversion ready (status register) bit values */ +#define CONV_RDY_BIT 0x4000 +/*---------------------------------------------------------------------------*/ +/* Register length */ +#define REGISTER_LENGTH 2 +/*---------------------------------------------------------------------------*/ +/* Sensor data size */ +#define DATA_SIZE 4 +/*---------------------------------------------------------------------------*/ +/* Byte swap of 16-bit register value */ +#define HI_UINT16(a) (((a) >> 8) & 0xFF) +#define LO_UINT16(a) ((a) & 0xFF) + +#define SWAP(v) ((LO_UINT16(v) << 8) | HI_UINT16(v)) +/*---------------------------------------------------------------------------*/ +#define SELECT() board_i2c_select(BOARD_I2C_INTERFACE_0, SENSOR_I2C_ADDRESS); +/*---------------------------------------------------------------------------*/ +static uint8_t buf[DATA_SIZE]; +static uint16_t val; +/*---------------------------------------------------------------------------*/ +#define SENSOR_STATUS_DISABLED 0 +#define SENSOR_STATUS_INITIALISED 1 +#define SENSOR_STATUS_NOT_READY 2 +#define SENSOR_STATUS_READY 3 + +static int enabled = SENSOR_STATUS_DISABLED; +/*---------------------------------------------------------------------------*/ +/* Wait SENSOR_STARTUP_DELAY clock ticks for the sensor to be ready - 275ms */ +#define SENSOR_STARTUP_DELAY 36 + +static struct ctimer startup_timer; +/*---------------------------------------------------------------------------*/ +/* Latched values */ +static int obj_temp_latched; +static int amb_temp_latched; +/*---------------------------------------------------------------------------*/ +static void +notify_ready(void *not_used) +{ + enabled = SENSOR_STATUS_READY; + sensors_changed(&tmp_007_sensor); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Turn the sensor on/off + */ +static bool +enable_sensor(bool enable) +{ + bool success; + + SELECT() + + if(enable) { + val = TMP007_VAL_CONFIG_ON; + } else { + val = TMP007_VAL_CONFIG_OFF; + } + val = SWAP(val); + + success = sensor_common_write_reg(TMP007_REG_ADDR_CONFIG, (uint8_t *)&val, + REGISTER_LENGTH); + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Read the sensor value registers + * \param raw_temp Temperature in 16 bit format + * \param raw_obj_temp object temperature in 16 bit format + * \return TRUE if valid data could be retrieved + */ +static bool +read_data(uint16_t *raw_temp, uint16_t *raw_obj_temp) +{ + bool success; + + SELECT(); + + success = sensor_common_read_reg(TMP007_REG_ADDR_STATUS, (uint8_t *)&val, + REGISTER_LENGTH); + + if(success) { + val = SWAP(val); + success = val & CONV_RDY_BIT; + } + + if(success) { + success = sensor_common_read_reg(TMP007_REG_ADDR_LOCAL_TEMP, &buf[0], + REGISTER_LENGTH); + if(success) { + success = sensor_common_read_reg(TMP007_REG_ADDR_OBJ_TEMP, &buf[2], + REGISTER_LENGTH); + } + } + + if(!success) { + sensor_common_set_error_data(buf, 4); + } + + /* Swap byte order */ + *raw_temp = buf[0] << 8 | buf[1]; + *raw_obj_temp = buf[2] << 8 | buf[3]; + + return success; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Convert raw data to values in degrees C + * \param raw_temp raw ambient temperature from sensor + * \param raw_obj_temp raw object temperature from sensor + * \param obj converted object temperature + * \param amb converted ambient temperature + */ +static void +convert(uint16_t raw_temp, uint16_t raw_obj_temp, float *obj, float *amb) +{ + const float SCALE_LSB = 0.03125; + float t; + int it; + + it = (int)((raw_obj_temp) >> 2); + t = ((float)(it)) * SCALE_LSB; + *obj = t; + + it = (int)((raw_temp) >> 2); + t = (float)it; + *amb = t * SCALE_LSB; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns a reading from the sensor + * \param type TMP_007_SENSOR_TYPE_OBJECT or TMP_007_SENSOR_TYPE_AMBIENT + * \return Object or Ambient temperature in milli degrees C + */ +static int +value(int type) +{ + int rv; + uint16_t raw_temp; + uint16_t raw_obj_temp; + float obj_temp; + float amb_temp; + + if(enabled != SENSOR_STATUS_READY) { + PRINTF("Sensor disabled or starting up (%d)\n", enabled); + return CC26XX_SENSOR_READING_ERROR; + } + + if((type & TMP_007_SENSOR_TYPE_ALL) == 0) { + PRINTF("Invalid type\n"); + return CC26XX_SENSOR_READING_ERROR; + } + + rv = CC26XX_SENSOR_READING_ERROR; + + if(type == TMP_007_SENSOR_TYPE_ALL) { + rv = read_data(&raw_temp, &raw_obj_temp); + + if(rv == 0) { + return CC26XX_SENSOR_READING_ERROR; + } + + convert(raw_temp, raw_obj_temp, &obj_temp, &amb_temp); + PRINTF("TMP: %04X %04X o=%d a=%d\n", raw_temp, raw_obj_temp, + (int)(obj_temp * 1000), (int)(amb_temp * 1000)); + + obj_temp_latched = (int)(obj_temp * 1000); + amb_temp_latched = (int)(amb_temp * 1000); + rv = 1; + } else if(type == TMP_007_SENSOR_TYPE_OBJECT) { + rv = obj_temp_latched; + } else if(type == TMP_007_SENSOR_TYPE_AMBIENT) { + rv = amb_temp_latched; + } + + return rv; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Configuration function for the TMP007 sensor. + * + * \param type Activate, enable or disable the sensor. See below + * \param enable + * + * When type == SENSORS_HW_INIT we turn on the hardware + * When type == SENSORS_ACTIVE and enable==1 we enable the sensor + * When type == SENSORS_ACTIVE and enable==0 we disable the sensor + */ +static int +configure(int type, int enable) +{ + switch(type) { + case SENSORS_HW_INIT: + enable_sensor(false); + enabled = SENSOR_STATUS_INITIALISED; + break; + case SENSORS_ACTIVE: + /* Must be initialised first */ + if(enabled == SENSOR_STATUS_DISABLED) { + return SENSOR_STATUS_DISABLED; + } + if(enable) { + enable_sensor(true); + ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL); + enabled = SENSOR_STATUS_NOT_READY; + } else { + ctimer_stop(&startup_timer); + enable_sensor(false); + enabled = SENSOR_STATUS_INITIALISED; + } + break; + default: + break; + } + return enabled; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the status of the sensor + * \param type SENSORS_ACTIVE or SENSORS_READY + * \return 1 if the sensor is enabled + */ +static int +status(int type) +{ + switch(type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return enabled; + break; + default: + break; + } + return SENSOR_STATUS_DISABLED; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(tmp_007_sensor, "TMP007", value, configure, status); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/srf06-cc26xx/sensortag/tmp-007-sensor.h b/platform/srf06-cc26xx/sensortag/tmp-007-sensor.h new file mode 100644 index 000000000..10c96c8a0 --- /dev/null +++ b/platform/srf06-cc26xx/sensortag/tmp-007-sensor.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup sensortag-cc26xx-peripherals + * @{ + * + * \defgroup sensortag-cc26xx-tmp-sensor SensorTag 2.0 IR thermophile sensor + * + * Due to the time required for the sensor to startup, this driver is meant to + * be used in an asynchronous fashion. The caller must first activate the + * sensor by calling SENSORS_ACTIVATE(). This will trigger the sensor's startup + * sequence, but the call will not wait for it to complete so that the CPU can + * perform other tasks or drop to a low power mode. + * + * Once the sensor is stable, the driver will generate a sensors_changed event. + * + * The caller should then use value(TMP_007_SENSOR_TYPE_ALL) to read sensor + * values and latch them. Once completed successfully, individual readings can + * be retrieved with calls to value(TMP_007_SENSOR_TYPE_OBJECT) or + * value(TMP_007_SENSOR_TYPE_AMBIENT). + * + * Once required readings have been taken, the caller has two options: + * - Turn the sensor off by calling SENSORS_DEACTIVATE, but in order to take + * subsequent readings SENSORS_ACTIVATE must be called again + * - Leave the sensor on. In this scenario, the caller can simply keep calling + * value(TMP_007_SENSOR_TYPE_ALL) to read and latch new values. However + * keeping the sensor on will consume more energy + * @{ + * + * \file + * Header file for the Sensortag-CC26xx TI TMP007 infrared thermophile sensor + */ +/*---------------------------------------------------------------------------*/ +#ifndef TMP_007_SENSOR_H_ +#define TMP_007_SENSOR_H_ +/*---------------------------------------------------------------------------*/ +#define TMP_007_SENSOR_TYPE_OBJECT 1 +#define TMP_007_SENSOR_TYPE_AMBIENT 2 +#define TMP_007_SENSOR_TYPE_ALL 3 +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor tmp_007_sensor; +/*---------------------------------------------------------------------------*/ +#endif /* TMP_007_SENSOR_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ From c9071b69528bb1709c942f7a74760146bb073ca6 Mon Sep 17 00:00:00 2001 From: Jonas Olsson Date: Wed, 25 Feb 2015 13:12:20 +0100 Subject: [PATCH 4/7] Add CC26xx common platform files --- platform/srf06-cc26xx/Makefile.srf06-cc26xx | 36 ++ platform/srf06-cc26xx/README.md | 180 ++++++++++ platform/srf06-cc26xx/contiki-conf.h | 350 ++++++++++++++++++++ platform/srf06-cc26xx/contiki-main.c | 256 ++++++++++++++ 4 files changed, 822 insertions(+) create mode 100644 platform/srf06-cc26xx/Makefile.srf06-cc26xx create mode 100644 platform/srf06-cc26xx/README.md create mode 100644 platform/srf06-cc26xx/contiki-conf.h create mode 100644 platform/srf06-cc26xx/contiki-main.c diff --git a/platform/srf06-cc26xx/Makefile.srf06-cc26xx b/platform/srf06-cc26xx/Makefile.srf06-cc26xx new file mode 100644 index 000000000..7feefa126 --- /dev/null +++ b/platform/srf06-cc26xx/Makefile.srf06-cc26xx @@ -0,0 +1,36 @@ +# srf06-cc26xx platform makefile + +ifndef CONTIKI + $(error CONTIKI not defined! You must specify where CONTIKI resides!) +endif + +### Board and BSP selection +ifeq ($(BOARD),) + BOARD=srf06 +endif + +### Configure the build for the board and pull in board-specific sources +CONTIKI_TARGET_DIRS += . $(BOARD) +PLATFORM_ROOT_DIR = $(CONTIKI)/platform/$(TARGET) + +### Include the board dir if one exists +-include $(PLATFORM_ROOT_DIR)/$(BOARD)/Makefile.$(BOARD) + +CONTIKI_TARGET_SOURCEFILES += contiki-main.c +CONTIKI_TARGET_SOURCEFILES += sensors.c leds.c +CONTIKI_TARGET_SOURCEFILES += $(BOARD_SOURCEFILES) + +CONTIKI_SOURCEFILES += $(CONTIKI_TARGET_SOURCEFILES) + +CLEAN += *.srf06-cc26xx + +### Unless the example dictates otherwise, build with code size optimisations +ifndef SMALL + SMALL = 0 +endif + +### Define the CPU directory +CONTIKI_CPU=$(CONTIKI)/cpu/cc26xx +include $(CONTIKI_CPU)/Makefile.cc26xx + +MODULES += core/net core/net/mac core/net/mac/contikimac core/net/llsec diff --git a/platform/srf06-cc26xx/README.md b/platform/srf06-cc26xx/README.md new file mode 100644 index 000000000..4cfb9945d --- /dev/null +++ b/platform/srf06-cc26xx/README.md @@ -0,0 +1,180 @@ +Getting Started with Contiki for TI CC26xx +========================================== + +This guide's aim is to help you start using Contiki for TI's CC26xx. The +platform supports two different boards: + +* SmartRF 06 Evaluation Board with a CC26xx Evaluation Module (relevant files + and drivers are under `srf06/`) +* CC26xx SensorTag 2.0 (relevant drivers under `sensortag/`) + +The CPU code, common for both platforms, can be found under `$(CONTIKI)/cpu/cc26xx`. +The port was developed and tested with CC2650s, but the intention is for it to +work with the CC2630 as well. Thus, bug reports are welcome for both chips. +Bear in mind that the CC2630 does not have BLE capability. + +This port is only meant to work with 7x7mm chips + +This guide assumes that you have basic understanding of how to use the command +line and perform basic admin tasks on UNIX family OSs. + +Port Features +============= +The platform has the following key features: + +* Deep Sleep support with RAM retention for ultra-low energy consumption. +* Support for CC26xx RF in IEEE as well as BLE mode (BLE support is very basic + since Contiki does not provide a BLE stack). + +In terms of hardware support, the following drivers have been implemented: + +* SmartRF06 EB peripherals + * LEDs + * Buttons + * UART connectivity over the XDS100v3 backchannel +* SensorTag 2.0 + * LEDs + * Buttons (One of the buttons can be used as a shutdown button) + * Reed relay + * Motion Processing Unit (MPU9250 - Accelerometer, Gyro) + * BMP280 sensor + * TMP007 sensor + * SHT21 sensor + * OPT3001 sensor + * Buzzer + * External SPI flash + +Examples +======== +The port comes with two examples: A very basic example and a mode advanced one +(web demo). The former demonstrates how to read sensors and how to use board +peripherals. It also demonstrates how to send out BLE advertisements. +The latter includes a CoAP server, an MQTT client which connects and publishes +to the IBM quickstart service, a net-based UART and lastly a web server that +can be used to configure the rest of the example. + +More details about those two examples can be found in their respective READMEs. + +Requirements +============ +To use the port you need: + +* TI's CC26xxware sources (more below) +* Software to program the nodes. Use TI's SmartRF Flash Programmer +* A toolchain to build firmware: The port has been developed and tested with + GNU Tools for ARM Embedded Processors . + The port was developed and tested using this version: + + $ arm-none-eabi-gcc -v + [...] + gcc version 4.9.3 20141119 (release) [ARM/embedded-4_9-branch revision 218278] (GNU Tools for ARM Embedded Processors) + +* You may also need other drivers so that the SmartRF can communicate with your +operating system and so that you can use the chip's UART for I/O. Please read +the section ["Drivers" in the CC2538DK readme](https://github.com/contiki-os/contiki/tree/master/platform/cc2538dk#drivers). + +Environment +=========== +To use this port, you will need to download and extract CC26xxware sources, +provided by TI here http://www.ti.com/tool/cc26xxware. Once you have done this, you will need to configure the Contiki +build system so that it can locate and compile them as part of the build process. + +To do this, you will need to set the following environment variable: + +* `TI_CC26XXWARE` + + Stores the path to a directory containing the following: + + * cc26xxware sources under `$(TI_CC26XXWARE)/driverlib` + * cc26xxware includes under `$(TI_CC26XXWARE)/inc` + * Startup files under `$(TI_CC26XXWARE)/startup_files` + +This _must_ be a path relative to the Contiki source directory. For +example, if Contiki is in `/home/user/contiki-2.x` and the CC26xxware is in +`/home/user/cc26xxware`, then `TI_CC26XXWARE` must be set to `../cc26xxware` + +The variable can be set within the example's Makefile, by adding this: + + TI_CC26XXWARE=../cc26xxware + +or you can use an environment variable, like so: + + export TI_CC26XXWARE=../cc26xxware + +Filename conflicts between Contiki and CC26xxware +================================================= +There is a file called `timer.c` both in Contiki as well as in CC26xxware. The +way things are configured now, we don't use the latter. However, if you need to +start using it at some point, you will need to rename it: + +From `$(TI_CC26XXWARE)/driverlib/cc26xx/source/timer.c` to `driverlib-timer.c` + +Sensortag vs Srf06 +================== +To build for the sensortag, set `BOARD=sensortag`. You can do that by exporting +it as an environment variable, by adding it to your Makefile or by adding it to +your make command as an argument + +If the `BOARD` variable is not equal to `sensortag`, an image for the Srf06 +CC26XXEM will be built instead. + +If you want to switch between building for one platform to the other, make +certain to `make clean` before building for the new one, or you will get linker +errors. + +Low Power Operation +=================== +The platform takes advantage of the CC26xx's power saving features. In a +nutshell, here is how things work: + +* When the RF is TXing, the CPU will enter sleep mode and will resume after TX + has completed. +* When there are no events in the Contiki event queue, the chip will enter + 'some' low power mode (more below). + +We do not use pre-defined power profiles (e.g. as mentioned in the TRM or as +we do for the CC2538 with LPM1, LPM2 etc). Each time we enter low power +operation, we either put the CM3 to sleep or to deep sleep. The latter case is +highly configurable: the LPM engine allows other code modules to register +themselves for notifications and to configure low power operation. With these +facilities, a module can e.g. prohibit deep sleep altogether, or it can request +that a power domain be kept powered. The LPM engine will turn off as many +CC26xx components as it can while satisfying all restrictions set by registered +modules. + +To determine which power mode to use, the following logic is followed: + +* The deepest available low power mode can be hard-coded by using + the `LPM_MODE_MAX_SUPPORTED` macro in the LPM driver (`lpm.[ch]`). Thus, it + is possible to prohibit deep sleep altogether. +* Code modules which are affected by low power operation can 'register' + themselves with the LPM driver. +* If the projected low-power duration is lower than `STANDBY_MIN_DURATION`, + the chip will simply sleep. +* If the projected low power duration is sufficiently long, the LPM will visit + all registered modules to query the maximum allowed power mode (maximum means + sleep vs deep sleep in this context). It will then drop to this power mode. + This is where a code module can forbid deep sleep if required. +* All registered modules will be notified when the chip is about to enter + deep sleep, as well as after wake-up. + +When the chip does enter deep sleep: + +* The RF Core, VIMS, SYSBUS and CPU power domains are always turned off. Due to + the way the RF driver works, the RFCORE PD should be off already. +* Peripheral clocks stop +* The Serial and Peripheral power domains are turned off, unless an LPM module + requests them to stay operational. For example, the net-uart demo keeps the + serial power domain powered on and the UART clocked under sleep and deep + sleep in order to retain UART RX functionality. +* If both SERIAL and PERIPH PDs are turned off, we also switch power source to + the uLDO for ultra low leakage under deep sleep. + +The chip will come out of low power mode by one of the following events: + +* Button press or, in the case of the SensorTag, a reed relay trigger +* Software clock tick (timer). The clock ticks at 128Hz, therefore the maximum + time we will ever spend in a sleep mode is 7.8125ms. In hardware terms, this + is an AON RTC Channel 2 compare interrupt. +* Rtimer triggers, as part of ContikiMAC's sleep/wake-up cycles. The rtimer + sits on the AON RTC channel 0. diff --git a/platform/srf06-cc26xx/contiki-conf.h b/platform/srf06-cc26xx/contiki-conf.h new file mode 100644 index 000000000..6a99c29b3 --- /dev/null +++ b/platform/srf06-cc26xx/contiki-conf.h @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-srf-tag + * @{ + * + * \file + * Configuration for the srf06-cc26xx platform + */ +#ifndef CONTIKI_CONF_H +#define CONTIKI_CONF_H + +#include +/*---------------------------------------------------------------------------*/ +/* Include Project Specific conf */ +#ifdef PROJECT_CONF_H +#include PROJECT_CONF_H +#endif /* PROJECT_CONF_H */ +/*---------------------------------------------------------------------------*/ +/** + * \name CC26xx flavour selection + * @{ + */ +#ifndef CC26XX_MODEL_CONF_CPU_VARIANT +#define CC26XX_MODEL_CONF_CPU_VARIANT 2650 /**< 2650 => CC2650, 2630 => CC2630 */ + +#endif +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Network Stack Configuration + * + * @{ + */ +#ifndef NETSTACK_CONF_NETWORK +#if NETSTACK_CONF_WITH_IPV6 +#define NETSTACK_CONF_NETWORK sicslowpan_driver +#else +#define NETSTACK_CONF_NETWORK rime_driver +#endif /* NETSTACK_CONF_WITH_IPV6 */ +#endif /* NETSTACK_CONF_NETWORK */ + +#ifndef NETSTACK_CONF_MAC +#define NETSTACK_CONF_MAC csma_driver +#endif + +#ifndef NETSTACK_CONF_RDC +#define NETSTACK_CONF_RDC contikimac_driver +#endif + +/* Configure NullRDC for when it's selected */ +#define NULLRDC_802154_AUTOACK 1 +#define NULLRDC_802154_AUTOACK_HW 1 + +/* Configure ContikiMAC for when it's selected */ +#define CONTIKIMAC_CONF_WITH_CONTIKIMAC_HEADER 0 +#define CONTIKIMAC_CONF_WITH_PHASE_OPTIMIZATION 0 +#define WITH_FAST_SLEEP 1 + +#ifndef NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE +#define NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE 8 +#endif + +#ifndef NETSTACK_CONF_FRAMER +#define NETSTACK_CONF_FRAMER framer_802154 +#endif + +#define NETSTACK_CONF_RADIO cc26xx_rf_driver +#define NETSTACK_RADIO_MAX_PAYLOAD_LEN 125 + +/* 6LoWPAN */ +#define SICSLOWPAN_CONF_COMPRESSION SICSLOWPAN_COMPRESSION_HC06 +#define SICSLOWPAN_CONF_COMPRESSION_THRESHOLD 63 +#define SICSLOWPAN_CONF_FRAG 1 +#define SICSLOWPAN_CONF_MAXAGE 8 +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name IEEE address configuration + * + * Used to generate our RIME & IPv6 address + * @{ + */ +/** + * \brief Location of the IEEE address + * 0 => Read from InfoPage, + * 1 => Use a hardcoded address, configured by IEEE_ADDR_CONF_ADDRESS + */ +#ifndef IEEE_ADDR_CONF_HARDCODED +#define IEEE_ADDR_CONF_HARDCODED 0 +#endif + +/** + * \brief The hardcoded IEEE address to be used when IEEE_ADDR_CONF_HARDCODED + * is defined as 1 + */ +#ifndef IEEE_ADDR_CONF_ADDRESS +#define IEEE_ADDR_CONF_ADDRESS { 0x00, 0x12, 0x4B, 0x00, 0x89, 0xAB, 0xCD, 0xEF } +#endif +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name RF configuration + * + * @{ + */ +/* RF Config */ +#ifndef IEEE802154_CONF_PANID +#define IEEE802154_CONF_PANID 0xABCD /**< Default PAN ID */ +#endif + +#ifndef CC26XX_RF_CONF_CHANNEL +#define CC26XX_RF_CONF_CHANNEL 25 /**< Default RF channel */ +#endif + +#ifndef CC26XX_RF_CONF_AUTOACK +#define CC26XX_RF_CONF_AUTOACK 1 /**< RF H/W generates ACKs */ +#endif + +#ifndef CC26XX_RF_CONF_PROMISCOUS +#define CC26XX_RF_CONF_PROMISCOUS 0 /**< 1 to enable promiscous mode */ +#endif + +#ifndef CC26XX_RF_CONF_BLE_SUPPORT +#define CC26XX_RF_CONF_BLE_SUPPORT 0 /**< 0 to disable BLE support */ +#endif + +/* + * Patch Management for the CPE itself and for BLE and IEEE modes + * + * Don't change these unless you know what you're doing + */ +#ifndef CC26XX_CONF_CPE_HAS_PATCHES +#define CC26XX_CONF_CPE_HAS_PATCHES 0 /**< 1 to enable patching the CPE */ +#endif + +#ifndef CC26XX_CONF_BLE_HAS_PATCHES +#define CC26XX_CONF_BLE_HAS_PATCHES 0 /**< 1 to enable patching BLE mode */ +#endif + +#ifndef CC26XX_CONF_IEEE_HAS_PATCHES +#define CC26XX_CONF_IEEE_HAS_PATCHES 0 /**< 1 to enable patching IEEE mode */ +#endif +/** @} */ +/*---------------------------------------------------------------------------*/ +/** @} */ +/** + * \name IPv6, RIME and network buffer configuration + * + * @{ + */ +/* Don't let contiki-default-conf.h decide if we are an IPv6 build */ +#ifndef NETSTACK_CONF_WITH_IPV6 +#define NETSTACK_CONF_WITH_IPV6 0 +#endif + +#if NETSTACK_CONF_WITH_IPV6 +/*---------------------------------------------------------------------------*/ +/* Addresses, Sizes and Interfaces */ +#define LINKADDR_CONF_SIZE 8 +#define UIP_CONF_LL_802154 1 +#define UIP_CONF_LLH_LEN 0 + +/* The size of the uIP main buffer */ +#ifndef UIP_CONF_BUFFER_SIZE +#define UIP_CONF_BUFFER_SIZE 1000 +#endif + +/* ND and Routing */ +#ifndef UIP_CONF_ROUTER +#define UIP_CONF_ROUTER 1 +#endif + +#define UIP_CONF_ND6_SEND_RA 0 +#define UIP_CONF_IP_FORWARD 0 +#define RPL_CONF_STATS 0 +#define RPL_CONF_MAX_DAG_ENTRIES 1 +#ifndef RPL_CONF_OF +#define RPL_CONF_OF rpl_mrhof +#endif + +#define UIP_CONF_ND6_REACHABLE_TIME 600000 +#define UIP_CONF_ND6_RETRANS_TIMER 10000 + +#ifndef NBR_TABLE_CONF_MAX_NEIGHBORS +#define NBR_TABLE_CONF_MAX_NEIGHBORS 20 +#endif +#ifndef UIP_CONF_MAX_ROUTES +#define UIP_CONF_MAX_ROUTES 20 +#endif + +#ifndef UIP_CONF_TCP +#define UIP_CONF_TCP 1 +#endif +#ifndef UIP_CONF_TCP_MSS +#define UIP_CONF_TCP_MSS 128 +#endif + +#define UIP_CONF_UDP 1 +#define UIP_CONF_UDP_CHECKSUMS 1 +#define UIP_CONF_ICMP6 1 +/*---------------------------------------------------------------------------*/ +#else /* NETSTACK_CONF_WITH_IPV6 */ +/* Network setup for non-IPv6 (rime). */ +#define UIP_CONF_IP_FORWARD 1 + +#define RIME_CONF_NO_POLITE_ANNOUCEMENTS 0 + +#endif /* NETSTACK_CONF_WITH_IPV6 */ +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Generic Configuration directives + * + * @{ + */ +#ifndef ENERGEST_CONF_ON +#define ENERGEST_CONF_ON 0 /**< Energest Module */ +#endif + +#ifndef STARTUP_CONF_VERBOSE +#define STARTUP_CONF_VERBOSE 1 /**< Set to 0 to decrease startup verbosity */ +#endif +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Character I/O Configuration + * + * @{ + */ +#ifndef CC26XX_UART_CONF_ENABLE +#define CC26XX_UART_CONF_ENABLE 1 /**< Enable/Disable UART I/O */ +#endif + +#ifndef CC26XX_UART_CONF_BAUD_RATE +#define CC26XX_UART_CONF_BAUD_RATE 115200 /**< Default UART0 baud rate */ +#endif + +/* Turn off example-provided putchars */ +#define SLIP_BRIDGE_CONF_NO_PUTCHAR 1 +#define SLIP_RADIO_CONF_NO_PUTCHAR 1 + +#ifndef SLIP_ARCH_CONF_ENABLED +/* + * Determine whether we need SLIP + * This will keep working while UIP_FALLBACK_INTERFACE and CMD_CONF_OUTPUT + * keep using SLIP + */ +#if defined (UIP_FALLBACK_INTERFACE) || defined (CMD_CONF_OUTPUT) +#define SLIP_ARCH_CONF_ENABLED 1 +#endif +#endif + +/** + * \brief Define this as 1 to build a headless node. + * + * The UART will not be initialised its clock will be gated, offering some + * energy savings. The USB will not be initialised either + */ +#ifndef CC26XX_CONF_QUIET +#define CC26XX_CONF_QUIET 0 +#endif + +/* CC26XX_CONF_QUIET is hard and overrides all other related defines */ +#if CC26XX_CONF_QUIET +#undef CC26XX_UART_CONF_ENABLE +#define CC26XX_UART_CONF_ENABLE 0 + +#undef STARTUP_CONF_VERBOSE +#define STARTUP_CONF_VERBOSE 0 +#endif /* CC26XX_CONF_QUIET */ +/** @} */ +/*---------------------------------------------------------------------------*/ +/** + * \name Button configurations + * + * Configure a button as power on/off: We use the right button for both boards. + * @{ + */ +#ifndef BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN +#define BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN 1 +#endif + +/* Notify various examples that we have Buttons */ +#define PLATFORM_HAS_BUTTON 1 +/** @} */ +/*---------------------------------------------------------------------------*/ +/* Platform-specific define to signify sensor reading failure */ +#define CC26XX_SENSOR_READING_ERROR 0x80000000 +/*---------------------------------------------------------------------------*/ +/** + * \name Compiler configuration and platform-specific type definitions + * + * Those values are not meant to be modified by the user + * @{ + */ +#define CLOCK_CONF_SECOND 128 + +/* Compiler configurations */ +#define CCIF +#define CLIF + +/* Platform typedefs */ +typedef uint32_t clock_time_t; +typedef uint32_t uip_stats_t; + +/* Clock (time) comparison macro */ +#define CLOCK_LT(a,b) ((signed long)((a)-(b)) < 0) + +/* + * rtimer.h typedefs rtimer_clock_t as unsigned short. We need to define + * RTIMER_CLOCK_LT to override this + */ +typedef uint32_t rtimer_clock_t; +#define RTIMER_CLOCK_LT(a,b) ((int32_t)((a)-(b)) < 0) +/** @} */ +/*---------------------------------------------------------------------------*/ +/* board.h assumes that basic configuration is done */ +#include "board.h" +/*---------------------------------------------------------------------------*/ +#endif /* CONTIKI_CONF_H */ + +/** @} */ diff --git a/platform/srf06-cc26xx/contiki-main.c b/platform/srf06-cc26xx/contiki-main.c new file mode 100644 index 000000000..d28643531 --- /dev/null +++ b/platform/srf06-cc26xx/contiki-main.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-platforms + * @{ + * + * \defgroup cc26xx-srf-tag SmartRF+CC26xx EM and the CC26xx SensorTag 2.0 + * + * This platform supports two different boards: + * 1) A standard TI SmartRF06EB with a CC26xx EM mounted on it and + * 2) The new TI SensorTag2.0 + * @{ + */ +#include "ti-lib.h" +#include "contiki.h" +#include "contiki-net.h" +#include "leds.h" +#include "lpm.h" +#include "gpio-interrupt.h" +#include "dev/watchdog.h" +#include "ieee-addr.h" +#include "vims.h" +#include "cc26xx-model.h" +#include "dev/cc26xx-uart.h" +#include "dev/cc26xx-rtc.h" +#include "dev/cc26xx-rf.h" +#include "sys_ctrl.h" +#include "uart.h" +#include "sys/clock.h" +#include "sys/rtimer.h" +#include "lib/sensors.h" +#include "button-sensor.h" +#include "dev/serial-line.h" +#include "net/mac/frame802154.h" + +#include "driverlib/driverlib_ver.h" + +#include +/*---------------------------------------------------------------------------*/ +static void +fade(unsigned char l) +{ + volatile int i; + int k, j; + for(k = 0; k < 800; ++k) { + j = k > 400 ? 800 - k : k; + + leds_on(l); + for(i = 0; i < j; ++i) { + __asm("nop"); + } + leds_off(l); + for(i = 0; i < 400 - j; ++i) { + __asm("nop"); + } + } +} +/*---------------------------------------------------------------------------*/ +static void +set_rf_params(void) +{ + uint16_t short_addr; + uint8_t ext_addr[8]; + radio_value_t val = 0; + + ieee_addr_cpy_to(ext_addr, 8); + + short_addr = ext_addr[7]; + short_addr |= ext_addr[6] << 8; + + /* Populate linkaddr_node_addr. Maintain endianness */ + memcpy(&linkaddr_node_addr, &ext_addr[8 - LINKADDR_SIZE], LINKADDR_SIZE); + + NETSTACK_RADIO.set_value(RADIO_PARAM_PAN_ID, IEEE802154_PANID); + NETSTACK_RADIO.set_value(RADIO_PARAM_16BIT_ADDR, short_addr); + NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, CC26XX_RF_CHANNEL); + NETSTACK_RADIO.set_object(RADIO_PARAM_64BIT_ADDR, ext_addr, 8); + + NETSTACK_RADIO.get_value(RADIO_PARAM_CHANNEL, &val); + printf(" RF: Channel %d\n", val); + +#if STARTUP_CONF_VERBOSE + { + int i; + printf(" Link layer addr: "); + for(i = 0; i < LINKADDR_SIZE - 1; i++) { + printf("%02x:", linkaddr_node_addr.u8[i]); + } + printf("%02x\n", linkaddr_node_addr.u8[i]); + } +#endif +} +/*---------------------------------------------------------------------------*/ +static void +select_lf_xosc(void) +{ + ti_lib_osc_interface_enable(); + + /* Make sure the SMPH clock within AUX is enabled */ + ti_lib_aux_wuc_clock_enable(AUX_WUC_SMPH_CLOCK); + while(ti_lib_aux_wuc_clock_status(AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY); + + /* Switch LF clock source to the LF RCOSC if required */ + if(ti_lib_osc_clock_source_get(OSC_SRC_CLK_LF) != OSC_XOSC_LF) { + ti_lib_osc_clock_source_set(OSC_SRC_CLK_LF, OSC_XOSC_LF); + } + + ti_lib_osc_interface_disable(); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Main function for CC26xx-based platforms + * + * The same main() is used for both Srf+CC26xxEM as well as for the SensorTag + */ +int +main(void) +{ + /* Set the LF XOSC as the LF system clock source */ + select_lf_xosc(); + + /* + * Make sure to open the latches - this will be important when returning + * from shutdown + */ + ti_lib_pwr_ctrl_io_freeze_disable(); + + /* Use DCDC instead of LDO to save current */ + ti_lib_pwr_ctrl_source_set(PWRCTRL_PWRSRC_DCDC); + + lpm_init(); + + board_init(); + + /* Enable flash cache and prefetch. */ + ti_lib_vims_mode_set(VIMS_BASE, VIMS_MODE_ENABLED); + ti_lib_vims_configure(VIMS_BASE, true, true); + + gpio_interrupt_init(); + + /* Clock must always be enabled for the semaphore module */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_MODCLKEN1) = AUX_WUC_MODCLKEN1_SMPH; + + leds_init(); + + fade(LEDS_RED); + + cc26xx_rtc_init(); + clock_init(); + rtimer_init(); + + board_init(); + + watchdog_init(); + process_init(); + + random_init(0x1234); + + /* Character I/O Initialisation */ +#if CC26XX_UART_CONF_ENABLE + cc26xx_uart_init(); + cc26xx_uart_set_input(serial_line_input_byte); +#endif + + serial_line_init(); + + printf("Starting " CONTIKI_VERSION_STRING "\n"); + printf("With CC26xxware v%u.%02u.%02u.%u\n", DRIVERLIB_MAJOR_VER, + DRIVERLIB_MINOR_VER, DRIVERLIB_PATCH_VER, DRIVERLIB_BUILD_ID); + printf(BOARD_STRING " using CC%u\n", CC26XX_MODEL_CPU_VARIANT); + + process_start(&etimer_process, NULL); + ctimer_init(); + + energest_init(); + ENERGEST_ON(ENERGEST_TYPE_CPU); + + fade(LEDS_YELLOW); + + printf(" Net: "); + printf("%s\n", NETSTACK_NETWORK.name); + printf(" MAC: "); + printf("%s\n", NETSTACK_MAC.name); + printf(" RDC: "); + printf("%s", NETSTACK_RDC.name); + + if(NETSTACK_RDC.channel_check_interval() != 0) { + printf(", Channel Check Interval: %u ticks", + NETSTACK_RDC.channel_check_interval()); + } + printf("\n"); + + netstack_init(); + + set_rf_params(); + +#if NETSTACK_CONF_WITH_IPV6 + memcpy(&uip_lladdr.addr, &linkaddr_node_addr, sizeof(uip_lladdr.addr)); + queuebuf_init(); + process_start(&tcpip_process, NULL); +#endif /* NETSTACK_CONF_WITH_IPV6 */ + + fade(LEDS_GREEN); + + process_start(&sensors_process, NULL); + + autostart_start(autostart_processes); + + watchdog_start(); + + fade(LEDS_ORANGE); + + while(1) { + uint8_t r; + do { + r = process_run(); + watchdog_periodic(); + } while(r > 0); + + /* Drop to some low power mode */ + lpm_drop(); + } +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ From 0f567e499f24318cf60957f019297d58dac82776 Mon Sep 17 00:00:00 2001 From: Jonas Olsson Date: Wed, 25 Feb 2015 13:15:35 +0100 Subject: [PATCH 5/7] Add CC26xx examples --- examples/cc26xx/Makefile | 9 + examples/cc26xx/Makefile.target | 1 + examples/cc26xx/README.md | 15 + examples/cc26xx/cc26xx-demo.c | 477 ++++++ examples/cc26xx/cc26xx-web-demo/Makefile | 24 + .../cc26xx/cc26xx-web-demo/Makefile.target | 1 + examples/cc26xx/cc26xx-web-demo/README.md | 169 +++ .../cc26xx/cc26xx-web-demo/cc26xx-web-demo.c | 893 ++++++++++++ .../cc26xx/cc26xx-web-demo/cc26xx-web-demo.h | 210 +++ .../cc26xx-web-demo/cetic-6lbr-client.c | 197 +++ examples/cc26xx/cc26xx-web-demo/coap-server.c | 157 ++ examples/cc26xx/cc26xx-web-demo/coap-server.h | 52 + .../cc26xx/cc26xx-web-demo/httpd-simple.c | 1298 +++++++++++++++++ .../cc26xx/cc26xx-web-demo/httpd-simple.h | 101 ++ .../cc26xx/cc26xx-web-demo/img/6lbr-web.png | Bin 0 -> 81741 bytes .../cc26xx-web-demo/img/coap-resources.png | Bin 0 -> 280210 bytes .../img/quickstart-sensortag.png | Bin 0 -> 158808 bytes .../img/sensor-readings-config.png | Bin 0 -> 33000 bytes .../cc26xx-web-demo/img/well-known-core.png | Bin 0 -> 9102 bytes examples/cc26xx/cc26xx-web-demo/mqtt-client.c | 975 +++++++++++++ examples/cc26xx/cc26xx-web-demo/mqtt-client.h | 74 + examples/cc26xx/cc26xx-web-demo/net-uart.c | 344 +++++ examples/cc26xx/cc26xx-web-demo/net-uart.h | 48 + .../cc26xx/cc26xx-web-demo/project-conf.h | 53 + .../cc26xx-web-demo/resources/res-ble-advd.c | 113 ++ .../cc26xx-web-demo/resources/res-device.c | 179 +++ .../cc26xx-web-demo/resources/res-leds.c | 110 ++ .../cc26xx-web-demo/resources/res-sensors.c | 269 ++++ .../resources/res-toggle-leds.c | 114 ++ examples/cc26xx/project-conf.h | 45 + 30 files changed, 5928 insertions(+) create mode 100644 examples/cc26xx/Makefile create mode 100644 examples/cc26xx/Makefile.target create mode 100644 examples/cc26xx/README.md create mode 100644 examples/cc26xx/cc26xx-demo.c create mode 100644 examples/cc26xx/cc26xx-web-demo/Makefile create mode 100644 examples/cc26xx/cc26xx-web-demo/Makefile.target create mode 100644 examples/cc26xx/cc26xx-web-demo/README.md create mode 100644 examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.c create mode 100644 examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.h create mode 100644 examples/cc26xx/cc26xx-web-demo/cetic-6lbr-client.c create mode 100644 examples/cc26xx/cc26xx-web-demo/coap-server.c create mode 100644 examples/cc26xx/cc26xx-web-demo/coap-server.h create mode 100644 examples/cc26xx/cc26xx-web-demo/httpd-simple.c create mode 100644 examples/cc26xx/cc26xx-web-demo/httpd-simple.h create mode 100644 examples/cc26xx/cc26xx-web-demo/img/6lbr-web.png create mode 100644 examples/cc26xx/cc26xx-web-demo/img/coap-resources.png create mode 100644 examples/cc26xx/cc26xx-web-demo/img/quickstart-sensortag.png create mode 100644 examples/cc26xx/cc26xx-web-demo/img/sensor-readings-config.png create mode 100644 examples/cc26xx/cc26xx-web-demo/img/well-known-core.png create mode 100644 examples/cc26xx/cc26xx-web-demo/mqtt-client.c create mode 100644 examples/cc26xx/cc26xx-web-demo/mqtt-client.h create mode 100644 examples/cc26xx/cc26xx-web-demo/net-uart.c create mode 100644 examples/cc26xx/cc26xx-web-demo/net-uart.h create mode 100644 examples/cc26xx/cc26xx-web-demo/project-conf.h create mode 100644 examples/cc26xx/cc26xx-web-demo/resources/res-ble-advd.c create mode 100644 examples/cc26xx/cc26xx-web-demo/resources/res-device.c create mode 100644 examples/cc26xx/cc26xx-web-demo/resources/res-leds.c create mode 100644 examples/cc26xx/cc26xx-web-demo/resources/res-sensors.c create mode 100644 examples/cc26xx/cc26xx-web-demo/resources/res-toggle-leds.c create mode 100644 examples/cc26xx/project-conf.h diff --git a/examples/cc26xx/Makefile b/examples/cc26xx/Makefile new file mode 100644 index 000000000..f23d49fe3 --- /dev/null +++ b/examples/cc26xx/Makefile @@ -0,0 +1,9 @@ +DEFINES+=PROJECT_CONF_H=\"project-conf.h\" +CONTIKI_PROJECT = cc26xx-demo + +all: $(CONTIKI_PROJECT) + +CONTIKI_WITH_IPV6 = 1 + +CONTIKI = ../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/cc26xx/Makefile.target b/examples/cc26xx/Makefile.target new file mode 100644 index 000000000..15890aa6a --- /dev/null +++ b/examples/cc26xx/Makefile.target @@ -0,0 +1 @@ +TARGET = srf06-cc26xx diff --git a/examples/cc26xx/README.md b/examples/cc26xx/README.md new file mode 100644 index 000000000..bd1be685b --- /dev/null +++ b/examples/cc26xx/README.md @@ -0,0 +1,15 @@ +CC26xx Demo +=========== +This example demonstrates basic functionality for the two supported CC26xx +boards. More specifically, the example demonstrates: + +* How to take sensor readings +* How to use buttons and the reed relay (triggered by holding a magnet near S3 + on the SensorTag). +* How to keep a power domain powered and a peripheral clocked under low power + operation +* How to send out BLE advertisements. The device will periodically send out BLE + beacons with the platform name as payload. Those beacons/BLE ADV packets can + be captured with any BLE capable device. Two such applications for iOS are the + TI Multitool and the TI Sensortag app. They can be found in the Apple App + Store. If you have a BLE-capable Mac, you can also use LightBlue for OS X. diff --git a/examples/cc26xx/cc26xx-demo.c b/examples/cc26xx/cc26xx-demo.c new file mode 100644 index 000000000..83f78e869 --- /dev/null +++ b/examples/cc26xx/cc26xx-demo.c @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-platforms + * @{ + * + * \defgroup cc26xx-examples CC26xx Example Projects + * + * Example projects for CC26xx-based platforms. + * @{ + * + * \defgroup cc26xx-demo CC26xx Demo Project + * + * Example project demonstrating the CC26xx platforms + * + * This example will work for the following boards: + * - srf06-cc26xx: SmartRF06EB + CC26XX EM + * - sensortag-cc26xx: CC26XX sensortag + * + * By default, the example will build for the srf06-cc26xx board. To switch + * between platforms: + * - make clean + * - make BOARD=sensortag-cc26xx savetarget + * + * or + * + * make BOARD=srf06-cc26xx savetarget + * + * This is an IPv6/RPL-enabled example. Thus, if you have a border router in + * your installation (same RDC layer, same PAN ID and RF channel), you should + * be able to ping6 this demo node. + * + * This example also demonstrates CC26xx BLE operation. The process starts + * the BLE beacon daemon (implemented in the RF driver). The daemon will + * send out a BLE beacon periodically. Use any BLE-enabled application (e.g. + * LightBlue on OS X or the TI BLE Multitool smartphone app) and after a few + * seconds the cc26xx device will be discovered. + * + * - etimer/clock : Every CC26XX_DEMO_LOOP_INTERVAL clock ticks the LED defined + * as CC26XX_DEMO_LEDS_PERIODIC will toggle and the device + * will print out readings from some supported sensors + * - sensors : Some sensortag sensors are read asynchronously (see sensor + * documentation). For those, this example will print out + * readings in a staggered fashion at a random interval + * - Buttons : CC26XX_DEMO_SENSOR_1 button will toggle CC26XX_DEMO_LEDS_BUTTON + * - CC26XX_DEMO_SENSOR_2 turns on LEDS_REBOOT and causes a + * watchdog reboot + * - The remaining buttons will just print something + * - The example also shows how to retrieve the duration of a + * button press (in ticks). The driver will generate a + * sensors_changed event upon button release + * - UART : Receiving an entire line of text over UART (ending + * in \\r) will cause CC26XX_DEMO_LEDS_SERIAL_IN to toggle + * This also demonstrates how a code module can influence + * low-power operation: In this example we keep the UART on + * and capable to RX even with the chip in deep sleep. + * see keep_uart_on() and the UART driver + * - Reed Relay : Will toggle the sensortag buzzer on/off + * + * @{ + * + * \file + * Example demonstrating the cc26xx platforms + */ +#include "contiki.h" +#include "sys/etimer.h" +#include "sys/ctimer.h" +#include "dev/leds.h" +#include "dev/serial-line.h" +#include "dev/watchdog.h" +#include "random.h" +#include "button-sensor.h" +#include "batmon-sensor.h" +#include "board-peripherals.h" +#include "lpm.h" +#include "cc26xx-rf.h" + +#include "ti-lib.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +#define CC26XX_DEMO_LOOP_INTERVAL (CLOCK_SECOND * 20) +#define CC26XX_DEMO_LEDS_PERIODIC LEDS_YELLOW +#define CC26XX_DEMO_LEDS_BUTTON LEDS_RED +#define CC26XX_DEMO_LEDS_SERIAL_IN LEDS_ORANGE +#define CC26XX_DEMO_LEDS_REBOOT LEDS_ALL +/*---------------------------------------------------------------------------*/ +#define CC26XX_DEMO_SENSOR_NONE (void *)0xFFFFFFFF + +#define CC26XX_DEMO_SENSOR_1 &button_left_sensor +#define CC26XX_DEMO_SENSOR_2 &button_right_sensor + +#if BOARD_SENSORTAG +#define CC26XX_DEMO_SENSOR_3 CC26XX_DEMO_SENSOR_NONE +#define CC26XX_DEMO_SENSOR_4 CC26XX_DEMO_SENSOR_NONE +#define CC26XX_DEMO_SENSOR_5 &reed_relay_sensor +#else +#define CC26XX_DEMO_SENSOR_3 &button_up_sensor +#define CC26XX_DEMO_SENSOR_4 &button_down_sensor +#define CC26XX_DEMO_SENSOR_5 &button_select_sensor +#endif +/*---------------------------------------------------------------------------*/ +static struct etimer et; +/*---------------------------------------------------------------------------*/ +PROCESS(cc26xx_demo_process, "cc26xx demo process"); +AUTOSTART_PROCESSES(&cc26xx_demo_process); +/*---------------------------------------------------------------------------*/ +#if BOARD_SENSORTAG +/*---------------------------------------------------------------------------*/ +/* + * Update sensor readings in a staggered fashion every SENSOR_READING_PERIOD + * ticks + a random interval between 0 and SENSOR_READING_RANDOM ticks + */ +#define SENSOR_READING_PERIOD (CLOCK_SECOND * 20) +#define SENSOR_READING_RANDOM (CLOCK_SECOND << 4) + +static struct ctimer bmp_timer, opt_timer, sht_timer, tmp_timer, mpu_timer; +/*---------------------------------------------------------------------------*/ +static void init_bmp_reading(void *not_used); +static void init_opt_reading(void *not_used); +static void init_sht_reading(void *not_used); +static void init_tmp_reading(void *not_used); +static void init_mpu_reading(void *not_used); +/*---------------------------------------------------------------------------*/ +static void +print_mpu_reading(int reading) +{ + if(reading < 0) { + printf("-"); + reading = -reading; + } + + printf("%d.%02d", reading / 100, reading % 100); +} +/*---------------------------------------------------------------------------*/ +static void +get_bmp_reading() +{ + int value; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + value = bmp_280_sensor.value(BMP_280_SENSOR_TYPE_PRESS); + if(value != CC26XX_SENSOR_READING_ERROR) { + printf("BAR: Pressure=%d.%02d hPa\n", value / 100, value % 100); + } else { + printf("BAR: Pressure Read Error\n"); + } + + value = bmp_280_sensor.value(BMP_280_SENSOR_TYPE_TEMP); + if(value != CC26XX_SENSOR_READING_ERROR) { + printf("BAR: Temp=%d.%02d C\n", value / 100, value % 100); + } else { + printf("BAR: Temperature Read Error\n"); + } + + SENSORS_DEACTIVATE(bmp_280_sensor); + + ctimer_set(&bmp_timer, next, init_bmp_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +get_tmp_reading() +{ + int value; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + value = tmp_007_sensor.value(TMP_007_SENSOR_TYPE_ALL); + + if(value == CC26XX_SENSOR_READING_ERROR) { + printf("TMP: Ambient Read Error\n"); + return; + } + + value = tmp_007_sensor.value(TMP_007_SENSOR_TYPE_AMBIENT); + printf("TMP: Ambient=%d.%03d C\n", value / 1000, value % 1000); + + value = tmp_007_sensor.value(TMP_007_SENSOR_TYPE_OBJECT); + printf("TMP: Object=%d.%03d C\n", value / 1000, value % 1000); + + SENSORS_DEACTIVATE(tmp_007_sensor); + + ctimer_set(&tmp_timer, next, init_tmp_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +get_sht_reading() +{ + int value; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + value = sht_21_sensor.value(SHT_21_SENSOR_TYPE_TEMP); + if(value != CC26XX_SENSOR_READING_ERROR) { + printf("SHT: Temp=%d.%02d C\n", value / 100, value % 100); + } else { + printf("SHT: Temp Read Error\n"); + } + + value = sht_21_sensor.value(SHT_21_SENSOR_TYPE_HUMIDITY); + if(value != CC26XX_SENSOR_READING_ERROR) { + printf("SHT: Humidity=%d.%02d %%RH\n", value / 100, value % 100); + } else { + printf("SHT: Humidity Read Error\n"); + } + + ctimer_set(&sht_timer, next, init_sht_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +get_light_reading() +{ + int value; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + value = opt_3001_sensor.value(0); + if(value != CC26XX_SENSOR_READING_ERROR) { + printf("OPT: Light=%d.%02d lux\n", value / 100, value % 100); + } else { + printf("OPT: Light Read Error\n"); + } + + SENSORS_DEACTIVATE(opt_3001_sensor); + + ctimer_set(&opt_timer, next, init_opt_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +get_mpu_reading() +{ + int value; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + printf("MPU Gyro: X="); + value = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_GYRO_X); + print_mpu_reading(value); + printf(" deg/sec\n"); + + printf("MPU Gyro: Y="); + value = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_GYRO_Y); + print_mpu_reading(value); + printf(" deg/sec\n"); + + printf("MPU Gyro: Z="); + value = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_GYRO_Z); + print_mpu_reading(value); + printf(" deg/sec\n"); + + printf("MPU Acc: X="); + value = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_ACC_X); + print_mpu_reading(value); + printf(" G\n"); + + printf("MPU Acc: Y="); + value = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_ACC_Y); + print_mpu_reading(value); + printf(" G\n"); + + printf("MPU Acc: Z="); + value = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_ACC_Z); + print_mpu_reading(value); + printf(" G\n"); + + SENSORS_DEACTIVATE(mpu_9250_sensor); + + ctimer_set(&mpu_timer, next, init_mpu_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +init_bmp_reading(void *not_used) +{ + SENSORS_ACTIVATE(bmp_280_sensor); +} +/*---------------------------------------------------------------------------*/ +static void +init_opt_reading(void *not_used) +{ + SENSORS_ACTIVATE(opt_3001_sensor); +} +/*---------------------------------------------------------------------------*/ +static void +init_sht_reading(void *not_used) +{ + SENSORS_ACTIVATE(sht_21_sensor); +} +/*---------------------------------------------------------------------------*/ +static void +init_tmp_reading(void *not_used) +{ + SENSORS_ACTIVATE(tmp_007_sensor); +} +/*---------------------------------------------------------------------------*/ +static void +init_mpu_reading(void *not_used) +{ + mpu_9250_sensor.configure(SENSORS_ACTIVE, MPU_9250_SENSOR_TYPE_ALL); +} +#endif +/*---------------------------------------------------------------------------*/ +static void +get_sync_sensor_readings(void) +{ + int value; + + printf("-----------------------------------------\n"); + + value = batmon_sensor.value(BATMON_SENSOR_TYPE_TEMP); + printf("Bat: Temp=%d.%02d C (%08x)\n", value >> 2, + (value & 0x00000003) * 25, value); + + value = batmon_sensor.value(BATMON_SENSOR_TYPE_VOLT); + printf("Bat: Volt=%d mV\n", (value * 125) >> 5); + + return; +} +/*---------------------------------------------------------------------------*/ +static void +init_sensors(void) +{ +#if BOARD_SENSORTAG + SENSORS_ACTIVATE(reed_relay_sensor); +#endif + + SENSORS_ACTIVATE(batmon_sensor); +} +/*---------------------------------------------------------------------------*/ +static void +init_sensor_readings(void) +{ +#if BOARD_SENSORTAG + SENSORS_ACTIVATE(sht_21_sensor); + SENSORS_ACTIVATE(tmp_007_sensor); + SENSORS_ACTIVATE(opt_3001_sensor); + SENSORS_ACTIVATE(bmp_280_sensor); + + init_mpu_reading(NULL); +#endif +} +/*---------------------------------------------------------------------------*/ +static lpm_power_domain_lock_t lock; +/*---------------------------------------------------------------------------*/ +/* + * In order to maintain UART input operation: + * - Keep the uart clocked in sleep and deep sleep + * - Keep the serial PD on in deep sleep + */ +static void +keep_uart_on(void) +{ + /* Keep the serial PD on */ + lpm_pd_lock_obtain(&lock, PRCM_DOMAIN_SERIAL); + + /* Keep the UART clock on during Sleep and Deep Sleep */ + ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_UART0); + ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_UART0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(cc26xx_demo_process, ev, data) +{ + + PROCESS_BEGIN(); + + printf("CC26XX demo\n"); + + init_sensors(); + + /* Init the BLE advertisement daemon */ + cc26xx_rf_ble_beacond_config(0, BOARD_STRING); + cc26xx_rf_ble_beacond_start(); + + etimer_set(&et, CC26XX_DEMO_LOOP_INTERVAL); + get_sync_sensor_readings(); + init_sensor_readings(); + + keep_uart_on(); + + while(1) { + + PROCESS_YIELD(); + + if(ev == PROCESS_EVENT_TIMER) { + if(data == &et) { + leds_toggle(CC26XX_DEMO_LEDS_PERIODIC); + + get_sync_sensor_readings(); + + etimer_set(&et, CC26XX_DEMO_LOOP_INTERVAL); + } + } else if(ev == sensors_event) { + if(data == CC26XX_DEMO_SENSOR_1) { + printf("Left: Pin %d, press duration %d clock ticks\n", + (CC26XX_DEMO_SENSOR_1)->value(BUTTON_SENSOR_VALUE_STATE), + (CC26XX_DEMO_SENSOR_1)->value(BUTTON_SENSOR_VALUE_DURATION)); + + if((CC26XX_DEMO_SENSOR_1)->value(BUTTON_SENSOR_VALUE_DURATION) > + CLOCK_SECOND) { + printf("Long button press!\n"); + } + + leds_toggle(CC26XX_DEMO_LEDS_BUTTON); + } else if(data == CC26XX_DEMO_SENSOR_2) { + leds_on(CC26XX_DEMO_LEDS_REBOOT); + watchdog_reboot(); + } else if(data == CC26XX_DEMO_SENSOR_3) { + printf("Up\n"); + } else if(data == CC26XX_DEMO_SENSOR_4) { + printf("Down\n"); + } else if(data == CC26XX_DEMO_SENSOR_5) { +#if BOARD_SENSORTAG + if(buzzer_state()) { + buzzer_stop(); + } else { + buzzer_start(1000); + } + } else if(ev == sensors_event && data == &bmp_280_sensor) { + get_bmp_reading(); + } else if(ev == sensors_event && data == &opt_3001_sensor) { + get_light_reading(); + } else if(ev == sensors_event && data == &sht_21_sensor) { + get_sht_reading(); + } else if(ev == sensors_event && data == &tmp_007_sensor) { + get_tmp_reading(); + } else if(ev == sensors_event && data == &mpu_9250_sensor) { + get_mpu_reading(); +#else + printf("Sel: Pin %d, press duration %d clock ticks\n", + button_select_sensor.value(BUTTON_SENSOR_VALUE_STATE), + button_select_sensor.value(BUTTON_SENSOR_VALUE_DURATION)); +#endif + } + } else if(ev == serial_line_event_message) { + leds_toggle(CC26XX_DEMO_LEDS_SERIAL_IN); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/Makefile b/examples/cc26xx/cc26xx-web-demo/Makefile new file mode 100644 index 000000000..622156a4d --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/Makefile @@ -0,0 +1,24 @@ +DEFINES+=PROJECT_CONF_H=\"project-conf.h\" + +all: cc26xx-web-demo + +REST_RESOURCES_DIR = ./resources + +REST_RESOURCES_FILES += res-leds.c res-toggle-leds.c res-device.c +REST_RESOURCES_FILES += res-sensors.c res-ble-advd.c + +PROJECTDIRS += $(REST_RESOURCES_DIR) +PROJECT_SOURCEFILES += $(REST_RESOURCES_FILES) + +PROJECT_SOURCEFILES += cetic-6lbr-client.c coap-server.c net-uart.c mqtt-client.c +PROJECT_SOURCEFILES += httpd-simple.c + +CONTIKI_WITH_IPV6 = 1 + +# REST Engine shall use Erbium CoAP implementation +APPS += er-coap +APPS += rest-engine +APPS += mqtt + +CONTIKI=../../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/cc26xx/cc26xx-web-demo/Makefile.target b/examples/cc26xx/cc26xx-web-demo/Makefile.target new file mode 100644 index 000000000..15890aa6a --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/Makefile.target @@ -0,0 +1 @@ +TARGET = srf06-cc26xx diff --git a/examples/cc26xx/cc26xx-web-demo/README.md b/examples/cc26xx/cc26xx-web-demo/README.md new file mode 100644 index 000000000..194336a56 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/README.md @@ -0,0 +1,169 @@ +CC26xx Web Demo Readme +====================== +This demo project combines a number of web-based applications aiming to +demonstrate the CC26xx capability. The applications are: + +* A network-based UART +* A client for [6lbr](http://cetic.github.io/6lbr/) +* A CoAP server +* An MQTT client +* A web server which can be used to display sensor readings but also to + configure MQTT functionality + +The example has been configured to run for both CC26xx-based boards: i) The +SensorTag 2.0 and ii) The Srf06EB with a CC26xx EM mounted on it. + +To build the example for the Srf, simply run `make`. To build for the tag, +run `make BOARD=sensortag`. Do not forget to `make clean` when switching +between the two platforms. + +You can disable some of those individual components by changing the respective +defines in `project-conf.h`. For instance, to disable the CoAP functionality, +set `#define CC26XX_WEB_DEMO_CONF_COAP_SERVER 0`. The web server cannot be +disabled, all other aforementioned applications can. + +Network UART (net-uart) +----------------------- +This example only makes sense if you are using the Srf or if you have taken +the sensortag out of its case and you have it connected over JTAG to the Srf. + +The net-uart does two things: + +* When you type a string to the UART console, the string will be sent verbatim + to a remote UDP port 7777 listener. This can be for example a netcat listener + on a linux or OS X PC: + `nc -6ulkw 1 7777` +* The net-uart also listens to UDP port 7777 and when it receives a string over + UDP, it will print it verbatim over UART. + +The example will initially send packets to a hard-coded IPv6 address. This can +be changed very easily by entering a different IPv6 address to the console. +Thus, when the serial input string is an IPv6 address, it will not be sent as +any other string would, but instead it will configure the example to send to a +different remote address. This new IPv6 address is not persistent across +device reboots. + +6lbr Client +----------- +This will periodically send a UDP packet to your 6lbr, containing network +information, which will be used by 6lbr to construct a network graph. To see +this in action, fire up a browser and navigate to the 6lbr web page. The +default address is http://[bbbb::100]. Once the page loads, click the 'sensors' +tab, as per the image below. + +![6lbr](img/6lbr-web.png) + +CoAP Server +----------- +For this functionality to work, you will need to install the +[Copper (Cu)](https://addons.mozilla.org/en-US/firefox/addon/copper-270430/) +addon to your browser. + +From the sensors tab in the 6lbr web page, click the 'coap' link in the line +corresponding to your CC26xx device. Once the addon fires up, select +".well-known/core" in the left pane and then hit the 'Get' button at the top. + +![CoAP Resources](img/coap-resources.png) + +The Device will respond with a list of all available CoAP resources. This list +will be different between the Srf and the SensorTag. The screenshot below shows +a (partial) list of resources exported by the SensorTag CoAP server. Select +a resource on the left pane and hit 'Get' to retrieve its value. Select +`lt/g` and hit 'Post' to toggle the green LED, `lt/r` for the red one. + +You can also use CoAP to enable/disable BLE advertisements! Select +`dev/ble_advd` and then hit the "Outgoing" button in the payload panel. Type in +the desired payload, which can be: + +* `mode=on|off` +* `name=` +* `interval=` + +or a combination of both delimited with an amp. For example, you can set as +payload `mode=on&name=My CC26xx Device 4&interval=5`. Once you have set the +payload, hit either the POST or PUT button. + +Bear in mind that you must set `name` at least once before enabling BLE +advertisements. If you fail to do so, the RF will refuse to enter BLE mode and +the CoAP engine will return 4.03 forbidden. The values of `name` and `interval` +persist across BLE on/off cycles, so you only have to set them once. The values +do _not_ persist through device powercycles. + +HTTPD +----- +Back on the 6lbr page, hit the 'web' link corresponding to your device. This +will take you to a web page served by the CC26xx. The HTTPD serves two pages: + +* index.html: Provides sensor readings and network information +* config.html: Can be used to configure the MQTT client (more below) + +In the navigation bar at the top there is also a third link, which will take +you directly to your device's page on IBM's quickstart service. + +IBM Quickstart / MQTT Client +---------------------------- +The MQTT client can be used to: + +* Publish sensor readings to an MQTT broker. +* Subscribe to a topic and as a result receive commands from an MQTT broker + +The device will try to connect to IBM's quickstart over NAT64, so you will +need a NAT64 gateway in your network to make this work. A guide on how to +setup NAT64 is out of scope here. If this is not an option for you, you can +configure the device to publish to a local MQTT broker over end-to-end IPv6. +See below on how to change the destination broker's address. + +By default the device will publish readings to IBM's quickstart service. The +publish messages include sensor readings but also some other information such +as device uptime in seconds and a message sequence number. Click the "IBM +Quickstart" link in the web page to go directly to your device's page +on Quickstart. After a few seconds, you will see something like this: + +![A SensorTag on IBM Quickstart](img/quickstart-sensortag.png) + +Sensor readings are only published if they have changed since the previous +reading (BatMon is an exception and always gets published). Additionally, you +can turn on/off individual readings from the config.html web page, as per the +figure below. + +![Sensor Readings Configuration](img/sensor-readings-config.png) + +Some of the MQTT client functionality can be configured even further: + +* You can change the broker IP and port. This is useful if you want to use your + own MQTT broker instead of IBM's quickstart. The example has been tested + successfully with [mosquitto](http://mosquitto.org/) +* You can change the publish interval. Recommended values are 10secs or higher. + You will not be allowed to set this to anything less than 5 seconds. +* If you want to use IBM's cloud service with a registered device, change + 'Org ID' and provide an 'Auth Token', which acts as a 'password', but bear in + mind that it gets transported in clear text, both over the web configuration + page as well as inside MQTT messages. +* The remaining configuration options are related to the content of MQTT + messages and in general you won't have to modify them. + +For the SensorTag, changes to the MQTT configuration get saved in external +flash and persist across device restarts. The same does not hold true for +Srf+EM builds. + +You can also subscribe to topics and receive commands, but this will only +work if you use "Org ID" != 'quickstart'. Thus, if you provide a different +Org ID (do not forget the auth token!), the device will subscribe to: + +`iot-2/cmd/+/fmt/json` + +You can then use this to toggle LEDs or to turn the buzzer on and off. +The buzzer is only available on the SensorTag. To do this, you can for example +use mosquitto client to publish to `iot-2/cmd/leds/fmt/json`. So, to turn +the buzzer on, you would do this: + +`mosquitto_pub -h -m "1" -t iot-2/cmd/buzz/fmt/json` + +Where `broker IP` should be replaced with the IP address of your mosquitto +broker (the one where you device has subscribed). Replace `-m "1'` with `-m "0"` +to turn the buzzer back off. Replace `buzz` with `leds` in the topic to change +the state of the LED. + +Bear in mind that, even though the topic suggests that messages are of json +format, they are in fact not. This was done in order to avoid linking a json +parser into the firmware. diff --git a/examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.c b/examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.c new file mode 100644 index 000000000..91f921b9d --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.c @@ -0,0 +1,893 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * Main module for the CC26XX web demo. Activates on-device resources, + * takes sensor readings periodically and caches them for all other modules + * to use. + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "contiki-net.h" +#include "rest-engine.h" +#include "board-peripherals.h" +#include "lib/sensors.h" +#include "lib/list.h" +#include "sys/process.h" +#include "button-sensor.h" +#include "batmon-sensor.h" +#include "httpd-simple.h" +#include "cc26xx-web-demo.h" +#include "mqtt-client.h" +#include "coap-server.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +PROCESS_NAME(cetic_6lbr_client_process); +PROCESS(cc26xx_web_demo_process, "CC26XX Web Demo"); +/*---------------------------------------------------------------------------*/ +/* + * Update sensor readings in a staggered fashion every SENSOR_READING_PERIOD + * ticks + a random interval between 0 and SENSOR_READING_RANDOM ticks + */ +#define SENSOR_READING_PERIOD (CLOCK_SECOND * 20) +#define SENSOR_READING_RANDOM (CLOCK_SECOND << 4) + +struct ctimer batmon_timer; + +#if BOARD_SENSORTAG +struct ctimer bmp_timer, sht_timer, tmp_timer, opt_timer, mpu_timer; +#endif +/*---------------------------------------------------------------------------*/ +/* Provide visible feedback via LEDS while searching for a network */ +#define NO_NET_LED_DURATION (CC26XX_WEB_DEMO_NET_CONNECT_PERIODIC >> 1) + +static struct etimer et; +static struct ctimer ct; +/*---------------------------------------------------------------------------*/ +process_event_t cc26xx_web_demo_publish_event; +process_event_t cc26xx_web_demo_config_loaded_event; +process_event_t cc26xx_web_demo_load_config_defaults; +/*---------------------------------------------------------------------------*/ +/* Saved settings on flash: store, offset, magic */ +#define CONFIG_FLASH_OFFSET 0 +#define CONFIG_MAGIC 0xCC265001 + +cc26xx_web_demo_config_t cc26xx_web_demo_config; +/*---------------------------------------------------------------------------*/ +/* A cache of sensor values. Updated periodically or upon key press */ +LIST(sensor_list); +/*---------------------------------------------------------------------------*/ +/* The objects representing sensors used in this demo */ +#define DEMO_SENSOR(name, type, descr, xml_element, form_field, units) \ + cc26xx_web_demo_sensor_reading_t name##_reading = \ + { NULL, 0, 0, descr, xml_element, form_field, units, type, 1, 1 } + +/* CC26xx sensors */ +DEMO_SENSOR(batmon_temp, CC26XX_WEB_DEMO_SENSOR_BATMON_TEMP, + "Battery Temp", "battery-temp", "batmon_temp", + CC26XX_WEB_DEMO_UNIT_TEMP); +DEMO_SENSOR(batmon_volt, CC26XX_WEB_DEMO_SENSOR_BATMON_VOLT, + "Battery Volt", "battery-volt", "batmon_volt", + CC26XX_WEB_DEMO_UNIT_VOLT); + +/* Sensortag sensors */ +#if BOARD_SENSORTAG +DEMO_SENSOR(bmp_pres, CC26XX_WEB_DEMO_SENSOR_BMP_PRES, + "Air Pressure", "air-pressure", "bmp_pres", + CC26XX_WEB_DEMO_UNIT_PRES); +DEMO_SENSOR(bmp_temp, CC26XX_WEB_DEMO_SENSOR_BMP_TEMP, + "Air Temp", "air-temp", "bmp_temp", + CC26XX_WEB_DEMO_UNIT_TEMP); +DEMO_SENSOR(sht_temp, CC26XX_WEB_DEMO_SENSOR_SHT_TEMP, + "SHT Temp", "sht-temp", "sht_temp", + CC26XX_WEB_DEMO_UNIT_TEMP); +DEMO_SENSOR(sht_hum, CC26XX_WEB_DEMO_SENSOR_SHT_HUMIDITY, + "SHT Humidity", "sht-humidity", "sht_hum", + CC26XX_WEB_DEMO_UNIT_HUMIDITY); +DEMO_SENSOR(tmp_amb, CC26XX_WEB_DEMO_SENSOR_TMP_AMBIENT, + "Ambient Temp", "ambient-temp", "tmp_amb", + CC26XX_WEB_DEMO_UNIT_TEMP); +DEMO_SENSOR(tmp_obj, CC26XX_WEB_DEMO_SENSOR_TMP_OBJECT, + "Object Temp", "object-temp", "tmp_obj", + CC26XX_WEB_DEMO_UNIT_TEMP); +DEMO_SENSOR(opt, CC26XX_WEB_DEMO_SENSOR_OPT_LIGHT, + "Light", "light", "light", + CC26XX_WEB_DEMO_UNIT_LIGHT); + +/* MPU Readings */ +DEMO_SENSOR(mpu_acc_x, CC26XX_WEB_DEMO_SENSOR_MPU_ACC_X, + "Acc X", "acc-x", "acc_x", + CC26XX_WEB_DEMO_UNIT_ACC); +DEMO_SENSOR(mpu_acc_y, CC26XX_WEB_DEMO_SENSOR_MPU_ACC_Y, + "Acc Y", "acc-y", "acc_y", + CC26XX_WEB_DEMO_UNIT_ACC); +DEMO_SENSOR(mpu_acc_z, CC26XX_WEB_DEMO_SENSOR_MPU_ACC_Z, + "Acc Z", "acc-z", "acc_z", + CC26XX_WEB_DEMO_UNIT_ACC); + +DEMO_SENSOR(mpu_gyro_x, CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_X, + "Gyro X", "gyro-x", "gyro_x", + CC26XX_WEB_DEMO_UNIT_GYRO); +DEMO_SENSOR(mpu_gyro_y, CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_Y, + "Gyro Y", "gyro-y", "gyro_y", + CC26XX_WEB_DEMO_UNIT_GYRO); +DEMO_SENSOR(mpu_gyro_z, CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_Z, + "Gyro Z", "gyro-z", "gyro_Z", + CC26XX_WEB_DEMO_UNIT_GYRO); +#endif +/*---------------------------------------------------------------------------*/ +#if BOARD_SENSORTAG +static void init_bmp_reading(void *data); +static void init_light_reading(void *data); +static void init_sht_reading(void *data); +static void init_tmp_reading(void *data); +static void init_mpu_reading(void *data); +#endif +/*---------------------------------------------------------------------------*/ +static void +publish_led_off(void *d) +{ + leds_off(CC26XX_WEB_DEMO_STATUS_LED); +} +/*---------------------------------------------------------------------------*/ +static void +save_config() +{ + /* Dump current running config to flash */ +#if BOARD_SENSORTAG + int rv; + cc26xx_web_demo_sensor_reading_t *reading = NULL; + + rv = ext_flash_open(); + + if(!rv) { + printf("Could not open flash to save config\n"); + ext_flash_close(); + return; + } + + rv = ext_flash_erase(CONFIG_FLASH_OFFSET, sizeof(cc26xx_web_demo_config_t)); + + if(!rv) { + printf("Error erasing flash\n"); + } else { + cc26xx_web_demo_config.magic = CONFIG_MAGIC; + cc26xx_web_demo_config.len = sizeof(cc26xx_web_demo_config_t); + cc26xx_web_demo_config.sensors_bitmap = 0; + + for(reading = list_head(sensor_list); + reading != NULL; + reading = list_item_next(reading)) { + if(reading->publish) { + cc26xx_web_demo_config.sensors_bitmap |= (1 << reading->type); + } + } + + rv = ext_flash_write(CONFIG_FLASH_OFFSET, sizeof(cc26xx_web_demo_config_t), + (uint8_t *)&cc26xx_web_demo_config); + if(!rv) { + printf("Error saving config\n"); + } + } + + ext_flash_close(); +#endif +} +/*---------------------------------------------------------------------------*/ +static void +load_config() +{ +#if BOARD_SENSORTAG + /* Read from flash into a temp buffer */ + cc26xx_web_demo_config_t tmp_cfg; + cc26xx_web_demo_sensor_reading_t *reading = NULL; + + int rv = ext_flash_open(); + + if(!rv) { + printf("Could not open flash to load config\n"); + ext_flash_close(); + return; + } + + rv = ext_flash_read(CONFIG_FLASH_OFFSET, sizeof(tmp_cfg), + (uint8_t *)&tmp_cfg); + + ext_flash_close(); + + if(!rv) { + printf("Error loading config\n"); + return; + } + + if(tmp_cfg.magic == CONFIG_MAGIC && tmp_cfg.len == sizeof(tmp_cfg)) { + memcpy(&cc26xx_web_demo_config, &tmp_cfg, sizeof(cc26xx_web_demo_config)); + } + + for(reading = list_head(sensor_list); + reading != NULL; + reading = list_item_next(reading)) { + if(cc26xx_web_demo_config.sensors_bitmap & (1 << reading->type)) { + reading->publish = 1; + } else { + reading->publish = 0; + snprintf(reading->converted, CC26XX_WEB_DEMO_CONVERTED_LEN, "\"N/A\""); + } + } +#endif +} +/*---------------------------------------------------------------------------*/ +/* Don't start everything here, we need to dictate order of initialisation */ +AUTOSTART_PROCESSES(&cc26xx_web_demo_process); +/*---------------------------------------------------------------------------*/ +int +cc26xx_web_demo_ipaddr_sprintf(char *buf, uint8_t buf_len, + const uip_ipaddr_t *addr) +{ + uint16_t a; + uint8_t len = 0; + int i, f; + for(i = 0, f = 0; i < sizeof(uip_ipaddr_t); i += 2) { + a = (addr->u8[i] << 8) + addr->u8[i + 1]; + if(a == 0 && f >= 0) { + if(f++ == 0) { + len += snprintf(&buf[len], buf_len - len, "::"); + } + } else { + if(f > 0) { + f = -1; + } else if(i > 0) { + len += snprintf(&buf[len], buf_len - len, ":"); + } + len += snprintf(&buf[len], buf_len - len, "%x", a); + } + } + + return len; +} +/*---------------------------------------------------------------------------*/ +const cc26xx_web_demo_sensor_reading_t * +cc26xx_web_demo_sensor_lookup(int sens_type) +{ + cc26xx_web_demo_sensor_reading_t *reading = NULL; + + for(reading = list_head(sensor_list); + reading != NULL; + reading = list_item_next(reading)) { + if(reading->type == sens_type) { + return reading; + } + } + + return NULL; +} +/*---------------------------------------------------------------------------*/ +const cc26xx_web_demo_sensor_reading_t * +cc26xx_web_demo_sensor_first() +{ + return list_head(sensor_list); +} +/*---------------------------------------------------------------------------*/ +void +cc26xx_web_demo_restore_defaults(void) +{ + cc26xx_web_demo_sensor_reading_t *reading = NULL; + + leds_on(LEDS_ALL); + + for(reading = list_head(sensor_list); + reading != NULL; + reading = list_item_next(reading)) { + reading->publish = 1; + } + +#if CC26XX_WEB_DEMO_MQTT_CLIENT + process_post_synch(&mqtt_client_process, + cc26xx_web_demo_load_config_defaults, NULL); +#endif + +#if CC26XX_WEB_DEMO_NET_UART + process_post_synch(&net_uart_process, cc26xx_web_demo_load_config_defaults, + NULL); +#endif + + save_config(); + + leds_off(LEDS_ALL); +} +/*---------------------------------------------------------------------------*/ +static int +defaults_post_handler(char *key, int key_len, char *val, int val_len) +{ + if(key_len != strlen("defaults") || + strncasecmp(key, "defaults", strlen("defaults")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + cc26xx_web_demo_restore_defaults(); + + return HTTPD_SIMPLE_POST_HANDLER_OK; +} +/*---------------------------------------------------------------------------*/ +static int +sensor_readings_handler(char *key, int key_len, char *val, int val_len) +{ + cc26xx_web_demo_sensor_reading_t *reading = NULL; + int rv; + + for(reading = list_head(sensor_list); + reading != NULL; + reading = list_item_next(reading)) { + if(key_len == strlen(reading->form_field) && + strncmp(reading->form_field, key, strlen(key)) == 0) { + + rv = atoi(val); + + /* Be pedantic: only accept 0 and 1, not just any non-zero value */ + if(rv == 0) { + reading->publish = 0; + snprintf(reading->converted, CC26XX_WEB_DEMO_CONVERTED_LEN, "\"N/A\""); + } else if(rv == 1) { + reading->publish = 1; + } else { + return HTTPD_SIMPLE_POST_HANDLER_ERROR; + } + + return HTTPD_SIMPLE_POST_HANDLER_OK; + } + } + + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; +} +/*---------------------------------------------------------------------------*/ +HTTPD_SIMPLE_POST_HANDLER(sensor, sensor_readings_handler); +HTTPD_SIMPLE_POST_HANDLER(defaults, defaults_post_handler); +/*---------------------------------------------------------------------------*/ +static void +get_batmon_reading(void *data) +{ + int value; + char *buf; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + if(batmon_temp_reading.publish) { + value = batmon_sensor.value(BATMON_SENSOR_TYPE_TEMP); + if(value != CC26XX_SENSOR_READING_ERROR) { + batmon_temp_reading.raw = value; + + buf = batmon_temp_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d.%02d", value >> 2, + (value & 0x00000003) * 25); + } + } + + if(batmon_volt_reading.publish) { + value = batmon_sensor.value(BATMON_SENSOR_TYPE_VOLT); + if(value != CC26XX_SENSOR_READING_ERROR) { + batmon_volt_reading.raw = value; + + buf = batmon_volt_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d", (value * 125) >> 5); + } + } + + ctimer_set(&batmon_timer, next, get_batmon_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +#if BOARD_SENSORTAG +/*---------------------------------------------------------------------------*/ +static void +compare_and_update(cc26xx_web_demo_sensor_reading_t *reading) +{ + if(reading->last == reading->raw) { + reading->changed = 0; + } else { + reading->last = reading->raw; + reading->changed = 1; + } +} +/*---------------------------------------------------------------------------*/ +static void +print_mpu_reading(int reading, char *buf) +{ + char *loc_buf = buf; + + if(reading < 0) { + sprintf(loc_buf, "-"); + reading = -reading; + loc_buf++; + } + + sprintf(loc_buf, "%d.%02d", reading / 100, reading % 100); +} +/*---------------------------------------------------------------------------*/ +static void +get_bmp_reading() +{ + int value; + char *buf; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + if(bmp_pres_reading.publish) { + value = bmp_280_sensor.value(BMP_280_SENSOR_TYPE_PRESS); + if(value != CC26XX_SENSOR_READING_ERROR) { + bmp_pres_reading.raw = value; + + compare_and_update(&bmp_pres_reading); + + buf = bmp_pres_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d.%02d", value / 100, + value % 100); + } + } + + if(bmp_temp_reading.publish) { + value = bmp_280_sensor.value(BMP_280_SENSOR_TYPE_TEMP); + if(value != CC26XX_SENSOR_READING_ERROR) { + bmp_temp_reading.raw = value; + + compare_and_update(&bmp_temp_reading); + + buf = bmp_temp_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d.%02d", value / 100, + value % 100); + } + } + + SENSORS_DEACTIVATE(bmp_280_sensor); + + ctimer_set(&bmp_timer, next, init_bmp_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +get_tmp_reading() +{ + int value; + char *buf; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + if(tmp_amb_reading.publish || tmp_obj_reading.publish) { + if(tmp_007_sensor.value(TMP_007_SENSOR_TYPE_ALL) == + CC26XX_SENSOR_READING_ERROR) { + + SENSORS_DEACTIVATE(tmp_007_sensor); + ctimer_set(&tmp_timer, next, init_tmp_reading, NULL); + } + } + + if(tmp_amb_reading.publish) { + value = tmp_007_sensor.value(TMP_007_SENSOR_TYPE_AMBIENT); + tmp_amb_reading.raw = value; + + compare_and_update(&tmp_amb_reading); + + buf = tmp_amb_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d.%03d", value / 1000, + value % 1000); + } + + if(tmp_obj_reading.publish) { + value = tmp_007_sensor.value(TMP_007_SENSOR_TYPE_OBJECT); + tmp_obj_reading.raw = value; + + compare_and_update(&tmp_obj_reading); + + buf = tmp_obj_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d.%03d", value / 1000, + value % 1000); + } + + SENSORS_DEACTIVATE(tmp_007_sensor); + + ctimer_set(&tmp_timer, next, init_tmp_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +get_sht_reading() +{ + int value; + char *buf; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + if(sht_temp_reading.publish) { + value = sht_21_sensor.value(SHT_21_SENSOR_TYPE_TEMP); + if(value != CC26XX_SENSOR_READING_ERROR) { + sht_temp_reading.raw = value; + + compare_and_update(&sht_temp_reading); + + buf = sht_temp_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d.%02d", value / 100, + value % 100); + } + } + + if(sht_hum_reading.publish) { + value = sht_21_sensor.value(SHT_21_SENSOR_TYPE_HUMIDITY); + if(value != CC26XX_SENSOR_READING_ERROR) { + sht_hum_reading.raw = value; + + compare_and_update(&sht_hum_reading); + + buf = sht_hum_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d.%02d", value / 100, + value % 100); + } + } + + ctimer_set(&sht_timer, next, init_sht_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +get_light_reading() +{ + int value; + char *buf; + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + + value = opt_3001_sensor.value(0); + + SENSORS_DEACTIVATE(opt_3001_sensor); + + if(value != CC26XX_SENSOR_READING_ERROR) { + opt_reading.raw = value; + + compare_and_update(&opt_reading); + + buf = opt_reading.converted; + memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d.%02d", value / 100, + value % 100); + } + + ctimer_set(&opt_timer, next, init_light_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +get_mpu_reading() +{ + clock_time_t next = SENSOR_READING_PERIOD + + (random_rand() % SENSOR_READING_RANDOM); + int raw; + + if(mpu_gyro_x_reading.publish) { + raw = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_GYRO_X); + if(raw != CC26XX_SENSOR_READING_ERROR) { + mpu_gyro_x_reading.raw = raw; + } + } + + if(mpu_gyro_y_reading.publish) { + raw = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_GYRO_Y); + if(raw != CC26XX_SENSOR_READING_ERROR) { + mpu_gyro_y_reading.raw = raw; + } + } + + if(mpu_gyro_z_reading.publish) { + raw = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_GYRO_Z); + if(raw != CC26XX_SENSOR_READING_ERROR) { + mpu_gyro_z_reading.raw = raw; + } + } + + if(mpu_acc_x_reading.publish) { + raw = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_ACC_X); + if(raw != CC26XX_SENSOR_READING_ERROR) { + mpu_acc_x_reading.raw = raw; + } + } + + if(mpu_acc_y_reading.publish) { + raw = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_ACC_Y); + if(raw != CC26XX_SENSOR_READING_ERROR) { + mpu_acc_y_reading.raw = raw; + } + } + + if(mpu_acc_z_reading.publish) { + raw = mpu_9250_sensor.value(MPU_9250_SENSOR_TYPE_ACC_Z); + if(raw != CC26XX_SENSOR_READING_ERROR) { + mpu_acc_z_reading.raw = raw; + } + } + + SENSORS_DEACTIVATE(mpu_9250_sensor); + + if(mpu_gyro_x_reading.publish) { + compare_and_update(&mpu_gyro_x_reading); + memset(mpu_gyro_x_reading.converted, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + print_mpu_reading(mpu_gyro_x_reading.raw, mpu_gyro_x_reading.converted); + } + + if(mpu_gyro_y_reading.publish) { + compare_and_update(&mpu_gyro_y_reading); + memset(mpu_gyro_y_reading.converted, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + print_mpu_reading(mpu_gyro_y_reading.raw, mpu_gyro_y_reading.converted); + } + + if(mpu_gyro_z_reading.publish) { + compare_and_update(&mpu_gyro_z_reading); + memset(mpu_gyro_z_reading.converted, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + print_mpu_reading(mpu_gyro_z_reading.raw, mpu_gyro_z_reading.converted); + } + + if(mpu_acc_x_reading.publish) { + compare_and_update(&mpu_acc_x_reading); + memset(mpu_acc_x_reading.converted, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + print_mpu_reading(mpu_acc_x_reading.raw, mpu_acc_x_reading.converted); + } + + if(mpu_acc_y_reading.publish) { + compare_and_update(&mpu_acc_y_reading); + memset(mpu_acc_y_reading.converted, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + print_mpu_reading(mpu_acc_y_reading.raw, mpu_acc_y_reading.converted); + } + + if(mpu_acc_z_reading.publish) { + compare_and_update(&mpu_acc_z_reading); + memset(mpu_acc_z_reading.converted, 0, CC26XX_WEB_DEMO_CONVERTED_LEN); + print_mpu_reading(mpu_acc_z_reading.raw, mpu_acc_z_reading.converted); + } + + /* We only use the single timer */ + ctimer_set(&mpu_timer, next, init_mpu_reading, NULL); +} +/*---------------------------------------------------------------------------*/ +static void +init_tmp_reading(void *data) +{ + if(tmp_amb_reading.publish || tmp_obj_reading.publish) { + SENSORS_ACTIVATE(tmp_007_sensor); + } else { + ctimer_set(&tmp_timer, CLOCK_SECOND, init_tmp_reading, NULL); + } +} +/*---------------------------------------------------------------------------*/ +static void +init_bmp_reading(void *data) +{ + if(bmp_pres_reading.publish || bmp_temp_reading.publish) { + SENSORS_ACTIVATE(bmp_280_sensor); + } else { + ctimer_set(&bmp_timer, CLOCK_SECOND, init_bmp_reading, NULL); + } +} +/*---------------------------------------------------------------------------*/ +static void +init_sht_reading(void *data) +{ + if(sht_hum_reading.publish || sht_temp_reading.publish) { + SENSORS_ACTIVATE(sht_21_sensor); + } else { + ctimer_set(&sht_timer, CLOCK_SECOND, init_sht_reading, NULL); + } +} +/*---------------------------------------------------------------------------*/ +static void +init_light_reading(void *data) +{ + if(opt_reading.publish) { + SENSORS_ACTIVATE(opt_3001_sensor); + } else { + ctimer_set(&opt_timer, CLOCK_SECOND, init_light_reading, NULL); + } +} +/*---------------------------------------------------------------------------*/ +static void +init_mpu_reading(void *data) +{ + int readings_bitmap = 0; + + if(mpu_acc_x_reading.publish || mpu_acc_y_reading.publish || + mpu_acc_z_reading.publish) { + readings_bitmap |= MPU_9250_SENSOR_TYPE_ACC; + } + + if(mpu_gyro_x_reading.publish || mpu_gyro_y_reading.publish || + mpu_gyro_z_reading.publish) { + readings_bitmap |= MPU_9250_SENSOR_TYPE_GYRO; + } + + if(readings_bitmap) { + mpu_9250_sensor.configure(SENSORS_ACTIVE, readings_bitmap); + } else { + ctimer_set(&mpu_timer, CLOCK_SECOND, init_mpu_reading, NULL); + } +} +#endif +/*---------------------------------------------------------------------------*/ +static void +init_sensor_readings(void) +{ + /* + * Make a first pass and get all initial sensor readings. This will also + * trigger periodic value updates + */ + get_batmon_reading(NULL); + +#if BOARD_SENSORTAG + init_bmp_reading(NULL); + init_light_reading(NULL); + init_sht_reading(NULL); + init_tmp_reading(NULL); + init_mpu_reading(NULL); +#endif /* BOARD_SENSORTAG */ + + return; +} +/*---------------------------------------------------------------------------*/ +static void +init_sensors(void) +{ + + list_add(sensor_list, &batmon_temp_reading); + list_add(sensor_list, &batmon_volt_reading); + SENSORS_ACTIVATE(batmon_sensor); + +#if BOARD_SENSORTAG + list_add(sensor_list, &bmp_pres_reading); + list_add(sensor_list, &bmp_temp_reading); + + list_add(sensor_list, &tmp_obj_reading); + list_add(sensor_list, &tmp_amb_reading); + + list_add(sensor_list, &opt_reading); + + list_add(sensor_list, &sht_hum_reading); + list_add(sensor_list, &sht_temp_reading); + + list_add(sensor_list, &mpu_acc_x_reading); + list_add(sensor_list, &mpu_acc_y_reading); + list_add(sensor_list, &mpu_acc_z_reading); + list_add(sensor_list, &mpu_gyro_x_reading); + list_add(sensor_list, &mpu_gyro_y_reading); + list_add(sensor_list, &mpu_gyro_z_reading); + + SENSORS_ACTIVATE(reed_relay_sensor); +#endif +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(cc26xx_web_demo_process, ev, data) +{ + PROCESS_BEGIN(); + + printf("CC26XX Web Demo Process\n"); + + init_sensors(); + + cc26xx_web_demo_publish_event = process_alloc_event(); + cc26xx_web_demo_config_loaded_event = process_alloc_event(); + cc26xx_web_demo_load_config_defaults = process_alloc_event(); + + /* Start all other (enabled) processes first */ + process_start(&httpd_simple_process, NULL); +#if CC26XX_WEB_DEMO_COAP_SERVER + process_start(&coap_server_process, NULL); +#endif + +#if CC26XX_WEB_DEMO_6LBR_CLIENT + process_start(&cetic_6lbr_client_process, NULL); +#endif + +#if CC26XX_WEB_DEMO_MQTT_CLIENT + process_start(&mqtt_client_process, NULL); +#endif + +#if CC26XX_WEB_DEMO_NET_UART + process_start(&net_uart_process, NULL); +#endif + + /* + * Now that processes have set their own config default values, set our + * own defaults and restore saved config from flash... + */ + cc26xx_web_demo_config.sensors_bitmap = 0xFFFFFFFF; /* all on by default */ + load_config(); + + /* + * Notify all other processes (basically the ones in this demo) that the + * configuration has been loaded from flash, in case they care + */ + process_post(PROCESS_BROADCAST, cc26xx_web_demo_config_loaded_event, NULL); + + init_sensor_readings(); + + httpd_simple_register_post_handler(&sensor_handler); + httpd_simple_register_post_handler(&defaults_handler); + + etimer_set(&et, CC26XX_WEB_DEMO_NET_CONNECT_PERIODIC); + + /* + * Update all sensor readings on a configurable sensors_event + * (e.g a button press / or reed trigger) + */ + while(1) { + if(ev == sensors_event && data == CC26XX_WEB_DEMO_SENSOR_READING_TRIGGER) { + if((CC26XX_WEB_DEMO_SENSOR_READING_TRIGGER)->value( + BUTTON_SENSOR_VALUE_DURATION) > CLOCK_SECOND * 5) { + printf("Restoring defaults!\n"); + cc26xx_web_demo_restore_defaults(); + } else { + init_sensor_readings(); + + process_post(PROCESS_BROADCAST, cc26xx_web_demo_publish_event, NULL); + } + } else if(ev == PROCESS_EVENT_TIMER && etimer_expired(&et)) { + if(uip_ds6_get_global(ADDR_PREFERRED) == NULL) { + leds_on(CC26XX_WEB_DEMO_STATUS_LED); + ctimer_set(&ct, NO_NET_LED_DURATION, publish_led_off, NULL); + etimer_set(&et, CC26XX_WEB_DEMO_NET_CONNECT_PERIODIC); + } + } else if(ev == httpd_simple_event_new_config) { + save_config(); +#if BOARD_SENSORTAG + } else if(ev == sensors_event && data == &bmp_280_sensor) { + get_bmp_reading(); + } else if(ev == sensors_event && data == &opt_3001_sensor) { + get_light_reading(); + } else if(ev == sensors_event && data == &sht_21_sensor) { + get_sht_reading(); + } else if(ev == sensors_event && data == &tmp_007_sensor) { + get_tmp_reading(); + } else if(ev == sensors_event && data == &mpu_9250_sensor) { + get_mpu_reading(); +#endif + } + + PROCESS_YIELD(); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.h b/examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.h new file mode 100644 index 000000000..89bf91d2b --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-examples + * @{ + * + * \defgroup cc26xx-web-demo CC26xx Web Demo + * @{ + * + * An example demonstrating: + * * how to use a CC26XX-powered node in a deployment driven by a 6LBR + * * how to expose on-device sensors as CoAP resources + * * how to build a small web page which reports networking and sensory data + * * how to configure functionality through the aforementioned web page using + * HTTP POST requests + * * a network-based UART + * + * \file + * Main header file for the CC26XX web demo. + */ +/*---------------------------------------------------------------------------*/ +#ifndef CC26XX_WEB_DEMO_H_ +#define CC26XX_WEB_DEMO_H_ +/*---------------------------------------------------------------------------*/ +#include "dev/leds.h" +#include "sys/process.h" +#include "mqtt-client.h" +#include "net-uart.h" + +#include +/*---------------------------------------------------------------------------*/ +#ifdef CC26XX_WEB_DEMO_CONF_MQTT_CLIENT +#define CC26XX_WEB_DEMO_MQTT_CLIENT CC26XX_WEB_DEMO_CONF_MQTT_CLIENT +#else +#define CC26XX_WEB_DEMO_MQTT_CLIENT 1 +#endif + +#ifdef CC26XX_WEB_DEMO_CONF_6LBR_CLIENT +#define CC26XX_WEB_DEMO_6LBR_CLIENT CC26XX_WEB_DEMO_CONF_6LBR_CLIENT +#else +#define CC26XX_WEB_DEMO_6LBR_CLIENT 1 +#endif + +#ifdef CC26XX_WEB_DEMO_CONF_COAP_SERVER +#define CC26XX_WEB_DEMO_COAP_SERVER CC26XX_WEB_DEMO_CONF_COAP_SERVER +#else +#define CC26XX_WEB_DEMO_COAP_SERVER 1 +#endif + +#ifdef CC26XX_WEB_DEMO_CONF_NET_UART +#define CC26XX_WEB_DEMO_NET_UART CC26XX_WEB_DEMO_CONF_NET_UART +#else +#define CC26XX_WEB_DEMO_NET_UART 1 +#endif +/*---------------------------------------------------------------------------*/ +/* User configuration */ +/* Take a sensor reading on button press */ +#define CC26XX_WEB_DEMO_SENSOR_READING_TRIGGER &button_left_sensor + +/* Payload length of ICMPv6 echo requests used to measure RSSI with def rt */ +#define CC26XX_WEB_DEMO_ECHO_REQ_PAYLOAD_LEN 20 + +#if BOARD_SENSORTAG +/* Force an MQTT publish on sensor event */ +#define CC26XX_WEB_DEMO_MQTT_PUBLISH_TRIGGER &reed_relay_sensor +#else +#define CC26XX_WEB_DEMO_MQTT_PUBLISH_TRIGGER &button_right_sensor +#endif + +#define CC26XX_WEB_DEMO_STATUS_LED LEDS_GREEN +/*---------------------------------------------------------------------------*/ +/* A timeout used when waiting to connect to a network */ +#define CC26XX_WEB_DEMO_NET_CONNECT_PERIODIC (CLOCK_SECOND >> 3) +/*---------------------------------------------------------------------------*/ +/* Default configuration values */ +#define CC26XX_WEB_DEMO_DEFAULT_ORG_ID "quickstart" +#define CC26XX_WEB_DEMO_DEFAULT_TYPE_ID "cc26xx" +#define CC26XX_WEB_DEMO_DEFAULT_EVENT_TYPE_ID "status" +#define CC26XX_WEB_DEMO_DEFAULT_SUBSCRIBE_CMD_TYPE "+" +#define CC26XX_WEB_DEMO_DEFAULT_BROKER_PORT 1883 +#define CC26XX_WEB_DEMO_DEFAULT_PUBLISH_INTERVAL (30 * CLOCK_SECOND) +#define CC26XX_WEB_DEMO_DEFAULT_KEEP_ALIVE_TIMER 60 +#define CC26XX_WEB_DEMO_DEFAULT_RSSI_MEAS_INTERVAL (CLOCK_SECOND * 30) +/*---------------------------------------------------------------------------*/ +/* + * You normally won't have to change anything from here onwards unless you are + * extending the example + */ +/*---------------------------------------------------------------------------*/ +/* Sensor types */ +#define CC26XX_WEB_DEMO_SENSOR_BATMON_TEMP 0 +#define CC26XX_WEB_DEMO_SENSOR_BATMON_VOLT 1 +#define CC26XX_WEB_DEMO_SENSOR_BMP_PRES 2 +#define CC26XX_WEB_DEMO_SENSOR_BMP_TEMP 3 +#define CC26XX_WEB_DEMO_SENSOR_TMP_AMBIENT 4 +#define CC26XX_WEB_DEMO_SENSOR_TMP_OBJECT 5 +#define CC26XX_WEB_DEMO_SENSOR_SHT_TEMP 6 +#define CC26XX_WEB_DEMO_SENSOR_SHT_HUMIDITY 7 +#define CC26XX_WEB_DEMO_SENSOR_OPT_LIGHT 8 +#define CC26XX_WEB_DEMO_SENSOR_MPU_ACC_X 9 +#define CC26XX_WEB_DEMO_SENSOR_MPU_ACC_Y 10 +#define CC26XX_WEB_DEMO_SENSOR_MPU_ACC_Z 11 +#define CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_X 12 +#define CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_Y 13 +#define CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_Z 14 +/*---------------------------------------------------------------------------*/ +extern process_event_t cc26xx_web_demo_publish_event; +extern process_event_t cc26xx_web_demo_config_loaded_event; +extern process_event_t cc26xx_web_demo_load_config_defaults; +/*---------------------------------------------------------------------------*/ +#define CC26XX_WEB_DEMO_UNIT_TEMP "C" +#define CC26XX_WEB_DEMO_UNIT_VOLT "mV" +#define CC26XX_WEB_DEMO_UNIT_PRES "hPa" +#define CC26XX_WEB_DEMO_UNIT_HUMIDITY "%RH" +#define CC26XX_WEB_DEMO_UNIT_LIGHT "lux" +#define CC26XX_WEB_DEMO_UNIT_ACC "G" +#define CC26XX_WEB_DEMO_UNIT_GYRO "deg per sec" +/*---------------------------------------------------------------------------*/ +/* A data type for sensor readings, internally stored in a linked list */ +#define CC26XX_WEB_DEMO_CONVERTED_LEN 12 + +typedef struct cc26xx_web_demo_sensor_reading { + struct cc26xx_web_demo_sensor_reading *next; + int raw; + int last; + const char *descr; + const char *xml_element; + const char *form_field; + char *units; + uint8_t type; + uint8_t publish; + uint8_t changed; + char converted[CC26XX_WEB_DEMO_CONVERTED_LEN]; +} cc26xx_web_demo_sensor_reading_t; +/*---------------------------------------------------------------------------*/ +/* Global configuration */ +typedef struct cc26xx_web_demo_config_s { + uint32_t magic; + int len; + uint32_t sensors_bitmap; + mqtt_client_config_t mqtt_config; + net_uart_config_t net_uart; +} cc26xx_web_demo_config_t; + +extern cc26xx_web_demo_config_t cc26xx_web_demo_config; +/*---------------------------------------------------------------------------*/ +/** + * \brief Performs a lookup for a reading of a specific type of sensor + * \param sens_type CC26XX_WEB_DEMO_SENSOR_BATMON_TEMP... + * \return A pointer to the reading data structure or NULL + */ +const cc26xx_web_demo_sensor_reading_t *cc26xx_web_demo_sensor_lookup(int sens_type); + +/** + * \brief Returns the first available sensor reading + * \return A pointer to the reading data structure or NULL + */ +const cc26xx_web_demo_sensor_reading_t *cc26xx_web_demo_sensor_first(void); + +/** + * \brief Print an IPv6 address into a buffer + * \param buf A pointer to the buffer where this function will print the IPv6 + * address + * \param buf_len the length of the buffer + * \param addr A pointer to the IPv6 address + * \return The number of bytes written to the buffer + * + * It is the caller's responsibility to allocate enough space for buf + */ +int cc26xx_web_demo_ipaddr_sprintf(char *buf, uint8_t buf_len, + const uip_ipaddr_t *addr); + +/** + * \brief Resets the example to a default configuration + */ +void cc26xx_web_demo_restore_defaults(void); +/*---------------------------------------------------------------------------*/ +#endif /* CC26XX_WEB_DEMO_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/cetic-6lbr-client.c b/examples/cc26xx/cc26xx-web-demo/cetic-6lbr-client.c new file mode 100644 index 000000000..68a890390 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/cetic-6lbr-client.c @@ -0,0 +1,197 @@ +/* + * 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 Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * An implementation of a 6LBR UDP client. Is used to populate the 6LBR + * web server's 'sensors' tab + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" +#include "net/rpl/rpl.h" +#include "net/ip/uip.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#include "net/ip/uip-debug.h" +/*---------------------------------------------------------------------------*/ +#ifndef CETIC_6LBR_NODE_INFO_PORT +#define CETIC_6LBR_NODE_INFO_PORT 3000 +#endif + +#define MAX_PAYLOAD_LEN 40 +#define MSG_INTERVAL (60 * CLOCK_SECOND) +/*---------------------------------------------------------------------------*/ +static struct uip_udp_conn *client_conn = NULL; +static struct etimer et; +static uip_ip6addr_t dest_addr; +/*---------------------------------------------------------------------------*/ +PROCESS(cetic_6lbr_client_process, "6LBR Client Process"); +/*---------------------------------------------------------------------------*/ +static void +tcpip_handler(void) +{ + char *str; + + if(uip_newdata()) { + str = uip_appdata; + str[uip_datalen()] = '\0'; + PRINTF("Response from the server: '%s'\n", str); + } +} +/*---------------------------------------------------------------------------*/ +static char * +add_ipaddr(char *buf, const uip_ipaddr_t *addr) +{ + uint16_t a; + unsigned int i; + int f; + char *p = buf; + + for(i = 0, f = 0; i < sizeof(uip_ipaddr_t); i += 2) { + a = (addr->u8[i] << 8) + addr->u8[i + 1]; + if(a == 0 && f >= 0) { + if(f++ == 0) { + p += sprintf(p, "::"); + } + } else { + if(f > 0) { + f = -1; + } else if(i > 0) { + p += sprintf(p, ":"); + } + p += sprintf(p, "%04x", a); + } + } + return p; +} +/*---------------------------------------------------------------------------*/ +static void +timeout_handler(void) +{ + static int seq_id; + char buf[MAX_PAYLOAD_LEN]; + int i; + uip_ip6addr_t *globaladdr = NULL; + uint16_t dest_port = CETIC_6LBR_NODE_INFO_PORT; + int has_dest = 0; + rpl_dag_t *dag; + + uip_ds6_addr_t *addr_desc = uip_ds6_get_global(ADDR_PREFERRED); + if(addr_desc != NULL) { + globaladdr = &addr_desc->ipaddr; + dag = rpl_get_any_dag(); + if(dag) { + uip_ipaddr_copy(&dest_addr, globaladdr); + memcpy(&dest_addr.u8[8], &dag->dag_id.u8[8], sizeof(uip_ipaddr_t) / 2); + has_dest = 1; + } + } + + if(has_dest) { + if(client_conn == NULL) { + PRINTF("UDP-CLIENT: address destination: "); + PRINT6ADDR(&dest_addr); + PRINTF("\n"); + client_conn = udp_new(&dest_addr, UIP_HTONS(dest_port), NULL); + + if(client_conn != NULL) { + PRINTF("Created a connection with the server "); + PRINT6ADDR(&client_conn->ripaddr); + PRINTF(" local/remote port %u/%u\n", + UIP_HTONS(client_conn->lport), UIP_HTONS(client_conn->rport)); + } else { + PRINTF("Could not open connection\n"); + } + } else { + if(memcmp(&client_conn->ripaddr, &dest_addr, sizeof(uip_ipaddr_t)) != 0) { + PRINTF("UDP-CLIENT: new address destination: "); + PRINT6ADDR(&dest_addr); + PRINTF("\n"); + uip_udp_remove(client_conn); + client_conn = udp_new(&dest_addr, UIP_HTONS(dest_port), NULL); + if(client_conn != NULL) { + PRINTF("Created a connection with the server "); + PRINT6ADDR(&client_conn->ripaddr); + PRINTF(" local/remote port %u/%u\n", + UIP_HTONS(client_conn->lport), UIP_HTONS(client_conn->rport)); + } else { + PRINTF("Could not open connection\n"); + } + } + } + if(client_conn != NULL) { + PRINTF("Client sending to: "); + PRINT6ADDR(&client_conn->ripaddr); + i = sprintf(buf, "%d | ", ++seq_id); + dag = rpl_get_any_dag(); + if(dag && dag->instance->def_route) { + add_ipaddr(buf + i, &dag->instance->def_route->ipaddr); + } else { + sprintf(buf + i, "(null)"); + } + PRINTF(" (msg: %s)\n", buf); + uip_udp_packet_send(client_conn, buf, strlen(buf)); + } else { + PRINTF("No connection created\n"); + } + } else { + PRINTF("No address configured\n"); + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(cetic_6lbr_client_process, ev, data) +{ + + PROCESS_BEGIN(); + + printf("6LBR Client Process\n"); + + memset(&dest_addr, 0, sizeof(uip_ipaddr_t)); + + etimer_set(&et, MSG_INTERVAL); + while(1) { + PROCESS_YIELD(); + if(etimer_expired(&et)) { + timeout_handler(); + etimer_set(&et, MSG_INTERVAL); + } else if(ev == tcpip_event) { + tcpip_handler(); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/coap-server.c b/examples/cc26xx/cc26xx-web-demo/coap-server.c new file mode 100644 index 000000000..65d748707 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/coap-server.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * A CC26XX-specific CoAP server + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "contiki-net.h" +#include "rest-engine.h" +#include "board-peripherals.h" +#include "dev/cc26xx-rf.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +/* Common resources */ +extern resource_t res_leds; + +extern resource_t res_batmon_temp; +extern resource_t res_batmon_volt; + +extern resource_t res_device_sw; +extern resource_t res_device_hw; +extern resource_t res_device_uptime; +extern resource_t res_device_cfg_reset; + +#if CC26XX_RF_BLE_SUPPORT +extern resource_t res_ble_advd; +#endif + +/* Board-specific resources */ +#if BOARD_SENSORTAG +extern resource_t res_bmp280_temp; +extern resource_t res_bmp280_press; +extern resource_t res_tmp007_amb; +extern resource_t res_tmp007_obj; +extern resource_t res_sht21_temp; +extern resource_t res_sht21_hum; +extern resource_t res_opt3001_light; +extern resource_t res_mpu_acc_x; +extern resource_t res_mpu_acc_y; +extern resource_t res_mpu_acc_z; +extern resource_t res_mpu_gyro_x; +extern resource_t res_mpu_gyro_y; +extern resource_t res_mpu_gyro_z; +extern resource_t res_toggle_red; +extern resource_t res_toggle_green; +#else +extern resource_t res_toggle_red; +extern resource_t res_toggle_green; +extern resource_t res_toggle_orange; +extern resource_t res_toggle_yellow; +#endif +/*---------------------------------------------------------------------------*/ +const char *coap_server_not_found_msg = "Resource not found"; +const char *coap_server_supported_msg = "Supported:" + "text/plain," + "application/json," + "application/xml"; +/*---------------------------------------------------------------------------*/ +static void +start_board_resources(void) +{ +#if BOARD_SENSORTAG + rest_activate_resource(&res_bmp280_temp, "sen/bar/temp"); + rest_activate_resource(&res_bmp280_press, "sen/bar/pres"); + rest_activate_resource(&res_tmp007_amb, "sen/tmp/amb"); + rest_activate_resource(&res_tmp007_obj, "sen/tmp/obj"); + rest_activate_resource(&res_sht21_temp, "sen/sht/t"); + rest_activate_resource(&res_sht21_hum, "sen/sht/h"); + rest_activate_resource(&res_opt3001_light, "sen/opt/light"); + rest_activate_resource(&res_mpu_acc_x, "sen/mpu/acc/x"); + rest_activate_resource(&res_mpu_acc_y, "sen/mpu/acc/y"); + rest_activate_resource(&res_mpu_acc_z, "sen/mpu/acc/z"); + rest_activate_resource(&res_mpu_gyro_x, "sen/mpu/gyro/x"); + rest_activate_resource(&res_mpu_gyro_y, "sen/mpu/gyro/y"); + rest_activate_resource(&res_mpu_gyro_z, "sen/mpu/gyro/z"); + rest_activate_resource(&res_leds, "lt"); + rest_activate_resource(&res_toggle_green, "lt/g"); + rest_activate_resource(&res_toggle_red, "lt/r"); +#elif BOARD_SMARTRF06EB + rest_activate_resource(&res_leds, "lt"); + rest_activate_resource(&res_toggle_red, "lt/r"); + rest_activate_resource(&res_toggle_yellow, "lt/y"); + rest_activate_resource(&res_toggle_green, "lt/g"); + rest_activate_resource(&res_toggle_orange, "lt/o"); +#endif +} +/*---------------------------------------------------------------------------*/ +PROCESS(coap_server_process, "CC26XX CoAP Server"); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(coap_server_process, ev, data) +{ + PROCESS_BEGIN(); + + printf("CC26XX CoAP Server\n"); + + /* Initialize the REST engine. */ + rest_init_engine(); + + rest_activate_resource(&res_batmon_temp, "sen/batmon/temp"); + rest_activate_resource(&res_batmon_volt, "sen/batmon/voltage"); + + rest_activate_resource(&res_device_hw, "dev/mdl/hw"); + rest_activate_resource(&res_device_sw, "dev/mdl/sw"); + rest_activate_resource(&res_device_uptime, "dev/uptime"); + rest_activate_resource(&res_device_cfg_reset, "dev/cfg_reset"); + +#if CC26XX_RF_BLE_SUPPORT + rest_activate_resource(&res_ble_advd, "dev/ble_advd"); +#endif + + start_board_resources(); + + /* Define application-specific events here. */ + while(1) { + PROCESS_WAIT_EVENT(); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/coap-server.h b/examples/cc26xx/cc26xx-web-demo/coap-server.h new file mode 100644 index 000000000..7399597c8 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/coap-server.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * Header file for the CC26xx web demo CoAP functionality + */ +/*---------------------------------------------------------------------------*/ +#include "sys/process.h" +/*---------------------------------------------------------------------------*/ +#ifndef COAP_SERVER_H_ +#define COAP_SERVER_H_ +/*---------------------------------------------------------------------------*/ +extern const char *coap_server_not_found_msg; +extern const char *coap_server_supported_msg; +/*---------------------------------------------------------------------------*/ +PROCESS_NAME(coap_server_process); +/*---------------------------------------------------------------------------*/ +#endif /* COAP_SERVER_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/httpd-simple.c b/examples/cc26xx/cc26xx-web-demo/httpd-simple.c new file mode 100644 index 000000000..281567acf --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/httpd-simple.c @@ -0,0 +1,1298 @@ +/* + * Copyright (c) 2010, Swedish Institute of Computer Science. + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * A simple web server which displays network and sensor information + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "httpd-simple.h" +#include "net/ipv6/uip-ds6-route.h" +#include "batmon-sensor.h" +#include "lib/sensors.h" +#include "lib/list.h" +#include "cc26xx-web-demo.h" +#include "mqtt-client.h" +#include "net-uart.h" + +#include +#include +#include +#include +#include +#include +#include +#include +/*---------------------------------------------------------------------------*/ +#define SEND_STRING(s, str) PSOCK_SEND(s, (uint8_t *)str, strlen(str)) +/*---------------------------------------------------------------------------*/ +#define CONNS 2 +#define CONTENT_LENGTH_MAX 256 +#define STATE_WAITING 0 +#define STATE_OUTPUT 1 +#define IPADDR_BUF_LEN 64 +/*---------------------------------------------------------------------------*/ +#define RETURN_CODE_OK 0 +#define RETURN_CODE_NF 1 /* Not Found */ +#define RETURN_CODE_SU 2 /* Service Unavailable */ +#define RETURN_CODE_BR 3 /* Bad Request */ +#define RETURN_CODE_LR 4 /* Length Required */ +#define RETURN_CODE_TL 5 /* Content Length too Large */ +/*---------------------------------------------------------------------------*/ +/* POST request machine states */ +#define PARSE_POST_STATE_INIT 0 +#define PARSE_POST_STATE_MORE 1 +#define PARSE_POST_STATE_READING_KEY 2 +#define PARSE_POST_STATE_READING_VAL 3 +#define PARSE_POST_STATE_ERROR 0xFFFFFFFF +/*---------------------------------------------------------------------------*/ +#define PARSE_POST_BUF_SIZES 64 + +/* Last byte always used to null terminate */ +#define PARSE_POST_MAX_POS (PARSE_POST_BUF_SIZES - 2) + +static char key[PARSE_POST_BUF_SIZES]; +static char val_escaped[PARSE_POST_BUF_SIZES]; +static char val[PARSE_POST_BUF_SIZES]; +static int key_len; +static int val_len; +static int state; +/*---------------------------------------------------------------------------*/ +/* Stringified min/max intervals */ +#define STRINGIFY(x) XSTR(x) +#define XSTR(x) #x + +#define RSSI_INT_MAX STRINGIFY(MQTT_CLIENT_RSSI_MEASURE_INTERVAL_MAX) +#define RSSI_INT_MIN STRINGIFY(MQTT_CLIENT_RSSI_MEASURE_INTERVAL_MIN) +#define PUB_INT_MAX STRINGIFY(MQTT_CLIENT_PUBLISH_INTERVAL_MAX) +#define PUB_INT_MIN STRINGIFY(MQTT_CLIENT_PUBLISH_INTERVAL_MIN) +/*---------------------------------------------------------------------------*/ +/* + * We can only handle a single POST request at a time. Since a second POST + * request cannot interrupt us while obtaining a lock, we don't really need + * this lock to be atomic. + * + * An HTTP connection will first request a lock before it starts processing + * a POST request. We maintain a global lock which is either NULL or points + * to the http conn which currently has the lock + */ +static struct httpd_state *lock; +/*---------------------------------------------------------------------------*/ +PROCESS(httpd_simple_process, "CC26XX Web Server"); +/*---------------------------------------------------------------------------*/ +#define ISO_nl 0x0A +#define ISO_space 0x20 +#define ISO_slash 0x2F +#define ISO_amp 0x26 +#define ISO_column 0x3A +#define ISO_equal 0x3D +/*---------------------------------------------------------------------------*/ +#define HTTP_200_OK "HTTP/1.0 200 OK\r\n" +#define HTTP_302_FO "HTTP/1.0 302 Found\r\n" +#define HTTP_400_BR "HTTP/1.0 400 Bad Request\r\n" +#define HTTP_404_NF "HTTP/1.0 404 Not Found\r\n" +#define HTTP_411_LR "HTTP/1.0 411 Length Required\r\n" +#define HTTP_413_TL "HTTP/1.0 413 Request Entity Too Large\r\n" +#define HTTP_503_SU "HTTP/1.0 503 Service Unavailable\r\n" +#define CONN_CLOSE "Connection: close\r\n" +/*---------------------------------------------------------------------------*/ +#define SECTION_TAG "div" +#define SECTION_OPEN "<" SECTION_TAG ">" +#define SECTION_CLOSE "" + +#define CONTENT_OPEN "
"
+#define CONTENT_CLOSE "
" +/*---------------------------------------------------------------------------*/ +#define REQUEST_TYPE_GET 1 +#define REQUEST_TYPE_POST 2 +/*---------------------------------------------------------------------------*/ +static const char *NOT_FOUND = "" + "
" + "

404 - file not found

" + "
" + "" + ""; +/*---------------------------------------------------------------------------*/ +/* Page template */ +static const char http_doctype[] = ""; +static const char http_header_200[] = HTTP_200_OK; +static const char http_header_302[] = HTTP_302_FO; +static const char http_header_400[] = HTTP_400_BR; +static const char http_header_404[] = HTTP_404_NF; +static const char http_header_411[] = HTTP_411_LR; +static const char http_header_413[] = HTTP_413_TL; +static const char http_header_503[] = HTTP_503_SU; +static const char http_get[] = "GET "; +static const char http_post[] = "POST "; +static const char http_index_html[] = "/index.html"; +static const char http_html_start[] = ""; +static const char *http_header_srv_str[] = { + "Server: Contiki, ", + BOARD_STRING "\r\n", + NULL +}; + +static const char *http_header_redir_location[] = { + "Location: /config.html\r\n", + NULL +}; + +static const char *http_header_con_close[] = { + CONN_CLOSE, + NULL +}; + +static const char *http_config_css[] = { + "", + NULL +}; +static const char http_head_charset[] = ""; +static const char http_title_start[] = ""; +static const char http_title_end[] = ""; +static const char http_head_end[] = ""; +static const char http_body_start[] = ""; +static const char http_bottom[] = ""; +/*---------------------------------------------------------------------------*/ +static const char http_content_type_html[] = "text/html"; +static const char http_content_type_plain[] = "text/plain"; +/*---------------------------------------------------------------------------*/ +/* For the config page */ +static const char config_div_left[] = "
"; +static const char config_div_right[] = "
"; +static const char config_div_close[] = "
"; +/*---------------------------------------------------------------------------*/ +typedef struct page { + struct page *next; + char *filename; + char *title; +} page_t; + +static page_t http_index_page = { + NULL, + "index.html", + "Index", +}; + +static page_t http_dev_cfg_page = { + NULL, + "config.html", + "Device Config", +}; + +#if CC26XX_WEB_DEMO_NET_UART +static page_t http_net_cfg_page = { + NULL, + "net.html", + "Net-UART Config", +}; +#endif + +#if CC26XX_WEB_DEMO_MQTT_CLIENT +static page_t http_mqtt_cfg_page = { + NULL, + "mqtt.html", + "MQTT/IBM Cloud Config", +}; +#endif +/*---------------------------------------------------------------------------*/ +#define IBM_QUICKSTART_LINK_LEN 128 +static char http_mqtt_a[IBM_QUICKSTART_LINK_LEN]; +/*---------------------------------------------------------------------------*/ +static uint16_t numtimes; +static const httpd_simple_post_handler_t *handler; +/*---------------------------------------------------------------------------*/ +static uint8_t config_ok; +process_event_t httpd_simple_event_new_config; +/*---------------------------------------------------------------------------*/ +struct httpd_state; +typedef char (*httpd_simple_script_t)(struct httpd_state *s); + +struct httpd_state { + char buf[HTTPD_SIMPLE_MAIN_BUF_SIZE]; + char tmp_buf[TMP_BUF_SIZE]; + struct timer timer; + struct psock sin, sout; + int blen; + const char **ptr; + const cc26xx_web_demo_sensor_reading_t *reading; + const page_t *page; + uip_ds6_route_t *r; + uip_ds6_nbr_t *nbr; + httpd_simple_script_t script; + int content_length; + int tmp_buf_len; + int tmp_buf_copied; + char filename[HTTPD_PATHLEN]; + char inputbuf[HTTPD_INBUF_LEN]; + struct pt outputpt; + struct pt generate_pt; + struct pt top_matter_pt; + char state; + char request_type; + char return_code; +}; +/*---------------------------------------------------------------------------*/ +LIST(post_handlers); +LIST(pages_list); +MEMB(conns, struct httpd_state, CONNS); +/*---------------------------------------------------------------------------*/ +#define HEX_TO_INT(x) (isdigit(x) ? x - '0' : x - 'W') +static size_t +url_unescape(const char *src, size_t srclen, char *dst, size_t dstlen) +{ + size_t i, j; + int a, b; + + for(i = j = 0; i < srclen && j < dstlen - 1; i++, j++) { + if(src[i] == '%' && isxdigit(*(unsigned char *)(src + i + 1)) + && isxdigit(*(unsigned char *)(src + i + 2))) { + a = tolower(*(unsigned char *)(src + i + 1)); + b = tolower(*(unsigned char *)(src + i + 2)); + dst[j] = ((HEX_TO_INT(a) << 4) | HEX_TO_INT(b)) & 0xff; + i += 2; + } else if(src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; + + return i == srclen; +} +/*---------------------------------------------------------------------------*/ +void +httpd_simple_register_post_handler(httpd_simple_post_handler_t *h) +{ + list_add(post_handlers, h); +} +/*---------------------------------------------------------------------------*/ +static void +get_neighbour_state_text(char *buf, uint8_t state) +{ + switch(state) { + case NBR_INCOMPLETE: + memcpy(buf, "INCOMPLETE", strlen("INCOMPLETE")); + break; + case NBR_REACHABLE: + memcpy(buf, "REACHABLE", strlen("REACHABLE")); + break; + case NBR_STALE: + memcpy(buf, "STALE", strlen("STALE")); + break; + case NBR_DELAY: + memcpy(buf, "DELAY", strlen("DELAY")); + break; + case NBR_PROBE: + memcpy(buf, "NBR_PROBE", strlen("NBR_PROBE")); + break; + } +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(enqueue_chunk(struct httpd_state *s, uint8_t immediate, + const char *format, ...)) +{ + va_list ap; + + PSOCK_BEGIN(&s->sout); + + va_start(ap, format); + + s->tmp_buf_len = vsnprintf(s->tmp_buf, TMP_BUF_SIZE, format, ap); + + va_end(ap); + + if(s->blen + s->tmp_buf_len < HTTPD_SIMPLE_MAIN_BUF_SIZE) { + /* Enough space for the entire chunk. Copy over */ + memcpy(&s->buf[s->blen], s->tmp_buf, s->tmp_buf_len); + s->blen += s->tmp_buf_len; + } else { + memcpy(&s->buf[s->blen], s->tmp_buf, HTTPD_SIMPLE_MAIN_BUF_SIZE - s->blen); + s->tmp_buf_copied = HTTPD_SIMPLE_MAIN_BUF_SIZE - s->blen; + s->blen = HTTPD_SIMPLE_MAIN_BUF_SIZE; + PSOCK_SEND(&s->sout, (uint8_t *)s->buf, s->blen); + s->blen = 0; + if(s->tmp_buf_copied < s->tmp_buf_len) { + memcpy(s->buf, &s->tmp_buf[s->tmp_buf_copied], + s->tmp_buf_len - s->tmp_buf_copied); + s->blen += s->tmp_buf_len - s->tmp_buf_copied; + } + } + + if(immediate != 0 && s->blen > 0) { + PSOCK_SEND(&s->sout, (uint8_t *)s->buf, s->blen); + s->blen = 0; + } + + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(generate_top_matter(struct httpd_state *s, const char *title, + const char **css)) +{ + + PT_BEGIN(&s->top_matter_pt); + + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, http_doctype)); + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, http_html_start)); + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, http_title_start)); + + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, title)); + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, http_title_end)); + + if(css != NULL) { + for(s->ptr = css; *(s->ptr) != NULL; s->ptr++) { + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, *(s->ptr))); + } + } + + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, http_head_charset)); + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, http_head_end)); + PT_WAIT_THREAD(&s->top_matter_pt, enqueue_chunk(s, 0, http_body_start)); + + /* Links */ + PT_WAIT_THREAD(&s->top_matter_pt, + enqueue_chunk(s, 0, SECTION_OPEN "

")); + + s->page = list_head(pages_list); + PT_WAIT_THREAD(&s->top_matter_pt, + enqueue_chunk(s, 0, "[ %s ]", + s->page->filename, s->page->title)); + + for(s->page = s->page->next; s->page != NULL; s->page = s->page->next) { + PT_WAIT_THREAD(&s->top_matter_pt, + enqueue_chunk(s, 0, " | [ %s ]", + s->page->filename, s->page->title)); + } + +#if CC26XX_WEB_DEMO_MQTT_CLIENT + PT_WAIT_THREAD(&s->top_matter_pt, + enqueue_chunk(s, 0, " | %s", http_mqtt_a)); +#endif + PT_WAIT_THREAD(&s->top_matter_pt, + enqueue_chunk(s, 0, "

" SECTION_CLOSE)); + + PT_END(&s->top_matter_pt); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(generate_index(struct httpd_state *s)) +{ + char ipaddr_buf[IPADDR_BUF_LEN]; /* Intentionally on stack */ + + PT_BEGIN(&s->generate_pt); + + /* Generate top matter (doctype, title, nav links etc) */ + PT_WAIT_THREAD(&s->generate_pt, + generate_top_matter(s, http_index_page.title, NULL)); + + /* ND Cache */ + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, SECTION_OPEN "Neighbors" CONTENT_OPEN)); + + for(s->nbr = nbr_table_head(ds6_neighbors); s->nbr != NULL; + s->nbr = nbr_table_next(ds6_neighbors, s->nbr)) { + + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "\n")); + + memset(ipaddr_buf, 0, IPADDR_BUF_LEN); + cc26xx_web_demo_ipaddr_sprintf(ipaddr_buf, IPADDR_BUF_LEN, &s->nbr->ipaddr); + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "%s", ipaddr_buf)); + + memset(ipaddr_buf, 0, IPADDR_BUF_LEN); + get_neighbour_state_text(ipaddr_buf, s->nbr->state); + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, " %s", ipaddr_buf)); + } + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, CONTENT_CLOSE SECTION_CLOSE)); + + /* Default Route */ + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + SECTION_OPEN "Default Route" CONTENT_OPEN)); + + memset(ipaddr_buf, 0, IPADDR_BUF_LEN); + cc26xx_web_demo_ipaddr_sprintf(ipaddr_buf, IPADDR_BUF_LEN, + uip_ds6_defrt_choose()); + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "%s", ipaddr_buf)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, CONTENT_CLOSE SECTION_CLOSE)); + + /* Routes */ + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, SECTION_OPEN "Routes" CONTENT_OPEN)); + + for(s->r = uip_ds6_route_head(); s->r != NULL; + s->r = uip_ds6_route_next(s->r)) { + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "\n")); + + memset(ipaddr_buf, 0, IPADDR_BUF_LEN); + cc26xx_web_demo_ipaddr_sprintf(ipaddr_buf, IPADDR_BUF_LEN, &s->r->ipaddr); + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "%s", ipaddr_buf)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, " / %u via ", s->r->length)); + + memset(ipaddr_buf, 0, IPADDR_BUF_LEN); + cc26xx_web_demo_ipaddr_sprintf(ipaddr_buf, IPADDR_BUF_LEN, + uip_ds6_route_nexthop(s->r)); + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "%s", ipaddr_buf)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + ", lifetime=%lus", s->r->state.lifetime)); + } + + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, + CONTENT_CLOSE SECTION_CLOSE)); + + /* Sensors */ + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, SECTION_OPEN "Sensors" CONTENT_OPEN)); + + for(s->reading = cc26xx_web_demo_sensor_first(); + s->reading != NULL; s->reading = s->reading->next) { + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "\n%s = %s %s", s->reading->descr, + s->reading->publish ? s->reading->converted : "N/A", + s->reading->units)); + } + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, CONTENT_CLOSE SECTION_CLOSE)); + + /* Footer */ + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, SECTION_OPEN)); + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "Page hits: %u
", + ++numtimes)); + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "Uptime: %lu secs
", + clock_seconds())); + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, SECTION_CLOSE)); + + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 1, http_bottom)); + + PT_END(&s->generate_pt); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(generate_config(struct httpd_state *s)) +{ + PT_BEGIN(&s->generate_pt); + + /* Generate top matter (doctype, title, nav links etc) */ + PT_WAIT_THREAD(&s->generate_pt, + generate_top_matter(s, http_dev_cfg_page.title, + http_config_css)); + + /* Sensor Settings */ + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "

Sensors

")); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "
generate_pt, + enqueue_chunk(s, 0, "method=\"post\" enctype=\"")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "application/x-www-form-urlencoded\" ")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "accept-charset=\"UTF-8\">")); + + for(s->reading = cc26xx_web_demo_sensor_first(); + s->reading != NULL; s->reading = s->reading->next) { + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%s%s:%s%s", config_div_left, + s->reading->descr, config_div_close, + config_div_right)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "generate_pt, + enqueue_chunk(s, 0, "title=\"On\" name=\"%s\"%s>", + s->reading->form_field, + s->reading->publish ? " Checked" : "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "generate_pt, + enqueue_chunk(s, 0, "title=\"Off\" name=\"%s\"%s>%s", + s->reading->form_field, + s->reading->publish ? "" : " Checked", + config_div_close)); + } + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "
")); + + /* Actions */ + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, "

Actions

")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "
generate_pt, + enqueue_chunk(s, 0, "method=\"post\" enctype=\"")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "application/x-www-form-urlencoded\" ")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "accept-charset=\"UTF-8\">")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "
")); + + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 1, http_bottom)); + + PT_END(&s->generate_pt); +} +/*---------------------------------------------------------------------------*/ +#if CC26XX_WEB_DEMO_MQTT_CLIENT +static +PT_THREAD(generate_mqtt_config(struct httpd_state *s)) +{ + PT_BEGIN(&s->generate_pt); + + /* Generate top matter (doctype, title, nav links etc) */ + PT_WAIT_THREAD(&s->generate_pt, + generate_top_matter(s, http_mqtt_cfg_page.title, + http_config_css)); + + /* MQTT client settings */ + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "

%s

", http_mqtt_cfg_page.title)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "
generate_pt, + enqueue_chunk(s, 0, "method=\"post\" enctype=\"")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "application/x-www-form-urlencoded\" ")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "accept-charset=\"UTF-8\">")); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sType ID:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%s\" ", + cc26xx_web_demo_config.mqtt_config.type_id)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "name=\"type_id\">%s", config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sOrg ID:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%s\" ", + cc26xx_web_demo_config.mqtt_config.org_id)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "name=\"org_id\">%s", config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sAuth Token:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"\" ")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "name=\"auth_token\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sCommand Type:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%s\" ", + cc26xx_web_demo_config.mqtt_config.cmd_type)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "name=\"cmd_type\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sEvent Type ID:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%s\" ", + cc26xx_web_demo_config.mqtt_config.event_type_id)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "name=\"event_type_id\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sInterval (secs):%s", + config_div_left, config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%lu\" ", + (clock_time_t) + (cc26xx_web_demo_config.mqtt_config.pub_interval + / CLOCK_SECOND))); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "min=\"" PUB_INT_MIN "\" " + "max=\"" PUB_INT_MAX "\" " + "name=\"interval\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sBroker IP:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%s\" ", + cc26xx_web_demo_config.mqtt_config.broker_ip)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "name=\"broker_ip\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sBroker Port:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%d\" ", + cc26xx_web_demo_config.mqtt_config.broker_port)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "min=\"1\" max=\"65535\" " + "name=\"broker_port\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sRSSI Interval (secs):%s", + config_div_left, config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%lu\" ", + (clock_time_t) + (cc26xx_web_demo_config.mqtt_config.def_rt_ping_interval + / CLOCK_SECOND))); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "min=\"" RSSI_INT_MIN "\" " + "max=\"" RSSI_INT_MAX "\" " + "name=\"ping_interval\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "
")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "
generate_pt, + enqueue_chunk(s, 0, "method=\"post\" enctype=\"")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "application/x-www-form-urlencoded\" ")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "accept-charset=\"UTF-8\">")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "
")); + + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 1, http_bottom)); + + PT_END(&s->generate_pt); +} +#endif +/*---------------------------------------------------------------------------*/ +#if CC26XX_WEB_DEMO_NET_UART +static +PT_THREAD(generate_net_uart_config(struct httpd_state *s)) +{ + + PT_BEGIN(&s->generate_pt); + + /* Generate top matter (doctype, title, nav links etc) */ + PT_WAIT_THREAD(&s->generate_pt, + generate_top_matter(s, http_net_cfg_page.title, + http_config_css)); + + /* Net-UART settings */ + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "

%s

", http_net_cfg_page.title)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "
generate_pt, + enqueue_chunk(s, 0, "method=\"post\" enctype=\"")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "application/x-www-form-urlencoded\" ")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "accept-charset=\"UTF-8\">")); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sRemote IPv6:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%s\" ", + cc26xx_web_demo_config.net_uart.remote_address)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "name=\"net_uart_ip\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sRemote Port:%s", config_div_left, + config_div_close)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%sgenerate_pt, + enqueue_chunk(s, 0, "value=\"%u\" ", + cc26xx_web_demo_config.net_uart.remote_port)); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "min=\"1\" max=\"65535\" " + "name=\"net_uart_port\">%s", + config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "%s%s:%s%s", config_div_left, + "Enable", config_div_close, + config_div_right)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "generate_pt, + enqueue_chunk(s, 0, "title=\"On\" name=\"net_uart_on\"%s>", + cc26xx_web_demo_config.net_uart.enable ? + " Checked" : "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "generate_pt, + enqueue_chunk(s, 0, "title=\"Off\" name=\"net_uart_on\"" + "%s>%s", + cc26xx_web_demo_config.net_uart.enable ? + "" : " Checked", config_div_close)); + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, + "")); + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "
")); + + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 1, http_bottom)); + + PT_END(&s->generate_pt); +} +#endif +/*---------------------------------------------------------------------------*/ +static void +lock_obtain(struct httpd_state *s) +{ + if(lock == NULL) { + lock = s; + } +} +/*---------------------------------------------------------------------------*/ +static void +lock_release(struct httpd_state *s) +{ + if(lock == s) { + lock = NULL; + } +} +/*---------------------------------------------------------------------------*/ +static void +parse_post_request_chunk(char *buf, int buf_len, int last_chunk) +{ + int i; + int finish; + + for(i = 0; i < buf_len; i++) { + switch(state) { + case PARSE_POST_STATE_INIT: + state = PARSE_POST_STATE_MORE; + /* continue */ + case PARSE_POST_STATE_MORE: + memset(key, 0, PARSE_POST_BUF_SIZES); + memset(val, 0, PARSE_POST_BUF_SIZES); + memset(val_escaped, 0, PARSE_POST_BUF_SIZES); + key_len = 0; + val_len = 0; + state = PARSE_POST_STATE_READING_KEY; + /* continue */ + case PARSE_POST_STATE_READING_KEY: + if(buf[i] == ISO_equal) { + state = PARSE_POST_STATE_READING_VAL; + } else if(buf[i] == ISO_amp) { + /* Don't accept an amp while reading a key */ + state = PARSE_POST_STATE_ERROR; + } else { + /* Make sure we don't overshoot key's boundary */ + if(key_len <= PARSE_POST_MAX_POS) { + key[key_len] = buf[i]; + key_len++; + } else { + /* Not enough space for the key. Abort */ + state = PARSE_POST_STATE_ERROR; + } + } + break; + case PARSE_POST_STATE_READING_VAL: + finish = 0; + if(buf[i] == ISO_amp) { + finish = 1; + } else if(buf[i] == ISO_equal) { + /* Don't accept an '=' while reading a val */ + state = PARSE_POST_STATE_ERROR; + } else { + /* Make sure we don't overshoot key's boundary */ + if(val_len <= PARSE_POST_MAX_POS) { + val[val_len] = buf[i]; + val_len++; + /* Last character of the last chunk */ + if((i == buf_len - 1) && (last_chunk == 1)) { + finish = 1; + } + } else { + /* Not enough space for the value. Abort */ + state = PARSE_POST_STATE_ERROR; + } + } + + if(finish == 1) { + /* + * Done reading a key=value pair, either because we encountered an amp + * or because we reached the end of the message body. + * + * First, unescape the value. + * + * Then invoke handlers. We will bail out with PARSE_POST_STATE_ERROR, + * unless the key-val gets correctly processed + */ + url_unescape(val, val_len, val_escaped, PARSE_POST_BUF_SIZES); + val_len = strlen(val_escaped); + + for(handler = list_head(post_handlers); handler != NULL; + handler = list_item_next((void *)handler)) { + if(handler->handler != NULL) { + finish = handler->handler(key, key_len, val_escaped, val_len); + } + if(finish == HTTPD_SIMPLE_POST_HANDLER_ERROR) { + state = PARSE_POST_STATE_ERROR; + break; + } else if(finish == HTTPD_SIMPLE_POST_HANDLER_OK) { + /* Restart the state machine to expect the next pair */ + state = PARSE_POST_STATE_MORE; + + /* + * At least one handler returned OK, therefore we must generate a + * new config event when we're done. + */ + config_ok = 1; + break; + } + /* Else, continue */ + } + } + break; + case PARSE_POST_STATE_ERROR: + /* If we entered the error state earlier, do nothing */ + return; + default: + break; + } + } +} +/*---------------------------------------------------------------------------*/ +static httpd_simple_script_t +get_script(const char *name) +{ + if(strlen(name) == 10 && strncmp(name, "index.html", 10) == 0) { + return generate_index; + } else if(strlen(name) == 11 && strncmp(name, "config.html", 11) == 0) { + return generate_config; +#if CC26XX_WEB_DEMO_MQTT_CLIENT + } else if(strlen(name) == 9 && strncmp(name, "mqtt.html", 9) == 0) { + return generate_mqtt_config; +#endif +#if CC26XX_WEB_DEMO_NET_UART + } else if(strlen(name) == 8 && strncmp(name, "net.html", 8) == 0) { + return generate_net_uart_config; +#endif + } + + return NULL; +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(send_string(struct httpd_state *s, const char *str)) +{ + PSOCK_BEGIN(&s->sout); + + SEND_STRING(&s->sout, str); + + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr, + const char *content_type, const char **additional)) +{ + PT_BEGIN(&s->generate_pt); + + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, statushdr)); + + for(s->ptr = http_header_srv_str; *(s->ptr) != NULL; s->ptr++) { + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, *(s->ptr))); + } + + if(additional) { + for(s->ptr = additional; *(s->ptr) != NULL; s->ptr++) { + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 0, *(s->ptr))); + } + } + + PT_WAIT_THREAD(&s->generate_pt, + enqueue_chunk(s, 0, "Content-type: %s; ", content_type)); + + PT_WAIT_THREAD(&s->generate_pt, enqueue_chunk(s, 1, "charset=UTF-8\r\n\r\n")); + + PT_END(&s->generate_pt); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(handle_output(struct httpd_state *s)) +{ + PT_BEGIN(&s->outputpt); + + s->script = NULL; + + PT_INIT(&s->generate_pt); + PT_INIT(&s->top_matter_pt); + + if(s->request_type == REQUEST_TYPE_POST) { + if(s->return_code == RETURN_CODE_OK) { + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_302, + http_content_type_plain, + http_header_redir_location)); + } else if(s->return_code == RETURN_CODE_LR) { + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_411, + http_content_type_plain, + http_header_con_close)); + PT_WAIT_THREAD(&s->outputpt, send_string(s, "Content-Length Required\n")); + } else if(s->return_code == RETURN_CODE_TL) { + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_413, + http_content_type_plain, + http_header_con_close)); + PT_WAIT_THREAD(&s->outputpt, send_string(s, "Content-Length too Large\n")); + } else if(s->return_code == RETURN_CODE_SU) { + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_503, + http_content_type_plain, + http_header_con_close)); + PT_WAIT_THREAD(&s->outputpt, send_string(s, "Service Unavailable\n")); + } else { + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_400, + http_content_type_plain, + http_header_con_close)); + PT_WAIT_THREAD(&s->outputpt, send_string(s, "Bad Request\n")); + } + } else if(s->request_type == REQUEST_TYPE_GET) { + s->script = get_script(&s->filename[1]); + if(s->script == NULL) { + strncpy(s->filename, "/notfound.html", sizeof(s->filename)); + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_404, + http_content_type_html, + http_header_con_close)); + PT_WAIT_THREAD(&s->outputpt, + send_string(s, NOT_FOUND)); + uip_close(); + PT_EXIT(&s->outputpt); + } else { + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_200, + http_content_type_html, + http_header_con_close)); + PT_WAIT_THREAD(&s->outputpt, s->script(s)); + } + } + s->script = NULL; + PSOCK_CLOSE(&s->sout); + PT_END(&s->outputpt); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(handle_input(struct httpd_state *s)) +{ + PSOCK_BEGIN(&s->sin); + + PSOCK_READTO(&s->sin, ISO_space); + + if(strncasecmp(s->inputbuf, http_get, 4) == 0) { + s->request_type = REQUEST_TYPE_GET; + PSOCK_READTO(&s->sin, ISO_space); + + if(s->inputbuf[0] != ISO_slash) { + PSOCK_CLOSE_EXIT(&s->sin); + } + + if(s->inputbuf[1] == ISO_space) { + strncpy(s->filename, http_index_html, sizeof(s->filename)); + } else { + s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; + strncpy(s->filename, s->inputbuf, sizeof(s->filename)); + } + } else if(strncasecmp(s->inputbuf, http_post, 5) == 0) { + s->request_type = REQUEST_TYPE_POST; + /* POST: Read out the rest of the line and ignore it */ + PSOCK_READTO(&s->sin, ISO_nl); + + /* + * Start parsing headers. We look for Content-Length and ignore everything + * else until we hit the start of the message body. + * + * We will return 411 if the client doesn't send Content-Length and 413 + * if Content-Length is too high + */ + s->content_length = 0; + s->return_code = RETURN_CODE_LR; + do { + s->inputbuf[PSOCK_DATALEN(&s->sin)] = 0; + /* We anticipate a content length */ + if((PSOCK_DATALEN(&s->sin) > 14) && + strncasecmp(s->inputbuf, "Content-Length:", 15) == 0) { + char *val_start = &s->inputbuf[15]; + s->content_length = atoi(val_start); + + /* So far so good */ + s->return_code = RETURN_CODE_OK; + } + PSOCK_READTO(&s->sin, ISO_nl); + } while(PSOCK_DATALEN(&s->sin) != 2); + + /* + * Done reading headers. + * Reject content length greater than CONTENT_LENGTH_MAX bytes + */ + if(s->content_length > CONTENT_LENGTH_MAX) { + s->content_length = 0; + s->return_code = RETURN_CODE_TL; + } + + if(s->return_code == RETURN_CODE_OK) { + /* Acceptable Content Length. Try to obtain a lock */ + lock_obtain(s); + + if(lock == s) { + state = PARSE_POST_STATE_INIT; + } else { + s->return_code = RETURN_CODE_SU; + } + } + + /* Parse the message body, unless we have detected an error. */ + while(s->content_length > 0 && lock == s && + s->return_code == RETURN_CODE_OK) { + PSOCK_READBUF_LEN(&s->sin, s->content_length); + s->content_length -= PSOCK_DATALEN(&s->sin); + + /* Parse the message body */ + parse_post_request_chunk(s->inputbuf, PSOCK_DATALEN(&s->sin), + (s->content_length == 0)); + if(state == PARSE_POST_STATE_ERROR) { + /* Could not parse: Bad Request and stop parsing */ + s->return_code = RETURN_CODE_BR; + } + } + + /* + * Done. If our return code is OK but the state machine is not in + * STATE_MORE, it means that the message body ended half-way reading a key + * or value. Set 'Bad Request' + */ + if(s->return_code == RETURN_CODE_OK && state != PARSE_POST_STATE_MORE) { + s->return_code = RETURN_CODE_BR; + } + + /* If the flag is set, we had at least 1 configuration value accepted */ + if(config_ok) { + process_post(PROCESS_BROADCAST, httpd_simple_event_new_config, NULL); + } + config_ok = 0; + + lock_release(s); + } else { + PSOCK_CLOSE_EXIT(&s->sin); + } + + s->state = STATE_OUTPUT; + + while(1) { + PSOCK_READTO(&s->sin, ISO_nl); + } + + PSOCK_END(&s->sin); +} +/*---------------------------------------------------------------------------*/ +static void +handle_connection(struct httpd_state *s) +{ + handle_input(s); + if(s->state == STATE_OUTPUT) { + handle_output(s); + } +} +/*---------------------------------------------------------------------------*/ +static void +appcall(void *state) +{ + struct httpd_state *s = (struct httpd_state *)state; + + if(uip_closed() || uip_aborted() || uip_timedout()) { + if(s != NULL) { + s->script = NULL; + s->blen = 0; + s->tmp_buf_len = 0; + memb_free(&conns, s); + } + } else if(uip_connected()) { + s = (struct httpd_state *)memb_alloc(&conns); + if(s == NULL) { + uip_abort(); + return; + } + tcp_markconn(uip_conn, s); + PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); + PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); + PT_INIT(&s->outputpt); + s->script = NULL; + s->state = STATE_WAITING; + timer_set(&s->timer, CLOCK_SECOND * 10); + handle_connection(s); + } else if(s != NULL) { + if(uip_poll()) { + if(timer_expired(&s->timer)) { + uip_abort(); + s->script = NULL; + memb_free(&conns, s); + } + } else { + timer_restart(&s->timer); + } + handle_connection(s); + } else { + uip_abort(); + } +} +/*---------------------------------------------------------------------------*/ +static void +init(void) +{ + tcp_listen(UIP_HTONS(80)); + memb_init(&conns); + + list_add(pages_list, &http_index_page); + list_add(pages_list, &http_dev_cfg_page); + +#if CC26XX_WEB_DEMO_NET_UART + list_add(pages_list, &http_net_cfg_page); +#endif + +#if CC26XX_WEB_DEMO_MQTT_CLIENT + list_add(pages_list, &http_mqtt_cfg_page); +#endif +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(httpd_simple_process, ev, data) +{ + PROCESS_BEGIN(); + + printf("CC26XX Web Server\n"); + + httpd_simple_event_new_config = process_alloc_event(); + + init(); + + snprintf(http_mqtt_a, IBM_QUICKSTART_LINK_LEN, + "[ IBM Quickstart ]", + linkaddr_node_addr.u8[0], linkaddr_node_addr.u8[1], + linkaddr_node_addr.u8[2], linkaddr_node_addr.u8[5], + linkaddr_node_addr.u8[6], linkaddr_node_addr.u8[7]); + + while(1) { + PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event); + appcall(data); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/httpd-simple.h b/examples/cc26xx/cc26xx-web-demo/httpd-simple.h new file mode 100644 index 000000000..c7631adb1 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/httpd-simple.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010, Swedish Institute of Computer Science. + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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 + * Header file for the HTTPD of the cc26xx web demo example. + * \author + * Adam Dunkels + * Niclas Finne + * Joakim Eriksson + * Texas Instruments Incorporated - http://www.ti.com/ + */ +/*---------------------------------------------------------------------------*/ +#ifndef HTTPD_SIMPLE_H_ +#define HTTPD_SIMPLE_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki-net.h" +#include "sys/process.h" +#include "cc26xx-web-demo.h" +/*---------------------------------------------------------------------------*/ +/* Ideally a multiple of TCP_MSS */ +#ifdef HTTPD_SIMPLE_CONF_MAIN_BUF_SIZE +#define HTTPD_SIMPLE_MAIN_BUF_SIZE HTTPD_SIMPLE_CONF_BUF_SIZE +#else +#define HTTPD_SIMPLE_MAIN_BUF_SIZE UIP_TCP_MSS +#endif +/*---------------------------------------------------------------------------*/ +#define HTTPD_PATHLEN 16 +#define HTTPD_INBUF_LEN (HTTPD_PATHLEN + 10) + +#define TMP_BUF_SIZE (UIP_TCP_MSS + 1) +/*---------------------------------------------------------------------------*/ +/* POST request handlers */ +#define HTTPD_SIMPLE_POST_HANDLER_OK 1 +#define HTTPD_SIMPLE_POST_HANDLER_UNKNOWN 0 +#define HTTPD_SIMPLE_POST_HANDLER_ERROR 0xFFFFFFFF + +/** + * \brief Datatype for a handler which can process incoming POST requests + * \param key The configuration key to be updated + * \param key_len The length of the key argument + * \param val The new configuration value for key + * \param val_len The length of the value argument + * + * \return 1: HTTPD_SIMPLE_POST_HANDLER_OK if the function can handle the + * request, HTTPD_SIMPLE_POST_HANDLER_UNKNOWN if it does not know how to handle + * it. HTTPD_SIMPLE_POST_HANDLER_ERROR if it does know how to handle it but + * the request was malformed. + */ +typedef struct httpd_simple_post_handler { + struct httpd_simple_post_handler *next; + int (*handler)(char *key, int key_len, char *val, int val_len); +} httpd_simple_post_handler_t; + +/* Declare a handler */ +#define HTTPD_SIMPLE_POST_HANDLER(name, fp) \ + httpd_simple_post_handler_t name##_handler = { NULL, fp } + +/** + * \brief Register a handler for POST requests + * \param h A pointer to the handler structure + */ +void httpd_simple_register_post_handler(httpd_simple_post_handler_t *h); +/*---------------------------------------------------------------------------*/ +/* + * An event generated by the HTTPD when a new configuration request has been + * received + */ +extern process_event_t httpd_simple_event_new_config; +/*---------------------------------------------------------------------------*/ +PROCESS_NAME(httpd_simple_process); +/*---------------------------------------------------------------------------*/ +#endif /* HTTPD_SIMPLE_H_ */ diff --git a/examples/cc26xx/cc26xx-web-demo/img/6lbr-web.png b/examples/cc26xx/cc26xx-web-demo/img/6lbr-web.png new file mode 100644 index 0000000000000000000000000000000000000000..5308c412bc183fe3f62930fec950a1029ebc036b GIT binary patch literal 81741 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 LdQl>7^!EP%AFu2O literal 0 HcmV?d00001 diff --git a/examples/cc26xx/cc26xx-web-demo/img/coap-resources.png b/examples/cc26xx/cc26xx-web-demo/img/coap-resources.png new file mode 100644 index 0000000000000000000000000000000000000000..c71c934fe8ab419f03c1ace336352b549e652eca GIT binary patch literal 280210 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

^W%BQh})en4Y ztzBPLz!gJAe_zObD!HMrcYMG*OZq#tO7TJNNa17|2nHnak*k&5&U`HXQ2QF&GLviL zma@PDI;1;*AwsT$T&-%6U%OFeuMKv+JQhm&oh(LRo%4ZAW2FrK;G?VW-I4+zVmZq0 zo63*t-uqvFXT;_@)!t6HMOAtLh9-YJ`KP4HvB%R*cBO@1B(gqTWs?UdM1T;)w43wd zP$Ux@Ml7%7pQY8E=EPomh~P=9jXWl?;>^U3`MWGon}8YnlSAJv%)R_W-< zO)KS}-p*!o?f6!k@10}h9FHBwDs{;(h&y@HAFWQ&&t(p}eDAMKgkj_mXEL=P=s$%a zwtliMM6r+igsXg}#hZ0R6w)(cb*Qm~F_31mK9+^E$b!nvb1rm@`jlUPU*NfJ987J2 z?Je<+xl`6X2$fK8fa*D%WR#XKzfl}q>dj6hg~VL=hrD?NbUbR-oOr6r`!_1kZgxAy ztjUV#9mx`l6GwEPlK3@(1CA-}@;G3)Kt-4J!B$psDn0ztJI>S9pcyklPkTDl1Bd7) zPU6Hy-pO`IF#ZYn*pN*m`bx%y5ed-o~c{t@Q9e@ zJz#hqY?nHFjXd1k?DhlW^GmFyTk}N?9`g3QAflCAu?KVGSA2!aOPl_p3_)GX6b7NLf*B@Y0_7@{sN3v0Dw76KG3x8b15$N@Axn zHj~;!2FULA3yHql$E3q_{RdOx7%ld_;KCZzC5-_48yh*4LOjxDjc8{Vi>blcff^?R zd_fQf=nb@d`38CDe^$dK@Fw8AM^$pBy}r4DAq(Tz7{hR$xvhRiK}>Ja7cV{be10An zh0SQRGc<9yT$knkw&5W-y)^a+E|k?8sL&yj&cAgc$Z^9e^nVv%IF66_8i%_UElZq>ef376*;K$P@F^HeA3+-5dEZbx7!3;YFFSa~!4yLOYKE1AO8GB9x1hbw2QeQ=68zUq%KZ zsmEOCuvC7Z)q0DWqU+dB=QQY>M5?F{nEcJ8Z?06)kQc6@wxO2CT3o$3&4wS9Eis%d zZ%P%(CaQCBU!F~NbENEm%O24GijIxGE$SPFIl3Ws!h+0ybV!8wmR20VxT;7bQ;c!| zg>f)PsT#Mbs9#36@Cwgcza=eXu{z8J2VWIL{7$S&2Gn--#+{SC-z^g1Rc{+jMHhsX zwev(8(5|3Lrf`8R*6d_4fgLjOtO?Ro>Wh*c2W`cz%^|jFHO272vKqZ?LQ8`U*TJTi zrzy9Mnfi8x4Nm$FCw^BZfBf?JU-;>2r!@j~Zc+n(f(!n-#g@?rdg}jNL=G1;QmIOJ zL#RSDIOT&A&YNF4G(b9Y_)4TYswyBo8kV-iVt%3)rmE z%ZI6LouR+dpV>Q${>^g50ZYA0>Nun8nO;6^s$Z#oh9w`1&yuDhJb+98ZHn?w3_^&- zr(3jY6I3ryyh035ldEnpO|qi0b6e)PcbKNTKAS5f+ONIQIQ~**0JVUuMCR%8tVFH^ zS4!NNRsnPvP=^08<9g3GsW`RO%zla1VVv}?l+FIe2_ch;k}8n_h?ZqrMC-&<&kByG zU4dTptkWYU{ZQnk(@zw$3riUS{QcA`f`0QG40R2)*~0Puu0o zIVBg%eB(OQ5V8Bo+I+;5>letVr_vJmb=uXg$qk9XlGdp}X6C_VLW7CX8sllfH>T^z zu5ap~Yq)SThho^K>vH<_HkQOv8?3M6Z5DoCTWGi#*ybvID%zduc+9Q>`D%XJ|J{2Z z_-yv&wOx#Jho6kuIrmnB|0+`Lek!TSWyDA8-ZUmA2j$kpSpAA7gVW>Wf_vJ&#g~YX zlgMVphqaN)9Jr3)(rl`EoDkKt<83sys!AH0z-&T1fnV#rR1U0+EIM27qSB%Nsfr?M z>G<8tG@OLz%u254V#hb;iZQjn%B_ecddI?Y5xIQI@uc<@m>&DBb0qPZ_|_w%)qlv@ zb?oVpBU1#dF+=~&wd$AYiKYx;8jn9@i7%kgG&eJcx6#$??Xsf0%0R2r+HJmC8EJ+c zFBgkosa%bKI!As;?*NW z`XRC47xdc}b82!UdSb=?(RrknbSkrMjI6iOn>Q|=76qplJLMTcXcaHE$+=H}M|vI@ z)Cwy2j@^5^YOI1QnxU!h;?cxY=Zet_GU)$>3)md--%V``*G&G(U@vzisS7&}G82{p{Blcuwbo*DU)#Fr{e9G%9 zqB!wJMOc9va{yWcO#ojnolkVI(-#_D3V zW%wv3IC2dF{g6vkqfwQ;0{Gz$DaO=3?zehOMU0q=6&N1{Z(Ltu^nkUf&GY-P2!0w5 z2FPbzzpqE(vb=7U%g2mnTjYhnCUkHbo8{XHrmMF-^sK+{S6RaINQVx3(W88>Ol?*| zJJ@Pr8*{~RG!IzCRW`ecf>~>(p2e84(O&A+I6lGUy`<37nIaeBAg&H(=AZDPI`zL2 ze7{0dVwCf7eX2_@S^fqUzaI(SiC5P-ICB}@Yw4FYnXd8=Tx_`!d|{^Q?5^`_5tBD4 z=72~!l^MvGHB$+xca&Tz_CGGEUYq}^@;oByDnUzAV@*ccbM16*k{C2u@{98xLXL0= zvU6>oM3jKZ_Kzswcoy>YsU_E6RFKJ0Ky1+ZM#M?L?D(9Jd3=y%F>OjK)e(-wnk0yi zwxc!mX@X}{8NWsE_gqYG*<@oWmH+GQC!pwIN=l}y?-S+iF4gi`oLG^@9yQI|`)7 z^Z@1@Gj~;Ub-E9s8V^vFQc&AE-Tt=CO13wMWwilnhl;3nJ1uIR#tDa&G?p*k!9Q<& zZXe=NcvbACoEwVW@0fI+N{$v7;a})kerNDws+)U=$n*DNo!o%r@LSlyaqPl&P8lm! z%?a47qIB{0L0^uDpGS=F#i6-OSd0?(*N*_2k(!{h#DWVM7$vQn6Qj1B2#;TN-|=|` z2!*rP=W1!IK9meh@S>bW+gh_)Y>fihj~Y5JTz^>zV+f+LIZpb+Jz$MDcB?J#wV_zd z7bM5l>rxk(3E_1FMS3QeE=#&!0}nI=;00Y+KxrNDb3>}Vnc*h2&!KK3nya`}=pf+= zcZ`t+7s{g)mt)uG>`D#TjD$N9ijB>u^@p#~^FUNV+cfcRLv`)w#y^aJD(xVEdriK_ zDyJo08-7FAI|3FzD7CmhAjB#ozc#Ats(HlPVLWsA@Ecx;TL*H!QLjA^Z4Qs@@LNae zvsU9VFBSp^k=WGGO`4CreISbZIpild~}C+p{6P?Xx4)NVpc6Toq-Fw*@d^ z%E1qN#?N1J`!Wh}DHz)#l5V0J5|Vn~cWik~6qB+Rns>cFnYwT%yUTZtN{AvQaxJhW zrOk>Mb2}{Awr6yuKGe865kr)xvAiC_?K{| z=ud-*o^_``u+@{UH;nOClyexUsYBZ=IwljORnP{@&x8yyvU;Rc`hGF> zoofj;ks06M#azO)zbX|#RoB_XiM|PvMGz+(#3t&J8ub2yWk+M6W zep3E^sl-kTegfW6)%aQDJwbUN<2+p)n)Y6Mo~2-ULx|)vWfb!W;%DqQQF?Uti)9&Yh~@Welj z+-^T!Z{}KiQOrVQqs>o9tk*z9a+6!Eh~$&(N@xC@@dM?ci=@xi__M!ncB*~C5cBCw zo08c?JY&xO>Qa>m!L4;@3FGz9?e-9lRR666AmCE_9^#))Rpg@GG8N;-LCGVJ&D(Hl;0{gb zX1!GNJz9Fq)@JS79J5a0kpoh|CF4}@Y9iW#9Y=HQiTJ=Op$W~}CA+2ggnIlfB-d($ zI%Pg*t4p2lO;nf1tp`Z4r@`(9=PW$bHD}vYOubhAwOWlOZ?eR@C){lXsoV+Cn3t23 zGFD3cs=ZvbeW*FxB8!~ z23B|}m0=Ech;RY$oqaG?(5I-g1d8)70h-<~IhyGLaX@O=++qX0^v@s|`p;`dOQ93# z*)gSs?u+Si{8>$!Z=!Zm8!G3pa>t58hrv4m&RE^UL!)QieL+z}>mJvM>J;6@=%*1~cINlA@lFD0D-1D8D~*0&a;CzX6^# zfs(>xcgY~njVq;rjIstl!n~7nCVVn`DBCWAE4vFCVaa}YEc6CrOw}P(@r1OK*|W@| zP$Js{{}MS&URxy=mi@rZETP?vo&vFNe+ogJ=<95nrTqENa!OXX)g3Z3m zO~RQmj2YE#4Dnx7#Tk4Q6Igk$&BCdbs-}e`nm*yoVb?d$vjbISn~MXYX91t;ZYm2Z zpl>0sF2<5tE^b**mCl!Lzp6v)sBZZDxJG$Hz7)KC9&rUOmkAtVewslU>f8Aos%96S zR9MC3{2=+~*_pTN0SEA!f#Qqj=5*Pmn*0QOt2Pp-3we6usswMMzuoM(Jm;O8tRLUj zZQXb0k9m2!X}`h~#=#e-Y>C`Zzbh3S4%sA`}0 zvOf;iQQ)&ChY36A=n);DI6rXPn(fjk%j~sR_r5?u>rpzKIgmiDVyV5(H6XZz26>d# zS4+)$F&7v&!!iHkN5?mxON7z%eF*8~4k5EQjRaVU=6!5^%Jsv&cL_8GH1}?zCR?O;q{Nx=`>}U}8*;{zuca&^qDdq0vcFFMOogn{%?RwH86w?CZOHu1#2_P< z!e z*quIo&S6(jZv(34Jk zxGFZOmgKnAH>YWACi}oQ+>3TTTN;DL3@#@_nyfYkVLW%{=(Ty@`KVDZvw5UPGbbSl z2*rk_e5xX?n96QZbj~y!d$8F$jvYIl%M>YI!+~)E3wUwTD3tr2M(X%xFaE!=4=nyf(TZgWsG&Sax7L^U6XRgk%2#RaNStAo}A%wi|{yrIClGp$ww?#7J zI&k$cr@`n zc(k-Z#g_bXjLUOLzgTIRqa51?w)`v96y9=JTxA_5H4MBB2q%KmB}U7!`n@Z7o5>-b zHG~}&c`HF?tIp=x}Vecg}; z(q`yfI79I;s5k{Fk0*J7M zI3J%E^h-Bl)@yNV(h-95jk;>uQ$$6L85F~z->ii^6?og0=`EwXfrj)@QvbvRp- zuRGZhB&m$`-c|WQ{V`Tbo4v+OCMc%dj>1NKRcNpg-hTF*;Us}^8M^B=>CYLg+#|AT zDV}bQGeI@J@Kc8M<>W-agvC>dBoCi`n6cA|Frau9nUsHrP-2E;vh2gZ0l(m6^Rvt9 zWQ&1U?)`DA@2-y>*@%(v#n^+=&ddble-|v&7O$ocq)cYdv&yapiCP>MRkXWvlI>R; zoBbe6eu^TxC=uL{mDPd@UMe(YnH3{^N8xkq=6HQR$Z4@2N~@(CFPPO+)H>B#0t*jl zZ3yxc9)8&zD1WfEq0!<TQHM?GP6|)N^J&^KXWjnZheW+!dCa((*Un*D*k+AMTRe z`K`E>CjH)%sQ0`b`{(|GGm_E$u}jtV7@F|qO62#K!{EK=$WR8BW&~~XpiRx-&wNcp zp4=k@R-0X&63WdsOOpg{Gid+Fyc{tgx__k?a^rTe4i)IP`u$0Z=Yml3v)TR|Ke^gE zpsbra$27p~^&rvP-+)?nERf$aLUn1bk6YD`tW5(JPiNvn+oTi>k!%!pq~3W_hRH@Y z;k$;Xqs)M(GLXdkSZir{{+ePwKcFZ%A*Oouz4GH&l}kz*eZv@VoePB9EWz5&5am0X z3*A)1gGnK$=3b0rN}UH`#FT5=P5-R{aQZmU@`PhhvBoE-MYw7kx~%s*Y07uc3OZZ- z%7%EAR|H7j)_PsUo<$mAn44TC-h`g$VNU~?aNo}?Mu@WkO9uE(8akC+s?qxefi02@ zA~IO=UWM#E=KL2Nem{!{kfI}%TE-CoulI(o1a*i}IhNuzr~aibR2Q3mZ2({mD^H6< zupML=F}0$6Yc&q{Y@;%vT+jkrz?RzHvMcn7jfm)WF$$5Ezcwv zumim&3Tu@`YU4J)-D*~lGV6C_E+y4lFtPxR(7H?1+z8;>j)@7Igr43b}t}Q$8zHH73#P^l%4EbWHoCb z+!@V0tfoSuj*GfE3=7N6>HN2PuckgXWx}w7GFz(B=y1!@R1q@)xjV&ps;`O3q5-bt z)Plzn`mXZ;w0%wYbRgV9(Mw5qT94lf;q-Sg|p_b;$ijZ@`Vqu-FbE5WZ z-D9j^wq7M84!4${%n}ItQz@tO{)fwr@FpXe5}P42C~cbstY2zpPC%7nbwh6U(kUe? zh;uc{wRdx@_D$NsQ5C|kur!b_!t44Lv9C02t~Pg18>kvU>X%FR=~$s=X#3_-o4V5q z_aU(<%)0&f;}LqaUUC!Nv?k#6fFBx!7;qI`YARSHM_H{aTBH1d0=@-biLv}x!E9`W570QzRs4$Sy4NYBEx*T(%mQ#plp#JR^RPv9-ni=8ZP z^9w!Z2Y)2&juk?1Kas^T^EZN1Xi^1Vs-r5NkH*=0n=Bd4c}ERDA9xUOFQ@CwHGDnv zG?|@RZJY8<0(w^xRM*J%swKJfc%G4Bglx1?gjt0)5Aa?HxX+Wh@jF~>}kA-;wob|)E$)KYmjeP@i4R6cLNiKV)aUj(5dp#q*bt} zv)rtV?7I8@>Zyxn)Ysy14oZi$7N3o%{zqXfv;yZD>M_IBXrZl8s5NVTDtY@S=@w@( zMZlKzSxFVC6vEsF^cdK6DzamyAO6zYa{m|n3|VJ-$O3G=qL21BL!?x|51A?0?5!tZ zQ`LV$=;Tacu4e3z@~-qSn=dJ7I=-L%7(a3~zp~m{_1BvZYKLpxl*@puPYC`BtJJYC zk+Iyo-I3Y~oJC;kWT2|2!34rcb8AE&i8Vt%gSV0+n7k?q;K>&Eb_&yAZg!P^oRl?u zr+i0E$8UjsJ-0G%xHx!dlX~hBJp2ATlK!0Et|^}18J2g4HRV`S`Y)Jq~@-2=wzt9I&zlu2LeZWJU|WI+%T61^`Cq@8Fh zq-wClPa9l{cc%R4WZ8)P>1|w`tab=0A6Jn9F`j^jsh|8vUkUh@zW=rU88rn(=HrCh zF_@C7x!f?7VwVSX}6#U_4X3wbn~1ODAI@tEJbjd41O1-s9_Pd{6*nfcCC?~ zh3#Z!+vx2yC}-Aa#SA>Y5jrZ4yL1h&2H=$2dJGIVDu7JffZ(AExVx(CSSrAJfTH-0 zQKUhs7Yz+VQb(viLLGH-z~Ayo6d=s{qtv67mdU+FkH?&wUO10R)oVqveF$7u_8h0t z`UNt;>LyIyVdMk~=>$Qn;d(Wkr@M@gq$FWIS~Y|lH;Q4VNoo~y_j ziFx4+M;tLoKZ{sj0K?)aBjm^NrnS-I&0w-$!?R^rsX1I){L#z$`F46vTyug??>{w9 zHEmfwqYF!ppAN1+7hrSY&nC}cm@D)PQiqV>y)$`7ZAZV09_Ei3T7v{z8utkU=f{Ga zoB`ch3Wt#AtM{=Fge>LYuQ!N3BDC7qJ(lfc-aA=IP7eQN`=?52{(`wgUZpY1lr5SYgsipT9aL?JLJEn}FCO@zNM$B7f!mB$ zMoJI<%7~PA3z)@JFR`T(B+E=ba6IBmjm42g$0v4BOQm6j$%$yMB@!QvfLhOS<8;rA z@=BfiK{#Ls?~HKGRth*Sk6}YL@*!IeK@){iWc2A!_+^{El`7Bzo{OS;d#?VBxwq9} zl4;;JwvVo!Xr&XSeJ&WchH*7$0Idq`@sL<-h;cYj*zQz?PAX1aN@7@Pjp^1Ck>7;M ziXsZ1Em2=HAI%VEgfw4-J?ap|k3O2HKN#B^q1kGSXzV>KB#X9^rM}SFoZqc>$YJB= z18MWdfm$V6nK$D=Y^6f}#?J(Y2ht4r>2usW|3nL9=J^Z>It{*M^ynKov}8kB38u$h z+5z=kjql5G>9?B>ac+|5O2*kAuH8Ga{D@wtSPJToQT{Diq?28LQ&}MiV{vDN;8tPu zd`aZc7r3AN7wuz2#owU^|bIP#=pK z-c)?Fm$Y+UU0!EM>dFvC)={^*MvS>O!6w}UgJ?!h`2#j*DE;G88;lsT&*mrZlFyZ* zTJi&SKH+Y*5u$qil8nQ;KBNgyy&}y|03g_fGE(Ii<5ra|a}8kZ>i&eZKw!5oR?mek z_PB!_rcUc1L!rIjvmu1`5<{*&Nt5J|zqn-PcGMnh%vOT%p;J23S4H?hCB3#P79zC) zFb@u_K#~y2R_$o=wMziLFH=*L>dU*ZUy8l%KQcyPc?*jC<~Zvdb|-fxpY@1I6gL|d zhF4`Opmv0;2BIo~!|$$`sdveGSY^CB@JfUF_Ga((mC?)Qg|mkJb1NZq8yp3Gv7jb= z)90oc7M4G31aG=%47-?QbV9>5ku|XkjKlP)Hzvpv*(|-bSCW!*0>Ig78epfNVLtVV zzPK)72W$#W6cR1z58u(OVG;clzUAACt3{yCRehY%=;m7p-~hbC;})^S^^QPTjuBqs zK`6=Smz~d|W_-T*H$pko!5^m$q>AF+u;-tzF=}6f3n6+^b$ofZsfC%<=~vy*E<<_w9clicX8?-)@B-QgoUged1(E}%iUp;@ z|Am+j;q}(Lu6T|;mwt713!3yYd;XN}NMf$DOra<~Ni0?ic<@wHRewiT~@4=st z@&qLru`ZkR#f`p1jJVL=OgU*RTw!LZqZVh;S+HWY%1Tx8 zm&M=CLv!wy)c3F~h>rC&P-H5`@Rl#HI(LK7>RY7oRnMRCxt&h1sOrll0b@JtH8a(9 z!LgiPxG&P|Tv)ceYv_|!e-xi=Bf@6c1L2EE zmNjaFN;L?~A(XxQ*8R!NUP@(ktIO|sIg_GKGqOmf!FI8F7XUc&A2ZtTt1+&g7GaJL>?&k=|&AH(cpq?`H)xE6I>fzVs(uGQcuh z!Zs->$pTQ4e&7-Xr80T%Gbwc%@f9VL%k|d`9e^*X2;cW#uj978jUYV;6c#J(T+!AA zU?`TwtgInYEtnP4rp4sXW}T^74*xP2YBzb-fUveW>o0*#zb9o5eQmoT+50E5>aG?E zg)nH;N26bS-fae5R76q!1^A?-_RG-=VvtthN_%Uot2;@ZSeslGknAQuI z3v5vDZb_bl-D~Pw`P;TU5zRLK$*YjYR*-s{2w{_i z?btlKST;&klr>nD@mM>Rf{)gV=6*i_Vh#I;UnvXX1I2=5RalY+Ne=poF9}^H@|eAf z`kuH&+OdjT^DfVm#MNK6IG)h(mhR?MyJ{7_gFHP1+se<%`IB$+1}MIwo-vHXp_rAZZg@iqbggt2tDUd;5b(7>elzo3I$1b8 zi5{!!XhdSWe|J)kow>LJ+MZGjnp0{E7fWvLTTE0$Mymo7UPqX}ZP*-cMC5e82$=YgmwG@{ z)L&+iaisGZetFQJ`FROhoOUgh0wd|>^n-XgLh*ZK#hF3=Ip z!GX!-vPK^eb0Kj`%(%+OZmSHRLxh|djuJx;gbeNl);Ks3K0UlfhPkEV(HTq_q4~-E z?H)PIh;6))djQ@lvQ_J;-3xW%HiR{1@t+StZ0@XnlGdsD6+uK%-eIat!K#)!2g+4o zs5uFQY6jCyLW&+d*!cN#)MA!H6Ou`Uz|rDGh=;1VjX9H2#6UAGM)doQO6-wZa>RJ= z;iqUETy2jt8<4h(WsN>#Z=$8OIB}qXRh`s44wB?}8emy4;xog}b(YE|C$&0U{F{Y! zRC()&AZ~gqqEsiN)WfN&W7~@!xZIh*|pNZ1SBoINU8JX#t~mGAq-z?TKxN zt)V?Re7svGn|;B+Uc+^P2jUJdaRy6 zwO2ruz$5z^%4zg&ln)q^x9rBr9YyyB7)>=&Trk}*@VM%#n(L|YrKrTE>fE$3X|6(A zCvvpni~Yp~@ACR1KO1rl7q_R{w+<=YHfOWKq9Q#sC2-$5JHj?cg=NQHqJ>Eo&sNdz zt#t1(=SP`|GSi=R3`08!=@gt}H(uu@7d75Ypw-!~*9HQjcJWNJI zE%9lqW6RY5a3lib&S?Vs@g7>&$h+EJp59ha)uTDZZFB9K7L&72*^S$J?_=G$F2|G{ zH90n+pK9305MHmBmL5f=&j1}EC5<<1eEvr@T-(z6@+VbEqS3SgrbVQEg};f=gt)m& zHB<>`t>q-dJjKN6bX_(uhGTQjesd~?b~eeWjhcmxsOtyRllC3EL0^77xo-WdLm;`< z6{GETq&Cn;RPNKU_&A`X-G31hRO&2{^<;^3YIBP1^*ZxK3YF2J$Dn+QI}|~!-^ulX zbiN!<2?8Ns8|$+#5jdcnDAtQm)b0J~`y@qQsPy?404)9e*4B9tE4Rgul2YW~^P1lM z=VX}Q{oBev7Uag+T@k)tz_uV02~_%tcNq1DmFjms$txY*Z|0OFi*~A75wJso?j&wbngCD#Rc~?iS-Q@6aU-RM)!2woU{(_QjrPqg1EHqraqZ01*Q1%opT<(+tN~OiMufX9pFtph zdl2W0N>#G8Bkr$DFJE?u>j`J4Eurs{aOK@U;YVRKeD688gOOEJ82^^}zL~yAFShwho~Ytt*8z3zP<8rq@|w@2wQrI?VO7CkFqb1a zq>oEKTzC4&_wp~SRXIZ&WOwj$(4 zM&7C1obJqa<*2w!Lrbky@3?Zo<^XwqxedK3)=u;`dbi&2JDSH`thrQ;2ctsMQ4Fun z2A^M}n6Sgvr)R%iS=(z+tZ7ZXG|53Z2d!T_M zVz_l&xFlj4hZtj7>i;Z>|DQ+!?jnEh41})St?A36{%M8&SCoTe1}U-Fp51D#GW|dQ z&HqVq|9@Toueyj3U`(g1sj%{Yu_L+Ja10&@O8L|Oi*X;OJ^lOH|NQEfLQ(dAvjF~Yzx{vG%>O@_ zCbyT;6Fgsd%H_7A)Sw(=4yYzN!SUqH)^d)w$%{;T`FjAPZ7=M|Z(AOnY*#hnU-~1y z%1YjUuVj2;Vh{hX$Apj)X6vF4f^!>$ysG+Yb%kdAuSP#XdA*^z2 zyS;l^o7WBN3PIEgo0#xrjDI?2{}Eb2I)o5$fByX0uD3B7dRc~xmUo|z%&bL@Ak*fB z0ZCZw&&QIu$e zskB|fcsf<%;z%}*Y<;9wi>3A`vOCpi;jf<0j78HQe zP(uIns6C$G$~ta;O@eXK?3qc zX=$m!{L@+d!t=c5d+d?l*ANz9>`v&Zb_#1M^2f;3nU5}mGaA1eX7k>~I=R0{1~J}^ zRiKi)zg;|kxtD*1UPhPIbOjUe6yQK!QVou){NVUML--%w=nEWyoSHgt%}T`CnH35O zN={1)uIP!+H_QQrhnsu%>MCkHiHgYA6^@RM&NPW}X3L%dx>A(Y$|~e{jj)+qTCmO0 zXk}rL88Vt~K2%1sEQpQb%GC&Tm0;HXLY?QanpJ(h5{FEks>#AwDCg9=AHa?%cOedW zbuK$&OqD@HqV3Xaco-w}oM%d$0t31Pw#XW<**H?rt187vPeu!wl;bhrP#T({aYW!! zvlI59eaWTXAwY(vEEN$qKwV~y%tj*`(CEdqXGx7Yw@i^NGts-G&SCylfzGh93|!md z+dvvSk{weNj?q(1yuXsR#eBM9m61TtO$g^4uAsW!XoBjRAsA@JM=HeBqNmq@`}CD( zMN-qZ+GknoI>}gxhfrnR{l%!!2h?d&ZUK2m+KwjbS6gupdDqX%->SUd1Qae zqt(;9xI|3 z;Z=D9SZ4%rHmo<&Xmrd7S8QT{NOWH=+>?J*+9C**LKgG|Z?zOBRgJQ-vgM60YHEii z+SAQiBxLYMV)TU9wY*i`V}_WJ2!2=lU8AW1)#1j_TB2go;(Fn(w3 zTQHasNNQTemkVA<2ZJ@7v&6l5)%WyUaZ=<;0sKnEAmY3-`Dh#s0-@YC-jj^>u7Q4+*bkQ3~=3#fE zfL}TnIp}`hAY5GcJ`2k+$fMb!CnlBRCm!Ni%40PEJ0-6S)Ae603EtxixJnN=wjP$s zw)pJ^)pRiGAb-G^Vz|~)L)31Xg_sJ4-ogKyG``t-WP3o=j^eL^2_}p1JF7Lo#1_

-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%0D=VkAz5J-CT~FS`OOwW?JxP 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 MIn&AxK&B#q%BQyt^9Mm;?p>DPv~0V;+Nskt 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 literal 0 HcmV?d00001 diff --git a/examples/cc26xx/cc26xx-web-demo/img/sensor-readings-config.png b/examples/cc26xx/cc26xx-web-demo/img/sensor-readings-config.png new file mode 100644 index 0000000000000000000000000000000000000000..79c8e61e3f410f2500dda37b87466863464e2b8c GIT binary patch literal 33000 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 literal 0 HcmV?d00001 diff --git a/examples/cc26xx/cc26xx-web-demo/img/well-known-core.png b/examples/cc26xx/cc26xx-web-demo/img/well-known-core.png new file mode 100644 index 0000000000000000000000000000000000000000..93552e502e404a2eb261e138835223a881ae0a02 GIT binary patch literal 9102 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 z +#include +/*---------------------------------------------------------------------------*/ +/* + * IBM server: messaging.quickstart.internetofthings.ibmcloud.com + * (184.172.124.189) mapped in an NAT64 (prefix 64:ff9b::/96) IPv6 address + * Note: If not able to connect; lookup the IP address again as it may change. + * + * If the node has a broker IP setting saved on flash, this value here will + * get ignored + */ +static const char *broker_ip = "0064:ff9b:0000:0000:0000:0000:b8ac:7cbd"; +/*---------------------------------------------------------------------------*/ +/* + * A timeout used when waiting for something to happen (e.g. to connect or to + * disconnect) + */ +#define STATE_MACHINE_PERIODIC (CLOCK_SECOND >> 1) +/*---------------------------------------------------------------------------*/ +/* Provide visible feedback via LEDS during various states */ +/* When connecting to broker */ +#define CONNECTING_LED_DURATION (CLOCK_SECOND >> 3) + +/* Each time we try to publish */ +#define PUBLISH_LED_ON_DURATION (CLOCK_SECOND) +/*---------------------------------------------------------------------------*/ +/* Connections and reconnections */ +#define RETRY_FOREVER 0xFF +#define RECONNECT_INTERVAL (CLOCK_SECOND * 2) + +/* + * Number of times to try reconnecting to the broker. + * Can be a limited number (e.g. 3, 10 etc) or can be set to RETRY_FOREVER + */ +#define RECONNECT_ATTEMPTS RETRY_FOREVER +#define CONNECTION_STABLE_TIME (CLOCK_SECOND * 5) +#define NEW_CONFIG_WAIT_INTERVAL (CLOCK_SECOND * 20) +static struct timer connection_life; +static uint8_t connect_attempt; +/*---------------------------------------------------------------------------*/ +/* Various states */ +static uint8_t state; +#define MQTT_CLIENT_STATE_INIT 0 +#define MQTT_CLIENT_STATE_REGISTERED 1 +#define MQTT_CLIENT_STATE_CONNECTING 2 +#define MQTT_CLIENT_STATE_CONNECTED 3 +#define MQTT_CLIENT_STATE_PUBLISHING 4 +#define MQTT_CLIENT_STATE_DISCONNECTED 5 +#define MQTT_CLIENT_STATE_NEWCONFIG 6 +#define MQTT_CLIENT_STATE_CONFIG_ERROR 0xFE +#define MQTT_CLIENT_STATE_ERROR 0xFF +/*---------------------------------------------------------------------------*/ +/* Maximum TCP segment size for outgoing segments of our socket */ +#define MQTT_CLIENT_MAX_SEGMENT_SIZE 32 +/*---------------------------------------------------------------------------*/ +/* + * Buffers for Client ID and Topic. + * Make sure they are large enough to hold the entire respective string + * + * d:quickstart:status:EUI64 is 32 bytes long + * iot-2/evt/status/fmt/json is 25 bytes + * We also need space for the null termination + */ +#define BUFFER_SIZE 64 +static char client_id[BUFFER_SIZE]; +static char pub_topic[BUFFER_SIZE]; +static char sub_topic[BUFFER_SIZE]; +/*---------------------------------------------------------------------------*/ +/* + * The main MQTT buffers. + * We will need to increase if we start publishing more data. + */ +#define APP_BUFFER_SIZE 512 +static struct mqtt_connection conn; +static char app_buffer[APP_BUFFER_SIZE]; +/*---------------------------------------------------------------------------*/ +#define QUICKSTART "quickstart" +/*---------------------------------------------------------------------------*/ +static struct mqtt_message *msg_ptr = 0; +static struct etimer publish_periodic_timer; +static struct ctimer ct; +static char *buf_ptr; +static uint16_t seq_nr_value = 0; +/*---------------------------------------------------------------------------*/ +static uip_ip6addr_t def_route; +/*---------------------------------------------------------------------------*/ +/* Parent RSSI functionality */ +static struct uip_icmp6_echo_reply_notification echo_reply_notification; +static struct etimer echo_request_timer; +int def_rt_rssi = 0; +/*---------------------------------------------------------------------------*/ +const static cc26xx_web_demo_sensor_reading_t *reading; +/*---------------------------------------------------------------------------*/ +mqtt_client_config_t *conf; +/*---------------------------------------------------------------------------*/ +PROCESS(mqtt_client_process, "CC26XX MQTT Client"); +/*---------------------------------------------------------------------------*/ +static void +publish_led_off(void *d) +{ + leds_off(CC26XX_WEB_DEMO_STATUS_LED); +} +/*---------------------------------------------------------------------------*/ +static void +new_net_config(void) +{ + /* + * We got a new configuration over the net. + * + * Disconnect from the current broker and stop the periodic timer. + * + * When the source of the new configuration is done, we will get notified + * via an event. + */ + if(state == MQTT_CLIENT_STATE_NEWCONFIG) { + return; + } + + state = MQTT_CLIENT_STATE_NEWCONFIG; + + etimer_stop(&publish_periodic_timer); + mqtt_disconnect(&conn); +} +/*---------------------------------------------------------------------------*/ +static int +org_id_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + if(key_len != strlen("org_id") || + strncasecmp(key, "org_id", strlen("org_id")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + if(val_len > MQTT_CLIENT_CONFIG_ORG_ID_LEN) { + /* Ours but bad value */ + rv = HTTPD_SIMPLE_POST_HANDLER_ERROR; + } else { + memset(conf->org_id, 0, MQTT_CLIENT_CONFIG_ORG_ID_LEN); + memcpy(conf->org_id, val, val_len); + + rv = HTTPD_SIMPLE_POST_HANDLER_OK; + } + + new_net_config(); + + return rv; +} +/*---------------------------------------------------------------------------*/ +static int +type_id_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + if(key_len != strlen("type_id") || + strncasecmp(key, "type_id", strlen("type_id")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + if(val_len > MQTT_CLIENT_CONFIG_TYPE_ID_LEN) { + /* Ours but bad value */ + rv = HTTPD_SIMPLE_POST_HANDLER_ERROR; + } else { + memset(conf->type_id, 0, MQTT_CLIENT_CONFIG_TYPE_ID_LEN); + memcpy(conf->type_id, val, val_len); + + rv = HTTPD_SIMPLE_POST_HANDLER_OK; + } + + new_net_config(); + + return rv; +} +/*---------------------------------------------------------------------------*/ +static int +event_type_id_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + if(key_len != strlen("event_type_id") || + strncasecmp(key, "event_type_id", strlen("event_type_id")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + if(val_len > MQTT_CLIENT_CONFIG_EVENT_TYPE_ID_LEN) { + /* Ours but bad value */ + rv = HTTPD_SIMPLE_POST_HANDLER_ERROR; + } else { + memset(conf->event_type_id, 0, MQTT_CLIENT_CONFIG_EVENT_TYPE_ID_LEN); + memcpy(conf->event_type_id, val, val_len); + + rv = HTTPD_SIMPLE_POST_HANDLER_OK; + } + + new_net_config(); + + return rv; +} +/*---------------------------------------------------------------------------*/ +static int +cmd_type_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + if(key_len != strlen("cmd_type") || + strncasecmp(key, "cmd_type", strlen("cmd_type")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + if(val_len > MQTT_CLIENT_CONFIG_CMD_TYPE_LEN) { + /* Ours but bad value */ + rv = HTTPD_SIMPLE_POST_HANDLER_ERROR; + } else { + memset(conf->cmd_type, 0, MQTT_CLIENT_CONFIG_CMD_TYPE_LEN); + memcpy(conf->cmd_type, val, val_len); + + rv = HTTPD_SIMPLE_POST_HANDLER_OK; + } + + new_net_config(); + + return rv; +} +/*---------------------------------------------------------------------------*/ +static int +auth_token_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + if(key_len != strlen("auth_token") || + strncasecmp(key, "auth_token", strlen("auth_token")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + if(val_len > MQTT_CLIENT_CONFIG_AUTH_TOKEN_LEN) { + /* Ours but bad value */ + rv = HTTPD_SIMPLE_POST_HANDLER_ERROR; + } else { + memset(conf->auth_token, 0, MQTT_CLIENT_CONFIG_AUTH_TOKEN_LEN); + memcpy(conf->auth_token, val, val_len); + + rv = HTTPD_SIMPLE_POST_HANDLER_OK; + } + + new_net_config(); + + return rv; +} +/*---------------------------------------------------------------------------*/ +static int +interval_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = 0; + + if(key_len != strlen("interval") || + strncasecmp(key, "interval", strlen("interval")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + rv = atoi(val); + + if(rv < MQTT_CLIENT_PUBLISH_INTERVAL_MIN || + rv > MQTT_CLIENT_PUBLISH_INTERVAL_MAX) { + return HTTPD_SIMPLE_POST_HANDLER_ERROR; + } + + conf->pub_interval = rv * CLOCK_SECOND; + + return HTTPD_SIMPLE_POST_HANDLER_OK; +} +/*---------------------------------------------------------------------------*/ +static int +port_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = 0; + + if(key_len != strlen("broker_port") || + strncasecmp(key, "broker_port", strlen("broker_port")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + rv = atoi(val); + + if(rv <= 65535 && rv > 0) { + conf->broker_port = rv; + } else { + return HTTPD_SIMPLE_POST_HANDLER_ERROR; + } + + new_net_config(); + + return HTTPD_SIMPLE_POST_HANDLER_OK; +} +/*---------------------------------------------------------------------------*/ +static int +ip_addr_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + + if(key_len != strlen("broker_ip") || + strncasecmp(key, "broker_ip", strlen("broker_ip")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + if(val_len > MQTT_CLIENT_CONFIG_IP_ADDR_STR_LEN) { + /* Ours but bad value */ + rv = HTTPD_SIMPLE_POST_HANDLER_ERROR; + } else { + memset(conf->broker_ip, 0, MQTT_CLIENT_CONFIG_IP_ADDR_STR_LEN); + memcpy(conf->broker_ip, val, val_len); + + rv = HTTPD_SIMPLE_POST_HANDLER_OK; + } + + new_net_config(); + + return rv; +} +/*---------------------------------------------------------------------------*/ +static int +reconnect_post_handler(char *key, int key_len, char *val, int val_len) +{ + if(key_len != strlen("reconnect") || + strncasecmp(key, "reconnect", strlen("reconnect")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + new_net_config(); + + return HTTPD_SIMPLE_POST_HANDLER_OK; +} +/*---------------------------------------------------------------------------*/ +static int +ping_interval_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = 0; + + if(key_len != strlen("ping_interval") || + strncasecmp(key, "ping_interval", strlen("ping_interval")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + rv = atoi(val); + + if(rv < MQTT_CLIENT_RSSI_MEASURE_INTERVAL_MIN || + rv > MQTT_CLIENT_RSSI_MEASURE_INTERVAL_MAX) { + return HTTPD_SIMPLE_POST_HANDLER_ERROR; + } + + conf->def_rt_ping_interval = rv * CLOCK_SECOND; + + return HTTPD_SIMPLE_POST_HANDLER_OK; +} +/*---------------------------------------------------------------------------*/ +HTTPD_SIMPLE_POST_HANDLER(org_id, org_id_post_handler); +HTTPD_SIMPLE_POST_HANDLER(type_id, type_id_post_handler); +HTTPD_SIMPLE_POST_HANDLER(event_type_id, event_type_id_post_handler); +HTTPD_SIMPLE_POST_HANDLER(cmd_type, cmd_type_post_handler); +HTTPD_SIMPLE_POST_HANDLER(auth_token, auth_token_post_handler); +HTTPD_SIMPLE_POST_HANDLER(ip_addr, ip_addr_post_handler); +HTTPD_SIMPLE_POST_HANDLER(port, port_post_handler); +HTTPD_SIMPLE_POST_HANDLER(interval, interval_post_handler); +HTTPD_SIMPLE_POST_HANDLER(reconnect, reconnect_post_handler); +HTTPD_SIMPLE_POST_HANDLER(ping_interval, ping_interval_post_handler); +/*---------------------------------------------------------------------------*/ +static void +echo_reply_handler(uip_ipaddr_t *source, uint8_t ttl, uint8_t *data, + uint16_t datalen) +{ + if(uip_ip6addr_cmp(source, uip_ds6_defrt_choose())) { + def_rt_rssi = sicslowpan_get_last_rssi(); + } +} +/*---------------------------------------------------------------------------*/ +static void +pub_handler(const char *topic, uint16_t topic_len, const uint8_t *chunk, + uint16_t chunk_len) +{ + DBG("Pub Handler: topic='%s' (len=%u), chunk_len=%u\n", topic, topic_len, + chunk_len); + + /* If we don't like the length, ignore */ + if(topic_len != 23 || chunk_len != 1) { + printf("Incorrect topic or chunk len. Ignored\n"); + return; + } + + /* If the format != json, ignore */ + if(strncmp(&topic[topic_len - 4], "json", 4) != 0) { + printf("Incorrect format\n"); + } + + if(strncmp(&topic[10], "leds", 4) == 0) { + if(chunk[0] == '1') { + leds_on(LEDS_RED); + } else if(chunk[0] == '0') { + leds_off(LEDS_RED); + } + return; + } + +#if BOARD_SENSORTAG + if(strncmp(&topic[10], "buzz", 4) == 0) { + if(chunk[0] == '1') { + buzzer_start(1000); + } else if(chunk[0] == '0') { + buzzer_stop(); + } + return; + } +#endif +} +/*---------------------------------------------------------------------------*/ +static void +mqtt_event(struct mqtt_connection *m, mqtt_event_t event, void *data) +{ + switch(event) { + case MQTT_EVENT_CONNECTED: { + DBG("APP - Application has a MQTT connection\n"); + timer_set(&connection_life, CONNECTION_STABLE_TIME); + state = MQTT_CLIENT_STATE_CONNECTED; + break; + } + case MQTT_EVENT_DISCONNECTED: { + DBG("APP - MQTT Disconnect. Reason %u\n", *((mqtt_event_t *)data)); + + /* Do nothing if the disconnect was the result of an incoming config */ + if(state != MQTT_CLIENT_STATE_NEWCONFIG) { + state = MQTT_CLIENT_STATE_DISCONNECTED; + process_poll(&mqtt_client_process); + } + break; + } + case MQTT_EVENT_PUBLISH: { + msg_ptr = data; + + /* Implement first_flag in publish message? */ + if(msg_ptr->first_chunk) { + msg_ptr->first_chunk = 0; + DBG("APP - Application received a publish on topic '%s'. Payload " + "size is %i bytes. Content:\n\n", + msg_ptr->topic, msg_ptr->payload_length); + } + + pub_handler(msg_ptr->topic, strlen(msg_ptr->topic), msg_ptr->payload_chunk, + msg_ptr->payload_length); + break; + } + case MQTT_EVENT_SUBACK: { + DBG("APP - Application is subscribed to topic successfully\n"); + break; + } + case MQTT_EVENT_UNSUBACK: { + DBG("APP - Application is unsubscribed to topic successfully\n"); + break; + } + case MQTT_EVENT_PUBACK: { + DBG("APP - Publishing complete.\n"); + break; + } + default: + DBG("APP - Application got a unhandled MQTT event: %i\n", event); + break; + } +} +/*---------------------------------------------------------------------------*/ +static int +construct_pub_topic(void) +{ + int len = snprintf(pub_topic, BUFFER_SIZE, "iot-2/evt/%s/fmt/json", + conf->event_type_id); + + /* len < 0: Error. Len >= BUFFER_SIZE: Buffer too small */ + if(len < 0 || len >= BUFFER_SIZE) { + printf("Pub Topic: %d, Buffer %d\n", len, BUFFER_SIZE); + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +construct_sub_topic(void) +{ + int len = snprintf(sub_topic, BUFFER_SIZE, "iot-2/cmd/%s/fmt/json", + conf->cmd_type); + + /* len < 0: Error. Len >= BUFFER_SIZE: Buffer too small */ + if(len < 0 || len >= BUFFER_SIZE) { + printf("Sub Topic: %d, Buffer %d\n", len, BUFFER_SIZE); + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +construct_client_id(void) +{ + int len = snprintf(client_id, BUFFER_SIZE, "d:%s:%s:%02x%02x%02x%02x%02x%02x", + conf->org_id, conf->type_id, + linkaddr_node_addr.u8[0], linkaddr_node_addr.u8[1], + linkaddr_node_addr.u8[2], linkaddr_node_addr.u8[5], + linkaddr_node_addr.u8[6], linkaddr_node_addr.u8[7]); + + /* len < 0: Error. Len >= BUFFER_SIZE: Buffer too small */ + if(len < 0 || len >= BUFFER_SIZE) { + printf("Client ID: %d, Buffer %d\n", len, BUFFER_SIZE); + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +static void +update_config(void) +{ + if(construct_client_id() == 0) { + /* Fatal error. Client ID larger than the buffer */ + state = MQTT_CLIENT_STATE_CONFIG_ERROR; + return; + } + + if(construct_sub_topic() == 0) { + /* Fatal error. Topic larger than the buffer */ + state = MQTT_CLIENT_STATE_CONFIG_ERROR; + return; + } + + if(construct_pub_topic() == 0) { + /* Fatal error. Topic larger than the buffer */ + state = MQTT_CLIENT_STATE_CONFIG_ERROR; + return; + } + + /* Reset the counter */ + seq_nr_value = 0; + + state = MQTT_CLIENT_STATE_INIT; + + /* + * Schedule next timer event ASAP + * + * If we entered an error state then we won't do anything when it fires. + * + * Since the error at this stage is a config error, we will only exit this + * error state if we get a new config. + */ + etimer_set(&publish_periodic_timer, 0); + + return; +} +/*---------------------------------------------------------------------------*/ +static int +init_config() +{ + /* Populate configuration with default values */ + memset(conf, 0, sizeof(mqtt_client_config_t)); + + memcpy(conf->org_id, CC26XX_WEB_DEMO_DEFAULT_ORG_ID, 11); + memcpy(conf->type_id, CC26XX_WEB_DEMO_DEFAULT_TYPE_ID, 7); + memcpy(conf->event_type_id, CC26XX_WEB_DEMO_DEFAULT_EVENT_TYPE_ID, 7); + memcpy(conf->broker_ip, broker_ip, strlen(broker_ip)); + memcpy(conf->cmd_type, CC26XX_WEB_DEMO_DEFAULT_SUBSCRIBE_CMD_TYPE, 1); + + conf->broker_port = CC26XX_WEB_DEMO_DEFAULT_BROKER_PORT; + conf->pub_interval = CC26XX_WEB_DEMO_DEFAULT_PUBLISH_INTERVAL; + conf->def_rt_ping_interval = CC26XX_WEB_DEMO_DEFAULT_RSSI_MEAS_INTERVAL; + + return 1; +} +/*---------------------------------------------------------------------------*/ +static void +register_http_post_handlers(void) +{ + httpd_simple_register_post_handler(&org_id_handler); + httpd_simple_register_post_handler(&type_id_handler); + httpd_simple_register_post_handler(&event_type_id_handler); + httpd_simple_register_post_handler(&cmd_type_handler); + httpd_simple_register_post_handler(&auth_token_handler); + httpd_simple_register_post_handler(&interval_handler); + httpd_simple_register_post_handler(&port_handler); + httpd_simple_register_post_handler(&ip_addr_handler); + httpd_simple_register_post_handler(&reconnect_handler); + httpd_simple_register_post_handler(&ping_interval_handler); +} +/*---------------------------------------------------------------------------*/ +static void +subscribe(void) +{ + /* Publish MQTT topic in IBM quickstart format */ + mqtt_status_t status; + + status = mqtt_subscribe(&conn, NULL, sub_topic, MQTT_QOS_LEVEL_0); + + DBG("APP - Subscribing!\n"); + if(status == MQTT_STATUS_OUT_QUEUE_FULL) { + DBG("APP - Tried to subscribe but command queue was full!\n"); + } +} +/*---------------------------------------------------------------------------*/ +static void +publish(void) +{ + /* Publish MQTT topic in IBM quickstart format */ + int len; + int remaining = APP_BUFFER_SIZE; + + seq_nr_value++; + + buf_ptr = app_buffer; + + len = snprintf(buf_ptr, remaining, + "{" + "\"d\":{" + "\"myName\":\"%s\"," + "\"Seq #\":%d," + "\"Uptime (sec)\":%lu", + BOARD_STRING, seq_nr_value, clock_seconds()); + + if(len < 0 || len >= remaining) { + printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len); + return; + } + + remaining -= len; + buf_ptr += len; + + /* Put our Default route's string representation in a buffer */ + char def_rt_str[64]; + memset(def_rt_str, 0, sizeof(def_rt_str)); + cc26xx_web_demo_ipaddr_sprintf(def_rt_str, sizeof(def_rt_str), + uip_ds6_defrt_choose()); + + len = snprintf(buf_ptr, remaining, ",\"Def Route\":\"%s\",\"RSSI (dBm)\":%d", + def_rt_str, def_rt_rssi); + + if(len < 0 || len >= remaining) { + printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len); + return; + } + remaining -= len; + buf_ptr += len; + + memcpy(&def_route, uip_ds6_defrt_choose(), sizeof(uip_ip6addr_t)); + + for(reading = cc26xx_web_demo_sensor_first(); + reading != NULL; reading = reading->next) { + if(reading->publish && reading->raw != CC26XX_SENSOR_READING_ERROR) { + len = snprintf(buf_ptr, remaining, + ",\"%s (%s)\":%s", reading->descr, reading->units, + reading->converted); + + if(len < 0 || len >= remaining) { + printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len); + return; + } + remaining -= len; + buf_ptr += len; + } + } + + len = snprintf(buf_ptr, remaining, "}}"); + + if(len < 0 || len >= remaining) { + printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len); + return; + } + + mqtt_publish(&conn, NULL, pub_topic, (uint8_t *)app_buffer, + strlen(app_buffer), MQTT_QOS_LEVEL_0, MQTT_RETAIN_OFF); + + DBG("APP - Publish!\n"); +} +/*---------------------------------------------------------------------------*/ +static void +connect_to_broker(void) +{ + /* Connect to MQTT server */ + mqtt_connect(&conn, conf->broker_ip, conf->broker_port, + conf->pub_interval * 3); + + state = MQTT_CLIENT_STATE_CONNECTING; +} +/*---------------------------------------------------------------------------*/ +static void +ping_parent(void) +{ + if(uip_ds6_get_global(ADDR_PREFERRED) == NULL) { + return; + } + + uip_icmp6_send(uip_ds6_defrt_choose(), ICMP6_ECHO_REQUEST, 0, + CC26XX_WEB_DEMO_ECHO_REQ_PAYLOAD_LEN); +} +/*---------------------------------------------------------------------------*/ +static void +state_machine(void) +{ + switch(state) { + case MQTT_CLIENT_STATE_INIT: + /* If we have just been configured register MQTT connection */ + mqtt_register(&conn, &mqtt_client_process, client_id, mqtt_event, + MQTT_CLIENT_MAX_SEGMENT_SIZE); + + /* + * If we are not using the quickstart service (thus we are an IBM + * registered device), we need to provide user name and password + */ + if(strncasecmp(conf->org_id, QUICKSTART, strlen(conf->org_id)) != 0) { + if(strlen(conf->auth_token) == 0) { + printf("User name set, but empty auth token\n"); + state = MQTT_CLIENT_STATE_ERROR; + break; + } else { + mqtt_set_username_password(&conn, "use-token-auth", + conf->auth_token); + } + } + + /* _register() will set auto_reconnect. We don't want that. */ + conn.auto_reconnect = 0; + connect_attempt = 1; + + /* + * Wipe out the default route so we'll republish it every time we switch to + * a new broker + */ + memset(&def_route, 0, sizeof(def_route)); + + state = MQTT_CLIENT_STATE_REGISTERED; + DBG("Init\n"); + /* Continue */ + case MQTT_CLIENT_STATE_REGISTERED: + if(uip_ds6_get_global(ADDR_PREFERRED) != NULL) { + /* Registered and with a public IP. Connect */ + DBG("Registered. Connect attempt %u\n", connect_attempt); + ping_parent(); + connect_to_broker(); + } + etimer_set(&publish_periodic_timer, CC26XX_WEB_DEMO_NET_CONNECT_PERIODIC); + return; + break; + case MQTT_CLIENT_STATE_CONNECTING: + leds_on(CC26XX_WEB_DEMO_STATUS_LED); + ctimer_set(&ct, CONNECTING_LED_DURATION, publish_led_off, NULL); + /* Not connected yet. Wait */ + DBG("Connecting (%u)\n", connect_attempt); + break; + case MQTT_CLIENT_STATE_CONNECTED: + /* Don't subscribe unless we are a registered device */ + if(strncasecmp(conf->org_id, QUICKSTART, strlen(conf->org_id)) == 0) { + DBG("Using 'quickstart': Skipping subscribe\n"); + state = MQTT_CLIENT_STATE_PUBLISHING; + } + /* Continue */ + case MQTT_CLIENT_STATE_PUBLISHING: + /* If the timer expired, the connection is stable. */ + if(timer_expired(&connection_life)) { + /* + * Intentionally using 0 here instead of 1: We want RECONNECT_ATTEMPTS + * attempts if we disconnect after a successful connect + */ + connect_attempt = 0; + } + + if(mqtt_ready(&conn) && conn.out_buffer_sent) { + /* Connected. Publish */ + if(state == MQTT_CLIENT_STATE_CONNECTED) { + subscribe(); + state = MQTT_CLIENT_STATE_PUBLISHING; + } else { + leds_on(CC26XX_WEB_DEMO_STATUS_LED); + ctimer_set(&ct, PUBLISH_LED_ON_DURATION, publish_led_off, NULL); + publish(); + } + etimer_set(&publish_periodic_timer, conf->pub_interval); + + DBG("Publishing\n"); + /* Return here so we don't end up rescheduling the timer */ + return; + } else { + /* + * Our publish timer fired, but some MQTT packet is already in flight + * (either not sent at all, or sent but not fully ACKd). + * + * This can mean that we have lost connectivity to our broker or that + * simply there is some network delay. In both cases, we refuse to + * trigger a new message and we wait for TCP to either ACK the entire + * packet after retries, or to timeout and notify us. + */ + DBG("Publishing... (MQTT state=%d, q=%u)\n", conn.state, + conn.out_queue_full); + } + break; + case MQTT_CLIENT_STATE_DISCONNECTED: + DBG("Disconnected\n"); + if(connect_attempt < RECONNECT_ATTEMPTS || + RECONNECT_ATTEMPTS == RETRY_FOREVER) { + /* Disconnect and backoff */ + clock_time_t interval; + mqtt_disconnect(&conn); + connect_attempt++; + + interval = connect_attempt < 3 ? RECONNECT_INTERVAL << connect_attempt : + RECONNECT_INTERVAL << 3; + + DBG("Disconnected. Attempt %u in %lu ticks\n", connect_attempt, interval); + + etimer_set(&publish_periodic_timer, interval); + + state = MQTT_CLIENT_STATE_REGISTERED; + return; + } else { + /* Max reconnect attempts reached. Enter error state */ + state = MQTT_CLIENT_STATE_ERROR; + DBG("Aborting connection after %u attempts\n", connect_attempt - 1); + } + break; + case MQTT_CLIENT_STATE_NEWCONFIG: + /* Only update config after we have disconnected */ + if(conn.state == MQTT_CONN_STATE_NOT_CONNECTED) { + update_config(); + DBG("New config\n"); + + /* update_config() scheduled next pass. Return */ + return; + } + break; + case MQTT_CLIENT_STATE_CONFIG_ERROR: + /* Idle away. The only way out is a new config */ + printf("Bad configuration.\n"); + return; + case MQTT_CLIENT_STATE_ERROR: + default: + leds_on(CC26XX_WEB_DEMO_STATUS_LED); + /* + * 'default' should never happen. + * + * If we enter here it's because of some error. Stop timers. The only thing + * that can bring us out is a new config event + */ + printf("Default case: State=0x%02x\n", state); + return; + } + + /* If we didn't return so far, reschedule ourselves */ + etimer_set(&publish_periodic_timer, STATE_MACHINE_PERIODIC); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(mqtt_client_process, ev, data) +{ + + PROCESS_BEGIN(); + + printf("CC26XX MQTT Client Process\n"); + + conf = &cc26xx_web_demo_config.mqtt_config; + if(init_config() != 1) { + PROCESS_EXIT(); + } + + register_http_post_handlers(); + + update_config(); + + def_rt_rssi = 0x8000000; + uip_icmp6_echo_reply_callback_add(&echo_reply_notification, + echo_reply_handler); + etimer_set(&echo_request_timer, conf->def_rt_ping_interval); + + /* Main loop */ + while(1) { + + PROCESS_YIELD(); + + if(ev == sensors_event && data == CC26XX_WEB_DEMO_MQTT_PUBLISH_TRIGGER) { + if(state == MQTT_CLIENT_STATE_ERROR) { + connect_attempt = 1; + state = MQTT_CLIENT_STATE_REGISTERED; + } + } + + if(ev == httpd_simple_event_new_config) { + /* + * Schedule next pass in a while. When HTTPD sends us this event, it is + * also in the process of sending the config page. Wait a little before + * reconnecting, so as to not cause congestion. + */ + etimer_set(&publish_periodic_timer, NEW_CONFIG_WAIT_INTERVAL); + } + + if((ev == PROCESS_EVENT_TIMER && data == &publish_periodic_timer) || + ev == PROCESS_EVENT_POLL || + ev == cc26xx_web_demo_publish_event || + (ev == sensors_event && data == CC26XX_WEB_DEMO_MQTT_PUBLISH_TRIGGER)) { + state_machine(); + } + + if(ev == PROCESS_EVENT_TIMER && data == &echo_request_timer) { + ping_parent(); + etimer_set(&echo_request_timer, conf->def_rt_ping_interval); + } + + if(ev == cc26xx_web_demo_load_config_defaults) { + init_config(); + etimer_set(&publish_periodic_timer, NEW_CONFIG_WAIT_INTERVAL); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/mqtt-client.h b/examples/cc26xx/cc26xx-web-demo/mqtt-client.h new file mode 100644 index 000000000..b4b50085f --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/mqtt-client.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * Header file for the CC26xx web demo MQTT client functionality + */ +/*---------------------------------------------------------------------------*/ +#ifndef MQTT_CLIENT_H_ +#define MQTT_CLIENT_H_ +/*---------------------------------------------------------------------------*/ +#define MQTT_CLIENT_CONFIG_ORG_ID_LEN 32 +#define MQTT_CLIENT_CONFIG_TYPE_ID_LEN 32 +#define MQTT_CLIENT_CONFIG_AUTH_TOKEN_LEN 32 +#define MQTT_CLIENT_CONFIG_EVENT_TYPE_ID_LEN 32 +#define MQTT_CLIENT_CONFIG_CMD_TYPE_LEN 8 +#define MQTT_CLIENT_CONFIG_IP_ADDR_STR_LEN 64 +/*---------------------------------------------------------------------------*/ +#define MQTT_CLIENT_RSSI_MEASURE_INTERVAL_MAX 86400 /* secs: 1 day */ +#define MQTT_CLIENT_RSSI_MEASURE_INTERVAL_MIN 5 /* secs */ +#define MQTT_CLIENT_PUBLISH_INTERVAL_MAX 86400 /* secs: 1 day */ +#define MQTT_CLIENT_PUBLISH_INTERVAL_MIN 5 /* secs */ +/*---------------------------------------------------------------------------*/ +PROCESS_NAME(mqtt_client_process); +/*---------------------------------------------------------------------------*/ +/** + * \brief Data structure declaration for the MQTT client configuration + */ +typedef struct mqtt_client_config { + char org_id[MQTT_CLIENT_CONFIG_ORG_ID_LEN]; + char type_id[MQTT_CLIENT_CONFIG_TYPE_ID_LEN]; + char auth_token[MQTT_CLIENT_CONFIG_AUTH_TOKEN_LEN]; + char event_type_id[MQTT_CLIENT_CONFIG_EVENT_TYPE_ID_LEN]; + char broker_ip[MQTT_CLIENT_CONFIG_IP_ADDR_STR_LEN]; + char cmd_type[MQTT_CLIENT_CONFIG_CMD_TYPE_LEN]; + clock_time_t pub_interval; + int def_rt_ping_interval; + uint16_t broker_port; +} mqtt_client_config_t; +/*---------------------------------------------------------------------------*/ +#endif /* MQTT_CLIENT_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/net-uart.c b/examples/cc26xx/cc26xx-web-demo/net-uart.c new file mode 100644 index 000000000..96463aee0 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/net-uart.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * A process which receives data over UART and transmits them over UDP + * to a pre-defined IPv6 address and port. It also listens on the same UDP + * port for messages, which it prints out over UART. + * + * For this example to work, you will have to modify the destination IPv6 + * address by adjusting the set_dest_addr() macro below. + * + * To listen on your linux or OS X box: + * nc -6ulkw 1 REMOTE_PORT + * + * (REMOTE_PORT should be the actual value of the define below, e.g. 7777) + * + * Once netcat is up and listening, type something to the CC26xx's terminal + * Bear in mind that the datagram will only be sent after a 0x0a (LF) char + * has been received. Therefore, if you are on Win, do NOT use PuTTY for + * this purpose, since it does not send 0x0a as part of the line end. On + * Win XP use hyperterm. On Win 7 use some other software (e.g. Tera Term, + * which can be configured to send CRLF on enter keystrokes). + * + * To send data in the other direction from your linux or OS X box: + * + * nc -6u \ REMOTE_PORT + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "sys/process.h" +#include "dev/serial-line.h" +#include "net/ip/uip.h" +#include "net/ip/uip-udp-packet.h" +#include "net/ip/uiplib.h" +#include "lpm.h" +#include "net-uart.h" +#include "httpd-simple.h" + +#include "ti-lib.h" + +#include +#include +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG DEBUG_NONE +#include "net/ip/uip-debug.h" +/*---------------------------------------------------------------------------*/ +#define REMOTE_PORT 7777 +#define MAX_MSG_SIZE 100 + +#define set_dest_addr() uip_ip6addr(&remote_addr, \ + 0xBBBB, 0x0000, 0x0000, 0x0000, \ + 0x3E07, 0x54FF, 0xFE74, 0x4885); +/*---------------------------------------------------------------------------*/ +#define ADDRESS_CONVERSION_OK 1 +#define ADDRESS_CONVERSION_ERROR 0 +/*---------------------------------------------------------------------------*/ +#ifndef MIN +#define MIN(n, m) (((n) < (m)) ? (n) : (m)) +#endif +/*---------------------------------------------------------------------------*/ +static struct uip_udp_conn *udp_conn = NULL; + +static uint8_t buffer[MAX_MSG_SIZE]; +static uint8_t msg_len; +static uip_ip6addr_t remote_addr; +/*---------------------------------------------------------------------------*/ +#define IPV6_ADDR_STR_LEN 64 +/*---------------------------------------------------------------------------*/ +PROCESS(net_uart_process, "Net UART Process"); +/*---------------------------------------------------------------------------*/ +/* + * \brief Attempts to convert a string representation of an IPv6 address to a + * numeric one. + * \param buf The buffer with the string to be converted. + * \return ADDRESS_CONVERSION_OK or ADDRESS_CONVERSION_ERROR + * + * ToDo: Add support for NAT64 conversion in case the incoming address is a v4 + * This is now supported in the current master, so when we pull it in this will + * be very straightforward. + */ +static int +set_new_ip_address(char *buf) +{ + /* + * uiplib_ip6addrconv will immediately start writing into the supplied buffer + * even if it subsequently fails. Thus, pass an intermediate buffer + */ + uip_ip6addr_t tmp_addr; + + int rv = uiplib_ip6addrconv(buf, &tmp_addr); + + if(rv == ADDRESS_CONVERSION_OK) { + /* Conversion OK, copy to our main buffer */ + memcpy(&remote_addr, &tmp_addr, sizeof(remote_addr)); + + PRINTF("Updated remote address "); + PRINT6ADDR(&remote_addr); + PRINTF("\n"); + } + + return rv; +} +/*---------------------------------------------------------------------------*/ +static void +net_input(void) +{ + if(uip_newdata()) { + memset(buffer, 0, MAX_MSG_SIZE); + msg_len = MIN(uip_datalen(), MAX_MSG_SIZE - 1); + + /* Copy data */ + memcpy(buffer, uip_appdata, msg_len); + printf("%s", (char *)buffer); + } + + return; +} +/*---------------------------------------------------------------------------*/ +/* + * In order to maintain UART input operation: + * - Keep the uart clocked in sleep and deep sleep + * - Keep the serial PD on in deep sleep + */ +static lpm_power_domain_lock_t lock; +/*---------------------------------------------------------------------------*/ +static void +release_uart(void) +{ + /* Release serial PD lock */ + lpm_pd_lock_release(&lock); + + /* Let the UART turn off during Sleep and Deep Sleep */ + ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_UART0); + ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_UART0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); +} +/*---------------------------------------------------------------------------*/ +static void +keep_uart_on(void) +{ + /* Keep the serial PD on */ + lpm_pd_lock_obtain(&lock, PRCM_DOMAIN_SERIAL); + + /* Keep the UART clock on during Sleep and Deep Sleep */ + ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_UART0); + ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_UART0); + ti_lib_prcm_load_set(); + while(!ti_lib_prcm_load_get()); +} +/*---------------------------------------------------------------------------*/ +static int +remote_port_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv; + + if(key_len != strlen("net_uart_port") || + strncasecmp(key, "net_uart_port", strlen("net_uart_port")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + rv = atoi(val); + + if(rv <= 65535 && rv > 0) { + cc26xx_web_demo_config.net_uart.remote_port = (uint16_t)rv; + } else { + return HTTPD_SIMPLE_POST_HANDLER_ERROR; + } + + return HTTPD_SIMPLE_POST_HANDLER_OK; +} +/*---------------------------------------------------------------------------*/ +static int +remote_ipv6_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv = HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + + if(key_len != strlen("net_uart_ip") || + strncasecmp(key, "net_uart_ip", strlen("net_uart_ip")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + if(val_len > IPV6_ADDR_STR_LEN) { + /* Ours but bad value */ + rv = HTTPD_SIMPLE_POST_HANDLER_ERROR; + } else { + if(set_new_ip_address(val)) { + memset(cc26xx_web_demo_config.net_uart.remote_address, 0, + NET_UART_IP_ADDR_STRLEN); + memcpy(cc26xx_web_demo_config.net_uart.remote_address, val, val_len); + rv = HTTPD_SIMPLE_POST_HANDLER_OK; + } + } + + return rv; +} +/*---------------------------------------------------------------------------*/ +static int +on_off_post_handler(char *key, int key_len, char *val, int val_len) +{ + int rv; + + if(key_len != strlen("net_uart_on") || + strncasecmp(key, "net_uart_on", strlen("net_uart_on")) != 0) { + /* Not ours */ + return HTTPD_SIMPLE_POST_HANDLER_UNKNOWN; + } + + rv = atoi(val); + + /* Be pedantic: only accept 0 and 1, not just any non-zero value */ + if(rv == 0) { + cc26xx_web_demo_config.net_uart.enable = 0; + release_uart(); + } else if(rv == 1) { + cc26xx_web_demo_config.net_uart.enable = 1; + keep_uart_on(); + } else { + return HTTPD_SIMPLE_POST_HANDLER_ERROR; + } + + return HTTPD_SIMPLE_POST_HANDLER_OK; +} +/*---------------------------------------------------------------------------*/ +HTTPD_SIMPLE_POST_HANDLER(remote_port, remote_port_post_handler); +HTTPD_SIMPLE_POST_HANDLER(remote_ipv6, remote_ipv6_post_handler); +HTTPD_SIMPLE_POST_HANDLER(on_off, on_off_post_handler); +/*---------------------------------------------------------------------------*/ +static void +set_config_defaults(void) +{ + /* Set a hard-coded destination address to start with */ + set_dest_addr(); + + /* Set config defaults */ + cc26xx_web_demo_ipaddr_sprintf(cc26xx_web_demo_config.net_uart.remote_address, + NET_UART_IP_ADDR_STRLEN, &remote_addr); + cc26xx_web_demo_config.net_uart.remote_port = REMOTE_PORT; + cc26xx_web_demo_config.net_uart.enable = 1; +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(net_uart_process, ev, data) +{ + PROCESS_BEGIN(); + + printf("CC26XX Net UART Process\n"); + + set_config_defaults(); + + udp_conn = udp_new(NULL, UIP_HTONS(0), NULL); + udp_bind(udp_conn, UIP_HTONS(REMOTE_PORT)); + + if(udp_conn == NULL) { + printf("No UDP connection available, exiting the process!\n"); + PROCESS_EXIT(); + } + + httpd_simple_register_post_handler(&remote_port_handler); + httpd_simple_register_post_handler(&remote_ipv6_handler); + httpd_simple_register_post_handler(&on_off_handler); + + while(1) { + + PROCESS_YIELD(); + + if(ev == serial_line_event_message) { + /* + * If the message contains a new IP address, save it and go back to + * waiting. + */ + if(set_new_ip_address((char *)data) == ADDRESS_CONVERSION_ERROR) { + /* Not an IP address in the message. Send to current destination */ + memset(buffer, 0, MAX_MSG_SIZE); + + /* We need to add a line feed, thus never fill the entire buffer */ + msg_len = MIN(strlen(data), MAX_MSG_SIZE - 1); + memcpy(buffer, data, msg_len); + + /* Add a line feed */ + buffer[msg_len] = 0x0A; + msg_len++; + + uip_udp_packet_sendto( + udp_conn, buffer, msg_len, &remote_addr, + UIP_HTONS(cc26xx_web_demo_config.net_uart.remote_port)); + } + } else if(ev == tcpip_event) { + net_input(); + } else if(ev == cc26xx_web_demo_config_loaded_event) { + /* + * New config. Check if it's possible to update the remote address. + * The port will have been updated already + */ + set_new_ip_address(cc26xx_web_demo_config.net_uart.remote_address); + + if(cc26xx_web_demo_config.net_uart.enable == 1) { + keep_uart_on(); + } + } else if(ev == cc26xx_web_demo_load_config_defaults) { + set_config_defaults(); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/examples/cc26xx/cc26xx-web-demo/net-uart.h b/examples/cc26xx/cc26xx-web-demo/net-uart.h new file mode 100644 index 000000000..5c4201672 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/net-uart.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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 NET_UART_H_ +#define NET_UART_H_ +/*---------------------------------------------------------------------------*/ +#include "net/ip/uip.h" + +#include +/*---------------------------------------------------------------------------*/ +#define NET_UART_IP_ADDR_STRLEN 64 +/*---------------------------------------------------------------------------*/ +PROCESS_NAME(net_uart_process); +/*---------------------------------------------------------------------------*/ +typedef struct net_uart_config_s { + char remote_address[NET_UART_IP_ADDR_STRLEN]; + uint16_t remote_port; + uint8_t enable; +} net_uart_config_t; +/*---------------------------------------------------------------------------*/ +#endif /* NET_UART_H_ */ +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc26xx/cc26xx-web-demo/project-conf.h b/examples/cc26xx/cc26xx-web-demo/project-conf.h new file mode 100644 index 000000000..d49a7b3e9 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/project-conf.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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 PROJECT_CONF_H_ +#define PROJECT_CONF_H_ +/*---------------------------------------------------------------------------*/ +/* Change to match your configuration */ +#define NETSTACK_CONF_RDC contikimac_driver +#define IEEE802154_CONF_PANID 0xABCD +#define CC26XX_RF_CONF_CHANNEL 25 +#define CC26XX_MODEL_CONF_CPU_VARIANT 2650 /* CC2650 */ +#define CC26XX_RF_CONF_BLE_SUPPORT 1 /* Only available with CC2650 */ +/*---------------------------------------------------------------------------*/ +/* Enable/Disable Components of this Demo */ +#define CC26XX_WEB_DEMO_CONF_MQTT_CLIENT 1 +#define CC26XX_WEB_DEMO_CONF_6LBR_CLIENT 1 +#define CC26XX_WEB_DEMO_CONF_COAP_SERVER 1 +#define CC26XX_WEB_DEMO_CONF_NET_UART 1 +/*---------------------------------------------------------------------------*/ +/* Shrink the size of the uIP buffer, routing table and ND cache */ +#define UIP_CONF_BUFFER_SIZE 900 +#define NBR_TABLE_CONF_MAX_NEIGHBORS 8 +#define UIP_CONF_MAX_ROUTES 8 +/*---------------------------------------------------------------------------*/ +#endif /* PROJECT_CONF_H_ */ +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc26xx/cc26xx-web-demo/resources/res-ble-advd.c b/examples/cc26xx/cc26xx-web-demo/resources/res-ble-advd.c new file mode 100644 index 000000000..0604c7ac9 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/resources/res-ble-advd.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015, Texas Instruments Incorporated - http://www.ti.com/ + * 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 Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * CoAP resource to start/stop/configure BLE advertisements + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "rest-engine.h" +#include "er-coap.h" +#include "dev/cc26xx-rf.h" + +#include +#include +/*---------------------------------------------------------------------------*/ +#define BLE_NAME_BUF_LEN 32 +/*---------------------------------------------------------------------------*/ +const char *forbidden_payload = "Name to advertise unspecified.\n" + "Use name= in the request"; +/*---------------------------------------------------------------------------*/ +static void +res_ble_post_put_handler(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + size_t len = 0; + const char *text = NULL; + char name[BLE_NAME_BUF_LEN]; + int success = 0; + int rv; + + memset(name, 0, BLE_NAME_BUF_LEN); + + len = REST.get_post_variable(request, "name", &text); + + if(len > 0 && len < BLE_NAME_BUF_LEN) { + memcpy(name, text, len); + cc26xx_rf_ble_beacond_config(0, name); + success = 1; + } + + len = REST.get_post_variable(request, "interval", &text); + + rv = atoi(text); + + if(rv > 0) { + cc26xx_rf_ble_beacond_config((clock_time_t)(rv * CLOCK_SECOND), NULL); + success = 1; + } + + len = REST.get_post_variable(request, "mode", &text); + + if(len) { + if(strncmp(text, "on", len) == 0) { + if(cc26xx_rf_ble_beacond_start()) { + success = 1; + } else { + REST.set_response_status(response, REST.status.FORBIDDEN); + REST.set_response_payload(response, forbidden_payload, + strlen(forbidden_payload)); + return; + } + } else if(strncmp(text, "off", len) == 0) { + cc26xx_rf_ble_beacond_stop(); + success = 1; + } else { + success = 0; + } + } + + if(!success) { + REST.set_response_status(response, REST.status.BAD_REQUEST); + } +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_ble_advd, + "title=\"BLE advd config: POST/PUT name=&mode=on|off" + "&interval=\";rt=\"Control\"", + NULL, + res_ble_post_put_handler, + res_ble_post_put_handler, + NULL); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/examples/cc26xx/cc26xx-web-demo/resources/res-device.c b/examples/cc26xx/cc26xx-web-demo/resources/res-device.c new file mode 100644 index 000000000..84cef8e24 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/resources/res-device.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * CoAP resource handler for CC26XX software and hardware version + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "rest-engine.h" +#include "er-coap.h" +#include "sys/clock.h" +#include "cc26xx-model.h" +#include "coap-server.h" +#include "cc26xx-web-demo.h" + +#include +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_hw(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + unsigned int accept = -1; + + REST.get_header_accept(request, &accept); + + if(accept == -1 || accept == REST.type.TEXT_PLAIN) { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%s on CC%u", BOARD_STRING, + CC26XX_MODEL_CPU_VARIANT); + + REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_JSON) { + REST.set_header_content_type(response, REST.type.APPLICATION_JSON); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{\"HW Ver\":\"%s on CC%u\"}", + BOARD_STRING, CC26XX_MODEL_CPU_VARIANT); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_XML) { + REST.set_header_content_type(response, REST.type.APPLICATION_XML); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, + "", BOARD_STRING, + CC26XX_MODEL_CPU_VARIANT); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else { + REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); + REST.set_response_payload(response, coap_server_supported_msg, + strlen(coap_server_supported_msg)); + } +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_sw(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + unsigned int accept = -1; + + REST.get_header_accept(request, &accept); + + if(accept == -1 || accept == REST.type.TEXT_PLAIN) { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%s", CONTIKI_VERSION_STRING); + + REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_JSON) { + REST.set_header_content_type(response, REST.type.APPLICATION_JSON); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{\"SW Ver\":\"%s\"}", + CONTIKI_VERSION_STRING); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_XML) { + REST.set_header_content_type(response, REST.type.APPLICATION_XML); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, + "", CONTIKI_VERSION_STRING); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else { + REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); + REST.set_response_payload(response, coap_server_supported_msg, + strlen(coap_server_supported_msg)); + } +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_uptime(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + unsigned int accept = -1; + + REST.get_header_accept(request, &accept); + + if(accept == -1 || accept == REST.type.TEXT_PLAIN) { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%lu", clock_seconds()); + + REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_JSON) { + REST.set_header_content_type(response, REST.type.APPLICATION_JSON); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{\"uptime\":%lu}", + clock_seconds()); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_XML) { + REST.set_header_content_type(response, REST.type.APPLICATION_XML); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, + "", clock_seconds()); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else { + REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); + REST.set_response_payload(response, coap_server_supported_msg, + strlen(coap_server_supported_msg)); + } +} +/*---------------------------------------------------------------------------*/ +static void +res_post_handler_cfg_reset(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + cc26xx_web_demo_restore_defaults(); +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_device_sw, + "title=\"Software version\";rt=\"text\"", + res_get_handler_sw, + NULL, + NULL, + NULL); +/*---------------------------------------------------------------------------*/ +RESOURCE(res_device_uptime, + "title=\"Uptime\";rt=\"seconds\"", + res_get_handler_uptime, + NULL, + NULL, + NULL); +/*---------------------------------------------------------------------------*/ +RESOURCE(res_device_hw, + "title=\"Hardware version\";rt=\"text\"", + res_get_handler_hw, + NULL, + NULL, + NULL); +/*---------------------------------------------------------------------------*/ +RESOURCE(res_device_cfg_reset, + "title=\"Reset Device Config: POST\";rt=\"Control\"", + NULL, res_post_handler_cfg_reset, NULL, NULL); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/examples/cc26xx/cc26xx-web-demo/resources/res-leds.c b/examples/cc26xx/cc26xx-web-demo/resources/res-leds.c new file mode 100644 index 000000000..2a86ad25b --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/resources/res-leds.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * CoAP resource handler for the CC26xx LEDs. Slightly modified copy of + * the one found in Contiki's original CoAP example. + * \author + * Matthias Kovatsch (original) + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "rest-engine.h" +#include "dev/leds.h" + +#include +/*---------------------------------------------------------------------------*/ +static void +res_post_put_handler(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + size_t len = 0; + const char *color = NULL; + const char *mode = NULL; + uint8_t led = 0; + int success = 1; + + if((len = REST.get_query_variable(request, "color", &color))) { + if(strncmp(color, "r", len) == 0) { + led = LEDS_RED; + } else if(strncmp(color, "g", len) == 0) { + led = LEDS_GREEN; +#if BOARD_SMARTRF06EB + } else if(strncmp(color, "y", len) == 0) { + led = LEDS_YELLOW; + } else if(strncmp(color, "o", len) == 0) { + led = LEDS_ORANGE; +#endif + } else { + success = 0; + } + } else { + success = 0; + } + + if(success && (len = REST.get_post_variable(request, "mode", &mode))) { + if(strncmp(mode, "on", len) == 0) { + leds_on(led); + } else if(strncmp(mode, "off", len) == 0) { + leds_off(led); + } else { + success = 0; + } + } else { + success = 0; + } + + if(!success) { + REST.set_response_status(response, REST.status.BAD_REQUEST); + } +} +/*---------------------------------------------------------------------------*/ +/* + * A simple actuator example, depending on the color query parameter and post + * variable mode, corresponding led is activated or deactivated + */ +#if BOARD_SENSORTAG +#define RESOURCE_PARAMS "r|g" +#elif BOARD_SMARTRF06EB +#define RESOURCE_PARAMS "r|g|y|o" +#endif + +RESOURCE(res_leds, + "title=\"LEDs: ?color=" RESOURCE_PARAMS ", POST/PUT mode=on|off\";rt=\"Control\"", + NULL, + res_post_put_handler, + res_post_put_handler, + NULL); +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/examples/cc26xx/cc26xx-web-demo/resources/res-sensors.c b/examples/cc26xx/cc26xx-web-demo/resources/res-sensors.c new file mode 100644 index 000000000..fecfca994 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/resources/res-sensors.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * CoAP resource handler for the Sensortag-CC26xx sensors + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "rest-engine.h" +#include "er-coap.h" +#include "cc26xx-web-demo.h" +#include "coap-server.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +/* + * Generic resource handler for any sensor in this example. Ultimately gets + * called by all handlers and populates the CoAP response + */ +static void +res_get_handler_all(int sens_type, void *request, void *response, + uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + unsigned int accept = -1; + const cc26xx_web_demo_sensor_reading_t *reading; + + reading = cc26xx_web_demo_sensor_lookup(sens_type); + + if(reading == NULL) { + REST.set_response_status(response, REST.status.NOT_FOUND); + REST.set_response_payload(response, coap_server_not_found_msg, + strlen(coap_server_not_found_msg)); + return; + } + + REST.get_header_accept(request, &accept); + + if(accept == -1 || accept == REST.type.TEXT_PLAIN) { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%s", reading->converted); + + REST.set_response_payload(response, (uint8_t *)buffer, + strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_JSON) { + REST.set_header_content_type(response, REST.type.APPLICATION_JSON); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{\"%s\":%s}", + reading->descr, reading->converted); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_XML) { + REST.set_header_content_type(response, REST.type.APPLICATION_XML); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, + "<%s val=\"%s\" unit=\"%s\"/>", reading->xml_element, + reading->converted, reading->units); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else { + REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); + REST.set_response_payload(response, coap_server_supported_msg, + strlen(coap_server_supported_msg)); + } +} +/*---------------------------------------------------------------------------*/ +/* BatMon resources and handler: Temperature, Voltage */ +static void +res_get_handler_batmon_temp(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_BATMON_TEMP, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_batmon_volt(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_BATMON_VOLT, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_batmon_temp, "title=\"Battery Temp\";rt=\"C\"", + res_get_handler_batmon_temp, NULL, NULL, NULL); +/*---------------------------------------------------------------------------*/ +RESOURCE(res_batmon_volt, "title=\"Battery Voltage\";rt=\"mV\"", + res_get_handler_batmon_volt, NULL, NULL, NULL); +/*---------------------------------------------------------------------------*/ +#if BOARD_SENSORTAG +/*---------------------------------------------------------------------------*/ +/* MPU resources and handler: Accelerometer and Gyro */ +static void +res_get_handler_mpu_acc_x(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_MPU_ACC_X, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_mpu_acc_y(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_MPU_ACC_Y, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_mpu_acc_z(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_MPU_ACC_Z, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_mpu_gyro_x(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_X, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_mpu_gyro_y(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_Y, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_mpu_gyro_z(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_Z, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_mpu_acc_x, "title=\"Acc X\";rt=\"G\"", res_get_handler_mpu_acc_x, + NULL, NULL, NULL); +RESOURCE(res_mpu_acc_y, "title=\"Acc Y\";rt=\"G\"", res_get_handler_mpu_acc_y, + NULL, NULL, NULL); +RESOURCE(res_mpu_acc_z, "title=\"Acc Z\";rt=\"G\"", res_get_handler_mpu_acc_z, + NULL, NULL, NULL); + +RESOURCE(res_mpu_gyro_x, "title=\"Gyro X\";rt=\"deg/sec\"", + res_get_handler_mpu_gyro_x, NULL, NULL, NULL); +RESOURCE(res_mpu_gyro_y, "title=\"Gyro Y\";rt=\"deg/sec\"", + res_get_handler_mpu_gyro_y, NULL, NULL, NULL); +RESOURCE(res_mpu_gyro_z, "title=\"Gyro Z\";rt=\"deg/sec\"", + res_get_handler_mpu_gyro_z, NULL, NULL, NULL); +/*---------------------------------------------------------------------------*/ +/* TMP sensor resources and handlers: Object, Ambient */ +static void +res_get_handler_obj_temp(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_TMP_OBJECT, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_amb_temp(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_TMP_AMBIENT, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_tmp007_obj, "title=\"Temperature (Object)\";rt=\"C\"", + res_get_handler_obj_temp, NULL, NULL, NULL); + +RESOURCE(res_tmp007_amb, "title=\"Temperature (Ambient)\";rt=\"C\"", + res_get_handler_amb_temp, NULL, NULL, NULL); +/*---------------------------------------------------------------------------*/ +/* BMP sensor resources: Temperature, Pressure */ +static void +res_get_handler_bmp_temp(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_BMP_TEMP, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_bmp_press(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_BMP_PRES, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_bmp280_temp, "title=\"Barometer (Temperature)\";rt=\"C\"", + res_get_handler_bmp_temp, NULL, NULL, NULL); + +RESOURCE(res_bmp280_press, + "title=\"Barometer (Pressure)\";rt=\"hPa (hectopascal / millibar)\"", + res_get_handler_bmp_press, NULL, NULL, NULL); +/*---------------------------------------------------------------------------*/ +/* SHT21 sensor resources and handler: Temperature, Pressure */ +static void +res_get_handler_sht_temp(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_SHT_TEMP, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +static void +res_get_handler_sht_humidity(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_SHT_HUMIDITY, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_sht21_temp, "title=\"Temperature\";rt=\"C\"", + res_get_handler_sht_temp, NULL, NULL, NULL); + +RESOURCE(res_sht21_hum, "title=\"Humidity\";rt=\"%RH\"", + res_get_handler_sht_humidity, NULL, NULL, NULL); +/*---------------------------------------------------------------------------*/ +/* Illuminance resources and handler */ +static void +res_get_handler_opt(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_OPT_LIGHT, request, response, + buffer, preferred_size, offset); +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_opt3001_light, "title=\"Illuminance\";rt=\"Lux\"", + res_get_handler_opt, NULL, NULL, NULL); +/*---------------------------------------------------------------------------*/ +#endif /* BOARD_SENSORTAG */ +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/examples/cc26xx/cc26xx-web-demo/resources/res-toggle-leds.c b/examples/cc26xx/cc26xx-web-demo/resources/res-toggle-leds.c new file mode 100644 index 000000000..2fb424a03 --- /dev/null +++ b/examples/cc26xx/cc26xx-web-demo/resources/res-toggle-leds.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc26xx-web-demo + * @{ + * + * \file + * CoAP resource to toggle LEDs. Slightly modified copy of the one found + * in Contiki's original CoAP example. + * \author + * Matthias Kovatsch (original) + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "rest-engine.h" +#include "dev/leds.h" + +#include +/*---------------------------------------------------------------------------*/ +static void +res_post_handler_red(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + leds_toggle(LEDS_RED); +} +/*---------------------------------------------------------------------------*/ +static void +res_post_handler_green(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + leds_toggle(LEDS_GREEN); +} +/*---------------------------------------------------------------------------*/ +/* Toggles the red led */ +RESOURCE(res_toggle_red, + "title=\"Red LED\";rt=\"Control\"", + NULL, + res_post_handler_red, + NULL, + NULL); + +/* Toggles the green led */ +RESOURCE(res_toggle_green, + "title=\"Green LED\";rt=\"Control\"", + NULL, + res_post_handler_green, + NULL, + NULL); +/*---------------------------------------------------------------------------*/ +/* An additional 2 LEDs on the Srf */ +#if BOARD_SMARTRF06EB +/*---------------------------------------------------------------------------*/ +static void +res_post_handler_yellow(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + leds_toggle(LEDS_YELLOW); +} +/*---------------------------------------------------------------------------*/ +static void +res_post_handler_orange(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + leds_toggle(LEDS_ORANGE); +} +/*---------------------------------------------------------------------------*/ +/* Toggles the yellow led */ +RESOURCE(res_toggle_yellow, + "title=\"Yellow LED\";rt=\"Control\"", + NULL, + res_post_handler_yellow, + NULL, + NULL); + +/* Toggles the orange led */ +RESOURCE(res_toggle_orange, + "title=\"Orange LED\";rt=\"Control\"", + NULL, + res_post_handler_orange, + NULL, + NULL); +#endif +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/examples/cc26xx/project-conf.h b/examples/cc26xx/project-conf.h new file mode 100644 index 000000000..97bb52591 --- /dev/null +++ b/examples/cc26xx/project-conf.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * 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 copyright holder 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 PROJECT_CONF_H_ +#define PROJECT_CONF_H_ +/*---------------------------------------------------------------------------*/ +/* Disable button shutdown functionality */ +#define BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN 0 +/*---------------------------------------------------------------------------*/ +/* Change to match your configuration */ +#define NETSTACK_CONF_RDC contikimac_driver +#define IEEE802154_CONF_PANID 0xABCD +#define CC26XX_RF_CONF_CHANNEL 25 +#define CC26XX_MODEL_CONF_CPU_VARIANT 2650 /* CC2650 */ +#define CC26XX_RF_CONF_BLE_SUPPORT 1 /* Only available with CC2650 */ +/*---------------------------------------------------------------------------*/ +#endif /* PROJECT_CONF_H_ */ +/*---------------------------------------------------------------------------*/ From fe6bbef69de06b6e363b6c0e7d168762bf2a7adb Mon Sep 17 00:00:00 2001 From: Jonas Olsson Date: Wed, 25 Feb 2015 13:16:07 +0100 Subject: [PATCH 6/7] Add CC26xx build artifacts to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7286a98ba..d8874dffb 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ *.c128 *.c64 *.cc2538dk +*.srf06-cc26xx *.ev-aducrf101mkxz *.report summary From 8bf35f9e4ae4ffb11ac9358eb98680f8dbfdfde7 Mon Sep 17 00:00:00 2001 From: Jonas Olsson Date: Wed, 25 Feb 2015 13:16:27 +0100 Subject: [PATCH 7/7] Add cc26xx travis tests --- .travis.yml | 7 +++++++ regression-tests/18-compile-arm-ports/Makefile | 1 + 2 files changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 386829b5d..03559d8a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,6 +48,13 @@ before_script: arm-none-eabi-gcc --version ; fi + ## Download and extract cc26xxware + - if [ ${BUILD_ARCH:-0} = arm ] ; then + wget http://www.ti.com/lit/sw/swrc296/swrc296.zip && + unzip swrc296.zip && + export TI_CC26XXWARE=cc26xxware_2_20_06_14829 ; + fi + ## Install RL78 GCC toolchain - sudo apt-get install libncurses5:i386 zlib1g:i386 - $WGET http://adamdunkels.github.io/contiki-fork/gnurl78-v13.02-elf_1-2_i386.deb && diff --git a/regression-tests/18-compile-arm-ports/Makefile b/regression-tests/18-compile-arm-ports/Makefile index 2de35da6d..4389c6d86 100644 --- a/regression-tests/18-compile-arm-ports/Makefile +++ b/regression-tests/18-compile-arm-ports/Makefile @@ -7,6 +7,7 @@ ipv6/rpl-border-router/ev-aducrf101mkxz \ webserver-ipv6/ev-aducrf101mkxz \ ipv6/multicast/ev-aducrf101mkxz \ cc2538dk/sniffer/ev-aducrf101mkxz \ +cc26xx/cc26xx-web-demo/srf06-cc26xx \ TOOLS=