mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-01-01 00:28:18 +00:00
626 lines
18 KiB
C
Executable File
626 lines
18 KiB
C
Executable File
/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
|
|
*
|
|
* The information contained herein is property of Nordic Semiconductor ASA.
|
|
* Terms and conditions of usage are described in detail in NORDIC
|
|
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
|
|
*
|
|
* Licensees are granted free, non-transferable use of the information. NO
|
|
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
|
|
* the file.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* BTstack Link Layer implementation
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "app_uart.h"
|
|
#include "app_error.h"
|
|
#include "nrf_delay.h"
|
|
#include "nrf.h"
|
|
#include "bsp.h"
|
|
|
|
#include "btstack_config.h"
|
|
#include "btstack_debug.h"
|
|
#include "btstack_memory.h"
|
|
#include "btstack_run_loop.h"
|
|
#include "btstack_run_loop_embedded.h"
|
|
#include "hci_transport.h"
|
|
#include "hci_dump.h"
|
|
#include "hal_cpu.h"
|
|
#include "hal_time_ms.h"
|
|
|
|
// bluetooth.h
|
|
#define ADVERTISING_RADIO_ACCESS_ADDRESS 0x8E89BED6
|
|
#define ADVERTISING_CRC_INIT 0x555555
|
|
|
|
// HCI CMD OGF/OCF
|
|
#define READ_CMD_OGF(buffer) (buffer[1] >> 2)
|
|
#define READ_CMD_OCF(buffer) ((buffer[1] & 0x03) << 8 | buffer[0])
|
|
|
|
typedef enum {
|
|
LL_STATE_STANDBY,
|
|
LL_STATE_SCANNING,
|
|
LL_STATE_ADVERTISING,
|
|
LL_STATE_INITIATING,
|
|
LL_STATE_CONNECTED
|
|
} ll_state_t;
|
|
|
|
// from SDK UART exzmple
|
|
#define UART_TX_BUF_SIZE 128 /**< UART TX buffer size. */
|
|
#define UART_RX_BUF_SIZE 1 /**< UART RX buffer size. */
|
|
|
|
// packet receive buffer
|
|
#define MAXLEN 255
|
|
static uint8_t rx_adv_buffer[2 + MAXLEN];
|
|
|
|
// hci transport
|
|
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size);
|
|
static hci_transport_t hci_transport;
|
|
static uint8_t hci_outgoing_event[258];
|
|
static volatile uint8_t hci_outgoing_event_ready;
|
|
static volatile uint8_t hci_outgoing_event_free;
|
|
static btstack_data_source_t hci_transport_data_source;
|
|
|
|
// Link Layer State
|
|
static ll_state_t ll_state;
|
|
static uint32_t ll_scan_interval_us;
|
|
static uint32_t ll_scan_window_us;
|
|
|
|
|
|
void uart_error_handle(app_uart_evt_t * p_event)
|
|
{
|
|
if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
|
|
{
|
|
APP_ERROR_HANDLER(p_event->data.error_communication);
|
|
}
|
|
else if (p_event->evt_type == APP_UART_FIFO_ERROR)
|
|
{
|
|
APP_ERROR_HANDLER(p_event->data.error_code);
|
|
}
|
|
}
|
|
|
|
static void init_timer(void) {
|
|
|
|
#if 1
|
|
// start high frequency clock source if not done yet
|
|
if ( !NRF_CLOCK->EVENTS_HFCLKSTARTED ) {
|
|
NRF_CLOCK->TASKS_HFCLKSTART = 1;
|
|
while ( !NRF_CLOCK->EVENTS_HFCLKSTARTED ){
|
|
// just wait
|
|
}
|
|
}
|
|
#endif
|
|
|
|
NRF_TIMER0->MODE = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
|
|
NRF_TIMER0->BITMODE = TIMER_BITMODE_BITMODE_32Bit;
|
|
NRF_TIMER0->PRESCALER = 4; // 16 Mhz / (2 ^ 4) = 1 Mhz == 1 us
|
|
|
|
NRF_TIMER0->TASKS_STOP = 1;
|
|
NRF_TIMER0->TASKS_CLEAR = 1;
|
|
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
|
|
NRF_TIMER0->EVENTS_COMPARE[1] = 0;
|
|
NRF_TIMER0->EVENTS_COMPARE[2] = 0;
|
|
NRF_TIMER0->EVENTS_COMPARE[3] = 0;
|
|
NRF_TIMER0->INTENCLR = 0xffffffff;
|
|
NRF_TIMER0->TASKS_START = 1;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static void radio_set_crc_init(uint32_t crc_init){
|
|
NRF_RADIO->CRCINIT = crc_init;
|
|
}
|
|
|
|
// look up RF Center Frequency for data channels 0..36 and advertising channels 37..39
|
|
static uint8_t radio_frequency_for_channel(uint8_t channel){
|
|
if (channel <= 10){
|
|
return 4 + 2 * channel;
|
|
}
|
|
if (channel <= 36){
|
|
return 6 + 2 * channel;
|
|
}
|
|
if (channel == 37){
|
|
return 2;
|
|
}
|
|
if (channel == 38){
|
|
return 26;
|
|
}
|
|
return 80;
|
|
}
|
|
|
|
static void radio_init(void){
|
|
|
|
#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;
|
|
|
|
// Shorts:
|
|
// - READY->START
|
|
// - ADDRESS0>RSSISTART
|
|
NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Enabled << RADIO_SHORTS_READY_START_Pos
|
|
| RADIO_SHORTS_ADDRESS_RSSISTART_Enabled << RADIO_SHORTS_ADDRESS_RSSISTART_Pos;
|
|
|
|
// Disable all interrrupts
|
|
NRF_RADIO->INTENCLR = 0xffffffff;
|
|
}
|
|
|
|
|
|
void radio_dump_state(void){
|
|
printf("Radio state: %lx\n", NRF_RADIO->STATE);
|
|
}
|
|
|
|
// static
|
|
void radio_receive_on_channel(int channel){
|
|
// set frequency based on channel
|
|
NRF_RADIO->FREQUENCY = radio_frequency_for_channel( channel );
|
|
|
|
// initializes data whitening with channel index
|
|
NRF_RADIO->DATAWHITEIV = channel & 0x3F;
|
|
|
|
// set receive buffer
|
|
NRF_RADIO->PACKETPTR = (uintptr_t) rx_adv_buffer;
|
|
|
|
// set MAXLEN based on receive buffer size
|
|
NRF_RADIO->PCNF1 = ( NRF_RADIO->PCNF1 & ~RADIO_PCNF1_MAXLEN_Msk ) | ( MAXLEN << RADIO_PCNF1_MAXLEN_Pos );
|
|
|
|
// clear events
|
|
NRF_RADIO->EVENTS_END = 0;
|
|
NRF_RADIO->EVENTS_DISABLED = 0;
|
|
NRF_RADIO->EVENTS_READY = 0;
|
|
NRF_RADIO->EVENTS_ADDRESS = 0;
|
|
|
|
radio_set_access_address(ADVERTISING_RADIO_ACCESS_ADDRESS);
|
|
radio_set_crc_init(ADVERTISING_CRC_INIT);
|
|
|
|
// ramp up receiver
|
|
NRF_RADIO->TASKS_RXEN = 1;
|
|
|
|
// enable IRQ for END event
|
|
NRF_RADIO->INTENSET = RADIO_INTENSET_END_Enabled << RADIO_INTENSET_END_Pos;
|
|
}
|
|
|
|
// static
|
|
void radio_disable(void){
|
|
// testing
|
|
NRF_RADIO->TASKS_DISABLE = 1;
|
|
|
|
// wait for ready
|
|
while (!NRF_RADIO->EVENTS_DISABLED) {
|
|
// just wait
|
|
}
|
|
}
|
|
|
|
// static
|
|
void radio_dump_packet(void){
|
|
// print data
|
|
int len = rx_adv_buffer[1] & 0x3f;
|
|
int i;
|
|
for (i=0;i<len;i++){
|
|
printf("%02x ", rx_adv_buffer[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
void RADIO_IRQHandler(void){
|
|
|
|
// IRQ only triggered on EVENTS_END so far
|
|
NRF_RADIO->EVENTS_END = 0;
|
|
|
|
if (ll_state == LL_STATE_SCANNING){
|
|
|
|
// check if outgoing buffer available and if CRC was ok
|
|
if (hci_outgoing_event_free &&
|
|
((NRF_RADIO->CRCSTATUS & RADIO_CRCSTATUS_CRCSTATUS_Msk) == (RADIO_CRCSTATUS_CRCSTATUS_CRCOk << RADIO_CRCSTATUS_CRCSTATUS_Pos))){
|
|
hci_outgoing_event_free = 0;
|
|
int len = rx_adv_buffer[1] & 0x3f;
|
|
hci_outgoing_event[0] = HCI_EVENT_LE_META;
|
|
hci_outgoing_event[1] = 11 + len - 6;
|
|
hci_outgoing_event[2] = HCI_SUBEVENT_LE_ADVERTISING_REPORT;
|
|
hci_outgoing_event[3] = 1;
|
|
hci_outgoing_event[4] = rx_adv_buffer[0] & 0x0f;
|
|
hci_outgoing_event[5] = (rx_adv_buffer[0] & 0x40) ? 1 : 0;
|
|
memcpy(&hci_outgoing_event[6], &rx_adv_buffer[2], 6);
|
|
hci_outgoing_event[12] = len - 6; // rest after bd addr
|
|
memcpy(&hci_outgoing_event[13], &rx_adv_buffer[8], len - 6);
|
|
hci_outgoing_event[13 + len - 6] = -NRF_RADIO->RSSISAMPLE; // RSSI is stored without sign but is negative
|
|
hci_outgoing_event_ready = 1;
|
|
} else {
|
|
// ... for now, we just throw the adv away and try to receive the next one
|
|
}
|
|
|
|
// TODO: check if there's enough time left before the end of the scan interval
|
|
|
|
// restart receiving
|
|
NRF_RADIO->TASKS_START = 1;
|
|
}
|
|
}
|
|
|
|
uint8_t random_generator_next(void){
|
|
NRF_RNG->SHORTS = RNG_SHORTS_VALRDY_STOP_Enabled << RNG_SHORTS_VALRDY_STOP_Pos;
|
|
NRF_RNG->TASKS_START = 1;
|
|
while (!NRF_RNG->EVENTS_VALRDY){
|
|
}
|
|
return NRF_RNG->VALUE;
|
|
}
|
|
|
|
// uses TIMER0-CC2 for reads
|
|
uint32_t get_time_us(void){
|
|
NRF_TIMER0->TASKS_CAPTURE[2] = 1;
|
|
return NRF_TIMER0->CC[2];
|
|
}
|
|
|
|
volatile int timer_irq_happened;
|
|
void TIMER0_IRQHandler(void){
|
|
// Reset IRQ flag
|
|
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
|
|
NRF_TIMER0->TASKS_CLEAR = 1;
|
|
|
|
timer_irq_happened = 1;
|
|
|
|
// if (ll_state == LL_STATE_SCANNING){
|
|
// // Restart scanning
|
|
// // TODO: use all channels
|
|
// radio_receive_on_channel(37);
|
|
// }
|
|
}
|
|
|
|
// TODO: implement
|
|
void hal_cpu_disable_irqs(void){}
|
|
void hal_cpu_enable_irqs(void){}
|
|
void hal_cpu_enable_irqs_and_sleep(void){}
|
|
|
|
// TODO: get time from RTC
|
|
uint32_t hal_time_ms(void){
|
|
return 999;
|
|
}
|
|
|
|
static void ll_set_scan_parameters(uint8_t le_scan_type, uint16_t le_scan_interval, uint16_t le_scan_window, uint8_t own_address_type, uint8_t scanning_filter_policy){
|
|
// TODO .. store other params
|
|
ll_scan_interval_us = ((uint32_t) le_scan_interval) * 625;
|
|
ll_scan_window_us = ((uint32_t) le_scan_window) * 625;
|
|
log_info("LE Scan Params: window %lu, interval %lu ms", ll_scan_interval_us, ll_scan_window_us);
|
|
}
|
|
|
|
static uint8_t ll_start_scanning(uint8_t filter_duplicates){
|
|
// COMMAND DISALLOWED if wrong state.
|
|
if (ll_state != LL_STATE_STANDBY) return 0x0c;
|
|
|
|
ll_state = LL_STATE_SCANNING;
|
|
|
|
log_info("LE Scan Start: window %lu, interval %lu ms", ll_scan_interval_us, ll_scan_window_us);
|
|
|
|
// reset timer and capature events
|
|
NRF_TIMER0->TASKS_CLEAR = 1;
|
|
NRF_TIMER0->TASKS_STOP = 1;
|
|
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
|
|
NRF_TIMER0->EVENTS_COMPARE[1] = 0;
|
|
|
|
// limit scanning
|
|
if (ll_scan_window_us < ll_scan_interval_us){
|
|
// setup PPI to disable radio after end of scan_window
|
|
NRF_TIMER0->CC[1] = ll_scan_window_us;
|
|
NRF_PPI->CHENSET = 1 << 22; // TIMER0->EVENTS_COMPARE[1] -> RADIO->TASKS_DISABLE
|
|
}
|
|
|
|
// set timer to trigger IRQ for next scan interval
|
|
NRF_TIMER0->CC[0] = ll_scan_interval_us;
|
|
NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos;
|
|
|
|
// next channel to scan
|
|
int adv_channel = (random_generator_next() % 3) + 37;
|
|
log_debug("LE Scan Channel: %u", adv_channel);
|
|
|
|
// start receiving
|
|
NRF_TIMER0->TASKS_START = 1;
|
|
radio_receive_on_channel(adv_channel);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t ll_stop_scanning(void){
|
|
// COMMAND DISALLOWED if wrong state.
|
|
if (ll_state != LL_STATE_SCANNING) return 0x0c;
|
|
|
|
log_info("LE Scan Stop");
|
|
|
|
ll_state = LL_STATE_STANDBY;
|
|
|
|
// stop radio
|
|
radio_disable();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t ll_set_scan_enable(uint8_t le_scan_enable, uint8_t filter_duplicates){
|
|
if (le_scan_enable){
|
|
return ll_start_scanning(filter_duplicates);
|
|
} else {
|
|
return ll_stop_scanning();
|
|
}
|
|
}
|
|
|
|
static void fake_command_complete(uint16_t opcode){
|
|
hci_outgoing_event[0] = HCI_EVENT_COMMAND_COMPLETE;
|
|
hci_outgoing_event[1] = 4;
|
|
hci_outgoing_event[2] = 1;
|
|
little_endian_store_16(hci_outgoing_event, 3, opcode);
|
|
hci_outgoing_event[5] = 0;
|
|
hci_outgoing_event_ready = 1;
|
|
}
|
|
|
|
static void send_hardware_error(uint8_t error_code){
|
|
hci_outgoing_event[0] = HCI_EVENT_HARDWARE_ERROR;
|
|
hci_outgoing_event[1] = 1;
|
|
hci_outgoing_event[2] = error_code;
|
|
hci_outgoing_event_ready = 1;
|
|
}
|
|
|
|
|
|
// command handler
|
|
static void controller_handle_hci_command(uint8_t * packet, uint16_t size){
|
|
uint16_t opcode = little_endian_read_16(packet, 0);
|
|
#if 0
|
|
uint16_t ocf = READ_CMD_OCF(packet);
|
|
switch (READ_CMD_OGF(packet)){
|
|
case OGF_CONTROLLER_BASEBAND:
|
|
switch (ocf):
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
if (opcode == hci_reset.opcode) {
|
|
fake_command_complete(opcode);
|
|
return;
|
|
}
|
|
if (opcode == hci_le_set_scan_enable.opcode){
|
|
ll_set_scan_enable(packet[3], packet[4]);
|
|
fake_command_complete(opcode);
|
|
return;
|
|
}
|
|
if (opcode == hci_le_set_scan_parameters.opcode){
|
|
ll_set_scan_parameters(packet[3], little_endian_read_16(packet, 4), little_endian_read_16(packet, 6), packet[8], packet[9]);
|
|
fake_command_complete(opcode);
|
|
return;
|
|
}
|
|
// try with "OK"
|
|
printf("CMD opcode %02x not handled yet\n", opcode);
|
|
fake_command_complete(opcode);
|
|
}
|
|
|
|
// ACL handler
|
|
static void controller_handle_acl_data(uint8_t * packet, uint16_t size){
|
|
// TODO
|
|
}
|
|
|
|
static void transport_run(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){
|
|
// deliver hci packet on main thread
|
|
if (hci_outgoing_event_ready){
|
|
hci_outgoing_event_ready = 0;
|
|
packet_handler(HCI_EVENT_PACKET, hci_outgoing_event, hci_outgoing_event[1]+2);
|
|
hci_outgoing_event_free = 1;
|
|
}
|
|
if (timer_irq_happened){
|
|
// printf("Timer irq occured\n");
|
|
// radio_dump_state();
|
|
timer_irq_happened = 0;
|
|
|
|
if (ll_state == LL_STATE_SCANNING){
|
|
// next channel to scan
|
|
int adv_channel = (random_generator_next() % 3) + 37;
|
|
log_debug("Restart scan on channel %u", adv_channel);
|
|
radio_receive_on_channel(adv_channel);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* init transport
|
|
* @param transport_config
|
|
*/
|
|
void transport_init(const void *transport_config){
|
|
|
|
}
|
|
|
|
/**
|
|
* open transport connection
|
|
*/
|
|
static int transport_open(void){
|
|
btstack_run_loop_set_data_source_handler(&hci_transport_data_source, &transport_run);
|
|
btstack_run_loop_enable_data_source_callbacks(&hci_transport_data_source, DATA_SOURCE_CALLBACK_POLL);
|
|
btstack_run_loop_add_data_source(&hci_transport_data_source);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* close transport connection
|
|
*/
|
|
static int transport_close(void){
|
|
btstack_run_loop_remove_data_source(&hci_transport_data_source);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* register packet handler for HCI packets: ACL, SCO, and Events
|
|
*/
|
|
static void transport_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
|
packet_handler = handler;
|
|
}
|
|
|
|
int transport_send_packet(uint8_t packet_type, uint8_t *packet, int size){
|
|
|
|
switch (packet_type){
|
|
case HCI_COMMAND_DATA_PACKET:
|
|
controller_handle_hci_command(packet, size);
|
|
break;
|
|
case HCI_ACL_DATA_PACKET:
|
|
controller_handle_acl_data(packet, size);
|
|
break;
|
|
default:
|
|
send_hardware_error(0x01); // invalid HCI packet
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int btstack_main(void);
|
|
|
|
/**
|
|
* @brief Function for main application entry.
|
|
*/
|
|
int main(void)
|
|
{
|
|
|
|
LEDS_CONFIGURE(LEDS_MASK);
|
|
LEDS_OFF(LEDS_MASK);
|
|
uint32_t err_code;
|
|
const app_uart_comm_params_t comm_params = {
|
|
RX_PIN_NUMBER,
|
|
TX_PIN_NUMBER,
|
|
RTS_PIN_NUMBER,
|
|
CTS_PIN_NUMBER,
|
|
APP_UART_FLOW_CONTROL_ENABLED,
|
|
false,
|
|
UART_BAUDRATE_BAUDRATE_Baud115200
|
|
};
|
|
|
|
APP_UART_FIFO_INIT(&comm_params,
|
|
UART_RX_BUF_SIZE,
|
|
UART_TX_BUF_SIZE,
|
|
uart_error_handle,
|
|
APP_IRQ_PRIORITY_LOW,
|
|
err_code);
|
|
|
|
APP_ERROR_CHECK(err_code);
|
|
|
|
init_timer();
|
|
radio_init();
|
|
hci_outgoing_event_free = 1;
|
|
|
|
// enable Radio IRQs
|
|
NVIC_SetPriority( RADIO_IRQn, 0 );
|
|
NVIC_ClearPendingIRQ( RADIO_IRQn );
|
|
NVIC_EnableIRQ( RADIO_IRQn );
|
|
|
|
// enable Timer IRQs
|
|
NVIC_SetPriority( TIMER0_IRQn, 0 );
|
|
NVIC_ClearPendingIRQ( TIMER0_IRQn );
|
|
NVIC_EnableIRQ( TIMER0_IRQn );
|
|
|
|
// HCI Controller Defaults
|
|
ll_scan_window_us = 10000;
|
|
ll_scan_interval_us = 10000;
|
|
|
|
// Bring up BTstack
|
|
printf("BTstack on Nordic nRF5 SDK\n");
|
|
|
|
btstack_memory_init();
|
|
btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
|
|
|
|
// setup hci transport wrapper
|
|
hci_transport.name = "nRF5";
|
|
hci_transport.init = transport_init;
|
|
hci_transport.open = transport_open;
|
|
hci_transport.close = transport_close;
|
|
hci_transport.register_packet_handler = transport_register_packet_handler;
|
|
hci_transport.can_send_packet_now = NULL;
|
|
hci_transport.send_packet = transport_send_packet;
|
|
hci_transport.set_baudrate = NULL;
|
|
|
|
// init HCI
|
|
hci_init(&hci_transport, NULL);
|
|
|
|
// enable full log output while porting
|
|
hci_dump_open(NULL, HCI_DUMP_STDOUT);
|
|
|
|
// hand over to btstack embedded code
|
|
btstack_main();
|
|
|
|
// go
|
|
btstack_run_loop_execute();
|
|
|
|
while (1){};
|
|
|
|
#if 0
|
|
|
|
// start listening
|
|
radio_receive_on_channel(37);
|
|
|
|
while (1){
|
|
if (NRF_RADIO->EVENTS_END){
|
|
NRF_RADIO->EVENTS_END = 0;
|
|
// process packet
|
|
radio_dump_packet();
|
|
// receive next packet
|
|
NRF_RADIO->TASKS_START = 1;
|
|
}
|
|
}
|
|
|
|
radio_disable();
|
|
#endif
|
|
}
|
|
|
|
|
|
/** @} */
|