NRF52832之ESB功能与NRF24L01进行2.4G通信
NRF52之ESB学习笔记
目的:为了能与NRF24L01通信,工程中加入nrf_esb.c, nrf_esb.h
- 先了解RADIO的各种工作状态及流程
- 1状态
RADIO的工作状态:
DISABLED RADIO无操作且功耗最低
RXRU RADIO加速进入准备接收 reception ramping up
RXIDLE RADIO已经准备好开始接收了
RX RADIO正在接收
TXRU RADIO加速进入准备发送
TXIDLE RADIO准备好发送了
TX RADIO正在发送一个数据包
RXDISABLE RADIO禁止接收
TXDISABLE RADIO禁止发送
- 2.发送序列
当触发TXEN任务时,启动TXRU作发送准备。 发送准备完成后 它将生成READY事件,
指示可以启动分组传输。 通过触发START任务启动分组传输。 START任务可以在RADIO 进入TXIDLE状态后首先触发。
非快捷方式:TXEN->TXRU->READY事件->DELAY->START任务->ADDRESS事件->PAYLOAD事件->END事件->DELAY->DISABLE任务->DISBALED事件, 如下图:
快捷方式:TXEN->TXRU->READY事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->DISABLE任务->DISBALED事件, 快捷方式可以避免延迟,如下图:
多包发送: RADIO能够一个接一个地发送多个数据包,而不必在数据包之间禁用和重新启用RADIO。
TXEN->TXRU->READY事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->DISABLE任务->DISBALED事件, 如下图:
- 3.接收序列:
非快捷方式:RXEN->RXRU->READY事件->DELAY->START任务->ADDRESS事件->PAYLOAD事件->END事件->DELAY->DISABLE任务->DISBALED事件, 如下图:
快捷方式:RXEN->RXRU->READY事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->DISABLE任务->DISBALED事件, 快捷方式可以避免延迟,如下图:
连续接收:RXEN->RXRU->READY事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->DELAY->START任务->ADDRESS事件->PAYLOAD事件->END事件->DISABLE任务->DISBALED事件,
- 4. 信号强度:利用RSSSTART任务启动接收信号强度的采样。 样本可以从RSSISAMPLE寄存器读取。 RSSI的示例周期由RSSIPERIOD定义,详见设备产品规范。 在此样本期间,RSSI样本将保持平均接收信号强度。为了使RSSI示例有效,必须在接收模式(RXEN任务)中启用无线电,并且必须启动接收(READY事件,然后是START任务)。
- 5.位计数器(在发送模式下接收应答时用到)
RADIO实现了一个简单的计数器,可以配置为在发送或接收特定数目的比特后生成事件, 通过使用快捷方式,这个计数器可以从RADIO生成的不同事件开始,因此相对于这些事件进行计数,位计数器通过触发BCS TART任务启动,通过触发BCSTOP任务停止。 当位计数器计算了指定i的位数时,将生成BCMATCH事件 在BCC寄存器中。 位计数器将继续计数位,直到产生DISABLED事件或直到触发BCSTOP任务。 因此,CPU可以在BCMATCH事件之后重新配置t 在同一数据包中,新的BCMATCH事件的BCC值。 位计数器只能在RADIO收到ADDRESS事件后启动。 位计数器将在BCSTOP、停止、END和禁用任务上停止和重置。
-
nrf_esb.c代码解析
- 1、ESB初始化
uint32_t nrf_esb_init(nrf_esb_config_t const * p_config)
{
uint32_t err_code;
VERIFY_PARAM_NOT_NULL(p_config);
if (m_esb_initialized)//如果已经初始化ESB,则禁止ESB
{
err_code = nrf_esb_disable();
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
//注册中断处理函数
m_event_handler = p_config->event_handler;
//将设置参数保存到本地m_config_local
memcpy(&m_config_local, p_config, sizeof(nrf_esb_config_t));
m_interrupt_flags = 0;//清全局中断标志
memset(m_rx_pipe_info, 0, sizeof(m_rx_pipe_info));//复位8个地址管道结构信息
//memset(m_pids, 0, sizeof(m_pids));//此处需要注释掉 不能复位8个管道的PID计数值,否则对方将收到本地重发数据。nrf_esb_disable函数中也需要注释掉此句。
//2.4G相关参数设置
VERIFY_TRUE(update_radio_parameters(), NRF_ERROR_INVALID_PARAM);
// 默认值,后续要重新配置地址
NRF_RADIO->BASE0 = 0xE7E7E7E7;
NRF_RADIO->BASE1 = 0x43434343;
NRF_RADIO->PREFIX0 = 0x23C343E7;
NRF_RADIO->PREFIX1 = 0x13E363A3;
initialize_fifos();//初始化发送接收缓存区
//16位定时器2,默认分频值4, 开启COMPA RE[1]事件和CLEAR任务之间的捷径, COMPA RE[1]事件和停止任务之间的捷径
sys_timer_init();
//将ESB的READY事件连接到定时器2的启动任务
//将ESB的ADDRESS事件连接到定时器2的停止任务
//将定时器2的通道0匹配事件连接到RADIO的终止任务DISABLE
//将定时器2的通道1匹配事件连接到RADIO的发送使能任务TXEN
ppi_init();
//设置RADIO中断优先级
NVIC_SetPriority(RADIO_IRQn,m_config_local.radio_irq_priority& ESB_IRQ_PRIORITY_MSK);
//设置ESB事件中断优先级
NVIC_SetPriority(ESB_EVT_IRQ,m_config_local.event_irq_priority&ESB_IRQ_PRIORITY_MSK);
//先使能ESB事件中断
NVIC_EnableIRQ(ESB_EVT_IRQ);
//NRF52才有ADDRESS事件后的位计数
#ifdef NRF52
if(m_address_hang_fix_enable)
{
// Setup a timeout timer to start on an ADDRESS match, and stop on a BCMATCH event.
// If the BCMATCH event never occurs the CC[0] event will fire, and the timer interrupt will disable the radio to recover.
/*m_radio_shorts_common初始值:RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk|RADIO_SHORTS_ADDRESS_RSSISTART_Msk|RADIO_SHORTS_DISABLED_RSSISTOP_Msk */
m_radio_shorts_common |= RADIO_SHORTS_ADDRESS_BCSTART_Msk;//ADDRESS事件后启动位计数
NRF_RADIO->BCC = 2;//2位计数
NRF_ESB_BUGFIX_TIMER->BITMODE = TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos;//定时器3为32位宽度
NRF_ESB_BUGFIX_TIMER->PRESCALER = 4;//分频值4
NRF_ESB_BUGFIX_TIMER->CC[0] = 5;//定时器3通道0比较值为5
//通道0匹配后马止STOP,CLEAR
NRF_ESB_BUGFIX_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_STOP_Msk | TIMER_SHORTS_COMPARE0_CLEAR_Msk;
//定时器模式
NRF_ESB_BUGFIX_TIMER->MODE = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
//使能定时器3通道0比较中断
NRF_ESB_BUGFIX_TIMER->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
NRF_ESB_BUGFIX_TIMER->TASKS_CLEAR = 1;
NVIC_SetPriority(NRF_ESB_BUGFIX_TIMER_IRQn, 5);//设置定时器3中断优先级
NVIC_EnableIRQ(NRF_ESB_BUGFIX_TIMER_IRQn);//使能定时器3中断
//将ADDRESS事件连接到定时器3的启动任务 NRF_PPI->CH[NRF_ESB_PPI_BUGFIX1].EEP=(uint32_t)&NRF_RADIO->EVENTS_ADDRESS; NRF_PPI->CH[NRF_ESB_PPI_BUGFIX1].TEP=(uint32_t)&NRF_ESB_BUGFIX_TIMER->TASKS_START;
// 将位计数匹配事件连接到定时器3停止任务 NRF_PPI->CH[NRF_ESB_PPI_BUGFIX2].EEP=(uint32_t)&NRF_RADIO->EVENTS_BCMATCH; NRF_PPI->CH[NRF_ESB_PPI_BUGFIX2].TEP=(uint32_t)&NRF_ESB_BUGFIX_TIMER->TASKS_STOP;
// 将位计数匹配事件连接到定时器3清除计数任务 NRF_PPI->CH[NRF_ESB_PPI_BUGFIX3].EEP=(uint32_t)&NRF_RADIO->EVENTS_BCMATCH; NRF_PPI->CH[NRF_ESB_PPI_BUGFIX3].TEP=(uint32_t)&NRF_ESB_BUGFIX_TIMER->TASKS_CLEAR;
//使能PPI通道
NRF_PPI->CHENSET = (1 << NRF_ESB_PPI_BUGFIX1) | (1 << NRF_ESB_PPI_BUGFIX2) | (1 << NRF_ESB_PPI_BUGFIX3);
}
#endif
m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE;//进入空闲
m_esb_initialized = true;//初始化完成标志
return NRF_SUCCESS;
}
//RADIO中断处理
void RADIO_IRQHandler()
{
//READY if(NRF_RADIO->EVENTS_READY&&(NRF_RADIO->INTENSET&RADIO_INTENSET_READY_Msk))
{
NRF_RADIO->EVENTS_READY = 0;
DEBUG_PIN_SET(DEBUGPIN1);
}
//END if(NRF_RADIO->EVENTS_END&&(NRF_RADIO->INTENSET&RADIO_INTENSET_END_Msk))
{
NRF_RADIO->EVENTS_END = 0;
DEBUG_PIN_SET(DEBUGPIN2);
// Call the correct on_radio_end function, depending on the current protocol state
if (on_radio_end)
{
on_radio_end();
}
}
//DISABLED if(NRF_RADIO->EVENTS_DISABLED&&(NRF_RADIO->INTENSET&RADIO_INTENSET_DISABLED_Msk))
{
NRF_RADIO->EVENTS_DISABLED = 0;
DEBUG_PIN_SET(DEBUGPIN3);
// Call the correct on_radio_disable function, depending on the current protocol state
if (on_radio_disabled)
{
on_radio_disabled();
}
}
DEBUG_PIN_CLR(DEBUGPIN1);
DEBUG_PIN_CLR(DEBUGPIN2);
DEBUG_PIN_CLR(DEBUGPIN3);
DEBUG_PIN_CLR(DEBUGPIN4);
}
- 2.接收相关 nrf_esb_start_rx -> on_radio_disabled_rx -> on_radio_disabled_rx_ack
RXEN=1,READY事件后快速START任务,定时器2启动,等待ADDRESS事件,ADDRESS事件到达后,定时器2停止,启动位计数(BCC=2),启动定时器3,开始信号强度监测,位计数满后产生BCMATCH事件,停止定时器3,并清除计数器。 当END事件发生时,执行DISABLE任务,当DISABLED事件发生时,停止信号强度监测,调用on_radio_disabled_rx函数,获取接收数据,准备应答,使能发送。当收到DISBALED事件时,调用on_radio_disabled_rx_ack函数,重新准备开始接收数据。
开始接收
uint32_t nrf_esb_start_rx(void)
{
//不在空闲状态不能开始接收,调用此函数需要判断返回值
VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY);
NRF_RADIO->INTENCLR = 0xFFFFFFFF;//清中断标志
NRF_RADIO->EVENTS_DISABLED = 0;//清DISABLED事件标志,即当前流程结束事件
on_radio_disabled = on_radio_disabled_rx;//开始接收后,进入拉收模式,此函数将在上面事件触发后执行
//禁止发送
NRF_RADIO->SHORTS=m_radio_shorts_common|RADIO_SHORTS_DISABLED_TXEN_Msk;
//使能流程结束中断DISABLED
NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;//使能 DISABLE中断
//更新ESB工作状态
m_nrf_esb_mainstate = NRF_ESB_STATE_PRX;//由空闲进入PRX状态
NRF_RADIO->RXADDRESSES = m_esb_addr.rx_pipes_enabled;//接收通道0-8选择使能
NRF_RADIO->FREQUENCY = m_esb_addr.rf_channel;//设置频点
NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer;//设置接收数据包指针
NVIC_ClearPendingIRQ(RADIO_IRQn);//清RADIO中断
NVIC_EnableIRQ(RADIO_IRQn);//使能RADIO中断
NRF_RADIO->EVENTS_ADDRESS = 0;//发送接收地址事件
NRF_RADIO->EVENTS_PAYLOAD = 0;//数据事件
NRF_RADIO->EVENTS_DISABLED = 0;//清DISABLED事件标志,即当前流程结束事件
NRF_RADIO->TASKS_RXEN = 1;//使能RADIO进入RX模式,第一步RXEN
}
//接收到数据的处理
void on_radio_disabled_rx(void)
{
//如果收到的数据CRC校验错误,则从新开始接收
if (NRF_RADIO->CRCSTATUS == 0)
{
clear_events_restart_rx();
return;
}
//如果接收缓存区已满,则丢弃当前,从新开始接收
if (m_rx_fifo.count >= NRF_ESB_RX_FIFO_SIZE)
{
clear_events_restart_rx();
return;
}
//获取当前RX管道pipe info PID and CRC and acknowledgment payload
p_pipe_info = &m_rx_pipe_info[NRF_RADIO->RXMATCH];//当前接收管道
//如果RADIO中的数据校验值及PID值都与上次接收到的一样
if (NRF_RADIO->RXCRC == p_pipe_info->crc &&
(m_rx_payload_buffer[1] >> 1) == p_pipe_info->pid
)
{
retransmit_payload = true;//同一包数据重复发送标志
send_rx_event = false; //因上次已处理接收数据,此时不需要发送接收事件
}
//PID的值为接收buf[1]的高7位,最低位为当前数据是否应答数据标志
p_pipe_info->pid = m_rx_payload_buffer[1] >> 1;//保存PID
p_pipe_info->crc = NRF_RADIO->RXCRC;//保存CRC
//此处要 注意了,有些怪异
//m_config_local.selective_auto_ack == false这个字面上理解就是没有开启自动应答,但实际刚好相反
//m_rx_payload_buffer[1] & 0x01) == 1 应答标志为1
//此处意思是通信双方都可以决定应答使能
if ((m_config_local.selective_auto_ack == false) || ((m_rx_payload_buffer[1] & 0x01) == 1))
{
ack = true;//应答
}
if (ack)//应答
{
NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk;//DISBALED事件后使能接收
switch (m_config_local.protocol)//根据协议选择
{
case NRF_ESB_PROTOCOL_ESB://固定数据长度32
{
update_rf_payload_format(0);//应答数据长度为0,即不带数据应答
m_tx_payload_buffer[0] = m_rx_payload_buffer[0];//接收到的数据长度
m_tx_payload_buffer[1] = 0;//最低位为0,此为应答数据,对方接收后不要再应答了。
//buf[2]开始才为数据
}
break;
}
m_nrf_esb_mainstate = NRF_ESB_STATE_PRX_SEND_ACK;//后面将进入接收模式中的发送应答步骤
NRF_RADIO->TXADDRESS = NRF_RADIO->RXMATCH;//回复当前管道数据
NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer;//加载发送数据指针
on_radio_disabled = on_radio_disabled_rx_ack;//后面将进行应答后处理,调用此函数
}
else//如果不需要应答,则从新开始接收
{
clear_events_restart_rx();
}
if (send_rx_event)//只有当是新的数据包时才会触发接收事件中断
{
// Push the new packet to the RX buffer and trigger a received event if the operation was
// successful. 获取到数据 PID NOACK RSSI
if (rx_fifo_push_rfbuf(NRF_RADIO->RXMATCH, p_pipe_info->pid))//保存pipe pid noack lenth data
{
m_interrupt_flags |= NRF_ESB_INT_RX_DATA_RECEIVED_MSK;//数据有效则触发事件中断
NVIC_SetPendingIRQ(ESB_EVT_IRQ);//触发中断
}
}
}
//应答后的处理
void on_radio_disabled_rx_ack(void)
{
NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_TXEN_Msk;//接收到数据后产生的DISABLED事件,将触发TXEN来应答
update_rf_payload_format(m_config_local.payload_length);//恢复数据长度到RADIO
NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer;//设置数据指针为接收缓存区
on_radio_disabled = on_radio_disabled_rx;//继续接收
m_nrf_esb_mainstate = NRF_ESB_STATE_PRX;//重新进入PRX状态
}
数据接收处理
bool rx_fifo_push_rfbuf(uint8_t pipe, uint8_t pid)
{
if (m_rx_fifo.count < NRF_ESB_RX_FIFO_SIZE)//缓存未满
{
if (m_config_local.protocol == NRF_ESB_PROTOCOL_ESB_DPL)//动态数据长度
{
if (m_rx_payload_buffer[0] > NRF_ESB_MAX_PAYLOAD_LENGTH)//超出长度
{
return false;
}
m_rx_fifo.p_payload[m_rx_fifo.entry_point]->length = m_rx_payload_buffer[0];//保存长度
}
else if (m_config_local.mode == NRF_ESB_MODE_PTX)
{
// Received packet is an acknowledgment
m_rx_fifo.p_payload[m_rx_fifo.entry_point]->length = 0;
}
else
{
m_rx_fifo.p_payload[m_rx_fifo.entry_point]->length = m_config_local.payload_length;//固定长度32
}
//保存接收数据到缓存区
memcpy(m_rx_fifo.p_payload[m_rx_fifo.entry_point]->data, &m_rx_payload_buffer[2],
m_rx_fifo.p_payload[m_rx_fifo.entry_point]->length);
m_rx_fifo.p_payload[m_rx_fifo.entry_point]->pipe = pipe;//保存管道号
m_rx_fifo.p_payload[m_rx_fifo.entry_point]->rssi = NRF_RADIO->RSSISAMPLE;//保存信号强度
m_rx_fifo.p_payload[m_rx_fifo.entry_point]->pid = pid;//保存PID识别码
//m_rx_payload_buffer[1] & 0x01 0: 接收到的数据是应答数据 1:接收到的是正常数据
//noack:是不是应答数据 0:正常数据 1:应答数据
//两个含义刚好是反的
m_rx_fifo.p_payload[m_rx_fifo.entry_point]->noack = !(m_rx_payload_buffer[1] & 0x01);
if (++m_rx_fifo.entry_point >= NRF_ESB_RX_FIFO_SIZE)//缓存区循环接收
{
m_rx_fifo.entry_point = 0;
}
m_rx_fifo.count++;//缓存计数递增
return true;//正常处理需要触发事件中断
}
return false;//缓存区已满,数据丢弃
}
//停止接收
uint32_t nrf_esb_stop_rx(void)
{
if (m_nrf_esb_mainstate == NRF_ESB_STATE_PRX)//只有在接收状态才能停止,应答时是不能停止的。
{
NRF_RADIO->SHORTS = 0;//停止一切
NRF_RADIO->INTENCLR = 0xFFFFFFFF;//清中断标志
on_radio_disabled = NULL;//无下一步执行函数
NRF_RADIO->EVENTS_DISABLED = 0;//清DISABLE事件标志
NRF_RADIO->TASKS_DISABLE = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0);//等待RADIO禁止
m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE;//进入空闲状态
return NRF_SUCCESS;
}
//刷新接收缓存区
uint32_t nrf_esb_flush_rx(void)
{
VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE);//初始化后才能刷新
DISABLE_RF_IRQ();//禁止中断
//清除所有接收相关
m_rx_fifo.count = 0;
m_rx_fifo.entry_point = 0;
m_rx_fifo.exit_point = 0;
memset(m_rx_pipe_info, 0, sizeof(m_rx_pipe_info));
ENABLE_RF_IRQ();
return NRF_SUCCESS;
}
return NRF_ESB_ERROR_NOT_IN_RX_MODE;
}
//清除事件重新开始接收
void clear_events_restart_rx(void)
{
NRF_RADIO->SHORTS = m_radio_shorts_common;//恢复接收
update_rf_payload_format(m_config_local.payload_length);
NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer;
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0);
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_TXEN_Msk;
NRF_RADIO->TASKS_RXEN = 1;//RADIO开始接收
}
//读取接收数据
uint32_t nrf_esb_read_rx_payload(nrf_esb_payload_t * p_payload)
{
VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE);
VERIFY_PARAM_NOT_NULL(p_payload);
if (m_rx_fifo.count == 0)
{
return NRF_ERROR_NOT_FOUND;
}
DISABLE_RF_IRQ();
p_payload->length = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->length;
p_payload->pipe = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->pipe;
p_payload->rssi = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->rssi;
p_payload->pid = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->pid;
p_payload->noack = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->noack;
memcpy(p_payload->data, m_rx_fifo.p_payload[m_rx_fifo.exit_point]->data, p_payload->length);
if (++m_rx_fifo.exit_point >= NRF_ESB_RX_FIFO_SIZE)
{
m_rx_fifo.exit_point = 0;
}
m_rx_fifo.count--;
ENABLE_RF_IRQ();
return NRF_SUCCESS;
}
- 3.发送相关函数nrf_esb_write_payload -> start_tx_transaction -> on_radio_disabled_tx -> on_radio_disabled_tx_wait_for_ack
//写数据到发送缓存区, 发送前得保证是在空闲状态
uint32_t nrf_esb_write_payload(nrf_esb_payload_t const * p_payload)
{
VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE);//未初始化不处理
VERIFY_PARAM_NOT_NULL(p_payload);//空指针不处理
VERIFY_PAYLOAD_LENGTH(p_payload);//数据长度不对不处理
VERIFY_FALSE(m_tx_fifo.count >= NRF_ESB_TX_FIFO_SIZE, NRF_ERROR_NO_MEM);//发送缓存区满不处理
VERIFY_TRUE(p_payload->pipe < NRF_ESB_PIPE_COUNT, NRF_ERROR_INVALID_PARAM);//管道号超出范围不处理
DISABLE_RF_IRQ();//禁止中断
//加载发送数据data及 pipe pid noack
memcpy(m_tx_fifo.p_payload[m_tx_fifo.entry_point], p_payload, sizeof(nrf_esb_payload_t));
//当前管道PID识别码递增
m_pids[p_payload->pipe] = (m_pids[p_payload->pipe] + 1) % (NRF_ESB_PID_MAX + 1);
//更新当前发送PID,实际PID与p_payload无关
m_tx_fifo.p_payload[m_tx_fifo.entry_point]->pid = m_pids[p_payload->pipe];
//发送缓存区循环
if (++m_tx_fifo.entry_point >= NRF_ESB_TX_FIFO_SIZE)
{
m_tx_fifo.entry_point = 0;
}
m_tx_fifo.count++;//已使用发送缓存区数量递增
ENABLE_RF_IRQ();//开启中断
//如果在发送模式且是自动发送模式且ESB在空闲状态,启动发送
if (m_config_local.mode == NRF_ESB_MODE_PTX &&
m_config_local.tx_mode == NRF_ESB_TXMODE_AUTO &&
m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE)
{
start_tx_transaction();//开始发送
}
return NRF_SUCCESS;
}
开始发送
static void start_tx_transaction()
{
bool ack;
m_last_tx_attempts = 1;
// Prepare the payload
mp_current_payload = m_tx_fifo.p_payload[m_tx_fifo.exit_point];//当前待发送的数据缓存区
switch (m_config_local.protocol)
{
case NRF_ESB_PROTOCOL_ESB:
update_rf_payload_format(mp_current_payload->length);//设置发送数据格式及长度
m_tx_payload_buffer[0] = mp_current_payload->pid;//发送数据第一个为PID
m_tx_payload_buffer[1] = 0; //第二个固定为0,发送的不是应答数据
memcpy(&m_tx_payload_buffer[2], mp_current_payload->data, mp_current_payload->length);//复制发送数据
NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk;//DISABLED事件后使能接收
NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk;//使能终止及就绪事件中断
// Configure the retransmit counter
m_retransmits_remaining = m_config_local.retransmit_count;//设置最大数据重发次数
on_radio_disabled = on_radio_disabled_tx;//下一步
m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX_ACK;//需要等待应答的数据发送
break;
case NRF_ESB_PROTOCOL_ESB_DPL:
ack = !mp_current_payload->noack || !m_config_local.selective_auto_ack;
m_tx_payload_buffer[0] = mp_current_payload->length;
m_tx_payload_buffer[1] = mp_current_payload->pid << 1;
m_tx_payload_buffer[1] |= mp_current_payload->noack ? 0x00 : 0x01;
memcpy(&m_tx_payload_buffer[2], mp_current_payload->data, mp_current_payload->length);
// Handling ack if noack is set to false or if selective auto ack is turned off
if (ack)
{
NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk;
NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk;
// Configure the retransmit counter
m_retransmits_remaining = m_config_local.retransmit_count;
on_radio_disabled = on_radio_disabled_tx;
m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX_ACK;
}
else
{
NRF_RADIO->SHORTS = m_radio_shorts_common;
NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
on_radio_disabled = on_radio_disabled_tx_noack;
m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX;
}
break;
default:
// Should not be reached
break;
}
NRF_RADIO->TXADDRESS = mp_current_payload->pipe;//更新当前发送管道号到RADIO
NRF_RADIO->RXADDRESSES = 1 << mp_current_payload->pipe;//使能当前接收管道,用来接收应答
NRF_RADIO->FREQUENCY = m_esb_addr.rf_channel;//通信频点
NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer;//加载发送缓存
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
NRF_RADIO->EVENTS_ADDRESS = 0;
NRF_RADIO->EVENTS_PAYLOAD = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
DEBUG_PIN_SET(DEBUGPIN4);
NRF_RADIO->TASKS_TXEN = 1;//开始发送
}
void NRF_ESB_BUGFIX_TIMER_IRQHandler(void)
{
if(NRF_ESB_BUGFIX_TIMER->EVENTS_COMPARE[0])
{
NRF_ESB_BUGFIX_TIMER->EVENTS_COMPARE[0] = 0;
// If the timeout timer fires and we are in the PTX receive ACK state, disable the radio
if(m_nrf_esb_mainstate == NRF_ESB_STATE_PTX_RX_ACK)
{
NRF_RADIO->TASKS_DISABLE = 1; //速率检测超时
}
}
}
- 4.发送过程: TXEN=1,READY事件后启动START任务,定时器2启动,等待ADDRESS事件,收到ADDRESS信号,定时器2停止,启动位计数(BCC=2),启动定时器3,开始信号强度监测,位计数满后产生BCMATCH事件,停止定时器3,并清除计数器。当END事件发生时,执行DISABLE任务,当DISABLED事件发生时,停止信号强度监测,调用on_radio_disabled_tx函数,设置定时器2通道0比较寄存器值为等待应答超时时间176微秒(1M),设置定时器2通道1比较寄存器值为重发延时(1M, 1200-130微秒),清计数器及中断标志,使能PPI通道(等待应答超时,RADIO 启动DISABLE任务;READY事件启动定时器; ADDRESS事件,停止定时器),比较通道1与TXEN联动,暂时关闭。开启接收,等待应答。超时或接收到应答将会启动DISABLE任务,执行on_radio_disabled_tx_wait_for_ack函数,关闭相关PPI通道,如果收到正确应答,停止定时器2,比较通道1与TXEN联动关闭。如果还有待发数据,则继续发送。如果未收到应答,且未到最大重发次数,启动重发,开启定时器2,比较通道1与TXEN联动启动,实现重发的延时控制TXEN,
- PPI通道
- 1、PPI通道[NRF_ESB_PPI_TIMER_START]:
事件:RADIO->READY 任务:定时器2->START
用途:等待应答用 RXEN后的READY,等待应答的超时控制 1M速率为64微秒,比较通道0中断后,即为等待超时,触发RADIO->DISABLE任务。
- 2.PPI通道[NRF_ESB_PPI_TIMER_STOP]:
事件:RADIO->ADDRESS 任务:定时器2->STOP
用途:等待应答用 当收到RADIO->ADDRESS信号,停止定时器2
- 3.PPI通道[NRF_ESB_PPI_RX_TIMEOUT]:
事件:定时器2->通道0比较中断 任务:RADIO->DISABLE
用途:等待应答用 等待应答超时,触发RADIO->DISABLE任务,数据重发。
- 4.PPI通道[NRF_ESB_PPI_TX_START]:
事件:定时器2->通道1比较中断 任务: RADIO->TXEN
用途:数据重发的延时控制
- 5.PPI通道[NRF_ESB_PPI_BUGFIX1]:
事件:RADIO->ADDRESS 任务:定时器3->START
用途:接收应答速率控制 发送后等待应答时,当收到ADDRESS信号,则启动定时器3进行接收2位数据的超时判断(1M速率不应超过5微秒),超时则触发DISABLE任务。进行重发。
- 6.PPI通道[NRF_ESB_PPI_BUGFIX2]:
事件:RADIO->BCMATCH 任务:定时器3->STOP
用途:接收应答速率控制 位计数事件到达时,停止定时器3的超时检测。
- 7.PPI通道[NRF_ESB_PPI_BUGFIX3]:
事件:RADIO->BCMATCH 任务:定时器3->CLEAR
用途:接收应答速率控制 位计数事件到达时,清定时器3计数器。
- 时序控制
- 1.等待应答超时:
NRF24L01的时序如上图,收到数据后,发送应答时间为128us+33us+5us=166us
#define RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS (176) //此处值不能设置得太小
- 模式之间的切换
- 1.在esb.c中添加两个函数
uint8_t Get_m_nrf_esb_mainstate(void)
{
return (uint8_t)m_nrf_esb_mainstate;
}
void Set_m_nrf_esb_mainstate(uint8_t state)
{
m_nrf_esb_mainstate = (nrf_esb_mainstate_t)state;
}
- 2.将m_nrf_esb_mainstate声明为volatile.
- 3.static nrf_esb_payload_t tx_payload = NRF_ESB_CREATE_PAYLOAD(0, 0x1F, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,0x1C, 0x1D, 0X1E, 0X1F);
static nrf_esb_payload_t rx_payload;
- 4为了能与NRF24L01通信,数据格式为定长32,而不是网上所说的变长格式。
nrf_esb_config_t nrf_esb_tx_config={
.protocol = NRF_ESB_PROTOCOL_ESB,//NRF_ESB_PROTOCOL_ESB_DPL;
.mode = NRF_ESB_MODE_PTX,
.event_handler = nrf_esb_event_handler,
.bitrate = NRF_ESB_BITRATE_1MBPS,
.crc = NRF_ESB_CRC_16BIT,
.tx_output_power = NRF_ESB_TX_POWER_0DBM,
.retransmit_delay = 1200,//重发延时不能太小
.retransmit_count = 15,//重发次数不能太少
.tx_mode = NRF_ESB_TXMODE_AUTO,
.radio_irq_priority = 1,
.event_irq_priority = 2,
.payload_length = 32,
.selective_auto_ack = false//false为发送后需要等待应答,在NRF_ESB_PROTOCOL_ESB模式,不管如何设置,都会等待应答
};
nrf_esb_config_t nrf_esb_rx_config={
.protocol = NRF_ESB_PROTOCOL_ESB,//NRF_ESB_PROTOCOL_ESB_DPL;
.mode = NRF_ESB_MODE_PRX,
.event_handler = nrf_esb_event_handler,
.bitrate = NRF_ESB_BITRATE_1MBPS,
.crc = NRF_ESB_CRC_16BIT,
.tx_output_power = NRF_ESB_TX_POWER_0DBM,
.retransmit_delay = 1200,
.retransmit_count = 15,
.tx_mode = NRF_ESB_TXMODE_AUTO,
.radio_irq_priority = 1,
.event_irq_priority = 2,
.payload_length = 32,
.selective_auto_ack = false//false为收到数据后需要应答
};
- 5、发送接收地址与NRF24L01的匹配
//发送地址 0X11, 0X22, 0X33, 0X44, 0X55
uint8_t tx_base_addr_0[4] = {0x22, 0x33, 0x44, 0x55};//对应NRF24L01的通道0的后4个地址
uint8_t tx_base_addr_1[4] = {0x22, 0x33, 0x44, 0x55};//对应NRF24L01的通道1-5的后4个地址
uint8_t tx_addr_prefix[8] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 };//分别对应NRF24L01的通道0-6的第一个地址
//接收地址 0X88, 0X22, 0X33, 0X44, 0X55
uint8_t rx_base_addr_0[4] = {0x22, 0x33, 0x44, 0x55};
uint8_t rx_base_addr_1[4] = {0x22, 0x33, 0x44, 0x55};
uint8_t rx_addr_prefix[8] = {0x88, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87 };
- 6、中断函数
void nrf_esb_event_handler(nrf_esb_evt_t const * p_event)
{
uint8_t Rxbuff_count;
switch (p_event->evt_id)
{
case NRF_ESB_EVENT_TX_SUCCESS://发送成功
NRF_info_str.Flag_Nrf_Tx_Over_Irq = TRUE;
NRF_info_str.Nrf_State = TX_OK;
break;
case NRF_ESB_EVENT_TX_FAILED://达到最大重发次数后失败中断
nrf_esb_flush_tx();
NRF_info_str.Flag_Nrf_Tx_Over_Irq = TRUE;
NRF_info_str.Nrf_State = MAX_TX;
break;
case NRF_ESB_EVENT_RX_RECEIVED://接收到数据中断
if(nrf_esb_read_rx_payload(&rx_payload) == NRF_SUCCESS)
{
if (rx_payload.length == 32)
{
Rxbuff_count = rx_payload.data[0];
if(Rxbuff_count && (Rxbuff_count<32) && (IsCardRight(rx_payload.data + 4) == TRUE)) // 是否为发给本卡号数据 协议数据最多31字节
{
for(int i=0; i<8; i++)
{
if(NRF_info_str.Nrf_RecData_Cache_Buf[i][0] != 0xAA) // 找到空闲存储空间 接收到的协议数据缓存8*33
{
NRF_info_str.Nrf_RecData_Cache_Buf[i][0] = 0xAA; // 标志存储空间已存有效数据
memcpy(&NRF_info_str.Nrf_RecData_Cache_Buf[i][1], rx_payload.data+1, Rxbuff_count);//跳过第一字节,协议数据最多31字节
NRF_info_str.Flag_Nrf_Rec_Data = TRUE; // 接收到协议数据标志位
freq_point_str.Nrf_Rssi = rx_payload.rssi;
break;
}
}
}
}
}
break;
default:
break;
}
}
- 7.高速时钟相关
void hf_clocks_start( void )
{
if(system_info_str.Flag_Hf_Clock_En == false)
{
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
system_info_str.Flag_Hf_Clock_En = true;
}
}
void hf_clocks_stop(void)
{
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
system_info_str.Flag_Hf_Clock_En = false;
}
- 8.定时器的停止
void systimer_stop(void)
{
NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_BUGFIX1) |
(1 << NRF_ESB_PPI_BUGFIX2) |
(1 << NRF_ESB_PPI_BUGFIX3);
NRF_ESB_BUGFIX_TIMER->TASKS_STOP = 1;
NRF_ESB_SYS_TIMER->TASKS_STOP = 1;
NRF_ESB_BUGFIX_TIMER->TASKS_SHUTDOWN = 1;
NRF_ESB_SYS_TIMER->TASKS_SHUTDOWN = 1;
NVIC_DisableIRQ(NRF_ESB_BUGFIX_TIMER_IRQn);
}
- 9.进入待机省电模式
void NRF24L01_CE_LOW(void)
{
uint32_t timeout=0x8ffff;
while((Get_m_nrf_esb_mainstate() == NRF_ESB_STATE_PRX_SEND_ACK) && (timeout--));
Set_m_nrf_esb_mainstate(NRF_ESB_STATE_PRX);
nrf_esb_stop_rx();
nrf_esb_suspend();
hf_clocks_stop();
}
- 10.接收模式
uint32_t esb_init_rx(uint8_t freqPoint)
{
uint32_t err_code;
hf_clocks_start();
err_code = nrf_esb_init(&nrf_esb_rx_config);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_0(rx_base_addr_0);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_1(rx_base_addr_1);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_prefixes(rx_addr_prefix, 8);
VERIFY_SUCCESS(err_code);
nrf_esb_set_rf_channel(freqPoint%(NRF_MAX_FREQ+1));
nrf_esb_set_tx_power(NRF_ESB_TX_POWER_3DBM);
nrf_esb_start_rx();
PRINTF("freq=%d\r\n", freqPoint);
return err_code;
}
- 11.发送模式
uint32_t esb_init_tx(uint8_t freqPoint)
{
uint32_t err_code;
hf_clocks_start();//此处有死循环
err_code = nrf_esb_init(&nrf_esb_tx_config);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_0(tx_base_addr_0);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_base_address_1(tx_base_addr_1);
VERIFY_SUCCESS(err_code);
err_code = nrf_esb_set_prefixes(tx_addr_prefix, 8);
VERIFY_SUCCESS(err_code);
nrf_esb_set_rf_channel(freqPoint%(NRF_MAX_FREQ+1));
nrf_esb_set_tx_power(NRF_ESB_TX_POWER_3DBM);
tx_payload.noack = false;
return err_code;
}
- 12.进入接收模式
void NRF24L01_Init_RX(uint8_t freqPoint)
{
uint32_t timeout=0xfffff;
while((nrf_esb_is_idle() == FALSE) && (timeout))
timeout--;
esb_init_rx(freqPoint);
}
- 13.从接收模式进入发送模式
void NRF24L01_Init_RX_To_Tx(uint8_t freqPoint)
{
//先结束接收模式
uint32_t timeout=0xfffff;
uint8_t state;
state = Get_m_nrf_esb_mainstate();
if((state == NRF_ESB_STATE_PRX) || (state == NRF_ESB_STATE_PRX_SEND_ACK))
{
while((Get_m_nrf_esb_mainstate() == NRF_ESB_STATE_PRX_SEND_ACK) && (timeout--));//如果正在发送应答则等待
Set_m_nrf_esb_mainstate(NRF_ESB_STATE_PRX);
nrf_esb_flush_rx();
nrf_esb_stop_rx();
}
//再进入发送模式
esb_init_tx(freqPoint);
nrf_esb_flush_tx();
Delay_ms(1);
}
- 14.加载数据发送
void NRF24L01_TxPacket(uint8_t *txbuf)
{
volatile uint32_t timeout=0XFFFFF;
bool Flag_timeout = FALSE; //超时标志
//Delay_ms(1);//使用自带ESB,此处须加延时
//加载发送数据
memcpy(tx_payload.data, txbuf, 32);
tx_payload.noack = false;
tx_payload.length = 32;
//tx_payload.pid++;//此pid设置是无效的。
//启动发送
NRF_info_str.Flag_Nrf_Tx_Over_Irq = FALSE;
nrf_esb_write_payload(&tx_payload);
//PRINTF("send over\r\n");
while((NRF_info_str.Flag_Nrf_Tx_Over_Irq == FALSE) && timeout) //等待发送完成 此标志在NRF发送完成中断置位 加入超时判断,防锁死
{
timeout--;
}
//发送完成,或成功或超时的处理
if(timeout == 0)
Flag_timeout = TRUE;
//...
NRF_info_str.Flag_Nrf_Tx_Over_Irq = FALSE; //接收中断会自动清此标志,发送中断,需要手动清理
Flag_timeout = FALSE;
NRF24L01_Init_RX(freq_point_str.Current_Freq_Point);
- 15.从接收进入发送模式后发送数据
NRF24L01_Init_RX_To_Tx(freq_point_str.Current_Freq_Point);
NRF24L01_TxPacket(NRF_info_str.Nrf_Tx_Buf); //发送协议数据
NRF52832的ESB已经与NRF24L01正常通信,发送与接收切换自如。笔记记得比较乱,凑和着看吧。