广播流程源码分析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;}
#elseif (!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;
#elseaddr = g_random_addr;
#endifadvsm->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);}
#endifreturn 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_VERSIONble_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等事件联系起来。
整个过程大概就是这样。

nimble源码学习——广播流程源码分析1相关推荐

  1. hadoop源码分析_Spark2.x精通:Job触发流程源码深度剖析(一)

    , 一.概述  之前几篇文章对Spark集群的Master.Worker启动流程进行了源码剖析,后面直接从客户端角度出发,讲解了spark-submit任务提交过程及driver的启动:集群启动.任务 ...

  2. 【源码分析】storm拓扑运行全流程源码分析

    [源码分析]storm拓扑运行全流程源码分析 @(STORM)[storm] 源码分析storm拓扑运行全流程源码分析 一拓扑提交流程 一stormpy 1storm jar 2def jar 3ex ...

  3. Myth源码解析系列之六- 订单下单流程源码解析(发起者)

    前面一章我们走完了服务启动的源码,这次我们进入下单流程的源码解析~ 订单下单流程源码解析(发起者) 首先保证myth-demo-springcloud-order.myth-demo-springcl ...

  4. SpringBoot2 | SpringBoot启动流程源码分析(一)

    首页 博客 专栏·视频 下载 论坛 问答 代码 直播 能力认证 高校 会员中心 收藏 动态 消息 创作中心 SpringBoot2 | SpringBoot启动流程源码分析(一) 置顶 张书康 201 ...

  5. 云原生小课堂|Envoy请求流程源码解析(三):请求解析

    ​ 前言 Envoy 是一款面向 Service Mesh 的高性能网络代理服务.它与应用程序并行运行,通过以平台无关的方式提供通用功能来抽象网络.当基础架构中的所有服务流量都通过 Envoy 网格时 ...

  6. YOLOv3反向传播原理 之 全流程源码分析

    YOLOv3反向传播原理 之 全流程源码分析 1.YOLOv3网络训练中反向传播主体流程 1.1 初始化 1.2 batch内梯度累加 1.3 network和layer 中的关键变量 2.YOLO层 ...

  7. Activity启动流程源码分析(基于Android N)

    Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...

  8. springsecurity不拦截某个接口_SpringSecurity 默认表单登录页展示流程源码

    SpringSecurity 默认表单登录页展示流程源码 本篇主要讲解 SpringSecurity提供的默认表单登录页 它是如何展示的的流程,涉及1.FilterSecurityIntercepto ...

  9. android系统加载主题的流程,详解Android布局加载流程源码

    一.首先看布局层次 看这么几张图 我们会发现DecorView里面包裹的内容可能会随着不同的情况而变化,但是在Decor之前的层次关系都是固定的.即Activity包裹PhoneWindow,Phon ...

最新文章

  1. 计算机显示网络地址,u盘装系统win7打开电脑显示正在获取网络地址怎么处理
  2. springboot获取sessionid_Spring Boot 整合Redis, 用起来真简单!
  3. Knative 快捷操作命令 Kn 介绍
  4. Centos6.5部署大众点评CAT
  5. 用Python的Tultle模块创建一个五角星
  6. 模块开发者使用 ES Modules 的正确姿势
  7. Linux 多线程编程使用pthread_creat()函数条件
  8. 刷新所有视图存储过程
  9. Bean的自动装配Autowiring
  10. mysql分库分表 mycat_你们要的MyCat实现MySQL分库分表来了
  11. java即时聊天系统_基于Java技术的即时聊天系统实现(含源文件).doc
  12. 单片机pwm控制基本原理详解
  13. 电脑调分辨率黑屏了怎么办_调显示器分辨率黑屏怎么办
  14. idea设置svn上传或下拉代码
  15. 遥信量采集、显示及信息处理功能 遥测及数据处理功能
  16. python编程猫下载_【编程猫软件下载】编程猫客户端 V1.6.5 电脑版-开心电玩
  17. http 1.php,php利用socket扩展写一个简单的单进程http服务1
  18. 如何用camtasia录制微课视频教程
  19. 总搞不懂区块链各共识机制的优缺点?来听听这位十多年经验技术老兵的吐血分享吧!
  20. 群晖 硬盘休眠检测 也适用于Linux

热门文章

  1. c语言的标识符可分为哪3种字符,c语言标识符有哪三类?
  2. 计算机的存储器有哪2类,存储器有哪两种
  3. Win10卸载OneDrive的方法
  4. 市面上微型计算机的主频,目前市面上最大屏幕的手机,你知道是哪款吗?
  5. java版阿里云发送短信
  6. Java-TCP通信(实现多发多收、群聊功能),BS通信源码
  7. STM32F407ZET6+NRF24L01实现一收多发(一发多收)
  8. 最方便的ICON、PNG转换工具
  9. 微信电脑端双击不能放大图片怎么回事_IOS 微信 聊天双击放大图片操作
  10. c语言怎么编写数控g指令,数控车床编程--G 代码 M代码命令