历史上的今天
返回首页

历史上的今天

今天是:2024年12月16日(星期一)

正在发生

2021年12月16日 | STM32 NRF24L01实现无线传输

2021-12-16 来源:eefocus

前言

STM32下NRF24L01实现无线传输


一、原理图

1.STM32F103C8T6

在这里插入图片描述

2.NRF24L01

NRF24L01是 nordic 的无线通信芯片,它具有以下特点:


1) 2.4G 全球开放的 ISM 频段(2.400 - 2.4835GHz),免许可证使用;

2)最高工作速率 2Mbps,高校的 GFSK 调制,抗干扰能力强;

3) 125 个可选的频道,满足多点通信和调频通信的需要;

4)内置 CRC 检错和点对多点的通信地址控制;

5)低工作电压(1.9~3.6V),待机模式下状态为 26uA;掉电模式下为 900nA;

6)可设置自动应答,确保数据可靠传输;

7)工作于EnhancedShockBurst 具有Automatic packet handling,Auto packet transaction handling ,可以实现点对点或是 1 对 6 的无线通信,速度可以达到 2M(bps),具有可选的内置包应答机制,极大的降低丢包率。

8)通过 SPI 总线与单片机进行交互,最大通信速率为10Mbps;


二、Keil代码

1.SPI_NRF2401.C

#include "Struct.h"


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

宏定义

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

#define NRF_CE_GPIO GPIOC

#define NRF_CE_Pin GPIO_Pin_14

#define NRF_CSN_GPIO GPIOC

#define NRF_CSN_Pin GPIO_Pin_13

#define NRF_IRQ_GPIO GPIOC

#define NRF_IRQ_Pin GPIO_Pin_15


#define NRF_CE_H    NRF_CE_GPIO ->BSRR = NRF_CE_Pin  //CE高电平

#define NRF_CE_L    NRF_CE_GPIO ->BRR  = NRF_CE_Pin  //CE低电平

#define NRF_CSN_H  NRF_CSN_GPIO->BSRR = NRF_CSN_Pin //CSN高电平

#define NRF_CSN_L  NRF_CSN_GPIO->BRR  = NRF_CSN_Pin //CSN高电平

#define NRF_IRQ_Read NRF_IRQ_GPIO->IDR  & NRF_IRQ_Pin //IRQ读数据

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

变量定义

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

uint8_t NRF24L01_RXDATA[32];//nrf24l01接收到的数据

uint8_t NRF24L01_TXDATA[32];//nrf24l01需要发送的数据

static uint8_t TX_ADDRESS[5]= {0x1A,0x2A,0x3A,0x4A,0x5A};//本地地址

static uint8_t RX_ADDRESS[5]= {0x1A,0x2A,0x3A,0x4A,0x5A};//接收地址

static uint16_t Nrf_Erro=0;


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

函数原型: void SPI2_Init(void)

功  能: 初始化SPI总线

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

void SPI2_Init(void)

{

SPI_InitTypeDef SPI_InitStructure; 

GPIO_InitTypeDef GPIO_InitStructure;

EXTI_InitTypeDef EXTI_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

//配置SCK,MISO,MOSI引脚  

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; 

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用功能 

GPIO_Init(GPIOB, &GPIO_InitStructure);

//配置CE引脚

GPIO_InitStructure.GPIO_Pin = NRF_CE_Pin; 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; 

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

GPIO_Init(NRF_CE_GPIO, &GPIO_InitStructure);

//配置CSN引脚

GPIO_InitStructure.GPIO_Pin = NRF_CSN_Pin; 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; 

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

GPIO_Init(NRF_CSN_GPIO, &GPIO_InitStructure);

//配置IRQ引脚

GPIO_InitStructure.GPIO_Pin = NRF_IRQ_Pin; 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入

GPIO_Init(NRF_IRQ_GPIO, &GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource15);

    EXTI_InitStructure.EXTI_Line=EXTI_Line15;

    EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//外部中断

    EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发

    EXTI_InitStructure.EXTI_LineCmd=ENABLE;

    EXTI_Init(&EXTI_InitStructure);

NRF_CSN_H; //禁止NRF器件

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线全双工 

SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主模式 

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据大小8位 

SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟极性,空闲时为低 

SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //第1个边沿有效,上升沿为采样时刻 

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;  //NSS信号由软件产生 

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //8分频,9MHz 

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前 

SPI_InitStructure.SPI_CRCPolynomial = 7; 

SPI_Init(SPI2, &SPI_InitStructure); 

   

SPI_Cmd(SPI2, ENABLE);//使能 SPI1

// PrintString("rn SPI2     初始化完成!");

}

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

函数原型: uint8_t SPI_RW(uint8_t data) 

功  能: SPI总线读写

返 回 值: 返回SPI总线读取数据

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

uint8_t SPI_RW(uint8_t data) 

while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);//当SPI发送缓冲器非空时等待  

SPI_I2S_SendData(SPI2, data);//通过SPI总线发送一字节数据

while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);//当SPI接收缓冲器为空时等待

return SPI_I2S_ReceiveData(SPI2);

}


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

函数原型: uint8_t NRF_Write_Reg(uint8_t reg, uint8_t value)

功    能: NRF写寄存器

返 回 值: NRF写寄存器返回值

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

uint8_t NRF_Write_Reg(uint8_t reg, uint8_t value)

{

uint8_t status;

NRF_CSN_L; //选通NRF器件

status = SPI_RW(reg);//写寄存器地址

SPI_RW(value); //写数据

NRF_CSN_H; //禁止NRF器件

return status;

}


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

函数原型: uint8_t NRF_Read_Reg(uint8_t reg)

功    能: NRF读寄存器

返 回 值: 寄存器数据

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

uint8_t NRF_Read_Reg(uint8_t reg)

{

uint8_t reg_val;

NRF_CSN_L; //选通NRF器件 

SPI_RW(reg); //写寄存器地址

reg_val = SPI_RW(0);//读取该寄存器返回数据

NRF_CSN_H; //禁止NRF器件 

    return reg_val;

}


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

函数原型: uint8_t NRF_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)

功    能: NRF写缓冲区

返 回 值: NRF写缓冲区返回值

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

uint8_t NRF_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)

{

uint8_t i;

uint8_t status;

NRF_CSN_L; //选通NRF器件 

status = SPI_RW(reg);//写寄存器地址 

for(i=0; i {

SPI_RW(pBuf[i]);//写数据 

}

NRF_CSN_H; //禁止NRF器件

    return status;

}


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

函数原型: uint8_t NRF_Read_Buff(uint8_t reg, uint8_t *pBuf, uint8_t uchars)

功    能: NRF读缓冲区

返 回 值: 缓冲区数据

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

uint8_t NRF_Read_Buff(uint8_t reg, uint8_t *pBuf, uint8_t uchars)

{

uint8_t i;

uint8_t status;

NRF_CSN_L; //选通NRF器件 

status = SPI_RW(reg);//写寄存器地址

for(i=0; i {

pBuf[i] = SPI_RW(0);//读取返回数据

}

NRF_CSN_H; //禁止NRF器件

    return status;

}


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

函数原型: void NRF24L01_Check(void)

功    能: 检查NRF器件是否正常

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

void NRF24L01_Check(void)

uint8_t buf[5]; 

uint8_t i; 

//写入5个字节的地址 

NRF_Write_Buf(NRF_WRITE_REG+TX_ADDR,TX_ADDRESS,5); 

//读出写入的地址 

NRF_Read_Buff(TX_ADDR,buf,5); 

//比较

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

if(buf[i]!=TX_ADDRESS[i]) 

break; 

// if(i==5)

// PrintString("rn NRF24L01 初始化成功!");

// else

// PrintString("rn NRF24L01 初始化失败!");

}


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

函数原型: static void NRF24L01_Set_TX(void)

功    能: 将NRF24L01设置为发送模式

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

static void NRF24L01_Set_TX(void)

{

NRF_CE_L;

NRF_Write_Reg(NRF_WRITE_REG + CONFIG,0x0E);//发送

NRF_CE_H;

}


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

函数原型: static void NRF24L01_Set_RX(void)

功    能: 将NRF24L01设置为接收模式

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

static void NRF24L01_Set_RX(void)

{

NRF_CE_L;

NRF_Write_Reg(NRF_WRITE_REG + CONFIG,0x0F);//接收

NRF_CE_H;

}


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

函数原型: void NRF_Send_TX(uint8_t * tx_buf, uint8_t len)

功    能: NRF2401发送数据包

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

void NRF_Send_TX(uint8_t * tx_buf, uint8_t len)

{

NRF24L01_Set_TX();

NRF_CE_L;//进入待机模式1

NRF_Write_Buf(WR_TX_PLOAD, tx_buf, len);//装载数据

NRF_CE_H;//设置CE为高,启动发射。CE高电平持续时间最小为10us

}

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

函数原型: void NRF24L01_Init(uint8_t Chanal,uint8_t Mode)

功    能: NRF24L01初始化

参    数: Chanal,RF通道

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

void NRF24L01_Init(uint8_t Chanal,uint8_t Mode)

{

NRF_CE_L;

NRF_Write_Reg(FLUSH_TX,0xff);//清空发送缓冲区

NRF_Write_Reg(FLUSH_RX,0xff);//清空接收缓冲区

NRF_Write_Buf(NRF_WRITE_REG + TX_ADDR,   TX_ADDRESS,5); //写TX节点地址 

NRF_Write_Buf(NRF_WRITE_REG + RX_ADDR_P0,RX_ADDRESS,5); //写RX节点地址 


NRF_Write_Reg(NRF_WRITE_REG + EN_AA,     0x01); //使能通道0的自动应答 

NRF_Write_Reg(NRF_WRITE_REG + EN_RXADDR, 0x01); //使能通道0的接收地址 

NRF_Write_Reg(NRF_WRITE_REG + SETUP_RETR,0x1a); //设置自动重发间隔时间:500us;最大自动重发次数:10次 

NRF_Write_Reg(NRF_WRITE_REG + RF_CH,   Chanal); //设置RF通道为CHANAL

NRF_Write_Reg(NRF_WRITE_REG + RX_PW_P0,    32); //设置通道0的有效数据宽度

NRF_Write_Reg(NRF_WRITE_REG + RF_SETUP,  0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启

if(Mode==TX)

NRF_Write_Reg(NRF_WRITE_REG + CONFIG,0x0E);//发送

else if(Mode==RX)

NRF_Write_Reg(NRF_WRITE_REG + CONFIG,0x0F);//接收

NRF_CE_H;

}


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

函数原型: static void NRF24L01_Analyse(void)

功    能: 分析NRF24L01收到的数据帧

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

static void NRF24L01_Analyse(void)

{

uint8_t sum = 0,i;

uint8_t len = NRF24L01_RXDATA[3] + 5;

//uint8_t i=0;

for(i=3;i sum ^= NRF24L01_RXDATA[i];

if( sum!=NRF24L01_RXDATA[len] ) return; //数据校验

推荐阅读

史海拾趣

Datasensor公司的发展小趣事

1978年,DATALOGIC公司成功研制出欧洲第一台条码扫描器,并将此产品应用于商业领域。这一创新不仅展示了公司在光电技术领域的实力,也为其在工业自动化领域赢得了良好的声誉。条码扫描器的成功推出,为公司带来了可观的收益,也为公司后续的技术研发和市场拓展提供了强有力的支持。

Cypress(赛普拉斯)公司的发展小趣事

近年来,随着汽车电子、物联网等应用领域的快速发展,Cypress也制定了新的发展战略。公司将以MCU+存储+USB为核心产品,为汽车电子、物联网等应用领域提供完整的嵌入式系统解决方案。为了实现这一目标,Cypress将继续加大研发投入,推动技术创新和产品升级。同时,公司还将加强与国际合作伙伴的合作,共同开拓新的市场和应用领域。

Fuji Electric Co Ltd公司的发展小趣事

Cypress在USB技术领域取得了显著的成就。自1996年开始深耕USB产品以来,Cypress逐渐成为了USB领域内的领先者。公司推出的EZ-PD系列产品是业界第一个支持USB PD 3.0供电规范的解决方案,赢得了市场的广泛认可。此外,Cypress还不断推出新的USB产品和技术,推动了USB技术的不断发展和创新。

Appointech Inc公司的发展小趣事

为了进一步扩大市场份额,Appointech Inc公司开始积极拓展海外市场。通过与国际知名企业的合作,公司成功打入国际市场,产品销量大幅提升。同时,公司还积极参加国际电子展会和交流活动,与全球同行建立了广泛的合作关系,为公司的长远发展奠定了坚实的基础。

中科芯(CKS)公司的发展小趣事

中科芯自成立以来,一直致力于集成电路技术的研发与创新。在早期的发展阶段,公司成功研制了我国首块超大规模集成电路,这一里程碑式的突破标志着中国集成电路技术达到了国际先进水平。这一成果不仅填补了国内在该领域的空白,也为中科芯赢得了广泛的国际声誉,为后续的技术突破奠定了坚实基础。

CLAIREX公司的发展小趣事

为了进一步扩大市场份额,Clairex开始积极寻求与各大企业的合作。他们与多家知名航空公司、电子设备制造商等建立了长期稳定的合作关系,为其提供高质量的电子部件和解决方案。同时,Clairex还积极参加各类行业展会和交流活动,与业界同行分享经验、探讨合作。这些举措不仅提升了Clairex的品牌知名度,也为其带来了更多的商业机会。

问答坊 | AI 解惑

WIFI如何设置静态IP

目前使用动态IP能连接上无线路由并上网,请问如何设置静态IP,是用WINCE的API还是只要把IP写入到注册表指定位置,系统自己处理?如果用API,需要哪些API?如果写注册表,具体写到哪,写哪些数据? …

查看全部问答>

请问是不是所有的MCU都可以用hex或者bin两种文件作为下载文件

是否大部分MCU都可以用这两种文件作为烧写文件,除了个别厂家自己搞得一些其他格式文件 还有,我如何知道这个MCU是支持哪种文件的烧写格式的? 另:除了HEX是16进制文件,BIN是二进制文件这个区别外,两者在使用上还有其他的不同吗? 望高手指点 ...…

查看全部问答>

请问如何在WinCE中得到当前的函数调用堆栈

请问如何在WinCE操作系统下,在程序中得到当前的函数调用堆栈?谢谢! 开发环境 EVC,PlatformBuilder 微处理器 Renesas SH-4 …

查看全部问答>

冯诺依曼结构与哈佛结构区别

请问冯诺依曼结构与哈佛结构的区别? 通过Google查询,有人如下解释: 区别是地址空间和数据空间分开与否 冯诺依曼结构数据空间和地址空间不分开 哈佛结构数据空间和地址空间是分开的 一般DSP都是采用改进型哈佛结构,就是分开的数据空间和地址空 ...…

查看全部问答>

XSCALE pxa270的dma采集图像的问题,请帮忙解答

一个9HZ的摄像头通过并口输出一个14bit灰度图像,请问如何通过Xscale PXA270 的DMA 把数据放到内存里呢??…

查看全部问答>

各位在使用MDK11时有没有发现什么问题?

我在使用MDK11开发STR912的片子时,常出现仿真时c语言环境下执行顺序乱跳,根本没有按照设计者的顺序执行程序,这是怎么回事?以前使用MDK的早期版本开发NXP的片子时可从来没出现过这样的问题啊。…

查看全部问答>

电子竞赛TI杯

对于即将到来的TI杯,大伙来讨论讨论吧....…

查看全部问答>

新手报到

本人最近开始学习单片机,感觉挺有意思的。希望得到大家的帮助,不胜感激。 最近有一个问题,就是单片机烧写进程序后蜂鸣器一直响,感觉引脚定义没错的,怎么回事呢?…

查看全部问答>