NXP JN5169使用硬件SPI读写NRF24L01

  • 一、SPI 介绍
  • 二、实现代码
    • 1、轮询模式
      • ① SPI.c
      • ② NRF24L01.c
      • ③ NRF24L01.h
      • ④ uart.c
      • ⑤ Main.c
      • ⑥ 效果图
    • 2、中断模式
      • ① SPI.c
      • ② 效果图

一、SPI 介绍

SPI 总线允许 JN5169 与外围设备之间进行高速同步数据传输。 JN5169 在 SPI 总线上作为主设备运行,并且在 JN5169 CPU 的控制下,连接到 SPI 总线的所有其他设备都应该是从设备。 SPI 总线包括以下功能:

  • 全双工 3 线同步数据传输
  • 可编程传输速率(最高16 Mbit / s)
  • 可编程数据传输长度,最大32位
  • 标准SPI总线模式0、1、2和3
  • 手动或自动从机选择生成(最多3个从机)
  • 可屏蔽事务完成中断
  • LSB 优先或 MSB 优先数据传输
  • 支持延迟的读取边缘

SPI总线框图:

SPI 总线采用简单的移位寄存器数据传输方案。 数据以先进先出的方式移出和移入有源设备,从而允许 SPI 总线设备同时发送和接收数据。 主机从机输入或主机从机输出数据传输与 JN5169 生成的时钟信号 SPICLK 有关。
        JN5169 提供 3 个从机选择 SPISEL0 至 SPISEL2,以允许总线上的三个 SPI 总线外设。 在 DI019 上访问 SPISEL0。 根据配置,在 DIO0 或 DIO14 上访问 SPISEL1。 在 DIO1 或 DIO15 上访问SPISEL2。 这是在软件控制下启用的。 下表详细说明了根据配置,哪些 DIO 用于 SPISEL 信号。

该接口可以在没有软件干预的情况下从 1 位传输到 32 位,并且可以在需要时在传输之间保持从选择线有效,从而可以执行更长的传输。
        当器件复位有效时,所有 SPI 总线主控引脚均配置为输入,且其上拉电阻处于活动状态。 引脚保持此状态,直到使能 SPI 总线主模块或将引脚配置为其他用途为止。

典型的 JN5169 SPI 总线外设连接

SPI 总线上的数据传输速率由 SPICLK 信号确定。 JN5169 支持以时钟分频器选择的 16 MHz 至 125 kHz 的可选数据速率进行传输。 SPICLK 时钟相位和极性均可配置。 时钟相位确定 JN5169 使用 SPICLK 的哪个边沿在 SPIMOSI 线上显示新数据。 另一边将用于从 SPIMISO 线读取数据。 该接口应针对要访问的 SPI 总线从机进行适当配置。

时钟极性 相位 SPI 模式 描述
0 0 0 空闲时 SPICLK 为低电平-第一个边沿为正。 有效数据在第一个时钟之前在 SPIMOSI 上输出,并在每个负沿改变。 SPIMISO 在每个上升沿进行采样。
0 1 1 空闲时 SPICLK 为低电平–第一个边沿为正。 有效数据在 SPIMOSI 的每个上升沿输出。 对 SPIMISO 的每个下降沿进行采样。
1 0 2 空闲时 SPICLK 为高电平–第一个边沿为负。 有效数据在第一个时钟沿之前在 SPIMOSI 上输出,并在每个上升沿更改。 对 SPIMISO 的每个下降沿进行采样。
1 1 3 空闲时 SPICLK 为高电平–第一个边沿为负。 有效数据在 SPIMOSI 的每个下降沿输出。 SPIMISO 在每个上升沿进行采样。

如果系统中要使用多条 SPISEL 线,则必须以数字顺序从 SPISEL0 开始使用它们。 如果需要,可以在事务之间自动将 SPISEL 线置为无效,或者可以在多个事务中保持置位。 对于诸如存储器这样的设备,主设备可以通过不断提供 SPICLK 转换来接收大量数据,选择线保持置位的能力是一个优势,因为它使从设备在整个传输过程中均保持使能状态。
        将从 SPI 总线设置为正确的配置开始事务,然后选择从设备。 传输开始时(1 位至 32 位),数据被放入 FIFO 数据缓冲区并被移出时钟,同时生成相应的 SPICLK 转换。 由于传输是全双工的,因此从机发送的数据位数是相同的。 可以读取此传输期间接收到的数据(1 位至 32 位)。 如果主机仅需要提供多个 SPICLK 转换以允许从机发送数据,则主机应使用伪数据执行发送。 事务完成时可以生成中断,或者可以轮询接口。
        如果从设备希望向 JN5169 发出信号,表明它有要提供的数据,则可以将其连接到可作为中断使能的 DIO 引脚之一。
        下图显示了复杂的 SPI 总线传输,它可以通过 SPI 总线主接口从闪存设备读取数据。 对于许多单独的 SPI 总线访问,从选择线必须保持低电平,因此必须使用手动从选择模式。 然后可以在传输开始时声明所需的从选择(低电平有效)。 可以使用一系列的 8 位和 32 位传输来向 Flash 设备发出命令和地址,然后读回数据。 最后,可以取消选择从属选择以结束事务。

SPI总线波形示例:使用模式0从Flash 读取

二、实现代码

1、轮询模式

① SPI.c

#define MSB      FALSE       //spi从最高位开始传输
#define LSB     TRUE        //spi从最低位开始传输/*** SPI时钟分频系数 = 2 * u8ClockDivider* SPI速率 = 外设时钟 / SPI时钟分频系数 = 外设时钟 / (2 * u8ClockDivider)* u8ClockDivider = 外设时钟 / SPI速率 / 2*/
#define CLOCK_FREQUENCY     16      //外设时钟16M
#define SPI_FREQUENCY        1          //设定SPI速率1M
#define CLOCK_DIVISOR       CLOCK_FREQUENCY / SPI_FREQUENCY / 2 //计算当前SPI速率的时钟分频系数PUBLIC void vSPI_Init()
{vAHI_SpiConfigure(1,       //从机数量1MSB, //高位传输FALSE,    //SPI模式0FALSE,CLOCK_DIVISOR,        //1MFALSE,  //禁止SPI中断FALSE);    //禁止自动选择从机
}//SPI传输接收1个字节
PUBLIC uint8 vSPI_Transfer_1Byte(uint8 dat)
{uint8 ReceiveData = 0;while(bAHI_SpiPollBusy());              /* 等待总线空闲                 */vAHI_SpiStartTransfer(7, dat);          /* 发送7+1=8位数据             */while(bAHI_SpiPollBusy());                /* 等待数据发送完毕             */ReceiveData = u8AHI_SpiReadTransfer8();  /* 读8位数据                   */return (ReceiveData);                  /* 返回接收到的数据             */
}

② NRF24L01.c

#define CE           1 << 1
#define IRQ         1 << 2uint8 TX_ADDRESS[TX_ADR_WIDTH] = { 0x04, 0x03, 0x02, 0x01, 0x00 }; //本地地址
uint8 RX_ADDRESS[RX_ADR_WIDTH] = { 0x04, 0x03, 0x02, 0x01, 0x00 }; //接收地址void vCbSysCtrl (uint32 u32Device, uint32 u32ItemBitmap)
{if(E_AHI_DEVICE_SYSCTRL == u32Device){if(E_AHI_DIO2_INT == u32ItemBitmap){ //IRQ中断vPrintf("NRF IRQ Interrupt!\n");}}
}void vDIO_Init(void) {vAHI_DioSetPullup(CE | IRQ, 0);vAHI_DioSetDirection(IRQ, CE);vAHI_DioInterruptEdge(0, IRQ);     // 下降沿触发vAHI_DioInterruptEnable(IRQ, 0);vAHI_SysCtrlRegisterCallback(vCbSysCtrl);//IRQ中断
}//SPI写寄存器
//reg:指定寄存器地址
//value:写入的值
uint8 vSPI_RW_Reg(uint8 reg, uint8 value) {uint8 status;vAHI_SpiSelect(1);      //选择从机1status = vSPI_Transfer_1Byte(reg); //返回从MISO读出的数据,status应为上次向该寄存器内写的valuevSPI_Transfer_1Byte(value);        //写入寄存器的值vAHI_SpiSelect(0);     //释放从机1return (status);       // 返回状态值
}//读取SPI寄存器值
//reg:要读的寄存器
uint8 vSPI_Read(uint8 reg) {uint8 reg_val;vAHI_SpiSelect(1);        //选择从机1vSPI_Transfer_1Byte(reg);         // 发送寄存器号reg_val = vSPI_Transfer_1Byte(NOP); // 读取寄存器内容vAHI_SpiSelect(0);     //释放从机1return (reg_val);     // 返回状态值
}//在指定位置写指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//bytes:数据长度
//返回值,此次读到的状态寄存器值
uint8 vSPI_Write_Buf(uint8 reg, uint8 *pBuf, uint8 bytes) {uint8 status, byte_ctr;vAHI_SpiSelect(1);        //选择从机1status = vSPI_Transfer_1Byte(reg);      // 发送寄存器值(位置),并读取状态值//Delay10us();for (byte_ctr = 0; byte_ctr < bytes; byte_ctr++) { // 写入数据vSPI_Transfer_1Byte(*pBuf++);}vAHI_SpiSelect(0);        //释放从机1return (status); // 返回读到的状态值
}//在指定位置读出指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//bytes:数据长度
//返回值,此次读到的状态寄存器值
uint8 vSPI_Read_Buf(uint8 reg, uint8 *pBuf, uint8 bytes) {uint8 status, byte_ctr;vAHI_SpiSelect(1);     //选择从机1status = vSPI_Transfer_1Byte(reg); // 发送寄存器值(位置),并读取状态值for (byte_ctr = 0; byte_ctr < bytes; byte_ctr++) {pBuf[byte_ctr] = vSPI_Transfer_1Byte(NOP); // 读出数据}vAHI_SpiSelect(0);       //释放从机1return (status); // 返回读到的状态值
}//检测24L01是否存在
//返回值:0,成功;1,失败
uint8 vNRF24L01_Check(void) {uint8 buf[5] = { 0xA5, 0xA5, 0xA5, 0xA5, 0xA5 };uint8 buf1[5];uint8 i;vAHI_DioSetOutput(0, CE);vSPI_Write_Buf(NRF_WRITE_REG + TX_ADDR, buf, 5);vSPI_Read_Buf(TX_ADDR, buf1, 5); //读出写入的地址vAHI_DioSetOutput(CE, 0);for (i = 0; i < 5; i++)if (buf1[i] != 0xA5)break;if (i != 5) {vPrintf("NFR24L01 Device is Connected fail!\r\n");return (1); //检测24L01错误} else {vPrintf("NFR24L01 Device is Connected OK!\r\n");return (0); //检测到24L01}}//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入TX模式,并可以发送数据了
void vTX_Mode(void) {vAHI_DioSetOutput(0, CE);vSPI_Write_Buf(NRF_WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); //写TX节点的地址vSPI_Write_Buf(NRF_WRITE_REG + RX_ADDR_P0, TX_ADDRESS, RX_ADR_WIDTH); //写RX节点的地址vSPI_RW_Reg(NRF_WRITE_REG + EN_AA, 0x01); //使能自动应答vSPI_RW_Reg(NRF_WRITE_REG + EN_RXADDR, 0x01); //接收数据通道0允许vSPI_RW_Reg(NRF_WRITE_REG + SETUP_RETR, 0x1b); // 自动重发延时500us + 86us,自动重发10次vSPI_RW_Reg(NRF_WRITE_REG + RF_CH, 40); //设置工作通道频率vSPI_RW_Reg(NRF_WRITE_REG + RF_SETUP, 0x07); // 设置发射速率2MHz,发射功率为最大值0dBvSPI_RW_Reg(NRF_WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度vSPI_RW_Reg(NRF_WRITE_REG + CONFIG, 0x0e); CRC使能,16位校验,上电,发送模式vSPI_RW_Reg(FLUSH_RX, NOP);vAHI_DioSetOutput(CE, 0);vAHI_DelayXus(10);
}

③ NRF24L01.h

#include <jendefs.h>void vDIO_Init(void);
//检测24L01是否存在
//返回值:0,成功;1,失败
uint8 vNRF24L01_Check(void);
//读取SPI寄存器值
//reg:要读的寄存器
uint8 vSPI_Read(uint8 reg);
//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入TX模式,并可以发送数据了
void vTX_Mode(void);#define TX_ADR_WIDTH 5      // 5 uints TX address width
#define RX_ADR_WIDTH 5      // 5 uints RX address width
#define TX_PLOAD_WIDTH 32   // 32 uints TX payload
#define RX_PLOAD_WIDTH 32   // 32 uints TX payload#ifndef NOP#define NOP 0xFF
#endif
/*******************命令寄存器***************************/
#define NRF_READ_REG    0x00       // Define read command to register
#define NRF_WRITE_REG   0x20      // Define write command to register
#define RD_RX_PLOAD     0x61    // Define RX payload register address
#define WR_TX_PLOAD     0xA0    // Define TX payload register address
#define FLUSH_TX        0xE1       // Define flush TX register command
#define FLUSH_RX        0xE2       // Define flush RX register command
#define REUSE_TX_PL     0xE3    // Define reuse TX payload register command
#define NOP             0xFF            // Define No Operation, might be used to read status register
/******************寄存器地址****************************/
#define CONFIG          0x00         // 'Config' register addres
#define EN_AA           0x01          // 'Enable Auto Acknowledgment' register address
#define EN_RXADDR       0x02      // 'Enabled RX addresses' register address
#define SETUP_AW        0x03       // 'Setup address width' register address
#define SETUP_RETR      0x04     // 'Setup Auto. Retrans' register address
#define RF_CH           0x05          // 'RF channel' register address
#define RF_SETUP        0x06       // 'RF setup' register address
#define STATUS          0x07         // 'Status' register address
#define OBSERVE_TX      0x08     // 'Observe TX' register address
#define CD              0x09             // 'Carrier Detect' register address
#define RX_ADDR_P0      0x0A     // 'RX address pipe0' register address
#define RX_ADDR_P1      0x0B     // 'RX address pipe1' register address
#define RX_ADDR_P2      0x0C     // 'RX address pipe2' register address
#define RX_ADDR_P3      0x0D     // 'RX address pipe3' register address
#define RX_ADDR_P4      0x0E     // 'RX address pipe4' register address
#define RX_ADDR_P5      0x0F     // 'RX address pipe5' register address
#define TX_ADDR         0x10        // 'TX address' register address
#define RX_PW_P0        0x11       // 'RX payload width, pipe0' register address
#define RX_PW_P1        0x12       // 'RX payload width, pipe1' register address
#define RX_PW_P2        0x13       // 'RX payload width, pipe2' register address
#define RX_PW_P3        0x14       // 'RX payload width, pipe3' register address
#define RX_PW_P4        0x15       // 'RX payload width, pipe4' register address
#define RX_PW_P5        0x16       // 'RX payload width, pipe5' register address
#define FIFO_STATUS     0x17    // 'FIFO Status Register' register address

④ uart.c

串口格式化输出:串口格式化输出
串口代码:UART0 2线模式发送数据

⑤ Main.c

PUBLIC void AppColdStart(void) {uint8 sta;/*等待系统时钟切换为外部32MHz晶振*/while (bAHI_GetClkSource() == TRUE);/*优化闪存等待状态*/vAHI_OptimiseWaitStates();vAHI_WatchdogStop();(void) u32AHI_Init();vSPI_Init();vUartInit();vDIO_Init();vAHI_DelayXms(500);vPrintf("System init!\n");vNRF24L01_Check();vTX_Mode();while (1) {vPrintf("\r\n\r\n");vAHI_DelayXms(2000);sta = vSPI_Read(CONFIG);vPrintf("NRF CONFIG REG: %x \n", sta);sta = vSPI_Read(EN_AA);vPrintf("NRF EN_AA REG: %x \n", sta);sta = vSPI_Read(EN_RXADDR);vPrintf("NRF EN_RXADDR REG: %x \n", sta);sta = vSPI_Read(SETUP_RETR);vPrintf("NRF SETUP_RETR REG: %x \n", sta);sta = vSPI_Read(RF_CH);vPrintf("NRF RF_CH REG: %x \n", sta);sta = vSPI_Read(RF_SETUP);vPrintf("NRF RF_SETUP REG: %x \n", sta);}
}PUBLIC void AppWarmStart(void) {AppColdStart();
}

⑥ 效果图

2、中断模式

① SPI.c

#define MSB      FALSE       //spi从最高位开始传输
#define LSB     TRUE        //spi从最低位开始传输/*** SPI时钟分频系数 = 2 * u8ClockDivider* SPI速率 = 外设时钟 / SPI时钟分频系数 = 外设时钟 / (2 * u8ClockDivider)* u8ClockDivider = 外设时钟 / SPI速率 / 2*/
#define CLOCK_FREQUENCY     16      //外设时钟16M
#define SPI_FREQUENCY        1          //设定SPI速率1M
#define CLOCK_DIVISOR       CLOCK_FREQUENCY / SPI_FREQUENCY / 2 //计算当前SPI速率的时钟分频系数bool flag;                    //数据传输状态void vCbSpiMasterInt(uint32 u32Device, uint32 u32ItemBitmap)
{if(E_AHI_DEVICE_SPIM == u32Device){          //SPI主机中断if(E_AHI_SPIM_TX_MASK == u32ItemBitmap){ //传输完成flag = FALSE;}}
}PUBLIC void vSPI_Init()
{vAHI_SpiConfigure(1,       //从机数量1MSB, //高位传输FALSE,    //SPI模式0FALSE,CLOCK_DIVISOR,        //1MTRUE,   //开启SPI中断FALSE);    //禁止自动选择从机vAHI_SpiRegisterCallback(vCbSpiMasterInt);flag = TRUE;           //初始化数据传输状态
}//SPI传输接收1个字节
PUBLIC uint8 vSPI_Transfer_1Byte(uint8 dat)
{uint8 ReceiveData = 0;        //SPI接收的数据flag = TRUE;vAHI_SpiStartTransfer(7, dat);           /* 发送7+1=8位数据             */vAHI_DelayXus(10);            //必须加延时,等待中断处理完成while(flag);             /* 等待数据发送完毕             */ReceiveData = u8AHI_SpiReadTransfer8();  /* 读8位数据                   */return (ReceiveData);                  /* 返回接收到的数据             */
}

滴答定时器延时代码:x us延时程序实现

其他部分代码和轮询模式一致!

② 效果图

NXP JN5169使用硬件SPI读写NRF24L01相关推荐

  1. STM32F1硬件SPI驱动nRF24L01通过按键控制数据收发带状态反馈

    STM32F1硬件SPI驱动nRF24L01通过按键控制数据收发带状态反馈

  2. [学习笔记]STM32F1硬件SPI读写W25Qx(寄存器、标准库、HAL库)

    目录 10. 硬件SPI读写W25Qx 0. 博主调侃: 1. 实验内容及步骤: 2. 硬件说明 3. 步骤详细讲解 3.1 RCC 3.2 配置GPIO 3.3 硬件SPI配置 3.4 发送和接收过 ...

  3. STM32学习笔记之硬件SPI读写与极性设置

    废话不多说讲重点!!! [软件中如何设置SPI的极性和相位]  SPI分主设备和从设备,两者通过SPI协议通讯. 而设置SPI的模式,是从设备的模式,决定了主设备的模式.  所以要先去搞懂从设备的SP ...

  4. 梳理STM32F429之通信传输部分---NO.8 硬件SPI

    目录 一.STM32 的 SPI 特性及架构: 二.SPI 初始化结构体详解: 三.硬件SPI-读写串行 FLASH 实验 一.STM32 的 SPI 特性及架构: 1.引脚简介: (1)   (Sl ...

  5. FM1722/02NL+STM32硬件SPI调试笔记

    1.介绍 FM17XX 系列通用非接触读卡机芯片是复旦微电子股份有限公司设计的,基于 ISO14443 标准的系 列通用非接触卡读卡机芯片,采用 0.6 微米 CMOS EEPROM 工艺. FM17 ...

  6. STM32F030 HAL库硬件SPI操作W25Q16存储芯片(二)

    上篇文章介绍了W25Q16芯片的一些基本信息,这篇主要介绍编程操作. /*封装读写操作 SPI 读写一个字节 //TxData:要写入的字节 //返回值:读取到的字节 */ uint8_t SPI_R ...

  7. NXP JN5169 读写片外 FLASH

    NXP JN5169 读写片外 FLASH 一.原理图 二.读写兼容的片外 FLASH 设备 三.读写不兼容的片外 FLASH 设备 一.原理图 二.读写兼容的片外 FLASH 设备 JN5169 片 ...

  8. 下载的内容:MSP430F149利用硬件SPI口读写串行Flash M25P64

    /*****************<SPI_Flash.C>**************************************/ #include "SPI_Flas ...

  9. 基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522

    目录 1. 实验硬件及原理图 1.1 RFID硬件 1.2 硬件原理图 2. 单片机与RFID硬件模块分析 3. 利用STM32CubeMX创建MDK工程 3.1 STM32CubeMX工程创建 3. ...

  10. PCM开发板模块实验指导--SPI读写PSRAM64实验

    模块推荐:https://item.taobao.com/item.htm?ft=t&id=671629736762         32---SPI读写PSRAM64实验实验:        ...

最新文章

  1. 【运维学习笔记】在 vSphere Client上创建新的虚拟机
  2. 您有一个新的订单mp3在线_Airtable,不仅仅是强大的在线表格应用,而是一个新物种...
  3. 图神经网络(AliGraph)在阿里巴巴的发展与应用
  4. 成长与迁移,全球半导体格局演变
  5. 微软拼音输入法2007状态栏无法显示!
  6. python中函数可以赋值给一个变量_python中函数赋值给变量时的问题注意详解
  7. mysql的dml全,MySQL数据管理----DML语言(全记住)(示例代码)
  8. java动态规划算阶乘_动态规划算法
  9. Angular - angular2升级到angular8
  10. java 使用适当的签名_关于数字签名和policy文件设置!
  11. Leetcode每日一题:197.rising-temperature(上升的温度)
  12. 烟花视频软件测试,基于视频分析的烟火自动检测预警系统
  13. SpringCloud之高可用的分布式配置中心(Spring Cloud Config)(七)
  14. Hbase之表的设计
  15. HTML5、canvas颜色拾取器
  16. Linux CentOS 7.2 排查系统木马
  17. EasyOCR,识别图片中的文字真的so easy
  18. 记一次m3u8文件转mp4的经历
  19. 关于DPABI头动参数问题
  20. 大气精美PHP虚拟资源网站源码

热门文章

  1. 读书笔记之《史记》读后感心得体会
  2. 京东联盟/好京客API与京东默认PID申请教程
  3. linux打开txt文件命令_linux系统文件及常用命令
  4. 触发器、锁存器、边沿触发器——最容易理解的方式
  5. 英语口语中的音变现象及读音规则
  6. 有道智云OCR图片识别文字+返回数据处理技巧(实现语言-按键精灵脚本请求识别+java服务端处理数据)...
  7. Windows系统磁盘清理C盘扩容
  8. 第一届程序设计竞赛题解(G题)
  9. Unity Editor 编辑器扩展 九 Gizmos
  10. java long to int_Java long(Long)与int(Integer)之间的转换