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口的连接正确.操作相对简单,方便及时对代码进行验证,查缺补漏,方便后面代码的调试

相关推荐
嗯嗯=20 分钟前
STM32单片机学习篇9
stm32·单片机·学习
小范馆5 小时前
ESP各模组的引脚图-小智接线图
stm32
松涛和鸣5 小时前
DAY63 IMX6ULL ADC Driver Development
linux·运维·arm开发·单片机·嵌入式硬件·ubuntu
想放学的刺客8 小时前
单片机嵌入式试题(第23期)嵌入式系统电源管理策略设计、嵌入式系统通信协议栈实现要点两个全新主题。
c语言·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆8 小时前
【Linux 驱动开发】五. 设备树
linux·arm开发·驱动开发·stm32·嵌入式硬件·mcu·硬件工程
YouEmbedded9 小时前
解码内部集成电路(IIC)与OLED屏
stm32·0.96寸oled·硬件iic·软件模拟iic·图片取模·汉字取模
jghhh0110 小时前
基于上海钜泉科技HT7017单相计量芯片的参考例程实现
科技·单片机·嵌入式硬件
恶魔泡泡糖10 小时前
51单片机外部中断
c语言·单片机·嵌入式硬件·51单片机
意法半导体STM3210 小时前
【官方原创】如何基于DevelopPackage开启安全启动(MP15x) LAT6036
javascript·stm32·单片机·嵌入式硬件·mcu·安全·stm32开发
v_for_van10 小时前
STM32低频函数信号发生器(四通道纯软件生成)
驱动开发·vscode·stm32·单片机·嵌入式硬件·mcu·硬件工程