Chinaunix首页 | 论坛 | 博客
  • 博客访问: 586014
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-23 22:57
个人简介

我本仁慈,奈何苍天不许

文章分类

全部博文(165)

文章存档

2018年(1)

2016年(33)

2015年(5)

2014年(34)

2013年(92)

分类: 嵌入式

2016-07-21 14:08:59

原文地址:51822模拟ble广播-实践 作者:ifndef


转载请注明出处,谢谢!

理论部分主要介绍了BLE广播包的 数据包格式,知道了数据包格式只需要按照格式要求设置数据包然后发送,那么BLE扫描者就能将这个数据包解析成ble广播包了。

这里我们用51822的radio来实现ble的广播包。

下图是51822空中包的格式。



Preamble: 该部分会根据接入地址而自动设置,不需要我们去设置

ADDRESS由BASE和PREFIX组成,就是前面理论部分说的接入地址,对于广播  信道的数据包来说,接入地址总是0x8E89BED6。注意这个不是ble的广播地      址,广播地址是在广播数据负载中的,而且是6字节,这里的接入地址是4      字节

S0,LENGTH,S1,PAYLOAD:这四个部分组成了理论部分介绍的 PDU。S0,LENGTH,S1这三个部分是可选的。理论部分介绍过PDU有2部分组成,2字节的header和payload所以我们可以使用S0,LENGTH来作为header,不使用s1,然后payload刚好就作为payload。 当然你也可以S0,LENGTH,S1一个都不使用,是使用payload,那么就将payload的前两字节按照理论部分中的2字节header设置,剩下的当做应用负载payload。

CRC我们需要设置一下 CRC的字节数,以及生成式,并且使之值计算的部分不包括前导和ADDRESS部分。

 

首先设置接入地址ADDRESS,因为广播数据是在广播信道中发送的,所以使用的是固定的接入地址0x8E89BED6. 51822有8个逻辑地址0-7,并且8个逻辑地址对应的实际地址可以设置,对应关系如下。也就是通过设置BASE0,BASE1,和PREFIX0,PREFIX1,四个寄存器,我们能分别设置8个逻辑地址的实际地址。


因为这里用的是广播信道的固定地址,所以我们将 8个逻辑地址的实际地址全部都设置成0x8E89BED6,然后设置发送地址为 逻辑地址0就行了。因为数据发送是LSByte先发送,而51822发送ADDRESS是先发送BASE再发送PREFIX,所以我们需要将PREFIX设置成高位字节0x8E,低位3字节设置到BASE中。

 

       NRF_RADIO->BASE0 = (0x89BED600);

       NRF_RADIO->BASE1 = (0x89BED600); 

       NRF_RADIO->PREFIX0 = 0x8E8E8E8E;

       NRF_RADIO->PREFIX1 = 0x8E8E8E8E;

 

       NRF_RADIO->TXADDRESS = 0; //使用逻辑地址0


然后是理论中介绍的PDU部分的设置,即51822中的S0,LENGTH,S1,PAYLOAD,我们使用S0,LENGTH来当做header,PAYLOAD就是PDU中的payload.并且设置S0,LENGTH都为1字节, 然后设置BLE可以发送的最大应用数据 (51822发送的包组成中的payload的长度)37字节,因为理论部分说过PDU2-39字节,22字节头,所以payload最长为37字节。并且设置接入地址的长度,上面已经设置了为4字节。设置发送顺序为LSB(规范要求)

然后使能白化功能,白化是为了将原始信息中转换为高度随机的Bit序列,避免出现太长连续的bit0bit 1从而导致接收出错。是规范要求

 

设置如下:

       //8bit长度的LENGTH  1字节长度的S0,不需要s1,因为广播格式里面负载数  //据前面只需要2字节头,一个是报头,一个是长度。

NRF_RADIO->PCNF0 = (8<<0) | (1<<8);

       //最大长度37,没有静态长度,基础地址为3字节,所以加一字节头后为四 //字节,就是蓝牙规范中接入地址.字节序为小端(CRC不在这里设置)

       //使能数据白化

NRF_RADIO->PCNF1 = (37<<0) | (3<<16) | (1<<25) ;

       //白化0x25;       初始值由报文所在链路层信道号决定,我们在37好广播信道上广播

NRF_RADIO->DATAWHITEIV = 0x25;

 

对于应用负载payload,理论部分说过我们使用 不可连接广播,

定义一个数组用来存放将要广播的数据,然后将数据指针指设置为改buff地址

static uint8_t adv_array[31] = {0};

NRF_RADIO->PACKETPTR = (uint32_t)&adv_array[0];

 

然后设置广播应用数据,如下函数所示

 

//广播地址

uint8_t device_add[6]={0xFF,0x01,0x02,0x03,0x04,0xff};

//广播数据,第一个0x01为flag设置成只支持BLE,

//第二个0x09为设备名,名字随便写的

uint8_t adv_data[10] = {0x02,0x01,0x04, 0x06,0x09,0x4e,0x6f,0x48,0x52,0x3d};

 

void set_advdata(void){

       //adv_array的前两字节为 header 即S0和LENGTH

       //PDU Type设置为ADV_NONCONN_IND,如果设置成普通广播的话,手机可能 //会发扫描包,因为这里没有做扫描回应,手机就会过滤该设备,导致手机       //搜不到设备。

       adv_array[0] = 2;                    

       adv_array[1] = 0;//最后再计算长度

      

       memcpy(adv_array+2, device_add, 6);

       memcpy(adv_array+2+6, adv_data, sizeof(adv_data));

      

       adv_array[1] = 6+sizeof(adv_data);

}

然后需要将 radio的发送指针寄存器赋值为 adv_array.那么发送的时候就会自动将这个数组里的数据发送出去了

       NRF_RADIO->PACKETPTR = (uint32_t)&adv_array[0];

 

 

 

对于CRC,需要设置其字节数,生成式,和初始值。设置如下

       //3字节crc,计算不包括接入地址部分和前导部分

NRF_RADIO->CRCCNF = (3<<0) | (1<<8);    

       //crc多项式为 x^24+x^10+x^9+x^6+x^4+x^3+x^1+x^0

NRF_RADIO->CRCPOLY = 0x100065b

       //广播信道的数据包中crc初始值为0x555555

NRF_RADIO->CRCINIT =  0x555555;   

 

这个里 BLE广播相关的规范设置都设置完了。

我们还需要设置一下,广播的信道。我们在37号广播信道上广播。

设置一下发射功率,以及模式选择为Ble_1Mbit

       //链路层信道编号 37:2402MHz, 38:2426MHz, 39:2480MHz

NRF_RADIO->FREQUENCY = 2;      

NRF_RADIO->TXPOWER = 0x04;

NRF_RADIO->MODE = 0x03;   //ble_1Mbit

 

另外根据手册说明 还有一个校准值的设置,如下图手册中的描述

       NRF_RADIO->OVERRIDE4 = 1<<31;

       NRF_RADIO->OVERRIDE0 = NRF_FICR->BLE_1MBIT[0];

       NRF_RADIO->OVERRIDE1 = NRF_FICR->BLE_1MBIT[1];

       NRF_RADIO->OVERRIDE2 = NRF_FICR->BLE_1MBIT[2];

       NRF_RADIO->OVERRIDE3 = NRF_FICR->BLE_1MBIT[3];

       NRF_RADIO->OVERRIDE4 = NRF_FICR->BLE_1MBIT[4];



实际使用中,我查看了FICR中的OVERRIDEEN 的值,两个指示位都为0,应该是要用FICR中的校准值覆盖RADIO中的校准值,不过代码实现中我屏蔽了设置也能收到广播。



最后就是发送数据的实现了

函数实现很简单,直接启动就可以了,radio会自动将上面设置的NRF_RADIO->PACKETPTR指向的数组数据发送出去

void send_data(void){

      

       NRF_RADIO->EVENTS_READY = 0;

       NRF_RADIO->TASKS_TXEN = 1;                     //启动发送使能

       while(NRF_RADIO->EVENTS_READY == 0){}       //等待准备好

      

       NRF_RADIO->EVENTS_END = 0;

 

       NRF_RADIO->TASKS_START = 1;            //开始发送 

       while(NRF_RADIO->EVENTS_END == 0)//等待发送完成

      

       NRF_RADIO->EVENTS_DISABLED = 0;

       NRF_RADIO->TASKS_DISABLE = 1;

       while(NRF_RADIO->EVENTS_DISABLED == 0){}//等待停止完成

}

 

下面贴出整体代码

 

#include "nrf51.h"

#include "nrf_gpio.h"

#include

#include

#include "nrf_delay.h"

 

uint8_t adv_data[10] = {0x02,0x01,0x04,   0x06,0x09,0x4e,0x6f,0x48,0x52,0x3d};

uint8_t device_add[6]={0xFF,0x01,0x02,0x03,0x05,0xff};

 

static uint8_t adv_array[37] = {0};

 

void  init_clock(void){

       NRF_CLOCK->XTALFREQ = 0xff;             //16M

       NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;

       NRF_CLOCK->TASKS_HFCLKSTART = 1;

       while(NRF_CLOCK->EVENTS_HFCLKSTARTED == 0){     

       }      //等待启振完成    

}

 

 

void radio_init(void){

       //这里设置8个逻辑地址的实际地址,因为我们只是做广播,所以把全部地址都设置成0x8E89BED6,

       NRF_RADIO->BASE0 = (0x89BED600);

       NRF_RADIO->BASE1 = (0x89BED600); 

       NRF_RADIO->PREFIX0 = 0x8E8E8E8E;

       NRF_RADIO->PREFIX1 = 0x8E8E8E8E;

 

       NRF_RADIO->TXADDRESS = 0; //使用逻辑地址0

      

       NRF_RADIO->CRCCNF = (3<<0) | (1<<8);     //3字节crc,计算不包括接入地址部分

       NRF_RADIO->CRCPOLY = 0x100065b;//crc多项式为 x^24+x^10+x^9+x^6+x^4+x^3+x^1+x^0

       NRF_RADIO->CRCINIT =  0x555555;    //广播信道的数据包中crc初始值为0x555555

       NRF_RADIO->PACKETPTR = (uint32_t)&adv_array[0];

       NRF_RADIO->FREQUENCY = 2;       //链路层信道编号 37:2402MHz, 38:2426MHz, 39:2480MHz

       NRF_RADIO->TXPOWER = 0x04;

       NRF_RADIO->MODE = 0x03;   //ble_1Mbit

      

       //8bit长度的LENGTH  1字节长度的S0,不需要s1,因为广播格式里面负载数据前面只需要2字节头,一个是报头,一个是长度。

       NRF_RADIO->PCNF0 = (8<<0) | (1<<8);

       //payload最大长度37,没有静态长度,基础地址为3字节,所以加一字节头后为四字节,就是蓝牙规范中接入地址.字节序为小端(CRC不在这里设置)

       //使能数据白化

       NRF_RADIO->PCNF1 = (31<<0) | (3<<16) | (1<<25) ;

       NRF_RADIO->DATAWHITEIV = 0x25;//0x25;        //初始值由报文所在链路层信道号决定,这里为37

             

       NRF_RADIO->OVERRIDE4 = 1<<31;

       NRF_RADIO->OVERRIDE0 = NRF_FICR->BLE_1MBIT[0];

       NRF_RADIO->OVERRIDE1 = NRF_FICR->BLE_1MBIT[1];

       NRF_RADIO->OVERRIDE2 = NRF_FICR->BLE_1MBIT[2];

       NRF_RADIO->OVERRIDE3 = NRF_FICR->BLE_1MBIT[3];

       NRF_RADIO->OVERRIDE4 = NRF_FICR->BLE_1MBIT[4];

}

 

void set_advdata(void){

       adv_array[0] = 2;       //PDU Type为ADV_NONCONN_IND,如果设置成普通广播的话,手机可能会发扫描包,因为这里没有做扫描回应,手机就会过滤该设备,导致手机搜不到设备。

       adv_array[1] = 0;//最后再计算长度

      

       memcpy(adv_array+2, device_add, 6);

       memcpy(adv_array+2+6, adv_data, sizeof(adv_data));

      

       adv_array[1] = 6+sizeof(adv_data);

}

 

void send_data(void){

      

       NRF_RADIO->EVENTS_READY = 0;

       NRF_RADIO->TASKS_TXEN = 1;

       while(NRF_RADIO->EVENTS_READY == 0){}       //等待准备好

      

       NRF_RADIO->EVENTS_END = 0;

 

       NRF_RADIO->TASKS_START = 1;           

       while(NRF_RADIO->EVENTS_END == 0)//等待发送完成

      

       NRF_RADIO->EVENTS_DISABLED = 0;

       NRF_RADIO->TASKS_DISABLE = 1;

       while(NRF_RADIO->EVENTS_DISABLED == 0){}//等待停止完成
}

 

int main(void){

       uint32_t data;

      

       init_clock();

       radio_init();

       set_advdata();

      

       nrf_gpio_cfg_output(22);

       nrf_gpio_pin_clear(22);

      

       while(1){

              nrf_delay_ms(50);

              send_data();

             

       }

       return 0;

}



阅读(854) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~