NRF24L01模块STM32通信-通信初始化

目录

前言

一、IO口初始化

二、模拟SPI的基础代码

1.一些代码的宏定义

2.起始信号

3.CS,SCK,MOSI操作

4.MISO,IRQ操作

三.中间层代码

1.字节的输入和读取

2.写操作

3.读操作

四.应用层代码

1.24L01的检测

2.在main函数进行简单验证

3.24L01宏定义的代码

总结


前言

环境:

芯片:STM32F103C8T6

Keil:V5.24.2.0

模块:NRF24L01


一、IO口初始化

根据:NRF24L01模块STM32通信-调试前言-CSDN博客

需要初始6个IO:4个输出,2个输入

我初始对应的IO口如下;

//输出
CSN		->PA3
CE		->PA4
MOSI	->PA7
SCK		->PA5	
//输入	
IRQ		->PB1
MISO	->PA6

IO口初始化,包含了OLED和其他的初始化代码.

void Gpio_Init(void)
{
/* 输出
CSN		->PA3//
CE		->PA4
MOSI	->PA7
SCK		->PA5	
	输入	
IRQ		->PB1
MISO	->PA6
*/	
//output
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);

//input
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

}

二、模拟SPI的基础代码

1.一些代码的宏定义

#define SPI_CS_PROT			GPIOA	//CS接线引脚通道,	CSN
#define SPI_CS_PIN			GPIO_Pin_3

#define SPI_DO_PROT			GPIOA	//D0接线引脚通道,	MOSI
#define SPI_DO_PIN			GPIO_Pin_7

#define SPI_SLK_PROT		GPIOA	//CL接线引脚通道,	SCK
#define SPI_SLK_PIN			GPIO_Pin_5

#define SPI_DI_PROT			GPIOA	//DI接线引脚通道,	MISO
#define SPI_DI_PIN			GPIO_Pin_6

#define SPI_IRQ_PROT		GPIOB	//DI接线引脚通道,	MISO
#define SPI_IRQ_PIN			GPIO_Pin_1

#define MYSPI_W_CS(x) 		GPIO_WriteBit(SPI_CS_PROT,SPI_CS_PIN,(BitAction)(x))//对CS线进行操作
#define MYSPI_W_DI(x) 		GPIO_WriteBit(SPI_DI_PROT,SPI_DI_PIN,(BitAction)(x))//对DI线进行操作
#define MYSPI_W_DO(x) 		GPIO_WriteBit(SPI_DO_PROT,SPI_DO_PIN,(BitAction)(x))//对DO线进行操作
#define MYSPI_W_SLK(x) 		GPIO_WriteBit(SPI_SLK_PROT,SPI_SLK_PIN,(BitAction)(x))//对SLK线进行操作
#define NRF24L01_CE(x) 		GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(x))//对CE线进行操作

2.起始信号

void MySPI_Start(void)
{
	MYSPI_W_CS(0);		//拉低为开始信号
}

void MySPI_Stop(void)
{
	MYSPI_W_CS(1);		//拉高为结束信号
}

3.CS,SCK,MOSI操作

void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, SPI_CS_PIN, (BitAction)BitValue);  
}

void MySPI_W_SCK(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, SPI_SLK_PIN, (BitAction)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, SPI_DO_PIN, (BitAction)BitValue);
}

4.MISO,IRQ操作

uint8_t MySPI_Read_MISO(void)
{
	return GPIO_ReadInputDataBit(SPI_DI_PROT, SPI_DI_PIN);
}

uint8_t MySPI_Read_IRQ(void)
{
	return GPIO_ReadInputDataBit(SPI_IRQ_PROT, SPI_IRQ_PIN);
}

三.中间层代码

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_Read_MISO() == 1){ByteReceive |= (0x80 >> i);}
		MySPI_W_SCK(0);
	}
	return ByteReceive;
}

关于这段代码更详细的解说可以观看江科大的视频SPI部分.

2.写操作

uint8_t NRF24l01_write_buf(uint8_t reg, uint8_t *pbuf, uint8_t len)
{
    uint8_t status, i;
                     
	MySPI_Start();					  /* 使能SPI传输 */
    status = MySPI_SwapByte(reg); /* 发送寄存器值(位置),并读取状态值 */

    for (i = 0; i < len; i++)
    {
        MySPI_SwapByte(*pbuf++);  /* 写入数据 */
    }
    MySPI_Stop();					 /* 关闭SPI传输 */                
    
    return status;                      /* 返回读到的状态值 */

}

3.读操作

uint8_t NRF24l01_read_buf(uint8_t reg, uint8_t *pbuf, uint8_t len)
{
    uint8_t status, i;    
                    
	MySPI_Start();  /* 使能SPI传输 */
    status = MySPI_SwapByte(reg);         /* 发送寄存器值(位置),并读取状态值 */

    for (i = 0; i < len; i++)
    {
        pbuf[i] = MySPI_SwapByte(0X55);   /* 读出数据 */		
    } 
	
    return status;                              /* 返回读到的状态值 */
}

四.应用层代码

1.24L01的检测

/**
 * @brief       检测24L01是否存在
 * @param       无
 * @retval      0, 成功; 1, 失败;
 */
uint8_t NRF24l01_check(void)
{
    uint8_t buf[5] = {0XA5, 0XA5, 0XA5, 0XA5, 0XA5};
    uint8_t i;
    NRF24l01_write_buf(NRF_WRITE_REG + 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 */
}

关于其中的一些宏定义,代码放在本文末.

2.在main函数进行简单验证

void MY24L01_Init(void)//对模块和通信线的前期操作
{

NRF24L01_CE(0);
MYSPI_W_CS(1);
MYSPI_W_SLK(0);

}

while(1)
{
	while (NRF24l01_check())    /* 检查NRF24L01是否在线 */
    {
        OLED_ShowString(16, 18, "NRF24l01 NGNGNG", OLED_6X8);
		OLED_Update();
    }
	
	OLED_ShowString(32, 18, "GOOD", OLED_6X8);
	OLED_Update();
}

可以使用OLED进行显示,当然因为结果只有0或1,所以也可以采用其他方式进行验证,如LED的亮灭

如果代码正确则可以进行后面的代码书写.

3.24L01宏定义的代码

/******************************************************************************************/
/* NRF24L01寄存器操作命令 */
#define NRF_READ_REG    0x00    /* 读配置寄存器,低5位为寄存器地址 */
#define NRF_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    /* 空操作,可以用来读状态寄存器 */

/* SPI(NRF24L01)寄存器地址 */
#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 NRF_FIFO_STATUS 0x17    /* FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留 */
                                /* bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环; */

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

总结

本文代码并未涉及很多的交互动作,只是验证基础代码和IO口的连接正确.操作相对简单,方便及时对代码进行验证,查缺补漏,方便后面代码的调试

相关推荐
神一样的老师31 分钟前
【行空板K10】获得当前所在城市及JSON库的移植:
单片机·嵌入式硬件·json
点灯之王5 小时前
基于32单片机的智能语音家居
单片机·嵌入式硬件
木宁kk6 小时前
嵌入式 TCP/UDP/透传/固件
单片机·嵌入式硬件·面试
跳河轻生的鱼9 小时前
海思Linux(一)-Hi3516CV610的开发-ubuntu22_04环境创建
linux·单片机·学习·华为
pirateeee10 小时前
STM32G070CB的USART1_RX引脚
stm32·单片机·嵌入式硬件
韦东山11 小时前
NUTTX移植到STM32
stm32·单片机·嵌入式硬件·nuttx
桃子丫11 小时前
Howland电流源
嵌入式硬件·能源·智能硬件·硬件
老薛爱吃大西瓜11 小时前
MPU中断处理
c语言·单片机·嵌入式硬件
wenchm11 小时前
细说STM32F407单片机FSMC连接外部SRAM的方法及HAL驱动
stm32·单片机·嵌入式硬件
厉昱辰11 小时前
一文读懂51单片机的中断系统
stm32·单片机·51单片机