楼主: alicedodo

[经验] 开贴讲讲NRF24L01P,让你彻底搞懂它的工作原理,持续更...

[复制链接]

该用户从未签到

发表于 2019-5-19 10:55 | 显示全部楼层
持续关注,最近被这玩意搞得头晕脑胀的,我用的是mega2560  由于tft屏幕占用了PTX端的spi  只能用模拟spi,网上找了一个库 调通了一次 ,但是不稳定,头文件都翻烂了,都没搞懂.可能是头文件过于简洁并没有设计多对一的函数,连 接收模式和发送模式函数都没有,可能只是一对一的一个实验nrf24l01的库...但是也调通了二对一.两个PRX端不定时的发送失败.头疼.  
   想请教下楼主!不胜感激!
我用了三块 mega2560  一块主控驱动 TFT 电阻屏幕 一个作用是显示两块副板收集的数据, 另外一个是通过对这些数据的处理 操作两块副板控制几个继电器.
三个模块都用了nrf24l01, 主控的频率太慢,刷新TFT都很慢 所以主控没用中断的方式用24l01 因为那样会造成屏幕刷新了一半卡顿了! 所以方式是 两个副板全是监听状态,主控需要刷新数据的时候 先发送一个指令给副板,然后转入监听,接受副板发来的数据,这段时间正好是屏幕清屏的时间,能使屏幕看上去正常一点.
现在的问题就是没找到可用的稳定的适用于mega2560的模拟spi的nrf24l01的驱动. 请楼主帮帮忙 提供一份能用的驱动 不胜感激 有偿帮助! qq:174802980,微信:doyouremeber

该用户从未签到

 楼主| 发表于 2019-5-20 09:42 | 显示全部楼层
mekiss123 发表于 2019-5-19 10:55
持续关注,最近被这玩意搞得头晕脑胀的,我用的是mega2560  由于tft屏幕占用了PTX端的spi  只能用模拟spi,网 ...

1."由于tft屏幕占用了PTX端的spi  只能用模拟spi"
这句话不正确,SPI是可以一主多从的,你研究一下这篇文章:
http://www.emtronix.com/article/article20181111.html
如果现成的硬件SPI的nrf库对一主多从有支持的话,你的项目是可以不使用软件SPI的

2. "由于tft屏幕占用了PTX端的spi" / "两个PRX端不定时的发送失败"
从这两句描述可以看出,你的多对一方案是 2个PRX [vs] 1个PTX, 我觉得为了方便实现,应该对调一下: 2个PTX [vs] 1个PRX, 然后PTX即你的副板采用定时上报的方式启动通信。
nrf2401最多可支持 6个PTX [vs] 1个PRX,即6对1

3. "所以主控没用中断的方式用24l01 因为那样会造成屏幕刷新了一半卡顿了"
这句话我没理解,我认为中断恰恰是解决这种问题的好方式:
(1)硬件SPI一主多从
(2)24l01 IRQ使用外部中断模式处理
(3)TFT代码写SPI的接口加中断屏蔽保护,进去之前关中断 出来后开中断

如果你的SPI CLK是8MHz,那么访问nrf2401单次最大耗时绝对不超过3/400微秒,就这么点阻断TFT刷新的时间,肯定不会对刷新造成卡顿


4. 比较抱歉,我这里确实没有软件SPI相关的驱动程序

码字不易,如果觉得学到了真家伙,请我喝碗咸豆花

该用户从未签到

发表于 2019-5-20 11:41 | 显示全部楼层
楼主,非常感谢您的讲解,受益匪浅,最近在用这个东西。我想问几个问题
1.这个编码标准是什么呢?
2.可否讲下内部配置寄存器的SPI时序的工作原理?
3.我用的RF24库,不太清楚如何更改寄存器配置,比如配置发射功率为-18DBM,可否举个例子介绍一下?
谢谢楼主!

该用户从未签到

 楼主| 发表于 2019-5-20 13:53 | 显示全部楼层
a727409529 发表于 2019-5-20 11:41
楼主,非常感谢您的讲解,受益匪浅,最近在用这个东西。我想问几个问题
1.这个编码标准是什么呢?
2.可否讲 ...

1. "这个编码标准是什么呢?"
这句话我没理解是什么意思,"编码"是说的什么编码?

2. 我计划在写完了双向通信原理之后会开始写SPI时序的部分,这里只是简单回答一下吧
   既然你关心的是寄存器的配置,那就只说一下写寄存器的过程:
写寄存器既是通过SPI给nrf2401发送一条命令(格式:命令字节+N个数据字节)
   (1)CSN拉低 (两个作用,即是使能nrf2401的SPI接口,也是让nrf2401知道接下来要开始接收一条命令)
   (2)MOSI输出命令字节,高bit先出,写寄存器命令字节是一个拼接出来的值: 0x20+目标寄存器地址(5bit)
   (3)MOSI依次输出数据字节(即你想给对应寄存器配置的值),多字节时低字节先出,字节内部高bit先出
   (4)CSN拉高,即表示关闭nrf2401的SPI接口,也让nrf2401知道命令结束
注意,写寄存器只能在nrf2401处于PowerDown/Standby-I/Standby-II 这3种工作状态下才有效
如果你还想了解更多又等不及帖子更新的话,可以去找一份官方NRF24L01+的datasheet,看第8章(不长,就五六页吧)

3. 实际操作例程的话,我计划在写完工作原理之后会挑一个库分期写几个demo
   比如第一节只讲如何正确写寄存器,然后再读出来验证我们写入的结果
  第二节在进一步讲如何写入payload,然后触发一下MAX-RT中断
  等等
码字不易,如果觉得学到了真家伙,请我喝碗咸豆花

1人打赏

该用户从未签到

发表于 2019-5-21 09:13 | 显示全部楼层
alicedodo 发表于 2019-5-20 13:53
1. "这个编码标准是什么呢?"
这句话我没理解是什么意思,"编码"是说的什么编码?

好的,谢谢楼主,非常感谢您,可否加个企鹅 727409529 ,常请教下,哈哈

该用户从未签到

发表于 2019-5-21 10:36 | 显示全部楼层
本帖最后由 mekiss123 于 2019-5-21 13:56 编辑

首先非常感谢你的回复,.
1:一主多从的的文章我看了,我还是先用当前的方案继续尝试下,如果解决不了,我就用gpio模拟片选信号试试硬spi驱动nrf24l01的方案,我的数据量不是很多 只是十几路经过处理的传感器的信号.也就20多个三位的整数,.
附:由于 nrf24只能传输char,因此高于125的整数 需要分解传输,懵逼了一上午才解决,解决方式是 在发送端将 整数的高低位 分解,传输完成后 在接收端以同样的方式组合起来, 大家如果碰到可以试试这个思路.
2: 当前用的确实就是这个方式 (一个PTX 两个PRX), 的确要更改,   其实我的设想是, 两个PTX端 平时只在采集数据和接收状态 ,当收到主机发过来的请求信号以后再转为发送状态,将需要发送的数据发送出去 ,这样的话就需要互相通信, 在PTX端用0号外部中断,只采集数据.当中断发生时 将数据整合 发送,然后继续采集数据,不需要PTX端定时发送,    而在PRX端 不用中断方式,是给1号PTX发送一个请求(其实就是一个数字,PTX收到这个数字就动作),然后确定发送成功以后,立即进入接收模式,等待数据的到来. 接收完毕以后 再给2号PTX发送请求...
3:mega2560的主频只有16m,刷新TFT的屏幕很吃力,所以大部分只是静态图像,只有数字部分需要刷新新的数据,还不能整屏刷新刷新一次整屏要好几秒....无语 我的方式是用背景颜色的和当前数值一样的数字覆盖, 所以在覆盖数字的时候中断发生了,就会出现覆盖了一半的情况.... 你说的在刷新时候关中断,刷新完再开我想可以的,只是会不会影响到 数据的接收呢?所以我想的方式就是上面的采用请求的方式,在刷新完以后我再去请求新的数据,这样两不耽误.
我把我找到的软spi的驱动发出来你看看.能不能给尝试修改一下.以我目前的水平还做不到.不胜感激!!!

该用户从未签到

发表于 2019-5-21 10:37 | 显示全部楼层
本帖最后由 mekiss123 于 2019-5-21 10:40 编辑

这是 h文件
#ifndef __nrf24l01_H__
#define __nrf24l01_H__


#include <Arduino.h>


//*****************************************************************************
/* SPI(nRF24L01) 命令 */
#define READ_REG        0x00  // 读寄存器
#define WRITE_REG       0x20  // 写寄存器
#define RD_RX_PLOAD     0x61  // 读RX有效数据1-32字节。读操作全部从字节0开始。当读RX有效数据完成后FIFO寄存器中有效


数据被清除。 应用于接收模式下
#define WR_TX_PLOAD     0xA0  // 写TX有效数据1-32字节。写操作从字节0开始。 应用于发射模式
#define FLUSH_TX        0xE1  // 清除TX FIFO寄存器应用于发射模式下。
#define FLUSH_RX        0xE2  // 清除RX FIFO寄存器应用于接收模式下。 在传输应答信号过程中不应执行此指令。也就是说若传


输应答信号过程中执行此指令的话将使得应答信号不能被完整的传输
#define REUSE_TX_PL     0xE3  // 重新使用上一包有效数据。当CE为高过程中数据包被不断的重新发射,在发射数据包过程//中必


须禁止数据包重利用功能
#define NOP             0xFF  // 空操作。可以用来读状态寄存器


/* Bit Mnemonics */
#define MASK_RX_DR  6
#define MASK_TX_DS  5
#define MASK_MAX_RT 4
#define EN_CRC      3
#define CRCO        2
#define PWR_UP      1
#define PRIM_RX     0
#define ENAA_P5     5
#define ENAA_P4     4
#define ENAA_P3     3
#define ENAA_P2     2
#define ENAA_P1     1
#define ENAA_P0     0
#define ERX_P5      5
#define ERX_P4      4
#define ERX_P3      3
#define ERX_P2      2
#define ERX_P1      1
#define ERX_P0      0
#define AW          0
#define ARD         4
#define ARC         0
#define PLL_LOCK    4
#define RF_DR       3
#define RF_PWR      1
#define LNA_HCURR   0        
#define RX_DR       6
#define TX_DS       5
#define MAX_RT      4
#define RX_P_NO     1
#define TX_FULL     0
#define PLOS_CNT    4
#define ARC_CNT     0
#define TX_REUSE    6
#define FIFO_FULL   5
#define TX_EMPTY    4
#define RX_FULL     1
#define RX_EMPTY    0       


/* SPI(nRF24L01) 寄存器地址*/
#define CONFIG          0x00  // 配置寄存器
#define EN_AA           0x01  // 自动应答
#define EN_RXADDR       0x02  // 接收地址
#define SETUP_AW        0x03  // 地址宽度
#define SETUP_RETR      0x04  // 自动重发
#define RF_CH           0x05  // 射频通道
#define RF_SETUP        0x06  // 射频配置
#define STATUS          0x07  // 寄存器状态
#define OBSERVE_TX      0x08  // 发送检测
#define CD              0x09  // 载波检测
#define RX_ADDR_P0      0x0A  // 数据接收通道0接收地址。最大长度:5个字节先写低字节
#define RX_ADDR_P1      0x0B  // 数据接收通道1接收地址。最大长度:5个字节先写低字节
#define RX_ADDR_P2      0x0C  // 数据接收通道2接收地址。最大长度:5个字节先写低字节
#define RX_ADDR_P3      0x0D  // 数据接收通道3接收地址。最大长度:5个字节先写低字节
#define RX_ADDR_P4      0x0E  // 数据接收通道4接收地址。最大长度:5个字节先写低字节
#define RX_ADDR_P5      0x0F  // 数据接收通道5接收地址。最大长度:5个字节先写低字节
#define TX_ADDR         0x10  // 发送地址先写低字节在增强型ShockBurstTM模式下RX_ADDR_P0与此地址相等
#define RX_PW_P0        0x11  // 接收数据通道0有效数据宽度(1到32字节) 0: 设置不合法 1:  1字节有效数据宽度 …… 32:  32


字节有效数据宽度
#define RX_PW_P1        0x12  // 接收数据通道1有效数据宽度(1到32字节) 0: 设置不合法 1:  1字节有效数据宽度 …… 32:  32


字节有效数据宽度
#define RX_PW_P2        0x13  // 接收数据通道2有效数据宽度(1到32字节) 0: 设置不合法 1:  1字节有效数据宽度 …… 32:  32


字节有效数据宽度
#define RX_PW_P3        0x14  // 接收数据通道3有效数据宽度(1到32字节) 0: 设置不合法 1:  1字节有效数据宽度 …… 32:  32


字节有效数据宽度
#define RX_PW_P4        0x15  // 接收数据通道4有效数据宽度(1到32字节) 0: 设置不合法 1:  1字节有效数据宽度 …… 32:  32


字节有效数据宽度
#define RX_PW_P5        0x16  // 接收数据通道5有效数据宽度(1到32字节) 0: 设置不合法 1:  1字节有效数据宽度 …… 32:  32


字节有效数据宽度
#define FIFO_STATUS     0x17  // FIFO 状态寄存器


/* 数据长度 */
#define ADR_WIDTH    5        // 地址长度
#define TX_PLOAD_WIDTH  32    // 发送数据长度


/* 中断状态 */
#define RX_IRQ                11
#define TX_IRQ                22
#define MAX_RT_IRQ        33


/* 默认端口号
#define CE       33 //5 33  CE_BIT:   Digital Input     Chip Enable Activates RX or TX mode
#define CSN      32 //4 32  CSN BIT:  Digital Input     SPI Chip Select
#define SCK      35 //7 35  SCK BIT:  Digital Input     SPI Clock
#define MOSI     34 //6 34  MOSI BIT: Digital Input     SPI Slave Data Input
#define MISO     37 //9 37  MISO BIT: Digital Output    SPI Slave Data Output, with tri-state option*/
//以上为主控端口


#define CE       27 //5 33  CE_BIT:   Digital Input     Chip Enable Activates RX or TX mode
#define CSN      26 //4 32  CSN BIT:  Digital Input     SPI Chip Select
#define SCK      24 //7 35  SCK BIT:  Digital Input     SPI Clock
#define MOSI     29 //6 34  MOSI BIT: Digital Input     SPI Slave Data Input
#define MISO     25 //9 37  MISO BIT: Digital Output    SPI Slave Data Output, with tri-state option
//以上为挂车端口
#define DEFAULT_CONFIG ((1 << EN_CRC) | (0 << CRCO))


//*****************************************************************************
class nRF24L01 {
public:
        unsigned char cePin;
        unsigned char csnPin;
        unsigned char sckPin;
        unsigned char mosiPin;
        unsigned char misoPin;
        unsigned char channel;
        unsigned char payload;


        nRF24L01();
        void init(void);
        void powerDown(void);
        unsigned char getStatus(void);
        void setTADDR(char * adr);
        void setRADDR(char * adr);
        void flushRx(void);
        void powerUpRx(void);
        void powerUpTx(void);
        void config(void);
        void send(char * data);
        bool isSending(void);
        bool rxFifoEmpty(void);
        bool dataReady(void);
        void getData(char * data);
        int irqStatus(void);
        bool verifySend(void);
private:
        unsigned char PTX;


        unsigned char SPI_RW(unsigned char byte);
        unsigned char SPI_RW_Reg(unsigned char reg, unsigned char value);
        unsigned char SPI_Read(unsigned char reg);
        unsigned char SPI_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes);
        unsigned char SPI_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes);


        void csnHi(void);
        void csnLow(void);


        void ceHi(void);
        void ceLow(void);




};


#endif /* __nrf24l01_H__ */

该用户从未签到

发表于 2019-5-21 10:37 | 显示全部楼层
本帖最后由 mekiss123 于 2019-5-21 11:05 编辑

这是 cpp  能不能帮忙解读一下,底子太薄 实在看不懂
#include "NRF24L01.h"

nRF24L01::nRF24L01() {
        cePin = CE;
        csnPin = CSN;
        sckPin = SCK;
        mosiPin = MOSI;
        misoPin = MISO;
        channel = 40;
        payload = TX_PLOAD_WIDTH;
}

unsigned char nRF24L01::SPI_RW(unsigned char byte) {
        for(int i = 0; i < 8; i++){
                if(byte & 0x80){
                        digitalWrite(mosiPin, HIGH);
                } else {
                        digitalWrite(mosiPin, LOW);
                }
                digitalWrite(sckPin, HIGH);
                byte <<= 1;
                if(digitalRead(misoPin) == 1){
                        byte |= 1;
                }
                digitalWrite(sckPin, LOW);
        }
        return byte;
}

unsigned char nRF24L01::SPI_RW_Reg(unsigned char reg, unsigned char value) {
        unsigned char status;
        csnLow();
        status = SPI_RW(reg);
        SPI_RW(value);
        csnHi();
        return status;
}

unsigned char nRF24L01::SPI_Read(unsigned char reg) {
        unsigned char reg_val;
        csnLow();
        SPI_RW(reg);
        reg_val = SPI_RW(0);
        csnHi();
        return reg_val;
}

unsigned char nRF24L01::SPI_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes) {
        unsigned char status;
        csnLow();
        status = SPI_RW(reg);
        for(int i = 0; i < bytes; i++) {
                pBuf = SPI_RW(0);
        }
        csnHi();
        return status;
}

unsigned char nRF24L01::SPI_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes) {
        unsigned char status;
        csnLow();
        status = SPI_RW(reg);
        for(int i = 0; i < bytes; i++){
                SPI_RW(*pBuf++);
        }
        csnHi();
        return status;
}

void nRF24L01::init(void) {
        pinMode(cePin,  OUTPUT);
        pinMode(csnPin, OUTPUT);
        pinMode(sckPin, OUTPUT);
        pinMode(mosiPin,  OUTPUT);
        pinMode(misoPin, INPUT);

        ceLow();
        csnHi();
}

void nRF24L01::csnHi(void) {
        digitalWrite(csnPin, HIGH);
}

void nRF24L01::csnLow(void) {
        digitalWrite(csnPin, LOW);
}

void nRF24L01::ceHi(void) {
        digitalWrite(cePin, HIGH);
}

void nRF24L01::ceLow(void) {
        digitalWrite(cePin,LOW);
}

void nRF24L01::powerDown(void) {
        ceLow();
        SPI_RW_Reg(WRITE_REG + CONFIG, DEFAULT_CONFIG);
}

unsigned char nRF24L01::getStatus(void) {
        return SPI_Read(STATUS);
}

void nRF24L01::setTADDR(char * adr) {
        SPI_Write_Buf(WRITE_REG + TX_ADDR, (unsigned char *)adr, ADR_WIDTH);
        SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, (unsigned char *)adr, ADR_WIDTH);
        /* 0号读通道必须和发送地址相同,使自动回复生效 */
}

void nRF24L01::setRADDR(char * adr) {
        ceLow();
        SPI_Write_Buf(WRITE_REG + RX_ADDR_P1, (unsigned char *)adr, ADR_WIDTH);
        ceHi();
}

void nRF24L01::flushRx(void) {
        SPI_RW_Reg(FLUSH_RX,0);
}

void nRF24L01::powerUpRx(void) {
        PTX = 0;
        ceLow();
        SPI_RW_Reg(WRITE_REG + CONFIG, DEFAULT_CONFIG | ((1 << PWR_UP) | (1 << PRIM_RX)));
        ceHi();
        SPI_RW_Reg(WRITE_REG + STATUS, (1 << TX_DS) | (1 << MAX_RT));
}

void nRF24L01::powerUpTx(void) {
        PTX = 1;
        SPI_RW_Reg(WRITE_REG + CONFIG, DEFAULT_CONFIG | ((1 << PWR_UP) | (0 << PRIM_RX)));
}

void nRF24L01::config(void) {
        SPI_RW_Reg(WRITE_REG + RF_CH, channel);

        SPI_RW_Reg(WRITE_REG + RX_PW_P0, payload);
        SPI_RW_Reg(WRITE_REG + RX_PW_P1, payload);

        powerUpRx();
        flushRx();
}

void nRF24L01::send(char * data) {
        unsigned char status;
        status = getStatus();

        while(PTX) {
                status = getStatus();

                if((status & ((1 << TX_DS) | (1 << MAX_RT)))) {
                        PTX = 0;
                        break;
                }
        }
        ceLow();
        powerUpTx();
        SPI_RW_Reg(FLUSH_TX,0);
        SPI_Write_Buf(WR_TX_PLOAD,(unsigned char *)data,payload);
        ceHi();
}

bool nRF24L01::isSending(void) {
        unsigned char status;
        if(PTX) {
                status = getStatus();
                if((status & ((1 << TX_DS) | (1 << MAX_RT)))) {
                        powerUpRx();
                        return false;
                }
                return true;
        }
        return false;
}

bool nRF24L01::rxFifoEmpty(void) {
        unsigned char fifoStatus;
        fifoStatus = SPI_Read(FIFO_STATUS);
        return(fifoStatus & (1 << RX_EMPTY));
}

bool nRF24L01::dataReady(void) {
        unsigned char status;
        status = getStatus();
        if(status & (1 << RX_DR)) {
                return 1;
        }
        return !rxFifoEmpty();
}

void nRF24L01::getData(char * data) {
        SPI_Read_Buf(RD_RX_PLOAD,(unsigned char *) data, payload);
        SPI_RW_Reg(WRITE_REG + STATUS, (1 << RX_DR));
}

int nRF24L01::irqStatus(void) {
        unsigned char status;
        status = getStatus();
        if (status & (1 << RX_DR)) {
                return RX_IRQ;
        }
        if (status & (1 << TX_DS)) {
                return TX_IRQ;
        }
        if (status & (1 << MAX_RT)) {
                return MAX_RT_IRQ;
        }
}


bool nRF24L01::verifySend(void) {
        unsigned char status;
        if(PTX) {
                delay(10);
                status = getStatus();
                if (status & (1 << TX_DS)) {
                        powerUpRx();
                        return true;
                }
                if (status & (1 << MAX_RT)) {
                        powerUpRx();
                        return false;
                }
        }
        return false;
}

该用户从未签到

发表于 2019-5-21 11:02 | 显示全部楼层
这两个函数是不是设置 接收和发送呢
void nRF24L01::powerUpRx(void) {
        PTX = 0;
        ceLow();
        SPI_RW_Reg(WRITE_REG + CONFIG, DEFAULT_CONFIG | ((1 << PWR_UP) | (1 << PRIM_RX)));
        ceHi();
        SPI_RW_Reg(WRITE_REG + STATUS, (1 << TX_DS) | (1 << MAX_RT));
}

void nRF24L01::powerUpTx(void) {
        PTX = 1;
        SPI_RW_Reg(WRITE_REG + CONFIG, DEFAULT_CONFIG | ((1 << PWR_UP) | (0 << PRIM_RX)));

点评

你的理解是对的  发表于 2019-5-21 18:07

该用户从未签到

 楼主| 发表于 2019-5-21 18:56 | 显示全部楼层
mekiss123 发表于 2019-5-21 10:37
这是 cpp  能不能帮忙解读一下,底子太薄 实在看不懂
#include "NRF24L01.h"



你的回复篇幅很长,里面提到的问题也很多,我一时间不知道该怎么回答。
想了一下,我还是按照我自己的思路综合回答一下吧,对每个不熟悉nrf2401的同学都有点用。所以不一定你提的每个问题都能得到解答,见谅。


1. 这个库是可以用的,虽然实现手法有些糙,但这份代码对你的项目来说是没必要修改的,于是这里有个问题:为什么要怀疑是库代码有问题而不是自己的使用方式有问题?


2. 再说一下我之前建议你PTX/PRX身份对调,我觉得你应该没理解这么建议的真正原因。
   确实这么一改,你项目中两块板数据交换过程会比现在复杂,但是有一个好处:
   按照我的方式修改之后,只要你的需求里不需要 【动态修改通信频率/波特率/超时时间/重发次数/发射功率】等等参数,你的无线模块只需要在初始化的时候配置一次,后面就再也不用动了,甚至连地址也不用再动了,剩下的只是发送数据/检查IRQ/读取数据之类的操作了。
   对于不熟悉模块的人来说,这个好处有多重要我就不用说了吧?
   
   为什么这么说?
   你发现了没,这个库想让nrf2401实现收发数据的话,必须让nrf2401在PTX和PRX两个身份之间频繁切换,要发数据,必须先切换为PTX身份,要收数据也必须要先切换为PRX,而且两端时间上要尽量同步动作,把握不好的话很容易时间错位导致通信失败。
   这是一种很挫X的方法,没有让模块的性能充分发挥出来。
   在nrf2401的高级模式中,我们可以让模块在不切换身份前提下实现双向通信:使用auto-ack功能。
   如果PTX和PRX都开启了auto-ack,那么:
   (1)W_TX_PAYLOAD命令给PTX写入数据 【手动】
   (2)PTX启动发送 【手动】
   (3)PTX发送完毕后转入接收模式【自动】
   (4)PRX收到数据,触发RX_DR IRQ ,同时自动转入发送模式,回复一个auto-ack,如果你预先在PRX端使用W_ACK_PAYLOAD命令给模块写入了数据,那么auto-ack会自动把数据发给PTX,如果没有数据则auto-ack中不附带数据【自动】
   (5)PTX收到这个auto-ack,触发TX_DS IRQ ,如果ack中有数据,则同时触发 RX_DR IRQ【自动】
   上面这个就是简单过程,不觉得这个模式很好用么?
   
3. 在中断中读写模块的问题
    如果nrf2401和TFT复用硬件SPI,而你的TFT写操作非常耗时,应该将写操作拆分,即不管有多少数据要写入,每次访问硬件SPI最多写比如256字节,再长的话要分多次写,小片数据写之前关中断,写完开中断,如此重复,让模块的中断有机会得到执行。
        如果不复用SPI,既不需要分片,也不需要开关中断,两者是互不影响的,何来中断会卡住TFT刷屏?除非你的代码在中断里呆了太长时间,这样使用中断是非常不合理的,失去了中断本来的意义。
       
4. 题外话,关于如何调试你的程序
   大面儿上说,调试程序是有技巧的:
   你要调试无线通信,就要排除其他因素干扰,先把TFT相关功能关掉,只保留无线通信相关代码,同时多使用串口输出调试信息,同时注意Serial库串口输出是死循环,要注意输出打印的时机,不要对无线模块的关键时序工作流程造成干扰。
   等一切调试妥当了,在打开TFT联合调试。
   
   串口输出调试信息这个东西很有用,写程序的时候要多用。

码字不易,如果觉得学到了真家伙,请我喝碗咸豆花
您需要登录后才可以回帖 登录 | 立即注册  

本版积分规则

Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
快速回复 返回顶部 返回列表