EDA365电子论坛网
标题:
arm学习笔记,NRF24L01学习全攻略
[打印本页]
作者:
Taio
时间:
2018-10-25 08:00
标题:
arm学习笔记,NRF24L01学习全攻略
arm学习笔记,NRF24L01学习全攻略
) W% |, t# T! O* I
) [: e$ m( G& B( g6 }" X
8 P0 ~! e* A( C
NRF24L01和其它外设差不多,SPI总线的代码也好写.有一种注意的就是,你丢任何命令进去,NRF24L01会第一时间丢0x07号寄存器的数据给你.是同步的,丢一个BIT的指令,就收一个BIT的数据.
0 V+ V& Q9 {9 R- X c: ?
最先的是:要用到的头文件.
: q3 f4 ~3 l9 l Y; z
! Z, ~2 k2 c5 G4 ?. @6 M0 N
#include "sys.h"
* |) @) _5 ~' V9 v9 R' P( t; X. y
#include "usart.h"
+ \6 t, b4 p% i* Q! K! o' {, Z: Y& a% N
#include "delay.h"
3 w, \' i6 D" N+ h" u
- g* [6 s, E* Z: @" m8 {5 D+ _
J k2 b4 Y7 f5 _$ K
然后是定义寄存器操作命令,这只是为了代码的可
读性好一些
.
( g2 j, K A- m" f5 h8 c
8 o# k: @ q9 D! b9 E1 u( N
//NRF24L01寄存器操作命令
, N- v3 D$ W; W* n
#define READ_REG 0x00 //读配置寄存器,低5位为寄存器地址
+ u7 E/ S8 D J4 ]9 i) J
#define WRITE_REG 0x20 //写配置寄存器,低5位为寄存器地址
" k( y7 N2 x: p; ^
#define RD_RX_PLOAD 0x61 //读RX有效数据,1~32字节
* K' o$ c3 @8 V
#define WR_TX_PLOAD 0xA0 //写TX有效数据,1~32字节
7 u$ y/ s: D3 J
#define FLUSH_TX 0xE1 //清除TXFIFO寄存器.发射模式下用
5 _1 q4 F) {) a7 p+ e
#define FLUSH_RX 0xE2 //清除RXFIFO寄存器.接收模式下用
% j. U% c' W+ C" z, T. k# W% N
#define REUSE_TX_PL 0xE3 //重新使用上一包数据,CE为高,数据包被不断发送.
1 C& x8 l% e3 m
#define NOP 0xFF //空操作,可以用来读状态寄存器
j' B4 t# |9 }2 {! A# U4 e
0 @1 |& w7 e, X( h. s9 U$ W+ y
//SPI(NRF24L01)寄存器地址
% h2 W/ y8 N* v1 z) u8 H+ S9 P
#define CONFIG 0x00 //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;
) s* ~ v5 m2 x+ e
//bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能
* O* r- L- P1 G! _
#define EN_AA 0x01 //使能自动应答功能 bit0~5,对应通道0~5
2 m5 b* O0 b5 B) m \% L" B; u3 v h
#define EN_RXADDR 0x02 //接收地址允许,bit0~5,对应通道0~5
1 m: U; Z. U9 ]' `: B% i: P* c, z
#define SETUP_AW 0x03 //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节;
% L/ r- ?0 G' B e k; D( Y" P
#define SETUP_RETR 0x04 //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时250*x+86us
. J7 X9 O; u- V1 y7 l: ^* s# ~6 P% O
#define RF_CH 0x05 //RF通道,bit6:0,工作通道频率;
3 C- K1 |' I# d+ v* g
#define RF_SETUP 0x06 //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益
. H4 ?: m: `$ Q! Z7 N& M7 Y4 `
#define STATUS 0x07 //状态寄存器;bit0:TXFIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发
, ]1 |5 R$ P! O* r2 C
//bit5:数据发送完成中断;bit6:接收数据中断;
$ o Z0 }+ R0 p8 O& h3 I/ j
( U. l7 W4 {; J0 W& Y$ T" v8 s' X+ o
#define OBSERVE_TX 0x08 //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器
" [# S5 a" b9 x9 K, V2 C
#define CD 0x09 //载波检测寄存器,bit0,载波检测;
# F' R, t8 r* G
#define RX_ADDR_P0 0x0A //数据通道0接收地址,最大长度5个字节,低字节在前
! g7 y0 Q* q7 T# @( I1 h3 o& q
#define RX_ADDR_P1 0x0B //数据通道1接收地址,最大长度5个字节,低字节在前
' J6 F+ o1 P% X' }7 N
#define RX_ADDR_P2 0x0C //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
" ^" d# I' K* ^
#define RX_ADDR_P3 0x0D //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
0 ?: S$ ]0 d' W' R
#define RX_ADDR_P4 0x0E //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
, c' Z: m7 u/ z- Y
#define RX_ADDR_P5 0x0F //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
% [, x5 t3 j }) a
#define TX_ADDR 0x10 //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等
! J+ { [! K8 q
#define RX_PW_P0 0x11 //接收数据通道0有效数据宽度(1~32字节),设置为0则非法
5 ?& L" J8 T4 m) r5 L# N7 |- `
#define RX_PW_P1 0x12 //接收数据通道1有效数据宽度(1~32字节),设置为0则非法
7 N& T- Z/ k, X- P1 j
#define RX_PW_P2 0x13 //接收数据通道2有效数据宽度(1~32字节),设置为0则非法
- B9 @9 |' h& i: I& L) L
#define RX_PW_P3 0x14 //接收数据通道3有效数据宽度(1~32字节),设置为0则非法
9 \+ k7 [& T5 P1 G* l
#define RX_PW_P4 0x15 //接收数据通道4有效数据宽度(1~32字节),设置为0则非法
5 z5 m1 \: ^! k$ Z& m" t- x
#define RX_PW_P5 0x16 //接收数据通道5有效数据宽度(1~32字节),设置为0则非法
8 [0 ^1 C( y& h" F' s% D( @
#define FIFO_STATUS 0x17 //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RXFIFO满标志;bit2,3,保留 bit4,TX FIFO空标志;bit5,TXFIFO满标志;bit6,1,循环发送上一数据包.0,不循环;
8 F c3 f/ l( n, \: Y/ |
0 E! z( f3 V1 Q! E9 Z
+ ?5 m$ E; J0 v
//24L01发送接收数据宽度定义
+ b) w) n% d3 W+ `6 J
#define TX_ADR_WIDTH 5 //5字节的地址宽度
; u2 i/ d& w/ C; ~+ `8 T
#define RX_ADR_WIDTH 5 //5字节的地址宽度
+ w) f0 T+ e r1 V' h
#define TX_PLOAD_WIDTH 32 //20字节的用户数据宽度
5 }" A! g: ~4 v" X
#define RX_PLOAD_WIDTH 32 //20字节的用户数据宽度
& `! X/ b* J; ]2 g4 T3 o- w$ J
3 [. M, p" Q/ ?9 }5 I4 ~8 [* r
/ {. R3 w1 G) d/ I \( L
#define MAX_TX 0x10 //达到最大发送次数中断
, k7 g; C5 Z7 t$ m; I
#define TX_OK 0x20 //TX发送完成中断
3 e: S! D. Q- g3 G
#define RX_OK 0x40 //接收到数据中断
& {; D) n C, K# L8 o b% P! ` \& P/ Z
+ |' ?7 I$ ^$ T
//24L01引脚
: K. f j* Y) z- [) R( [
#define NRF24L01_SCK PAout(5)
0 f& z$ u5 f1 I: S! _9 Y
#define NRF24L01_MISO PAin(6)
3 P5 T3 t9 w6 Y" s3 G" S
#define NRF24L01_MOSI PAout(7)
6 d* g. `' {. r Q2 o/ E7 F r' Q& c. h
8 s" `, ?" ` ~
#define NRF24L01_CE PAout(4)//24L01片选信号
# R8 @* p& N+ _6 @# y
#define NRF24L01_CSN PCout(4) //SPI片选信号
9 r- R( o5 d3 r7 [. ]+ j
#define NRF24L01_IRQ PCin(5) //IRQ主机数据输入
5 y1 r Q, D% H& r( ]3 `/ ~* Y
0 ?5 Z9 a) H4 r1 V" L
- U" b6 N( Z; V5 u
然后是发送地址的设定:
7 |/ l; H: e: V2 Y7 b
Z! w K. E+ b. h4 w; a# ~
const u8 TX_ADDRESS[TX_ADR_WIDTH]={0xe7,0xe7,0xe7,0xe7,0xe7};//发送地址
0 b5 G1 k' A: w8 a, t: v' N
& ~# l9 q) T/ S& h) Y8 G
const u8 RX_ADDRESS [RX_ADR_WIDTH]={0x01,0x01,0xc2,0xc2,0xc2};//接收0通道地址
% h0 l, Q% C6 i/ @
const u8 RX_ADDRESS1[RX_ADR_WIDTH]={0x02,
0x01,0xc2,0xc2,0xc2
};//接收1通道地址
2 T4 q$ V, R( m/ K2 M
( R, ?. e Q( T7 s
const u8 RX_ADDRESS2[RX_ADR_WIDTH]={
0xc2,0xc2,0xc2,0xc1,
0x03};//接收2通道地址
* ?6 {! d2 h. q4 S: V
const u8 RX_ADDRESS3[RX_ADR_WIDTH]={
0xc2,0xc2,0xc2,0xc1,
0x04};//接收3通道地址
$ U- \3 N( J/ A# D! e
const u8 RX_ADDRESS4[RX_ADR_WIDTH]={
0x02,0xc2,0xc2,0xc1,
0x05};//接收4通道地址
( D F9 ^3 C: S9 D' d" K3 s* r7 |3 f
const u8 RX_ADDRESS5[RX_ADR_WIDTH]={
0xc2,0xc2,0xc2,0xc1,
0x06};//接收5通道地址
7 Q% a) t5 ]0 t( v# ]
9 a! C/ ^# i+ V" u2 T" |
这里我要特别说的是:接收
2
通道地址,接收
3
通道地址,接收
4
通道地址,接收
5
通道地址,高
8
位到
39
位必须与接收
1
通道的地址的高8位到39位相同.
6 ^3 Z2 a' V" V, h6 ?
如上例子,橙色的一定要和红色相同.这样的话通道1可以设40位的地址,2,3,4,5可在通道1的基础上设别外256个地址.可能厂家目的就是节省那12个8位寄存器(橙色那堆)的成本.=.=!!
$ a, q4 B/ r- E% H a
6 p/ B6 D/ h' @% r
5 O' k& t7 h2 r/ p, j2 a/ b
通道0可与通道1没关系爱怎么设就怎么设.
1 L, n9 t' O) n* ?) w! x4 z
那有人问如我5个通道的地址都一样,行不行,我告诉大家, 行!!!!这和说明书上的不一样.
1 G- |3 x! d( U+ z4 [/ a
3 b: G+ D% [/ U0 n! q7 E/ Q
) J) G2 k- A; V
如果地址都相同,读出来数据的是频道号数最大的那个频道.就是接收5通道.大家可以做下实验,看对不对.
# A! B5 v* `$ p2 w7 C2 }) T
/ x$ c6 \0 s# L: @
9 K: R5 ~" ^& m( o$ e$ Q8 [& d' \. s, _
好了,我们可以这写代码了.
; h# J1 a- U* Y K, M- Y7 B3 f9 l
, |! r1 ~/ n. b1 a' _8 J
//初始化24L01的IO口
9 t6 h6 `- @) ?# K
void NRF24L01_Init(void)
$ V" R- i2 q& M) E: h8 x
{
; g$ c1 I2 c% e5 i) Z" F: Y
RCC->APB2ENR|=1<<2; //使能PORTA口时钟
2 K) h9 E! }: _% e2 z1 U
RCC->APB2ENR|=1<<4; //使能PORTC口时钟
( A# B1 l% [0 p" E6 Y: z
% r! r i) r- b
GPIOA->CRL&=0X0000FFFF; //MOSI MISOSCK CE
* m7 i ?) k" O& O5 A6 E3 p
GPIOA->CRL|=0X38330000;
$ @2 e8 M& x5 H: d
GPIOA->ODR|=0xf0;//7<<5; //PA4.5.6.7 输出1
6 _+ P1 s* L% d {) C4 H6 o m# `
GPIOC->CRL&=0XFF00FFFF; //PC4 CSN 输出 PC5 IRQ 输入
0 a- x- |0 u; m3 N. v- ^
GPIOC->CRL|=0X00830000;
( Z5 \9 i& U( E
GPIOC->ODR|=0x30;//3<<4; //上拉
0 ^+ ^! G3 k6 c. Y6 N
u) _ Q# a8 {5 Z# ]) L
NRF24L01_CE=0;
9 h* L1 Z2 d) D
NRF24L01_CSN=1; //SPI片选取消
8 }. x9 O# H% x: a; G9 c
NRF24L01_SCK =0; //时钟置底
+ V2 Q/ h0 }' b4 n, E- i
}
' Z( P/ Y- ?, V3 I5 K
& A, B i- o; I" o
) x7 s! B, K! l/ k0 L
" E& t( v T* x' N, w
因为我们还没学会ARM的SPI数据总线,所以和51一样,我们模似出SPI出来.这是读写
字
的代码.
3 H2 t& _0 j K8 v
. F a: T9 x1 @# e( P! l( N
u8 SPIx_ReadWriteByte(u8 data)
' H/ y. A" L* V- ~4 F2 o1 Q/ _
{
! n& k8 q7 d# X5 ~# S
u8 i,temp;
$ f' R4 e8 T5 W0 M# s- H
temp=data;
! p7 X& S W" d! F( F- U8 V
7 T- Y0 U. A5 f: G
for (i=0;i<8;i++)
, a$ r5 V; B. O& n1 Z4 [3 `3 G; ]
{
- V. a" K5 I# |# {5 V& W
if((temp&0x80)==0)
' M) ^- ~+ s$ f1 x8 l
{
' Y, E9 a. }+ e$ S) Q
NRF24L01_MOSI=0;
0 }; \0 F# n0 H( _! o* N
}
, i$ S# o7 p5 u @5 v$ ?* _' k
else
2 l* x2 v: E! s+ Q- D6 K
{
5 ]2 F6 T Z {! a B
NRF24L01_MOSI=1;
4 Y7 F: U) b: t2 r
}
# v9 J8 y9 i$ g6 x
data=(data<<1);
5 {# w( w. x# K% X+ X6 |' h- A
temp=data;
* L& r( `5 J% u% \# ^6 D
, _7 j5 t! v. b# t
NRF24L01_SCK =1;
//
时钟线 上升沿 的时候 从机丢到主机
3 E0 L& J0 a! r
& R3 d5 r5 {+ S# [) ]& K' X
data |=NRF24L01_MISO;
# C+ t) Y4 q p1 G; f8 e: C
delay_us(10);
, M, W. _. H5 y0 |3 ^3 M, J
) _: m' {! D7 _6 }# c
NRF24L01_SCK=0;
//
时钟线下降沿的时候从主机丢到从机
0 j# b' ^! g( d' ^. r# c
delay_us(10);
6 q2 P" E' A; q$ S" @4 J3 z
}
( S( m9 _2 t w0 X$ y! |
6 w: M8 N6 z( \; P
return (data);
& G9 y ]0 B5 P7 @7 o K: K- e
}
, Y: ]! b% k4 J h( c) g
7 k- v) M0 D' S0 r
看到没,一读一写一个周期内搞定.上边的
红字
与
橙字
.这里是双工通信,我们首先丢进去的是指令,同时NRF24返回状态寄存器里的数据,然后如果还要写进数据就直接写进数据,
8 T) q' C* T" l/ ~ O
如果要读出数据呢,怎么办呢?因为读和写是同时的呀?有办法的,那就直接写进0x00或0xff.NRF24不会理会这些杂碎的,专心输出数据给你.因为它要的是时钟信号.
8 V- Z9 A8 q( ~% |2 p0 B
8 I2 [$ o, T. {
好以下就是
读写数据
的代码了:
2 i7 k0 m( g/ p/ a) c$ C# u7 ?
: ~5 d- k* R# c! c; ~
//SPI写寄存器
* T# X: k* ^, r, B6 }7 o9 E! [
//reg:指定寄存器地址
) o) u/ m' U2 N% K0 ]2 l$ @" T+ T5 `
//value:写入的值
. t( W6 }* U6 P0 n
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
' C3 u8 ], ?; R; g b$ X& J2 g y
{
' g2 i, A8 L8 A
u8 status;
( @" i+ K- p- }8 I5 |5 [
NRF24L01_CSN=0; //使能SPI传输
9 u9 U" b- g! ^' g9 I8 P! R1 Z
status=SPIx_ReadWriteByte(reg);//发送寄存器号
) j( N" N3 \. V) {
SPIx_ReadWriteByte(value); //写入寄存器的值
& z+ L# m' b* C* K6 \4 U1 G
NRF24L01_CSN=1; //禁止SPI传输
& Q" Y* O: b5 e j1 p
return(status); //返回状态值
4 J$ U& ], J1 a0 Q, g+ {5 `
}
) y" K) S) j. K5 F# f
9 K6 q% m1 [' p; _6 P+ ^
* [" l) V) p( }# B* p( r/ K' ]
//SPI读取寄存器值
4 [" b& @# y9 C& ^' |7 O9 ^4 Q
//reg:要读的寄存器
$ t. W, s2 T) _
u8 NRF24L01_Read_Reg(u8 reg)
4 X% M8 R2 `) [
{
- x4 t3 S, `% h$ _4 E
u8 reg_val;
1 o. }/ D% X7 F6 ~# B% [
NRF24L01_CSN = 0; //使能SPI传输
' J0 T, ^8 a4 t3 p
SPIx_ReadWriteByte(reg); //发送寄存器号
Z$ K* ?; [0 ~1 A5 [( X: C
reg_val=SPIx_ReadWriteByte(0X00);//读取寄存器内容
3 A: e' R8 e' K+ x2 o2 U |1 R
NRF24L01_CSN= 1; //禁止SPI传输
) D$ P7 v, S6 Y6 Z) T* e1 t1 ^0 N" z
return(reg_val); //返回状态值
& z+ w9 R _5 E5 C& E: w1 w; o
}
0 l& Y" t( X3 o5 V- e
& @/ w9 K0 G* n2 v# \. e
多好,同时还能得到
返回状态值,
买一送一呀.
1 h- i; l( _5 `/ ]4 ^+ f$ G
( r" R/ P. @6 `* t( v2 {$ h
以上代码是不是很简单!
( G& P3 @' l- }: U( B
5 a, d/ o! \/ U1 s" w- D- r
好了,如我们要读写一堆数据怎么办?写个丢和收一堆数据的代码吧.直接剪原子兄的代码.
: l$ I: b. Q0 M7 A1 `* `' E4 D6 C7 B& s6 R
: x8 O! f" C3 L$ W% _. o6 A2 n
//在指定位置读出指定长度的数据
0 |7 Q' [# P _- R2 ]- k. F
//reg:寄存器(位置)
5 l# L* A" B! }8 V' ~7 Z- d
//*pBuf:数据指针
' ^! ^! M* V, s3 _. w% m* J' C
//len:数据长度
# x# `, y+ m: m8 k# N6 f
//返回值,此次读到的状态寄存器值
) b/ M0 o7 B, l# n
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)
* {% o" D9 W% @+ W, E( @9 H0 N
{
9 c, R( w+ z- R) F; ~+ J/ y+ e
u8 status,u8_ctr;
/ `# p5 }6 q" Y: P! j/ d& j" P
NRF24L01_CSN= 0; //使能SPI传输
5 \- `. x' C5 {0 S, L3 f/ `; P
status=SPIx_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值
: O: U$ I. e* f
for(u8_ctr=0;u8_ctr
! X7 {1 t( _2 i/ V; m
NRF24L01_CSN=1; //关闭SPI传输
4 j& W+ W1 d- t. U
returnstatus; //返回读到的状态值
# O( I1 G; W3 G9 w1 `+ b$ U
}
, P6 ?' w G. ?0 P# D
; H3 W3 v6 o$ R( D) z. ~, W
1 E/ N7 b- W% [
//在指定位置写指定长度的数据
0 q& s/ U1 `% f
//reg:寄存器(位置)
; W0 J; g, h9 [9 b, J
//*pBuf:数据指针
4 M% m2 o) ]# ]- `/ E: j! c
//len:数据长度
, o2 Q% s4 E9 d
//返回值,此次读到的状态寄存器值
+ z1 k) _. f R e; C6 g' x
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len)
: C2 y! r- ]" x, Y5 j- f
{
0 A' {" S- A9 A8 e! u% V. G5 E
u8 status,u8_ctr;
9 e7 K( K* v' |. R! x8 T, @" Q1 Y7 o
NRF24L01_CSN = 0; //使能SPI传输
7 I; P6 f V: v! D' }; P
status =SPIx_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值
3 k) Z3 `+ |3 a2 |
for(u8_ctr=0; u8_ctr
" A i( _$ Y4 Q, g C
NRF24L01_CSN= 1; //关闭SPI传输
) u/ p3 I" M, u# y* C+ v# {3 G8 |" W. |
returnstatus; //返回读到的状态值
: @% d2 X4 P' ^) K! }0 p
}
: v2 O' e+ T* ]. T1 [6 \1 e
. {3 ~9 }: Z. N
" B( x% U3 A" W& ?& G
首先,是设置发送模式.
( q- A8 r7 C, j* z* l
$ m. H# l2 u& Q+ W
//该函数初始化NRF24L01到TX模式
M7 ]. j# n* I+ V9 ]( Z
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNAHCURR
1 T" a9 X1 K B
//PWR_UP,CRC使能
: k8 |" |; ^, I, Z: J! c/ `( r/ `
//当CE变高后,即进入RX模式,并可以接收数据了
- I) j# I& g1 a! r3 c6 l: k
//CE为高大于10us,则启动发送.
) @+ ~: K: u5 Z1 B5 O/ ]
void TX_Mode(void)
# y b+ l7 w& X, Q: r- e* M4 Q
{
+ {& V# [# H4 k8 s: R3 Y5 S$ j$ t. U
NRF24L01_CE=0;
2 @+ S- P+ E# x2 ~" C$ G4 R1 S
* B0 a( d* c' Y$ R& \6 L3 e
NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
3 W( T6 ?8 b7 u/ M* A9 p) c f2 y
; e0 F3 A" v& N/ L3 {, p
NRF24L01_Write_Buf(WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
. ~, g+ W5 A: ]1 S8 \
NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);//设置TX节点地址,主要为了使能ACK
: V" R" R1 g0 }+ z
0 X' z0 c- u. A: e4 h, f
NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01); //使能通道0的自动应答
; o. e4 ^! u" O" [* }' ]
NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址
- \! @( y( h! _5 @/ Q
NRF24L01_Write_Reg(WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us +86us;最大自动重发次数:10次
2 `( C7 g: Q) A2 j. ~8 {" e
NRF24L01_Write_Reg(WRITE_REG+RF_CH,40); //设置RF通道为40
6 R Y" D3 o. M& ]( o; c
NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启
8 Z% H3 `* f. \" E7 G$ X
NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0e); //配置基本工作模式的参数
WR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
/ e1 K" |! U# p P( F5 S3 [% f
/ g+ k1 x. z" W$ m& X
NRF24L01_CE=1;//CE为高,10us后启动发送
( o) p! A. Y) k; W( H- q. R2 j5 H
}
6 X( X; d8 y, h* Y5 O6 T
" k' b3 r. |" g. m5 g
第一要设置要发的地址,比方说你要发信给一个人,这个人的地址是那呢,你得填一个地址在信封上是不是(当然,对现在的很多90后来说,信是啥东东可能都不知道了)
第二个要做的是人家收到信后得告诉你是不是,那他丢信息到那让你知道呢???
发明NRF24的GOD 专们给它一个地方接收回信的,那就是 0频道!!!!所以,发达模式很简单,就两个频道有用,一个是TX_ADDR,一个是RX_ADDR_P0. 其它的RX_ADDR_Px 你可以无视.在发送模式,他们都是废柴,没用的!!! 记住了.
第三个就是使能通道0的自动应答,感觉很无聊.脱裤子放屁呀,这是发达模式必须做的呀,一进入发送模式自动进入
使能通道0的自动应答不就行了吗?真搞不懂!可能中国人来做这个可能就没这个事了.
第四个就是 使能通道0的接收地址 和第3一样,无聊至极!
第五个就是 设置自动重发间隔时间:500us +86us;最大自动重发次数:10次 这个NRF24的说明书说得很清楚,大家看说明书的
04寄存器
,说得很清楚,有多种选择的.
第六个就是 和第五一样,看说明书的
05寄存器
,也说得清楚,大家可以设不同的频率玩一下.但你的接收器一定要在一定的频率!
第七个就是 同上,大家看说明书的
06寄存器.
也说得清楚.
第八个 这个是必做的, 大家看很重要的一个寄存器
00寄存器! 看低四位的说明.
设好后,相当于将电闸打开,说: 偶射了~~~~~~~~~~~~
0 q. Z4 ~' j& ^# F( y3 S9 j; I, v
M2 y1 X& z5 Y( c% q. _4 r- z* F
发送模式很简单,很明白. 下面我来说接收摸式.有必要说的一样是:
接收摸式下可以不设置发送的有效数据宽度,但接收模式一定要!!
1 O8 d4 \! d. t( D. ]! P
/ w9 ~$ q* e$ F E
/ F0 }8 q6 } E4 I9 a
上一季我说过我们比方设定的接收地址是:
8 p& h/ {* E3 i0 ^
, ]+ M% G% h# W7 s, Q7 O7 R
const u8 TX_ADDRESS[TX_ADR_WIDTH]={0xe7,0xe7,0xe7,0xe7,0xe7};//发送地址
1 A9 n* s8 }$ {* b+ R4 [( ^2 K
8 R8 Q- S5 w5 p
const u8 RX_ADDRESS [RX_ADR_WIDTH]={0x01,0x01,0xc2,0xc2,0xc2};//接收0通道地址
( t7 _8 w/ h4 L; j
const u8 RX_ADDRESS1[RX_ADR_WIDTH]={0x02,
0x01,0xc2,0xc2,0xc2
}; //接收1通道地址
- c6 `6 ~7 X* f7 `
5 [3 ]" Y/ P6 b! Q/ S/ n% g- R
const u8 RX_ADDRESS2[RX_ADR_WIDTH]={
0xc2,0xc2,0xc2,0xc1,
0x03};//接收2通道地址
) W7 Q: m% [8 ^7 A
const u8 RX_ADDRESS3[RX_ADR_WIDTH]={
0xc2,0xc2,0xc2,0xc1,
0x04};//接收3通道地址
# ?* C; S- `1 t) R2 c* Q
const u8 RX_ADDRESS4[RX_ADR_WIDTH]={
0x02,0xc2,0xc2,0xc1,
0x05};//接收4通道地址
' M: |: S* n% D# H1 w
const u8 RX_ADDRESS5[RX_ADR_WIDTH]={
0xc2,0xc2,0xc2,0xc1,
0x06};//接收5通道地址
/ T* [7 _ C. [ r+ [9 S
' u" Q! q6 f) ?
- y+ d* Y7 b$ |1 x. k
& u8 v. |% g8 y+ }" g- |1 n) p
' ]- j( x+ n/ O1 j
看到没,有人说怪了,你怎么能这么设,说明书不是低字节在前.高字节在后写进地址寄存器的吗?对了!!!!!
现在的说明书很坑爹
!!!
( s! w' V9 {- B( n0 B
其实,在写入第2,3,4,5通道地址的时候,无论你写多少个进去,它只认最后一个!!!!!为什么没人发现??因为大家只用0和1频道玩完就OK了!!没人去玩其它通道!!因为0和1频道按说明书做是没事的!!
6 k" ?) v! J' N) x
因为只认一个,所以我们更改一下接收地址.
% G% u- a1 i$ S n' f* ^+ g
& d- W: H2 E. H5 J4 }& d+ v
$ W$ v* I6 w$ M( ]8 n
const u8 TX_ADDRESS[TX_ADR_WIDTH]={0xe7,0xe7,0xe7,0xe7,0xe7};//发送地址
2 C4 v( V# E+ P3 B$ l
' j, @: R' R5 y
const u8 RX_ADDRESS [RX_ADR_WIDTH]={0x01,0x01,0xc2,0xc2,0xc2};//接收0通道地址
; d: @. H* R& P
const u8 RX_ADDRESS1[RX_ADR_WIDTH]={0x02,
0x01,0xc2,0xc2,0xc2
}; //接收1通道地址
/ r) b% g. ]* ?, T2 K
. U9 S# ^6 z6 |% ^- x) {. {7 r
const u8 RX_ADDRESS2[RX_ADR_WIDTH]={
0x03
}; //接收2通道地址
- U) l) {% e0 u8 W5 m4 p, h3 g
const u8 RX_ADDRESS3[RX_ADR_WIDTH]={
0x04
}; //接收3通道地址
1 l- \! D* x- l# N$ y# A
const u8 RX_ADDRESS4[RX_ADR_WIDTH]={
0x05
}; //接收4通道地址
2 D2 \* `; d" _$ W. D
const u8 RX_ADDRESS5[RX_ADR_WIDTH]={
0x06
}; //接收5通道地址
" _6 ^7 h) O; T) N/ U$ c1 Q6 x
( W2 H/ d! p y7 m$ ^$ S" E
$ m Z7 B& ^8 o7 q# Z
然后接收模式代码如下:
+ |* b. ?/ g. h7 P0 K+ t# v
3 x" g$ d) h9 ?; H8 k ]/ L
//该函数初始化NRF24L01到RX模式
# A+ m' `: w: A& g% z2 Z% U4 c4 v
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
* G4 H z% C* g* ]2 i: f, c
//当CE变高后,即进入RX模式,并可以接收数据了
; g0 q' S9 W v: B
void RX_Mode(void)
/ U6 J% s% ^& {
{
2 F4 Z9 `- T7 ~/ ~. U3 F
NRF24L01_CE=0;
# b$ |5 Y+ _5 c7 W2 I' R6 m
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除TX FIFO寄存器
( ]$ K1 w) C, U! E' W
2 ?1 z$ d1 L2 r0 D: j7 d8 q
NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址
( r% G" R0 g% U* k
NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P1,(u8*)RX_ADDRESS1,RX_ADR_WIDTH);//写RX节点地址
3 W2 a% H. R+ ]: z
' M: m/ `" h; k7 {) K \) N- Y) T# A
NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P2,(u8*)RX_ADDRESS2,
1
);//写RX节点地址
0 \- B( I# s i1 o$ `1 l4 H
NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P3,(u8*)RX_ADDRESS3,
1
);//写RX节点地址
1 p- D9 z; N( m9 C8 ~' W
NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P4,(u8*)RX_ADDRESS4,
1
);//写RX节点地址
6 a, t9 i$ u0 x# b: A3 ]' {( M
NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P5,(u8*)RX_ADDRESS5,
1
);//写RX节点地址
! Z @2 _' ]! `8 t5 p/ F; \) p
+ u0 x+ Y# x" @% f+ G+ G8 N' P" ?
NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x3f); //使能通道0的自动应答
. L C1 G& d6 B
NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x3f);//使能通道0-5的接收地址
4 f- d; }) t4 s% d2 z) K
; e" {. a6 c3 d; h; s
NRF24L01_Write_Reg(WRITE_REG+RF_CH,40); //设置RF通信频率
' j+ \+ E" \* r
5 `0 c; {( Q6 T! M! R
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
+ `, T& M6 F4 U9 |' s
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P1,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
/ Z' v. A6 r* u- T- q2 H- M/ L
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P2,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
- K2 N( y2 [) [" v. Y' M/ T
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P3,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
! [/ b" g9 b3 ?# o. a! \# q, h
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P4,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
) b! {! A6 d3 Y8 d% f
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P5,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
0 C% ` p# O' k( M$ s) a: f5 x
. g; V/ O5 A- K u( W( D
NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
0 |) r) E8 ?2 q0 y' j
1 Q& U& H# d! S
NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0f);//配置基本工作模式的参数
WR_UP,EN_CRC,16BIT_CRC,接收模式
H7 {3 a, p$ S, S
) ~! t& G* u8 _+ u: S; a
NRF24L01_CE = 1;//CE为高,进入接收模式
9 c' M2 v H) }) T9 M: k: O( ^% V
}
, b. Q0 _7 `7 O4 g% b% _
3 T- ]; ?; p2 x: m5 c! ?
看到红色1没有,只写一个进去就够了!!到时要读这个寄存器,也是读一个出来就行了,无论你读多少个,都和第一个一样的.
' p, S6 s. y% K
' R6 }% b. q5 C0 `% t: d
* t! N" |4 ~( e# f- y3 A1 d! {6 m
说明一下:
* I& {& @/ Y9 w; o, \+ E3 P
+ l& {- `8 u. e( p. ^
第0行 NRF24L01_CE=0; 设置时一定要先拉低!
第一行,清掉RX_FIFO寄存器.
第二行,在频道0的门上写上接收地址!
第三行,在频道1的门上写上接收地址!
从频道2开始,只写一位就行了,因为他的门上的从高8位一直到高39位已经写上去了,现在只能写最低8位.
第四行,在频道2的门上写上最底8位接收地址!
第五行,在频道3的门上写上最底8位接收地址!
第六行,在频道4的门上写上最底8位接收地址!
第七行,在频道5的门上写上最底8位接收地址!
% @: J3 Z: S# b0 T
" g" ~! P$ h; I) X8 O8 M
然后:
6 O7 R5 ?3 T; y1 r
NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x3f); //使能通道0的自动应答
8 e7 R$ a. t' m! ~. Z3 y n. _" G
% ^6 T8 w8 I4 J2 @8 F( Z
这一行是启动 0至5频道的自动应答! 0x3f=00111111
也可以不要,因为复位值就是0x3f!!!!
& s9 F7 ]6 E5 I' `4 [: O
这一行的意思是,收到信后将自己的地址号码自动发回给发信方.让发信方知道接收方收到信了.如果这里不设置会玩死发信方,发信会拼命发同一包数据给你.或向他的老板(主程式)说信发丢了!!哈哈!!
0 s8 n3 y# Z. a5 {! `" `, Q& e
* T4 f" v# C! X. E7 }
然后
* T2 p) R+ A6 p$ T% ~
0 [! S, c6 w- k5 _5 p: X) `
NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x3f);//使能通道0的接收地址
9 Y2 z7 O J5 c7 ^; A. V; Y
2 k' \& K0 |$ V8 u. k* i2 A! g& H
NRF24L01_Write_Reg(WRITE_REG+RF_CH,40); //设置RF通信频率
, w4 O* J+ ]' u/ g% b0 Q
0 ~2 F8 b% H: \2 a
这些和发送模式一样样,一定要一样,要不收不到的.
* H( h1 P% X% E* t6 u/ D P
* K7 Z4 ^, v/ u5 ~& O& U
然后就是:
4 G( k) Q% t, m8 L/ R: V5 c
9 p/ U3 I7 V1 n( L+ n
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
+ N4 C3 D8 H" x0 n+ L$ E2 h
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P1,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
: I- Z, `5 @; G7 I. W2 p
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P2,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
8 ^# {7 n1 S2 U0 o$ z( E' K
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P3,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
9 S. K, W; i# T9 f; u( g; x
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P4,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
; l8 l- n6 I" q( k3 q8 M" m4 F
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P5,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
h- f! H1 g5 y" |% y% A4 z; A
$ m6 j" Z# k4 Q. b' V. x
这个一定要设呀,要不RX_FIFO不鸟你,说木有收到信!!
1 y+ E7 j% g. C% u9 R' c
% i# _: w) S0 M$ X) `1 j
然后这一行:
3 L7 t; c- ~% d$ @7 x% j
NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
, T8 O: f* p+ r6 J8 U g+ H6 f# ^
8 t9 M7 i$ @3 U' X3 o$ A
看说明书 06寄存器.说得很明白,这就不说了.
- I! o: y- w$ ^! s) ^: p4 t c
; {, E" H& m5 q; ?
最后是 NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0f);//配置基本工作模式的参数
WR_UP,EN_CRC,16BIT_CRC,接收模式
3 r, @% u3 q/ q) r2 u1 |: T& t. F
% t3 ?# W1 d3 I& v( Z; W
发一个包和收一个包数据的代码,给大家帖出来.
) e- ?$ B3 p: p0 F* l2 ~9 ~
//启动NRF24L01发送一次数据
8 ^, r$ r) r8 u/ t* `& R K
//txbuf:待发送数据首地址
/ {7 I% g7 t6 _8 h4 Q& b$ V5 C
//返回值:发送完成状况
2 S c/ r3 i U- h( v# n
u8 NRF24L01_TxPacket(u8 *txbuf)
* x y* I6 W" m# H
{
7 J/ x0 s; J6 P3 x: W
u8 sta;
$ b0 m: y: @6 D& m. ^( `
( a/ c+ ^% q) [2 @" a
NRF24L01_CE=0;
' i) n" y! ?/ {: O3 w9 C- o# t$ F* g9 l
( b5 O) i( s$ J0 q2 S2 j
NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF 32个字节
( ?% x+ g5 Q% c* u% M+ s+ a4 L
( \- D: @4 ~$ H- r: x
NRF24L01_CE=1;//启动发送
^. p0 X) [ ?
# z$ b: J: I; b% \% ^$ x
while(NRF24L01_IRQ!=0);//等待发送完成
0 J) G( b& r" r7 b( E
; L% L2 J: M4 }/ f9 R" D
sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值
9 l/ f; C( f ?6 V5 V
6 ^: q! w4 d# K+ y4 G
NRF24L01_Write_Reg(WRITE_REG+STATUS,sta);//清除TX_DS或MAX_RT中断标志
$ k1 {, t9 e9 \
3 G2 T V* o4 t' m
if(sta&MAX_TX)//达到最大重发次数
7 Y" Q6 S& `( J5 V: ?' D
{
2 E. d! b2 t! j/ j1 D) w# [
NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TXFIFO寄存器
; r3 P8 o; H( M9 t0 s- O' _3 o
return MAX_TX;
4 |3 r+ S" r4 e2 M
}
3 F: n) a9 c2 |6 |% L/ Z
if(sta&TX_OK)//发送完成
. W+ J9 S: d7 z' @5 a+ }
{
/ L" A* U. j$ b8 N% g9 H+ {
return TX_OK;
( y3 v4 O& Y& o: t, P4 r
}
. _( @* c% V) u1 f p5 I% \/ l" I
return 0xff;//其他原因发送失败
: K9 z& d: Q/ l
}
3 K, m0 K6 |, F8 L0 C
$ ~7 z3 f- F6 I
记住要记住
有颜色
的这几行.
7 E K2 l( a$ w5 W: p6 _' [; l
然后是收一包数据.
# A: s: @7 r% b+ M& b
- T; q# F$ O3 P. x- t
u8 NRF24L01_RxPacket(u8 *rxbuf)
* V3 a' ]" w% Q* ? l6 Y7 C) O
{
6 `5 D) C6 C S4 M3 {% p! j
u8 sta,sta1;
+ y" }( K. ~; d" x5 P M# `+ E
& r) J, N8 I8 ?. H
sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值
. K C! Z) b! E: u8 j
# A, ]: b I) `" x; v/ w+ @
NRF24L01_Write_Reg(WRITE_REG+STATUS,sta);//清除TX_DS或MAX_RT中断标志
% r! m0 T! L0 G; [7 I, x
, E0 \; N0 R) G! B- o
Clear_line(18,0,30);
$ n4 e2 @/ j' c5 X/ F
sta1=NRF24L01_Read_Reg(STATUS);
- ~& r9 ?7 L+ x! c9 g& Q/ K8 g2 b
Bit_show(18,11,sta1);
! p9 ?' E# q- p! x0 D/ u( D
0 c- D0 h( E$ u- l. t; {" i* ^- O
if(sta&RX_OK)//接收到数据
/ I1 y2 w% n& W. z8 ~5 [
{
6 H3 K$ k! j5 V9 ?* m* D
NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
; o. c4 h: b" t @4 S
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RXFIFO寄存器
8 t4 `6 u1 w7 |5 f5 w" o
return 0;
# P6 F4 v# W& `
}
" }8 u9 Y" Y; p5 C; z+ }5 m+ s( u* U
return 1;//没收到任何数据
9 Z3 ^7 F$ w$ m: r+ d
}
: {$ v0 z$ |. m5 r! k
B4 H6 t; X5 r. Z% |; s# Q; {
看到没有,和发数据不同,这里不用
NRF24L01_CE=0
和
NRF24L01_CE=1.
( G' x1 ~; _# G2 f3 W" U
" {3 B5 Q9 z) L/ x6 S; c
如下几点在调试的时候总结出来的:
% n" P; k2 \! T8 s' Q
- P% s( ?* @' R1 |! N
; }: @4 \2 `8 s$ [
如果 TX FIFO刚好够32个数时,状态寄存器都会显示 0 未满.如再丢进去就会说满了.
重启计算机时要记住重起一下NRF24 因为里边的数据还是之前的,除非重写一次.
中断位是要写1清0的.
如果地址都相同,读出来数据的是频道号数最大的那个频道.就是接收5通道.大家可以做下实验,看对不对.
频道2~5只需写一个8位的地址就行.
+ J( F8 p+ ]3 p- I- h/ Y' s7 ~0 T
有时中断产生了,但RX_FIFO会为0,要重读一次.
* r6 Y# }$ ?5 _! ~. _4 ?, J. U
4 L3 ^( F0 X& F: |$ d; i" p
7 m5 j6 q" [$ i& j+ F0 a7 _
作者:
青山绿水
时间:
2018-10-25 16:50
谢谢分享
欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/)
Powered by Discuz! X3.2