最近胶囊内窥镜项目中用到了业界常用的无线收发模块,即恩智浦公司nRF系列无线收发模块,该模块当前有好几种选择,比如nRF24L01只有无线收发模块,需要外部MCU进行驱动及数据收发,还有nRF24LE1自带单片机内核,即单片机集成在收发模块内。另外还有nRF24xx+USB模块,这种模块使用起来更方便,数据收发后直接跟上位机通信。nRF系列常用模块有nRF24L01、nRF24LE1,当前项目架构是内部胶囊使用的是nRF24LE1无线模块,而外部接收仪(fpga做主控)使用的是nRF24L01模块。这两种无线模块的寄存器配置完全兼容。

下面针对nRF24L01的驱动及数据收发的FPGA实现进行总结。

首先本人之前从未用过nRF2401,因此先看起nRF2401的数据手册。英文手册的特点是内容详实,想查的内容手册里肯定全有,当然也有特殊情况,比如在调试OV公司的相机模块时,OV公司的datasheet真的是shit,反复查找资料结合中文资料以及调试推断才搞清楚出图的几个最重要寄存器的配置。据说咨询OV的FAE需要付费。

闲话少说,继续2401的FPGA驱动配置。英文手册内容详实,但缺点是零散分散,想快速找到配置方式,需要反复阅读,并结合调试进行确认。比如我需要快速对2401进行初始化并将2401配置到接收模式,如果看英文手册你会云里雾里好一会儿,另外FPGA在配置时对各个控制信号的时序需要详细说明,而手册上只有SPI的时序图及相关参数,不能一下子看出各个信号之间的时序要求。因此本人首先结合中文资料理清了nRF2401的初始化流程和接收模式的配置要求(即寄存器配置相关参数),然后结合单片机代码和英文手册确认每一个寄存器的配置,最后将200行的单片机c代码转换成我需要的verilog代码。(ps:HDL玩转嵌套循环真是累啊,C代码轻松几个函数,verilog需要兜兜转转一大圈)

下面首先粘贴上单片机C代码:

#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
#define TX_ADDR_WITDH 5 //发送地址宽度设置为5个字节
#define RX_ADDR_WITDH 5 //接收地址宽度设置为5个字节
#define TX_DATA_WITDH 2 //发送数据宽度
#define RX_DATA_WITDH 2 //接收数据宽度//以下为命令寄存器
#define R_REGISTER 0x00 // 读寄存器
#define W_REGISTER 0x20 // 写寄存器
#define R_RX_PLOAD 0x61 // 读RX FIFO有效数据,1-32字节,当读数据完成后,数据被清除,应用于接收模式
#define W_TX_PLOAD 0xA0 // 写TX FIFO有效数据,1-32字节,写操作从字节0开始,应用于发射模式
#define FLUSH_TX 0xE1 // 清除TX FIFO寄存器,应用于发射模式
#define FLUSH_RX 0xE2 // 清除RX FIFO寄存器,应用于接收模式
#define REUSE_TX_PL 0xE3 // 重新使用上一包有效数据,当CE为高过程中,数据包被不断的重新发射
#define NOP 0xFF // 空操作,可以用来读状态寄存器//以下为寄存器地址
#define CONFIG 0x00 // 配置寄存器
#define EN_AA 0x01 // “自动应答”功能寄存
#define EN_RX_ADDR 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接收地址寄存器
#define RX_ADDR_P1 0x0B // 数据通道1接收地址寄存器
#define RX_ADDR_P2 0x0C // 数据通道2接收地址寄存器
#define RX_ADDR_P3 0x0D // 数据通道3接收地址寄存器
#define RX_ADDR_P4 0x0E // 数据通道4接收地址寄存器
#define RX_ADDR_P5 0x0F // 数据通道5接收地址寄存器
#define TX_ADDR 0x10 // 发送地址寄存器
#define RX_PW_P0 0x11 // 数据通道0有效数据宽度设置寄存器
#define RX_PW_P1 0x12 // 数据通道1有效数据宽度设置寄存器
#define RX_PW_P2 0x13 // 数据通道2有效数据宽度设置寄存器
#define RX_PW_P3 0x14 // 数据通道3有效数据宽度设置寄存器
#define RX_PW_P4 0x15 // 数据通道4有效数据宽度设置寄存器
#define RX_PW_P5 0x16 // 数据通道5有效数据宽度设置寄存器
#define FIFO_STATUS 0x17 // FIFO状态寄存器uchar bdata sta; // 状态变量
#define RX_DR (sta & 0x40) // 接收成功中断标志 0100 0000(sta^6)
#define TX_DS (sta & 0x20) // 发射成功中断标志 0010 0000 (sta^5)
#define MAX_RT (sta & 0x10) // 重发溢出中断标志 0001 0000 (sta^4)//nRF24L01引脚定义
sbit MISO=P2^3;
sbit IRQ=P2^2;
sbit SCK=P2^4;
sbit MOSI=P2^1;
sbit CE=P2^5;
sbit CSN=P2^0;//外围引脚定义
sbit LED=P3^7;uchar code TX_Addr[TX_ADDR_WITDH]={0x34,0x43,0x10,0x10,0x01};
uchar code RX_Addr[RX_ADDR_WITDH]={0x34,0x43,0x10,0x10,0x01};
uchar RX_Buffer[RX_DATA_WITDH]={0};
uchar m=0;
void _delay_us(int x)
{int i,j;for (j=0;j<x;j++)for (i=0;i<12;i++);
}
void _delay_ms(int x)
{int i,j;for (j=0;j<x;j++)for (i=0;i<120;i++);
}//SPI时序函数(1)
uchar SPI_RW(uchar byte)
{uchar i;for(i=0;i<8;i++){if(byte&0x80)MOSI=1;else MOSI=0;byte<<=1;SCK=1;if(MISO)byte|=0x01;SCK=0;}return byte;
}//从寄存器中读一字节(3)
uchar SPI_R_byte(uchar reg)
{uchar reg_value;CSN=0;SPI_RW(reg);reg_value=SPI_RW(0);CSN=1;return reg_value;
}//从寄存器中读多个字节(4)
uchar SPI_R_DBuffer(uchar reg,uchar *Dat_Buffer,uchar Dlen)
{uchar status,i;CSN=0;status=SPI_RW(reg);for(i=0;i<Dlen;i++){Dat_Buffer[i]=SPI_RW(0);}CSN=1;return status;
} //往寄存器中写多个字节(5)
uchar SPI_W_DBuffer(uchar reg,uchar *TX_Dat_Buffer,uchar Dlen)
{uchar status,i;CSN=0;status=SPI_RW(reg);for(i=0;i<Dlen;i++){SPI_RW(TX_Dat_Buffer[i]);}CSN=1;return status;
}//往寄存器写一字节(2)
uchar SPI_W_Reg(uchar reg,uchar value)
{uchar status;//返回状态CSN=0;//SPI片选status=SPI_RW(reg);//写入寄存器地址,同时读取状态SPI_RW(value);//写入一字节CSN=1;//
    return status;//返回状态
}//nRF24L01初始化
void nRF24L01_Init(void)
{_delay_us(100);CE=0;CSN=1;SCK=0;IRQ=1;SPI_W_DBuffer(W_REGISTER+TX_ADDR,TX_Addr,TX_ADDR_WITDH);//本机地址SPI_W_DBuffer(W_REGISTER+RX_ADDR_P0,RX_Addr,RX_ADDR_WITDH);//接收地址SPI_W_Reg(W_REGISTER+EN_AA,0x01); //自动应答SPI_W_Reg(W_REGISTER+EN_RX_ADDR,0x01);//通道一SPI_W_Reg(W_REGISTER+RF_CH,0);//频道SPI_W_Reg(W_REGISTER+RX_PW_P0,RX_DATA_WITDH);//接收数据长度SPI_W_Reg(W_REGISTER+RF_SETUP,0x07);//发射速率
}void nRF24L01_Set_RX_Mode()
{CE=0;SPI_W_Reg(W_REGISTER+CONFIG,0x0f);CE=1;_delay_us(300);
}uchar nRF24L01_RX(uchar *rx_buf)
{uchar value=0;sta=SPI_R_byte(STATUS);if(RX_DR){CE=0;SPI_R_DBuffer(R_RX_PLOAD,rx_buf,RX_DATA_WITDH);SPI_W_Reg(W_REGISTER+STATUS,sta);value=1;CSN=0;SPI_W_Reg(FLUSH_RX,0x00);CSN=1; LED=0;_delay_ms(100);LED=1;_delay_ms(100);}return value;
}void main(){nRF24L01_Init();LED=1;while(1){nRF24L01_Set_RX_Mode();nRF24L01_RX(RX_Buffer);}}
}

如上所示,代码里几个常用函数功能概述:

_delay_us:延时函数us级

_delay_ms:延时函数ms级

SPI_RW:SPI读写时序(最底层spi时序)

SPI_R_byte: 从寄存器里读出一个字节(后面会反复读2401的STATUS寄存器)

SPI_R_DBuffer:连续 读出多个字节,当配置到接收模式后,连续从2401的RX_FIFO中读取数据;

SPI_W_DBuffer:连续写入多个字节,对2401进行收发地址初始化时会用到;

SPI_W_Reg:往寄存器里写入一个字节

nRF24L01_Init:2401初始化

nRF24L01_Set_RX_Mode:2401设置到接收模式

nRF24L01_RX:2401在接收模式下接收RX_FIFO里的数据

// ------------------------------------------------------------------------------

正是反复阅读上述C代码后,提取了各个信号之间的时序关系,并结合英文手册第8章的spi时序参数才理清整个初始化和配置接收模式的状态机流程。还是写C的好,干FPGA搞多重循环不容易,调试也麻烦,FPGA没有单步调试,遇到问题都是看片内资源多不多,多的话可以加大signalTap或者chipscope的采样深度,不够的话需要拉测试引脚,用示波器或者逻辑分析仪来玩转,逻辑分析仪还不是每家公司都有的,毕竟很贵嘛。

下面先粘贴FPGA的.v代码

// nRF2401 ctrl_mod
`timescale 1 ns / 1 psmodule nrf2401_receive_mod
(input             clk,   // 24MHz-> 4MHzinput             rst_n,// nRF2401 Interrupt    input             IRQ_in,// to spi_driver_2401.voutput reg        rece_config_en,        // tx_eninput             rece_config_data_done, // tx_rdyoutput [7:0]      rece_config_data_out,  // tx_dboutput reg        read_en,              // rx_eninput             rx_data_done,         // rx_rdyinput  [7:0]      rx_data,              // rx_dboutput reg        spi_cs_out,output reg        spi_ce_out,output reg        rece_init_done, output reg        rece_data_done);endmodule 

 

spi驱动代码如下`timescale 1 ns/1 ps

module spi_driver_2401
(input clk,input rst_n,// spi interface
    input             spi_din,    // MISOoutput reg        spi_dout,   // MOSIoutput            spi_clk,    // SCKoutput            spi_cs,     // chip select// to nrf2401_receive_mod.v    input             spi_tx_en,  // send enableoutput reg        spi_tx_rdy, // send doneinput  [7:0]      spi_tx_db,  // send byteinput             spi_rx_en,  // receive enable output reg        spi_rx_rdy, // receive doneoutput [7:0]      spi_rx_db,  // receive byteinput             spi_cs_in   //
);
endmodule 

spi驱动很简单,就是将数据发送出去或者接收进来,都是在发送使能和接收使能下将数据发送或者接收进来。数据收发结束后会产生一个时钟周期的完成信号,通知上一级模块。具体可见上述代码。

综上所述,nRF24L01的接收模块代码如上所示,再在nrf2401_receive_mod.v和spi_driver_2401.v上封一层顶层就OK了。

详细工程代码可见我下面的网盘分享链接,利用Quartus13.0编译的工程,带signalTap信号观察窗口。

另外接收模式的初始化流程我会在发送模式里一并写了,此处只贴上代码和基本的说明。

鉴于需要的朋友比较多,大家可加我qq:806518565(或微信搜索806518565也行),谢谢啦!

By 我有风衣~~

~~~~~~~~~~~~~~~~下回见~~~~~~~~~~~~~~~~~~

下回写上发送模式的FPGA配置实现!

转载于:https://www.cnblogs.com/jaymyfriend/p/6984677.html

nRF2401无线模块接收_FPGA实现~~相关推荐

  1. STM32控制NRF24L01无线模块进行通信

    一.NRF2401无线模块 1.模块介绍 功能介绍 (1)2.4Ghz 全球开放ISM 频段免许可证使 2) 最高工作速率2Mbps,高效GFSK调制,抗干扰能力强,特别适合工业控制场合 (3)126 ...

  2. nRF2401A/nRF24L01/nRF24L01+无线模块最常见问题汇集(转)

    俗话说:每个人一生下来什么都会的,都是通过自己努力和探索出来的,NRF系列芯片,刚开始都好奇心加兴趣才来捣鼓它的,刚开始做硬件和软件,没有收发数据弄得整个人头都快炸开了,所以在此和大家分享一下前辈的经 ...

  3. 无线发射器c语言程序代码,单片机编码 无线模块发送与接收 程序

    单片机编码 无线模块发送与接收 程序 模块型号,可在万能的某宝搜索[超再生无线模块] 接收距离:空旷200米[这是卖家说的,有待考验] 接收端在没有收到讯号,会生成间歇性尖状脉冲,data接LED负极 ...

  4. 315M接收 PT2264发送 无线模块的使用

    315M接收 PT2264发送 无线模块的使用 315M接收波形说明 315M接收波形说明 最后的一大段低电平是每次发完数据的静默时间,前面是几帧相同正式的数据. 每帧数据周期大概19.4ms,包含了 ...

  5. 单片机无线模块编码和解码c语言,单片机编码 无线模块发送与接收 程序

    单片机编码 无线模块发送与接收 程序 模块型号,可在万能的某宝搜索[超再生无线模块] 接收距离:空旷200米[这是卖家说的,有待考验] 接收端在没有收到讯号,会生成间歇性尖状脉冲,data接LED负极 ...

  6. 2.4G NRF24L01无线模块总结

    前言   最近野火STM32论坛(www.firebbs.cn)发起了一个开源平衡车的项目,于是就跟着大家一起动手做起了开源平衡车,其中就用到了2.4G NRF24L01无线模块.通过对NRF24L0 ...

  7. 基于python的modbus协议编程_IM5D.6B利用(2.4G)无线模块实现远程控制(基于智能编程任务赛,2019版)...

    点击「蓝色微信名」关注更多比赛信息 引  言 在<中国儿童青少年威盛中国芯计算机表演赛>从第十七届活动开始,搭建了一个全新的互联网技术支持平台,实现了网络在线比赛,在全国建立了三十个省级赛 ...

  8. CC1101超低功耗无线模块在物联网能门锁中的应用

    CC1101是TI的超低功耗无线收发芯片,支持sub-1 GHz频段,其主要针对工业.科研和医疗(ISM)以及短距离无线通信设备(SRD).CC1101可提供对数据包处理.数据缓冲.突发传输.接收信号 ...

  9. 低功耗无线模块超远距离无线传输实现中继的方法

    一.无线中继发展背景 近年来,随着社会的发展和科学技术的进步,人们开始进入数字网络化的智能社会,各种各样的智能设备改变着我们的生活,而无线传输在这些智能化发展中占有不可或缺的地位.在无线网络中,实现终 ...

最新文章

  1. 关于内容分发网络 CDN 的可靠性和冗余性
  2. redis——HyperLogLog
  3. 2019年1月份GitHub上最热门的Java开源项目
  4. centos7 yun安装mysql,CentOS7 yum方式安装MySQL5.7
  5. shell开启飞行模式_手机飞行模式,太神奇了,今天总算明白了,不用真是太浪费了...
  6. 从 Java 替代品到打造完整生态,Kotlin 10 岁了!
  7. c jni 调用java_JNI NDK (AndroidStudio+CMake )实现C C++调用Java代码流程
  8. mysql处理字符串函数,MySQL常用函数--字符串处理
  9. vasp-分子动力学模拟
  10. 简单工具类HttpUtils
  11. Qualcomm Ramdump debugging
  12. CS231n Assiganment#1解析(一)——KNN
  13. [译]关于Android图形系统一些事实真相
  14. ERROR 2002 (HY000): Cant connect to local MySQL server through socket的解决方法
  15. You earned your Program Management Professional (PgMP)® Credential
  16. 鸿蒙生态菁英难,重磅!华为联合西工大开设“鸿蒙生态菁英班”!
  17. 低延迟平价游戏蓝牙耳机推荐,2021值得入手的五款品牌蓝牙耳机
  18. java 自省_javabean的自省机制
  19. oracle数据库_实例_用户_表空间之间的关系
  20. 【智能优化算法】基于黑猩猩算法求解多目标优化问题附matlab代码

热门文章

  1. 基于java计算机组成原理虚拟仿真实验系统计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署
  2. 模拟退火算法详细讲解(含实例python代码)
  3. Linux配置防火墙 开启80端口的方法
  4. JavaScript 代码整洁之道
  5. Linux命令之mount挂载
  6. WebDriver学习笔记(八)windows弹窗(警告弹窗)处理
  7. XFF与Referer(含实操)
  8. 巨杉数据库SequoiaDB在Java开发中的增删改查CURD
  9. Havok物理引擎与Unity3D游戏的结合
  10. Jeep公布首款纯电动SUV;法液空成立合资公司建设上海最大规模氢气充装中心 | 美通社头条...