赞
踩
我将通过一个BLE蓝牙广播的例子,深入的解释蓝牙数据从controller到rf天线发送到空中这个路径的数据是如何处理,整个过程涉及蓝牙协议5.0,常见嵌入式芯片的架构,数字电路设计,通信原理,信号处理等相关的知识,最需要了解的是蓝牙核心规范5.0,如下图展示的内容,这里推荐学习的博客:参考博文链接 1 2 3 4 5 9章节的所有内容,通过这里的学习将掌握蓝牙广播数据包的格式和蓝牙广播事件的行为,对后续的数据的收发发送才能更加容易的理解。
原图引用:原图链接
本文主要介绍主机接口HCI往下的内容,统称为蓝牙的controller,为了方便理解并没有涉及蓝牙音频相关的。
(1)用打开enable adv进行广播数据为例
(2)在host端开启adv enable即是发送hci指令在协议规范中的描述
在此之前还会设置广播数据和广播参数
在进行广播参数和广播数据设置的时候controller将这些数据通过全局变量存储起来,在收到adv enable的指令时,把这些数据按照协议规范定义好的pdu格式拼接起来。如下图所示Payload部分根据adv的类型不同又存在不同的格式,比如常见的ADV_IND,ADV_NONCONN_IND,等同时由于5.0规范的扩充还存在extend adv的格式更加的复杂,所以在contrller看似按照固定的格式进行简单的拼接其实需要考虑的东西还是很复杂的。
蓝牙的广播是一个周期性的事件,细心的朋友应该在set adv parameter的参数中看到一个interval_min和interval_max,这就是蓝牙广播的周期,controller会在这个最大值和最小值权衡一个合适的interval,也就是每隔这么长的时间就发送一次adv_packt,至于这个是如何实现的我们在后面数字基带部分详细的介绍。在这里我想说的是controller在封包结束后也是会考虑当前的时间,和下一次发送adv的时间。还有需要考虑的adv的channel的映射,对于规范5.0之前的蓝牙广播只使用了37 38 39三个信道称为primary channel,这里我们只考虑5.0之前的channel map,adv 的参数设定channel map是通过设置一个变量的三个位来表示开启的广播通道,比如UINT8 channel_map |= 0x01<<0;表示只使用37通道UINT8 channel_map |= 0x03<<0;表示使用37 38,依次类推,当然在controller这边也只是将这个参数拿到,具体通道的切换是由数字基带实现的。到此controller在adv enable相关的事情就完成了,接下来将封装好的adv pdu,以及时间值的设定,通道映射等写入数字基带的寄存器。
在上述中我不断提到数字基带,那么到底什么是基带呢?如何将数据写入到基带的呢?基带又是如何处理controller写下来的数据呢?比如提到的对adv packt interval的控制。在这里我将为大家详细的介绍。首先需要了解芯片的结构,我们以最常见的stm32芯片为例。图1中芯片由m3的内核和总线以及外设组成,m3就是我们常说的核心处理器,处理我们在软件端编写的程序,当然这个程序是经过预处理->编译->连接->汇编的二进制机器码。外设的话我们常见的uart,spi等相信大家都已经了解过或者使用过,在芯片设计阶段会为每个外设分配一个地址,再将这个外设挂在合适的总线上面,比如这里所有外设基本都挂在apb2上面,在图中看到的总线矩阵AHB、APB1、APB2等都是AMBA协议中的内容,大家感兴趣的可以详细去了解,这里简单介绍,总线的设备分为master和slave,只有master才能够发起操作slave响应,master首先访问总裁器获取对总线的控制,再在总线广播要操作slave的地址,经过地址译码器选中操作的slave,master就可以进行读写slave的操作了,这里的master主要就是cm3核心处理器了,slave就是片上的外设,这里我们又想到操作都是由处理器发起的,万一外设还没等到处理器来读取,但是有数据需要处理该怎么办呢?这个时候就是触发中断,在内核中也叫触发异常来处理特殊情况。到这儿芯片基本的结构相信大家也基本清除了,再说数字基带其实也就是一个片上的外设,挂在AHB总线上的一个从设备。假定基带分配的起始地址为BB_Base = 0x0050000,其中adv_pack寄存器的地址的相对基地址偏移为ADV_Pack_Reg = 0x10;那么我们把数据写进去的操作就是:
(uint_32 *)(BB_Base+ADV_Pack_Reg) = adv_pack;是不是感觉说了半天结果就这么一句简单的赋值操作,把人给看懵了,上述的总线寻址是通过数子电路已经完成了的硬件,在软件端我们其实只要知道地址就可以直接操作了这些复杂的事情都交给硬件去完成。同样基带有需要处理的事情都是通过触发中断交给软件端。
到此我们已经了解了基带在芯片的结构,然后我们需要进一步了解基带的功能。我认为基带在蓝牙通信最核心的是两个功能,一个是空口数据包的发送封装和接收解析,还有就是蓝牙通讯的时序控制,特别是在时序控制,BLE协议规范规定每两个蓝牙包的间隔是T_IFS 150(±2)us,特别是在创建连接的过程以及保持连接是需要非常准确的时间控制。
协议中规定的空口包主要是编码和非编码这两种数据包格式,PDU就是controller处理完后写入基带的数据,基带的实现主要是通过状态机fsm来处理的,如果大家了解数字前端相关的知识应该知道数字电路都是通过写硬件描述语言verilog或者VHDL来实现的,当然这个相比软件写的代码,可就复杂谨慎的多了,软件端基本上只要逻辑能够实现,随便你怎么写,出bug了大不了就改,而硬件描述写的代码,最终是生成数字电路流片生产芯片,如果有bug那这批芯片全费,直接造成巨大损失,如果是初创公司,基本也就宣告凉凉,所以前期数字电路是要经过无数次的验证和改版才能投入流片,继续说回基带的数据处理,这里简单的写个verilog和画个流程图帮助理解,实际情况肯定比这个复杂很多。
//状态机的主状态只列举发送 localparame IDLE = 8’h11; localparame STR_TX = 8’h22; localparame TX = 8’h33; localparame TX_DONE = 8’h44; //发送包的格式这里只举例ADV_IND localparame ADV_IND = 8’h0; localparame ADV_DIRECT_IND = 8’h1; localparame ADV_NONCONN_IND = 8’h2; localparame SCAN_REQ = 8’h3; .......................... always@(posedge clk or negedge rst) begin if(!rst) //复位代码 else begin case(state) IDLE:begin if(s_en) begin //需要处理时把这个信号拉起来 case(pdu_mode) ADV_IND:begin //做打开tx的准备工作 state <= STR_TX; end ADV_DIRECT_IND:begin // 处理这个包需要的任务 end ADV_NONCONN_IND:begin // 处理这个包需要的任务 end SCAN_REQ:begin // 处理这个包需要的任务 end default:begin //数字电路的严谨性不完整的case需要加上 //default以免生成锁存器 end end STR_TX:begin //等待tx的所有事物完成 if(complete) state <= TX; end TX:begin //打开TX,当这个信号拉起来的同时 //数据封装模块检测到这个信号将启动起来 tx_enbale <= 1; if(tx_packt_process)//如果数据发送完 state <=TX_DONE; end TX_DONE:begin //关闭tx tx_enbale<=0; state <=IDLE; end else state <= IDLE; end end end
数据处理的TX也是状态机控制的,当检测到tx_enable信号状态机就工作起来,空口数据包发送完后拉起一个完成信号通知主状态机,关闭TX状态。流程图如下。
以上介绍了BB发送数据的封装,对于接收其实就是一个逆过程,关键在于接收的时机也就是BB打开RX的时机,如果是可扫描的广播态 BB在一个pramiry channel发送广播数据包后经过一个I_TFS会打开RX接收数据如果有数据就接收处理,没有数据就继续在下一个通道重复操作,关于时序控制会在下一章详细的介绍。
由于编者经验水平有限,如有错误请更正!有感兴趣朋友也欢迎讨论,谢谢!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。