port: new nrf5 port with Cinnamon Controller/Link Layer

This commit is contained in:
Matthias Ringwald 2021-04-21 09:37:22 +02:00
parent aff7b6f1f5
commit 0138fcae13
13 changed files with 6210 additions and 0 deletions

View File

@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 3.14)
project(n5rf-ll)
# nrf5 sdk root with nrfx
set(NRF5_SDK_ROOT /Users/mringwal/Projects/Nordic/nRF5_SDK_17.0.2_d674dde)
# list all sources
file(GLOB_RECURSE btstack_src_src ../../src/*.c)
file(GLOB_RECURSE btstack_src_hdr ../../src/*.h)
file(GLOB_RECURSE btstack_embedded_src ../../platform/embedded/src/*.c)
file(GLOB_RECURSE btstack_embedded_hdr ../../platform/embedded/src/*.h)
file(GLOB_RECURSE btstack_port_src *.c)
file(GLOB_RECURSE btstack_port_hdr *.h)
file(GLOB_RECURSE nrfx_src ${NRF5_SDK_ROOT}/modules/*.c)
file(GLOB_RECURSE nrfx_hdr ${NRF5_SDK_ROOT}/modules/*.h)
# Compile pca10040 port using Make
find_program(MAKE make)
add_custom_target(pca10040
ALL
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/pca10040/armgcc
COMMAND ${MAKE}
SOURCES ${btstack_src_src} ${btstack_embedded_src} ${btstack_port_src} ${nrfx_src})
# fake executable for navigation in IDE
add_executable(port EXCLUDE_FROM_ALL
${btstack_src_src}
${btstack_src_hdr}
${btstack_embedded_src}
${btstack_embedded_hdr}
${btstack_port_src}
${btstack_port_hdr}
${nrfx_src}
${nrfx_hdr}
)
target_include_directories(port SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/port
../../3rd-party/segger-rtt
../../platform/embedded
../../chipset/controller
../../src
${NRF5_SDK_ROOT}/components/boards
${NRF5_SDK_ROOT}/components/libraries/delay
${NRF5_SDK_ROOT}/modules/nrfx/drivers/include
${NRF5_SDK_ROOT}/modules/nrfx/mdk
${NRF5_SDK_ROOT}/modules/nrfx/hal
${NRF5_SDK_ROOT}/modules/nrfx
)

View File

@ -0,0 +1,25 @@
# BTstack port with Cinnamon for Nordic nRF5 Series
*Cinnamon* is BlueKitchen's minimal, yet robust Controller/Link Layer implementation for use with BTstack.
In contrast to common Link Layer implementations, our focus is on a robust and compact implementation for production use,
where code size matters (e.g. current code size about 8 kB).
## Status
The current implementation supports a single Peripheral role, or, passive scanning in Observer role. In the Peripheral role,
channel map updates, as well as connection param updates are supported.
Support for LE Central Role as well as Encryption is planned but not supported yet.
## Requirements
- arm-none-eabi toolchain
- Nordic's nRF5-SDK
## Supported Hardware
All nNRF5x SOCs. Built files are provided for PCA10040 (52832 DK), but others can be supported with minimal changes.
## Use
- Provide path to nRF5-SDK either in `NRF5_SDK_ROOT` environment variable or directly in `pca10040/armgcc/Makefile`.
- run make
- All supported examples are built in the `build` folder.
- You can use Segger's OZONE with the provided `EXAMPLE.jdebug` project file to flash and run the examples.

174
port/nrf5-cinnamon/main.c Normal file
View File

@ -0,0 +1,174 @@
/**
* Copyright (c) 2014 - 2020, Nordic Semiconductor ASA
*
* 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, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, 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 Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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
*
* @defgroup blinky_example_main main.c
* @{
* @ingroup blinky_example
* @brief Blinky Example Application main file.
*
* This file contains the source code for a sample application to blink LEDs.
*
*/
#include <stdbool.h>
#include <stdint.h>
#include "boards.h"
#include "SEGGER_RTT.h"
#include "nrf.h"
#include "nrf52.h"
#include "nrf_delay.h"
#include "nrfx_clock.h"
#include "hal_timer.h"
#include "radio.h"
#include <stdio.h>
#include "btstack_memory.h"
#include "btstack_run_loop_embedded.h"
#include "controller.h"
#include "btstack_tlv.h"
#include "btstack_tlv_none.h"
#include "ble/le_device_db_tlv.h"
#include "hci_dump.h"
#include "hci_dump_segger_rtt_stdout.h"
#include "hci_dump_segger_rtt_binary.h"
#include "hci_dump_embedded_stdout.h"
void btstack_assert_failed(const char * file, uint16_t line_nr){
printf("Assert: file %s, line %u\n", file, line_nr);
while (1);
}
/** hal_time_ms.h */
#include "hal_time_ms.h"
extern uint32_t hal_timer_get_ticks(void);
uint32_t hal_time_ms(void){
uint32_t ticks = hal_timer_get_ticks();
uint32_t seconds = ticks >> 15; // / 32768
uint32_t remaining_ms = (ticks & 0x7fff) * 1000 / 32768;
return seconds * 1000 + remaining_ms;
}
/** hal_cpu.h */
// TODO: implement
void hal_cpu_disable_irqs(void){
__disable_irq();
}
void hal_cpu_enable_irqs(void){
__enable_irq();
}
void hal_cpu_enable_irqs_and_sleep(void){
__enable_irq();
// __asm__("wfe"); // go to sleep if event flag isn't set. if set, just clear it. IRQs set event flag
}
static void lf_clock_init(void) {
// select 32.768 kHz XTAL as LF Clock source and start
NRF_CLOCK->LFCLKSRC = NRF_CLOCK_LFCLK_Xtal;
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
NRF_CLOCK->TASKS_LFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0);
}
void btstack_main(void);
int main(void){
// system init
lf_clock_init();
hal_timer_init();
#if 0
// get startup time, around 9 ticks and verify that we don't need to wait until it's disabled
uint32_t t0 = hal_timer_get_ticks();
radio_hf_clock_enable(true);
uint32_t t1 = hal_timer_get_ticks();
radio_hf_clock_disable();
radio_hf_clock_enable(true);
radio_hf_clock_disable();
printf("HF Startup time: %lu ticks\n", t1-t0);
#endif
btstack_memory_init();
btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
// initialize controller
controller_init();
// get virtual HCI transpoft
const hci_transport_t * hci_transport = controller_get_hci_transport();
// TODO: use flash storage
const btstack_tlv_t * btstack_tlv_impl = btstack_tlv_none_init_instance();
// setup global tlv
btstack_tlv_set_instance(btstack_tlv_impl, NULL);
// setup LE Device DB using TLV
le_device_db_tlv_configure(btstack_tlv_impl, NULL);
// init HCI
hci_init(hci_transport, NULL);
// uncomment to enable packet logger
#ifdef ENABLE_SEGGER_RTT
// hci_dump_init(hci_dump_segger_rtt_stdout_get_instance());
// hci_dump_segger_rtt_binary_open(HCI_DUMP_PACKETLOGGER);
// hci_dump_init(hci_dump_segger_rtt_binary_get_instance());
#else
// hci_dump_init(hci_dump_embedded_stdout_get_instance());
#endif
// hand over to btstack embedded code
btstack_main();
// go
btstack_run_loop_execute();
while (1){};}
/**
*@}
**/

View File

@ -0,0 +1,213 @@
NRF5_SDK_ROOT ?= /Users/mringwal/Projects/Nordic/nRF5_SDK_17.0.2_d674dde
# unset GNU_INSTALL_ROOT will select arm-none-eabi-gcc from your PATH
export GNU_INSTALL_ROOT ?=
OUTPUT_DIRECTORY := build
SDK_ROOT := $(NRF5_SDK_ROOT)
PROJ_DIR := ../..
BTSTACK_ROOT = ../../../..
LINKER_SCRIPT := nrf52832_xxaa.ld
# Source files common to all targets
C_SOURCES = \
$(SDK_ROOT)/modules/nrfx/mdk/system_nrf52.c \
$(SDK_ROOT)/components/boards/boards.c \
$(PROJ_DIR)/main.c \
$(PROJ_DIR)/port/hal_timer_nrf5.c \
$(PROJ_DIR)/port/ll_nrf5.c \
$(PROJ_DIR)/port/radio_nrf5.c \
${BTSTACK_ROOT}/3rd-party/micro-ecc/uECC.c \
${BTSTACK_ROOT}/3rd-party/rijndael/rijndael.c \
${BTSTACK_ROOT}/3rd-party/segger-rtt/SEGGER_RTT.c \
${BTSTACK_ROOT}/3rd-party/segger-rtt/SEGGER_RTT_printf.c \
${BTSTACK_ROOT}/3rd-party/segger-rtt/SEGGER_RTT_Syscalls_GCC.c \
${BTSTACK_ROOT}/chipset/controller/controller.c \
${BTSTACK_ROOT}/chipset/controller/hci_event.c \
${BTSTACK_ROOT}/chipset/controller/hopping.c \
${BTSTACK_ROOT}/platform/embedded/btstack_run_loop_embedded.c \
${BTSTACK_ROOT}/platform/embedded/hci_dump_embedded_stdout.c \
${BTSTACK_ROOT}/platform/embedded/hci_dump_segger_rtt_stdout.c \
${BTSTACK_ROOT}/platform/embedded/hci_dump_segger_rtt_binary.c \
${BTSTACK_ROOT}/src/ad_parser.c \
${BTSTACK_ROOT}/src/ble/att_db.c \
${BTSTACK_ROOT}/src/ble/att_dispatch.c \
${BTSTACK_ROOT}/src/ble/att_server.c \
${BTSTACK_ROOT}/src/ble/gatt-service/ancs_client.c \
${BTSTACK_ROOT}/src/ble/gatt-service/battery_service_client.c \
${BTSTACK_ROOT}/src/ble/gatt-service/battery_service_server.c \
${BTSTACK_ROOT}/src/ble/gatt-service/device_information_service_client.c \
${BTSTACK_ROOT}/src/ble/gatt-service/device_information_service_server.c \
${BTSTACK_ROOT}/src/ble/gatt-service/hids_device.c \
${BTSTACK_ROOT}/src/ble/gatt_client.c \
${BTSTACK_ROOT}/src/ble/le_device_db_memory.c \
${BTSTACK_ROOT}/src/ble/le_device_db_tlv.c \
${BTSTACK_ROOT}/src/ble/sm.c \
${BTSTACK_ROOT}/src/btstack_audio.c \
${BTSTACK_ROOT}/src/btstack_crypto.c \
${BTSTACK_ROOT}/src/btstack_hid_parser.c \
${BTSTACK_ROOT}/src/btstack_linked_list.c \
${BTSTACK_ROOT}/src/btstack_linked_queue.c \
${BTSTACK_ROOT}/src/btstack_memory.c \
${BTSTACK_ROOT}/src/btstack_memory_pool.c \
${BTSTACK_ROOT}/src/btstack_resample.c \
${BTSTACK_ROOT}/src/btstack_ring_buffer.c \
${BTSTACK_ROOT}/src/btstack_run_loop.c \
${BTSTACK_ROOT}/src/btstack_tlv.c \
${BTSTACK_ROOT}/src/btstack_tlv_none.c \
${BTSTACK_ROOT}/src/btstack_util.c \
${BTSTACK_ROOT}/src/hci.c \
${BTSTACK_ROOT}/src/hci_cmd.c \
${BTSTACK_ROOT}/src/hci_dump.c \
${BTSTACK_ROOT}/src/hci_transport_h4.c \
${BTSTACK_ROOT}/src/l2cap.c \
${BTSTACK_ROOT}/src/l2cap_signaling.c \
ASM_SOURCES= \
$(SDK_ROOT)/modules/nrfx/mdk/gcc_startup_nrf52.S \
# Include folders common to all targets
INC_FOLDERS += \
$(PROJ_DIR) \
$(SDK_ROOT)/integration/nrfx \
$(SDK_ROOT)/integration/nrfx/legacy \
$(SDK_ROOT)/modules/nrfx \
$(SDK_ROOT)/modules/nrfx/hal \
$(SDK_ROOT)/modules/nrfx/mdk \
$(SDK_ROOT)/modules/nrfx/drivers/include/ \
$(SDK_ROOT)/components/boards \
$(SDK_ROOT)/components/drivers_nrf/nrf_soc_nosd \
$(SDK_ROOT)/components/libraries/bsp \
$(SDK_ROOT)/components/toolchain/cmsis/include \
$(SDK_ROOT)/components/libraries/util \
$(SDK_ROOT)/components/libraries/delay \
$(SDK_ROOT)/components/libraries/log/ \
$(SDK_ROOT)//components/libraries/log/src \
$(SDK_ROOT)/components/libraries/experimental_section_vars/ \
$(BTSTACK_ROOT)/3rd-party/segger-rtt \
$(BTSTACK_ROOT)/chipset/controller \
$(BTSTACK_ROOT)/src/ \
$(BTSTACK_ROOT)/platform/embedded \
../config \
../../port \
$(OUTPUT_DIRECTORY)\
# Libraries common to all targets
LIB_FILES += \
# Optimization flags
# OPT = -O3 -g3
# Uncomment the line below to enable link time optimization
#OPT += -flto
OPT = -Os -g
# C flags common to all targets
CFLAGS += $(OPT)
CFLAGS += -DBOARD_PCA10040
CFLAGS += -DBSP_DEFINES_ONLY
CFLAGS += -DCONFIG_GPIO_AS_PINRESET
CFLAGS += -DFLOAT_ABI_HARD
CFLAGS += -DNRF52
CFLAGS += -DNRF52832_XXAA
CFLAGS += -DNRF52_PAN_74
CFLAGS += -mcpu=cortex-m4
CFLAGS += -mthumb -mabi=aapcs
CFLAGS += -Wall -Werror
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
# keep every function in a separate section, this allows linker to discard unused ones
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin -fshort-enums
# C++ flags common to all targets
CXXFLAGS += $(OPT)
# Assembler flags common to all targets
ASMFLAGS += -g3
ASMFLAGS += -mcpu=cortex-m4
ASMFLAGS += -mthumb -mabi=aapcs
ASMFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
ASMFLAGS += -DBOARD_PCA10040
ASMFLAGS += -DBSP_DEFINES_ONLY
ASMFLAGS += -DCONFIG_GPIO_AS_PINRESET
ASMFLAGS += -DFLOAT_ABI_HARD
ASMFLAGS += -DNRF52
ASMFLAGS += -DNRF52832_XXAA
ASMFLAGS += -DNRF52_PAN_74
# Linker flags
LDFLAGS += $(OPT)
LDFLAGS += -mthumb -mabi=aapcs -L$(SDK_ROOT)/modules/nrfx/mdk -T$(LINKER_SCRIPT)
LDFLAGS += -mcpu=cortex-m4
LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
# let linker dump unused sections
LDFLAGS += -Wl,--gc-sections
# use newlib in nano version
LDFLAGS += --specs=nano.specs
# Add standard libraries at the very end of the linker input, after all objects
# that may need symbols provided by these libraries.
LIB_FILES += -lc -lnosys -lm
# default action: build all
EXAMPLES = \
gap_le_advertisements \
gatt_counter \
gatt_streamer_server \
# require encryption:
# hog_keyboard_demo \
# hog_mouse_demo \
# sm_pairing_peripheral \
GATT_FILES = \
gatt_counter.gatt \
gatt_streamer_server.gatt \
# require encryption:
# hog_keyboard_demo.gatt \
# hog_mouse_demo.gatt \
# sm_pairing_peripheral.gatt \
# list of objects
OBJECTS = $(addprefix $(OUTPUT_DIRECTORY)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
# list of ASM program objects
OBJECTS += $(addprefix $(OUTPUT_DIRECTORY)/,$(notdir $(ASM_SOURCES:.S=.o)))
vpath %.S $(sort $(dir $(ASM_SOURCES)))
.PHONY: default help
# VPATH to find .gatt files
VPATH += ${BTSTACK_ROOT}/example
# Default target - first one defined
default: \
$(OBJECTS) \
$(addprefix $(OUTPUT_DIRECTORY)/,$(GATT_FILES:.gatt=.h)) \
$(addprefix $(OUTPUT_DIRECTORY)/,$(EXAMPLES:=.elf)) \
$(addprefix $(OUTPUT_DIRECTORY)/,$(EXAMPLES:=.jdebug)) \
$(OUTPUT_DIRECTORY)/%.h: %.gatt | $(OUTPUT_DIRECTORY)
python3 ${BTSTACK_ROOT}/tool/compile_gatt.py $< $@
$(OUTPUT_DIRECTORY)/%.o: %.S Makefile | $(OUTPUT_DIRECTORY)
$(CC) -c $(ASMFLAGS) $< -o $@
$(OUTPUT_DIRECTORY)/%.o: %.c Makefile | $(OUTPUT_DIRECTORY)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(OUTPUT_DIRECTORY)/$(notdir $(<:.c=.lst)) $< -o $@
$(OUTPUT_DIRECTORY)/%.elf: Makefile $(OBJECTS) $(OUTPUT_DIRECTORY)/%.o
$(CC) $(filter-out Makefile,$^) $(LDFLAGS) -o $@
$(SIZE) $@
$(OUTPUT_DIRECTORY)/%.jdebug: ozone.jdebug | $(OUTPUT_DIRECTORY)
sed -e "s|EXAMPLE|$(basename $(notdir $@))|" $< > $@
TEMPLATE_PATH := $(SDK_ROOT)/components/toolchain/gcc
include $(TEMPLATE_PATH)/Makefile.common
# Convert INC_FOLDERS into CFLAGS
$(eval INC_PATHS := $(call get_inc_paths, $(INC_FOLDERS) $(call target_specific, INC_FOLDERS, $(1))))
CFLAGS += ${INC_PATHS}

View File

@ -0,0 +1,64 @@
/* Linker script to configure memory regions. */
SEARCH_DIR(.)
GROUP(-lgcc -lc -lnosys)
MEMORY
{
FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x80000
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000
}
SECTIONS
{
}
SECTIONS
{
. = ALIGN(4);
.mem_section_dummy_ram :
{
}
.log_dynamic_data :
{
PROVIDE(__start_log_dynamic_data = .);
KEEP(*(SORT(.log_dynamic_data*)))
PROVIDE(__stop_log_dynamic_data = .);
} > RAM
.log_filter_data :
{
PROVIDE(__start_log_filter_data = .);
KEEP(*(SORT(.log_filter_data*)))
PROVIDE(__stop_log_filter_data = .);
} > RAM
} INSERT AFTER .data;
SECTIONS
{
.mem_section_dummy_rom :
{
}
.log_const_data :
{
PROVIDE(__start_log_const_data = .);
KEEP(*(SORT(.log_const_data*)))
PROVIDE(__stop_log_const_data = .);
} > FLASH
.log_backends :
{
PROVIDE(__start_log_backends = .);
KEEP(*(SORT(.log_backends*)))
PROVIDE(__stop_log_backends = .);
} > FLASH
.nrf_balloc :
{
PROVIDE(__start_nrf_balloc = .);
KEEP(*(.nrf_balloc))
PROVIDE(__stop_nrf_balloc = .);
} > FLASH
} INSERT AFTER .text
INCLUDE "nrf_common.ld"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
//
// btstack_config.h for nRF5x LL
//
#ifndef BTSTACK_CONFIG_H
#define BTSTACK_CONFIG_H
// Port related features
#define HAVE_EM9304_PATCH_CONTAINER
#define HAVE_EMBEDDED_TIME_MS
// BTstack features that can be enabled
#define ENABLE_BLE
#define ENABLE_LE_CENTRAL
#define ENABLE_LE_DATA_LENGTH_EXTENSION
#define ENABLE_LE_PERIPHERAL
#define ENABLE_LOG_ERROR
#define ENABLE_LOG_INFO
#define ENABLE_PRINTF_HEXDUMP
#define ENABLE_SEGGER_RTT
#define ENABLE_BTSTACK_ASSERT
// BTstack configuration. buffers, sizes, ...
#define HCI_ACL_PAYLOAD_SIZE 100
#define MAX_NR_GATT_CLIENTS 1
#define MAX_NR_HCI_CONNECTIONS 1
#define MAX_NR_L2CAP_CHANNELS 1
#define MAX_NR_L2CAP_SERVICES 1
#define MAX_NR_SM_LOOKUP_ENTRIES 3
#define MAX_NR_WHITELIST_ENTRIES 1
// LE Device DB using TLV on top of Flash Sector interface
#define NVM_NUM_DEVICE_DB_ENTRIES 16
// GPIO debugging
#define DEBUG_PIN_HF_CLOCK 19
#define DEBUG_PIN_ADDRESS 20
#define DEBUG_PIN_RX 22
#define DEBUG_PIN_TX 23
#define DEBUG_PIN_RADIO_IRQ 24
#endif

View File

@ -0,0 +1,51 @@
/*******************************************************************************
 *
 *      Copyright (c) 2018, Raccon BLE Sniffer
 *      All rights reserved.
 *
 *      Redistribution and use in source and binary forms, with or without
 *      modification, are permitted provided that the following conditions are
 *      met:
 *      
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following disclaimer
 *        in the documentation and/or other materials provided with the
 *        distribution.
 *      * Neither the name of "btlejack2" 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 _DEBUG_H_
#define _DEBUG_H_
#ifdef DEBUG
#include "SEGGER_RTT.h"
#define printf(...) SEGGER_RTT_printf( 0, __VA_ARGS__ )
#define LOG_DBG(...) SEGGER_RTT_printf( 0, __VA_ARGS__ )
#define log(format, ...) SEGGER_RTT_printf(0, "[%010u] " format, __HAL_TIM_GET_COUNTER(&htim2), ## __VA_ARGS__)
#else
#define printf(...)
#define LOG_DBG(...)
#endif
#endif // _DEBUG_H_

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2020 BlueKitchen GmbH
*
* 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 holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS
* RINGWALD 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.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
/*
* hci_event.h
*/
#ifndef HAL_TIMER_H
#define HCI_TIMER_H
#include "bluetooth.h"
#include <stdint.h>
#include <stdarg.h>
#if defined __cplusplus
extern "C" {
#endif
/*
* @brief Initialize 32.768 kHz timer, usually low power and used by RTC and in deep sleep
*/
void hal_timer_init(void);
/*
* @brief Set Timer Callback
* @param callback
*/
void hal_timer_set_callback(void (*callback)(void));
/**
* @brief Get current ticks
* @return num_ticks
*/
uint32_t hal_timer_get_ticks(void);
/**
* @brief Stop Timer
*/
void hal_timer_stop(void);
/**
* @brief Start Timer and fire at given timeout
* @param timeout_ticks timeout in ticks
*/
void hal_timer_start(uint32_t timeout_ticks);
#if defined __cplusplus
}
#endif
#endif // HAL_TIMER_H

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2020 BlueKitchen GmbH
*
* 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 holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS
* RINGWALD 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.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "hal_timer_nrf5.c"
/*
* hal_timer.c
* HAL for 32.768 kHz low power timer with 16 bit resolution
* @note Only uses one of multiple RTCs and only a single Capture-Compare unit
*/
#include "hal_timer.h"
#include "btstack_debug.h"
#include "nrf.h"
static void (*hal_timer_callback)(void);
void RTC0_IRQHandler(void){
if (NRF_RTC0->EVENTS_COMPARE[0]){
NRF_RTC0->EVENTS_COMPARE[0] = 0;
btstack_assert(hal_timer_callback != NULL);
(*hal_timer_callback)();
}
}
void hal_timer_init(void) {
/* Stop the timer first */
NRF_RTC0->TASKS_STOP = 1;
NRF_RTC0->TASKS_CLEAR = 1;
/* Always no prescaler */
NRF_RTC0->PRESCALER = 0;
/* Clear overflow events and set overflow interrupt */
NRF_RTC0->EVENTS_OVRFLW = 0;
NRF_RTC0->INTENSET = RTC_INTENSET_OVRFLW_Msk;
/* Start the timer */
NRF_RTC0->TASKS_START = 1;
/* Set isr in vector table and enable interrupt */
NVIC_EnableIRQ( RTC0_IRQn );
}
void hal_timer_set_callback(void (*callback)(void)){
hal_timer_callback = callback;
}
uint32_t hal_timer_get_ticks(void){
return NRF_RTC0->COUNTER;
}
void hal_timer_stop(void){
NRF_RTC0->INTENCLR =RTC_INTENCLR_COMPARE0_Msk;
}
void hal_timer_start(uint32_t timeout_ticks){
NRF_RTC0->CC[0] = timeout_ticks & 0x00ffffff;
NRF_RTC0->EVENTS_COMPARE[0] = 0;
NRF_RTC0->INTENSET =RTC_INTENSET_COMPARE0_Msk;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,138 @@
/*
* Copyright (C) 2020 BlueKitchen GmbH
*
* 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 holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS
* RINGWALD 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.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
/*
* radio.h
*/
#ifndef RADIO_H
#define RADIO_H
#include "btstack_bool.h"
#include <stdint.h>
#if defined __cplusplus
extern "C" {
#endif
/* API_START */
typedef struct {
void (*tx_done)(void);
void (*rx_done)(void);
} radio_callbacks_t;
typedef enum {
RADIO_TRANSITION_TX_ONLY,
RADIO_TRANSITION_TX_TO_RX,
} radio_transition_t;
typedef enum {
RADIO_RESULT_OK,
RADIO_RESULT_CRC_ERROR,
RADIO_RESULT_TIMEOUT,
} radio_result_t;
typedef void (*radio_callback_t)(radio_result_t result);
/**
* Init radio
*/
void radio_init(void);
/**
* Set Access Address
* @param access_address
*/
void radio_set_access_address(uint32_t access_address);
/**
* Enable RF CLock
* @param wait_until_ready if true, waits until HF clock is ready
*/
void radio_hf_clock_enable(bool wait_until_ready);
/**
* Disable RF CLock
*/
void radio_hf_clock_disable(void);
/**
* Set CRC Init value
* @param crc 24-bit init value
*/
void radio_set_crc_init(uint32_t crc);
/**
* Set Channel: frequency and whitening
* @param channel 0..39
*/
void radio_set_channel(uint8_t channel);
/**
* Transmit packet.
* @param callback
* @param transition - on RADIO_TRANSITION_TX_TO_RX, radio transitions to RX
* @param packet
* @param len
*/
void radio_transmit(radio_callback_t callback, radio_transition_t transition, const uint8_t * packet, uint16_t len);
/**
* Receive packet
* @note automatic transition to TX
* @param callback
* @param timeout_us if radio was disabled before (i.e. not in tx -> rx transition)
* @param buffer
* @param len
* @param rssi (out)
*/
void radio_receive(radio_callback_t callback, uint32_t timeout_us, uint8_t * buffer, uint16_t len, int8_t * rssi);
/**
* Stop active transmission, e.g. tx after rx
* @param callback
*/
void radio_stop(radio_callback_t callback);
/* API_END */
#if defined __cplusplus
}
#endif
#endif // LL_H

View File

@ -0,0 +1,546 @@
/*
* Copyright (C) 2021 BlueKitchen GmbH
*
* 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 holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS
* RINGWALD 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.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "radio_nrf5.c"
#include "radio.h"
#include "btstack_debug.h"
#include <inttypes.h>
#include "nrf.h"
#include "nrf52.h"
#include "nrf_gpio.h"
#include "nrf_gpiote.h"
#define MAXLEN 37
static enum {
RADIO_OFF,
RADIO_DISABLED,
RADIO_W2_TX,
RADIO_W4_TX_DONE,
RADIO_W4_TX_TO_RX,
RADIO_W2_RX,
RADIO_W4_RX_DONE,
RADIO_W4_RX_TIMEOUT,
RADIO_W4_DISABLED,
} volatile radio_state;
static radio_callback_t radio_callback;
static int8_t * rssi_buffer;
// channel table: freq in hertz and whitening seed
static const struct {
uint8_t freq_index;
uint8_t whitening;
} channel_table[] = {
{ 4, 0x01 /* 00000001 */ },
{ 6, 0x41 /* 01000001 */ },
{ 8, 0x21 /* 00100001 */ },
{ 10, 0x61 /* 01100001 */ },
{ 12, 0x11 /* 00010001 */ },
{ 14, 0x51 /* 01010001 */ },
{ 16, 0x31 /* 00110001 */ },
{ 18, 0x71 /* 01110001 */ },
{ 20, 0x09 /* 00001001 */ },
{ 22, 0x49 /* 01001001 */ },
{ 24, 0x29 /* 00101001 */ },
{ 28, 0x69 /* 01101001 */ },
{ 30, 0x19 /* 00011001 */ },
{ 32, 0x59 /* 01011001 */ },
{ 34, 0x39 /* 00111001 */ },
{ 36, 0x79 /* 01111001 */ },
{ 38, 0x05 /* 00000101 */ },
{ 40, 0x45 /* 01000101 */ },
{ 42, 0x25 /* 00100101 */ },
{ 44, 0x65 /* 01100101 */ },
{ 46, 0x15 /* 00010101 */ },
{ 48, 0x55 /* 01010101 */ },
{ 50, 0x35 /* 00110101 */ },
{ 52, 0x75 /* 01110101 */ },
{ 54, 0x0d /* 00001101 */ },
{ 56, 0x4d /* 01001101 */ },
{ 58, 0x2d /* 00101101 */ },
{ 60, 0x6d /* 01101101 */ },
{ 62, 0x1d /* 00011101 */ },
{ 64, 0x5d /* 01011101 */ },
{ 66, 0x3d /* 00111101 */ },
{ 68, 0x7d /* 01111101 */ },
{ 70, 0x03 /* 00000011 */ },
{ 72, 0x43 /* 01000011 */ },
{ 74, 0x23 /* 00100011 */ },
{ 76, 0x63 /* 01100011 */ },
{ 78, 0x13 /* 00010011 */ },
{ 2, 0x53 /* 01010011 */ },
{ 26, 0x33 /* 00110011 */ },
{ 80, 0x73 /* 01110011 */ },
};
void radio_init(void){
radio_state = RADIO_OFF;
/* TIMER0 setup */
NRF_TIMER0->TASKS_STOP = 1;
NRF_TIMER0->TASKS_SHUTDOWN = 1;
NRF_TIMER0->BITMODE = 3; /* 32-bit timer */
NRF_TIMER0->MODE = 0; /* Timer mode */
NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */
// PPI setup
// Channel 0: RADIO END -> TIMER0 Start
NRF_PPI->CH[0].EEP = (uint32_t)&(NRF_RADIO->EVENTS_END);
NRF_PPI->CH[0].TEP = (uint32_t)&(NRF_TIMER0->TASKS_START);
// Channel 1: RADIO ADDRESS -> TIMER0 Stop
NRF_PPI->CH[1].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[1].TEP = (uint32_t)&(NRF_TIMER0->TASKS_STOP);
#ifdef NRF51
// Handle BLE Radio tuning parameters from production if required.
// Does not exist on NRF52
// See PCN-083.
if (NRF_FICR->OVERRIDEEN & FICR_OVERRIDEEN_BLE_1MBIT_Msk){
NRF_RADIO->OVERRIDE0 = NRF_FICR->BLE_1MBIT[0];
NRF_RADIO->OVERRIDE1 = NRF_FICR->BLE_1MBIT[1];
NRF_RADIO->OVERRIDE2 = NRF_FICR->BLE_1MBIT[2];
NRF_RADIO->OVERRIDE3 = NRF_FICR->BLE_1MBIT[3];
NRF_RADIO->OVERRIDE4 = NRF_FICR->BLE_1MBIT[4] | 0x80000000;
}
#endif // NRF51
// Mode: BLE 1 Mbps
NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit << RADIO_MODE_MODE_Pos;
// PacketConfig 0:
// ---
// LENGTH field in bits = 8
// S0 field in bytes = 1
// S1 field not used
// 8 bit preamble
NRF_RADIO->PCNF0 =
( 8 << RADIO_PCNF0_LFLEN_Pos ) |
( 1 << RADIO_PCNF0_S0LEN_Pos ) |
( 0 << RADIO_PCNF0_S1LEN_Pos );
// PacketConfig 1:
// ---
// Payload MAXLEN = MAXLEN
// No additional bytes
// 4 address bytes (1 + 3)
// S0, LENGTH, S1, PAYLOAD in little endian
// Packet whitening enabled
NRF_RADIO->PCNF1 =
( MAXLEN << RADIO_PCNF1_MAXLEN_Pos) |
( 0 << RADIO_PCNF1_STATLEN_Pos ) |
( 3 << RADIO_PCNF1_BALEN_Pos ) |
( RADIO_PCNF1_ENDIAN_Little << RADIO_PCNF1_ENDIAN_Pos ) |
( RADIO_PCNF1_WHITEEN_Enabled << RADIO_PCNF1_WHITEEN_Pos );
// Use logical address 0 for sending and receiving
NRF_RADIO->TXADDRESS = 0;
NRF_RADIO->RXADDRESSES = 1 << 0;
// 24 bit CRC, skip address field
NRF_RADIO->CRCCNF =
( RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos ) |
( RADIO_CRCCNF_LEN_Three << RADIO_CRCCNF_LEN_Pos );
// The polynomial has the form of x^24 +x^10 +x^9 +x^6 +x^4 +x^3 +x+1
NRF_RADIO->CRCPOLY = 0x100065B;
// Inter frame spacing 150 us
NRF_RADIO->TIFS = 150;
// Transmit with max power
NRF_RADIO->TXPOWER = (RADIO_TXPOWER_TXPOWER_Pos4dBm << RADIO_TXPOWER_TXPOWER_Pos);
// Disable all interrupts
NRF_RADIO->INTENCLR = 0xffffffff;
// enable Radio IRQs
NVIC_SetPriority( RADIO_IRQn, 0 );
NVIC_ClearPendingIRQ( RADIO_IRQn );
NVIC_EnableIRQ( RADIO_IRQn );
#ifdef DEBUG_PIN_HF_CLOCK
// debug pins
nrf_gpio_cfg_output(DEBUG_PIN_HF_CLOCK);
nrf_gpio_cfg_output(DEBUG_PIN_ADDRESS);
nrf_gpio_cfg_output(DEBUG_PIN_RX);
nrf_gpio_cfg_output(DEBUG_PIN_TX);
nrf_gpio_cfg_output(DEBUG_PIN_RADIO_IRQ);
// toggle DEBUG_PIN_ADDRESS on RADIO ADDRESS event. Use PPI Channel 19 and GPIOT[0]
// NOTE: unclear how pin could be cleared after set on address.
nrf_gpiote_task_configure(0, DEBUG_PIN_ADDRESS, GPIOTE_CONFIG_POLARITY_Toggle, NRF_GPIOTE_INITIAL_VALUE_LOW);
nrf_gpiote_task_enable(0);
NRF_PPI->CH[19].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[19].TEP = nrf_gpiote_task_addr_get(0);
NRF_PPI->CHENSET = PPI_CHEN_CH19_Msk;
#endif
}
// Enable the High Frequency clock on the processor.
static void radio_hf_clock_enable_reset(radio_result_t result){
UNUSED(result);
}
void radio_hf_clock_enable(bool wait_until_ready){
// Work around for incomplete RX
if (radio_state == RADIO_W4_RX_DONE){
#ifdef DEBUG_PIN_HF_CLOCK
nrf_gpio_pin_clear(DEBUG_PIN_HF_CLOCK);
nrf_gpio_pin_set(DEBUG_PIN_HF_CLOCK);
#endif
#if 0
// state = RX, PAYLOAD = 1, END = 0, DISABLED = 0
printf("Enable: STATE %u\n", (int) NRF_RADIO->STATE);
printf("Enable: PAYLOAD %u\n", (int) NRF_RADIO->EVENTS_PAYLOAD);
printf("Enable: END %u\n", (int) NRF_RADIO->EVENTS_END);
printf("Enable: DISABLED %u\n", (int) NRF_RADIO->EVENTS_DISABLED);
btstack_assert(false);
#else
printf("\n\nRADIO_W4_RX_DONE hang\n\n\n");
radio_stop(&radio_hf_clock_enable_reset);
radio_state = RADIO_DISABLED;
return;
#endif
}
#ifdef DEBUG_PIN_HF_CLOCK
nrf_gpio_pin_set(DEBUG_PIN_HF_CLOCK);
#endif
// the RADIO module. Without this clock, no communication is possible.
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
if (wait_until_ready){
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
}
radio_state = RADIO_DISABLED;
}
void radio_hf_clock_disable(void) {
#ifdef DEBUG_PIN_HF_CLOCK
nrf_gpio_pin_clear(DEBUG_PIN_HF_CLOCK);
#endif
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
radio_state = RADIO_OFF;
}
void radio_set_access_address(uint32_t access_address) {
NRF_RADIO->BASE0 = ( access_address << 8 ) & 0xFFFFFF00;
NRF_RADIO->PREFIX0 = ( access_address >> 24 ) & RADIO_PREFIX0_AP0_Msk;
}
void radio_set_crc_init(uint32_t crc_init){
NRF_RADIO->CRCINIT = crc_init;
}
void radio_set_channel(uint8_t channel){
// set frequency based on channel
NRF_RADIO->FREQUENCY = channel_table[channel].freq_index;
// initializes data whitening with channel index
NRF_RADIO->DATAWHITEIV = channel & 0x3F;
}
void radio_transmit(radio_callback_t callback, radio_transition_t transition, const uint8_t * packet, uint16_t len){
#ifdef DEBUG_PIN_TX
nrf_gpio_pin_set(DEBUG_PIN_TX);
#endif
uint16_t state = (uint16_t) NRF_RADIO->STATE;
switch (radio_state){
case RADIO_W2_TX:
// already in transition to tx
if (state != RADIO_STATE_STATE_TxRu){
log_info("TX Start after RX, transition %u, state 0x%04x", (int) transition, state);
btstack_assert(false);
}
break;
case RADIO_DISABLED:
if (state != RADIO_STATE_STATE_Disabled){
log_info("TX Start after Disabled, transition %u, state 0x%04x", (int) transition, state);
btstack_assert(false);
}
// start tx
NRF_RADIO->TASKS_TXEN = 1;
break;
default:
log_info("TX Start unexpected state: our state %u, transition %u, state 0x%04x", radio_state, (int) transition, state);
btstack_assert(false);
break;
}
radio_callback = callback;
// set data to send (assume it's valid until tx done)
NRF_RADIO->PACKETPTR = (uint32_t) packet;
switch (transition){
case RADIO_TRANSITION_TX_ONLY:
radio_state = RADIO_W4_TX_DONE;
NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk;
break;
case RADIO_TRANSITION_TX_TO_RX:
radio_state = RADIO_W4_TX_TO_RX;
NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_DISABLED_RXEN_Msk;
// - Clear Timer0
NRF_TIMER0->TASKS_CLEAR = 1;
// - Set CC for receive (ca. 300 us)
NRF_TIMER0->CC[1] = 300; // 300 us
NRF_TIMER0->EVENTS_COMPARE[1] = 0;
// - END -> Start Timer0
NRF_PPI->CHENSET = PPI_CHEN_CH0_Msk;
// - Timer0 CC[1] -> Radio END
NRF_PPI->CHENSET = PPI_CHEN_CH22_Msk;
// - Disable address->stop
NRF_PPI->CHENCLR = PPI_CHEN_CH1_Msk;
break;
default:
btstack_assert(false);
break;
}
NRF_RADIO->INTENCLR = 0xffffffff;
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
// Interrupt on DISABLED
NRF_RADIO->INTENSET = 0x00000010;
}
static void radio_setup_rx(void){
NRF_RADIO->EVENTS_ADDRESS = 0;
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
// PPI0: END -> Start Timer0
NRF_PPI->CHENCLR = PPI_CHEN_CH0_Msk;
// PPI1: Radio Address -> Stop Timer
NRF_PPI->CHENSET = PPI_CHEN_CH1_Msk;
// Update Shortcuts
NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_DISABLED_TXEN_Msk;
}
void radio_receive(radio_callback_t callback, uint32_t timeout_us, uint8_t * buffer, uint16_t len, int8_t * rssi){
#ifdef DEBUG_PIN_RX
nrf_gpio_pin_set(DEBUG_PIN_RX);
#endif
uint16_t state = (uint16_t) NRF_RADIO->STATE;
// log_info("RX Start: our state = 0x%0x, radio_state 0x%04x", radio_state, state);
radio_callback = callback;
rssi_buffer = rssi;
NRF_RADIO->PACKETPTR = (uint32_t) buffer;
buffer[0] = 0;
buffer[1] = 0;
switch (radio_state){
case RADIO_W2_RX:
// radio setup as part of TX->RX transition
switch (state){
case RADIO_STATE_STATE_RxRu:
case RADIO_STATE_STATE_RxIdle:
case RADIO_STATE_STATE_Rx:
break;
default:
btstack_assert(false);
break;
}
break;
case RADIO_DISABLED:
btstack_assert(state == RADIO_STATE_STATE_Disabled);
// - Stop Timer0
NRF_TIMER0->TASKS_STOP = 1;
// - Clear Timer0
NRF_TIMER0->TASKS_CLEAR = 1;
// - Set CC for receive
NRF_TIMER0->CC[1] = timeout_us;
NRF_TIMER0->EVENTS_COMPARE[1] = 0;
// - Timer0 CC[1] -> Radio Disable
NRF_PPI->CHENSET = PPI_CHEN_CH22_Msk;
// - Start Timer0
NRF_TIMER0->TASKS_START = 1;
// Start Receive
radio_setup_rx();
NRF_RADIO->TASKS_RXEN = 1;
break;
default:
log_info("RX unexpected radio_state: state 0x%04x / phy state state 0x%04x", radio_state, state);
log_info("cc[1] %" PRIu32 "events_compare[1] %u", NRF_TIMER0->CC[1], (int) NRF_TIMER0->EVENTS_COMPARE[1]);
btstack_assert(false);
break;
}
// Disable all interrupts
NRF_RADIO->INTENCLR = 0xffffffff;
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
// Interrupt on DISABLED
NRF_RADIO->INTENSET = 0x00000010;
radio_state = RADIO_W4_RX_DONE;
}
void radio_stop(radio_callback_t callback){
// log_info("Disable, state 0x%04x", (uint16_t) NRF_RADIO->STATE);
radio_callback = callback;
NRF_RADIO->SHORTS = 0;
uint16_t state = (uint16_t) NRF_RADIO->STATE;
switch (state){
case RADIO_STATE_STATE_Disabled:
(*callback)(RADIO_RESULT_OK);
break;
default:
radio_state = RADIO_W4_DISABLED;
NRF_RADIO->TASKS_DISABLE = 1;
break;
}
}
void RADIO_IRQHandler(void){
uint16_t state = (uint16_t) NRF_RADIO->STATE;
#ifdef DEBUG_PIN_RADIO_IRQ
nrf_gpio_pin_toggle(DEBUG_PIN_RADIO_IRQ);
#endif
#ifdef DEBUG_PIN_RX
nrf_gpio_pin_clear(DEBUG_PIN_RX);
#endif
#ifdef DEBUG_PIN_TX
nrf_gpio_pin_clear(DEBUG_PIN_TX);
#endif
switch (radio_state){
case RADIO_W4_TX_DONE:
// TX Done, no transition to rx requested
btstack_assert(state == RADIO_STATE_STATE_Disabled);
NRF_RADIO->EVENTS_DISABLED = 0;
radio_state = RADIO_DISABLED;
(*radio_callback)(RADIO_RESULT_OK);
break;
case RADIO_W4_TX_TO_RX:
// TX Done, transition to rx
btstack_assert(state == RADIO_STATE_STATE_RxRu);
NRF_RADIO->EVENTS_DISABLED = 0;
radio_state = RADIO_W2_RX;
radio_setup_rx();
(*radio_callback)(RADIO_RESULT_OK);
break;
case RADIO_W4_RX_DONE:
// RX Done
btstack_assert(state == RADIO_STATE_STATE_TxRu);
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_TIMER0->TASKS_STOP = 1;
// check EVENTS_COMPARE[1]
if (NRF_TIMER0->EVENTS_COMPARE[1]){
// compare event -> timeout
radio_state = RADIO_W4_RX_TIMEOUT;
NRF_RADIO->SHORTS = 0;
NRF_RADIO->TASKS_DISABLE = 1;
#ifdef DEBUG_PIN_RX
// toggle twice for timeout
nrf_gpio_pin_toggle(DEBUG_PIN_RX);
nrf_gpio_pin_toggle(DEBUG_PIN_RX);
nrf_gpio_pin_toggle(DEBUG_PIN_RX);
nrf_gpio_pin_toggle(DEBUG_PIN_RX);
#endif
} else {
// no compare event -> packet with address received
radio_state = RADIO_W2_TX;
// RSSI is stored without sign but is negative (range: 0..127)
if (rssi_buffer != NULL){
uint32_t rssi_sample = NRF_RADIO->RSSISAMPLE;
int8_t rssi;
if (rssi_sample < 128){
rssi = -rssi_sample;
} else {
rssi = -128;
}
*rssi_buffer = rssi;
}
// check CRC
radio_result_t result = ((NRF_RADIO->CRCSTATUS & RADIO_CRCSTATUS_CRCSTATUS_Msk) != 0) ? RADIO_RESULT_OK : RADIO_RESULT_CRC_ERROR;
#ifdef DEBUG_PIN_RX
// toggle once for crc error
if (result == RADIO_RESULT_CRC_ERROR){
nrf_gpio_pin_toggle(DEBUG_PIN_RX);
nrf_gpio_pin_toggle(DEBUG_PIN_RX);
}
#endif
(*radio_callback)(result);
}
break;
case RADIO_W4_RX_TIMEOUT:
// after RX Timeout, RX was started and stopped again
btstack_assert(state == RADIO_STATE_STATE_Disabled);
NRF_RADIO->EVENTS_DISABLED = 0;
radio_state = RADIO_DISABLED;
(*radio_callback)(RADIO_RESULT_TIMEOUT);
break;
case RADIO_W4_DISABLED:
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->INTENCLR = 0xffffffff;
radio_state = RADIO_DISABLED;
(*radio_callback)(RADIO_RESULT_OK);
break;
default:
log_info("IRQ: our state = 0x%0x, radio_state 0x%04x", radio_state, state);
btstack_assert(false);
break;
}
#ifdef DEBUG_PIN_RADIO_IRQ
nrf_gpio_pin_toggle(DEBUG_PIN_RADIO_IRQ);
#endif
}