2201_75749718 2026-04-04 13:33 采纳率: 71.4%
浏览 4
已结题

stm32f103c8t6驱动的nrf24l01远离提示电路

上板后主机连接很不稳定,哪怕主从机挨在一起,也在报警和正常一直跳变,从机好一些。寻找模式也没作用,消音消不了5s,只有按下松开后的瞬间消音(轻触按键),主从机有一方断开后,另一个持续报警,这个没问题,OLED显示情况和状态是对上的。
电路连接情况如下

img


main程序:

#include "stm32f10x.h"
#include "delay.h"
#include "key.h"
#include "beep.h"
#include "oled.h"
#include "led.h"
#include "NRF24L01.h"

// 一块写1,一块写0
#define IS_HOST             1

// 超稳参数(不跳 + 反应快)
#define LOST_COUNT_MAX      25
#define RESTORE_COUNT       10
#define MUTE_DURATION       5000
#define BLINK_INTERVAL      200

typedef enum {
    STATUS_UNPAIR,
    STATUS_NORMAL,
    STATUS_SEARCH,
    STATUS_ALARM,
    STATUS_MUTE
} Status;

Status sta      = STATUS_UNPAIR;
Status last_sta = STATUS_UNPAIR;

uint8_t  paired     = 0;
uint16_t lost_cnt   = 0;
uint32_t mute_timer = 0;
uint32_t blink_timer = 0;
uint8_t  blink_flag  = 0;

// 保存最新收到的指令
uint8_t last_cmd = 0;

int main(void)
{
    KEY_Init();
    Beep_Init();
    LED_Init();
    OLED_Init();
    delay_ms(100);
    NRF24L01_Init();

    OLED_Clear();
    OLED_ShowString(1, 1, IS_HOST ? "HOST" : "SLAVE");
    OLED_ShowString(2, 1, "UNPAIRED");

    while (1)
    {
        // 非阻塞闪烁
        if (blink_timer < 50) blink_timer = BLINK_INTERVAL;
        blink_timer -= 50;
        if (blink_timer == 0) {
            blink_flag ^= 1;
        }

        // ===================== 固定流程:收 → 处理 → 发 =====================
        uint8_t rec_result = NRF24L01_Receive();
        if (rec_result == 1)
        {
            paired = 1;
            lost_cnt = 0;
            last_cmd = NRF24L01_RxPacket[0];
        }
        else
        {
            if (paired && lost_cnt < 200) lost_cnt++;
        }

        // 处理寻物指令(必须在这里处理,最稳定)
        if (paired && sta == STATUS_NORMAL)
        {
            if (IS_HOST && last_cmd == 0xCC)
            {
                sta = STATUS_ALARM; // 从机寻主机
            }
            if (!IS_HOST && last_cmd == 0xBB)
            {
                sta = STATUS_ALARM; // 主机寻从机
            }
        }

        // 发送(收完再发)
        if (IS_HOST)
        {
            NRF24L01_TxPacket[0] = (sta == STATUS_SEARCH) ? 0xBB : 0xAA;
        }
        else
        {
            // 从机:主机在寻我时,我不能发寻物指令
            if (last_cmd == 0xBB)
            {
                NRF24L01_TxPacket[0] = 0xAA;
            }
            else
            {
                NRF24L01_TxPacket[0] = (sta == STATUS_SEARCH) ? 0xCC : 0xAA;
            }
        }
        NRF24L01_Send();
        delay_ms(10); // 必须加!防止NRF切换太快丢包

        // ===================== 状态机 =====================
        if (paired && sta == STATUS_UNPAIR)
            sta = STATUS_NORMAL;

        if (paired && lost_cnt > LOST_COUNT_MAX)
            sta = STATUS_ALARM;

        if (paired && lost_cnt < RESTORE_COUNT)
        {
            if (sta == STATUS_ALARM || sta == STATUS_MUTE)
            {
                sta = STATUS_NORMAL;
                mute_timer = 0;
            }
        }

        // 消音
        if (mute_timer > 0) mute_timer -= 50;
        if (mute_timer == 0 && sta == STATUS_MUTE)
            sta = STATUS_ALARM;

        // ===================== 按键 =====================
        if (paired)
        {
            if (KEY_Mode_Press())
            {
                if (sta == STATUS_NORMAL)
                {
                    sta = STATUS_SEARCH;
                }
                else if (sta == STATUS_SEARCH)
                {
                    sta = STATUS_NORMAL;
                }
            }
        }

        if (KEY_Mute_Press() && sta == STATUS_ALARM)
        {
            sta = STATUS_MUTE;
            mute_timer = MUTE_DURATION;
        }

        // 强制:主机寻物时,从机不能寻物
        if (!IS_HOST && last_cmd == 0xBB && sta == STATUS_SEARCH)
        {
            sta = STATUS_ALARM;
        }

        // ===================== OLED =====================
        if (sta != last_sta)
        {
            OLED_Clear();
            last_sta = sta;
        }
        OLED_ShowString(1, 1, IS_HOST ? "HOST" : "SLAVE");

        // ===================== 声光 =====================
        switch (sta)
        {
            case STATUS_UNPAIR:
                blink_flag ? LED_Green_On() : LED_Green_Off();
                LED_Red_Off(); Beep_Off();
                OLED_ShowString(2, 1, "UNPAIRED");
                break;

            case STATUS_NORMAL:
                LED_Green_On(); LED_Red_Off(); Beep_Off();
                OLED_ShowString(2, 1, "NORMAL    ");
                OLED_ShowString(4, 1, "CONNECTED ");
                break;

            case STATUS_SEARCH:
                LED_Green_Off(); LED_Red_On(); Beep_Off();
                OLED_ShowString(2, 1, "SEARCHING ");
                break;

            case STATUS_ALARM:
                LED_Green_Off();
                blink_flag ? LED_Red_On() : LED_Red_Off();
                Beep_On();
                OLED_ShowString(2, 1, "ALARM!!!  ");
                break;

            case STATUS_MUTE:
                LED_Green_Off();
                blink_flag ? LED_Red_On() : LED_Red_Off();
                Beep_Off();
                OLED_ShowString(2, 1, "MUTE      ");
                break;
        }

        delay_ms(40);
    }
}

江协科技的nrf24l01驱动

#ifndef __NRF24L01_DEFINE_H
#define __NRF24L01_DEFINE_H

/*NRF24L01指令宏定义*/
#define NRF24L01_R_REGISTER            0x00    //读寄存器,高3位为指令码,低5位为寄存器地址,后续跟1~5字节读数据
#define NRF24L01_W_REGISTER            0x20    //写寄存器,高3位为指令码,低5位为寄存器地址,后续跟1~5字节写数据
#define NRF24L01_R_RX_PAYLOAD        0x61    //读Rx有效载荷,后续跟1~32字节读数据
#define NRF24L01_W_TX_PAYLOAD        0xA0    //写Tx有效载荷,后续跟1~32字节写数据
#define NRF24L01_FLUSH_TX            0xE1    //清空Tx FIFO所有数据,单独指令
#define NRF24L01_FLUSH_RX            0xE2    //清空Rx FIFO所有数据,单独指令
#define NRF24L01_REUSE_TX_PL        0xE3    //重新使用最后一次发送的有效载荷,单独指令
#define NRF24L01_R_RX_PL_WID        0x60    //读取Rx FIFO最前面一个数据包的宽度,后续跟1字节读数据,仅适用于动态包长模式
#define NRF24L01_W_ACK_PAYLOAD        0xA8    //写应答附带的有效载荷,高5位为指令码,低3位为通道号,后续跟1~32字节写数据,仅适用于应答附带载荷模式
#define NRF24L01_W_TX_PAYLOAD_NOACK    0xB0    //写Tx有效载荷,不要求应答,后续跟1~32字节写数据,仅适用于不要求应答模式
#define NRF24L01_NOP                0xFF    //空操作,单独指令,可以用读取状态寄存器

/*NRF24L01寄存器地址宏定义*/
#define NRF24L01_CONFIG                0x00    //配置寄存器,1字节
#define NRF24L01_EN_AA                0x01    //使能自动应答,1字节
#define NRF24L01_EN_RXADDR            0x02    //使能接收通道,1字节
#define NRF24L01_SETUP_AW            0x03    //设置地址宽度,1字节
#define NRF24L01_SETUP_RETR            0x04    //设置自动重传,1字节
#define NRF24L01_RF_CH                0x05    //射频通道,1字节
#define NRF24L01_RF_SETUP            0x06    //射频相关参数设置,1字节
#define NRF24L01_STATUS                0x07    //状态寄存器,1字节
#define NRF24L01_OBSERVE_TX            0x08    //发送观察寄存器,1字节
#define NRF24L01_RPD                0x09    //接收功率检测,1字节
#define NRF24L01_RX_ADDR_P0            0x0A    //接收通道0地址,5字节
#define NRF24L01_RX_ADDR_P1            0x0B    //接收通道1地址,5字节
#define NRF24L01_RX_ADDR_P2            0x0C    //接收通道2地址,1字节,高位地址与接收通道1相同
#define NRF24L01_RX_ADDR_P3            0x0D    //接收通道3地址,1字节,高位地址与接收通道1相同
#define NRF24L01_RX_ADDR_P4            0x0E    //接收通道4地址,1字节,高位地址与接收通道1相同
#define NRF24L01_RX_ADDR_P5            0x0F    //接收通道5地址,1字节,高位地址与接收通道1相同
#define NRF24L01_TX_ADDR            0x10    //发送地址,5字节
#define NRF24L01_RX_PW_P0            0x11    //接收通道0有效载荷数据宽度,1字节
#define NRF24L01_RX_PW_P1            0x12    //接收通道1有效载荷的数据宽度,1字节
#define NRF24L01_RX_PW_P2            0x13    //接收通道2有效载荷的数据宽度,1字节
#define NRF24L01_RX_PW_P3            0x14    //接收通道3有效载荷的数据宽度,1字节
#define NRF24L01_RX_PW_P4            0x15    //接收通道4有效载荷的数据宽度,1字节
#define NRF24L01_RX_PW_P5            0x16    //接收通道5有效载荷的数据宽度,1字节
#define NRF24L01_FIFO_STATUS        0x17    //发送和接收FIFO状态,1字节
#define NRF24L01_DYNPD                0x1C    //使能接收通道的动态包长模式,1字节
#define NRF24L01_FEATURE            0x1D    //使能高级功能,1字节

#endif

#ifndef __NRF24L01_H
#define __NRF24L01_H

#include "NRF24L01_Define.h"

/*外部可调用全局数组***********/

extern uint8_t NRF24L01_TxAddress[];
extern uint8_t NRF24L01_TxPacket[];

extern uint8_t NRF24L01_RxAddress[];
extern uint8_t NRF24L01_RxPacket[];

/***********外部可调用全局数组*/


/*函数声明*********************/

/*指令实现*/
uint8_t NRF24L01_ReadReg(uint8_t RegAddress);
void NRF24L01_ReadRegs(uint8_t RegAddress, uint8_t *DataArray, uint8_t Count);
void NRF24L01_WriteReg(uint8_t RegAddress, uint8_t Data);
void NRF24L01_WriteRegs(uint8_t RegAddress, uint8_t *DataArray, uint8_t Count);
void NRF24L01_ReadRxPayload(uint8_t *DataArray, uint8_t Count);
void NRF24L01_WriteTxPayload(uint8_t *DataArray, uint8_t Count);
void NRF24L01_FlushTx(void);
void NRF24L01_FlushRx(void);
uint8_t NRF24L01_ReadStatus(void);

/*功能函数*/
void NRF24L01_PowerDown(void);
void NRF24L01_StandbyI(void);
void NRF24L01_Rx(void);
void NRF24L01_Tx(void);

void NRF24L01_Init(void);
uint8_t NRF24L01_Send(void);
uint8_t NRF24L01_Receive(void);
void NRF24L01_UpdateRxAddress(void);

/*********************函数声明*/


#endif

#include "stm32f10x.h"
#include "NRF24L01_Define.h"

/*全局变量*********************/

/*发送部分*/
uint8_t NRF24L01_TxAddress[5] = {0x11, 0x22, 0x33, 0x44, 0x55};        //发送地址,固定5字节
#define NRF24L01_TX_PACKET_WIDTH        4                            //发送数据包宽度,范围:1~32字节
uint8_t NRF24L01_TxPacket[NRF24L01_TX_PACKET_WIDTH];                //发送数据包

/*接收部分*/
uint8_t NRF24L01_RxAddress[5] = {0x11, 0x22, 0x33, 0x44, 0x55};        //接收通道0地址,固定5字节
#define NRF24L01_RX_PACKET_WIDTH        4                            //接收通道0数据包宽度,范围:1~32字节
uint8_t NRF24L01_RxPacket[NRF24L01_RX_PACKET_WIDTH];                //接收数据包

/**
  * 提示:设备A和设备B进行通信
  * A发B收时,A的发送地址、发送数据包宽度要与B的接收地址、接收数据包宽度对应相同
  * B发A收时,B的发送地址、发送数据包宽度要与A的接收地址、接收数据包宽度对应相同
  * 通常情况下,可以将A和B的发送地址、接收地址全设置一样,A和B的发送数据包宽度、接收数据包宽度也全设置一样
  * 这样A和B可以使用完全一样的模块程序,操作更加方便,也不容易搞混
  * 
  */

/*********************全局变量*/


/*引脚配置*********************/

/**
  * 函    数:NRF24L01写CE高低电平
  * 参    数:要写入CE的电平值,范围:0/1
  * 返 回 值:无
  * 说    明:当上层函数需要写CE时,此函数会被调用
  *           用户需要根据参数传入的值,将CE置为高电平或者低电平
  *           当参数传入0时,置CE为低电平,当参数传入1时,置CE为高电平
  */
void NRF24L01_W_CE(uint8_t BitValue)
{
    /*根据BitValue的值,将CE置高电平或者低电平*/
    GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)BitValue);
}

/**
  * 函    数:NRF24L01写CSN高低电平
  * 参    数:要写入CSN的电平值,范围:0/1
  * 返 回 值:无
  * 说    明:当上层函数需要写CSN时,此函数会被调用
  *           用户需要根据参数传入的值,将CSN置为高电平或者低电平
  *           当参数传入0时,置CSN为低电平,当参数传入1时,置CSN为高电平
  */
void NRF24L01_W_CSN(uint8_t BitValue)
{
    /*根据BitValue的值,将CSN置高电平或者低电平*/
    GPIO_WriteBit(GPIOA, GPIO_Pin_1, (BitAction)BitValue);
}

/**
  * 函    数:NRF24L01写SCK高低电平
  * 参    数:要写入SCK的电平值,范围:0/1
  * 返 回 值:无
  * 说    明:当上层函数需要写SCK时,此函数会被调用
  *           用户需要根据参数传入的值,将SCK置为高电平或者低电平
  *           当参数传入0时,置SCK为低电平,当参数传入1时,置SCK为高电平
  */
void NRF24L01_W_SCK(uint8_t BitValue)
{
    /*根据BitValue的值,将SCK置高电平或者低电平*/
    GPIO_WriteBit(GPIOA, GPIO_Pin_2, (BitAction)BitValue);
}

/**
  * 函    数:NRF24L01写MOSI高低电平
  * 参    数:要写入MOSI的电平值,范围:0/1
  * 返 回 值:无
  * 说    明:当上层函数需要写MOSI时,此函数会被调用
  *           用户需要根据参数传入的值,将MOSI置为高电平或者低电平
  *           当参数传入0时,置MOSI为低电平,当参数传入1时,置MOSI为高电平
  */
void NRF24L01_W_MOSI(uint8_t BitValue)
{
    /*根据BitValue的值,将MOSI置高电平或者低电平*/
    GPIO_WriteBit(GPIOA, GPIO_Pin_3, (BitAction)BitValue);
}

/**
  * 函    数:NRF24L01读MISO高低电平
  * 参    数:无
  * 返 回 值:读取得到MISO的电平值,范围:0/1
  * 说    明:当上层函数需要读MISO时,此函数会被调用
  *           用户需要读取MISO引脚,返回此引脚的高低电平状态
  *           当MISO为高电平时,返回1,当MISO为低电平时,返回0
  */
uint8_t NRF24L01_R_MISO(void)
{
    /*取MISO引脚的高低电平并返回*/
    return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4);
}

/**
  * 本代码使用查询的方式获取设备状态,因此不需要使用IRQ引脚
  */
/**
  * 函    数:NRF24L01引脚初始化
  * 参    数:无
  * 返 回 值:无
  * 说    明:当上层函数需要初始化时,此函数会被调用
  *           用户需要将CSN、CE、MISO、SCK引脚初始化为推挽输出模式,MISO引脚初始化为上拉输入模式
  */
void NRF24L01_GPIO_Init(void)
{
    /*开启GPIO时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    /*将CE、CSN、SCK、MOSI引脚初始化为推挽输出模式*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /*将MISO引脚初始化为上拉输入模式*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /*置引脚初始化后的默认电平*/
    NRF24L01_W_CE(0);        //CE默认为0,退出收发模式
    NRF24L01_W_CSN(1);        //CSN默认为1,不选中从机
    NRF24L01_W_SCK(0);        //SCK默认为0,对应SPI模式0
    NRF24L01_W_MOSI(0);        //MOSI默认电平随意,10均可
}

/*********************引脚配置*/


/*通信协议*********************/

/**
  * 函    数:SPI交换一个字节
  * 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  * 返 回 值:接收得到的一个字节数据,范围:0x00~0xFF
  */
uint8_t NRF24L01_SPI_SwapByte(uint8_t Byte)
{
    uint8_t i;
    
    /*此处使用SPI模式0进行通信*/
    /*循环8次,主机依次移出和移入数据的每一位*/
    for (i = 0; i < 8; i ++)
    {
        /*SPI为高位先行,因此移出高位至MOSI引脚*/
        if (Byte & 0x80)            //判断Byte的最高位
        {
            NRF24L01_W_MOSI(1);        //如果为1,则给MOSI输出1
        }
        else
        {
            NRF24L01_W_MOSI(0);        //如果为0,则给MOSI输出0
        }
        Byte <<= 1;                    //Byte左移一位,最低位空出来用于接收数据位
        
        /*产生SCK上升沿*/
        NRF24L01_W_SCK(1);
        
        /*从MISO引脚移入数据,存入Byte的最低位*/
        if (NRF24L01_R_MISO())        //读取MISO引脚
        {
            Byte |= 0x01;            //如果为1,则给Byte最低位置1
        }                            //如果为0,则不做任何操作,因为左移后低位默认补0
        
        /*产生SCK下降沿*/
        NRF24L01_W_SCK(0);
    }
    
    /*返回Byte数据,此时的Byte为SPI交换接收得到的一个字节数据*/
    return Byte;
}

/*********************通信协议*/


/*指令实现*********************/

/**
  * 函    数:NRF24L01读取寄存器(一个字节)
  * 参    数:RegAddress 指定寄存器地址,范围:0x00~0x1F
  * 返 回 值:指定寄存器的数据,范围:0x00~0xFF
  */
uint8_t NRF24L01_ReadReg(uint8_t RegAddress)
{
    uint8_t Data;
    
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);
    
    /*交换发送一个字节,通信开始的第一个字节为指令码,读寄存器(低5位为寄存器地址)*/
    NRF24L01_SPI_SwapByte(NRF24L01_R_REGISTER | RegAddress);
    
    /*发送读寄存器指令后,开始交换接收,得到指定地址的数据*/
    Data = NRF24L01_SPI_SwapByte(NRF24L01_NOP);
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
    
    /*返回读到的一个字节数据*/
    return Data;
}

/**
  * 函    数:NRF24L01读取寄存器(多个字节)
  * 参    数:RegAddress 指定寄存器的地址,范围:0x00~0x1F
  * 参    数:DataArray 读取得到的数据数组,输出参数
  * 参    数:Count 指定读取的数量,范围:0~5
  * 返 回 值:无
  */
void NRF24L01_ReadRegs(uint8_t RegAddress, uint8_t *DataArray, uint8_t Count)
{
    uint8_t i;
    
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);
    
    /*交换发送一个字节,通信开始的第一个字节为指令码,读寄存器(低5位为寄存器地址)*/
    NRF24L01_SPI_SwapByte(NRF24L01_R_REGISTER | RegAddress);
    
    /*发送读寄存器指令后,开始交换接收,循环接收多次,得到指定地址下的多个数据*/
    for (i = 0; i < Count; i ++)
    {
        /*将接收到的数据写入到输出参数DataArray中*/
        DataArray[i] = NRF24L01_SPI_SwapByte(NRF24L01_NOP);
    }
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
}

/**
  * 函    数:NRF24L01写入寄存器(一个字节)
  * 参    数:RegAddress 指定寄存器地址,范围:0x00~0x1F
  * 参    数:Data 要写入的一个字节数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void NRF24L01_WriteReg(uint8_t RegAddress, uint8_t Data)
{
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);
    
    /*交换发送一个字节,通信开始的第一个字节为指令码,写寄存器(低5位为寄存器地址)*/
    NRF24L01_SPI_SwapByte(NRF24L01_W_REGISTER | RegAddress);
    
    /*发送写寄存器指令后,开始交换发送,在指定地址下写入数据*/
    NRF24L01_SPI_SwapByte(Data);
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
}

/**
  * 函    数:NRF24L01写入寄存器(多个字节)
  * 参    数:RegAddress 指定寄存器地址,范围:0x00~0x1F
  * 参    数:DataArray 要写入的数据数组,输入参数
  * 参    数:Count 指定写入的数量,范围:0~5
  * 返 回 值:无
  */
void NRF24L01_WriteRegs(uint8_t RegAddress, uint8_t *DataArray, uint8_t Count)
{
    uint8_t i;
    
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);
    
    /*交换发送一个字节,通信开始的第一个字节为指令码,写寄存器(低5位为寄存器地址)*/
    NRF24L01_SPI_SwapByte(NRF24L01_W_REGISTER | RegAddress);
    
    /*发送写寄存器指令后,开始交换发送,循环发送多次,在指定地址下写入多个数据*/
    for (i = 0; i < Count; i ++)
    {
        /*将输入参数DataArray的数据写入到指定地址中*/
        NRF24L01_SPI_SwapByte(DataArray[i]);
    }
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
}

/**
  * 函    数:NRF24L01读取Rx有效载荷
  * 参    数:DataArray 读取得到的数据数组,输出参数
  * 参    数:Count 指定读取的数量,范围:0~32
  * 返 回 值:无
  */
void NRF24L01_ReadRxPayload(uint8_t *DataArray, uint8_t Count)
{
    uint8_t i;
    
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);
    
    /*交换发送一个字节,通信开始的第一个字节为指令码,读取Rx有效载荷*/
    NRF24L01_SPI_SwapByte(NRF24L01_R_RX_PAYLOAD);
    
    /*发送读取Rx有效载荷指令后,开始交换接收,循环接收多次,得到多个数据*/
    for (i = 0; i < Count; i ++)
    {
        /*将读取的数据写入到输出参数DataArray中*/
        DataArray[i] = NRF24L01_SPI_SwapByte(NRF24L01_NOP);
    }
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
}
/**
  * 函    数:NRF24L01写入Tx有效载荷
  * 参    数:DataArray 要写入的数据数组,输入参数
  * 参    数:Count 指定写入的数量,范围:0~5
  * 返 回 值:无
  */
void NRF24L01_WriteTxPayload(uint8_t *DataArray, uint8_t Count)
{
    uint8_t i;
    
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);
    
    /*交换发送一个字节,通信开始的第一个字节为指令码,写入Tx有效载荷*/
    NRF24L01_SPI_SwapByte(NRF24L01_W_TX_PAYLOAD);
    
    /*发送写入Tx有效载荷指令后,开始交换发送,循环发送多次,写入多个数据*/
    for (i = 0; i < Count; i ++)
    {
        /*将输入参数DataArray的数据写入到Tx有效载荷中*/
        NRF24L01_SPI_SwapByte(DataArray[i]);
    }
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
}

/**
  * 函    数:NRF24L01清空Tx FIFO的所有数据
  * 参    数:无
  * 返 回 值:无
  */
void NRF24L01_FlushTx(void)
{
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);

    /*交换发送一个字节,通信开始的第一个字节为指令码,清空Tx FIFO*/
    NRF24L01_SPI_SwapByte(NRF24L01_FLUSH_TX);
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
}

/**
  * 函    数:NRF24L01清空Rx FIFO的所有数据
  * 参    数:无
  * 返 回 值:无
  */
void NRF24L01_FlushRx(void)
{
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);

    /*交换发送一个字节,通信开始的第一个字节为指令码,清空Rx FIFO*/
    NRF24L01_SPI_SwapByte(NRF24L01_FLUSH_RX);
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
}

/**
  * 函    数:NRF24L01读取状态寄存器
  * 参    数:无
  * 返 回 值:状态寄存器的值,范围:0x00~0xFF
  */
uint8_t NRF24L01_ReadStatus(void)
{
    uint8_t Status;
    
    /*CSN置低,通信开始*/
    NRF24L01_W_CSN(0);

    /*交换发送一个字节,通信开始的第一个字节为指令码,空指令*/
    /*第一个字节发送任意指令,都可以交换得到状态寄存器的值*/
    Status = NRF24L01_SPI_SwapByte(NRF24L01_NOP);
    
    /*CSN置高,通信结束*/
    NRF24L01_W_CSN(1);
    
    /*返回状态寄存器的值*/
    return Status;
}

/*********************指令实现*/


/*功能函数*********************/

/**
  * 函    数:NRF24L01进入掉电模式(CE = 0,PWR_UP = 0)
  * 参    数:无
  * 返 回 值:无
  */
void NRF24L01_PowerDown(void)
{
    uint8_t Config;
    
    /*CE置0,退出收发模式*/
    NRF24L01_W_CE(0);
    
    /*读-改-写操作流程,单独修改配置寄存器的某些位而不影响其他位*/
    Config = NRF24L01_ReadReg(NRF24L01_CONFIG);        //读取配置寄存器
    if (Config == 0xFF) {return;}                    //配置寄存器全为1,出错,退出函数
    Config &= ~0x02;                                //配置寄存器位1(PWR_UP)置0
    NRF24L01_WriteReg(NRF24L01_CONFIG, Config);        //写回配置寄存器
}

/**
  * 函    数:NRF24L01进入待机模式1(CE = 0,PWR_UP = 1)
  * 参    数:无
  * 返 回 值:无
  */
void NRF24L01_StandbyI(void)
{
    uint8_t Config;
    
    /*CE置0,退出收发模式*/
    NRF24L01_W_CE(0);
    
    /*读-改-写操作流程,单独修改配置寄存器的某些位而不影响其他位*/
    Config = NRF24L01_ReadReg(NRF24L01_CONFIG);        //读取配置寄存器
    if (Config == 0xFF) {return;}                    //配置寄存器全为1,出错,退出函数
    Config |= 0x02;                                    //配置寄存器位1(PWR_UP)置1
    NRF24L01_WriteReg(NRF24L01_CONFIG, Config);        //写回配置寄存器
}

/**
  * 函    数:NRF24L01进入接收模式(CE = 1,PWR_UP = 1,PRIM_RX = 1)
  * 参    数:无
  * 返 回 值:无
  */
void NRF24L01_Rx(void)
{
    uint8_t Config;
    
    /*CE置0,退出收发模式*/
    NRF24L01_W_CE(0);
    
    /*读-改-写操作流程,单独修改配置寄存器的某些位而不影响其他位*/
    Config = NRF24L01_ReadReg(NRF24L01_CONFIG);        //读取配置寄存器
    if (Config == 0xFF) {return;}                    //配置寄存器全为1,出错,退出函数
    Config |= 0x03;                                    //配置寄存器位1(PWR_UP)和位0(PRIM_RX)都置1
    NRF24L01_WriteReg(NRF24L01_CONFIG, Config);        //写回配置寄存器
    
    /*CE置1,进入收发模式,因为PRIM_RX为1,所以进入接收模式*/
    NRF24L01_W_CE(1);
}

/**
  * 函    数:NRF24L01进入发送模式(CE = 1,PWR_UP = 1,PRIM_RX = 0)
  * 参    数:无
  * 返 回 值:无
  */
void NRF24L01_Tx(void)
{
    uint8_t Config;
    
    /*CE置0,退出收发模式*/
    NRF24L01_W_CE(0);
    
    /*读-改-写操作流程,单独修改配置寄存器的某些位而不影响其他位*/
    Config = NRF24L01_ReadReg(NRF24L01_CONFIG);        //读取配置寄存器
    if (Config == 0xFF) {return;}                    //配置寄存器全为1,出错,退出函数
    Config |= 0x02;                                    //配置寄存器位1(PWR_UP)置1
    Config &= ~0x01;                                //配置寄存器位0(PRIM_RX)置0
    NRF24L01_WriteReg(NRF24L01_CONFIG, Config);        //写回配置寄存器
    
    /*CE置1,进入收发模式,因为PRIM_RX为0,所以进入发送模式*/
    NRF24L01_W_CE(1);
}

/**
  * 函    数:NRF24L01初始化
  * 参    数:无
  * 返 回 值:无
  * 说    明:使用前,需要调用此初始化函数
  */
void NRF24L01_Init(void)
{
    /*先调用底层的端口初始化*/
    NRF24L01_GPIO_Init();
    
    /*初始化配置一系列寄存器,寄存器值的意义需参考手册中的寄存器描述*/
    /*以下配置通信双方必须保持一致,否则无法进行通信*/
    NRF24L01_WriteReg(NRF24L01_CONFIG, 0x08);        //配置寄存器,不屏蔽中断,使能CRC,CRC为1字节,PWR_UP = 0,PRIM_RX = 0
    NRF24L01_WriteReg(NRF24L01_EN_AA, 0x3F);        //使能自动应答,开启接收通道0~通道5的自动应答
    NRF24L01_WriteReg(NRF24L01_EN_RXADDR, 0x01);    //使能接收通道,只开启接收通道0
    NRF24L01_WriteReg(NRF24L01_SETUP_AW, 0x03);        //设置地址宽度,地址宽度为5字节
    NRF24L01_WriteReg(NRF24L01_SETUP_RETR, 0x03);    //设置自动重传,间隔250us,重传3次
    NRF24L01_WriteReg(NRF24L01_RF_CH, 0x02);        //射频通道,频率为(2400 + 2)MHz = 2.402GHz
    NRF24L01_WriteReg(NRF24L01_RF_SETUP, 0x0E);        //射频设置,通信速率为2Mbps,发射功率为0dBm
    
    /*接收通道0的数据包宽度,设置为宏定义NRF24L01_RX_PACKET_WIDTH指定的值*/
    NRF24L01_WriteReg(NRF24L01_RX_PW_P0, NRF24L01_RX_PACKET_WIDTH);
    
    /*接收通道0地址,设置为全局数组NRF24L01_RxAddress指定的地址,地址宽度固定为5字节*/
    NRF24L01_WriteRegs(NRF24L01_RX_ADDR_P0, NRF24L01_RxAddress, 5);
    
    /*清空Tx FIFO的所有数据*/
    NRF24L01_FlushTx();
    
    /*清空Rx FIFO的所有数据*/
    NRF24L01_FlushRx();
    
    /*给状态寄存器的位4(MAX_RT)、位5(TX_DS)和位6(RX_DR)写1,清标志位*/
    NRF24L01_WriteReg(NRF24L01_STATUS, 0x70);
    
    /*初始化配置完成,芯片默认进入接收模式*/
    NRF24L01_Rx();
}

/**
  * 函    数:NRF24L01发送数据包
  * 参    数:无
  * 返 回 值:发送标志位,方便用户了解发送状态
  *             1:发送成功,无错误
  *             2:达到了最大重发次数仍未收到应答,可能是收发双方配置不一致、接收方不存在、接收FIFO已满或者多个发送数据包碰撞
  *             3:状态寄存器的值不合法,可能是设备不存在、断路、短路或者引脚配置不正确
  *             4:发送超时,可能是设备未初始化、断路、短路或者引脚配置不正确
  * 说    明:调用此函数前,直接修改全局数组NRF24L01_TxAddress和NRF24L01_TxPacket来设置发送的地址和数据
  */
uint8_t NRF24L01_Send(void)
{
    uint8_t Status;
    uint8_t SendFlag;
    uint32_t Timeout;
    
    /*发送地址,设置为全局数组NRF24L01_TxAddress指定的地址,地址宽度固定为5字节*/
    NRF24L01_WriteRegs(NRF24L01_TX_ADDR, NRF24L01_TxAddress, 5);
    
    /*接收通道0地址,此处必须也设置为发送地址,用于接收应答*/
    NRF24L01_WriteRegs(NRF24L01_RX_ADDR_P0, NRF24L01_TxAddress, 5);
    
    /*写发送有效载荷,写入全局数组NRF24L01_TxPacket指定的数据,数据宽度为NRF24L01_TX_PACKET_WIDTH*/
    NRF24L01_WriteTxPayload(NRF24L01_TxPacket, NRF24L01_TX_PACKET_WIDTH);
    
    /*发送的地址和有效载荷写入完成,进入发送模式,开始发送数据*/
    NRF24L01_Tx();
    
    /*指定超时时间,即循环读取状态寄存器的次数,具体值可以实测确定*/
    Timeout = 10000;
    
    /*循环读取状态寄存器*/
    while (1)
    {
        /*读取状态寄存器,保存至Status变量*/
        Status = NRF24L01_ReadStatus();
        
        /*超时计次*/
        Timeout --;
        if (Timeout == 0)            //如果计次减至0
        {
            SendFlag = 4;            //发送超时,置标志位为4
            NRF24L01_Init();        //发送出错,重新初始化一次设备,这样有助于设备从错误中恢复正常
            break;                    //跳出循环
        }
        
        /*根据状态寄存器的值,判断发送状态*/
        if ((Status & 0x30) == 0x30)        //状态寄存器位4(MAX_RT)和位5(TX_DS)同时为1
        {
            SendFlag = 3;            //状态寄存器的值不合法,置标志位为3
            NRF24L01_Init();        //发送出错,重新初始化一次设备,这样有助于设备从错误中恢复正常
            break;                    //跳出循环
        }
        else if ((Status & 0x10) == 0x10)    //状态寄存器位4(MAX_RT)为1
        {
            SendFlag = 2;            //达到了最大重发次数仍未收到应答,置标志位为2
            NRF24L01_Init();        //发送出错,重新初始化一次设备,这样有助于设备从错误中恢复正常
            break;                    //跳出循环
        }
        else if ((Status & 0x20) == 0x20)    //状态寄存器位5(TX_DS)为1
        {
            SendFlag = 1;            //发送成功,无错误,置标志位为1
            break;                    //跳出循环
        }
    }
    
    /*给状态寄存器的位4(MAX_RT)和位5(TX_DS)写1,清标志位*/
    NRF24L01_WriteReg(NRF24L01_STATUS, 0x30);
    
    /*清空Tx FIFO的所有数据*/
    NRF24L01_FlushTx();
    
    /*发送完成后,恢复接收通道0原来的地址*/
    /*如果发送地址和接收通道0地址设置相同,则可不执行这一句*/
    NRF24L01_WriteRegs(NRF24L01_RX_ADDR_P0, NRF24L01_RxAddress, 5);
    
    /*发送完成,芯片恢复为接收模式*/
    NRF24L01_Rx();
        
    /*返回发送标志位*/
    return SendFlag;
}

/**
  * 函    数:NRF24L01接收数据包
  * 参    数:无
  * 返 回 值:接收标志位,方便用户了解接收状态
  *             0:未接收到数据包
  *             1:成功接收到一个数据包
  *             2:状态寄存器的值不合法,可能是设备不存在、断路、短路或者引脚配置不正确
  *             3:设备仍处于掉电模式,可能是设备未初始化、曾经断电过、断路、短路或者引脚配置不正确
  * 说    明:如果收到了数据包,则可直接从全局数组NRF24L01_RxPacket取数据
  */
uint8_t NRF24L01_Receive(void)
{
    uint8_t Status, Config;
    uint8_t ReceiveFlag;
    
    /*读取状态寄存器,保存至Status变量*/
    Status = NRF24L01_ReadStatus();
    
    /*读取配置寄存器,保存至Config变量*/
    Config = NRF24L01_ReadReg(NRF24L01_CONFIG);
    
    /*根据配置寄存器和状态寄存器的值,判断接收状态*/
    if ((Config & 0x02) == 0x00)        //配置寄存器位1(PWR_UP)为0
    {
        ReceiveFlag = 3;                //设备仍处于掉电模式,置标志位为3
        NRF24L01_Init();                //接收出错,重新初始化一次设备,这样有助于设备从错误中恢复正常
    }
    else if ((Status & 0x30) == 0x30)    //状态寄存器位4(MAX_RT)和位5(TX_DS)同时为1
    {
        ReceiveFlag = 2;                //状态寄存器的值不合法,置标志位为2
        NRF24L01_Init();                //接收出错,重新初始化一次设备,这样有助于设备从错误中恢复正常
    }
    else if ((Status & 0x40) == 0x40)    //状态寄存器位6(RX_DR)为1
    {
        ReceiveFlag = 1;                //接收到数据,置标志位为1
        
        /*读接收有效载荷,存放在全局数组NRF24L01_RxPacket中,数据宽度为NRF24L01_RX_PACKET_WIDTH*/
        NRF24L01_ReadRxPayload(NRF24L01_RxPacket, NRF24L01_RX_PACKET_WIDTH);
        
        /*给状态寄存器的位6(RX_DR)写1,清标志位*/
        NRF24L01_WriteReg(NRF24L01_STATUS, 0x40);

        /*清空Rx FIFO的所有数据*/
        NRF24L01_FlushRx();
    }
    else
    {
        ReceiveFlag = 0;                //未接收到数据,置标志位为0
    }
    
    /*返回接收标志位*/
    return ReceiveFlag;
}

/**
  * 函    数:NRF24L01更新接收地址
  * 参    数:无
  * 返 回 值:无
  * 说    明:如果想在运行时动态修改接收地址,则可先向全局数组NRF24L01_RxAddress写入修改的地址
  *           然后再调用此函数,使修改的接收地址生效
  */
void NRF24L01_UpdateRxAddress(void)
{
    /*接收通道0地址,设置为全局数组NRF24L01_RxAddress指定的地址,地址宽度固定为5字节*/
    NRF24L01_WriteRegs(NRF24L01_RX_ADDR_P0, NRF24L01_RxAddress, 5);
}

  • 写回答

2条回答 默认 最新

  • 紫雾凌寒 新星创作者: 人工智能技术领域 2026-04-04 13:38
    关注

    让 “紫雾凌寒” 来帮你回答,本回答结合 DeepSeek

    如果还有问题,请评论或点击头像私信,如果有帮助到您, 请关注、采纳、点赞

    一、问题关键点分析

    你的问题核心是NRF24L01通信不稳定,衍生出消音异常、模式寻找无效等问题,关键点包括:
    1. 通信不稳定根源
    - 硬件:电源纹波大、SPI时序错误、CE/CSN控制不当、天线匹配差
    - 软件:寄存器配置错误(地址/通道/CRC)、收发逻辑不同步、SPI速率过高
    2. 消音异常
    - 按键无防抖、消音用延时阻塞(而非定时器)、消音标志未正确计数
    3. 模式寻找无效
    - 模式切换后未重新配置NRF24L01、主从模式不同步

    二、解决方案(硬件优先+软件优化)

    NRF24L01通信问题80%源于硬件,需先排查硬件,再优化软件逻辑。

    🔧 第一部分:硬件问题排查与修复(优先!)

    1. 电源问题(最常见!)

    NRF24L01发射时电流达12mA,若电源纹波>0.3V则通信失败。
    修复方案
    - 给NRF24L01单独加10uF电解电容 + 100nF陶瓷电容(靠近VCC/GND引脚)
    - 禁止直接从STM32取电,用外部LDO(如AMS1117-3.3)供电,保证发射时VCC≥3.0V
    - 用万用表测量发射时VCC电压,需稳定在3.0~3.6V之间

    2. SPI通信优化

    NRF24L01最高支持10Mbps,但实际稳定需≤5Mbps,且需匹配时序。
    修复方案
    - 引脚确认:STM32F103选SPI1(PA5=SCK、PA6=MISO、PA7=MOSI)或SPI2(PB13=SCK、PB14=MISO、PB15=MOSI),避免引脚冲突
    - 速率设置:SPI分频设为16(SPI1:72MHz/16=4.5Mbps;SPI2:36MHz/16=2.25Mbps)
    - MISO上拉:给MISO引脚加10k上拉电阻(部分模块为开漏输出)

    3. CE/CSN时序修复

    NRF24L01对CE/CSN时序要求严格:
    - CE引脚:接收时保持高,发送时需先低→高脉冲(≥10us)
    - CSN引脚:SPI通信前拉低(≥10us),结束后拉高(≥10us)
    验证:用逻辑分析仪确认CE/CSN时序符合要求。

    4. 天线与匹配
    • 确认ANT引脚焊接无虚焊,避免靠近金属(USB/外壳)
    • 给ANT引脚接0.5~1pF匹配电容(靠近模块),提升发射效率

    🧩 第二部分:软件问题修复(关键代码示例)

    1. NRF24L01寄存器配置(核心错误:写命令格式)

    NRF24L01的写寄存器命令是0x20 | 寄存器地址,读是直接地址,此为常见误区!

    #include "stm32f10x.h"
    
    // NRF24L01寄存器地址
    #define NRF24L01_REG_CONFIG     0x00
    #define NRF24L01_REG_RF_CH      0x05
    #define NRF24L01_REG_STATUS     0x07
    #define NRF24L01_REG_TX_ADDR    0x10
    #define NRF24L01_REG_RX_ADDR_P0 0x0A
    
    // 引脚定义(示例:CE=PA0,CSN=PA1)
    #define CE_PIN   GPIO_Pin_0
    #define CE_PORT  GPIOA
    #define CSN_PIN  GPIO_Pin_1
    #define CSN_PORT GPIOA
    
    // SPI读写一个字节(正确时序)
    uint8_t NRF24L01_SPI_RW(uint8_t tx_data) {
      while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // 等待发送空
      SPI_I2S_SendData(SPI1, tx_data);
      while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); // 等待接收非空
      return SPI_I2S_ReceiveData(SPI1);
    }
    
    // 写寄存器(正确命令格式:0x20 | reg)
    void NRF24L01_WriteReg(uint8_t reg, uint8_t data) {
      NRF24L01_CSN_Low();   // 拉低CSN
      NRF24L01_SPI_RW(0x20 | reg); // 写命令
      NRF24L01_SPI_RW(data);
      NRF24L01_CSN_High();  // 拉高CSN
    }
    
    // 读寄存器
    uint8_t NRF24L01_ReadReg(uint8_t reg) {
      uint8_t data;
      NRF24L01_CSN_Low();
      NRF24L01_SPI_RW(reg); // 读命令
      data = NRF24L01_SPI_RW(0x00); // 空数据读取
      NRF24L01_CSN_High();
      return data;
    }
    
    // 初始化(关键参数:地址/通道/CRC匹配)
    void NRF24L01_Init(void) {
      // 1. 配置CE/CSN为输出
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      GPIO_InitStruct.GPIO_Pin = CE_PIN | CSN_PIN;
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
      GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(CE_PORT, &GPIO_InitStruct);
      NRF24L01_CE_Low(); NRF24L01_CSN_High();
    
      // 2. 配置SPI1(速率4.5Mbps)
      SPI_InitTypeDef SPI_InitStruct = {0};
      SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
      SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
      SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
      SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;   // NRF24L01要求CPOL=0
      SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // NRF24L01要求CPHA=1
      SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // 72/16=4.5Mbps
      SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
      SPI_Init(SPI1, &SPI_InitStruct);
      SPI_Cmd(SPI1, ENABLE);
    
      // 3. 配置核心寄存器(主从需匹配!)
      uint8_t addr[5] = {0x12,0x34,0x56,0x78,0x90}; // 5字节地址(主从一致)
      NRF24L01_WriteReg(NRF24L01_REG_CONFIG, 0x0E); // 使能2字节CRC、接收模式
      NRF24L01_WriteReg(NRF24L01_REG_RF_CH, 0x15); // 通道0x15(避免WiFi冲突)
      NRF24L01_WriteReg(NRF24L01_REG_TX_ADDR, addr[0]); // 写发送地址
      NRF24L01_WriteReg(NRF24L01_REG_RX_ADDR_P0, addr[0]); // 写接收地址(通道0)
      NRF24L01_WriteReg(NRF24L01_REG_RX_ADDR_P0+1, addr[1]); // 连续写地址
      NRF24L01_WriteReg(NRF24L01_REG_RX_ADDR_P0+2, addr[2]);
      NRF24L01_WriteReg(NRF24L01_REG_RX_ADDR_P0+3, addr[3]);
      NRF24L01_WriteReg(NRF24L01_REG_RX_ADDR_P0+4, addr[4]);
      NRF24L01_WriteReg(NRF24L01_REG_STATUS, 0x70); // 清除所有标志
    
      // 4. 切换到接收模式
      NRF24L01_CE_High();
      delay_us(100);
    }
    
    2. 收发逻辑(主从同步+标志清除)

    发送后必须切换回接收模式,且每次收发后清除STATUS寄存器标志(TX_DS/MAX_RT/RX_DR)。

    // 发送数据(主机用)
    uint8_t NRF24L01_TxPacket(uint8_t* tx_buf, uint8_t len) {
      uint8_t status;
      NRF24L01_CE_Low(); // 拉低CE
    
      // 切换到发送模式
      NRF24L01_WriteReg(NRF24L01_REG_CONFIG, 0x0E & ~(1<<0)); // 关闭接收
      NRF24L01_CE_High();
      delay_us(10);
    
      // 写TX载荷(命令:0xA0)
      NRF24L01_CSN_Low();
      NRF24L01_SPI_RW(0xA0);
      for(int i=0; i<len; i++) NRF24L01_SPI_RW(tx_buf[i]);
      NRF24L01_CSN_High();
    
      // 等待发送完成(超时10ms)
      uint32_t timeout = 10000;
      while(timeout--) {
        status = NRF24L01_ReadReg(NRF24L01_REG_STATUS);
        if(status & (1<<5)) break; // TX_DS:发送成功
        if(status & (1<<4)) break; // MAX_RT:重发超时
        delay_us(1);
      }
    
      // 清除标志
      NRF24L01_WriteReg(NRF24L01_REG_STATUS, (1<<5)|(1<<4));
    
      // 切换回接收模式
      NRF24L01_CE_Low();
      NRF24L01_WriteReg(NRF24L01_REG_CONFIG, 0x0E | (1<<0)); // 使能接收
      NRF24L01_CE_High();
      delay_us(100);
    
      return (status & (1<<5)) ? 0 : 1; // 0=成功,1=失败
    }
    
    // 接收数据(从机用)
    uint8_t NRF24L01_RxPacket(uint8_t* rx_buf) {
      uint8_t status = NRF24L01_ReadReg(NRF24L01_REG_STATUS);
      if(!(status & (1<<6))) return 0; // 无RX_DR(无数据)
    
      // 读RX载荷(命令:0x61)
      NRF24L01_CE_Low();
      NRF24L01_CSN_Low();
      NRF24L01_SPI_RW(0x61);
      for(int i=0; i<32; i++) rx_buf[i] = NRF24L01_SPI_RW(0x00);
      NRF24L01_CSN_High();
    
      // 清除标志
      NRF24L01_WriteReg(NRF24L01_REG_STATUS, (1<<6));
    
      // 重新使能接收
      NRF24L01_CE_High();
      delay_us(100);
    
      return 32; // 返回接收长度
    }
    
    3. 消音逻辑(定时器中断+按键防抖)

    定时器中断实现消音(不阻塞主循环),按键必须防抖(100ms)。

    // 消音相关变量
    uint8_t g_mute_flag = 0;   // 消音标志(1=消音中)
    uint16_t g_mute_timer = 0; // 消音定时器(单位:10ms)
    #define MUTE_TIME 500      // 消音5s(500*10ms)
    
    // 定时器2初始化(周期10ms)
    void TIM2_Init(void) {
      TIM_TimeBaseInitTypeDef TIM_InitStruct = {0};
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
      TIM_InitStruct.TIM_Period = 7199;   // 72MHz/8192=8.789kHz → 周期≈10ms
      TIM_InitStruct.TIM_Prescaler = 8191;
      TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
    
      TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能更新中断
      TIM_Cmd(TIM2, ENABLE);
    
      // NVIC配置
      NVIC_InitTypeDef NVIC_InitStruct = {0};
      NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
      NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
      NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
      NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStruct);
    }
    
    // 定时器中断服务函数
    void TIM2_IRQHandler(void) {
      if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    
        // 消音计时
        if(g_mute_flag) {
          g_mute_timer++;
          if(g_mute_timer >= MUTE_TIME) {
            g_mute_flag = 0;
            g_mute_timer = 0;
            GPIO_SetBits(GPIOA, GPIO_Pin_2); // 恢复报警(示例:PA2=报警引脚)
          }
        }
      }
    }
    
    // 按键处理(带防抖)
    void Key_Process(void) {
      static uint8_t key_state = 0; // 0=松开,1=按下中,2=确认按下
      static uint16_t key_timer = 0;
      uint8_t key_val = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3); // 按键按下为低
    
      switch(key_state) {
        case 0: // 松开状态
          if(key_val == 0) { key_state = 1; key_timer = 0; }
          break;
        case 1: // 防抖中(100ms)
          key_timer++;
          if(key_timer >= 10) { // 10*10ms=100ms
            if(key_val == 0) {
              key_state = 2;
              // 消音操作
              g_mute_flag = 1;
              g_mute_timer = 0;
              GPIO_ResetBits(GPIOA, GPIO_Pin_2); // 关闭报警
            } else {
              key_state = 0; // 抖动,返回松开
            }
          }
          break;
        case 2: // 按下确认
          if(key_val == 1) key_state = 0; // 松开
          break;
      }
    }
    
    4. 模式寻找逻辑(主从同步)

    模式切换后必须重新初始化NRF24L01(通道/地址变化)。

    ```c
    typedef enum { MODE_NORMAL=0, MODE_SEARCH=1 } WorkMode;
    WorkMode g_mode = MODE_NORMAL;

    // 模式切换函数
    void Mode_Switch(WorkMode mode) {
    if(g_mode == mode) return;
    g_mode = mode;

    // 重新配置NRF24L01
    uint8_t addr[5] = (mode == MODE_SEARCH) ?
    (uint8_t[]){0x00,0x00,0x00,0x00,0x00} : // 搜索地址
    (uint8_t[]){0x12,0x34,0x56,0x78,0x90}; // 正常地址
    uint8_t ch = (mode == MODE_SEARCH) ? 0x15 : 0x2A; // 通道

    NRF24L01_WriteReg(NRF24L01_REG_RF_CH, ch);
    NRF24L01_WriteReg(NRF24L01_REG_TX_ADDR, addr[0]);
    NRF24L01_WriteReg(NRF24L01_REG_RX_ADDR_P0, addr[0]);
    // 连续写地址…(同初始化)
    }

    // 主循环
    int main(void

    希望以上解答对您有所帮助。如果您有任何疑问,欢迎在评论区提出。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 4月13日
  • 已采纳回答 4月5日
  • 创建了问题 4月4日