完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
最近在一个项目中用到了nRF24L01这个无线2.4G收发芯片,项目中有主机和分机,默认都是使用数据通道0,主机通过nRF24L01发送数据后,对应地址的分机在收到数据后会返回一个确认数据包给主机(注意:这个确认数据包并不是nRF24L01自动应答时的数据包,而是自定义的一个数据包,说明了就是双方都能进行收发),在经过长时间的摸索之后,终于将接收和发送都调通了,基本的SPI驱动我使用的是正点原子的教程,我是使用的中断法来处理相应的收发工作。
我的软件硬件环境大致如下: 服务端:STM32F103VET6+u/COS-III 从机端:STM32F103RBT6+u/COS-III 库函数是使用的3.5版本的。 在这里我只是列出主机部分的代码,从机上的都是差不多的。 下面是nRF24L01的驱动部分 头文件部分(24l01.h): #ifndef __24L01_H #define __24L01_H #include “config.h” #if nRF24L01_EN 》 0u #include “sys_temp.h” /*--------------------------------------------------------------------------------------------- NRF24L01寄存器操作命令及寄存器地址 -----------------------------------------------------------------------------------------------*/ #define SPI_READ_REG 0x00 //读配置寄存器,低5位为寄存器地址 #define SPI_WRITE_REG 0x20 //写配置寄存器,低5位为寄存器地址 #define RD_RX_PLOAD 0x61 //读RX有效数据,1~32字节 #define WR_TX_PLOAD 0xA0 //写TX有效数据,1~32字节 #define FLUSH_TX 0xE1 //清除TX FIFO寄存器。发射模式下用 #define FLUSH_RX 0xE2 //清除RX FIFO寄存器。接收模式下用 #define REUSE_TX_PL 0xE3 //重新使用上一包数据,CE为高,数据包被不断发送。 #define NOP 0xFF //空操作,可以用来读状态寄存器 #define CONFIG 0x00 //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能; //bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能 #define EN_AA 0x01 //使能自动应答功能 bit0~5,对应通道0~5 #define EN_RXADDR 0x02 //接收地址允许,bit0~5,对应通道0~5 #define SETUP_AW 0x03 //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节; #define SETUP_RETR 0x04 //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us #define RF_CH 0x05 //RF通道,bit6:0,工作通道频率; #define RF_SETUP 0x06 //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益 #define STATUS 0x07 //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,自动重发完成中断 //bit5:数据发送完成中断;bit6:接收数据完成中断; #define MAX_TX 0x10 //达到最大发送次数中断,即自动重发完成中断 #define TX_OK 0x20 //TX发送完成中断,即数据发送完成中断 #define RX_OK 0x40 //接收到数据中断,即数据接收完成中断 #define OBSERVE_TX 0x08 //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器 #define CD 0x09 //载波检测寄存器,bit0,载波检测; #define RX_ADDR_P0 0x0A //数据通道0接收地址,最大长度5个字节,低字节在前 #define RX_ADDR_P1 0x0B //数据通道1接收地址,最大长度5个字节,低字节在前 #define RX_ADDR_P2 0x0C //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; #define RX_ADDR_P3 0x0D //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; #define RX_ADDR_P4 0x0E //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; #define RX_ADDR_P5 0x0F //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; #define TX_ADDR 0x10 //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等 #define RX_PW_P0 0x11 //接收数据通道0有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P1 0x12 //接收数据通道1有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P2 0x13 //接收数据通道2有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P3 0x14 //接收数据通道3有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P4 0x15 //接收数据通道4有效数据宽度(1~32字节),设置为0则非法 #define RX_PW_P5 0x16 //接收数据通道5有效数据宽度(1~32字节),设置为0则非法 #define FIFO_STATUS 0x17 //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留 //bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环; /*--------------------------------------------------------------------------------------------- 24L01的用到的单片机引脚 -----------------------------------------------------------------------------------------------*/ #define NRF24L01_SPI_Periph_CLK RCC_APB2Periph_GPIOB //无线2.4G模块用到的引脚的外设时钟源 #define NRF24L01_SPI_GPIO_SRC GPIOB #define NRF24L01_SPI_SCK_PIN GPIO_Pin_13 //SCK时钟 #define NRF24L01_SPI_MISO_PIN GPIO_Pin_14 //主机输入,从机输出 #define NRF24L01_SPI_MOSI_PIN GPIO_Pin_15 //主机输出,从机输入 #define NRF24L01_CE_PIN GPIO_Pin_10 //24L01芯片使能信号 #define NRF24L01_IRQ_PIN GPIO_Pin_11 //IRQ主机数据输入 #define NRF24L01_CSN_PIN GPIO_Pin_12 //SPI片选 /*--------------------------------------------------------------------------------------------- 24L01芯片使能信号和片选信号操作 -----------------------------------------------------------------------------------------------*/ #define NRF24L01_CE PBout(10) //24L01芯片使能信号 #define NRF24L01_CSN PBout(12) //SPI片选信号 #define NRF24L01_IRQ PBin(11) //IRQ主机数据输入 /*--------------------------------------------------------------------------------------------- 24L01中断线配置,当接收到数据,发送数据,达到最大重发次数时都会触发中断 相应的状态寄存器会置位1,注意需要手动清除中断,写1清零 -----------------------------------------------------------------------------------------------*/ #define NRF24L01_INT_SOURCE_PORT GPIO_PortSourceGPIOB //中断引脚组 #define NRF24L01_INT_IRQ EXTI15_10_IRQn //中断号 #define NRF24L01_STATUS_INT_SOURCE GPIO_PinSource11 //中断源 #define NRF24L01_STATUS_LINE EXTI_Line11 //中断线 #define NRF24L01_STATUS_INT_MODE EXTI_Trigger_Falling //中断触发方式 /*--------------------------------------------------------------------------------------------- 24L01发送接收数据宽度定义 -----------------------------------------------------------------------------------------------*/ #define TX_ADR_WIDTH 5 //5字节的地址宽度 #define RX_ADR_WIDTH 5 //5字节的地址宽度 #define TX_PLOAD_WIDTH 32 //32字节的用户数据宽度 #define RX_PLOAD_WIDTH 32 //32字节的用户数据宽度 /*--------------------------------------------------------------------------------------------- 24L01相关操作函数定义 -----------------------------------------------------------------------------------------------*/ void NRF24L01_Init(void);//初始化 void RX_Mode(void);//配置为接收模式 //void TX_Mode(void);//配置为发送模式(原始定义) void TX_Mode(u8 * addr);//配置为发送模式 static u8 SPIx_ReadWriteByte(u8 TxData); u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 u8s);//写数据区 u8 NRF24L01_Read_Buf(u8 reg, u8 *pBuf, u8 u8s);//读数据区 u8 NRF24L01_Read_Reg(u8 reg); //读寄存器 u8 NRF24L01_Write_Reg(u8 reg, u8 value);//写寄存器 u8 NRF24L01_Check(void);//检查24L01是否存在 u8 NRF24L01_TxPacket(u8 *txbuf);//发送一个包的数据 //u8 NRF24L01_RxPacket(u8 *rxbuf);//接收一个包的数据(原始定义) u8 NRF24L01_RxPacket(u8 *rxbuf, u8 *chl);//接收一个包的数据 #endif /* nRF24L01_EN */ #endif /* __24L01_H */ 源文件部分(24l01.c): /******************** (C) COPYRIGHT 2015 ASTO *************************** @* 文件名 :24l01.c @* 描述 :nRF24L01驱动程序 @* 开发平台:STM32F103VET6主控制MCU @* 硬件连接:SPI2 @* 库版本 :ST3.5.0 @* 作者 : @* 公司网址:《a target=_blank href=“http://www.test.com”》www.test.com《/a》 @* 总部网址:《a target=_blank href=“http://www.test.com”》www.test.com《/a》 **********************************************************************************/ #include “config.h” #if nRF24L01_EN 》 0u #include “sys_temp.h” #include “24l01.h” #include “delay.h” const u8 RX0_Address[RX_ADR_WIDTH]={0x01,0x01,0x01,0x01,0x01}; //接收方通道0地址 /* const u8 RX1_Address[RX_ADR_WIDTH]={0x02,0x20,0x20,0x20,0x20}; //接收方通道1地址 const u8 RX2_Address[1] = {0x03}; //接收方通道2地址 const u8 RX3_Address[1] = {0x04}; //接收方通道3地址 const u8 RX4_Address[1] = {0x05}; //接收方通道4地址 const u8 RX5_Address[1] = {0x06}; //接收方通道5地址 */ /** @函数名称:EXTI_Configuration() @函数功能:nRF24L01的IRQ中断配置(只响应接收数据的中断) @输入:无 @输出:无 @调用:内部调用 **/ static void EXTI_Configuration(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; //中断优先级设置 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = NRF24L01_INT_IRQ; //10-15的中断线共享一个中断处理程序 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优行级 NVIC_Init(&NVIC_InitStructure); //中断线配置EXTI_Line11--》PB11 GPIO_EXTILineConfig(NRF24L01_INT_SOURCE_PORT, NRF24L01_STATUS_INT_SOURCE); //设置中断源引脚 EXTI_InitStructure.EXTI_Line = NRF24L01_STATUS_LINE; //中断线设置 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断方式 EXTI_InitStructure.EXTI_Trigger = NRF24L01_STATUS_INT_MODE;//模式,这里设置为下降沿触发 EXTI_Init(&EXTI_InitStructure); } /** @* 函数名:NRF24L01_SPI_Init() @* 描述 :初始化SPI2端口及基模式,用于操作nRF24L01无线2.4G模块 @* 输入 :无 @* 输出 : 无 @* 调用 :内部调用 */ static void NRF24L01_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; //注意将普通IO作为中断线时必须开启AFIO时钟 RCC_APB2PeriphClockCmd(NRF24L01_SPI_Periph_CLK|RCC_APB2Periph_AFIO, ENABLE);//开启GPIOB的时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//开启SPI2的时钟 /*配置 SPI的SCK, MISO, MOSI引脚,GPIOB^13,GPIOB^14,GPIOB^15 */ GPIO_InitStructure.GPIO_Pin = NRF24L01_SPI_SCK_PIN| NRF24L01_SPI_MISO_PIN | NRF24L01_SPI_MOSI_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用输出功能 GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure); /*GPIOB^10为nRF24L01的CE引脚,CSN引脚GPIOB^12 */ GPIO_InitStructure.GPIO_Pin = NRF24L01_CE_PIN|NRF24L01_CSN_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//通用推挽输出 GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure); /* GIOB^11为nRF24L01的IRQ中断输入引脚 */ GPIO_InitStructure.GPIO_Pin = NRF24L01_IRQ_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮动输入 GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure); SPI_Cmd(SPI2,DISABLE);/*先失能,然后再使能*/ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ; /* 全双工 */ SPI_InitStructure.SPI_Mode = SPI_Mode_Master ; /*当前的设备为主机模式*/ SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low ; /* 时钟极性为低,即SPI空闲时,SCK为低电平 */ SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge ; /* 时钟相位,第一个时钟沿(也就是奇数边沿)捕捉数据 */ SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; /* 数据宽度 */ SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ; /* 低地址存放最高有效字节 */ SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; /*配置片选为软件控制方式*/ SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; /* 时钟分频为8分频,即72MHz/8=9MHz */ SPI_InitStructure.SPI_CRCPolynomial = 7; /* 校验多项式,这个不起作用 */ SPI_Init(SPI2, &SPI_InitStructure); //使能SPI2 SPI_Cmd(SPI2,ENABLE); EXTI_Configuration();//配置中断线 } /** @* 函数名:SPIx_ReadWriteByte() @* 描述 :SPI2 读写一个字节 @* 输入 :TxData:要写入的字节 @* 输出 : 读取到的字节 @* 调用 :内部调用 */ static u8 SPIx_ReadWriteByte(u8 TxData) { /** 原来的实现方式 u8 retry=0; while((SPI2-》SR&1《《1)==0) {//等待发送区空 retry++; if(retry》200)return 0; } SPI2-》DR=TxData; //发送一个byte retry=0; while((SPI2-》SR&1《《0)==0) { //等待接收完一个byte retry++; if(retry》200)return 0; } return SPI2-》DR; //返回收到的数据 */ /* Loop while DR register in not empty */ while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); /* Send byte through the SPI2 peripheral */ SPI_I2S_SendData(SPI2, TxData); /* Wait to receive a byte */ while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET); /* Return the byte read from the SPI bus */ return SPI_I2S_ReceiveData(SPI2); } /** @* 函数名:NRF24L01_Init() @* 描述 :初始化24L01的相关IO口 @* 输入 :无 @* 输出 : 无 @* 调用 :外部板级支持包调用 */ void NRF24L01_Init(void) { NRF24L01_SPI_Init(); //初始化SPI2 NRF24L01_CE=0; //使能24L01 NRF24L01_CSN=1; //SPI片选取消 } /** @* 函数名:NRF24L01_Check() @* 描述 :检测24L01是否存在 @* 输入 :无 @* 输出 : 0:成功,1:失败 @* 调用 :外部板级支持包调用 */ u8 NRF24L01_Check(void) { u8 buf[5]={0x21,0x21,0x21,0x21,0x21}; u8 buf1[5]; u8 i; //SPIx_SetSpeed(SPI_SPEED_8); //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz) NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址。 NRF24L01_Read_Buf(TX_ADDR,buf1, 5); //读出写入的地址 for(i=0;i《5;i++)if(buf1[i]!=0x21)break; //判断读出的数据和写入的数据是否完全一致 if(i!=5)return 1;//检测24L01错误 return 0; //执行到这里,表示成功检测到24L01 } /** @* 函数名:NRF24L01_Write_Reg() @* 描述 :SPI写寄存器 @* 输入 :reg: 指定的寄存器地址 @* value: 要写入的值 @* 输出 : 返回寄存器的状态值 @* 调用 :内部调用 */ u8 NRF24L01_Write_Reg(u8 reg,u8 value) { u8 status; NRF24L01_CSN=0; //使能SPI传输 status =SPIx_ReadWriteByte(reg); //发送寄存器号 SPIx_ReadWriteByte(value); //写入寄存器的值 NRF24L01_CSN=1; //禁止SPI传输 return(status); //返回状态值 } /** @* 函数名:NRF24L01_Read_Reg() @* 描述 :读取SPI寄存器值 @* 输入 :reg: 指定的寄存器地址 @* 输出 : 返回寄存器的状态值 @* 调用 :内部调用 */ u8 NRF24L01_Read_Reg(u8 reg) { u8 reg_val; NRF24L01_CSN = 0; //使能SPI传输 SPIx_ReadWriteByte(reg); //发送寄存器号 reg_val=SPIx_ReadWriteByte(0XFF); //读取寄存器内容 NRF24L01_CSN = 1; //禁止SPI传输 return(reg_val); //返回状态值 } /** @* 函数名:NRF24L01_Read_Buf() @* 描述 :在指定位置读出指定长度的数据 @* 输入 :reg:指定的寄存器位置 @* pBuf:数据指针用来存储读到的数据,一般是数组 @* len:数据长度 @* 输出 : 此次读到的状态寄存器值 @* 调用 :内部调用 */ u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len) { u8 status,u8_ctr; NRF24L01_CSN = 0; //使能SPI传输 status=SPIx_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值 for(u8_ctr=0;u8_ctr《len;u8_ctr++)pBuf[u8_ctr]=SPIx_ReadWriteByte(0xFF);//读出数据 NRF24L01_CSN=1; //关闭SPI传输 return status; //返回读到的状态值 } /** @* 函数名:NRF24L01_Write_Buf() @* 描述 :在指定位置写指定长度的数据 @* 输入 :reg:寄存器(位置) @* pBuf:数据指针 @* len:数据长度 @* 输出 : 此次读到的状态寄存器值 @* 调用 :内部调用 */ u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len) { u8 status,u8_ctr; NRF24L01_CSN = 0; //使能SPI传输 status = SPIx_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值 for(u8_ctr=0; u8_ctr《len; u8_ctr++)SPIx_ReadWriteByte(*pBuf++); //写入数据 NRF24L01_CSN = 1; //关闭SPI传输 return status; //返回读到的状态值 } /** @* 函数名:NRF24L01_TxPacket() @* 描述 :启动NRF24L01发送一次数据 @* 输入 :txbuf:待发送数据首地址 @* 输出 : 发送完成状况 @* 调用 :外部板级支持包调用 */ u8 NRF24L01_TxPacket(u8 *txbuf) { //u8 sta; //SPIx_SetSpeed(SPI_SPEED_8);//spi速度为9Mhz(24L01的最大SPI时钟为10Mhz) NRF24L01_CE=0; NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF 最长32个字节 NRF24L01_CE=1;//启动发送,10微秒后启动发送 return 0xff;//其他原因发送失败 } /** @* 函数名:NRF24L01_RxPacket() @* 描述 :启动NRF24L01接收数据 @* 输入 :rxbuf:用来接收数据的buffer @* chl:接收到数据的通道号 @* 输出 : 0,接收完成;其他,错误代码 @* 调用 :外部板级支持包调用 */ u8 NRF24L01_RxPacket(u8 *rxbuf, u8 *chl) { u8 sta; //SPIx_SetSpeed(SPI_SPEED_8); //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz) sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值 //状态寄存器的bit1-bit3是表示接收到数据的通道号,最大值为6 *chl=(0x0e&sta)》》1; //清除TX_DS或MAX_RT中断标志,注意这里是写1清零,只有清零后设备才能正常通讯 NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,sta); if(sta&RX_OK) { //接收到数据 NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据 NRF24L01_Write_Reg(FLUSH_RX, 0xff);//清除RX FIFO寄存器 return 0; } return 1;//没收到任何数据 } /** @* 函数名:RX_Mode() @* 描述 :该函数初始化NRF24L01到RX模式 @* 设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR @* 当CE变高后,即进入RX模式,并可以接收数据了 @* 输入 :无 @* 输出 : 无 @* 调用 :外部板级支持包调用 */ void RX_Mode(void) { NRF24L01_CE=0; //CE为0进入待机模式 //配置通道的接收 NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)RX0_Address,RX_ADR_WIDTH);//写RX节点地址(通道0),也就是表示用哪个通道接收数据 //======================注意以下被注释掉的内容可作参考和========================================= //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P1,(u8*)RX1_Address,RX_ADR_WIDTH);//写RX节点地址(通道1),也就是表示用哪个通道接收数据 //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P2,(u8*)RX2_Address,1);//写RX节点地址(通道2),也就是表示用哪个通道接收数据 //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P3,(u8*)RX3_Address,1);//写RX节点地址(通道3),也就是表示用哪个通道接收数据 //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P4,(u8*)RX4_Address,1);//写RX节点地址(通道4),也就是表示用哪个通道接收数据 //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P5,(u8*)RX5_Address,1);//写RX节点地址(通道5),也就是表示用哪个通道接收数据 NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA, 0x01); //使能通道0的自动应答 NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址,共有6个通道,高两位保留固定为00 NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,0); //设置RF通信频率 NRF24L01_Write_Reg(SPI_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度 NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x07);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启 NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 NRF24L01_CE = 1; //CE为高,进入接收模式 } /** @* 函数名:TX_Mode() @* 描述 :该函数初始化NRF24L01到TX模式 @* 设置TX地址,写TX数据宽度,设置RX自动应答的地址, @* 填充TX发送数据,选择RF频道,波特率和LNA HCURR,PWR_UP,CRC使能 @* 当CE变高后,即进入RX模式,并可以接收数据了 @* CE为高大于10us,则启动发送 @* 输入 :addr,要发送数据的目标地址 @* 输出 : 无 @* 调用 :外部板级支持包调用 */ void TX_Mode(u8 * addr) { NRF24L01_CE=0; //CE为0进入待机模式 NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR, addr, TX_ADR_WIDTH);//写TX节点地址,也就是接收方的地址(目标地址) //在发送端,数据通道0被用作接收应答信号,因此数据通道0的接收地址必须要与发送端的地址相同以确保收到正确的应答信号 NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)addr, RX_ADR_WIDTH); //先单独测试发送方,看发送是否正常 NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答 NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址 NRF24L01_Write_Reg(SPI_WRITE_REG+SETUP_RETR, 0x1a); //使能自动重发,设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次 NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,0); //设置RF通道为40 NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x07); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启 NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG,0x0e); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断 NRF24L01_CE=1;//CE为高,10us后启动发送 } #endif /******************************************END OF FILE*************************************************/ 中断处理函数(stm32f10x_it.c) #if nRF24L01_EN 》 0u #include “24l01.h” /** @*功能简介:I/O线中断处理函数,处理nRF24L01的数据接收中断,IO口是PB11 @*参数:None @*返回值:None */ void EXTI15_10_IRQHandler(void) { OSIntEnter(); //用于统计中断的嵌套层数,对嵌套层数+1,请注意:这适用于有内核参与的中断 if (EXTI_GetITStatus(NRF24L01_STATUS_LINE)!=RESET) { EXTI_ClearITPendingBit(NRF24L01_STATUS_LINE);//清除中断标志位 if (!GPIO_ReadInputDataBit(NRF24L01_SPI_GPIO_SRC, NRF24L01_IRQ_PIN)) {//如果为低电平表示产生中断 u8 sta; u8 sta2; sta=NRF24L01_Read_Reg(STATUS); //读取nRF24L01状态寄存器的值 sta2=NRF24L01_Read_Reg(FIFO_STATUS);//读取FIFO状态寄存器 //状态寄存器的bit1-bit3是表示接收到数据的通道号,最大值为6 //*chl=(0x0e&sta)》》1; NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志,注意这里是写1清零,只有清零后设备才能正常通讯 if(sta & RX_OK) { //此次中断表示是接收到了数据 OS_ERR err; USART1_SendData((u8*)“Feedback received!”, 19); //直接发信号量给任务,这种方式比使用中介信号量更快,更节省资源 //第二个参数:1. OS_OPT_POST_NONE表明在发布任务信号量之后调用任务调度程序 // 2. OS_OPT_POST_NO_SCHED则OSTaskSemPost后不会调用调度程序 OSTaskSemPost(&nRF24L01_TCB, OS_OPT_POST_NONE, &err); }else if (sta & MAX_TX) { //此次中断表示达到最大重发次数后必须手动清除TX FIFO寄存器 USART1_SendData((u8 *)“Max retransmission reached!”, 28); NRF24L01_Write_Reg(FLUSH_TX,0xff); } //如果数据发送成功,则相应的寄存器值为下列值,可以调试用: //FIFO_STATUS: 0x11 //STATUS: 0x2E else if (sta & TX_OK) { //此次中断表示发送数据成功,无需手动清除TX FIFO寄存器 USART1_SendData((u8 *)“Send data OK!”, 14); RX_Mode(); //数据成功发送后直接转为接收模式 } } } OSIntExit();//对嵌套层数减1,在退出中断前启动任务调度(适用于有u/COS-III内核参与的中断) } #endi 在中断处理函数中,当收到数据的中断产生时,也就是RX_DR位置高时,我会将接收数据的工作交给某个任务去做,这里我使用的是发布信号量的方法,这样可以尽量减少中断处理的时间。这里我把RX_Mode();这句放在中断处理函数中,也就是数据发送完成后将nRF24L01转换为接收模式,也是出于无奈,因为我在函数NRF24L01_TxPacket中如果使用以下语句,就会出现问题: while(NRF24L01_IRQ!=0);//等待发送完成,IRQ变为0后表示发送完成或者达到最大重发次数 后来我就不用这一句,然后让主程序执行其他的语句,当产生中断后(无论怎样都会产生一种中断,要么发送成功,要么达到最大重传次数)再将nRF24L01的模式变为接收模式,不知这样做是否合理,还望看过的朋友指点一下。 nRF24L01_TCB任务控制块的任务代码主函数(接收数据): #if nRF24L01_EN 》 0u /** @* 函数名:Task_nRF24L01_Sendback_Process() @* 描述 : 无线2.4G模块nRF24L01接收反馈数据包处理任务,优先级为4 @* 当Main_Task或USART1_Task或Wi-Fi模块确定是要以无线2.4G方式发送数据给客户端后,会等待客户端设备的回应 @* 以确定此次操作是否成功并同时会启动一个定时器,此任务采用等待信号量方式确认是否有客户端的回复 @* 如果收到回复,则会根据上位机发送数据给主控制MCU的方式将回应数据包回发给上位机。 @* @* 发送数据给上位机的方式有以下几种: @* ***************************** @* * 1. 网络方式 * @* * 2. 串口方式 * @* * 3. Wi-Fi方式 * @* ***************************** @* 输入 :p_arg: 创建任务时赋给任务的参数,该参数可以是任意类型的 @* 输出 : 无 */ void Task_nRF24L01_Sendback_Process(void *p_arg) { OS_ERR err; //用于记录错误代号 //u8 chl=0; //接收到数据的通道号 CPU_TS ts; u8 n24l01_buf[32]; //接收2.4G数据缓冲区 (void)p_arg; //保存创建任务控制块时传递的任务参数 while(NRF24L01_Check()) { //如果检测不到24L01模块则打印出错信息 printf(“No nRF24L01 device is found!rn”); } RX_Mode(); //默认为接收模式 OSTimeDlyHMSM(0, 0, 0, 2, OS_OPT_TIME_HMSM_STRICT,&err); //至少延时130微秒后,nRF24L01模块进入接收状态 while(DEF_ON) { IWDG_Feed(); //每个任务都会调用看门狗程序 //*************************使用中断方式************************************* OSTaskSemPend(0, OS_OPT_PEND_BLOCKING, &ts, //信号量等待的时间 &err); switch(err) { //处理Pend的结果 case OS_ERR_NONE: OSTmrDel(&ClientDeviceTimeout_Tmr,&err); //正确接收到反馈数据包则删除定时器 《strong》《span style=“color:#990000;”》NRF24L01_Read_Buf(RD_RX_PLOAD, n24l01_buf, RX_PLOAD_WIDTH); //从nRF24L01中读取接收到的数据 NRF24L01_Write_Reg(FLUSH_RX, 0xff); //清除RX FIFO接收数据寄存器《/span》《/strong》 switch(snd_type){ //根据上位机发送指令的方式分别处理 #if ETHNET_EN 》 0u case Ethernet: //以太网方式 if (custom_udp_send.cus_pbuf!=NULL) { custom_udp_send.cus_pbuf-》payload=n24l01_buf; //得到反馈数据包 custom_udp_send.cus_pbuf-》tot_len=custom_udp_send.cus_pbuf-》len=sizeof(n24l01_buf); udp_sendto(custom_udp_send.cus_udp_pcb, //使用当前pbuf发送反馈数据包到客户端 custom_udp_send.cus_pbuf, custom_udp_send.cus_ip_addr, custom_udp_send.cus_udp_port); pbuf_free(custom_udp_send.cus_pbuf); //释放缓冲池中的当前pbuf对象 custom_udp_send.cus_pbuf=NULL; } break; #endif #if RS232_EN 》 0u case RS232: //RS232串口方式 USART1_SendData(n24l01_buf, sizeof(n24l01_buf)); break; #endif #if HF_LPB100_EN 》 0u case WiFi: //Wi-Fi方式(需要Wi-Fi模块作为服务端) USART3_SendData(n24l01_buf, sizeof(n24l01_buf)); break; #endif default: break; } break; case OS_ERR_PEND_ABORT: //等待(挂起)状态被其他任务打断 break; case OS_ERR_OBJ_DEL: //内核对象已被删除 break; default: break; } // //*************************原来的方式,使用查询法**************************** // // if(NRF24L01_RxPacket(n24l01_buf, &chl)==0) { //如果正确接收到数据 // //nRF24L01_BUF[31]=chl; //最后一个字节表示模块接收到数据的通道号(暂时不用) // USART1_SendData(n24l01_buf,sizeof(n24l01_buf)); //测试接收到的数据 // OSTmrDel(&ClientDeviceTimeout_Tmr,&err); //正确接收到反馈数据包则删除定时器 // // switch(snd_type){ //根据上位机发送指令的方式分别处理 // #if ETHNET_EN 》 0u // case Ethernet: //以太网方式 // if (custom_udp_send.cus_pbuf!=NULL) { // custom_udp_send.cus_pbuf-》payload=n24l01_buf; //得到反馈数据包 // custom_udp_send.cus_pbuf-》tot_len=custom_udp_send.cus_pbuf-》len=sizeof(n24l01_buf); // udp_sendto(custom_udp_send.cus_udp_pcb, //使用当前pbuf发送反馈数据包到客户端 // custom_udp_send.cus_pbuf, // custom_udp_send.cus_ip_addr, // custom_udp_send.cus_udp_port); // pbuf_free(custom_udp_send.cus_pbuf); //释放缓冲池中的当前pbuf对象 // custom_udp_send.cus_pbuf=NULL; // } // break; // #endif // // #if RS232_EN 》 0u // case RS232: //RS232串口方式 // USART1_SendData(n24l01_buf, sizeof(n24l01_buf)); // break; // #endif // // #if HF_LPB100_EN 》 0u // case WiFi: //Wi-Fi方式(需要Wi-Fi模块作为服务端) // USART3_SendData(n24l01_buf, sizeof(n24l01_buf)); // break; // #endif // // default: // break; // } // } // // OSTimeDlyHMSM(0, 0, 0, 1, OS_OPT_TIME_HMSM_NON_STRICT,&err); //阻塞1毫秒,即延时1个时钟节拍 } } #endif 发送数据的代码就比较简单了,这里只列出部分代码: ##if nRF24L01_EN 》 0u /** @* 函数名:Task_nRF24L01_Sendback_Process() @* 描述 : 无线2.4G模块nRF24L01接收反馈数据包处理任务,优先级为4 @* 当Main_Task或USART1_Task或Wi-Fi模块确定是要以无线2.4G方式发送数据给客户端后,会等待客户端设备的回应 @* 以确定此次操作是否成功并同时会启动一个定时器,此任务采用等待信号量方式确认是否有客户端的回复 @* 如果收到回复,则会根据上位机发送数据给主控制MCU的方式将回应数据包回发给上位机。 @* @* 发送数据给上位机的方式有以下几种: @* ***************************** @* * 1. 网络方式 * @* * 2. 串口方式 * @* * 3. Wi-Fi方式 * @* ***************************** @* 输入 :p_arg: 创建任务时赋给任务的参数,该参数可以是任意类型的 @* 输出 : 无 */ void Task_nRF24L01_Sendback_Process(void *p_arg) { OS_ERR err; //用于记录错误代号 //u8 chl=0; //接收到数据的通道号 CPU_TS ts; u8 n24l01_buf[32]; //接收2.4G数据缓冲区 (void)p_arg; //保存创建任务控制块时传递的任务参数 while(NRF24L01_Check()) { //如果检测不到24L01模块则打印出错信息 printf(“No nRF24L01 device is found!rn”); } RX_Mode(); //默认为接收模式 OSTimeDlyHMSM(0, 0, 0, 2, OS_OPT_TIME_HMSM_STRICT,&err); //至少延时130微秒后,nRF24L01模块进入接收状态 while(DEF_ON) { IWDG_Feed(); //每个任务都会调用看门狗程序 //*************************使用中断方式************************************* OSTaskSemPend(0, OS_OPT_PEND_BLOCKING, &ts, //信号量等待的时间 &err); switch(err) { //处理Pend的结果 case OS_ERR_NONE: OSTmrDel(&ClientDeviceTimeout_Tmr,&err); //正确接收到反馈数据包则删除定时器 《strong》《span style=“color:#990000;”》NRF24L01_Read_Buf(RD_RX_PLOAD, n24l01_buf, RX_PLOAD_WIDTH); //从nRF24L01中读取接收到的数据 NRF24L01_Write_Reg(FLUSH_RX, 0xff); //清除RX FIFO接收数据寄存器《/span》《/strong》 switch(snd_type){ //根据上位机发送指令的方式分别处理 #if ETHNET_EN 》 0u case Ethernet: //以太网方式 if (custom_udp_send.cus_pbuf!=NULL) { custom_udp_send.cus_pbuf-》payload=n24l01_buf; //得到反馈数据包 custom_udp_send.cus_pbuf-》tot_len=custom_udp_send.cus_pbuf-》len=sizeof(n24l01_buf); udp_sendto(custom_udp_send.cus_udp_pcb, //使用当前pbuf发送反馈数据包到客户端 custom_udp_send.cus_pbuf, custom_udp_send.cus_ip_addr, custom_udp_send.cus_udp_port); pbuf_free(custom_udp_send.cus_pbuf); //释放缓冲池中的当前pbuf对象 custom_udp_send.cus_pbuf=NULL; } break; #endif #if RS232_EN 》 0u case RS232: //RS232串口方式 USART1_SendData(n24l01_buf, sizeof(n24l01_buf)); break; #endif #if HF_LPB100_EN 》 0u case WiFi: //Wi-Fi方式(需要Wi-Fi模块作为服务端) USART3_SendData(n24l01_buf, sizeof(n24l01_buf)); break; #endif default: break; } break; case OS_ERR_PEND_ABORT: //等待(挂起)状态被其他任务打断 break; case OS_ERR_OBJ_DEL: //内核对象已被删除 break; default: break; } // //*************************原来的方式,使用查询法**************************** // // if(NRF24L01_RxPacket(n24l01_buf, &chl)==0) { //如果正确接收到数据 // //nRF24L01_BUF[31]=chl; //最后一个字节表示模块接收到数据的通道号(暂时不用) // USART1_SendData(n24l01_buf,sizeof(n24l01_buf)); //测试接收到的数据 // OSTmrDel(&ClientDeviceTimeout_Tmr,&err); //正确接收到反馈数据包则删除定时器 // // switch(snd_type){ //根据上位机发送指令的方式分别处理 // #if ETHNET_EN 》 0u // case Ethernet: //以太网方式 // if (custom_udp_send.cus_pbuf!=NULL) { // custom_udp_send.cus_pbuf-》payload=n24l01_buf; //得到反馈数据包 // custom_udp_send.cus_pbuf-》tot_len=custom_udp_send.cus_pbuf-》len=sizeof(n24l01_buf); // udp_sendto(custom_udp_send.cus_udp_pcb, //使用当前pbuf发送反馈数据包到客户端 // custom_udp_send.cus_pbuf, // custom_udp_send.cus_ip_addr, // custom_udp_send.cus_udp_port); // pbuf_free(custom_udp_send.cus_pbuf); //释放缓冲池中的当前pbuf对象 // custom_udp_send.cus_pbuf=NULL; // } // break; // #endif // // #if RS232_EN 》 0u // case RS232: //RS232串口方式 // USART1_SendData(n24l01_buf, sizeof(n24l01_buf)); // break; // #endif // // #if HF_LPB100_EN 》 0u // case WiFi: //Wi-Fi方式(需要Wi-Fi模块作为服务端) // USART3_SendData(n24l01_buf, sizeof(n24l01_buf)); // break; // #endif // // default: // break; // } // } // // OSTimeDlyHMSM(0, 0, 0, 1, OS_OPT_TIME_HMSM_NON_STRICT,&err); //阻塞1毫秒,即延时1个时钟节拍 } } #endif |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1752 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1611 浏览 1 评论
1052 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
721 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1666 浏览 2 评论
1926浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
711浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
560浏览 3评论
583浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
544浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-18 17:28 , Processed in 0.835920 second(s), Total 47, Slave 40 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号