nRF52832 Technical Exchange Group: 680723714
nRF52832-Bluefruit52 Core Board Details:
https://blog.csdn.net/solar_Lan/article/details/88688451
Primary Function Analysis of Bluetooth BLE
The project directory has been described before. Most of the files in the project directory are provided in the official SDK library. If you add files independently, you need to be aware of the settings of the file directory.Of course, it is recommended that you write your own program directly using SDK templates to speed up program development.
But to really work, you need to understand the basic framework of the entire Bluetooth project and know where and why?Where do you need to write it?For example, if you are building a building now, you need to make a frame and fill it with something.Discuss the following section with the questions above.
The students who have studied single-chip computer all know that the main function main is the basic framework of a program.And the main function in the Bluetooth template project is where we need to write, so how?To understand this problem, we must analyze the code function and significance of this template project, starting with the main function:
/**@brief Function for application main entry. */ int main(void) { bool erase_bonds; // Initialize. log_init(); //log print initialization timers_init(); //timer initiated buttons_leds_init(&erase_bonds); //Key and LED Initialization power_management_init(); //Power management initialization ble_stack_init(); //BLE protocol stack initialization gap_params_init(); //GAP Parameter Initialization gatt_init(); //GATT Initialization advertising_init(); //Broadcast Initialization services_init(); //Service Initialization conn_params_init(); //Connection parameter initialization peer_manager_init(); //Device Management Initialization // Start execution. NRF_LOG_INFO("Template example started."); application_timers_start(); //Apply Timer Start advertising_start(erase_bonds); //Broadcast Start // Enter main loop. for (;;) { idle_state_handle(); } }
The main function is very long and has many functions. Let's draw a picture to visualize it:
The following is a detailed analysis of the initialization process in the main function:
1. Peripheral initialization
The peripheral initialization implementations require some external devices, which you can learn from the peripheral tutorials (PDF tutorials and project codes are provided with the purchased supporting materials).Here's how the peripherals under the protocol stack are initialized.
1.1, log_init() function
/**@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();//Initialize log settings, configure log channels }
The LOG printing function is designed to facilitate debugging and establish a way of information exchange between devices and people.Select two channel information outputs, one is the serial channel and the other is the RTT channel of the emulator jlink.Since there is only one serial port for nrf5x chips, the output of RTT becomes the only method when the serial port is occupied.See the detailed tutorial "Bluetooth RTT output log information".
1.2, timers_init() function
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); */ }
Create any timer to be used by the application, and officially give an instance of how to create one.It is then used when the ECG or near an application instance, setting the counter id.For each new timer need, add the macro APP_TIMER_MAX_TIMERS.If you don't use a timer, you can leave this unchanged. See "Setting up a Software Timer under the Protocol Stack" in more detail.
1.3, buttons_leds_init(&erase_bonds) function
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); }
Initialize the operation of the LED and keys, this note, directly invoked app files, that is, defined application libraries, officially provided, you can drill into the source code specifically, if you want to use interrupts to modify, later if you encounter applications in this area described in detail, if you do not use port interrupts, thisCan not be modified.
The keys and LEDs are defined here, and a bsp_event_handler that needs to be callback is given below, which means that after the function initializes the keys, the system will automatically detect the keys or the LEDs change for you, and if they do, the system will call back.
The key control function bsp_btn_ble_init is also configured to sleep on keys and start broadcasting on Bluetooth devices.
uint32_t bsp_btn_ble_init(bsp_btn_ble_error_handler_t error_handler, bsp_event_t * p_startup_bsp_evt) { uint32_t err_code = NRF_SUCCESS; m_error_handler = error_handler; if (p_startup_bsp_evt != NULL) { startup_event_extract(p_startup_bsp_evt); } if (m_num_connections == 0) { err_code = advertising_buttons_configure(); } return err_code; }
The callback bsp_event_handler, also known as the BSP interrupt service function, implements multiple events such as BSP_EVENT_SLEEP going to sleep, BSP_EVENT_DISCONNECT Bluetooth disconnection event, BSP_EVENT_WHITELIST_OFF re-broadcasting without whitelist events, and so on, corresponding operations are performed.Take a closer look at the "Use of keys under the protocol stack" tutorial.
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 default: break; } }
2. Initialization of power management
2.1, power_management_init() function
The power management function initialization function mainly implements the initialization of the low power management settings in the cortex-m4 kernel SCB, which can generally remain unchanged.
/**@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); }
ret_code_t nrf_pwr_mgmt_init(void) { NRF_LOG_INFO("Init"); m_shutdown_started = false; nrf_mtx_init(&m_sysoff_mtx);//Initialize Mutex nrf_section_iter_init(&m_handlers_iter, &pwr_mgmt_data);//Functions that initialize iterators PWR_MGMT_SLEEP_INIT();//Hibernation Initialization PWR_MGMT_DEBUG_PINS_INIT();//Debug pin initialization PWR_MGMT_STANDBY_TIMEOUT_INIT();//Standby timeout initialization PWR_MGMT_CPU_USAGE_MONITOR_INIT();//CPU Usage Tracking Initialization return PWR_MGMT_TIMER_CREATE(); }
3. Initialization of protocol stack
3.1, ble_stack_init() protocol initialization
The protocol stack initialization mainly does the following:
1: Protocol stack reply enables reply. The main work is protocol stack clock initialization configuration.
2: Initialize the protocol stack, set up the protocol stack related processing functions to enable the protocol stack.
3: Register Bluetooth to handle scheduling events.
The specific code is as follows, please refer to the "Bluetooth protocol stack initialization details" tutorial for detailed explanation:
static void ble_stack_init(void) { ret_code_t err_code; err_code = nrf_sdh_enable_request();//Protocol stack replies enable replies, mainly by configuring the protocol stack clock APP_ERROR_CHECK(err_code); // Configure the BLE stack using the default settings. // Fetch the start address of the application RAM. // Configure the protocol stack to use the default address to get the start address of RAM uint32_t ram_start = 0; //Default configuration includes protocol stack start address, connection configuration, etc. err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start); APP_ERROR_CHECK(err_code); // Enables the protocol stack. err_code = nrf_sdh_ble_enable(&ram_start); APP_ERROR_CHECK(err_code); // Register Bluetooth for event handling. NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); }
4. GAP Initialization and GATT Initialization
4.1, gap_params_init() function gap initialization
Generic Access Profile (GAP), which ensures that different Bluetooth products can discover each other and establish connections.
(GAP) defines how Bluetooth devices discover and establish secure (or insecure) connections to other devices.It handles general-pattern businesses such as inquiry, naming, and search, security issues such as guarantees, and connection-related businesses such as link-building, channel-building, and connection-building.GAP specifies some general running tasks.Therefore, it is mandatory and serves as the basis for all other Bluetooth application specifications.
GAP is the basis for all other profiles and defines a common method for establishing baseband links between Bluetooth devices.In addition, GAP defines the following:
Functions that must be implemented in all Bluetooth devices;
(2) General steps for discovering and linking devices;
(3) Basic user interface terminology.
There are actually only two things to do in the GAP initialization, one is to configure the device name, or to generate the device icon.The second function is to configure the connection parameters of GAP. The code is as follows. See "GAP Initialization Settings Detailed".
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);//Set device name 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)); //Initialize GAP connection interval, slave delay, timeout 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; //Setting configuration parameters successfully err_code = sd_ble_gap_ppcp_set(&gap_conn_params); APP_ERROR_CHECK(err_code); }
4.2, gatt_init() function
GATT is referred to as the Generic Attribute profile of the generic attribute specification, and the GATT layer is where the real data is transmitted.Includes a data transfer and storage framework and its basic operations.Most of the settings are made in the service, and only the parameter of data length needs to be initialized in the main function. The code is as follows. The gatt_init function calls the nrf_ble_gatt_init function, which defines the host in the gatt, the maximum MTU length of the slave, and the length of the negotiated data.
/**@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); } ret_code_t nrf_ble_gatt_init(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_handler_t evt_handler) { VERIFY_PARAM_NOT_NULL(p_gatt); p_gatt->evt_handler = evt_handler; p_gatt->att_mtu_desired_periph = NRF_SDH_BLE_GATT_MAX_MTU_SIZE; p_gatt->att_mtu_desired_central = NRF_SDH_BLE_GATT_MAX_MTU_SIZE; p_gatt->data_length = NRF_SDH_BLE_GAP_DATA_LENGTH; for (uint32_t i = 0; i < NRF_BLE_GATT_LINK_COUNT; i++) { link_init(&p_gatt->links[i]); } return NRF_SUCCESS; }
For a detailed analysis of GAP and GATT, please refer to our other tutorial, GAP Initialization Settings Detailed, which will be discussed in detail in the subsequent service creation tutorials.
5. Broadcast Initialization
5.1, static void advertising_init(void): function initializes broadcast function
/**@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;//Name display on broadcast init.advdata.include_appearance = true;//Is an icon required //Bluetooth Device Mode init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; //UUID 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;//Broadcast type init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;//Broadcast interval init.config.ble_adv_fast_timeout = APP_ADV_DURATION;//Broadcast timeout init.evt_handler = on_adv_evt; err_code = ble_advertising_init(&m_advertising, &init);//Initialize broadcast, import parameters APP_ERROR_CHECK(err_code); ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);//Set Broadcast Identification Number }
Broadcast initialization essentially initializes two structures, one is the &advdata broadcast data and the other is the &config selector broadcast data structure, which lists some broadcast data that needs initialization:
/**@brief Advertising data structure. This structure contains all options and data needed for encoding and * setting the advertising data. */ typedef struct { ble_advdata_name_type_t name_type; /**< Type of device name. */ uint8_t short_name_len; /**< Length of short device name (if short type is specified). */ bool include_appearance; /**< Determines if Appearance shall be included. */ uint8_t flags; /**< Advertising data Flags field. */ int8_t * p_tx_power_level; /**< TX Power Level field. */ ble_advdata_uuid_list_t uuids_more_available; /**< List of UUIDs in the 'More Available' list. */ ble_advdata_uuid_list_t uuids_complete; /**< List of UUIDs in the 'Complete' list. */ ble_advdata_uuid_list_t uuids_solicited; /**< List of solicited UUIDs. */ ble_advdata_conn_int_t * p_slave_conn_int; /**< Slave Connection Interval Range. */ ble_advdata_manuf_data_t * p_manuf_specific_data; /**< Manufacturer specific data. */ ble_advdata_service_data_t * p_service_data_array; /**< Array of Service data structures. */ uint8_t service_data_count; /**< Number of Service data structures. */ bool include_ble_device_addr; /**< Determines if LE Bluetooth Device Address shall be included. */ ble_advdata_le_role_t le_role; /**< LE Role field. Included when different from @ref BLE_ADVDATA_ROLE_NOT_PRESENT. @warning This field can be used only for NFC. For BLE advertising, set it to NULL. */ ble_advdata_tk_value_t * p_tk_value; /**< Security Manager TK value field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/ uint8_t * p_sec_mgr_oob_flags; /**< Security Manager Out Of Band Flags field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/ ble_gap_lesc_oob_data_t * p_lesc_data; /**< LE Secure Connections OOB data. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/ } ble_advdata_t;
A broadcast data can actually carry up to 31 bytes of data. It usually contains user-readable names, information about the packets sent by the device, and similar flags used to indicate whether the device is discoverable, such as the definition of the structure above.
When a host receives a broadcast packet, it may send a request for more packets, called a scan response. If it is set to active scan, the slave device will send a scan response as a response to the host request, which can also carry up to 31 bytes of data.The data structure type of the broadcast scan response package can be identical to that of the broadcast package, as defined by the structure above.So how to set up a broadcast package, refer to the "Broadcast Initialization Details" tutorial.
6. Service Initialization
6.1, services_init(): Service initialization
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); /* YOUR_JOB: Add code to initialize the services used by the application. ble_xxs_init_t xxs_init; ble_yys_init_t yys_init; // Initialize XXX Service. memset(&xxs_init, 0, sizeof(xxs_init)); xxs_init.evt_handler = NULL; xxs_init.is_xxx_notify_supported = true; xxs_init.ble_xx_initial_value.level = 100; err_code = ble_bas_init(&m_xxs, &xxs_init); APP_ERROR_CHECK(err_code); // Initialize YYY Service. memset(&yys_init, 0, sizeof(yys_init)); yys_init.evt_handler = on_yys_evt; yys_init.ble_yy_initial_value.counter = 0; err_code = ble_yy_service_init(&yys_init, &yy_init); APP_ERROR_CHECK(err_code); */ }
Service initialization is the creation of a service declaration, which gives a RAM space for service initialization and declaration.This code is empty in the sample, which means that there is only a framework in the Bluetooth sample and no Bluetooth service is established, so how to add services will be explained in the subsequent application sample tutorials.For a specific example, see the following tutorial on Bluetooth services.
7. Initialization of connection and security parameters
7.1, conn_params_init(): connection parameter initialization
/**@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); }
The SDK provides a module named ble_conn_params to manage connection parameter updates, which are processed through the SoftDevice API, including when the request was made and when the first request was rejected to send a new request.The SoftDevice API function is encapsulated and cannot be viewed as a source function. As long as you help the documentation to find the meaning of the function, all function names with the sd prefix are SoftDevice API functions.
In the initialization structure ble_conn_params_init_t, parameters related to the update process are defined, such as whether to start a connection, what to start writing to a specific CCCD, whether to use connection parameters, delay in sending update requests, and so on.You can see the source code in the BLE_CONN_PARAMS.H file.
In the initialization function ble_conn_params_init(), the structure ble_conn_params_init(), which encapsulates the initialization connection parameters (ble_gap_conn_params_t), is used as the input parameter to initialize the structure.
In the SDK module, ble_conn_params ensures that the connection parameters of the host (concentrator) are compatible. If not, the peripheral device will require changes to the connection parameters. After no successful updates have been made more than the set number of times, it will disconnect or return an event to the application layer based on the settings.For a detailed analysis, refer to our other tutorial, Connection Parameters Update Details.
8. Initialization of Device Management
/**@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); }
9. Start of Broadcast
advertising_start(erase_bonds);
/**@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); } }
Broadcast data is set in advertise_start of main.c. The broadcast data structure set in the initialization of Secure Broadcasting starts broadcasting. In the main function, fast broadcasting is started first. See the explanation in Broadcast Initialization Details for the type of broadcasting.
10. Power Standby
idle_state_handle(); power standby
In the main function, the last loop waits and calls the idle_state_handle() function, which literally means invalid state operation, that is, when there are no Bluetooth events or chip processing events, the device calls this function to put the device in the system on state, that is, standby state.The idle_state_handle() function calls the nrf_pwr_mgmt_run function, which uses SDK to provide a module called sd_app_evt_wait protocol stack function for power management, which can be called directly, and the device wakes up after an interrupt event occurs.
/**@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(); } }
11. Download verification
Previously, all downloads were peripheral cases. The peripheral code does not have a protocol stack. The Bluetooth program explained after this chapter will have a Bluetooth program, Bluetooth program with protocol stack, the download process and peripherals are different. The details below describe the download method of the following Bluetooth program is similar.
1. First use nrfgo to download the protocol stack, open the protocol stack to download the software nrfgo, as shown in the following figure. If the emulator is not connected, nRF5x Programming appears gray:
- After installing the emulator driver, connect the emulator to the development connection as described in the Download Error Resolution document:
3 Select the Program Application, click Browse to select the version of the protocol you want to download, and then click Program to download:
4. Download the protocol stack and then use keil to download the project. The project catalog is as follows:
5. Install the mobile APP NRF connection, or download it directly from the app store. Open the mobile APP NRF connection after installation to observe the following phenomena and find the broadcast signal with the broadcasting name Nordic_Template:
6. Click on the device to show that the connection is successful, the LED 1 light is off, the LED 2 light is on, view the device properties, and observe the relevant parameters:
The basic UUID used by the Bluetooth Technology Consortium is 16bit UUID. In this case, the Bluetooth service is empty. What we observed is the GAP basic UUID and GATT basic UUID. Then we build an empty service, the basic GAP and GATT.
UUIDs are native to the underlying, in the file ble_types.h, for this GATT specific UUID and
GAP specific UUID s are listed as follows:
After the APP connection broadcasts, the first part of the service is GAP specific UUID and its feature values, and the second part is GATT specific UUID and its feature values, comparing the APP connection parameters with the definition class table shown above.