轉載於:http://blog.chinaunix.net/uid-28852942-id-5745469.html
這裏我們用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字節,因爲理論部分說過PDU爲2-39字節,2爲2字節頭,所以payload最長爲37字節。並且設置接入地址的長度,上面已經設置了爲4字節。設置發送順序爲LSB(規範要求)。
然後使能白化功能,白化是爲了將原始信息中轉換爲高度隨機的Bit序列,避免出現太長連續的bit0或bit 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
NRF_RADIO->FREQUENCY = 2; //鏈路層信道編號 37:2402MHz, 38:2426MHz, 39:2480MHz
NRF_RADIO->TXPOWER = 0x04;
NRF_RADIO->MODE = 0x03; //ble_1Mbit
實際使用中,我查看了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 <stdio.h>
#include <string.h>
#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
}
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();
while(1){
nrf_delay_ms(50);
send_data();
}
return 0;
}