关注、星标公众号,直达精彩内容

来源:https://blog.csdn.net/solar_Lan/article/details/88964043

工程目录中大部分文件都是官方 SDK 库中提供好了的,如果独立添加文件需要注意文件目录的设置。当然推荐大家直接用 SDK 模板来编写自己的方案,加快程序开发。

但是真正要运行,需要大家弄清楚整个蓝牙工程的基本框架,知道哪里是干嘛的?哪里需要大家编写的?就比如现在建楼房,需要先打一个框架,然后往框架上填东西。带着上面几个问题进行下面这节的讨论。

学过单片机的同学都知道,主函数 main 是一个程序的基本框架。而蓝牙样板工程中主函数 main 就是我们需要编写的地方,那怎么编写了?为了弄清楚这个问题,我们就必须来分析下这个模板工程的代码功能及意义,首先来看下主函数:

主函数很长,功能很多,我们画个图来直观的表示下:

下面详细分析main函数中的初始化过程:

1、外设初始化

外设初始化实现需要使用的一些外部设备,外部设备的使用大家可以先学习外设教程的部分(购买的配套资料有提供PDF教程和工程代码)。下面主要看看协议栈下外设部分如何初始化。

1.1、log_init()函数

/**@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();//初始化log设置,配置log通道
}

LOG 打印功能是为了方便调试,在设备和人之间建立一个信息交换方式。选择两个通道信息输出就行,一个是串口通道,一个是仿真器 jlink 的 RTT 通道。由于 nrf5x 芯片串口端口只有一个,当串口通道被占用,RTT 的输出成为唯一的方法。

1.2、timers_init()函数

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 byone.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); */
}

创建要由应用程序使用的任何定时器,并且官方给了如何创建一个计时器的实例。后面在心电,或者接近等应用实例的时候就会被使用,设置计数器 id 。对于每一个新的计时器需要,增加宏 APP_TIMER_MAX_TIMERS。如果不使用定时器,这个可以不改变。

1.3、buttons_leds_init(&erase_bonds)函数

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);
}

初始化 LED 和按键的操作,这个注意,直接调用的 app 文件,也就是定义了的应用库,官方提供,大家可以具体深入源代码,如果要使用中断需要修改,后面如果遇到这方面的应用再详细说明,如果不使用端口中断,这个可以不做修改。

这里定义了按键和 LED,下面给了一个需要回调的 bsp_event_handler,就是说这个函数初始化按键后,系统会自动帮你检测按键或者 LED 发生变化,如果有变化了,系统就回来调用。

同时配置了一个按键控制函数 bsp_btn_ble_init,可以用按键休眠和启动蓝牙设备广播。

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;
}

回调的 bsp_event_handler,也就是 bsp 中断服务函数如下,实现多个事件,比如 BSP_EVENT_SLEEP 进入睡眠, BSP_EVENT_DISCONNECT 蓝牙断开事件, BSP_EVENT_WHITELIST_OFF 重新广播没有白名单事件等,对应事件执行对应操作。

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、电源管理初始化

2.1、power_management_init()函数

电源管理函数初始化函数,主要是实现 cortex-m4 内核 SCB 里低功耗管理设置的初始化,一般情况下可以不变动。

/**@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);//初始化互斥量
nrf_section_iter_init(&m_handlers_iter, &pwr_mgmt_data);//初始化迭代器的函数
PWR_MGMT_SLEEP_INIT();//休眠初始化
PWR_MGMT_DEBUG_PINS_INIT();//调试管脚初始化
PWR_MGMT_STANDBY_TIMEOUT_INIT();//待机超时初始化
PWR_MGMT_CPU_USAGE_MONITOR_INIT();//CPU使用率跟踪初始化
return PWR_MGMT_TIMER_CREATE();
}

3、协议栈初始化

3.1、ble_stack_init()协议初始化

协议栈初始化工作主要做下面几点:

1:协议栈回复使能应答,主要工作就是协议栈时钟初始化配置。

2:初始化协议栈,设置协议栈相关处理函数,使能协议栈。

3:注册蓝牙处理调度事件。

具体代码如下:

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.
// 配置协议栈使用默认地址,获取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);
// 使能协议栈.
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
// 注册蓝牙处理事件.
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}

4、GAP初始化和GATT初始化

4.1、gap_params_init()函数gap初始化

通用访问配置文件(Generic Access Profile, GAP),该 Profile 保证不同的 Bluetooth 产品可以互相发现对方并建立连接。

(GAP)定义了蓝牙设备如何发现和建立与其他设备的安全(或不安全)连接。它处理一些一般模式的业务(如询问、命名和搜索)和一些安全性问题(如担保),同时还处理一些有关连接的业务(如链路建立、信道和连接建立)。GAP 规定的是一些一般性的运行任务。因此,它具有强制性,并作为所有其它蓝牙应用规范的基础。

GAP 是所有其他配置文件的基础,它定义了在蓝牙设备间建立基带链路的通用方法。除此之外,GAP 还定义了下列内容:

①必须在所有蓝牙设备中实施的功能;

②发现和链接设备的通用步骤;

③基本用户界面术语。

在 GAP 初始化里实际上只做了两个工作,一个是配置设备名称,或者生成设备图标。第二个功能就是配置 GAP 的连接参数,代码如下所.

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连接间隔,从机延迟,超时时间
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);
}

4.2、gatt_init()函数

GATT 称为通用属性规范 Generic Attribute profile,GATT 层是传输真正数据所在的层。包括了一个数据传输和存储框架以及其基本操作。其大部分设置是在服务中进行的,在主函数中只需要初始化数据长度这个参数,代码如下所示,gatt_init 函数中调用了 nrf_ble_gatt_init 函数,该函数中定义了 gatt 中的主机,从机的最大 MTU 的长度,以及协商数据的长度。

/**@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;
}

5、广播初始化

5.1、static void advertising_init(void):函数初始化广播功能

/**@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;
//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;//广播类型
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);//设置广播识别号
}

广播初始化实际上就是初始化两个结构体,一个是&advdata 广播数据,一个是&config 选择项广播数据结构体如下,列出了一些需要初始化的广播数据:

/**@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
} ble_advdata_t;

一个广播数据实际上最多可以携带 31 字节的数据,它通常包含用户可读的名字、关于设备发送数据包的有关信息、用于表示此设备是否可被发现的标志等类似的标志,如上面结构体的定义。

当主机接收到广播包后,它可能发送请求更多数据包的请求,称为扫描回应,如果它被设置成主动扫描,从机设备将会发送一个扫描回应做为对主机请求的回应,扫描回应最多也可以携带31字节的数据。广播扫描回应包的数据结构类型可以和广播包一致,也如上面结构体定义。那么如何设置广播包了。

6、服务初始化

6.1、services_init():服务初始化

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);*/
}

服务初始化就是建立一个服务声明,给一个 RAM 空间,专门对服务进行初始化和声明。这个代码在样例里是空的,也就是说蓝牙样例里只搭了一个框架,而没有建立蓝牙服务,那么在后面的应用实例教程里会来讲解如何添加服务。具体实例请看后面的蓝牙服务篇教程。

7、连接参数和安全参数初始化

7.1、conn_params_init():连接参数初始化

/**@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);
}

SDK 提供了一个名为 ble_conn_params 的模块用于管理连接参数更新,它通过 SoftDevice API 进行处理,包括请求的时间和第一次请求被拒绝再发送一个新的请求。SoftDevice API 函数是经过封装后的函数,无法查看源函数,大家只要通过帮助文档查找函数意义,所有带 sd 前缀的函数名就是SoftDevice API 函数。

在初始化结构体 ble_conn_params_init_t 中,定义了更新过程的有关参数,例如,是否开始连接,什么情况开始写入一个特定的 CCCD,是否使用连接参数,发送更新请求的延时等等。大家可以查看源码,在 BLE_CONN_PARAMS.H 文件内。

在初始化函数 ble_conn_params_init()中,使用封装了初始化连接参数(ble_gap_conn_params_t)的结构体 ble_conn_params_init_t 作为输入参数进行连接参数初始化结构体。

ble_conn_params 在 SDK 模块确保与主机(集中器)的连接参数相适应,如果不适应,外围设备将要求更改连接参数,超过设定的更新次数都没有更新成功后,它就会断开连接或者根据设置返回一个事件到应用层。

8、设备管理初始化

/**@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、广播开始

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);
}
}

广播数据的设置在 main.c 的 advertising_start 中,安全广播初始化中设置的广播数据结构开始广播,主函数中,首先启动的为快速广播。

10、电源待机

idle_state_handle();电源待机

主函数中,最后一个循环等待,调用了 idle_state_handle()函数,这个函数字面意思为无效状态操作,也就是说在没有蓝牙事件或者芯片处理事件的时候,设备调用这个函数,可以使设备处于 system on 状态,也就是待机状态。idle_state_handle()函数中调用了 nrf_pwr_mgmt_run 函数中,这个函数中使用了 SDK 提供了一个协议栈函数名为 sd_app_evt_wait 的模块用于管理电源,可以直接调用,当中断事件发生后,设备被唤醒工作。

/**@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、 下载验证

之前下载的都是外设案例,外设代码不带协议栈,从本章后讲解的都将上蓝牙程序,蓝牙程序带协议栈,下载过程和外设有区别,下面详解进行介绍,后面的蓝牙程序下载方法类似。

1、首先使用 nrfgo 下载协议栈,打开协议栈下载软件 nrfgo,如下图所示,如果仿真器没有连接,nRF5x Programming 显示为灰色:

  1. 按照《下载错误解决办法里的方法》文档里的方法,把仿真器驱动安装好后,把仿真器和开发连接上,会如下图所示:

3 选择 Program Application ,点击 Browse 选择添加你要下载的协议版本,然后点击 Program 进行下载:

4.下载完协议栈,然后再用 keil 下载工程项目,工程项目目录如下图所示:

5. 安装手机 APP nrf connect,或者应用商店直接下载,安装后打开手机 APP nrf connect 观察如下现象,发现广播名称为 Nordic_Template 的广播信号:

6.点击设备,显示连接成功,LED1 灯熄灭,LED2 灯点亮,查看设备属性,观察相关参数:

蓝牙技术联盟所用的基本 UUID 为 16bit UUID,本例中蓝牙服务为空,我们观察到的是 GAP 基础 UUID 和 GATT 基础 UUID,再搭建一个空服务中,基础 GAP 和 GATT。

UUID 是底层自带的,在文件 ble_types.h 文件中,对这个 GATT specific UUID 和

GAP specific UUID 有列表出来,如下图所示:

在 APP 连接广播后,服务中第一部分就是 GAP specific UUID 以及其的特征值,第二部分就是 GATT specific UUID 以及其的特征值,对比 APP 连接参数和上图的定义类表。

免责声明:本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

从零开始的nrf52832蓝牙开发--蓝牙BLE主函数分析相关推荐

  1. 【Android 逆向】Android 逆向通用工具开发 ( adb forward 网络端口重定向命令 | PC 端逆向程序主函数分析 )

    文章目录 前言 一.adb forward 网络端口重定向命令 二.PC 端逆向程序主函数分析 前言 本篇博客重点分析 PC 端 hacktool 模块 ; 一.adb forward 网络端口重定向 ...

  2. iOS蓝牙开发---CoreBluetooth[BLE 4.0] 初级篇[内附Demo地址]

    一.蓝牙基础知识 (一)常见简称 1.MFI  make for ipad ,iphone, itouch 专们为苹果设备制作的设备,开发使用ExternalAccessory 框架(认证流程貌似挺复 ...

  3. android低耗能蓝牙开发,Android BLE低功耗蓝牙开发

    最近做了一个智能硬件开发(针灸仪)的项目,有一部分涉及到低功耗蓝牙的开发,就是通过蓝牙和设备进行数据的交互,比如控制改设备的LED的开关,设备的开关机,设置设备的时间和温度等,下面就项目中遇到的坑一一 ...

  4. 蓝牙开发|蓝牙技术介绍

    蓝牙技术介绍 1. 蓝牙概述 蓝牙,是一种支持设备短距离通信(一般10m内)的无线电技术,能在包括移动电话.PDA.无线耳机.笔记本电脑.相关外设等众多设备之间进行无线信息交换.利用"蓝牙& ...

  5. Android Bluetooth蓝牙开发\蓝牙协议\蓝牙通信例子_Android支持蓝牙4.0版本_BLE开发

    一.Android Bluetooth现状 在android官网可以了解到android4.2新增了部分新功能,但是对于BT熟悉的人或许开始头疼了,那就是Android4.2引入了一个新的蓝牙协议栈针 ...

  6. Android bluetooth 蓝牙开发/蓝牙协议/蓝牙通信

    一.Android Bluetooth现状 在android官网可以了解到android4.2新增了部分新功能,但是对于BT熟悉的人或许开始头疼了,那就是Android4.2引入了一个新的蓝牙协议栈针 ...

  7. Android Bluetooth蓝牙开发\蓝牙协议\蓝牙通信

    一.Android Bluetooth现状 在android官网可以了解到android4.2新增了部分新功能,但是对于BT熟悉的人或许开始头疼了,那就是Android4.2引入了一个新的蓝牙协议栈针 ...

  8. 【Android 性能优化】应用启动优化 ( 安卓应用启动分析 | ActivityThread 主函数分析 | 应用初始化 | 启动优化项目 )

    文章目录 一. 应用入口函数 ActivityThread 主函数 main 二. ActivityThread 类 attach 方法 ( 应用加载 ) 三. ActivityThread 类 ha ...

  9. 使用C#进行蓝牙开发-接收BLE广播

    使用BluetoothLEAdvertisementWatcher类来接收附近的蓝牙广播,这个是UWP的类,关于如何在WPF或者命令行程序中使用这个类,随便一搜就有了,很简单,.NET6之后简单设置一 ...

最新文章

  1. Compressor detection can only be called on some ……
  2. linux内核配置系统浅析(转)
  3. 暗杀TIME-WAIT
  4. 【Linux】一步一步学Linux——tail命令(42)
  5. am5718_基于TI AM5718 车牌识别系统解决方案 - 飞凌嵌入式行业资讯 - 保定飞凌嵌入式技术有限公司...
  6. Maven的Archetype简介
  7. How to deal with error message Could not start the app due to a configuration problem
  8. GDG Xian: 假如我是一个浏览器
  9. 法拉第未来获得2.25亿美元债权及信托融资
  10. AI给植物看病,宾大用TensorFlow做的这款应用造福坦桑尼亚农民
  11. url 获取 geoserver 中对应的style
  12. 关于Scala和面向对象的函数式编程
  13. AD09 pcb绘制技巧笔记
  14. 文章编辑数据结构课程设计
  15. [Ansible系列⑦]ansible fact变量
  16. 解决java获取系统时间差8个小时 专题
  17. 大学四年,我把私藏「B站」 20 个学习 UP 主贡献出来!
  18. Electron渲染页面(Renderer Process)引入ipcRenderer
  19. python滤波与图像去噪
  20. 从阿里 Weex 一窥移动技术发展之路

热门文章

  1. 一生用10亿元也买不来的经商经验
  2. VSCode运行Vue项目后自动打开浏览器
  3. 成为高可视化地图应用专家-赋能智慧城市建设
  4. java计算机毕业设计汽车站车辆运管系统源码+系统+数据库+lw文档+mybatis+运行部署
  5. Arduino与Proteus仿真实例-温控风扇仿真
  6. 5G自组网调度指挥系统现场救援人员的千里眼
  7. 汉语拼音新规将实施 拼音中文名须姓在前名在后
  8. SCI写作中常见的转折用法
  9. 东北大学大数据班机器学习大作业——印度房价预测
  10. oracle坏块 戴明明,Oracle数据库问题解决方案和故障排除手册