nRF52832蓝牙主机实战:用Nordic SDK实现按键控制从机与定时发送(附完整代码)
2026/6/5 3:46:03 网站建设 项目流程

nRF52832蓝牙主机实战:用Nordic SDK实现按键控制从机与定时发送

在物联网设备开发中,蓝牙主机(Central)与从机(Peripheral)的交互是最常见的应用场景之一。nRF52832作为Nordic Semiconductor的明星产品,凭借其低功耗特性和强大的蓝牙5.0支持,成为许多嵌入式工程师的首选。本文将基于nRF5 SDK,深入讲解如何构建一个功能完整的蓝牙主机系统,实现按键触发数据发送、接收从机数据并打印,以及通过定时器实现周期性通信。

1. 项目架构与核心组件

一个典型的蓝牙主机系统需要处理多个关键任务:

  • 设备发现与连接:扫描并识别支持NUS(Nordic UART Service)的从机设备
  • 数据收发:通过GATT协议实现双向通信
  • 用户交互:通过物理按键触发特定操作
  • 定时任务:周期性执行特定功能

nRF5 SDK为这些功能提供了完善的API支持,主要涉及以下模块:

模块功能关键API
BLE Stack蓝牙协议栈基础sd_ble_*系列函数
NUS ClientUART服务客户端ble_nus_c_*系列函数
BSP板级支持包bsp_init,bsp_event_handler
App Timer应用定时器app_timer_create,app_timer_start

硬件初始化是项目的第一步,需要正确配置GPIO、定时器和蓝牙协议栈:

static void buttons_leds_init(void) { ret_code_t err_code; bsp_event_t startup_event; // 初始化LED和按键,注册事件回调 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); }

2. NUS服务实现与数据交互

Nordic UART Service(NUS)是模拟串口通信的蓝牙服务,极大简化了数据传输实现。主机端需要完成服务发现、特性配置和数据收发三个关键步骤。

2.1 服务发现与连接建立

当主机成功连接从机后,会触发服务发现过程。ble_nus_c_evt_handler是处理这些事件的核心:

static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, ble_nus_c_evt_t const * p_ble_nus_evt) { ret_code_t err_code; switch (p_ble_nus_evt->evt_type) { case BLE_NUS_C_EVT_DISCOVERY_COMPLETE: NRF_LOG_INFO("发现服务完成"); // 分配连接句柄 err_code = ble_nus_c_handles_assign(p_ble_nus_c, p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles); APP_ERROR_CHECK(err_code); // 启用从机通知 err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c); APP_ERROR_CHECK(err_code); break; case BLE_NUS_C_EVT_NUS_TX_EVT: // 处理接收到的数据 break; case BLE_NUS_C_EVT_DISCONNECTED: NRF_LOG_INFO("连接断开"); scan_start(); // 重新开始扫描 break; } }

2.2 数据发送机制

主机通过ble_nus_c_string_send函数向从机发送数据。按键触发是最常见的场景:

void bsp_event_handler(bsp_event_t event) { uint8_t data_array[1]; uint32_t ret_val; switch (event) { case BSP_EVENT_KEY_0: data_array[0] = 0x02; ret_val = ble_nus_c_string_send(&m_ble_nus_c, data_array, 1); break; case BSP_EVENT_KEY_1: data_array[0] = 0x04; ret_val = ble_nus_c_string_send(&m_ble_nus_c, data_array, 1); break; // 其他按键处理... } }

3. 定时器集成与周期性通信

许多应用需要定期发送心跳包或采集数据。nRF5 SDK的app_timer模块提供了轻量级的定时器解决方案。

3.1 定时器初始化

#define SEND_INTERVAL APP_TIMER_TICKS(2000) // 2秒间隔 APP_TIMER_DEF(m_send_timer_id); // 定时器实例 static void timer_init(void) { ret_code_t err_code = app_timer_init(); APP_ERROR_CHECK(err_code); // 创建重复定时器 err_code = app_timer_create(&m_send_timer_id, APP_TIMER_MODE_REPEATED, timer_handler); APP_ERROR_CHECK(err_code); }

3.2 定时器控制

通过按键开启/关闭定时发送功能:

static bool timer_active = false; void bsp_event_handler(bsp_event_t event) { // ...其他按键处理 case BSP_EVENT_KEY_3: if(!timer_active) { application_timers_start(); timer_active = true; } else { application_timers_stop(); timer_active = false; } break; } static void application_timers_start(void) { ret_code_t err_code; err_code = app_timer_start(m_send_timer_id, SEND_INTERVAL, NULL); APP_ERROR_CHECK(err_code); }

3.3 定时器回调实现

static void timer_handler(void * p_context) { UNUSED_PARAMETER(p_context); static uint8_t counter = 0; uint8_t data_array[1]; data_array[0] = 0x02; // 定时发送的数据 ble_nus_c_string_send(&m_ble_nus_c, data_array, 1); counter++; if(counter >= 10) counter = 0; // 防止溢出 }

4. 常见问题与调试技巧

在开发过程中,经常会遇到各种错误和异常情况。以下是几个典型问题及其解决方案:

4.1 NRF_ERROR_INVALID_STATE错误

这个错误通常发生在尝试操作尚未准备好的蓝牙连接时。常见原因包括:

  1. 服务发现未完成就尝试发送数据
  2. 连接已断开但未正确更新状态
  3. CCCD(Client Characteristic Configuration Descriptor)未正确配置

解决方案

uint32_t ble_nus_c_tx_notif_enable(ble_nus_c_t * p_ble_nus_c) { // 检查连接状态和句柄有效性 if ((p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID) || (p_ble_nus_c->handles.nus_tx_cccd_handle == BLE_GATT_HANDLE_INVALID)) { return NRF_ERROR_INVALID_STATE; } return cccd_configure(p_ble_nus_c->conn_handle, p_ble_nus_c->handles.nus_tx_cccd_handle, true); }

4.2 数据收发不稳定的处理

  • 增加重试机制:对于关键数据,实现简单的重发逻辑
  • 添加数据校验:在应用层实现简单的校验和或CRC检查
  • 优化MTU大小:通过sd_ble_gattc_exchange_mtu_request协商更大的MTU

4.3 功耗优化建议

  1. 在不需通信时降低广播间隔
  2. 合理设置连接参数(conn_params)
  3. 使用sd_app_evt_wait进入低功耗模式
  4. 动态调整射频输出功率
// 设置蓝牙发射功率 ret_code_t err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 4); // +4dBm APP_ERROR_CHECK(err_code);

5. 项目扩展与进阶功能

基础功能实现后,可以考虑添加更多实用功能提升系统价值:

5.1 多从机连接管理

nRF52832支持同时连接多个从机设备。需要维护多个连接句柄和服务实例:

#define MAX_CONNECTIONS 3 typedef struct { ble_nus_c_t nus_instance; uint16_t conn_handle; bool connected; } ble_connection_t; ble_connection_t m_connections[MAX_CONNECTIONS]; // 在连接事件中分配实例 static void on_connect(ble_evt_t const * p_ble_evt) { for(int i=0; i<MAX_CONNECTIONS; i++) { if(!m_connections[i].connected) { m_connections[i].conn_handle = p_ble_evt->evt.gap_evt.conn_handle; m_connections[i].connected = true; // 初始化NUS客户端实例 ble_nus_c_init_t nus_init = {0}; nus_init.evt_handler = nus_evt_handler; ble_nus_c_init(&m_connections[i].nus_instance, &nus_init); break; } } }

5.2 数据加密与安全

对于敏感数据,应启用蓝牙配对和加密:

static void ble_stack_init(void) { ble_gap_conn_params_t gap_conn_params; ble_gap_conn_sec_mode_t sec_mode; BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&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); // 设置配对参数 ble_opt_t opt; opt.gap_opt.passkey.p_passkey = "123456"; // 静态配对码 err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &opt); APP_ERROR_CHECK(err_code); }

5.3 OTA固件更新

通过蓝牙实现固件更新可以极大提升产品维护效率。nRF5 SDK提供了DFU(Device Firmware Update)功能:

  1. 实现Bootloader引导程序
  2. 使用nrfutil工具生成DFU包
  3. 通过NUS服务传输固件数据
  4. 校验完成后跳转到新固件
// DFU数据传输示例 static void handle_dfu_data(uint8_t * p_data, uint16_t length) { static uint32_t dfu_offset = 0; // 写入Flash ret_code_t err_code = nrf_dfu_flash_store(dfu_offset, p_data, length); if(err_code == NRF_SUCCESS) { dfu_offset += length; send_dfu_progress(dfu_offset); } else { send_dfu_error(err_code); } }

在实际项目中,蓝牙主机的开发远不止于代码实现。合理的状态机设计、完善的错误处理和用户反馈机制同样重要。通过按键LED指示当前状态、使用蜂鸣器提示重要事件、记录运行日志到Flash等辅助功能,都能显著提升产品的用户体验和可靠性。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询