diff --git a/.gitignore b/.gitignore index 87128e6426d25b53d2044f0a00fe805d8e571e8d..9afc659eb12e7c32954abc4372b73d9f23812ebd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ !.gitignore !main.c +!ble.c +!imu.c !template.eww !ex_2/pca10040/blank/ses/* diff --git a/project/ble.c b/project/ble.c new file mode 100644 index 0000000000000000000000000000000000000000..2afc9dc19173d60b60c73c8c75640b465920873a --- /dev/null +++ b/project/ble.c @@ -0,0 +1,632 @@ +// Flag to keep track of when an indication confirmation is pending +static bool m_hts_meas_ind_conf_pending = false; +volatile uint32_t hts_counter; // hold dummy hts data + +// Function declarations +static void on_hts_evt(ble_hts_t * p_hts, ble_hts_evt_t * p_evt); +static void temperature_measurement_send(void); + +// YOUR_JOB: Use UUIDs for service(s) used in your application. +static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifiers. */ +{ + {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE} +}; + +static void advertising_start(bool erase_bonds); + +/* +* Function for generating a dummy temperature information packet. +*/ +static void generate_temperature(ble_hts_meas_t * p_meas) { + static ble_date_time_t time_stamp = { 2018, 16, 10, 16, 15, 0 }; + + uint32_t celciusX100; + + p_meas->temp_in_fahr_units = false; + p_meas->time_stamp_present = true; + p_meas->temp_type_present = (TEMP_TYPE_AS_CHARACTERISTIC ? false : true); + + celciusX100 = 2000+hts_counter++; // one unit is 0.01 Celcius + + p_meas->temp_in_celcius.exponent = -2; + p_meas->temp_in_celcius.mantissa = celciusX100; + p_meas->temp_in_fahr.exponent = -2; + p_meas->temp_in_fahr.mantissa = (32 * 100) + ((celciusX100 * 9) / 5); + p_meas->time_stamp = time_stamp; + p_meas->temp_type = BLE_HTS_TEMP_TYPE_FINGER; + + // update simulated time stamp + time_stamp.seconds += 27; + if (time_stamp.seconds > 59){ + time_stamp.seconds -= 60; + time_stamp.minutes++; + if (time_stamp.minutes > 59){ + time_stamp.minutes = 0; + } + } +} + +/* +* Function for handling the Health Thermometer Service events. +*/ +static void on_hts_evt(ble_hts_t * p_hts, ble_hts_evt_t * p_evt) { + switch (p_evt->evt_type) { + case BLE_HTS_EVT_INDICATION_ENABLED: + // Indication has been enabled, send a single temperature measurement + temperature_measurement_send(); + break; + case BLE_HTS_EVT_INDICATION_CONFIRMED: + m_hts_meas_ind_conf_pending = false; + break; + default: + // No implementation needed. + break; + } +} + +static void temperature_measurement_send(void) { + ble_hts_meas_t hts_meas; //Health Thermometer Service measurement structure + ret_code_t + err_code; + + if (!m_hts_meas_ind_conf_pending) { + generate_temperature(&hts_meas); + err_code = ble_hts_measurement_send(&m_hts, &hts_meas); + + switch (err_code) { + case NRF_SUCCESS: + // Measurement was successfully sent, wait for confirmation. + m_hts_meas_ind_conf_pending = true; + break; + case NRF_ERROR_INVALID_STATE: + // Ignore error. + break; + default: + APP_ERROR_HANDLER(err_code); + break; + } + } +} + +/**@brief Callback function for asserts in the SoftDevice. + * + * @details This function will be called in case of an assert in the SoftDevice. + * + * @warning This handler is an example only and does not fit a final product. You need to analyze + * how your product is supposed to react in case of Assert. + * @warning On assert from the SoftDevice, the system can only recover on reset. + * + * @param[in] line_num Line number of the failing ASSERT call. + * @param[in] file_name File name of the failing ASSERT call. + */ +void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) { + app_error_handler(DEAD_BEEF, line_num, p_file_name); +} + + +/**@brief Function for handling Peer Manager events. + * + * @param[in] p_evt Peer Manager event. + */ +static void pm_evt_handler(pm_evt_t const * p_evt) { + pm_handler_on_pm_evt(p_evt); + pm_handler_flash_clean(p_evt); + + switch (p_evt->evt_id) { + case PM_EVT_PEERS_DELETE_SUCCEEDED: + advertising_start(false); + break; + + default: + break; + } +} + + +/**@brief Function for the Timer initialization. + * + * @details Initializes the timer module. This creates and starts application timers. + */ +static void timers_init(void) { + // Initialize timer module. + ret_code_t err_code = app_timer_init(); + APP_ERROR_CHECK(err_code); + + // Create timers. + + /* YOUR_JOB: Create any timers to be used by the application. + Below is an example of how to create a timer. + For every new timer needed, increase the value of the macro APP_TIMER_MAX_TIMERS by + one. + ret_code_t err_code; + err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler); + APP_ERROR_CHECK(err_code); */ +} + + +/**@brief Function for the GAP initialization. + * + * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the + * device including the device name, appearance, and the preferred connection parameters. + */ +static void gap_params_init(void) { + ret_code_t err_code; + 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); + + err_code = sd_ble_gap_device_name_set(&sec_mode, + (const uint8_t *)DEVICE_NAME, + strlen(DEVICE_NAME)); + APP_ERROR_CHECK(err_code); + + /* YOUR_JOB: Use an appearance value matching the application's use case. + err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_); + APP_ERROR_CHECK(err_code); */ + + memset(&gap_conn_params, 0, sizeof(gap_conn_params)); + + gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL; + gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL; + gap_conn_params.slave_latency = SLAVE_LATENCY; + gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; + + err_code = sd_ble_gap_ppcp_set(&gap_conn_params); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Function for initializing the GATT module. + */ +static void gatt_init(void) { + ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Function for handling Queued Write Module errors. + * + * @details A pointer to this function will be passed to each service which may need to inform the + * application about an error. + * + * @param[in] nrf_error Error code containing information about what went wrong. + */ +static void nrf_qwr_error_handler(uint32_t nrf_error) { + APP_ERROR_HANDLER(nrf_error); +} + + +/**@brief Function for handling the YYY Service events. + * YOUR_JOB implement a service handler function depending on the event the service you are using can generate + * + * @details This function will be called for all YY Service events which are passed to + * the application. + * + * @param[in] p_yy_service YY Service structure. + * @param[in] p_evt Event received from the YY Service. + * + * +static void on_yys_evt(ble_yy_service_t * p_yy_service, + ble_yy_service_evt_t * p_evt) +{ + switch (p_evt->evt_type) + { + case BLE_YY_NAME_EVT_WRITE: + APPL_LOG("[APPL]: charact written with value %s. ", p_evt->params.char_xx.value.p_str); + break; + + default: + // No implementation needed. + break; + } +} +*/ + +/**@brief Function for initializing services that will be used by the application. + */ +static void services_init(void) { + ret_code_t err_code; + nrf_ble_qwr_init_t qwr_init = {0}; + + // Initialize Queued Write Module. + qwr_init.error_handler = nrf_qwr_error_handler; + + err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init); + APP_ERROR_CHECK(err_code); + + ble_hts_init_t hts_init; + ble_bas_init_t bas_init; + + // Initialize Health Thermometer Service + memset(&hts_init, 0, sizeof(hts_init)); + hts_init.evt_handler = on_hts_evt; + hts_init.temp_type_as_characteristic = TEMP_TYPE_AS_CHARACTERISTIC; + hts_init.temp_type = BLE_HTS_TEMP_TYPE_BODY; + + // Here the sec level for the Health Thermometer Service can be changed/increased. + hts_init.ht_meas_cccd_wr_sec = SEC_JUST_WORKS; + hts_init.ht_type_rd_sec = SEC_OPEN; + err_code = ble_hts_init(&m_hts, &hts_init); + APP_ERROR_CHECK(err_code); + + // Initialize Battery Service. + memset(&bas_init, 0, sizeof(bas_init)); + + // Here the sec level for the Battery Service can be changed/increased. + bas_init.bl_rd_sec = SEC_OPEN; + bas_init.bl_cccd_wr_sec = SEC_OPEN; + bas_init.bl_report_rd_sec = SEC_OPEN; + + bas_init.evt_handler = NULL; + bas_init.support_notification = true; + bas_init.p_report_ref = NULL; + bas_init.initial_batt_level = 100; + + err_code = ble_bas_init(&m_bas, &bas_init); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Function for handling the Connection Parameters Module. + * + * @details This function will be called for all events in the Connection Parameters Module which + * are passed to the application. + * @note All this function does is to disconnect. This could have been done by simply + * setting the disconnect_on_fail config parameter, but instead we use the event + * handler mechanism to demonstrate its use. + * + * @param[in] p_evt Event received from the Connection Parameters Module. + */ +static void on_conn_params_evt(ble_conn_params_evt_t * p_evt) { + ret_code_t err_code; + + if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED) { + err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE); + APP_ERROR_CHECK(err_code); + } +} + + +/**@brief Function for handling a Connection Parameters error. + * + * @param[in] nrf_error Error code containing information about what went wrong. + */ +static void conn_params_error_handler(uint32_t nrf_error) { + APP_ERROR_HANDLER(nrf_error); +} + + +/**@brief Function for initializing the Connection Parameters module. + */ +static void conn_params_init(void) { + ret_code_t err_code; + ble_conn_params_init_t cp_init; + + memset(&cp_init, 0, sizeof(cp_init)); + + cp_init.p_conn_params = NULL; + cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY; + cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY; + cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT; + cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID; + cp_init.disconnect_on_fail = false; + cp_init.evt_handler = on_conn_params_evt; + cp_init.error_handler = conn_params_error_handler; + + err_code = ble_conn_params_init(&cp_init); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Function for starting timers. + */ +static void application_timers_start(void) { + /* YOUR_JOB: Start your timers. below is an example of how to start a timer. + ret_code_t err_code; + err_code = app_timer_start(m_app_timer_id, TIMER_INTERVAL, NULL); + APP_ERROR_CHECK(err_code); */ + +} + + +/**@brief Function for putting the chip into sleep mode. + * + * @note This function will not return. + */ +static void sleep_mode_enter(void) { + ret_code_t err_code; + + err_code = bsp_indication_set(BSP_INDICATE_IDLE); + APP_ERROR_CHECK(err_code); + + // Prepare wakeup buttons. + err_code = bsp_btn_ble_sleep_mode_prepare(); + APP_ERROR_CHECK(err_code); + + // Go to system-off mode (this function will not return; wakeup will cause a reset). + err_code = sd_power_system_off(); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Function for handling advertising events. + * + * @details This function will be called for advertising events which are passed to the application. + * + * @param[in] ble_adv_evt Advertising event. + */ +static void on_adv_evt(ble_adv_evt_t ble_adv_evt) { + ret_code_t err_code; + + switch (ble_adv_evt) { + case BLE_ADV_EVT_FAST: + NRF_LOG_INFO("Fast advertising."); + err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING); + APP_ERROR_CHECK(err_code); + break; + + case BLE_ADV_EVT_IDLE: + sleep_mode_enter(); + break; + + default: + break; + } +} + + +/**@brief Function for handling BLE events. + * + * @param[in] p_ble_evt Bluetooth stack event. + * @param[in] p_context Unused. + */ +static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) { + ret_code_t err_code = NRF_SUCCESS; + + switch (p_ble_evt->header.evt_id) { + case BLE_GAP_EVT_DISCONNECTED: + NRF_LOG_INFO("Disconnected."); + // LED indication will be changed when advertising starts. + break; + + case BLE_GAP_EVT_CONNECTED: + NRF_LOG_INFO("Connected."); + err_code = bsp_indication_set(BSP_INDICATE_CONNECTED); + APP_ERROR_CHECK(err_code); + m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle); + APP_ERROR_CHECK(err_code); + break; + + case BLE_GAP_EVT_PHY_UPDATE_REQUEST: + { + NRF_LOG_DEBUG("PHY update request."); + ble_gap_phys_t const phys = + { + .rx_phys = BLE_GAP_PHY_AUTO, + .tx_phys = BLE_GAP_PHY_AUTO, + }; + err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); + APP_ERROR_CHECK(err_code); + } break; + + case BLE_GATTC_EVT_TIMEOUT: + // Disconnect on GATT Client timeout event. + NRF_LOG_DEBUG("GATT Client Timeout."); + err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, + BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); + APP_ERROR_CHECK(err_code); + break; + + case BLE_GATTS_EVT_TIMEOUT: + // Disconnect on GATT Server timeout event. + NRF_LOG_DEBUG("GATT Server Timeout."); + err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle, + BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); + APP_ERROR_CHECK(err_code); + break; + + default: + // No implementation needed. + break; + } +} + + +/**@brief Function for initializing the BLE stack. + * + * @details Initializes the SoftDevice and the BLE event interrupt. + */ +static void ble_stack_init(void) { + ret_code_t err_code; + + err_code = nrf_sdh_enable_request(); + APP_ERROR_CHECK(err_code); + + // Configure the BLE stack using the default settings. + // Fetch the start address of the application RAM. + uint32_t ram_start = 0; + err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start); + APP_ERROR_CHECK(err_code); + + // Enable BLE stack. + err_code = nrf_sdh_ble_enable(&ram_start); + APP_ERROR_CHECK(err_code); + + // Register a handler for BLE events. + NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); +} + + +/**@brief Function for the Peer Manager initialization. + */ +static void peer_manager_init(void) { + ble_gap_sec_params_t sec_param; + ret_code_t err_code; + + err_code = pm_init(); + APP_ERROR_CHECK(err_code); + + memset(&sec_param, 0, sizeof(ble_gap_sec_params_t)); + + // Security parameters to be used for all security procedures. + sec_param.bond = SEC_PARAM_BOND; + sec_param.mitm = SEC_PARAM_MITM; + sec_param.lesc = SEC_PARAM_LESC; + sec_param.keypress = SEC_PARAM_KEYPRESS; + sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES; + sec_param.oob = SEC_PARAM_OOB; + sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE; + sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE; + sec_param.kdist_own.enc = 1; + sec_param.kdist_own.id = 1; + sec_param.kdist_peer.enc = 1; + sec_param.kdist_peer.id = 1; + + err_code = pm_sec_params_set(&sec_param); + APP_ERROR_CHECK(err_code); + + err_code = pm_register(pm_evt_handler); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Clear bond information from persistent storage. + */ +static void delete_bonds(void) { + ret_code_t err_code; + + NRF_LOG_INFO("Erase bonds!"); + + err_code = pm_peers_delete(); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Function for handling events from the BSP module. + * + * @param[in] event Event generated when button is pressed. + */ +static void bsp_event_handler(bsp_event_t event) { + ret_code_t err_code; + + switch (event) { + case BSP_EVENT_SLEEP: + sleep_mode_enter(); + break; // BSP_EVENT_SLEEP + + case BSP_EVENT_DISCONNECT: + err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); + if (err_code != NRF_ERROR_INVALID_STATE) { + APP_ERROR_CHECK(err_code); + } + break; // BSP_EVENT_DISCONNECT + + case BSP_EVENT_WHITELIST_OFF: + if (m_conn_handle == BLE_CONN_HANDLE_INVALID) { + err_code = ble_advertising_restart_without_whitelist(&m_advertising); + if (err_code != NRF_ERROR_INVALID_STATE) { + APP_ERROR_CHECK(err_code); + } + } + break; // BSP_EVENT_KEY_0 + case BSP_EVENT_KEY_0: + if (m_conn_handle != BLE_CONN_HANDLE_INVALID) { + temperature_measurement_send(); + } + break; + + default: + break; + } +} + + +/**@brief Function for initializing the Advertising functionality. + */ +static void advertising_init(void) { + ret_code_t err_code; + ble_advertising_init_t init; + + memset(&init, 0, sizeof(init)); + + init.advdata.name_type = BLE_ADVDATA_FULL_NAME; + init.advdata.include_appearance = true; + init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]); + init.advdata.uuids_complete.p_uuids = m_adv_uuids; + + init.config.ble_adv_fast_enabled = true; + init.config.ble_adv_fast_interval = APP_ADV_INTERVAL; + init.config.ble_adv_fast_timeout = APP_ADV_DURATION; + + init.evt_handler = on_adv_evt; + + err_code = ble_advertising_init(&m_advertising, &init); + APP_ERROR_CHECK(err_code); + + ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG); +} + + +/**@brief Function for initializing buttons and leds. + * + * @param[out] p_erase_bonds Will be true if the clear bonding button was pressed to wake the application up. + */ +static void buttons_leds_init(bool * p_erase_bonds) { + ret_code_t err_code; + bsp_event_t startup_event; + + err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler); + APP_ERROR_CHECK(err_code); + + err_code = bsp_btn_ble_init(NULL, &startup_event); + APP_ERROR_CHECK(err_code); + + *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA); +} + + +/**@brief Function for initializing the nrf log module. + */ +static void log_init(void) { + ret_code_t err_code = NRF_LOG_INIT(NULL); + APP_ERROR_CHECK(err_code); + + NRF_LOG_DEFAULT_BACKENDS_INIT(); +} + + +/**@brief Function for initializing power management. + */ +static void power_management_init(void) { + ret_code_t err_code; + err_code = nrf_pwr_mgmt_init(); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Function for handling the idle state (main loop). + * + * @details If there is no pending log operation, then sleep until next the next event occurs. + */ +static void idle_state_handle(void) { + if (NRF_LOG_PROCESS() == false) { + nrf_pwr_mgmt_run(); + } +} + + +/**@brief Function for starting advertising. + */ +static void advertising_start(bool erase_bonds) { + if (erase_bonds == true) { + delete_bonds(); + // Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED event + } else { + ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST); + + APP_ERROR_CHECK(err_code); + } +} diff --git a/project/imu.c b/project/imu.c new file mode 100644 index 0000000000000000000000000000000000000000..7452480b888422e3614366de853e3e5af49a959f --- /dev/null +++ b/project/imu.c @@ -0,0 +1,231 @@ +/** +* Function for initializing the FIR filter instance +*/ +void dsp_config() { + // Note that the init function requires the address of the coefficient table B as an input + arm_fir_init_f32(&fir_lpf, NUM_TAPS, (float32_t *)&B[0], &firStateF32[0], BLOCK_SIZE); +} + +/** +* Function for computing the filter response +*/ +void compute_fir() { + + // Each axis is processed on its own in 5 blocks of data + + // x-axis + for (uint8_t i = 0; i < 5; i++) { + arm_fir_f32(&fir_lpf, &acc_x_in_buf[0] + i * BLOCK_SIZE, &acc_x_out_buf[0] + i * BLOCK_SIZE, BLOCK_SIZE); + } + + // y-axis + for (uint8_t i = 0; i < 5; i++) { + arm_fir_f32(&fir_lpf, &acc_y_in_buf[0] + i * BLOCK_SIZE, &acc_y_out_buf[0] + i * BLOCK_SIZE, BLOCK_SIZE); + } + + // z-axis + for (uint8_t i = 0; i < 5; i++) { + arm_fir_f32(&fir_lpf, &acc_z_in_buf[0] + i * BLOCK_SIZE, &acc_z_out_buf[0] + i * BLOCK_SIZE, BLOCK_SIZE); + } + + block_cnt = 0; // Reset block counting +} + +/** +* Function for reading FIFO data +*/ +int8_t get_bmi160_fifo_data() { + int8_t rslt = BMI160_OK; + uint8_t acc_frames_req = 28; + // Read the fifo buffer using SPI + rslt = bmi160_get_fifo_data(&sensor); + // Parse the data and extract 28 accelerometer frames + rslt = bmi160_extract_accel(acc_data, &acc_frames_req, &sensor); + + // Copy the contents of each axis to a FIR input buffer + for (uint8_t i = 0; i < acc_frames_req; i++) { + acc_x_in_buf[acc_frames_req*block_cnt + i] = acc_data[i].x; + acc_y_in_buf[acc_frames_req*block_cnt + i] = acc_data[i].y; + acc_z_in_buf[acc_frames_req*block_cnt + i] = acc_data[i].z; + } + + // Increase block count after each sensor read + block_cnt++; + + // After 5 reads the buffer is almost full and the data is ready to be processed + if (block_cnt == 5) { + compute_fir(); + } + + return rslt; +} + +/** +* SPI user event handler. +*/ +void spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context) { + spi_xfer_done = true; // Set a flag when transfer is done +} + +void gpio_event_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { + get_bmi160_fifo_data(); +} + +/** +* Function for setting up the SPI communication. +*/ +uint32_t spi_config() { + uint32_t err_code; + + // Use nRF's default configurations + nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG; + + // Define each GPIO pin + spi_config.ss_pin = SPI_SS_PIN; + spi_config.miso_pin = SPI_MISO_PIN; + spi_config.mosi_pin = SPI_MOSI_PIN; + spi_config.sck_pin = SPI_SCK_PIN; + + // Initialize the SPI peripheral and give it a function pointer to + // it’s event handler + err_code = nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL); + + return err_code; +} + +/** +* Function for writing to the BMI160 via SPI. +*/ +int8_t bmi160_spi_bus_write(uint8_t hw_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t cnt) { + spi_xfer_done = false; // set the flag down during transfer + + int32_t error = 0; + + // Allocate array, which lenght is address + number of data bytes to be sent + uint8_t tx_buff[cnt+1]; + uint16_t stringpos; + + // AND address with 0111 1111; set msb to '0' (write operation) + tx_buff[0] = reg_addr & 0x7F; + for (stringpos = 0; stringpos < cnt; stringpos++) { + tx_buff[stringpos+1] = *(reg_data + stringpos); + } + + // Do the actual SPI transfer + nrf_drv_spi_transfer(&spi, tx_buff, cnt+1, NULL, 0); + while (!spi_xfer_done) {}; // Loop until the transfer is complete + + return (int8_t)error; +} + +/** +* Function for reading from the BMI160 via SPI. +*/ +int8_t bmi160_spi_bus_read(uint8_t hw_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t len) { + spi_xfer_done = false; // set the flag down during transfer + + int32_t error = 0; + + uint8_t tx_buff = reg_addr | 0x80; // OR address with 1000 0000; Read -> set msb to '1'; + uint8_t * rx_buff_pointer; + uint16_t stringpos; + + rx_buff_pointer = (uint8_t *) (SPI_RX_Buffer); + + // Do the actual SPI transfer + nrf_drv_spi_transfer(&spi, &tx_buff, 1, rx_buff_pointer, len+1); + + while (!spi_xfer_done) {} // Loop until the transfer is complete + + // Copy received bytes to reg_data + for (stringpos = 0; stringpos < len; stringpos++) + *(reg_data + stringpos) = SPI_RX_Buffer[stringpos + 1]; + + return (int8_t)error; +} + +/** +* Function for configuring the sensor +*/ +int8_t sensor_config() { + int8_t rslt = BMI160_OK; + + sensor.id = 0; // We use SPI so id == 0 + sensor.interface = BMI160_SPI_INTF; + + // Give the driver the correct interfacing functions + sensor.read = bmi160_spi_bus_read; + sensor.write = bmi160_spi_bus_write; + sensor.delay_ms = nrf_delay_ms; + + // Initialize the sensor and check if everything went ok + rslt = bmi160_init(&sensor); + + // Configure the accelerometer's sampling freq, range and modes + sensor.accel_cfg.odr = BMI160_ACCEL_ODR_100HZ; + sensor.accel_cfg.range = BMI160_ACCEL_RANGE_8G; + sensor.accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; + sensor.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; + // Set the configurations + rslt = bmi160_set_sens_conf(&sensor); + // Some fifo settings + fifo_frame.data = fifo_buff; + fifo_frame.length = 200; + sensor.fifo = &fifo_frame; + + // Configure the sensor's FIFO settings + rslt = bmi160_set_fifo_config(BMI160_FIFO_ACCEL, BMI160_ENABLE, &sensor); + // Create an instance for interrupt settings + struct bmi160_int_settg int_config; + // Interrupt channel/pin 1 + int_config.int_channel = BMI160_INT_CHANNEL_1; + // Choosing fifo watermark interrupt + int_config.int_type = BMI160_ACC_GYRO_FIFO_WATERMARK_INT; + // Set fifo watermark level to 180 + rslt = bmi160_set_fifo_wm((uint8_t) 180, &sensor); + // Enabling interrupt pins to act as output pin + int_config.int_pin_settg.output_en = BMI160_ENABLE; + // Choosing push-pull mode for interrupt pin + int_config.int_pin_settg.output_mode = BMI160_DISABLE; + // Choosing active high output + int_config.int_pin_settg.output_type = BMI160_ENABLE; + // Choosing edge triggered output + int_config.int_pin_settg.edge_ctrl = BMI160_ENABLE; + // Disabling interrupt pin to act as input + int_config.int_pin_settg.input_en = BMI160_DISABLE; + // Non-latched output + int_config.int_pin_settg.latch_dur = BMI160_LATCH_DUR_NONE; + // Enabling FIFO watermark interrupt + int_config.fifo_WTM_int_en = BMI160_ENABLE; + // Set interrupt configurations + rslt = bmi160_set_int_config(&int_config, &sensor); + + return rslt; +} + +/** +* Function for configuring General Purpose I/O. +*/ +uint32_t config_gpio() { + uint32_t err_code = NRF_SUCCESS; + + if(!nrf_drv_gpiote_is_init()) { + err_code = nrf_drv_gpiote_init(); + } + + // Set which clock edge triggers the interrupt + nrf_drv_gpiote_in_config_t config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true); + + // Configure the internal pull up resistor + //config.pull = NRF_GPIO_PIN_PULLUP; + + // Configure the pin as input + err_code = nrf_drv_gpiote_in_init(INT_PIN, &config, gpio_event_handler); + if (err_code != NRF_SUCCESS) { + // handle error condition + } + + // Enable events + nrf_drv_gpiote_in_event_enable(INT_PIN, true); + return err_code; +} diff --git a/project/main.c b/project/main.c index b82caf1c13f01a3c3bb0371eb62ca3616ce9702b..1682b52ebeb5c0b234b3469a3ec99ffa587445ab 100644 --- a/project/main.c +++ b/project/main.c @@ -90,6 +90,127 @@ #include "fdacoefs.h" #include "arm_math.h" +#define SPI_INSTANCE 0 // SPI instance index. We use SPI master 0 +#define SPI_SS_PIN 26 +#define SPI_MISO_PIN 23 +#define SPI_MOSI_PIN 24 +#define SPI_SCK_PIN 22 +#define INT_PIN 27 + +#define NUM_TAPS 58 +#define BLOCK_SIZE 28 + +#define DEVICE_NAME "nRF_Aapo" /**< Name of device. Will be included in the advertising data. */ +#define MANUFACTURER_NAME "NordicSemiconductor" /**< Manufacturer. Will be passed to Device Information Service. */ +#define APP_ADV_INTERVAL 300 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 187.5 ms). */ + +#define APP_ADV_DURATION 18000 /**< The advertising duration (180 seconds) in units of 10 milliseconds. */ +#define APP_BLE_OBSERVER_PRIO 3 /**< Application's BLE observer priority. You shouldn't need to modify this value. */ +#define APP_BLE_CONN_CFG_TAG 1 /**< A tag identifying the SoftDevice BLE configuration. */ + +#define MIN_CONN_INTERVAL MSEC_TO_UNITS(100, UNIT_1_25_MS) /**< Minimum acceptable connection interval (0.1 seconds). */ +#define MAX_CONN_INTERVAL MSEC_TO_UNITS(200, UNIT_1_25_MS) /**< Maximum acceptable connection interval (0.2 second). */ +#define SLAVE_LATENCY 0 /**< Slave latency. */ +#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds). */ + +#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */ +#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */ +#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */ + +#define SEC_PARAM_BOND 1 /**< Perform bonding. */ +#define SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */ +#define SEC_PARAM_LESC 0 /**< LE Secure Connections not enabled. */ +#define SEC_PARAM_KEYPRESS 0 /**< Keypress notifications not enabled. */ +#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */ +#define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */ +#define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */ +#define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */ + +#define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */ + +// Determines if temperature type is given as characteristic (1) or as a field of measurement (0) +#define TEMP_TYPE_AS_CHARACTERISTIC 0 + +// Declare a state array +static float32_t firStateF32[BLOCK_SIZE + NUM_TAPS - 1]; +// Declare an instance for the low-pass FIR filter +arm_fir_instance_f32 fir_lpf; + +float32_t acc_x_in_buf[140]; +float32_t acc_y_in_buf[140]; +float32_t acc_z_in_buf[140]; +float32_t acc_x_out_buf[140]; +float32_t acc_y_out_buf[140]; +float32_t acc_z_out_buf[140]; + +int in_array[16]; +int out_array[16]; + +int block_cnt = 0; + +//SPI instance +static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE); +//Flag used to indicate that SPI instance completed the transfer +static volatile bool spi_xfer_done; + +static uint8_t SPI_RX_Buffer[201]; // Allocate a buffer for SPI reads +struct bmi160_dev sensor; // An instance of bmi160 sensor + +// Declare memory to store the raw FIFO buffer information +uint8_t fifo_buff[200]; +// Modify the FIFO buffer instance and link to the device instance +struct bmi160_fifo_frame fifo_frame; + +// 200 bytes -> ~7bytes per frame -> ~28 data frames +struct bmi160_sensor_data acc_data[28]; + +NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */ +NRF_BLE_QWR_DEF(m_qwr); /**< Context for the Queued Write module.*/ +BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */ + +static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ + +BLE_HTS_DEF(m_hts); // Macro for defining a ble_hts instance +BLE_BAS_DEF(m_bas); // Macro for defining a ble_bas instance + +#include "imu.c" +#include "ble.c" + int main(void) { + bool erase_bonds; + + dsp_config(); + spi_config(); + config_gpio(); + sensor_config(); + + while(true) { + __WFE(); // sleep until an event wakes us up + __SEV(); // sleep until an event wakes us up + __WFE(); // sleep until an event wakes us up + } + + log_init(); + timers_init(); + buttons_leds_init(&erase_bonds); + power_management_init(); + ble_stack_init(); + gap_params_init(); + gatt_init(); + advertising_init(); + services_init(); + conn_params_init(); + peer_manager_init(); + + // Start execution. + NRF_LOG_INFO("Template example started."); + application_timers_start(); + + advertising_start(erase_bonds); + + // Enter main loop. + for (;;) { + idle_state_handle(); + } }