历史上的今天
返回首页

历史上的今天

今天是:2024年11月25日(星期一)

正在发生

2019年11月25日 | NRF24L01无线模块多机通信单片机程序 上位机+下位机

2019-11-25 来源:51hei

最近在接了一个项目,要求各个设备能够联网(不是互联网)控制。nrf24l01刚好有这个功能。但是之前只做过一对一的通信,还是用例程的那一种。我在想,用两个地址,共用同一个通道应该也可以完成。后来他又要求发过去的数据还要能回传,这下我只好来研究多通道通信了。

多机通信和一对一通信基本上相同,就是要配置其他通道的地址和使能其他通道的有效数据宽度,还有自动应答。

这是接收机的:

void NRF24L01_Init_RX(void)
{   
CE=0;   
CSN=1;   
SCK=0;   
SPI_Write_Buf(WRITE_REG + TX_ADDR, RX_ADDRESS, TX_ADR_WIDTH);
SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH);
SPI_Write_Buf(WRITE_REG + RX_ADDR_P1, RX_ADDRESS1, RX_ADR_WIDTH);
SPI_Write_Reg(WRITE_REG + EN_AA, 0x3f);
SPI_Write_Reg(WRITE_REG + EN_RXADDR, 0x3f);
SPI_Write_Reg(WRITE_REG + RF_CH, 40);
SPI_Write_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH);
SPI_Write_Reg(WRITE_REG + RX_PW_P1, RX_PLOAD_WIDTH);
SPI_Write_Reg(WRITE_REG + RF_SETUP, 0x0F);
SPI_Write_Reg(WRITE_REG + CONFIG, 0x0f);
}

由于我用的是KeilC51和MDK5共存的,所以注释复制过来之后就成了乱码,在此贴图一张:

NRF24L01_Init_Rx(void)
下面就到了发送机了的配置了:

void NRF24L01_Init_TX(uint8_t *ADDRn)
{   
CE=0;
CSN=1;
SCK=0;
SPI_Write_Buf(WRITE_REG + TX_ADDR, (uint8_t*)ADDRn, TX_ADR_WIDTH);
SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, (uint8_t*)ADDRn, RX_ADR_WIDTH);
SPI_Write_Reg(WRITE_REG + EN_AA, 0x3f);
SPI_Write_Reg(WRITE_REG + EN_RXADDR, 0x3f);
SPI_Write_Reg(WRITE_REG + RF_CH, 40);
SPI_Write_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH);
SPI_Write_Reg(WRITE_REG + RX_PW_P1, RX_PLOAD_WIDTH);
SPI_Write_Reg(WRITE_REG + SETUP_RETR, 0xff);
SPI_Write_Reg(WRITE_REG + RF_SETUP, 0x0f);
SPI_Write_Reg(WRITE_REG + CONFIG, 0x0E);
}

同样:


NRF24L01_Init_TX(uint8_t *ADDRn)
发送给其他通道,只要改成其他通道的地址就可以了,不用改通道号,现在还不知道为什么。

接下来就是发送函数:

uint8_t nRF24L01_TxPacket(unsigned char * tx_buf)
{
uint8_t Return_Flag=0;
CE=0;
SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH);
Delayms(2);
Return_Flag = Check_ACK(1);
return Return_Flag;
}

由于我是通过电脑串口控制上位机,发给某一个下位机,然后再发回到我的上位机中,最后回传到电脑,所以没有数据抢答的问题。

最后再说一句,要配置第N个的通道,前N个通道也必须被配置,要不然配置不成功。

最后贴上上位机程序:

#include

#include

#include "nrf24l01.h"

#include "delay.h"

#define uint32_t unsigned int

#define uint8_t unsigned char


uint8_t const RX_ADDRESS[TX_ADR_WIDTH]= {0x01,0x13,0x5C,0x0C,0x03};        //本地地址

uint8_t const RX_ADDRESS1[TX_ADR_WIDTH]={0x02,0x13,0x5C,0x0C,0x03}; //本地地址1



extern uint8_t Rx_Buf[32];

extern uint8_t Tx_Buf[32];


/**********************************RNF24L01状态标志位************************************************/

uint8_t         bdata sta;   

sbit        RX_DR        =sta^6;

sbit        TX_DS        =sta^5;

sbit        MAX_RT        =sta^4;


/**********************************NRF24L01管脚与单片机接口定义**************************************/



sbit  MISO =P3^4;

sbit  MOSI =P3^6;

sbit SCK =P3^3;

sbit  CE =P3^2;

sbit CSN =P3^5;

sbit IRQ =P3^7;




/***************************************************************************

函数名称:uchar SPI_RW(uint8_t dat)

函数功能:NRF24L01的SPI写时序

函数备注:

***************************************************************************/

uint8_t SPI_RW(uint8_t dat)

{

        uint8_t i;

           for(i=0;i<8;i++) 

           {

                MOSI = (dat & 0x80);         

                dat = (dat << 1);           

                SCK = 1;                      

                dat |= MISO;                         

                SCK = 0;                              

           }

    return(dat);                             

}


/***************************************************************************

函数名称:uint8_t SPI_Read(uint8_t cmd_reg)

函数功能:NRF24L01的SPI读时序

函数备注:

***************************************************************************/

uint8_t SPI_Read(uint8_t cmd_reg)

{

        uint8_t value;

        

        CSN = 0;               

        SPI_RW(cmd_reg);            

        value = SPI_RW(0);    

        CSN = 1;                

        

        return(value);        

}


/***************************************************************************

函数名称:void SPI_Write_Reg(uint8_t cmd_reg, uint8_t value)

函数功能:写入NRF24L01寄寄存器和从NRF24L01寄存器中读出

函数备注:

***************************************************************************/

void SPI_Write_Reg(uint8_t cmd_reg, uint8_t value)

{        

        CSN = 0;                  

        SPI_RW(cmd_reg);     

        SPI_RW(value);            

        CSN = 1;                   

}


/***************************************************************************

函数名称:uint8_t SPI_Read_Buf(uint8_t cmd_reg, uint8_t *pBuf, uint8_t num)

函数功能:从NRF24L01寄存器中读出数据

函数备注:reg:为寄存器地址,pBuf:为待读出数据地址,uint8_t:读出数据的个数

***************************************************************************/

uint8_t SPI_Read_Buf(uint8_t cmd_reg, uint8_t *pBuf, uint8_t num)

{

        uint8_t status,i;

        

        CSN = 0;                                    

        status = SPI_RW(cmd_reg);                       

        for(i=0;i        CSN = 1;                           

        

        return(status);                    

}


/***************************************************************************

函数名称:void SPI_Write_Buf(uint8_t cmd_reg, uint8_t *pBuf, uint8_t num)

函数功能:在NRF24L01寄存器中写入数据

函数备注:reg:为寄存器地址,pBuf:为待写入数据地址,uint8_t:写入数据的个数

***************************************************************************/

void SPI_Write_Buf(uint8_t cmd_reg, uint8_t *pBuf, uint8_t num)

{

        uint8_t i;        

        CSN = 0;            //SPI使能       

        SPI_RW(cmd_reg);   

        for(i=0; i        CSN = 1;           //关闭SPI

}


/***************************************************************************

函数名称:uint8_t nRF24L01_RxPacket(uint8_t* rx_buf)

函数功能:数据读取后放入rx_buf接收缓冲区中

函数备注:

***************************************************************************/

uint8_t nRF24L01_RxPacket(uint8_t* rx_buf)

{

   uint8_t flag;

        CE = 1;                          //很重要!启动接收!

        Delayms(1);

        sta=SPI_Read(READ_REG+STATUS);        // 读取状态寄存其来判断数据接收状况           //寄存器前面要加是读还是写

        if(RX_DR)                                // 判断是否接收到数据  如果置1则说明接到数据并且放置在接收缓存器

        {

           CE = 0;                 

                SPI_Read_Buf(RD_RX_PLOAD,rx_buf,RX_PLOAD_WIDTH); //这本来就是指令,不用加,意思说去缓存器里读

                flag =1;                        //读取数据完成标志

        }

        SPI_Write_Reg(WRITE_REG+STATUS,sta);   //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标志

        return flag;

}





/**************************************************

函数:Check_ACK(bit clear)

描述:检查接收设备有无接收到数据包,设定没有收到应答信

            号是否重发

/**************************************************/

uint8_t Check_ACK(bit clear)

{

        while(IRQ);

        sta = SPI_RW(NOP);                    // 返回状态寄存器

        if(MAX_RT)

                if(clear)                         // 是否清除TX FIFO,没有清除在复位MAX_RT中断标志后重发

                        SPI_RW(FLUSH_TX);   //清空寄存器,很重要!!!

        SPI_Write_Reg(WRITE_REG + STATUS, sta);  // 清除TX_DS或MAX_RT中断标志

        IRQ = 1;

        if(TX_DS)

                return(0x01);

        else

                return(0x00);

}



/***************************************************************************

函数名称:void nRF24L01_TxPacket(unsigned char * tx_buf)

函数功能:发送 tx_buf中数据

函数备注:

***************************************************************************/

推荐阅读

史海拾趣

思瑞浦微电子科技(3PEAK INCORPORATED)公司的发展小趣事

思瑞浦微电子科技(3PEAK INCORPORATED)公司是一家总部位于台湾的电子元器件制造商,专注于高性能模拟和混合信号集成电路(IC)的设计、开发和销售。以下是关于该公司发展的五个相关故事:

  1. 公司成立和早期发展: 思瑞浦微电子科技公司成立于2003年,初期主要致力于集成电路设计和技术研发。公司在成立初期便注重技术创新和产品质量,通过不断提升研发能力和技术水平,逐渐树立了良好的品牌声誉。

  2. 产品线扩展和市场拓展: 随着公司技术实力的增强和市场需求的不断变化,思瑞浦逐步扩展了产品线,并开始向汽车电子、工业控制、消费电子等领域拓展市场。公司致力于提供高性能、低功耗的模拟和混合信号IC,满足不同领域客户的需求。

  3. 技术创新和专利积累: 思瑞浦在模拟和混合信号集成电路领域不断进行技术创新和研发投入,取得了多项关键技术的突破和专利成果。公司建立了完善的研发体系和技术团队,持续推出具有竞争优势的新产品,加强了在市场上的地位和竞争力。

  4. 国际合作和市场扩张: 为了进一步拓展市场和提升品牌影响力,思瑞浦积极开展国际合作,与全球知名的客户和合作伙伴建立了长期稳定的合作关系。公司产品逐渐进入国际市场,销售网络覆盖了全球多个地区,并取得了可观的业绩和市场份额。

  5. 持续发展和未来展望: 思瑞浦微电子科技公司在不断发展壮大的过程中,始终坚持技术创新和客户导向,不断提升产品质量和服务水平。公司未来将继续致力于研发投入、市场拓展和国际合作,加强在模拟和混合信号集成电路领域的领先地位,为客户提供更优质的产品和解决方案。

CommScope Inc公司的发展小趣事

随着通信行业的快速发展,康普公司意识到光纤光缆在有线电视行业的重要性。因此,在1977年,康普公司合并了Valtec公司,一个独立的光纤技术领导者。这次合并不仅增强了康普在光纤技术方面的实力,也为其日后的全球化布局打下了坚实的基础。随后,康普通过一系列的战略合作和收购,逐渐在全球范围内建立了供应链和生产基地,为全球客户提供高效、可靠的通讯网络解决方案。

Andon Electronics公司的发展小趣事

随着全球经济的不断发展,亚洲市场逐渐成为通信行业的重要增长点。康普公司敏锐地捕捉到了这一机遇,于1997年在苏州工业园区成立了康普通讯技术(中国)有限公司,这是康普在亚洲开设的第一家工厂。该工厂的成立不仅提高了康普在亚洲市场的生产效率,也为其进一步开拓亚洲市场提供了有力的支持。此后,康普在亚洲市场的业务逐渐扩大,为众多国内大型项目提供了网络解决方案。

DAESAN公司的发展小趣事

在电子行业的激烈竞争中,DAESAN公司也曾遭遇过困境。一次严重的金融危机让公司的资金链几乎断裂,许多项目被迫中断。然而,DAESAN公司并没有放弃,他们积极寻求外部支持,同时加强内部管理,降低成本,提高效率。在困境中,公司不断总结经验教训,优化经营策略。最终,他们成功度过了危机,实现了逆境中的成长。

上海晶丰明源(BPS)公司的发展小趣事

作为一家有社会责任感的企业,晶丰明源始终注重可持续发展。公司积极履行环保责任,采用环保材料和工艺,减少生产过程中的污染排放。同时,公司还关注员工福利和社区建设,为员工提供良好的工作环境和福利待遇,积极参与社会公益活动,为社区的和谐发展做出了贡献。

这五个故事从不同角度展现了上海晶丰明源(BPS)公司在电子行业中的发展历程和成就。从创立到上市、从技术突破到市场拓展、从应对挑战到履行社会责任,晶丰明源始终坚持以市场为导向、以技术为核心的发展理念,不断追求卓越和创新,为电子行业的发展做出了重要贡献。

AZM [Arizona Microtek, Inc]公司的发展小趣事

AZM公司成立于电子行业的蓬勃发展时期,创始人凭借对微电子技术的深刻理解和敏锐的市场洞察力,决定投身于这一领域。创业初期,公司面临着资金短缺、技术瓶颈和市场认可度低等多重挑战。然而,创始人凭借坚定的信念和不懈的努力,带领团队攻克了一个又一个技术难题,成功研发出了具有竞争力的微电子产品,并逐渐在市场上获得了认可。

问答坊 | AI 解惑

大功率LED封装的要点

replyreload += \',\' + 380115;Timson,如果您要查看本帖隐藏内容请回复…

查看全部问答>

contact ID 协议 (安定宝协议)

我们公司现在做一个报警控制器,需要支持Contact Id 协议,但自己以前没有做过,而且那个协议文档写的不是很清楚,希望以前做过的能指点一下,或者也可以和我们公司合作(你以前做过硬件电路和软件实现)。 QQ:275556641 Email: yuangangaaa@163 ...…

查看全部问答>

wince6.0 支持TransparentBlt吗?

wince6.0 支持TransparentBlt吗?我在调试的时候该函数返回一直是0,不知道是为什么?各位大虾指导下!…

查看全部问答>

PB 下如何实现USB键盘和鼠标

想问一下Core OS\\Core OS Services\\USB Host Support下的内容已经Add to OS Desgin,但USB的键盘和鼠标还是没有用,并且开机时蜂鸣器长响,是什么原因? 另外,Device Drivers\\USB Function 和USB Host 之间的区别?…

查看全部问答>

创意手工LED发光怀表

LED怀表”这是一个DIY作品,作者觉得这可能是第一个LED怀表,来自Paul,其对祖父留下来的一个旧怀表进行改造,当然能用的也只有怀表的外壳。   怀表里面一共安装了133个LED,每一个都是手工焊接而成,LED可显示60秒、60分钟和12小时,另外还有一 ...…

查看全部问答>

怎样破解iarewarm5.30?

我下的iar ewarm5.30照别个说的方法怎么补能破解呢? 未命名.JPG (53.53 KB) 下载次数:7 2010-6-15 20:52 …

查看全部问答>

请问IARSTM8的中断程序怎么搞?最好能提供个范本,谢谢!

                                 请问IAR STM8的中断程序怎么搞?最好能提供个范本,谢谢!…

查看全部问答>

帮忙看看2407的程序吧

帮忙看看2407点灯的程序吧,为什么lacc    #8语句中输入>8的数时,在单步调试运行时,延时程序就跳不出来了,好像死机了似的。可是lacc    #8语句中输入<=8的数就什么问题都没有。快帮忙看看吧,我都在这个问题上停留好长 ...…

查看全部问答>

谈喇叭阻抗问题。

  大家都知道喇叭最常见的是8欧阻抗,用万用表量,通常在8欧左右,小功率的就不说了。如上图这个8欧30W喇叭,满载电压约为15.5V 1.9365A,自损损耗就是30W,这样一算我就怀疑了,它上面标的8欧30W,30W是输出功率还是自身损耗呢???(喇 ...…

查看全部问答>

选择深度睡眠还是深度掉电?

对于LPC1114,想让它平时不工作,当按键按下时打醒它,进而用它控制电路其它部分上电,不知道平时让它在深度睡眠模式还是深度掉电模式啊?…

查看全部问答>