这个指南描述了什么是ESB(Enhanced ShockBurst)以及如何在nRF5系列中使用ESB.
ESB支持双向数据包通信,包括数据包缓冲,数据包确认和丢失数据包的自动重传的基本协议.ESB提供低功耗的射频传输功能
ESB 支持双向数据包通信的基本协议包括数据包缓冲,数据包确认和丢失数据包的自动重传。实现代码小且易于使用.
ESB协议嵌入在传统nRF24L系列的硬件中,而增强型ShockBurst模块(即ESB)使nRF5系列设备能够使用ESB协议与nRF5或nRF24L系列设备进行通信。
ESB具有自动数据包事务处理功能,可轻松实现可靠的双向数据链路, 在这当中,传输包(A transaction is a packet)是两个收发器之间的分组交换,一个收发器作为主接收PRX,一个收发器作为主发送PTX.
ESB特性:
- 支持一个星型网络拓扑结构,典型的是一路主接收,多达八路的主发送。
- 传统模式下支持1到32字节的动态payload(数据传输宽度)
- NRF5系列之间支持1-252字节的静态的payload(数据传输宽度)
- 每个PTX和PRX节点都支持双工数据发送接收
- 数据包确认和自动数据包重传功能
- 每个管道都有独立的发送和接收FIFO
- 后向兼容NRF2401的增强型ESB。
资源:
ESB使用一组固定的资源,并要求对它们进行独占访问以确保正确操作。
- 射频(NRF_RADIO)
- 定时器:NRF_TIMER2,NRF_TIMER3:
- PPI(可编程外围互联)通道7,8,9,10,11,12,13
- 软件中断0
注意:没有MPU强制执行此独占访问,因此操纵这些资源会产生未定义的行为。(即最好避免操作这些资源)
另外:射频和定时器中断处理运行在0优先级(即最高优先级),ESB回调功能函数运行在1优先级。因此在应用程序中使用的其他中断应当运行在2-7优先级以确保正确操作。
/********************************************************************************
* @file biz_esb.c
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-07-06
* @brief [业务]2.4G管理 资料参考:
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <stdbool.h>
#include <string.h>
#include "nrf.h"
#include "nrf_esb.h"
#include "nrf_error.h"
#include "nrf_esb_error_codes.h"
#include "sdk_macros.h"
#include "queue.h"
/* Private Includes ----------------------------------------------------------*/
#include "business_function.h"
#include "business_gpio.h"
#include "log.h"
#include "app_main.h"
#include "biz_esb.h"
#include "os_api.h"
#include "bsp_rng.h"
#include "biz_fds.h"
/* Private Define ------------------------------------------------------------*/
/* External Variables --------------------------------------------------------*/
/* Private Variables ---------------------------------------------------------*/
static nrf_esb_payload_t tx_payload = NRF_ESB_CREATE_PAYLOAD(0, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00);
static nrf_esb_payload_t rx_payload = {0};
#pragma pack(4)
__attribute__((aligned(4))) uint8_t rf_freq_table[RF_FREQ_MAX_VAL] = {0x20, 0x21, 0x22, 0x23, 0x24};
__attribute__((aligned(4))) uint8_t rf_mac_addr[RF_MAC_MAX_VAL] = {0x55, 0x56, 0x57, 0x58, 0x59};
#pragma pack()
bool g_esb_init_flag = false;
bool g_flash_write_24g_mac_flag = 0;
bool g_flash_write_24g_freq_flag = 0;
static uint8_t base_addr_0[4] = {0xE7, 0xE7, 0xE7, 0xE7};
static uint8_t base_addr_1[4] = {0xC2, 0xC2, 0xC2, 0xC2};
static uint8_t addr_prefix[8] = {0xE7, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8};
// 发送端
uint8_t rf_tx_freq_index = 0; // 当前射频通道号
bool rf_tx_connect_status = false; // false--未连接 ture--已连接
uint8_t rf_tx_heart_tick = 0; // 当前心跳次数
uint8_t rf_tx_connect_time = 0; // 当前连接时长
// 接收端
uint8_t rf_rx_freq_index = 0; // 当前射频通道号
bool rf_rx_connect_status = false; // false--未连接 ture--已连接
uint8_t rf_rx_heart_time = 0; // 当前连接时长
// 注册接收回调函数,给外部使用
typedef void(*esb_rx_event_callback)(uint8_t *data, uint16_t len);
static esb_rx_event_callback g_rx_dispose_callback;
// 消息队列
#define RF_TX_Q_ITEM_CNT 30
#define RF_TX_Q_ITEM_SIZE (12)
/***************消息队列定义**************/
// 用于rf_tx消息队列总缓存区
static uint8_t m_rf_tx_q_buff[RF_TX_Q_ITEM_CNT * RF_TX_Q_ITEM_SIZE] = {0};
queue_t m_rf_tx_q =
{
.pbuff = m_rf_tx_q_buff,
.front = 0,
.rear = 0,
.item_cnt = RF_TX_Q_ITEM_CNT,
.item_size = RF_TX_Q_ITEM_SIZE
};
uint8_t g_rf_tx_data[RF_TX_Q_ITEM_SIZE] = {0}; // 用于RF消息临时缓存区
/* Private Function Prototypes -----------------------------------------------*/
/*------------------------------------------------【公共函数】------------------------------------------------*/
/**
* @brief [ESB操作]ESB反初始化
*/
void esb_deinit(void)
{
if (!g_esb_init_flag)
{
return;
}
g_esb_init_flag = false;
nrf_esb_disable();
}
/**
* @brief [ESB回调事件] 接收通知收发事件,并处理事件
* @param *p_event: esb事件类型
*/
void nrf_esb_event_handler(nrf_esb_evt_t const *p_event)
{
switch (p_event->evt_id)
{
case NRF_ESB_EVENT_TX_SUCCESS:
//LOG_D("---------------NRF_ESB_EVENT_TX_SUCCESS---------------");
if (get_comm_connect_state() == SYS_CONNECT_TYPE_RF)
{
esb_clean_tx_heart_time();
// 如果上次处于未连接,则清除运行时间记录
if (esb_get_tx_connect_state() == false)
{
esb_set_tx_connect_state(true);
main_send_signal(SIGNAL_REFRESH_RF_INDICATOR_LIGHT);
}
}
break;
case NRF_ESB_EVENT_TX_FAILED:
// 发送失败,切换频道继续发送
rf_tx_freq_index++;
if (rf_tx_freq_index >= RF_FREQ_MAX_VAL)
{
// LOG_D("NRF_ESB_EVENT_TX_FAILED");
rf_tx_freq_index = 0;
}
nrf_esb_set_rf_channel(rf_freq_table[rf_tx_freq_index]);
nrf_esb_start_tx();
break;
case NRF_ESB_EVENT_RX_RECEIVED:
if (nrf_esb_read_rx_payload(&rx_payload) == NRF_SUCCESS)
{
if (get_comm_connect_state() == SYS_CONNECT_TYPE_RF)
{
esb_clean_rx_heart_time();
esb_set_rx_connect_state(true);
// LOG_D("data:%02x,%02x,%02x,%02x,%02x|%02x", rx_payload.data[0], rx_payload.data[1],
// rx_payload.data[2], rx_payload.data[3], rx_payload.data[4], rx_payload.length);
// 跳转业务处理函数
if (g_rx_dispose_callback)
{
g_rx_dispose_callback(rx_payload.data, rx_payload.length);
}
}
}
break;
}
}
/**
* @brief [ESB操作][得到指针] 射频通道数组
* @retval 返回射频通道数组指针,长度由RF_FREQ_MAX_VAL决定
*/
uint8_t *esb_get_rf_freq_table(void)
{
return rf_freq_table;
}
/**
* @brief [ESB操作][得到指针] 得到新的MAC地址数组
* @retval 返回MAC地址数组指针,长度由RF_MAC_MAX_VAL决定
*/
uint8_t *esb_get_new_rf_mac_addr(void)
{
random_vector_generate(rf_mac_addr, RF_MAC_MAX_VAL);
return rf_mac_addr;
}
/**
* @brief [ESB操作][得到指针] MAC地址数组
* @retval 返回MAC地址数组指针,长度由RF_MAC_MAX_VAL决定
*/
uint8_t *esb_get_rf_mac_addr(void)
{
return rf_mac_addr;
}
/**
* @brief [ESB操作] MAC重置,取随机值
*/
void esb_rf_mac_init(void)
{
uint8_t *mac_addr_temp = esb_get_rf_mac_addr();
if (mac_addr_temp[0] == 0x55 &&
mac_addr_temp[1] == 0x56 &&
mac_addr_temp[2] == 0x57 &&
mac_addr_temp[3] == 0x58 &&
mac_addr_temp[4] == 0x59)
{
random_vector_generate(mac_addr_temp, RF_MAC_MAX_VAL);
g_flash_write_24g_mac_flag = true;
LOG_D("<DEBUG> NEW_MAC:%02x %02x %02x %02x %02x\r\n", esb_get_rf_mac_addr()[0], esb_get_rf_mac_addr()[1],
esb_get_rf_mac_addr()[2], esb_get_rf_mac_addr()[3], esb_get_rf_mac_addr()[4]);
}
}
/*------------------------------------------------【发送端】------------------------------------------------*/
/**
* @brief [ESB操作][发送端] ESB初始化
* @retval 状态
*/
uint32_t esb_tx_init(void)
{
if (g_esb_init_flag)
{
return 0;
}
g_esb_init_flag = true;
uint32_t err_code;
addr_prefix[2] = rf_mac_addr[0];
base_addr_1[0] = rf_mac_addr[1];
base_addr_1[1] = rf_mac_addr[2];
base_addr_1[2] = rf_mac_addr[3];
base_addr_1[3] = rf_mac_addr[4];
nrf_esb_config_t nrf_esb_config = NRF_ESB_DEFAULT_CONFIG;
nrf_esb_config.protocol = NRF_ESB_PROTOCOL_ESB_DPL;
nrf_esb_config.retransmit_delay = 250;
nrf_esb_config.retransmit_count = 3;
nrf_esb_config.bitrate = NRF_ESB_BITRATE_2MBPS;
nrf_esb_config.event_handler = nrf_esb_event_handler;
nrf_esb_config.mode = NRF_ESB_MODE_PTX;
nrf_esb_config.selective_auto_ack = false;
nrf_esb_config.crc = NRF_ESB_CRC_16BIT;
nrf_esb_config.tx_output_power = NRF_ESB_TX_POWER_4DBM;
nrf_esb_config.payload_length = 32;
err_code = nrf_esb_init(&nrf_esb_config);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_0(base_addr_0);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_1(base_addr_1);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_prefixes(addr_prefix, NRF_ESB_PIPE_COUNT);
VERIFY_SUCCESS(err_code);
tx_payload.pipe = 2;
return err_code;
}
/**
* @brief [ESB操作][发送端][得到状态] 2.4G连接状态
* @retval false--未连接 ture--已连接
*/
bool esb_get_tx_connect_state(void)
{
return rf_tx_connect_status;
}
/**
* @brief [ESB操作][发送端][设置状态] 2.4G连接状态
* @param state: false--未连接 ture--已连接
*/
void esb_set_tx_connect_state(bool state)
{
rf_tx_connect_status = state;
}
/**
* @brief [ESB操作][发送端][复位变量] 清除[发送心跳]次数和连接时间
*/
void esb_clean_tx_heart_time(void)
{
rf_tx_heart_tick = 0;
rf_tx_connect_time = 0;
}
/**
* @brief [ESB操作][发送端][操作变量] 心跳次数递增1
*/
void esb_heart_tx_tick_add(void)
{
rf_tx_heart_tick++;
}
/**
* @brief [ESB操作][发送端][得到变量值] 心跳次数
* @retval 心跳次数
*/
uint8_t esb_get_heart_tx_tick(void)
{
return rf_tx_heart_tick;
}
/**
* @brief [ESB操作][发送端][发送数据] 发送一包数据(异步)
* @note 异步发送
* @param *pdata: 待发送的数据指针
* @param len: 数据长度
* @retval 发送状态
*/
uint32_t esb_tx_send(uint8_t *pdata, uint8_t len)
{
tx_payload.length = len;
memcpy(tx_payload.data, pdata, len);
return nrf_esb_write_payload(&tx_payload);;
}
const uint8_t g_heart_array = 0;
/**
* @brief [ESB操作][发送端] 发送心跳包
*/
void esb_send_heart_pack(void)
{
//*************[ESB 2.4G操作] 实现心跳包**********************
esb_heart_tx_tick_add();
if (esb_get_heart_tx_tick() > 3)
{
send_rf_data((uint8_t *)&g_heart_array, 1);
rf_tx_connect_time++;
if (rf_tx_connect_time > 3)
{
esb_clean_tx_heart_time();
if (esb_get_tx_connect_state() == true)
{
esb_set_tx_connect_state(false);
main_send_signal(SIGNAL_REFRESH_RF_INDICATOR_LIGHT);
}
}
}
}
/**
* @brief 发送数据给rf通道(消息入队,非实时发送)
* @param *data: 欲发送内容
* @param len: 内容长度(字节大小)
*/
void send_rf_data(uint8_t *data, uint16_t len)
{
if (!queue_en(&m_rf_tx_q, data, len))
{
LOG_D("<DEBUG> [send_rf_data] queue_en fail\r\n");
}
}
/**
* @brief 处理消息队列中的消息,通过rf发送(放在空闲线程使用)
*/
bool rf_data_dispose(void)
{
if (!queue_de(&m_rf_tx_q, g_rf_tx_data))
return false;
esb_tx_send(g_rf_tx_data, RF_TX_Q_ITEM_SIZE);
return true;
}
void esb_set_rf_and_start_tx(void)
{
nrf_esb_set_rf_channel(rf_freq_table[rf_tx_freq_index]);
nrf_esb_start_tx();
}
/*------------------------------------------------【接收端】------------------------------------------------*/
/**
* @brief [ESB操作][接收端] ESB初始化
* @retval 状态
*/
uint32_t esb_rx_init(void)
{
if (g_esb_init_flag)
{
return 0;
}
g_esb_init_flag = true;
uint32_t err_code;
addr_prefix[2] = rf_mac_addr[0];
base_addr_1[0] = rf_mac_addr[1];
base_addr_1[1] = rf_mac_addr[2];
base_addr_1[2] = rf_mac_addr[3];
base_addr_1[3] = rf_mac_addr[4];
nrf_esb_config_t nrf_esb_config = NRF_ESB_DEFAULT_CONFIG;
nrf_esb_config.protocol = NRF_ESB_PROTOCOL_ESB_DPL;
nrf_esb_config.bitrate = NRF_ESB_BITRATE_2MBPS;
nrf_esb_config.event_handler = nrf_esb_event_handler;
nrf_esb_config.mode = NRF_ESB_MODE_PRX;
nrf_esb_config.selective_auto_ack = false;
nrf_esb_config.crc = NRF_ESB_CRC_16BIT;
nrf_esb_config.tx_output_power = NRF_ESB_TX_POWER_4DBM;
nrf_esb_config.payload_length = 32;
err_code = nrf_esb_init(&nrf_esb_config);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_0(base_addr_0);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_1(base_addr_1);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_prefixes(addr_prefix, NRF_ESB_PIPE_COUNT);
VERIFY_SUCCESS(err_code);
return err_code;
}
/**
* @brief [ESB操作][接收端][设置状态] 2.4G连接状态
* @param state: false--未连接 ture--已连接
*/
void esb_set_rx_connect_state(bool state)
{
rf_rx_connect_status = state;
}
/**
* @brief [ESB操作][接收端][得到状态] 2.4G连接状态
* @retval false--未连接 ture--已连接
*/
bool esb_get_rx_connect_state(void)
{
return rf_rx_connect_status;
}
/**
* @brief [ESB操作][接收端][复位变量] 清除[发送心跳]次数和连接时间
*/
void esb_clean_rx_heart_time(void)
{
rf_rx_heart_time = 0;
}
/**
* @brief [ESB操作][接收端][操作变量] 心跳次数递增1
*/
void esb_heart_rx_tick_add(void)
{
rf_rx_heart_time++;
}
/**
* @brief [ESB操作][接收端][得到变量值] 心跳次数
* @retval 心跳次数
*/
uint8_t esb_get_heart_rx_tick(void)
{
return rf_rx_heart_time;
}
/**
* @brief [ESB操作][接收端] 接收超时则切换通道
*/
void esb_receipt_heart_pack(void)
{
esb_heart_rx_tick_add();
if (esb_get_heart_rx_tick() > 4)
{
esb_clean_rx_heart_time();
rf_rx_freq_index++;
if (rf_rx_freq_index >= RF_FREQ_MAX_VAL)
{
rf_rx_freq_index = 0;
}
nrf_esb_set_rf_channel(rf_freq_table[rf_rx_freq_index]);
nrf_esb_start_rx();
if (esb_get_rx_connect_state() == true)
{
esb_set_rx_connect_state(false);
}
}
}
void esb_set_rf_and_start_rx(void)
{
nrf_esb_set_rf_channel(rf_freq_table[rf_rx_freq_index]);
nrf_esb_start_rx();
}
/**
* @brief [ESB操作][接收端] ESB注册数据接收函数
* @param *event: 绑定的函数指针
* @retval false--错误 ture--正确
*/
bool biz_esb_rx_dispose_reg_callback(void *event)
{
if (g_rx_dispose_callback != NULL)
{
return false;
}
else
{
g_rx_dispose_callback = (esb_rx_event_callback)event;
}
return true;
}
/********************************************************************************
* @file biz_esb.h
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-07-06
* @brief [业务]2.4G管理
********************************************************************************/
#ifndef __BIZ_ESB_H
#define __BIZ_ESB_H
/* Public Include ------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
/* Public Define -------------------------------------------------------------*/
/* External Variables --------------------------------------------------------*/
extern bool g_flash_write_24g_mac_flag;
extern bool g_flash_write_24g_freq_flag;
/* Public Function Prototypes ------------------------------------------------*/
uint32_t esb_tx_init(void);
uint32_t esb_rx_init(void);
void esb_deinit(void);
void esb_rf_mac_init(void);
uint32_t esb_tx_send(uint8_t *pdata, uint8_t len);
uint8_t *esb_get_rf_freq_table(void);
uint8_t *esb_get_rf_mac_addr(void);
uint8_t *esb_get_new_rf_mac_addr(void);
void send_rf_data(uint8_t *data, uint16_t len);
bool rf_data_dispose(void);
// 发送端 功能函数
void esb_send_heart_pack(void);
void esb_clean_tx_heart_time(void);
uint8_t esb_get_heart_tx_tick(void);
void esb_heart_tx_tick_add(void);
bool esb_get_tx_connect_state(void);
void esb_set_tx_connect_state(bool state);
void esb_set_rf_and_start_tx(void);
// 接收端 功能函数
void esb_receipt_heart_pack(void);
void esb_clean_rx_heart_time(void);
uint8_t esb_get_heart_rx_tick(void);
void esb_heart_rx_tick_add(void);
bool esb_get_rx_connect_state(void);
void esb_set_rx_connect_state(bool state);
void esb_set_rf_and_start_rx(void);
bool biz_esb_rx_dispose_reg_callback(void *event);
#endif
伪代码,演示使用:
/**
* @brief [软定时器回调事件] 用于进入低功耗【前期】处理 基准100ms
* @note 让系统进入低功耗模式
*/
static void timer_low_power(void const *argument)
{
if (get_comm_connect_state() == SYS_CONNECT_TYPE_RF)
{
g_sys_run_times += TIMER_LOW_POWER_PERIOD;
}
else
{
g_sys_run_times = 0;
}
g_free_times ++;
#if BS_24G_SUPPORT
if (get_sys_state() == SYS_STATE_POWER_ON)
{
if (get_comm_connect_state() == SYS_CONNECT_TYPE_RF)
{
main_send_signal(SIGNAL_RF_SEND_HEART_PACK);
}
}
#endif
}
//主线程信号量
if (event.value.signals & SIGNAL_RF_SEND_HEART_PACK)
{
esb_send_heart_pack();
}
/**
* @brief 线程空闲处理函数
*/
static void idle_state_handle(void)
{
static uint16_t tick = 1;
static uint16_t g_free_tick = 1;
tick++;
g_free_tick++;
if (tick % 1000 == 0)
{
app_task_led();
if (get_comm_connect_state() == SYS_CONNECT_TYPE_RF)
{
#if BS_24G_SUPPORT
if (rf_data_dispose())
{
LOG_D("<DEBUG> [RF] send ok!!!\r\n");
g_free_tick = 0;
return;
}
#endif
}
if (g_free_tick > 3000 && flash_write_sys_cfg())
{
g_free_tick = 0;
return;
}
else
{
#if !DEBUG_MODE
if (g_free_tick > 10000)
{
sys_sleep();
}
#endif
}
}