STM32
直播中

从未拥有

9年用户 934经验值
擅长:可编程逻辑 电源/新能源 光电显示
私信 关注
[问答]

请问可以使用nRF24L01去发送蓝牙数据吗

可以使用nRF24L01去发送蓝牙数据吗?
如何在Arduino上实现发送蓝牙广播呢?

回帖(1)

刘倩

2021-12-16 14:31:25
前言


经过分析,发现的nRF24L01的无线频段和调制方式和蓝牙是相同的,都是2.4GHz的和高斯键控频移,由此产生了是否可以使用的nRF24L01发送蓝牙数据的想法,在网络上搜索发现有人在Arduino 上实现了发送蓝牙广播,精彩内容使用 nrf24l01 发送蓝牙数据是音频的。

一、硬件平台

方便,减少接入的工作,本文使用正点原子的探索开发板,在《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/BT中的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 = ((uint8_t*)buf);
        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的数据只有32字节,每次广播的数据非常有限,而且没有同步地址匹配中断等细化的功能,也不能做蓝牙的无线收发器,因此实现蓝牙数据广播有很多的使用意义,实现本程序的意义在于学习蓝牙协议。
举报

更多回帖

×
20
完善资料,
赚取积分