1 概念
蓝牙芯片:系统级芯片(System on chip)是一种集成多功能模块到单一芯片的集成电路。蓝牙芯片就是一种系统级芯片,蓝牙芯片集成MCU(MicroControlUnit,微型控制单元)和RF(RadioFrequence,射频)两个功能,其中MCU负责蓝牙协议栈处理,RF负责无线数据收发。
Nordic:蓝牙系统级芯片生产公司。
nRF52832:Nordic公司生产的系统级芯片型号,参数如下表所示:
CPU运算器 | Flash闪存 | RAM随机存储器 |
64 MHz Arm Cortex-M4 with FPU | 512/256 KB | 64/32 KB |
TWI/SPI/UART | PWM, PDM, I2S | ADC, Comparator |
2xTWI/SPI, SPI, UART | Yes | Yes |
Operating temperature | Supply voltage range | Packages |
-40 to 85 °C | 1.7 to 3.6 V | 6x6 mm QFN48 3.0x3.2 mm WLCSP50 |
TWI/I2C:两线串行接口,一根SCL,一根SDA,半双工通信;
SPI:同步串行总线,SDI(输入)、SDO(输出)、SCK(串行移位时钟)、CS(信号);
UART:串口,RX(接收)、TX(发送)和GND;
I2S:将两路音频信号变成单一的数据队列,比PCM更适合于立体声系统;
PDM:脉冲密度调制;
PWM:脉冲宽度调制;
ADC:模拟数字转换器;
Comparator:比较器,接受两路模拟信号输入,比较输入产生一路二进制输出信号;
Packages:封装方式。
BLE协议栈:实现低功耗蓝牙协议的代码,分为控制层、主协议层、应用层。
应用层:包括公有服务和私有服务等;
协议层:包括GAP、GATT、L2CAP、SMP、ATT等;
控制层:包括主机控制器HCI、链路层LL、物理层PHY等。
通用访问规范GAP:定义了蓝牙设备的广播、连接、发现和身份识别等基本行为的协议,主要功能包括:广播和扫描、连接和断开、安全性和身份识别;
通用属性配置文件GATT:定义设备之间数据通信的协议框架,主要包括服务、特性及描述符的定义,设备之间通过这些属性的读取和写入来交换数据。
服务配置列表:包含一个或多个服务的文件。
服务:蓝牙BLE中组织数据传输和接收的最小单位,包含一个或多个特性。
特性:包括特性参数和GATT属性;特性参数包括空中属性(写;没有回应的写;读;通知;指示)和描述符;GATT属性包括一个128位的UUID、属性参数和属性特征值。
服务UUID与特性UUID:用于标识一个服务/特性的唯一标识符,128位,通常表示为十六进制字符串。在 GATT 协议中,通过使用服务 UUID 和特性 UUID,蓝牙设备可以组织和交换数据。中央设备(如智能手机)可以通过读取和写入特性来与外围设备(如传感器)进行通信。这种基于 UUID 的标识机制确保了设备之间的互操作性。
MAC地址(Media Access Control Address):媒体存取控制位地址,蓝牙BLE的MAC地址可分为两种,分别为Public Device Address(公共设备地址)和Random Device Address(随机设备地址)。公共设备地址由企业向IEEE购买以保证唯一性;设置随机设备地址:静态设备地址11开头、不可解析私有地址00开头、可解析私有地址。
DFU(Device Firmware Update):设备固件升级,可以通过无线方式(OTA),重新烧录app到蓝牙芯片中,也可以通过有线方式(UART、USB及SPI)。
SWD(Serial Wire Debug):一种用于在嵌入式系统中进行调试和编程的通信接口。它是一种串行单线调试接口,用于连接调试工具(如调试器)与目标芯片,以进行调试、烧录和监控操作。
2 开发软件
keil MDK:C语言编写微控制器系统的软件。
nRFgo Studio:由 Nordic Semiconductor 开发的集成开发环境(IDE),配置和管理Nordic Semiconductor芯片相关的参数,可将程序烧录至芯片。
nRFconnect:由 Nordic Semiconductor 开发的一组工具和应用程序,用于开发、测试和调试基于 Nordic Semiconductor 射频(RF)芯片的无线应用。Program模块可烧录程序至芯片。
3 开发硬件
JLink:将在keil mdk等软件上编写的程序烧录至ARM、Cortex等芯片上的硬件。
nRF52832:Nordic公司生产的蓝牙芯片型号。
蓝牙芯片pinout通过杜邦线连接JLink仿真器,JLink仿真器通过USB连接电脑,安装驱动,重启电脑,打开Keil、nRFStudio和nRFconnect软件可连接并识别到设备。
4 硬件连接
4.1 实物连接方式(一)
如上图所示,传感器因电阻变化引起电压变化,通过数模转化器(HX711模块)将模拟信号转为数字信号,并通过(GND、VCC、RX和TX,即接地、电压、接收端和发送端)4个引脚将数据传输给蓝牙模块(HC-08)。该图中所用蓝牙芯片为TI厂家。
4.2 实物连接方式(二)
如上图所示,JLink仿真器通过USB连接至电脑,通过杜邦线连接至蓝牙芯片的程序烧录端口(对应蓝牙芯片的25、26号引脚),蓝牙芯片(7、8、9、10号引脚)通过CH340芯片(串口转USB)串口转为USB,再通过USB线连接电脑端串口小程序。
电脑端通过软件Keil将蓝牙程序烧录至蓝牙芯片,手机连接蓝牙后,可以发送指令让蓝牙芯片问串口要数据。
4.3 逻辑连接方式
上图所示为nordic 厂家的nRF52832型号的蓝牙芯片引脚图。
上图所示为SWD接口及传感器与蓝牙芯片引脚对应图。
上图所示为串口与蓝牙芯片引脚对应图。
5 程序从机设计
5.1 总体流程
连接流程:从机广播-》主机扫描-》发起连接-》连接建立-》数据交换。
蓝牙BLE基础工程搭建流程:打印输出LOG实现;板级设备初始化;内存管理初始化;协议栈初始化;GAP与GATT初始化;广播初始化;服务初始化;连接参数初始化。
5.2 协议栈初始化
该部分主要功能为使蓝牙能用。主要配置内容包括:时钟设置(内部RC时钟、外部晶振时钟、合成时钟)、广播事件、主从机设置、MTU、Gatt服务特性、注册蓝牙事件等。
外部晶振时钟:对电流消耗最低;
内部RC时钟:多消耗电流,节省成本;
内部高速时钟合成:消耗电流最多。
5.3 GAP设置
该部分主要功能为使蓝牙被发现和连接。主要配置内容包括:设备名称设置;连接参数设置(最小/最大连接间隔;从机延迟;连接超时监督时间)等。
5.4 添加服务
1)加入服务配置文件
创建services文件夹->引用官方xxx.c文件->在options for taret的C++中添加路径->在main.c文件中声明xxx.h文件,并声明xxx.h中的观察者函数名称
2)修改sdk_config.h服务配置
修改sdk_config.h文件中的NRF_SDH_BLE_VS_UUID_COUNT的数量,(因为该xx.c服务为官方提供服务)在配置文件中将BLE_LBS_C_ENABLED-ble_lbs_c-NordicLEDButtonServiceClient勾上
3)添加服务
main.c文件中services_init()方法调用xxx.c文件中声明的xxx_init()服务初始化函数,该函数的2个入参分别是该函数的观察者函数和初始化函数,在这个函数中调用sd_ble_gatts_service_add()来添加主服务
// Add Main Service
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_lbs->service_handle);
第一个参数:服务类型为主服务
第二个参数:用于服务的基础UUID
ble_uuid128_t base_uuid = {LBS_UUID_BASE};
err_code = sd_ble_uuid_vs_add(&base_uuid, &p_lbs->uuid_type);
VERIFY_SUCCESS(err_code);
ble_uuid.type = p_lbs->uuid_type;
ble_uuid.uuid = LBS_UUID_SERVICE;
第三个参数:唯一的服务句柄,作为服务的回调
4)添加特性
如果该服务不是SIG定义的服务,那么就需要自己根据GATT框架定义服务的特性。
// Add HT characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
// GATT
add_char_params.uuid = LBS_UUID_HT_CHAR; //
add_char_params.uuid_type = p_lbs->uuid_type;
add_char_params.init_len = 2;
add_char_params.max_len = 2;
// characteristic
add_char_params.char_props.read = 1;
add_char_params.char_props.write = 1;
add_char_params.char_props.notify = 1;
add_char_params.read_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
add_char_params.cccd_write_access = SEC_OPEN;
err_code = characteristic_add(p_lbs->service_handle,
&add_char_params,
&p_lbs->ht_char_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
5.5 广播设置
该部分主要功能为广播发包及回包内容设置。主要配置内容包括:设备全称类型、图标、服务UUID、广播模式设置等。
广播初始配置结构体:
typedef struct
{
ble_advdata_t advdata; /**< Advertising data: name, appearance, discovery flags, and more. ¹ã²¥°ü*/
ble_advdata_t srdata; /**< Scan response data: Supplement to advertising data. »Ø°ü*/
ble_adv_modes_config_t config; /**< Select which advertising modes and intervals will be utilized.*/
ble_adv_evt_handler_t evt_handler; /**< Event handler that will be called upon advertising events. */
ble_adv_error_handler_t error_handler; /**< Error handler that will propogate internal errors to the main applications. */
} ble_advertising_init_t;
广播发包和回收包结构体:
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;
广播模式配置结构体:
typedef struct
{
bool ble_adv_on_disconnect_disabled; /**< Enable or disable automatic return to advertising upon disconnecting.*/
bool ble_adv_whitelist_enabled; /**< Enable or disable use of the whitelist. */
bool ble_adv_directed_high_duty_enabled; /**< Enable or disable high duty direct advertising mode. Can not be used together with extended advertising. */
bool ble_adv_directed_enabled; /**< Enable or disable direct advertising mode. */
bool ble_adv_fast_enabled; /**< Enable or disable fast advertising mode. */
bool ble_adv_slow_enabled; /**< Enable or disable slow advertising mode. */
uint32_t ble_adv_directed_interval; /**< Advertising interval for directed advertising. */
uint32_t ble_adv_directed_timeout; /**< Time-out (number of tries) for direct advertising. */
uint32_t ble_adv_fast_interval; /**< Advertising interval for fast advertising. */
uint32_t ble_adv_fast_timeout; /**< Time-out (in units of 10ms) for fast advertising. */
uint32_t ble_adv_slow_interval; /**< Advertising interval for slow advertising. */
uint32_t ble_adv_slow_timeout; /**< Time-out (in units of 10ms) for slow advertising. */
bool ble_adv_extended_enabled; /**< Enable or disable extended advertising. */
uint32_t ble_adv_secondary_phy; /**< PHY for the secondary (extended) advertising @ref BLE_GAP_PHYS (BLE_GAP_PHY_1MBPS, BLE_GAP_PHY_2MBPS or BLE_GAP_PHY_CODED). */
uint32_t ble_adv_primary_phy; /**< PHY for the primary advertising. @ref BLE_GAP_PHYS (BLE_GAP_PHY_1MBPS, BLE_GAP_PHY_2MBPS or BLE_GAP_PHY_CODED). */
} ble_adv_modes_config_t;
入口文件中定义广播观察函数,连接发生变化后调用:
BLE_ADVERTISING_DEF(m_advertising);
#define BLE_ADVERTISING_DEF(_name)
static ble_advertising_t _name;
NRF_SDH_BLE_OBSERVER(_name ## _ble_obs,
BLE_ADV_BLE_OBSERVER_PRIO,
ble_advertising_on_ble_evt, &_name)
void ble_advertising_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_advertising_t * p_advertising = (ble_advertising_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connected(p_advertising, p_ble_evt);
break;
// Upon disconnection, whitelist will be activated and direct advertising is started.
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_advertising, p_ble_evt);
break;
// 广播超时后,开始下一个广播模式
case BLE_GAP_EVT_ADV_SET_TERMINATED:
on_terminated(p_advertising, p_ble_evt);
break;
default:
break;
}
}
static void on_terminated(ble_advertising_t * const p_advertising, ble_evt_t const * p_ble_evt)
{
ret_code_t ret;
if (p_ble_evt->header.evt_id != BLE_GAP_EVT_ADV_SET_TERMINATED)
{
// Nothing to do.
return;
}
if ( p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_TIMEOUT//¹ã²¥³¬Ê±
||p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_LIMIT_REACHED)´ïµ½ÏÞÖƵĹ㲥ʼþ
{
//开始广播下一个广播模式
ret = ble_advertising_start(p_advertising, adv_mode_next_get(p_advertising->adv_mode_current));
if ((ret != NRF_SUCCESS) && (p_advertising->error_handler != NULL))
{
p_advertising->error_handler(ret);
}
}
}
5.6 启动广播
advertising_start(erase_bonds):启动广播;
ret = set_adv_mode_fast(p_advertising, &p_advertising->adv_params):设置为快速广播;
主函数BLE_ADVERTISING_DEF(m_advertising):广播观察函数,设置时间内未连接切换为慢速广播,最终切换为无效广播,设备进入休眠状态。
5.7 数据传输
采用观察者模型,即事先在main.c文件中定义监听函数,一旦收到主机消息即可调用:
NRF_SDH_BLE_OBSERVER(_name ## _obs,=BLE_LBS_BLE_OBSERVER_PRIO,ble_lbs_on_ble_evt, &_name)
服务建立后,主机通过服务中的写属性写一个数据给从机,从机接收到数据后,从协议栈上产生一个BLE_GATTS_EVT_WRITE事件,此时可调度服务处理事件。调度事件派发函数ble_lbs_on_ble_evt,该函数使用简单的switch-case语句通过回调事件头部的id区分不同的事件,并进行不同的处理。
(1)主从机通信方式
1)串口通道AT指令:将蓝牙设置为串口,通过串口协议进行通信,暂未调研。
2)私有任务通道:通过配置的服务和特性进行数据传输,即5.4节服务配置。
(2)从机获取数据方式
1)传感器数据通信
通过引脚直接获取数据,按照官方文件修改。
2)蓝牙串口通信
串口:串行通讯端口,9针或25针。
CH340T:USB转串口的硬件板。从机上配置CH340T可将串口转为USB接口。
传感器主推数据,蓝牙从缓存中获取数据app_uart_get(),app_uart_fifo.c库文件
static void uart_loopback_test()
{
while (true)
{
uint8_t cr;
while(app_uart_get(&cr) != NRF_SUCCESS);
while(app_uart_put(cr) != NRF_SUCCESS);
if (cr == 'q' || cr == 'Q')
{
printf(" \n\rExit!\n\r");
while (true)
{
// Do nothing.
}
}
}
}
3)Flash存储
可从存储器中获取数据,暂未调研。
1)fstorage;
2)fds。