diff options
| author | Glenn Ruben Bakke <glennbakke@gmail.com> | 2017-10-04 21:46:48 +0200 |
|---|---|---|
| committer | Glenn Ruben Bakke <glennbakke@gmail.com> | 2017-10-04 21:46:48 +0200 |
| commit | 44e2cb415f0b571e36b9d08def533c018fa99f1f (patch) | |
| tree | 92bcc0f49b7612d3ffb366688deefe225abc9b5c /ports/nrf/drivers | |
| parent | bcab2ba0a80297100919366add6bada140c6ed75 (diff) | |
ports/nrf: Moving nrf51/52 port to new ports directory
Diffstat (limited to 'ports/nrf/drivers')
| -rw-r--r-- | ports/nrf/drivers/bluetooth/ble_drv.c | 1065 | ||||
| -rw-r--r-- | ports/nrf/drivers/bluetooth/ble_drv.h | 129 | ||||
| -rw-r--r-- | ports/nrf/drivers/bluetooth/ble_uart.c | 266 | ||||
| -rw-r--r-- | ports/nrf/drivers/bluetooth/ble_uart.h | 42 | ||||
| -rw-r--r-- | ports/nrf/drivers/bluetooth/bluetooth_common.mk | 55 | ||||
| -rwxr-xr-x | ports/nrf/drivers/bluetooth/download_ble_stack.sh | 74 | ||||
| -rw-r--r-- | ports/nrf/drivers/bluetooth/ringbuffer.h | 99 | ||||
| -rw-r--r-- | ports/nrf/drivers/softpwm.c | 257 | ||||
| -rw-r--r-- | ports/nrf/drivers/softpwm.h | 13 | ||||
| -rw-r--r-- | ports/nrf/drivers/ticker.c | 162 | ||||
| -rw-r--r-- | ports/nrf/drivers/ticker.h | 30 |
11 files changed, 2192 insertions, 0 deletions
diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c new file mode 100644 index 000000000..2bb6fac2d --- /dev/null +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -0,0 +1,1065 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Glenn Ruben Bakke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if BLUETOOTH_SD + +#include <stdio.h> +#include <string.h> +#include <stdbool.h> + +#include "py/runtime.h" +#include "ble_drv.h" +#include "mpconfigport.h" +#include "nrf_sdm.h" +#include "ble_gap.h" +#include "ble.h" // sd_ble_uuid_encode + + +#define BLE_DRIVER_VERBOSE 0 +#if BLE_DRIVER_VERBOSE +#define BLE_DRIVER_LOG printf +#else +#define BLE_DRIVER_LOG(...) +#endif + +#define BLE_ADV_LENGTH_FIELD_SIZE 1 +#define BLE_ADV_AD_TYPE_FIELD_SIZE 1 +#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 + +#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) +#define UNIT_0_625_MS (625) +#define UNIT_10_MS (10000) +#define APP_CFG_NON_CONN_ADV_TIMEOUT 0 // Disable timeout. +#define NON_CONNECTABLE_ADV_INTERVAL MSEC_TO_UNITS(100, UNIT_0_625_MS) + +#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(12, UNIT_0_625_MS) +#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(12, UNIT_0_625_MS) +#define BLE_SLAVE_LATENCY 0 +#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) + +#define SD_TEST_OR_ENABLE() \ +if (ble_drv_stack_enabled() == 0) { \ + (void)ble_drv_stack_enable(); \ +} + +static volatile bool m_adv_in_progress; +static volatile bool m_tx_in_progress; + +static ble_drv_gap_evt_callback_t gap_event_handler; +static ble_drv_gatts_evt_callback_t gatts_event_handler; + +static mp_obj_t mp_gap_observer; +static mp_obj_t mp_gatts_observer; + +#if (BLUETOOTH_SD == 130) || (BLUETOOTH_SD == 132) +static volatile bool m_primary_service_found; +static volatile bool m_characteristic_found; +static volatile bool m_write_done; + +static volatile ble_drv_adv_evt_callback_t adv_event_handler; +static volatile ble_drv_gattc_evt_callback_t gattc_event_handler; +static volatile ble_drv_disc_add_service_callback_t disc_add_service_handler; +static volatile ble_drv_disc_add_char_callback_t disc_add_char_handler; +static volatile ble_drv_gattc_char_data_callback_t gattc_char_data_handle; + +static mp_obj_t mp_adv_observer; +static mp_obj_t mp_gattc_observer; +static mp_obj_t mp_gattc_disc_service_observer; +static mp_obj_t mp_gattc_disc_char_observer; +static mp_obj_t mp_gattc_char_data_observer; +#endif + +#if (BLUETOOTH_SD != 100) && (BLUETOOTH_SD != 110) +#include "nrf_nvic.h" + +#ifdef NRF52 +nrf_nvic_state_t nrf_nvic_state = {0}; +#endif // NRF52 + +#endif // (BLUETOOTH_SD != 100) + +#if (BLUETOOTH_SD == 100 ) || (BLUETOOTH_SD == 110) +void softdevice_assert_handler(uint32_t pc, uint16_t line_number, const uint8_t * p_file_name) { + BLE_DRIVER_LOG("ERROR: SoftDevice assert!!!"); +} +#else +void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) { + BLE_DRIVER_LOG("ERROR: SoftDevice assert!!!"); +} +#endif +uint32_t ble_drv_stack_enable(void) { + m_adv_in_progress = false; + m_tx_in_progress = false; + +#if (BLUETOOTH_SD == 100) || (BLUETOOTH_SD == 110) +#if BLUETOOTH_LFCLK_RC + uint32_t err_code = sd_softdevice_enable(NRF_CLOCK_LFCLKSRC_RC_250_PPM_250MS_CALIBRATION, + softdevice_assert_handler); +#else + uint32_t err_code = sd_softdevice_enable(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, + softdevice_assert_handler); +#endif // BLUETOOTH_LFCLK_RC +#else +#if BLUETOOTH_LFCLK_RC + nrf_clock_lf_cfg_t clock_config = { + .source = NRF_CLOCK_LF_SRC_RC, + .rc_ctiv = 16, + .rc_temp_ctiv = 2, + .xtal_accuracy = 0 + }; +#else + nrf_clock_lf_cfg_t clock_config = { + .source = NRF_CLOCK_LF_SRC_XTAL, + .rc_ctiv = 0, + .rc_temp_ctiv = 0, + .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM + }; +#endif + uint32_t err_code = sd_softdevice_enable(&clock_config, + softdevice_assert_handler); +#endif + + BLE_DRIVER_LOG("SoftDevice enable status: " UINT_FMT "\n", (uint16_t)err_code); + +#if NRF51 + err_code = sd_nvic_EnableIRQ(SWI2_IRQn); +#else + err_code = sd_nvic_EnableIRQ(SWI2_EGU2_IRQn); +#endif + + BLE_DRIVER_LOG("IRQ enable status: " UINT_FMT "\n", (uint16_t)err_code); + + // Enable BLE stack. + ble_enable_params_t ble_enable_params; + memset(&ble_enable_params, 0x00, sizeof(ble_enable_params)); + ble_enable_params.gatts_enable_params.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT; + ble_enable_params.gatts_enable_params.service_changed = 0; +#if (BLUETOOTH_SD == 132) + ble_enable_params.gap_enable_params.periph_conn_count = 1; + ble_enable_params.gap_enable_params.central_conn_count = 1; +#endif + + +#if (BLUETOOTH_SD == 100) || (BLUETOOTH_SD == 110) + err_code = sd_ble_enable(&ble_enable_params); +#else + +#if (BLUETOOTH_SD == 132) + uint32_t app_ram_start = 0x200039c0; + err_code = sd_ble_enable(&ble_enable_params, &app_ram_start); // 8K SD headroom from linker script. + BLE_DRIVER_LOG("BLE ram size: " UINT_FMT "\n", (uint16_t)app_ram_start); +#else + err_code = sd_ble_enable(&ble_enable_params, (uint32_t *)0x20001870); +#endif + +#endif + + BLE_DRIVER_LOG("BLE enable status: " UINT_FMT "\n", (uint16_t)err_code); + + // set up security mode + ble_gap_conn_params_t gap_conn_params; + ble_gap_conn_sec_mode_t sec_mode; + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); + + const char device_name[] = "micr"; + + if ((err_code = sd_ble_gap_device_name_set(&sec_mode, + (const uint8_t *)device_name, + strlen(device_name))) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Cannot apply GAP parameters.")); + } + + // set connection parameters + memset(&gap_conn_params, 0, sizeof(gap_conn_params)); + + gap_conn_params.min_conn_interval = BLE_MIN_CONN_INTERVAL; + gap_conn_params.max_conn_interval = BLE_MAX_CONN_INTERVAL; + gap_conn_params.slave_latency = BLE_SLAVE_LATENCY; + gap_conn_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT; + + if (sd_ble_gap_ppcp_set(&gap_conn_params) != 0) { + + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Cannot set PPCP parameters.")); + } + + return err_code; +} + +void ble_drv_stack_disable(void) { + sd_softdevice_disable(); +} + +uint8_t ble_drv_stack_enabled(void) { + uint8_t is_enabled; + uint32_t err_code = sd_softdevice_is_enabled(&is_enabled); + (void)err_code; + + BLE_DRIVER_LOG("Is enabled status: " UINT_FMT "\n", (uint16_t)err_code); + + return is_enabled; +} + +void ble_drv_address_get(ble_drv_addr_t * p_addr) { + SD_TEST_OR_ENABLE(); + + ble_gap_addr_t local_ble_addr; +#if (BLUETOOTH_SD == 132 && BLE_API_VERSION == 3) + uint32_t err_code = sd_ble_gap_addr_get(&local_ble_addr); +#else + uint32_t err_code = sd_ble_gap_address_get(&local_ble_addr); +#endif + + if (err_code != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not query for the device address.")); + } + + BLE_DRIVER_LOG("ble address, type: " HEX2_FMT ", " \ + "address: " HEX2_FMT ":" HEX2_FMT ":" HEX2_FMT ":" \ + HEX2_FMT ":" HEX2_FMT ":" HEX2_FMT "\n", \ + local_ble_addr.addr_type, \ + local_ble_addr.addr[5], local_ble_addr.addr[4], local_ble_addr.addr[3], \ + local_ble_addr.addr[2], local_ble_addr.addr[1], local_ble_addr.addr[0]); + + p_addr->addr_type = local_ble_addr.addr_type; + memcpy(p_addr->addr, local_ble_addr.addr, 6); +} + +bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx) { + SD_TEST_OR_ENABLE(); + + if (sd_ble_uuid_vs_add((ble_uuid128_t const *)p_uuid, idx) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not add Vendor Specific 128-bit UUID.")); + } + + return true; +} + +bool ble_drv_service_add(ubluepy_service_obj_t * p_service_obj) { + SD_TEST_OR_ENABLE(); + + if (p_service_obj->p_uuid->type > BLE_UUID_TYPE_BLE) { + + ble_uuid_t uuid; + uuid.type = p_service_obj->p_uuid->uuid_vs_idx; + uuid.uuid = p_service_obj->p_uuid->value[0]; + uuid.uuid += p_service_obj->p_uuid->value[1] << 8; + + if (sd_ble_gatts_service_add(p_service_obj->type, + &uuid, + &p_service_obj->handle) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not add Service.")); + } + } else if (p_service_obj->p_uuid->type == BLE_UUID_TYPE_BLE) { + BLE_DRIVER_LOG("adding service\n"); + + ble_uuid_t uuid; + uuid.type = p_service_obj->p_uuid->type; + uuid.uuid = p_service_obj->p_uuid->value[0]; + uuid.uuid += p_service_obj->p_uuid->value[1] << 8; + + if (sd_ble_gatts_service_add(p_service_obj->type, + &uuid, + &p_service_obj->handle) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not add Service.")); + } + } + return true; +} + +bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj) { + ble_gatts_char_md_t char_md; + ble_gatts_attr_md_t cccd_md; + ble_gatts_attr_t attr_char_value; + ble_uuid_t uuid; + ble_gatts_attr_md_t attr_md; + + memset(&char_md, 0, sizeof(char_md)); + + char_md.char_props.broadcast = (p_char_obj->props & UBLUEPY_PROP_BROADCAST) ? 1 : 0; + char_md.char_props.read = (p_char_obj->props & UBLUEPY_PROP_READ) ? 1 : 0; + char_md.char_props.write_wo_resp = (p_char_obj->props & UBLUEPY_PROP_WRITE_WO_RESP) ? 1 : 0; + char_md.char_props.write = (p_char_obj->props & UBLUEPY_PROP_WRITE) ? 1 : 0; + char_md.char_props.notify = (p_char_obj->props & UBLUEPY_PROP_NOTIFY) ? 1 : 0; + char_md.char_props.indicate = (p_char_obj->props & UBLUEPY_PROP_INDICATE) ? 1 : 0; +#if 0 + char_md.char_props.auth_signed_wr = (p_char_obj->props & UBLUEPY_PROP_NOTIFY) ? 1 : 0; +#endif + + + char_md.p_char_user_desc = NULL; + char_md.p_char_pf = NULL; + char_md.p_user_desc_md = NULL; + char_md.p_sccd_md = NULL; + + // if cccd + if (p_char_obj->attrs & UBLUEPY_ATTR_CCCD) { + memset(&cccd_md, 0, sizeof(cccd_md)); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm); + cccd_md.vloc = BLE_GATTS_VLOC_STACK; + char_md.p_cccd_md = &cccd_md; + } else { + char_md.p_cccd_md = NULL; + } + + uuid.type = p_char_obj->p_uuid->type; + uuid.uuid = p_char_obj->p_uuid->value[0]; + uuid.uuid += p_char_obj->p_uuid->value[1] << 8; + + memset(&attr_md, 0, sizeof(attr_md)); + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + + attr_md.vloc = BLE_GATTS_VLOC_STACK; + attr_md.rd_auth = 0; + attr_md.wr_auth = 0; + attr_md.vlen = 1; + + memset(&attr_char_value, 0, sizeof(attr_char_value)); + + attr_char_value.p_uuid = &uuid; + attr_char_value.p_attr_md = &attr_md; + attr_char_value.init_len = sizeof(uint8_t); + attr_char_value.init_offs = 0; + attr_char_value.max_len = (GATT_MTU_SIZE_DEFAULT - 3); + + ble_gatts_char_handles_t handles; + + if (sd_ble_gatts_characteristic_add(p_char_obj->service_handle, + &char_md, + &attr_char_value, + &handles) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not add Characteristic.")); + } + + // apply handles to object instance + p_char_obj->handle = handles.value_handle; + p_char_obj->user_desc_handle = handles.user_desc_handle; + p_char_obj->cccd_handle = handles.cccd_handle; + p_char_obj->sccd_handle = handles.sccd_handle; + + return true; +} + +bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { + SD_TEST_OR_ENABLE(); + + uint8_t byte_pos = 0; + + uint8_t adv_data[BLE_GAP_ADV_MAX_SIZE]; + + if (p_adv_params->device_name_len > 0) { + ble_gap_conn_sec_mode_t sec_mode; + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); + + if (sd_ble_gap_device_name_set(&sec_mode, + p_adv_params->p_device_name, + p_adv_params->device_name_len) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not apply device name in the stack.")); + } + + BLE_DRIVER_LOG("Device name applied\n"); + + adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + p_adv_params->device_name_len); + byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; + adv_data[byte_pos] = BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME; + byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE; + memcpy(&adv_data[byte_pos], p_adv_params->p_device_name, p_adv_params->device_name_len); + // increment position counter to see if it fits, and in case more content should + // follow in this adv packet. + byte_pos += p_adv_params->device_name_len; + } + + // Add FLAGS only if manually controlled data has not been used. + if (p_adv_params->data_len == 0) { + // set flags, default to disc mode + adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + BLE_AD_TYPE_FLAGS_DATA_SIZE); + byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; + adv_data[byte_pos] = BLE_GAP_AD_TYPE_FLAGS; + byte_pos += BLE_AD_TYPE_FLAGS_DATA_SIZE; + adv_data[byte_pos] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + byte_pos += 1; + } + + if (p_adv_params->num_of_services > 0) { + + bool type_16bit_present = false; + bool type_128bit_present = false; + + for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { + ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i]; + if (p_service->p_uuid->type == UBLUEPY_UUID_16_BIT) { + type_16bit_present = true; + } + + if (p_service->p_uuid->type == UBLUEPY_UUID_128_BIT) { + type_128bit_present = true; + } + } + + if (type_16bit_present) { + uint8_t size_byte_pos = byte_pos; + + // skip length byte for now, apply total length post calculation + byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; + + adv_data[byte_pos] = BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE; + byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE; + + uint8_t uuid_total_size = 0; + uint8_t encoded_size = 0; + + for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { + ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i]; + + ble_uuid_t uuid; + uuid.type = p_service->p_uuid->type; + uuid.uuid = p_service->p_uuid->value[0]; + uuid.uuid += p_service->p_uuid->value[1] << 8; + // calculate total size of uuids + if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not encode UUID, to check length.")); + } + + // do encoding into the adv buffer + if (sd_ble_uuid_encode(&uuid, &encoded_size, &adv_data[byte_pos]) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can encode UUID into the advertisment packet.")); + } + + BLE_DRIVER_LOG("encoded uuid for service %u: ", 0); + for (uint8_t j = 0; j < encoded_size; j++) { + BLE_DRIVER_LOG(HEX2_FMT " ", adv_data[byte_pos + j]); + } + BLE_DRIVER_LOG("\n"); + + uuid_total_size += encoded_size; // size of entry + byte_pos += encoded_size; // relative to adv data packet + BLE_DRIVER_LOG("ADV: uuid size: %u, type: %u, uuid: %x%x, vs_idx: %u\n", + encoded_size, p_service->p_uuid->type, + p_service->p_uuid->value[1], + p_service->p_uuid->value[0], + p_service->p_uuid->uuid_vs_idx); + } + + adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size); + } + + if (type_128bit_present) { + uint8_t size_byte_pos = byte_pos; + + // skip length byte for now, apply total length post calculation + byte_pos += BLE_ADV_LENGTH_FIELD_SIZE; + + adv_data[byte_pos] = BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE; + byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE; + + uint8_t uuid_total_size = 0; + uint8_t encoded_size = 0; + + for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) { + ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i]; + + ble_uuid_t uuid; + uuid.type = p_service->p_uuid->uuid_vs_idx; + uuid.uuid = p_service->p_uuid->value[0]; + uuid.uuid += p_service->p_uuid->value[1] << 8; + + // calculate total size of uuids + if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not encode UUID, to check length.")); + } + + // do encoding into the adv buffer + if (sd_ble_uuid_encode(&uuid, &encoded_size, &adv_data[byte_pos]) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can encode UUID into the advertisment packet.")); + } + + BLE_DRIVER_LOG("encoded uuid for service %u: ", 0); + for (uint8_t j = 0; j < encoded_size; j++) { + BLE_DRIVER_LOG(HEX2_FMT " ", adv_data[byte_pos + j]); + } + BLE_DRIVER_LOG("\n"); + + uuid_total_size += encoded_size; // size of entry + byte_pos += encoded_size; // relative to adv data packet + BLE_DRIVER_LOG("ADV: uuid size: %u, type: %x%x, uuid: %u, vs_idx: %u\n", + encoded_size, p_service->p_uuid->type, + p_service->p_uuid->value[1], + p_service->p_uuid->value[0], + p_service->p_uuid->uuid_vs_idx); + } + + adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size); + } + } + + if ((p_adv_params->data_len > 0) && (p_adv_params->p_data != NULL)) { + if (p_adv_params->data_len + byte_pos > BLE_GAP_ADV_MAX_SIZE) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not fit data into the advertisment packet.")); + } + + memcpy(adv_data, p_adv_params->p_data, p_adv_params->data_len); + byte_pos += p_adv_params->data_len; + } + + // scan response data not set + uint32_t err_code; + if ((err_code = sd_ble_gap_adv_data_set(adv_data, byte_pos, NULL, 0)) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not apply advertisment data. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } + BLE_DRIVER_LOG("Set Adv data size: " UINT_FMT "\n", byte_pos); + + static ble_gap_adv_params_t m_adv_params; + + // initialize advertising params + memset(&m_adv_params, 0, sizeof(m_adv_params)); + if (p_adv_params->connectable) { + m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND; + } else { + m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_NONCONN_IND; + } + + m_adv_params.p_peer_addr = NULL; // undirected advertisement + m_adv_params.fp = BLE_GAP_ADV_FP_ANY; + m_adv_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); // approx 8 ms + m_adv_params.timeout = 0; // infinite advertisment + + ble_drv_advertise_stop(); + + err_code = sd_ble_gap_adv_start(&m_adv_params); + if (err_code != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not start advertisment. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } + + m_adv_in_progress = true; + + return true; +} + +void ble_drv_advertise_stop(void) { + if (m_adv_in_progress == true) { + uint32_t err_code; + if ((err_code = sd_ble_gap_adv_stop()) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not stop advertisment. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } + } + m_adv_in_progress = false; +} + +void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) { + ble_gatts_value_t gatts_value; + memset(&gatts_value, 0, sizeof(gatts_value)); + + gatts_value.len = len; + gatts_value.offset = 0; + gatts_value.p_value = p_data; + + uint32_t err_code = sd_ble_gatts_value_get(conn_handle, + handle, + &gatts_value); + if (err_code != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not read attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } + +} + +void ble_drv_attr_s_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) { + ble_gatts_value_t gatts_value; + memset(&gatts_value, 0, sizeof(gatts_value)); + + gatts_value.len = len; + gatts_value.offset = 0; + gatts_value.p_value = p_data; + + uint32_t err_code = sd_ble_gatts_value_set(conn_handle, handle, &gatts_value); + + if (err_code != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not write attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } +} + +void ble_drv_attr_s_notify(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) { + uint16_t hvx_len = len; + ble_gatts_hvx_params_t hvx_params; + + memset(&hvx_params, 0, sizeof(hvx_params)); + + hvx_params.handle = handle; + hvx_params.type = BLE_GATT_HVX_NOTIFICATION; + hvx_params.offset = 0; + hvx_params.p_len = &hvx_len; + hvx_params.p_data = p_data; + + while (m_tx_in_progress) { + ; + } + + m_tx_in_progress = true; + uint32_t err_code; + if ((err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params)) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not notify attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } +} + +void ble_drv_gap_event_handler_set(mp_obj_t obj, ble_drv_gap_evt_callback_t evt_handler) { + mp_gap_observer = obj; + gap_event_handler = evt_handler; +} + +void ble_drv_gatts_event_handler_set(mp_obj_t obj, ble_drv_gatts_evt_callback_t evt_handler) { + mp_gatts_observer = obj; + gatts_event_handler = evt_handler; +} + +#if (BLUETOOTH_SD == 130) || (BLUETOOTH_SD == 132) + +void ble_drv_gattc_event_handler_set(mp_obj_t obj, ble_drv_gattc_evt_callback_t evt_handler) { + mp_gattc_observer = obj; + gattc_event_handler = evt_handler; +} + +void ble_drv_adv_report_handler_set(mp_obj_t obj, ble_drv_adv_evt_callback_t evt_handler) { + mp_adv_observer = obj; + adv_event_handler = evt_handler; +} + + +void ble_drv_attr_c_read(uint16_t conn_handle, uint16_t handle, mp_obj_t obj, ble_drv_gattc_char_data_callback_t cb) { + + mp_gattc_char_data_observer = obj; + gattc_char_data_handle = cb; + + uint32_t err_code = sd_ble_gattc_read(conn_handle, + handle, + 0); + if (err_code != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not read attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } + + while (gattc_char_data_handle != NULL) { + ; + } +} + +void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data, bool w_response) { + + ble_gattc_write_params_t write_params; + + if (w_response) { + write_params.write_op = BLE_GATT_OP_WRITE_REQ; + } else { + write_params.write_op = BLE_GATT_OP_WRITE_CMD; + } + + write_params.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_CANCEL; + write_params.handle = handle; + write_params.offset = 0; + write_params.len = len; + write_params.p_value = p_data; + + m_write_done = !w_response; + + uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); + + if (err_code != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not write attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } + + while (m_write_done != true) { + ; + } +} + +void ble_drv_scan_start(void) { + SD_TEST_OR_ENABLE(); + + ble_gap_scan_params_t scan_params; + scan_params.active = 1; + scan_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); + scan_params.window = MSEC_TO_UNITS(100, UNIT_0_625_MS); + scan_params.timeout = 0; // Infinite + +#if (BLUETOOTH_SD == 130) + scan_params.selective = 0; + scan_params.p_whitelist = NULL; +#elif (BLUETOOTH_SD == 132 && BLE_API_VERSION == 3) + scan_params.use_whitelist = 0; +#endif + + uint32_t err_code; + if ((err_code = sd_ble_gap_scan_start(&scan_params)) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not start scanning. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } +} + +void ble_drv_scan_stop(void) { + sd_ble_gap_scan_stop(); +} + +void ble_drv_connect(uint8_t * p_addr, uint8_t addr_type) { + SD_TEST_OR_ENABLE(); + + ble_gap_scan_params_t scan_params; + scan_params.active = 1; + scan_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); + scan_params.window = MSEC_TO_UNITS(100, UNIT_0_625_MS); + scan_params.timeout = 0; // infinite + +#if (BLUETOOTH_SD == 130) + scan_params.selective = 0; + scan_params.p_whitelist = NULL; +#elif (BLUETOOTH_SD == 132 && BLE_API_VERSION == 3) + scan_params.use_whitelist = 0; +#endif + + ble_gap_addr_t addr; + memset(&addr, 0, sizeof(addr)); + + addr.addr_type = addr_type; + memcpy(addr.addr, p_addr, 6); + + BLE_DRIVER_LOG("GAP CONNECTING: "HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT", type: %d\n", + addr.addr[0], addr.addr[1], addr.addr[2], addr.addr[3], addr.addr[4], addr.addr[5], addr.addr_type); + + ble_gap_conn_params_t conn_params; + +// (void)sd_ble_gap_ppcp_get(&conn_params); + + // set connection parameters + memset(&conn_params, 0, sizeof(conn_params)); + + conn_params.min_conn_interval = BLE_MIN_CONN_INTERVAL; + conn_params.max_conn_interval = BLE_MAX_CONN_INTERVAL; + conn_params.slave_latency = BLE_SLAVE_LATENCY; + conn_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT; + + uint32_t err_code; + if ((err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params)) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Can not connect. status: 0x" HEX2_FMT, (uint16_t)err_code)); + } +} + +bool ble_drv_discover_services(mp_obj_t obj, uint16_t conn_handle, uint16_t start_handle, ble_drv_disc_add_service_callback_t cb) { + BLE_DRIVER_LOG("Discover primary services. Conn handle: 0x" HEX2_FMT "\n", + conn_handle); + + mp_gattc_disc_service_observer = obj; + disc_add_service_handler = cb; + + m_primary_service_found = false; + + uint32_t err_code; + err_code = sd_ble_gattc_primary_services_discover(conn_handle, + start_handle, + NULL); + if (err_code != 0) { + return false; + } + + // busy loop until last service has been iterated + while (disc_add_service_handler != NULL) { + ; + } + + if (m_primary_service_found) { + return true; + } else { + return false; + } +} + +bool ble_drv_discover_characteristic(mp_obj_t obj, + uint16_t conn_handle, + uint16_t start_handle, + uint16_t end_handle, + ble_drv_disc_add_char_callback_t cb) { + BLE_DRIVER_LOG("Discover characteristicts. Conn handle: 0x" HEX2_FMT "\n", + conn_handle); + + mp_gattc_disc_char_observer = obj; + disc_add_char_handler = cb; + + ble_gattc_handle_range_t handle_range; + handle_range.start_handle = start_handle; + handle_range.end_handle = end_handle; + + m_characteristic_found = false; + + uint32_t err_code; + err_code = sd_ble_gattc_characteristics_discover(conn_handle, &handle_range); + if (err_code != 0) { + return false; + } + + // busy loop until last service has been iterated + while (disc_add_char_handler != NULL) { + ; + } + + if (m_characteristic_found) { + return true; + } else { + return false; + } +} + +void ble_drv_discover_descriptors(void) { + +} + +#endif + +static void ble_evt_handler(ble_evt_t * p_ble_evt) { +// S132 event ranges. +// Common 0x01 -> 0x0F +// GAP 0x10 -> 0x2F +// GATTC 0x30 -> 0x4F +// GATTS 0x50 -> 0x6F +// L2CAP 0x70 -> 0x8F + switch (p_ble_evt->header.evt_id) { + case BLE_GAP_EVT_CONNECTED: + BLE_DRIVER_LOG("GAP CONNECT\n"); + m_adv_in_progress = false; + gap_event_handler(mp_gap_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gap_evt.conn_handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL); + + ble_gap_conn_params_t conn_params; + (void)sd_ble_gap_ppcp_get(&conn_params); + (void)sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, &conn_params); + break; + + case BLE_GAP_EVT_DISCONNECTED: + BLE_DRIVER_LOG("GAP DISCONNECT\n"); + gap_event_handler(mp_gap_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gap_evt.conn_handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL); + break; + + case BLE_GATTS_EVT_HVC: + gatts_event_handler(mp_gatts_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gatts_evt.params.hvc.handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL); + break; + + case BLE_GATTS_EVT_WRITE: + BLE_DRIVER_LOG("GATTS write\n"); + + uint16_t handle = p_ble_evt->evt.gatts_evt.params.write.handle; + uint16_t data_len = p_ble_evt->evt.gatts_evt.params.write.len; + uint8_t * p_data = &p_ble_evt->evt.gatts_evt.params.write.data[0]; + + gatts_event_handler(mp_gatts_observer, p_ble_evt->header.evt_id, handle, data_len, p_data); + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE: + BLE_DRIVER_LOG("GAP CONN PARAM UPDATE\n"); + break; + + case BLE_GATTS_EVT_SYS_ATTR_MISSING: + // No system attributes have been stored. + (void)sd_ble_gatts_sys_attr_set(p_ble_evt->evt.gatts_evt.conn_handle, NULL, 0, 0); + break; + +#if (BLUETOOTH_SD == 132 && BLE_API_VERSION == 3) + case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: + BLE_DRIVER_LOG("GATTS EVT EXCHANGE MTU REQUEST\n"); + (void)sd_ble_gatts_exchange_mtu_reply(p_ble_evt->evt.gatts_evt.conn_handle, 23); // MAX MTU size + break; +#endif + + case BLE_EVT_TX_COMPLETE: + BLE_DRIVER_LOG("BLE EVT TX COMPLETE\n"); + m_tx_in_progress = false; + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + BLE_DRIVER_LOG("BLE EVT SEC PARAMS REQUEST\n"); + // pairing not supported + (void)sd_ble_gap_sec_params_reply(p_ble_evt->evt.gatts_evt.conn_handle, + BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, + NULL, NULL); + break; + +#if (BLUETOOTH_SD == 130) || (BLUETOOTH_SD == 132) + case BLE_GAP_EVT_ADV_REPORT: + BLE_DRIVER_LOG("BLE EVT ADV REPORT\n"); + ble_drv_adv_data_t adv_data = { + .p_peer_addr = p_ble_evt->evt.gap_evt.params.adv_report.peer_addr.addr, + .addr_type = p_ble_evt->evt.gap_evt.params.adv_report.peer_addr.addr_type, + .is_scan_resp = p_ble_evt->evt.gap_evt.params.adv_report.scan_rsp, + .rssi = p_ble_evt->evt.gap_evt.params.adv_report.rssi, + .data_len = p_ble_evt->evt.gap_evt.params.adv_report.dlen, + .p_data = p_ble_evt->evt.gap_evt.params.adv_report.data, + .adv_type = p_ble_evt->evt.gap_evt.params.adv_report.type + }; + + // TODO: Fix unsafe callback to possible undefined callback... + adv_event_handler(mp_adv_observer, + p_ble_evt->header.evt_id, + &adv_data); + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: + BLE_DRIVER_LOG("BLE EVT CONN PARAM UPDATE REQUEST\n"); + + (void)sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, + &p_ble_evt->evt.gap_evt.params.conn_param_update_request.conn_params); + break; + + case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: + BLE_DRIVER_LOG("BLE EVT PRIMARY SERVICE DISCOVERY RESPONSE\n"); + BLE_DRIVER_LOG(">>> service count: %d\n", p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count); + + for (uint16_t i = 0; i < p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count; i++) { + ble_gattc_service_t * p_service = &p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i]; + + ble_drv_service_data_t service; + service.uuid_type = p_service->uuid.type; + service.uuid = p_service->uuid.uuid; + service.start_handle = p_service->handle_range.start_handle; + service.end_handle = p_service->handle_range.end_handle; + + disc_add_service_handler(mp_gattc_disc_service_observer, &service); + } + + if (p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count > 0) { + m_primary_service_found = true; + } + + // mark end of service discovery + disc_add_service_handler = NULL; + + break; + + case BLE_GATTC_EVT_CHAR_DISC_RSP: + BLE_DRIVER_LOG("BLE EVT CHAR DISCOVERY RESPONSE\n"); + BLE_DRIVER_LOG(">>> characteristic count: %d\n", p_ble_evt->evt.gattc_evt.params.char_disc_rsp.count); + + for (uint16_t i = 0; i < p_ble_evt->evt.gattc_evt.params.char_disc_rsp.count; i++) { + ble_gattc_char_t * p_char = &p_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[i]; + + ble_drv_char_data_t char_data; + char_data.uuid_type = p_char->uuid.type; + char_data.uuid = p_char->uuid.uuid; + char_data.decl_handle = p_char->handle_decl; + char_data.value_handle = p_char->handle_value; + + char_data.props |= (p_char->char_props.broadcast) ? UBLUEPY_PROP_BROADCAST : 0; + char_data.props |= (p_char->char_props.read) ? UBLUEPY_PROP_READ : 0; + char_data.props |= (p_char->char_props.write_wo_resp) ? UBLUEPY_PROP_WRITE_WO_RESP : 0; + char_data.props |= (p_char->char_props.write) ? UBLUEPY_PROP_WRITE : 0; + char_data.props |= (p_char->char_props.notify) ? UBLUEPY_PROP_NOTIFY : 0; + char_data.props |= (p_char->char_props.indicate) ? UBLUEPY_PROP_INDICATE : 0; + #if 0 + char_data.props |= (p_char->char_props.auth_signed_wr) ? UBLUEPY_PROP_NOTIFY : 0; + #endif + + disc_add_char_handler(mp_gattc_disc_char_observer, &char_data); + } + + if (p_ble_evt->evt.gattc_evt.params.char_disc_rsp.count > 0) { + m_characteristic_found = true; + } + + // mark end of characteristic discovery + disc_add_char_handler = NULL; + + break; + + case BLE_GATTC_EVT_READ_RSP: + BLE_DRIVER_LOG("BLE EVT READ RESPONSE, offset: 0x"HEX2_FMT", length: 0x"HEX2_FMT"\n", + p_ble_evt->evt.gattc_evt.params.read_rsp.offset, + p_ble_evt->evt.gattc_evt.params.read_rsp.len); + + gattc_char_data_handle(mp_gattc_char_data_observer, + p_ble_evt->evt.gattc_evt.params.read_rsp.len, + p_ble_evt->evt.gattc_evt.params.read_rsp.data); + + // mark end of read + gattc_char_data_handle = NULL; + + break; + + case BLE_GATTC_EVT_WRITE_RSP: + BLE_DRIVER_LOG("BLE EVT WRITE RESPONSE\n"); + m_write_done = true; + break; + + case BLE_GATTC_EVT_HVX: + BLE_DRIVER_LOG("BLE EVT HVX RESPONSE\n"); + break; +#endif + + default: + BLE_DRIVER_LOG(">>> unhandled evt: 0x" HEX2_FMT "\n", p_ble_evt->header.evt_id); + break; + } +} + +static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (GATT_MTU_SIZE_DEFAULT)] __attribute__ ((aligned (4))); + +#ifdef NRF51 +void SWI2_IRQHandler(void) { +#else +void SWI2_EGU2_IRQHandler(void) { +#endif + + uint32_t evt_id; + uint32_t err_code; + do { + err_code = sd_evt_get(&evt_id); + // TODO: handle non ble events + } while (err_code != NRF_ERROR_NOT_FOUND && err_code != NRF_SUCCESS); + + uint16_t evt_len = sizeof(m_ble_evt_buf); + do { + err_code = sd_ble_evt_get(m_ble_evt_buf, &evt_len); + ble_evt_handler((ble_evt_t *)m_ble_evt_buf); + } while (err_code != NRF_ERROR_NOT_FOUND && err_code != NRF_SUCCESS); +} + +#endif // BLUETOOTH_SD diff --git a/ports/nrf/drivers/bluetooth/ble_drv.h b/ports/nrf/drivers/bluetooth/ble_drv.h new file mode 100644 index 000000000..d8b715467 --- /dev/null +++ b/ports/nrf/drivers/bluetooth/ble_drv.h @@ -0,0 +1,129 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Glenn Ruben Bakke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BLUETOOTH_LE_DRIVER_H__ +#define BLUETOOTH_LE_DRIVER_H__ + +#if BLUETOOTH_SD + +#include <stdint.h> +#include <stdbool.h> + +#include "modubluepy.h" + +typedef struct { + uint8_t addr[6]; + uint8_t addr_type; +} ble_drv_addr_t; + +typedef struct { + uint8_t * p_peer_addr; + uint8_t addr_type; + bool is_scan_resp; + int8_t rssi; + uint8_t data_len; + uint8_t * p_data; + uint8_t adv_type; +} ble_drv_adv_data_t; + +typedef struct { + uint16_t uuid; + uint8_t uuid_type; + uint16_t start_handle; + uint16_t end_handle; +} ble_drv_service_data_t; + +typedef struct { + uint16_t uuid; + uint8_t uuid_type; + uint8_t props; + uint16_t decl_handle; + uint16_t value_handle; +} ble_drv_char_data_t; + +typedef void (*ble_drv_gap_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data); +typedef void (*ble_drv_gatts_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); +typedef void (*ble_drv_gattc_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data); +typedef void (*ble_drv_adv_evt_callback_t)(mp_obj_t self, uint16_t event_id, ble_drv_adv_data_t * data); +typedef void (*ble_drv_disc_add_service_callback_t)(mp_obj_t self, ble_drv_service_data_t * p_service_data); +typedef void (*ble_drv_disc_add_char_callback_t)(mp_obj_t self, ble_drv_char_data_t * p_desc_data); +typedef void (*ble_drv_gattc_char_data_callback_t)(mp_obj_t self, uint16_t length, uint8_t * p_data); + +uint32_t ble_drv_stack_enable(void); + +void ble_drv_stack_disable(void); + +uint8_t ble_drv_stack_enabled(void); + +void ble_drv_address_get(ble_drv_addr_t * p_addr); + +bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx); + +bool ble_drv_service_add(ubluepy_service_obj_t * p_service_obj); + +bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj); + +bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params); + +void ble_drv_advertise_stop(void); + +void ble_drv_gap_event_handler_set(mp_obj_t obs, ble_drv_gap_evt_callback_t evt_handler); + +void ble_drv_gatts_event_handler_set(mp_obj_t obj, ble_drv_gatts_evt_callback_t evt_handler); + +void ble_drv_gattc_event_handler_set(mp_obj_t obj, ble_drv_gattc_evt_callback_t evt_handler); + +void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data); + +void ble_drv_attr_c_read(uint16_t conn_handle, uint16_t handle, mp_obj_t obj, ble_drv_gattc_char_data_callback_t cb); + +void ble_drv_attr_s_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data); + +void ble_drv_attr_s_notify(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data); + +void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data, bool w_response); + +void ble_drv_scan_start(void); + +void ble_drv_scan_stop(void); + +void ble_drv_adv_report_handler_set(mp_obj_t obj, ble_drv_adv_evt_callback_t evt_handler); + +void ble_drv_connect(uint8_t * p_addr, uint8_t addr_type); + +bool ble_drv_discover_services(mp_obj_t obj, uint16_t conn_handle, uint16_t start_handle, ble_drv_disc_add_service_callback_t cb); + +bool ble_drv_discover_characteristic(mp_obj_t obj, + uint16_t conn_handle, + uint16_t start_handle, + uint16_t end_handle, + ble_drv_disc_add_char_callback_t cb); + +void ble_drv_discover_descriptors(void); + +#endif // BLUETOOTH_SD + +#endif // BLUETOOTH_LE_DRIVER_H__ diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c new file mode 100644 index 000000000..cc829750c --- /dev/null +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -0,0 +1,266 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Glenn Ruben Bakke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if BLUETOOTH_SD + +#include <string.h> +#include "ble_uart.h" +#include "ringbuffer.h" +#include "hal/hal_time.h" + +#if MICROPY_PY_BLE_NUS + +#if BLUETOOTH_WEBBLUETOOTH_REPL +#include "hal_time.h" +#endif // BLUETOOTH_WEBBLUETOOTH_REPL + +static ubluepy_uuid_obj_t uuid_obj_service = { + .base.type = &ubluepy_uuid_type, + .type = UBLUEPY_UUID_128_BIT, + .value = {0x01, 0x00} +}; + +static ubluepy_uuid_obj_t uuid_obj_char_tx = { + .base.type = &ubluepy_uuid_type, + .type = UBLUEPY_UUID_128_BIT, + .value = {0x03, 0x00} +}; + +static ubluepy_uuid_obj_t uuid_obj_char_rx = { + .base.type = &ubluepy_uuid_type, + .type = UBLUEPY_UUID_128_BIT, + .value = {0x02, 0x00} +}; + +static ubluepy_service_obj_t ble_uart_service = { + .base.type = &ubluepy_service_type, + .p_uuid = &uuid_obj_service, + .type = UBLUEPY_SERVICE_PRIMARY +}; + +static ubluepy_characteristic_obj_t ble_uart_char_rx = { + .base.type = &ubluepy_characteristic_type, + .p_uuid = &uuid_obj_char_rx, + .props = UBLUEPY_PROP_WRITE | UBLUEPY_PROP_WRITE_WO_RESP, + .attrs = 0, +}; + +static ubluepy_characteristic_obj_t ble_uart_char_tx = { + .base.type = &ubluepy_characteristic_type, + .p_uuid = &uuid_obj_char_tx, + .props = UBLUEPY_PROP_NOTIFY, + .attrs = UBLUEPY_ATTR_CCCD, +}; + +static ubluepy_peripheral_obj_t ble_uart_peripheral = { + .base.type = &ubluepy_peripheral_type, + .conn_handle = 0xFFFF, +}; + +static volatile bool m_cccd_enabled; +static volatile bool m_connected; + +ringBuffer_typedef(uint8_t, ringbuffer_t); + +static ringbuffer_t m_rx_ring_buffer; +static ringbuffer_t * mp_rx_ring_buffer = &m_rx_ring_buffer; +static uint8_t m_rx_ring_buffer_data[128]; + +static ubluepy_advertise_data_t m_adv_data_uart_service; + +#if BLUETOOTH_WEBBLUETOOTH_REPL +static ubluepy_advertise_data_t m_adv_data_eddystone_url; +#endif // BLUETOOTH_WEBBLUETOOTH_REPL + +int mp_hal_stdin_rx_chr(void) { + while (isBufferEmpty(mp_rx_ring_buffer)) { + ; + } + + uint8_t byte; + bufferRead(mp_rx_ring_buffer, byte); + return (int)byte; +} + +void mp_hal_stdout_tx_strn(const char *str, size_t len) { + uint8_t *buf = (uint8_t *)str; + size_t send_len; + + while (len > 0) { + if (len >= 20) { + send_len = 20; // (GATT_MTU_SIZE_DEFAULT - 3) + } else { + send_len = len; + } + + ubluepy_characteristic_obj_t * p_char = &ble_uart_char_tx; + + ble_drv_attr_s_notify(p_char->p_service->p_periph->conn_handle, + p_char->handle, + send_len, + buf); + + len -= send_len; + buf += send_len; + } +} + +void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { + mp_hal_stdout_tx_strn(str, len); +} + +STATIC void gap_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data) { + ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in); + + if (event_id == 16) { // connect event + self->conn_handle = conn_handle; + m_connected = true; + } else if (event_id == 17) { // disconnect event + self->conn_handle = 0xFFFF; // invalid connection handle + m_connected = false; + } +} + +STATIC void gatts_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data) { + ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in); + (void)self; + + if (event_id == 80) { // gatts write + if (ble_uart_char_tx.cccd_handle == attr_handle) { + m_cccd_enabled = true; + } else if (ble_uart_char_rx.handle == attr_handle) { + for (uint16_t i = 0; i < length; i++) { + bufferWrite(mp_rx_ring_buffer, data[i]); + } + } + } +} + +void ble_uart_init0(void) { + uint8_t base_uuid[] = {0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}; + uint8_t uuid_vs_idx; + + (void)ble_drv_uuid_add_vs(base_uuid, &uuid_vs_idx); + + uuid_obj_service.uuid_vs_idx = uuid_vs_idx; + uuid_obj_char_tx.uuid_vs_idx = uuid_vs_idx; + uuid_obj_char_rx.uuid_vs_idx = uuid_vs_idx; + + (void)ble_drv_service_add(&ble_uart_service); + ble_uart_service.char_list = mp_obj_new_list(0, NULL); + + // add TX characteristic + ble_uart_char_tx.service_handle = ble_uart_service.handle; + bool retval = ble_drv_characteristic_add(&ble_uart_char_tx); + if (retval) { + ble_uart_char_tx.p_service = &ble_uart_service; + } + mp_obj_list_append(ble_uart_service.char_list, MP_OBJ_FROM_PTR(&ble_uart_char_tx)); + + // add RX characteristic + ble_uart_char_rx.service_handle = ble_uart_service.handle; + retval = ble_drv_characteristic_add(&ble_uart_char_rx); + if (retval) { + ble_uart_char_rx.p_service = &ble_uart_service; + } + mp_obj_list_append(ble_uart_service.char_list, MP_OBJ_FROM_PTR(&ble_uart_char_rx)); + + // setup the peripheral + ble_uart_peripheral.service_list = mp_obj_new_list(0, NULL); + mp_obj_list_append(ble_uart_peripheral.service_list, MP_OBJ_FROM_PTR(&ble_uart_service)); + ble_uart_service.p_periph = &ble_uart_peripheral; + + ble_drv_gap_event_handler_set(MP_OBJ_FROM_PTR(&ble_uart_peripheral), gap_event_handler); + ble_drv_gatts_event_handler_set(MP_OBJ_FROM_PTR(&ble_uart_peripheral), gatts_event_handler); + + ble_uart_peripheral.conn_handle = 0xFFFF; + + char device_name[] = "mpus"; + + mp_obj_t service_list = mp_obj_new_list(0, NULL); + mp_obj_list_append(service_list, MP_OBJ_FROM_PTR(&ble_uart_service)); + + mp_obj_t * services = NULL; + mp_uint_t num_services; + mp_obj_get_array(service_list, &num_services, &services); + + m_adv_data_uart_service.p_services = services; + m_adv_data_uart_service.num_of_services = num_services; + m_adv_data_uart_service.p_device_name = (uint8_t *)device_name; + m_adv_data_uart_service.device_name_len = strlen(device_name); + m_adv_data_uart_service.connectable = true; + m_adv_data_uart_service.p_data = NULL; + +#if BLUETOOTH_WEBBLUETOOTH_REPL + // for now point eddystone URL to https://goo.gl/x46FES => https://glennrub.github.io/webbluetooth/micropython/repl/ + static uint8_t eddystone_url_data[27] = {0x2, 0x1, 0x6, + 0x3, 0x3, 0xaa, 0xfe, + 19, 0x16, 0xaa, 0xfe, 0x10, 0xee, 0x3, 'g', 'o', 'o', '.', 'g', 'l', '/', 'x', '4', '6', 'F', 'E', 'S'}; + // eddystone url adv data + m_adv_data_eddystone_url.p_data = eddystone_url_data; + m_adv_data_eddystone_url.data_len = sizeof(eddystone_url_data); + m_adv_data_eddystone_url.connectable = false; +#endif + + m_cccd_enabled = false; + + // initialize ring buffer + m_rx_ring_buffer.size = sizeof(m_rx_ring_buffer_data) + 1; + m_rx_ring_buffer.start = 0; + m_rx_ring_buffer.end = 0; + m_rx_ring_buffer.elems = m_rx_ring_buffer_data; + + m_connected = false; + + ble_uart_advertise(); +} + +void ble_uart_advertise(void) { +#if BLUETOOTH_WEBBLUETOOTH_REPL + while (!m_connected) { + (void)ble_drv_advertise_data(&m_adv_data_uart_service); + mp_hal_delay_ms(500); + (void)ble_drv_advertise_data(&m_adv_data_eddystone_url); + mp_hal_delay_ms(500); + } + + ble_drv_advertise_stop(); +#else + (void)ble_drv_advertise_data(&m_adv_data_uart_service); +#endif // BLUETOOTH_WEBBLUETOOTH_REPL +} + +bool ble_uart_connected(void) { + return (m_connected); +} + +bool ble_uart_enabled(void) { + return (m_cccd_enabled); +} + +#endif // MICROPY_PY_BLE_NUS + +#endif // BLUETOOTH_SD diff --git a/ports/nrf/drivers/bluetooth/ble_uart.h b/ports/nrf/drivers/bluetooth/ble_uart.h new file mode 100644 index 000000000..e67176a26 --- /dev/null +++ b/ports/nrf/drivers/bluetooth/ble_uart.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Glenn Ruben Bakke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BLUETOOTH_LE_UART_H__ +#define BLUETOOTH_LE_UART_H__ + +#if BLUETOOTH_SD + +#include "modubluepy.h" +#include "ble_drv.h" + +void ble_uart_init0(void); +void ble_uart_advertise(void); +bool ble_uart_connected(void); +bool ble_uart_enabled(void); + +#endif // BLUETOOTH_SD + +#endif // BLUETOOTH_LE_UART_H__ diff --git a/ports/nrf/drivers/bluetooth/bluetooth_common.mk b/ports/nrf/drivers/bluetooth/bluetooth_common.mk new file mode 100644 index 000000000..38c604e04 --- /dev/null +++ b/ports/nrf/drivers/bluetooth/bluetooth_common.mk @@ -0,0 +1,55 @@ + +SOFTDEV_HEX_NAME ?= +SOFTDEV_HEX_PATH ?= + +ifeq ($(SD), s110) + INC += -Idrivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_API/include + CFLAGS += -DBLUETOOTH_SD_DEBUG=1 + CFLAGS += -DBLUETOOTH_SD=110 + SOFTDEV_HEX_NAME = $(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_softdevice.hex + SOFTDEV_HEX_PATH = drivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION) + +else ifeq ($(SD), s120) + $(error No BLE wrapper available yet) +else ifeq ($(SD), s130) + $(error No BLE wrapper available yet) +else ifeq ($(SD), s132) + INC += -Idrivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_API/include + INC += -Idrivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_API/include/$(MCU_VARIANT) + CFLAGS += -DBLUETOOTH_SD_DEBUG=1 + CFLAGS += -DBLUETOOTH_SD=132 + +ifeq ($(SOFTDEV_VERSION), 2.0.1) + CFLAGS += -DBLE_API_VERSION=2 +else ifeq ($(SOFTDEV_VERSION), 3.0.0) + CFLAGS += -DBLE_API_VERSION=3 +endif + + SOFTDEV_HEX_NAME = $(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_softdevice.hex + SOFTDEV_HEX_PATH = drivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION) +else + $(error Incorrect softdevice set flag) +endif + +define STACK_MISSING_ERROR + + +###### ERROR: Bluetooth LE Stack not found ############ +# # +# The build target requires a Bluetooth LE stack. # +# $(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION) Bluetooth LE stack not found. # +# # +# Please run the download script: # +# # +# drivers/bluetooth/download_ble_stack.sh # +# # +####################################################### + +endef + + +SOFTDEV_HEX = $(SOFTDEV_HEX_PATH)/$(SOFTDEV_HEX_NAME) + +ifeq ($(shell test ! -e $(SOFTDEV_HEX) && echo -n no),no) + $(error $(STACK_MISSING_ERROR)) +endif diff --git a/ports/nrf/drivers/bluetooth/download_ble_stack.sh b/ports/nrf/drivers/bluetooth/download_ble_stack.sh new file mode 100755 index 000000000..537742605 --- /dev/null +++ b/ports/nrf/drivers/bluetooth/download_ble_stack.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +function download_s110_nrf51_8_0_0 +{ + echo "" + echo "####################################" + echo "### Downloading s110_nrf51_8.0.0 ###" + echo "####################################" + echo "" + + mkdir -p $1/s110_nrf51_8.0.0 + cd $1/s110_nrf51_8.0.0 + wget https://www.nordicsemi.com/eng/nordic/download_resource/45846/3/78153065/80234 + mv 80234 temp.zip + unzip -u temp.zip + rm temp.zip + cd - +} + +function download_s132_nrf52_2_0_1 +{ + echo "" + echo "####################################" + echo "### Downloading s132_nrf52_2.0.1 ###" + echo "####################################" + echo "" + + mkdir -p $1/s132_nrf52_2.0.1 + cd $1/s132_nrf52_2.0.1 + wget https://www.nordicsemi.com/eng/nordic/download_resource/51479/6/84640562/95151 + mv 95151 temp.zip + unzip -u temp.zip + rm temp.zip + cd - +} + +function download_s132_nrf52_3_0_0 +{ + echo "" + echo "####################################" + echo "### Downloading s132_nrf52_3.0.0 ###" + echo "####################################" + echo "" + + mkdir -p $1/s132_nrf52_3.0.0 + cd $1/s132_nrf52_3.0.0 + + wget https://www.nordicsemi.com/eng/nordic/download_resource/56261/6/26298825/108144 + mv 108144 temp.zip + unzip -u temp.zip + rm temp.zip + cd - +} + + +SCRIPT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +if [ $# -eq 0 ]; then + echo "No Bluetooth LE stack defined, downloading all." + download_s110_nrf51_8_0_0 ${SCRIPT_DIR} + download_s132_nrf52_2_0_1 ${SCRIPT_DIR} + download_s132_nrf52_3_0_0 ${SCRIPT_DIR} +else + case $1 in + "s110_nrf51" ) + download_s110_nrf51_8_0_0 ${SCRIPT_DIR} ;; + "s132_nrf52_2_0_1" ) + download_s132_nrf52_2_0_1 ${SCRIPT_DIR} ;; + "s132_nrf52_3_0_0" ) + download_s132_nrf52_3_0_0 ${SCRIPT_DIR} ;; + esac +fi + +exit 0 diff --git a/ports/nrf/drivers/bluetooth/ringbuffer.h b/ports/nrf/drivers/bluetooth/ringbuffer.h new file mode 100644 index 000000000..3438b5c9b --- /dev/null +++ b/ports/nrf/drivers/bluetooth/ringbuffer.h @@ -0,0 +1,99 @@ +/* The MIT License (MIT) + * + * Copyright (c) 2013 Philip Thrasher + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * Philip Thrasher's Crazy Awesome Ring Buffer Macros! + * + * Below you will find some naughty macros for easy owning and manipulating + * generic ring buffers. Yes, they are slightly evil in readability, but they + * are really fast, and they work great. + * + * Example usage: + * + * #include <stdio.h> + * + * // So we can use this in any method, this gives us a typedef + * // named 'intBuffer'. + * ringBuffer_typedef(int, intBuffer); + * + * int main() { + * // Declare vars. + * intBuffer myBuffer; + * + * bufferInit(myBuffer,1024,int); + * + * // We must have the pointer. All of the macros deal with the pointer. + * // (except for init.) + * intBuffer* myBuffer_ptr; + * myBuffer_ptr = &myBuffer; + * + * // Write two values. + * bufferWrite(myBuffer_ptr,37); + * bufferWrite(myBuffer_ptr,72); + * + * // Read a value into a local variable. + * int first; + * bufferRead(myBuffer_ptr,first); + * assert(first == 37); // true + * + * int second; + * bufferRead(myBuffer_ptr,second); + * assert(second == 72); // true + * + * return 0; + * } + * + */ + +#ifndef _ringbuffer_h +#define _ringbuffer_h + +#define ringBuffer_typedef(T, NAME) \ + typedef struct { \ + int size; \ + volatile int start; \ + volatile int end; \ + T* elems; \ + } NAME + +#define bufferInit(BUF, S, T) \ + BUF.size = S+1; \ + BUF.start = 0; \ + BUF.end = 0; \ + BUF.elems = (T*)calloc(BUF.size, sizeof(T)) + + +#define bufferDestroy(BUF) free(BUF->elems) +#define nextStartIndex(BUF) ((BUF->start + 1) % BUF->size) +#define nextEndIndex(BUF) ((BUF->end + 1) % BUF->size) +#define isBufferEmpty(BUF) (BUF->end == BUF->start) +#define isBufferFull(BUF) (nextEndIndex(BUF) == BUF->start) + +#define bufferWrite(BUF, ELEM) \ + BUF->elems[BUF->end] = ELEM; \ + BUF->end = (BUF->end + 1) % BUF->size; \ + if (isBufferEmpty(BUF)) { \ + BUF->start = nextStartIndex(BUF); \ + } + +#define bufferRead(BUF, ELEM) \ + ELEM = BUF->elems[BUF->start]; \ + BUF->start = nextStartIndex(BUF); + +#endif diff --git a/ports/nrf/drivers/softpwm.c b/ports/nrf/drivers/softpwm.c new file mode 100644 index 000000000..22564f7d0 --- /dev/null +++ b/ports/nrf/drivers/softpwm.c @@ -0,0 +1,257 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Mark Shannon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" + +#if MICROPY_PY_MACHINE_SOFT_PWM + +#include "stddef.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "hal_timer.h" +#include "hal_gpio.h" +#include "pin.h" + +#include "ticker.h" + +#define CYCLES_PER_MICROSECONDS 16 + +#define MICROSECONDS_PER_TICK 16 +#define CYCLES_PER_TICK (CYCLES_PER_MICROSECONDS*MICROSECONDS_PER_TICK) +// This must be an integer multiple of MICROSECONDS_PER_TICK +#define MICROSECONDS_PER_MACRO_TICK 6000 +#define MILLISECONDS_PER_MACRO_TICK 6 + +#define PWM_TICKER_INDEX 2 + +// Default period of 20ms +#define DEFAULT_PERIOD ((20*1000)/MICROSECONDS_PER_TICK) + +typedef struct _pwm_event { + uint16_t time; + uint8_t pin; + uint8_t turn_on; +} pwm_event; + +typedef struct _pwm_events { + uint8_t count; + uint16_t period; + uint32_t all_pins; + pwm_event events[1]; +} pwm_events; + +static const pwm_events OFF_EVENTS = { + .count = 1, + .period = DEFAULT_PERIOD, + .all_pins = 0, + .events = { + { + .time = 1024, + .pin = 31, + .turn_on = 0 + } + } +}; + +#define active_events MP_STATE_PORT(pwm_active_events) +#define pending_events MP_STATE_PORT(pwm_pending_events) + +void softpwm_init(void) { + active_events = &OFF_EVENTS; + pending_events = NULL; +} + +static uint8_t next_event = 0; + +static inline int32_t pwm_get_period_ticks(void) { + const pwm_events *tmp = pending_events; + if (tmp == NULL) + tmp = active_events; + return tmp->period; +} + +#if 0 +void pwm_dump_events(const pwm_events *events) { + printf("Count %d, period %d, all pins %d\r\n", events->count, events->period, events->all_pins); + for (uint32_t i = 0; i < events->count; i++) { + const pwm_event *event = &events->events[i]; + printf("Event. pin: %d, duty cycle: %d, turn_on: %d\r\n", + event->pin, event->time, event->turn_on); + } +} + +void pwm_dump_state(void) { + while(pending_events); + pwm_dump_events(active_events); +} +#endif + +static const pwm_events *swap_pending(const pwm_events *in) { + __disable_irq(); + const pwm_events *result = pending_events; + pending_events = in; + __enable_irq(); + return result; +} + +static pwm_events *copy_events(const pwm_events *orig, uint32_t count) { + pwm_events *events = m_malloc(sizeof(pwm_events) + (count-1)*sizeof(pwm_event)); + events->count = count; + uint32_t copy = count > orig->count ? orig->count : count; + for (uint32_t i = 0; i < copy; i++) { + events->events[i] = orig->events[i]; + } + return events; +} + +static int find_pin_in_events(const pwm_events *events, uint32_t pin) { + for (int i = 0; i < events->count; i++) { + if (events->events[i].pin == pin) + return i; + } + return -1; +} + +static void sort_events(pwm_events *events) { + // Insertion sort + for (int32_t i = 1; i < events->count; i++) { + pwm_event x = events->events[i]; + int32_t j; + for (j = i - 1; j >= 0 && events->events[j].time > x.time; j--) { + events->events[j+1] = events->events[j]; + } + events->events[j+1] = x; + } +} + +int32_t pwm_callback(void) { + int32_t tdiff; + const pwm_events *events = active_events; + const pwm_event *event = &events->events[next_event]; + int32_t tnow = (event->time*events->period)>>10; + do { + if (event->turn_on) { + hal_gpio_pin_set(0, event->pin); + next_event++; + } else { + hal_gpio_out_clear(0, events->all_pins); + next_event = 0; + tnow = 0; + if (pending_events) { + events = pending_events; + active_events = events; + pending_events = NULL; + } + } + event = &events->events[next_event]; + tdiff = ((event->time*events->period)>>10) - tnow; + } while (tdiff == 0); + return tdiff; +} + +void pwm_start(void) { + set_ticker_callback(PWM_TICKER_INDEX, pwm_callback, 120); +} + +void pwm_stop(void) { + clear_ticker_callback(PWM_TICKER_INDEX); +} + +static void pwm_set_period_ticks(int32_t ticks) { + const pwm_events *old_events = swap_pending(NULL); + if (old_events == NULL) { + old_events = active_events; + } + pwm_events *events = copy_events(old_events, old_events->count); + events->all_pins = old_events->all_pins; + events->period = ticks; + pending_events = events; +} + +int pwm_set_period_us(int32_t us) { + if ((us < 256) || + (us > 1000000)) { + return -1; + } + pwm_set_period_ticks(us/MICROSECONDS_PER_TICK); + return 0; +} + +int32_t pwm_get_period_us(void) { + return pwm_get_period_ticks()*MICROSECONDS_PER_TICK; +} + +void pwm_set_duty_cycle(int32_t pin, uint32_t value) { + if (value >= (1<<10)) { + value = (1<<10)-1; + } + uint32_t turn_on_time = 1024-value; + const pwm_events *old_events = swap_pending(NULL); + if (old_events == NULL) { + old_events = active_events; + } + if (((1<<pin)&old_events->all_pins) == 0) { + hal_gpio_cfg_pin(0, pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_PULL_DISABLED); + } + int ev = find_pin_in_events(old_events, pin); + pwm_events *events; + if (ev < 0 && value == 0) { + return; + } else if (ev < 0) { + events = copy_events(old_events, old_events->count+1); + events->all_pins = old_events->all_pins | (1<<pin); + events->events[old_events->count].time = turn_on_time; + events->events[old_events->count].pin = pin; + events->events[old_events->count].turn_on = 1; + } else if (value == 0) { + events = copy_events(old_events, old_events->count-1); + events->all_pins = old_events->all_pins & ~(1<<pin); + if (ev < old_events->count-1) { + events->events[ev] = old_events->events[old_events->count-1]; + } + } else { + events = copy_events(old_events, old_events->count); + events->all_pins = old_events->all_pins; + events->events[ev].time = turn_on_time; + } + events->period = old_events->period; + sort_events(events); + pending_events = events; + return; +} + +void pwm_release(int32_t pin) { + pwm_set_duty_cycle(pin, 0); + const pwm_events *ev = active_events; + int i = find_pin_in_events(ev, pin); + if (i < 0) + return; + // If i >= 0 it means that `ev` is in RAM, so it safe to discard the const qualifier + ((pwm_events *)ev)->events[i].pin = 31; + hal_gpio_pin_clear(0, pin); +} + +#endif // MICROPY_PY_MACHINE_SOFT_PWM diff --git a/ports/nrf/drivers/softpwm.h b/ports/nrf/drivers/softpwm.h new file mode 100644 index 000000000..a73c15cd8 --- /dev/null +++ b/ports/nrf/drivers/softpwm.h @@ -0,0 +1,13 @@ +#ifndef __MICROPY_INCLUDED_LIB_PWM_H__ +#define __MICROPY_INCLUDED_LIB_PWM_H__ + +void softpwm_init(void); +void pwm_start(void); +void pwm_stop(void); + +int pwm_set_period_us(int32_t us); +int32_t pwm_get_period_us(void); +void pwm_set_duty_cycle(int32_t pin, int32_t value); +void pwm_release(int32_t pin); + +#endif // __MICROPY_INCLUDED_LIB_PWM_H__ diff --git a/ports/nrf/drivers/ticker.c b/ports/nrf/drivers/ticker.c new file mode 100644 index 000000000..aa730d643 --- /dev/null +++ b/ports/nrf/drivers/ticker.c @@ -0,0 +1,162 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Mark Shannon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" + +#if MICROPY_PY_MACHINE_SOFT_PWM + +#include "ticker.h" +#include "hal_irq.h" + +#define FastTicker NRF_TIMER1 +#define FastTicker_IRQn TIMER1_IRQn +#define FastTicker_IRQHandler TIMER1_IRQHandler + +#define SlowTicker_IRQn SWI0_IRQn +#define SlowTicker_IRQHandler SWI0_IRQHandler + +// Ticker callback function called every MACRO_TICK +static volatile callback_ptr slow_ticker; + +void ticker_init(callback_ptr slow_ticker_callback) { + slow_ticker = slow_ticker_callback; + + NRF_TIMER_Type *ticker = FastTicker; +#ifdef NRF51 + ticker->POWER = 1; +#endif + __NOP(); + ticker_stop(); + ticker->TASKS_CLEAR = 1; + ticker->CC[3] = MICROSECONDS_PER_MACRO_TICK; + ticker->MODE = TIMER_MODE_MODE_Timer; + ticker->BITMODE = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos; + ticker->PRESCALER = 4; // 1 tick == 1 microsecond + ticker->INTENSET = TIMER_INTENSET_COMPARE3_Msk; + ticker->SHORTS = 0; + +#ifdef NRF51 + hal_irq_priority(FastTicker_IRQn, 1); +#else + hal_irq_priority(FastTicker_IRQn, 2); +#endif + + hal_irq_priority(SlowTicker_IRQn, 3); + hal_irq_priority(SlowTicker_IRQn, 3); + + hal_irq_enable(SlowTicker_IRQn); +} + +/* Start and stop timer 1 including workarounds for Anomaly 73 for Timer +* http://www.nordicsemi.com/eng/content/download/29490/494569/file/nRF51822-PAN%20v3.0.pdf +*/ +void ticker_start(void) { + hal_irq_enable(FastTicker_IRQn); +#ifdef NRF51 + *(uint32_t *)0x40009C0C = 1; // for Timer 1 +#endif + FastTicker->TASKS_START = 1; +} + +void ticker_stop(void) { + hal_irq_disable(FastTicker_IRQn); + FastTicker->TASKS_STOP = 1; +#ifdef NRF51 + *(uint32_t *)0x40009C0C = 0; // for Timer 1 +#endif +} + +int32_t noop(void) { + return -1; +} + +volatile uint32_t ticks; + +static ticker_callback_ptr callbacks[3] = { noop, noop, noop }; + +void FastTicker_IRQHandler(void) { + NRF_TIMER_Type *ticker = FastTicker; + ticker_callback_ptr *call = callbacks; + if (ticker->EVENTS_COMPARE[0]) { + ticker->EVENTS_COMPARE[0] = 0; + ticker->CC[0] += call[0]()*MICROSECONDS_PER_TICK; + } + if (ticker->EVENTS_COMPARE[1]) { + ticker->EVENTS_COMPARE[1] = 0; + ticker->CC[1] += call[1]()*MICROSECONDS_PER_TICK; + } + if (ticker->EVENTS_COMPARE[2]) { + ticker->EVENTS_COMPARE[2] = 0; + ticker->CC[2] += call[2]()*MICROSECONDS_PER_TICK; + } + if (ticker->EVENTS_COMPARE[3]) { + ticker->EVENTS_COMPARE[3] = 0; + ticker->CC[3] += MICROSECONDS_PER_MACRO_TICK; + ticks += MILLISECONDS_PER_MACRO_TICK; + hal_irq_pending(SlowTicker_IRQn); + } +} + + +static const uint32_t masks[3] = { + TIMER_INTENCLR_COMPARE0_Msk, + TIMER_INTENCLR_COMPARE1_Msk, + TIMER_INTENCLR_COMPARE2_Msk, +}; + +int set_ticker_callback(uint32_t index, ticker_callback_ptr func, int32_t initial_delay_us) { + if (index > 3) + return -1; + NRF_TIMER_Type *ticker = FastTicker; + callbacks[index] = noop; + ticker->INTENCLR = masks[index]; + ticker->TASKS_CAPTURE[index] = 1; + uint32_t t = FastTicker->CC[index]; + // Need to make sure that set tick is aligned to lastest tick + // Use CC[3] as a reference, as that is always up-to-date. + int32_t cc3 = FastTicker->CC[3]; + int32_t delta = t+initial_delay_us-cc3; + delta = (delta/MICROSECONDS_PER_TICK+1)*MICROSECONDS_PER_TICK; + callbacks[index] = func; + ticker->INTENSET = masks[index]; + FastTicker->CC[index] = cc3 + delta; + return 0; +} + +int clear_ticker_callback(uint32_t index) { + if (index > 3) + return -1; + FastTicker->INTENCLR = masks[index]; + callbacks[index] = noop; + return 0; +} + +void SlowTicker_IRQHandler(void) +{ + slow_ticker(); +} + +#endif // MICROPY_PY_MACHINE_SOFT_PWM diff --git a/ports/nrf/drivers/ticker.h b/ports/nrf/drivers/ticker.h new file mode 100644 index 000000000..6ac87cd50 --- /dev/null +++ b/ports/nrf/drivers/ticker.h @@ -0,0 +1,30 @@ +#ifndef __MICROPY_INCLUDED_LIB_TICKER_H__ +#define __MICROPY_INCLUDED_LIB_TICKER_H__ + +/************************************* + * 62.5kHz (16µs cycle time) ticker. + ************************************/ + +#include "nrf.h" + +typedef void (*callback_ptr)(void); +typedef int32_t (*ticker_callback_ptr)(void); + +void ticker_init(callback_ptr slow_ticker_callback); +void ticker_start(void); +void ticker_stop(void); + +int clear_ticker_callback(uint32_t index); +int set_ticker_callback(uint32_t index, ticker_callback_ptr func, int32_t initial_delay_us); + +int set_low_priority_callback(callback_ptr callback, int id); + +#define CYCLES_PER_MICROSECONDS 16 + +#define MICROSECONDS_PER_TICK 16 +#define CYCLES_PER_TICK (CYCLES_PER_MICROSECONDS*MICROSECONDS_PER_TICK) +// This must be an integer multiple of MICROSECONDS_PER_TICK +#define MICROSECONDS_PER_MACRO_TICK 6000 +#define MILLISECONDS_PER_MACRO_TICK 6 + +#endif // __MICROPY_INCLUDED_LIB_TICKER_H__
\ No newline at end of file |
