基于STM32F103C8T6标准库的NRF24L01模块通信

        在学习完STM32一些标准外设后,我开始尝试一些不同的模块使用,NRF24L01算是第一个真正自己根据手册来编写的完整代码的模块,还是挺有纪念意义的。整个项目分为两个板块,SPI通信层和NRF24L01驱动层,我将TX和RX的初始化都统一放在了NRF24L01的驱动层,这样使用起来更方便,需要发送就初始化TX,需要接收就初始化RX即可。

实验现象如下

        进入正题,首先说说参考的资料吧,如只需要参考代码,可以直接跳到代码部分

在功能文档中,详细给出了RX,TX的初始化步骤,如下

然后在模块说明书中你可以找到这个模块的引脚分布,我在接线时就参考这张图

       

 除此之外你还需要到中文说明书中找到寄存器地址

以及指令操作

还有工作模式的选择

        通过上面这些资料,基本上就能完成NRF24L01模块最简单的通信了,接下来就详细介绍代码部分

        首先是SPI通信层代码,因为只需要将两个模块进行通信,对传输速率可以说没有什么要求,所以我选择使用软件模拟SPI,具体代码如下:

引脚的电平操作:

void MySPI_W_CSN(uint8_t BitValue)
{
	GPIO_WriteBit(CSN_Port, CSN_Pin, (BitAction)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue)
{
	GPIO_WriteBit(SCK_Port, SCK_Pin, (BitAction)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(MOSI_Port, MOSI_Pin, (BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)
{
	return GPIO_ReadInputDataBit(MISO_Port, MISO_Pin);
}

初始化SPI,其实也就是初始化I/O口:

void MySPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//输出引脚配置为推挽输出
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = MOSI_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(MOSI_Port, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = SCK_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(SCK_Port, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = CSN_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(CSN_Port, &GPIO_InitStructure);
	
	//输入引脚配置为上拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = MISO_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(MISO_Port, &GPIO_InitStructure);
	
	MySPI_W_CSN(1);
	MySPI_W_SCK(0);
}

然后就是最重要的部分,SPI的三个基本时序:

void MySPI_Start(void)
{
	MySPI_W_CSN(0);
}

void MySPI_Stop(void)
{
	MySPI_W_CSN(1);
}

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = 0x00;
	
	for (i = 0; i < 8; i ++)
	{
		MySPI_W_MOSI(ByteSend & (0x80 >> i));
		MySPI_W_SCK(1);
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
		MySPI_W_SCK(0);
	}
	
	return ByteReceive;
}

        接下来就是NRF24L01的驱动层代码了

收发地址的配置以及NRF24L01引脚电平操作

//收发地址
uint8_t T_ADDR[]={0x01,0x01,0x01,0x01,0x01};
uint8_t R_ADDR[]={0x01,0x01,0x01,0x01,0x01};	


//NRF24L01的引脚电平操作
void NRF24L01_CE(uint8_t Value)
{
	GPIO_WriteBit(CE_Port,CE_Pin,(BitAction)Value);
}

uint8_t NRF24L01_IRQ(void)
{
	return GPIO_ReadInputDataBit(IRQ_Port,IRQ_Pin);
}

NRF24L01的引脚初始化

void NRF24L01_IO_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = CE_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(CE_Port, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = IRQ_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(IRQ_Port, &GPIO_InitStructure);
	
	MySPI_Init();
}

四种对NRF24L01寄存器的读写操作


uint8_t NRF24L01_Write_Reg(uint8_t Reg,uint8_t Value)
{
	uint8_t Status;	
   	MySPI_Start();                  
  	Status = MySPI_SwapByte(Reg);	 
  	MySPI_SwapByte(Value);      	
  	MySPI_Stop();                  	   
  	return(Status);       			
}

uint8_t NRF24L01_Read_Reg(uint8_t Reg)
{
	uint8_t Reg_Val;	    
 	MySPI_Start();         					
  	MySPI_SwapByte(Reg);   			
  	Reg_Val=MySPI_SwapByte(NOP);	
  	MySPI_Stop();          				    
  	return(Reg_Val);           		
}


uint8_t NRF24L01_Read_Buf(uint8_t Reg,uint8_t *Buf,uint8_t Len)
{
	uint8_t Status,uint8_t_ctr;	       
  	MySPI_Start();           							
  	Status=MySPI_SwapByte(Reg);							   	   
 	for(uint8_t_ctr=0;uint8_t_ctr<Len;uint8_t_ctr++)	
	{
		Buf[uint8_t_ctr]=MySPI_SwapByte(NOP);
	}
  	MySPI_Stop();       								
  	return Status;        								
}

uint8_t NRF24L01_Write_Buf(uint8_t Reg, uint8_t *Buf, uint8_t Len)
{
	uint8_t Status,uint8_t_ctr;	    
 	MySPI_Start();          
  	Status = MySPI_SwapByte(Reg);
  	for(uint8_t_ctr=0; uint8_t_ctr<Len; uint8_t_ctr++)	{
		MySPI_SwapByte(Buf[uint8_t_ctr]);
	}			 
  	MySPI_Stop();       
  	return Status;         
}

TX模式初始化

void NRF24L01_TX_Init(void)
{
	NRF24L01_CE(0);
	
	//NRF24L01引脚初始化
	NRF24L01_IO_Init();
	
	//写TX节点的地址
	NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,T_ADDR,5);
	//写RX节点的地址
	NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0,R_ADDR,5);
	//使能AUTO ACK(自动应答)
	NRF24L01_Write_Reg(W_REGISTER+EN_AA,0x01);
	//使能PIPE O(通道0)
	NRF24L01_Write_Reg(W_REGISTER+EN_RXADDR,0x01);
	//配置自动重发次数
	NRF24L01_Write_Reg(W_REGISTER+SETUP_RETR,0x1A);
	//选择通信频率
	NRF24L01_Write_Reg(W_REGISTER+RF_CH,0x00);//2.4GHz
	//配置NRF24L01的基本参数以及切换工作模式
	NRF24L01_Write_Reg(W_REGISTER+CONFIG,0x0E);
	
	NRF24L01_CE(1);
}

RX模式初始化

void NRF24L01_RX_Init(void)
{
	NRF24L01_CE(0);
	
	NRF24L01_IO_Init();
	
	//写RX节点的地址
	NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0,R_ADDR,5);
	//使能AUTO ACK(自动应答)
	NRF24L01_Write_Reg(W_REGISTER+EN_AA,0x01);
	//使能PIPE 0(通道0)
	NRF24L01_Write_Reg(W_REGISTER+EN_RXADDR,0x01);
	//选择通信频率
	NRF24L01_Write_Reg(W_REGISTER+RF_CH,0x00);//2.4GHz
	//选择通道0有效数据宽度
	NRF24L01_Write_Reg(W_REGISTER+RX_PW_P0,32);//32字节
	//配置NRF24L01的基本参数以及切换工作模式
	NRF24L01_Write_Reg(W_REGISTER+CONFIG,0x0F);
	
	NRF24L01_CE(1);
}

NRF24L01接收数据

uint8_t NRF24L01_ReceiveDate(uint8_t* Buf)
{
	uint8_t Flag;
	Flag=NRF24L01_Read_Reg(R_REGISTER+STATUS);
	NRF24L01_Write_Reg(W_REGISTER+STATUS,Flag);
	if(Flag&RX_Flag)
	{
		NRF24L01_Read_Buf(R_RX_PAYLOAD,Buf,32);
		NRF24L01_Write_Reg(FLUSH_RX,NOP);
		Delay_us(150);
		return 0;
	}
	return 1;//没收到任何数据
}

NRF24L01发送数据

uint8_t NRF24L01_SendDate(uint8_t* Buf)
{
	uint8_t Flag;

	NRF24L01_CE(0);
	NRF24L01_Write_Reg(W_REGISTER+CONFIG,0x0E);
	NRF24L01_Write_Buf(W_TX_PAYLOAD,Buf,32);
	NRF24L01_CE(1);
	
	while(NRF24L01_IRQ()==1);
	Flag=NRF24L01_Read_Reg(R_REGISTER+STATUS);
	NRF24L01_Write_Reg(W_REGISTER+STATUS,Flag);
	if(Flag&MAX_TX)
	{
		NRF24L01_Write_Reg(FLUSH_TX,NOP);
		return MAX_TX;
	}
	if(Flag&TX_Flag)
	{
		return TX_Flag;
	}
	return NOP;//其他原因发送失败
}

监测NRF24L01是否存在(来自demo)

uint8_t NRF24L01_Check(void)
{
	uint8_t Buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
	uint8_t i;   	 
	NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,Buf,5);//写入5个字节的地址	
	NRF24L01_Read_Buf(TX_ADDR,Buf,5); //读出写入的地址  
	for(i=0;i<5;i++)
	{
		if(Buf[i]!=0XA5)
			break;	
	}		
	if(i!=5)
		return 1;//检测24L01错误	
	return 0;		 //检测到24L01
}

        到这里,整个NRF24L01的功能就编写完成了,如果你也想像我一样进行简单验证的话,这里也有发送端和接收端的主函数代码

发送端

int main(void)
{
	uint8_t Date[32]={0x1A,0x2B,0x3C,0x4D,0x5E,0x6F};
	
	
	OLED_Init();
	NRF24L01_TX_Init();
	
	
	while (1)
	{
		for(uint8_t i=0;i<100;i++)
	{
		Date[i]++;
		NRF24L01_SendDate(Date);

	}
	}
}

接收端

int main(void)
{
	uint8_t Date[32]={0};

	OLED_Init();
	NRF24L01_RX_Init();
	
	
	while (1)
	{
		if(NRF24L01_IRQ()==0)
		{
		NRF24L01_ReceiveDate(Date);
		}
		
		OLED_ShowHexNum(2,1,Date[0],2);
		OLED_ShowHexNum(2,4,Date[1],2);
		OLED_ShowHexNum(3,1,Date[2],2);
		OLED_ShowHexNum(3,4,Date[3],2);
		OLED_ShowHexNum(4,1,Date[4],2);
		OLED_ShowHexNum(4,4,Date[5],2);
	}
}

        到这里,这篇文章就结束了,感谢各位的观看,如果觉得文章对你有帮助,可以点赞,如果有什么问题,大家也可以留言一起探讨,但请营造一个和谐友好的交流环境。当然,如果发现了什么问题也欢迎各位指正,多谢!

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值