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禁止发送

NRF52832之ESB功能与NRF24L01进行2.4G通信

 

  • 2.发送序列

 当触发TXEN任务时,启动TXRU作发送准备。 发送准备完成后 它将生成READY事件,

指示可以启动分组传输。 通过触发START任务启动分组传输。 START任务可以在RADIO 进入TXIDLE状态后首先触发。

非快捷方式:TXEN->TXRU->READY事件->DELAY->START任务->ADDRESS事件->PAYLOAD事件->END事件->DELAY->DISABLE任务->DISBALED事件, 如下图:

NRF52832之ESB功能与NRF24L01进行2.4G通信

 

快捷方式:TXEN->TXRU->READY事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->DISABLE任务->DISBALED事件, 快捷方式可以避免延迟,如下图:

NRF52832之ESB功能与NRF24L01进行2.4G通信

 

多包发送: RADIO能够一个接一个地发送多个数据包,而不必在数据包之间禁用和重新启用RADIO。

TXEN->TXRU->READY事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->DISABLE任务->DISBALED事件, 如下图:

NRF52832之ESB功能与NRF24L01进行2.4G通信

 

  •  3.接收序列:

 非快捷方式:RXEN->RXRU->READY事件->DELAY->START任务->ADDRESS事件->PAYLOAD事件->END事件->DELAY->DISABLE任务->DISBALED事件, 如下图:

NRF52832之ESB功能与NRF24L01进行2.4G通信

 

快捷方式:RXEN->RXRU->READY事件->START任务->ADDRESS事件->PAYLOAD事件->END事件->DISABLE任务->DISBALED事件, 快捷方式可以避免延迟,如下图:

NRF52832之ESB功能与NRF24L01进行2.4G通信

 

连续接收: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.等待应答超时:
  • NRF52832之ESB功能与NRF24L01进行2.4G通信

 

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正常通信,发送与接收切换自如。笔记记得比较乱,凑和着看吧。