基于stm32使用NRF24l01广播蓝牙数据

前言

经过分析,发现nrf24l01的无线频段和调制方式和蓝牙是相同的,都是2.4Ghz和高斯键控频移,由此产生了是否可以使用nrf24l01发送蓝牙数据的想法,在网络上搜索发现有人在Arduino上实现了发送蓝牙广播,由此确信使用nrf24l01发送蓝牙数据是可行的。
本文章参考了:http://www.github.com/floe/BTLE

一、硬件平台

为了方便,减少不必要的工作,本文使用正点原子的探索者开发板,并且在《nrf24l01无线通信实验》工程下做修改。

二、编写蓝牙兼容代码

修改发送数据函数为蓝牙兼容的形式:
其中主要的修改点是,修改同步地址为蓝牙广播的同步地址,由于nrf24l01的数据存储方式和蓝牙是相反的,所以要颠倒位序;然后不使能crc

void NRF24L01_TX_Mode(void)
{														 
	NRF24L01_CE=0;	  
		
	//发送节点地址4
	u8 addr[4]={0x6B,0x7D,0x91,0x71};
  NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)addr,4);
  NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)addr,4); 
	
	//禁止通道自动应答    
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x00);  

	//使能通道0的接收地址  
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); 
	
	//禁止自动重发
  NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x00);
	
	//设置RF通道为40
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,2);    
	
  //设置TX发射参数,0db增益,1Mbps,低噪声增益开启  
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x07);   
	
	//配置基本工作模式的参数;PWR_UP,不启用CRC,接收模式,开启所有中断
  NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x06);  
  
	NRF24L01_CE=1;//CE为高,10us后启动发送
}





接下来仿照 http://www.github.com/floe/BTLE 中的ble.cpp文件编写数据帧构造的相关代码。
新建ble.c和ble.h文件,在ble.h中添加如下代码:


#include "sys.h"   



// advertisement PDU
__packed struct btle_adv_pdu {

	// packet header
	uint8_t pdu_type; // PDU type
	uint8_t pl_size;  // payload size 负载大小,包括6字节的mac

	// MAC address
	uint8_t mac[6];

	// payload (including 3 bytes for CRC)
	uint8_t payload[24];
};

// payload chunk in advertisement PDU payload
__packed struct btle_pdu_chunk {
	uint8_t size;
	uint8_t type;
	uint8_t data[];
};






typedef struct
{
	struct btle_adv_pdu buffer;
	char *name;
	uint8_t current;   // current channel index
}ble_struct;




//把无线模块初始化为蓝牙兼容的形式
void ble_begin( ble_struct *ble,char* _name ) ;

//发送广播数据
int ble_advertise( ble_struct *ble,uint8_t data_type, void* buf, uint8_t buflen ) ;

//改变信道
void ble_hopChannel(ble_struct *ble) ;




#endif

在ble.c中添加如下代码:
主要需要做的是构造广播数据帧,其中包括蓝牙的mac地址,蓝牙名称等,用户还可以添加其他数据比如传感器的温湿度等,最后还要交换所有数据的位序和“白化”操作


#include "ble.h"
#include "string.h"
#include "24l01.h"




// This is a rather convoluted hack to extract the month number from the build date in
// the __DATE__ macro using a small hash function + lookup table. Since all inputs are
// const, this can be fully resolved by the compiler and saves over 200 bytes of code.
#define month(m) month_lookup[ (( ((( (m[0] % 24) * 13) + m[1]) % 24) * 13) + m[2]) % 24 ]
const uint8_t month_lookup[24] = { 0,6,0,4,0,1,0,17,0,8,0,0,3,0,0,0,18,2,16,5,9,0,1,7 };


const uint8_t channel[3]   = {37,38,39};  // logical BTLE channel number (37-39)
const uint8_t frequency[3] = { 2,26,80};  // physical frequency (2400+x MHz)








void ble_preparePacket(ble_struct *ble) ;
void ble_transmitPacket(ble_struct *ble) ;
void ble_whiten( ble_struct *ble,uint8_t len ) ;
void ble_swapbuf( ble_struct *ble,uint8_t len ) ;
void ble_crc( ble_struct *ble,uint8_t len, uint8_t* dst ) ;





//添加数据段,返回0,成功
int ble_addChunk(ble_struct *ble,uint8_t chunk_type, uint8_t buflen, const void* buf)
{
	if (ble->buffer.pl_size + buflen + 2 > 21 + 6) // (buflen+2) is how much this chunk will take, 21 is payload size without crc and 6 is MAC size
		return -1;
	
	struct btle_pdu_chunk* chunk = (struct btle_pdu_chunk*) (ble->buffer.payload+ble->buffer.pl_size-6);
	chunk->type = chunk_type;
	for (uint8_t i = 0; i < buflen; i++)
		chunk->data[i] = ((uint8_t*)buf)[i];
	chunk->size = buflen + 1;
	ble->buffer.pl_size += buflen + 2;
	return 0;
}



void ble_hopChannel(ble_struct *ble) {
	ble->current++;
	if (ble->current >= sizeof(channel)) ble->current = 0;
	NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH, frequency[ble->current] );
}






//发送一个广播包
int ble_advertise( ble_struct *ble,uint8_t data_type, void* buf, uint8_t buflen ) {
	ble_preparePacket(ble);
	
	// add custom data, if applicable
	if (buflen > 0) {
		int success = ble_addChunk(ble,data_type, buflen, buf);
		if (0!=success) {
			return -1;
		}
	}
	
	ble_transmitPacket(ble );
	return 0;
}



void ble_preparePacket(ble_struct *ble) 
{
	// insert pseudo-random MAC address
	ble->buffer.mac[0] = ((__TIME__[6]-0x30) << 4) | (__TIME__[7]-0x30);
	ble->buffer.mac[1] = ((__TIME__[3]-0x30) << 4) | (__TIME__[4]-0x30);
	ble->buffer.mac[2] = ((__TIME__[0]-0x30) << 4) | (__TIME__[1]-0x30);
	ble->buffer.mac[3] = ((__DATE__[4]-0x30) << 4) | (__DATE__[5]-0x30);
	ble->buffer.mac[4] = month(__DATE__);
	ble->buffer.mac[5] = ((__DATE__[9]-0x30) << 4) | (__DATE__[10]-0x30) | 0xC0; // static random address should have two topmost bits set
	
	//ble->buffer.pdu_type = 0x42;    // PDU type: ADV_NONCONN_IND, TX address is random
	ble->buffer.pdu_type = 0x02;
	ble->buffer.pl_size = 6; //including MAC
	
	// add device descriptor chunk
	uint8_t flags = 0x05;
	ble_addChunk(ble,0x01, 1, &flags);
	
	// add "complete name" chunk
	if (strlen(ble->name) > 0) {
		ble_addChunk(ble,0x09, strlen(ble->name), ble->name);
	}
}




void ble_transmitPacket(ble_struct *ble) 
{
	uint8_t pls = ble->buffer.pl_size - 6;
	// calculate CRC over header+MAC+payload, append after payload
	uint8_t* outbuf = (uint8_t*)&ble->buffer;
	ble_crc( ble,pls+8, outbuf+pls+8);
	
	// whiten header+MAC+payload+CRC, swap bit order
	ble_whiten(ble, pls+11 );
	ble_swapbuf( ble,pls+11 );
	
	// flush buffers and send
	//radio->stopListening();
	//radio->write( outbuf, pls+11 );
	NRF24L01_TxPacket(outbuf,32);
}






// change buffer contents to "wire bit order"
void ble_swapbuf( ble_struct *ble,uint8_t len ) {

	uint8_t* buf = (uint8_t*)&ble->buffer;

	while (len--) {

		uint8_t a = *buf;
		uint8_t v = 0;

		if (a & 0x80) v |= 0x01;
		if (a & 0x40) v |= 0x02;
		if (a & 0x20) v |= 0x04;
		if (a & 0x10) v |= 0x08;
		if (a & 0x08) v |= 0x10;
		if (a & 0x04) v |= 0x20;
		if (a & 0x02) v |= 0x40;
		if (a & 0x01) v |= 0x80;

		*(buf++) = v;
	}
}






// see BT Core Spec 4.0, Section 6.B.3.2
void ble_whiten( ble_struct *ble,uint8_t len ) {

	uint8_t* buf = (uint8_t*)&ble->buffer;

	// initialize LFSR with current channel, set bit 6
	uint8_t lfsr = channel[ble->current] | 0x40;

	while (len--) {
		uint8_t res = 0;
		// LFSR in "wire bit order"
		for (uint8_t i = 1; i; i <<= 1) {
			if (lfsr & 0x01) {
				lfsr ^= 0x88;
				res |= i;
			}
			lfsr >>= 1;
		}
		*(buf++) ^= res;
	}
}



void ble_crc( ble_struct *ble,uint8_t len, uint8_t* dst ) {

	uint8_t* buf = (uint8_t*)&ble->buffer;

	// initialize 24-bit shift register in "wire bit order"
	// dst[0] = bits 23-16, dst[1] = bits 15-8, dst[2] = bits 7-0
	dst[0] = 0xAA;
	dst[1] = 0xAA;
	dst[2] = 0xAA;

	while (len--) {

		uint8_t d = *(buf++);

		for (uint8_t i = 1; i; i <<= 1, d >>= 1) {

			// save bit 23 (highest-value), left-shift the entire register by one
			uint8_t t = dst[0] & 0x01;         dst[0] >>= 1;
			if (dst[1] & 0x01) dst[0] |= 0x80; dst[1] >>= 1;
			if (dst[2] & 0x01) dst[1] |= 0x80; dst[2] >>= 1;

			// if the bit just shifted out (former bit 23) and the incoming data
			// bit are not equal (i.e. bit_out ^ bit_in == 1) => toggle tap bits
			if (t != (d & 1)) {
				// toggle register tap bits (=XOR with 1) according to CRC polynom
				dst[2] ^= 0xDA; // 0b11011010 inv. = 0b01011011 ^= x^6+x^4+x^3+x+1
				dst[1] ^= 0x60; // 0b01100000 inv. = 0b00000110 ^= x^10+x^9
			}
		}
	}
}



void ble_begin( ble_struct *ble,char* _name ) 
{

	ble->name = _name;



	NRF24L01_CE=0;	  
		
	//发送节点地址4
//	u8 addr[4]={0x6B,0x7D,0x91,0x71};
	u8 addr[4]={0x71,0x91,0x7D,0x6B};
  NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)addr,4);
  NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)addr,4); 
	
	//禁止通道自动应答    
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x00);  

	//使能通道0的接收地址  
  NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); 
	
	//禁止自动重发
  NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x00);
	
	//设置RF通道为40
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,frequency[ble->current]);    
	
  //设置TX发射参数,0db增益,1Mbps,低噪声增益开启  
  NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x07);   
	
	//配置基本工作模式的参数;PWR_UP,不启用CRC,接收模式,开启所有中断
  NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x06);  
  
	NRF24L01_CE=1;//CE为高,10us后启动发送



}

三、调用

在主函数中添加如下代码:

		int index=0;
		
		//初始化ble对象
		ble_begin(&ble,"ble_chuan");
		while(1)
		{	  
			//在屏幕上显示发送次数
			sprintf (TEXT_Buffer,"Tx runed:%d",index);
			LCD_ShowString(30,150,200,16,16,(u8 *)TEXT_Buffer);	
			//发送广播
			ble_advertise(&ble,0xff,0,0);
			//蓝牙广播信道有3个,这里切换广播信道
			ble_hopChannel(&ble); 
			index++;
			LED0=!LED0;
			delay_ms(300);				    
		};

四、现象

开发板照片
手机端搜索到如下设备:
手机搜索的设备
其中“ble_chuan”和程序中设置的名称相同,实验成功。

五、总结

由于nrf24l01的数据fifo只有32字节,每次广播的数据非常有限,并且由于其没有同步地址匹配中断等细化的功能,并不能用来做蓝牙的无线收发器,因此实现蓝牙数据广播并不具有太大的使用意义,实现本程序更大的意义在于学习蓝牙协议。

以下是基于STM32nrf24l01例程,你可以根据自己的需求进行修改: ```c #include "stm32f10x.h" #include "nrf24l01.h" #include "spi.h" #include "delay.h" uint8_t tx_data[32] = "Hello, World!"; uint8_t rx_data[32]; void NRF24L01_Init(void) { SPI1_Init(); NRF24L01_CE_LOW(); NRF24L01_CSN_HIGH(); NRF24L01_PWR_UP(); NRF24L01_Set_Address_Width(5); NRF24L01_Set_Retries(0x0F, 0x0F); NRF24L01_Set_Channel(10); NRF24L01_Set_Data_Rate(NRF24L01_DR_250kbps); NRF24L01_Set_PA_Level(NRF24L01_PA_MAX); NRF24L01_Set_CRC_Mode(NRF24L01_CRC_2byte); NRF24L01_Set_RX_Pipe(0, 0xE7E7E7E7E7); NRF24L01_Set_RX_Pipe(1, 0xC2C2C2C2C2); NRF24L01_Set_RX_Pipe(2, 0xC3); NRF24L01_Set_RX_Pipe(3, 0xC4); NRF24L01_Set_RX_Pipe(4, 0xC5); NRF24L01_Set_RX_Pipe(5, 0xC6); NRF24L01_Enable_RX_Pipe(0); NRF24L01_Enable_RX_Pipe(1); NRF24L01_Enable_RX_Pipe(2); NRF24L01_Enable_RX_Pipe(3); NRF24L01_Enable_RX_Pipe(4); NRF24L01_Enable_RX_Pipe(5); NRF24L01_Set_Mode(NRF24L01_MODE_RX); NRF24L01_CE_HIGH(); } int main(void) { uint8_t i, j; NRF24L01_Init(); Delay_Init(); while (1) { NRF24L01_CE_LOW(); NRF24L01_Write_Buffer(NRF24L01_CMD_W_TX_PAYLOAD, tx_data, 32); NRF24L01_CE_HIGH(); Delay_Ms(10); if (NRF24L01_Get_Status() & NRF24L01_STATUS_TX_DS) { NRF24L01_Write_Register(NRF24L01_REG_STATUS, NRF24L01_STATUS_TX_DS); for (i = 0; i < 32; i++) { tx_data[i]++; } } else if (NRF24L01_Get_Status() & NRF24L01_STATUS_MAX_RT) { NRF24L01_Write_Register(NRF24L01_REG_STATUS, NRF24L01_STATUS_MAX_RT); } NRF24L01_CE_LOW(); NRF24L01_Set_Mode(NRF24L01_MODE_RX); NRF24L01_CE_HIGH(); Delay_Ms(10); if (NRF24L01_Data_Ready()) { NRF24L01_Read_Buffer(NRF24L01_CMD_R_RX_PAYLOAD, rx_data, 32); NRF24L01_Write_Register(NRF24L01_REG_STATUS, NRF24L01_STATUS_RX_DR); for (j = 0; j < 32; j++) { rx_data[j]++; } } } } ``` 以上代码中,我们首先初始化了nrf24l01的各项参数,然后在主函数中循环进行数据发送和接收。发送数据时,我们先将CE拉低,然后将待发送的数据写入TX FIFO中,并将CE拉高,开始发送数据。发送完成后,我们判断发送状态寄存器中是否有数据发送成功的标志位,如果有,则清除标志位,并将发送的数据加1。如果没有成功,则清除发送状态寄存器中的最大重传次数标志位。 接收数据时,我们先将CE拉低,然后将nrf24l01的模式设置为接收模式,并将CE拉高,开始接收数据。接收到数据后,我们将数据读取出来,并将接收状态寄存器中的数据接收标志位清除,并将接收到的数据加1。 请注意,以上代码仅供参考,具体实现取决于你的实际需求。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值