nimble源码学习——广播流程源码分析1

广播流程源码分析1

  在controller层有多种状态:广播、空闲、连接等等,这次分析的是广播这个状态或者叫
做角色。
  在前面controller层循环的分析中,可以明确controller层也有event以供这层处理。在
分析的过程中,发现nimble的广播功能主要是由以下部分组成。
	1. RTC0作为controller层的周期事件处理器(可能某个周期没有什么事件)
	2. PPI作为事件发生连接器(一个事件发生触发另一个事件,比如时钟事件触发radio失能)
	3. controller层任务大循环,不断接受处理各种事件。

在这里插入图片描述

static void
ble_phy_isr(void)
{
    uint32_t irq_en;
		//rt_kprintf("%d\r\n",NRF_RADIO->STATE);
    os_trace_isr_enter();

    /* Read irq register to determine which interrupts are enabled */
    irq_en = NRF_RADIO->INTENCLR;

    /*
     * NOTE: order of checking is important! Possible, if things get delayed,
     * we have both an ADDRESS and DISABLED interrupt in rx state. If we get
     * an address, we disable the DISABLED interrupt.
     */

    /* We get this if we have started to receive a frame */
    if ((irq_en & RADIO_INTENCLR_ADDRESS_Msk) && NRF_RADIO->EVENTS_ADDRESS) {
        /*
         * wfr timer is calculated to expire at the exact time we should start
         * receiving a packet (with 1 usec precision) so it is possible  it will
         * fire at the same time as EVENT_ADDRESS. If this happens, radio will
         * be disabled while we are waiting for EVENT_BCCMATCH after 1st byte
         * of payload is received and ble_phy_rx_start_isr() will fail. In this
         * case we should not clear DISABLED irq mask so it will be handled as
         * regular radio disabled event below. In other case radio was disabled
         * on purpose and there's nothing more to handle so we can clear mask.
         */
        if (ble_phy_rx_start_isr()) {
            irq_en &= ~RADIO_INTENCLR_DISABLED_Msk;
        }
    }

    /* Check for disabled event. This only happens for transmits now */
    if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) {
        if (g_ble_phy_data.phy_state == BLE_PHY_STATE_RX) {
            NRF_RADIO->EVENTS_DISABLED = 0;
            ble_ll_wfr_timer_exp(NULL);
        } else if (g_ble_phy_data.phy_state == BLE_PHY_STATE_IDLE) {
            assert(0);
        } else {
            ble_phy_tx_end_isr();
        }
    }

    /* Receive packet end (we dont enable this for transmit) */
    if ((irq_en & RADIO_INTENCLR_END_Msk) && NRF_RADIO->EVENTS_END) {
        ble_phy_rx_end_isr();
    }

    g_ble_phy_data.phy_transition_late = 0;

    /* Ensures IRQ is cleared */
    irq_en = NRF_RADIO->SHORTS;

    /* Count # of interrupts */
    STATS_INC(ble_phy_stats, phy_isrs);

    os_trace_isr_exit();
}

打断点函数,按照执行顺序

1. ble_ll.c            ble_ll_task()的 ble_npl_event_run(ev)处			    1122行左右
2. ble_ll_adv.c        ble_ll_adv_sm_start()               				    1678行左右
3. ble_ll_adv.c        ble_ll_adv_sm_start()的ble_ll_sched_adv_new          1777行左右     		
4. nrf5x_isr.c		  RTC0_IRQHandler                                          28行左右
5. ble_ll_adv.c        ble_ll_adv_tx_start_cb()                                839行左右
注意,1断点须在4运行后再打,因为host与controller层还有其他event和本身一些event.
此时你会发现,在运行一段后,其都是以 1 4 5这个顺序运行。符合上面说的运行过程。至于2和3,
则是在host层发送adv初始化的时候才会跑的。

 (将上面断点都删去)
因为仿真和中断的原因,rtc和radio中断断点会冲突。所以radio中断另分析
6.nrf5x_isr.c       RTC0_IRQHandler							  	17行左右
7.ble_phy.c        ble_phy_isr中ble_ll_wfr_timer_exp(NULL);     1263行左右
8.ble_phy.c        ble_phy_isr中ble_phy_tx_end_isr()       1267行左右。
断点调试会发现,在执行顺序 6 8  6 7,通过代码分析,其就是两次radio中断来实现一次广播中的
发送和接受,第一次是tx发送完,第二次是超时(timer+PPI 嘿嘿)或者接受到请求。

在这里插入图片描述

static int
ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm)
{
    uint8_t adv_chan;
    uint8_t *addr;
    uint8_t *evbuf;

    /* only clear flags that are not set from HCI */
    advsm->flags &= ~BLE_LL_ADV_SM_FLAG_TX_ADD;
    advsm->flags &= ~BLE_LL_ADV_SM_FLAG_RX_ADD;
    advsm->flags &= ~BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD;

    if (advsm->own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        if (!ble_ll_is_valid_random_addr(advsm->adv_random_addr)) {
            return BLE_ERR_INV_HCI_CMD_PARMS;
        }
#else
        if (!ble_ll_is_valid_random_addr(g_random_addr)) {
            return BLE_ERR_CMD_DISALLOWED;
        }
#endif
    }

    /*
     * Get an event with which to send the connection complete event if
     * this is connectable
     */
    if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
        /* We expect this to be NULL but if not we wont allocate one... */
        if (advsm->conn_comp_ev == NULL) {
            evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
            if (!evbuf) {
                return BLE_ERR_MEM_CAPACITY;
            }
            advsm->conn_comp_ev = evbuf;
        }
    }

    /* Set advertising address */
    if ((advsm->own_addr_type & 1) == 0) {
        addr = g_dev_addr;
    } else {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
        addr = advsm->adv_random_addr;
#else
        addr = g_random_addr;
#endif
        advsm->flags |= BLE_LL_ADV_SM_FLAG_TX_ADD;
    }
    memcpy(advsm->adva, addr, BLE_DEV_ADDR_LEN);

    if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
        memcpy(advsm->initiator_addr, advsm->peer_addr, BLE_DEV_ADDR_LEN);
        if (advsm->peer_addr_type & 1) {
            advsm->flags |= BLE_LL_ADV_SM_FLAG_RX_ADD;
        }
    }

#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
    /* This will generate an RPA for both initiator addr and adva */
    if (advsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
        ble_ll_adv_rpa_update(advsm);
    }
#endif

    /* Set flag telling us that advertising is enabled */
    advsm->adv_enabled = 1;

    /* Determine the advertising interval we will use */
    if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
        /* Set it to max. allowed for high duty cycle advertising */
        advsm->adv_itvl_usecs = BLE_LL_ADV_PDU_ITVL_HD_MS_MAX;
    } else {
        advsm->adv_itvl_usecs = (uint32_t)advsm->adv_itvl_max;
        advsm->adv_itvl_usecs *= BLE_LL_ADV_ITVL;
    }

    /* Set first advertising channel */
    adv_chan = ble_ll_adv_first_chan(advsm);
    advsm->adv_chan = adv_chan;

    /*
     * XXX: while this may not be the most efficient, schedule the first
     * advertising event some time in the future (5 msecs). This will give
     * time to start up any clocks or anything and also avoid a bunch of code
     * to check if we are currently doing anything. Just makes this simple.
     *
     * Might also want to align this on a slot in the future.
     *
     * NOTE: adv_event_start_time gets set by the sched_adv_new
     */
    advsm->adv_pdu_start_time = os_cputime_get32() +
                                os_cputime_usecs_to_ticks(5000);

    /*
     * Schedule advertising. We set the initial schedule start and end
     * times to the earliest possible start/end.
     */
    ble_ll_adv_set_sched(advsm);
    ble_ll_sched_adv_new(&advsm->adv_sch, ble_ll_adv_scheduled, NULL);

#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
    if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) {
        ble_ll_adv_aux_schedule(advsm);
    }
#endif

    return BLE_ERR_SUCCESS;
}

在这里插入图片描述
在这里插入图片描述

void RTC0_IRQHandler(void)
{		//rt_kprintf("aaaaa\r\n");
    rtc0_isr_addr();
}

在这里插入图片描述

void
ble_ll_task(void *arg)
{
    struct ble_npl_event *ev;

    /*
     * XXX RIOT ties event queue to a thread which initialized it so we need to
     * create event queue in LL task, not in general init function. This can
     * lead to some races between host and LL so for now let us have it as a
     * hack for RIOT where races can be avoided by proper initialization inside
     * package.
     */
#ifdef RIOT_VERSION
    ble_npl_eventq_init(&g_ble_ll_data.ll_evq);
#endif

    /* Init ble phy */
    ble_phy_init();

    /* Set output power to 1mW (0 dBm) */
    ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM));

    /* Tell the host that we are ready to receive packets */
   // ble_ll_hci_send_noop();

    ble_ll_rand_start();

    while (1) {
			rt_kprintf("controller\r\n");
        ev = ble_npl_eventq_get(&g_ble_ll_data.ll_evq, BLE_NPL_TIME_FOREVER);
        assert(ev);
        ble_npl_event_run(ev);
				//if(ev->arg) rt_kprintf("%x %x\r\n",(*(int*)ev->arg),(*(int*)(ev->arg+4)));
    }
}

在这里插入图片描述

void RADIO_IRQHandler(void)
{
    radio_isr_addr();
}

在这里插入图片描述

广播功能大致流程(广播中等待接收超时的)

如果过程简单化是这样的
(1)host层发起adv的event,
 (2)controller事件循环处理,开启controller时间事件循环(rtc0)
 (2)时间时间循环处理对应事件,开启广播发送,剩下的就交给radio外设
 (3)radio产生发送完成中断,然后设置接受超时操作
 (4)如果超时了,触发radio的disable中断,说明本次广播事件完成,将本事件及处理发送到
 controller事件队列中。执行2一直循环下去。如果未超时情况下则会产生address接受的radio
 中断。

 	
 
这个细节很复杂的。
大致详细说明,看着就很恶心:
1.来自host层开启controller功能,controller层事件队列处理,第一次启动将广播事件交于
RTC调度(至少现在从代码中看是这样),然后在事件中将任务交于controller层事件队列了。
	    ble_ll_adv.c中 1677行左右的ble_ll_adv_sm_start(),其中ble_ll_sched_adv_new(&advsm->adv_sch, ble_ll_adv_scheduled, NULL);
2. controller事件队列处理。
3. 然后RTC时间调度器执行调度中的事件,以完成发送报文等命令
	    打断点调试,其中一个很重要的函数(也是RTC的事件回调)ble_ll_adv_tx_start_cb,
	 其中会调用ble_phy_tx_set_start_time(),来设置在多久后使能txen(也就是发送使能,
	 其利用的就是PPI外设将timer一个事件以触发txen),
4.  然后就是RADIO中断操作了,主要是两个中断:tx完成与等待超时PPI触发的disable中断
		怎么发现这的呢,都是慢慢断点debug,然后查看外设寄存器值和某些参考值。emm...呜呜呜
		因为nrf52832的外设radio设置了Shortcut register,tx发送完成就会触发一个disable
	中断,然后进入中断执行ble_phy_tx_end_isr(),里面有利用PPI将timer0与radio结合的操作。
	当超时的时候,就触发radio中disable中断或radio接收到请求后触发timer0关闭。
5. 假如超时触发radio中断,执行ble_ll_wfr_timer_exp(NULL)来处理,然后就是从2开始执行了
		里面有ble_ll_adv_tx_done(g_ble_ll_cur_adv_sm);这个就是将事件打入到controller事件队列里,
	ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);

总结

函数调用流程图后面再补吧。
大概就是这些啦,简单的来说就是
	1.controller事件队列 ,处理各个事件
	2.RTC0 事件调度,调度事件。比如传输开始
	3. radio中断,用于传输和接受无线报文
	4. PPI将radio、timer、rtc等事件联系起来。
整个过程大概就是这样。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: HP Nimble维护手册是一本详细说明如何维护和管理HP Nimble存储系统的文档。 在维护手册中,将会包括以下内容: 1. 硬件维护指南:介绍如何正确处理和更换存储系统的硬件组件,如磁盘驱动器、电源模块和风扇。这些指南将会包括详细的步骤和注意事项,确保维护人员能够正确、安全地操作存储系统。 2. 软件维护指南:涵盖了HP Nimble存储系统的软件维护方面,包括固件升级、系统设置和配置、网络连接和安全设置等。这些指南将会提供清晰的步骤和注意事项,以保证系统在正常运行的同时具备最新的功能和安全性。 3. 故障排除指南:提供在存储系统出现问题时的故障排除指导。从监测和诊断问题到解决方案的提供,这些指南将会帮助用户迅速恢复正常运行,并找到并解决潜在问题的根本原因。 4. 性能优化指南:通过优化存储系统的配置和设置,从而提高系统的性能和效率。这些指南将会提供性能监测、调整和优化的步骤,以确保存储系统能够最大程度地满足用户的需求。 总之,HP Nimble维护手册是一本详细说明如何维护和管理HP Nimble存储系统的指南。它提供了硬件维护、软件维护、故障排除和性能优化的指导,旨在帮助维护人员正确、安全地操作和管理存储系统,并确保其处于高效、可靠的状态。 ### 回答2: HP Nimble维护手册是一份为HP Nimble系统用户提供的指南,旨在帮助用户了解如何维护和保障系统正常运行。手册包含了系统的基本信息、安装和设置、故障排除、维护和保养等方面的内容。 首先,手册详细介绍了HP Nimble系统的基本信息,包括硬件和软件规格、系统组成部分以及与其他设备的兼容性等。用户可以通过这些信息了解系统的组成和功能,从而更好地进行维护和保养。 其次,手册提供了系统的安装和设置指南,帮助用户正确地安装和配置HP Nimble系统。这包括介绍系统的安装要求、步骤和注意事项,以及如何设置和管理存储卷、数据保护和备份等功能。 此外,手册还提供了故障排除的方法和步骤,以帮助用户解决系统出现的问题。用户可以根据手册提供的故障排查流程逐步查找和修复问题,从而确保系统的稳定性和可靠性。 最后,手册还介绍了系统的维护和保养方法,帮助用户正确地进行系统维护和定期保养。这包括对系统进行软件升级、硬件检查和清洁、性能监控等方面的指导,以确保系统的长期运行和优化性能。 总之,HP Nimble维护手册是一份详细而全面的指南,为用户提供了维护和保障系统安全运行的重要信息和方法。用户可以通过仔细阅读手册,了解并掌握系统的运行原理和维护流程,从而更好地管理和维护自己的HP Nimble系统。 ### 回答3: HP Nimble维护手册是一本提供用户关于HP Nimble存储设备维护的指南。它包含了关于设备的硬件和软件维护的详细信息,帮助用户正确地使用和维护HP Nimble存储设备,确保其性能和可靠性。 手册内容涵盖了多个方面,首先是硬件维护。它提供了有关如何正确连接和安装设备的指导,以及设备的基本硬件组件和连接线路的解释。此外,手册还介绍了如何进行设备的机械维护,例如风扇和电源的更换以及硬盘的安装和替换。 另外,软件维护也是手册的重要部分。它提供了有关如何更新设备固件和软件的说明,以确保设备始终能够运行在最新和最稳定的版本上。手册还包含了有关如何备份和还原设备配置以及数据的操作指南。 此外,手册还介绍了如何诊断和解决一些常见的设备故障和问题。它提供了针对故障排除的详细步骤,以帮助用户快速找到并解决问题。 总的来说,HP Nimble维护手册是一本非常重要的资源,它为用户提供了对HP Nimble存储设备的全面了解和管理。通过正确地使用和维护设备,用户可以最大限度地发挥设备的性能和可靠性,确保数据的安全和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值