Skip to content

Commit 877dba3

Browse files
committed
drivers: Add NRF24L01 driver (written in pure Python).
Comes with test script. Copy both files to pyboard and run "import nrf24l01test".
1 parent e535a61 commit 877dba3

File tree

2 files changed

+334
-0
lines changed

2 files changed

+334
-0
lines changed

drivers/nrf24l01/nrf24l01.py

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
"""NRF24L01 driver for Micro Python"""
2+
3+
import pyb
4+
5+
# nRF24L01+ registers
6+
CONFIG = const(0x00)
7+
EN_RXADDR = const(0x02)
8+
SETUP_AW = const(0x03)
9+
SETUP_RETR = const(0x04)
10+
RF_CH = const(0x05)
11+
RF_SETUP = const(0x06)
12+
STATUS = const(0x07)
13+
OBSERVE_TX = const(0x08)
14+
RX_ADDR_P0 = const(0x0a)
15+
TX_ADDR = const(0x10)
16+
RX_PW_P0 = const(0x11)
17+
FIFO_STATUS = const(0x17)
18+
DYNPD = const(0x1c)
19+
20+
# CONFIG register
21+
EN_CRC = const(0x08) # enable CRC
22+
CRCO = const(0x04) # CRC encoding scheme; 0=1 byte, 1=2 bytes
23+
PWR_UP = const(0x02) # 1=power up, 0=power down
24+
PRIM_RX = const(0x01) # RX/TX control; 0=PTX, 1=PRX
25+
26+
# RF_SETUP register
27+
POWER_0 = const(0x00) # -18 dBm
28+
POWER_1 = const(0x02) # -12 dBm
29+
POWER_2 = const(0x04) # -6 dBm
30+
POWER_3 = const(0x06) # 0 dBm
31+
SPEED_1M = const(0x00)
32+
SPEED_2M = const(0x08)
33+
SPEED_250K = const(0x20)
34+
35+
# STATUS register
36+
RX_DR = const(0x40) # RX data ready; write 1 to clear
37+
TX_DS = const(0x20) # TX data sent; write 1 to clear
38+
MAX_RT = const(0x10) # max retransmits reached; write 1 to clear
39+
40+
# FIFO_STATUS register
41+
RX_EMPTY = const(0x01) # 1 if RX FIFO is empty
42+
43+
# constants for instructions
44+
R_RX_PL_WID = const(0x60) # read RX payload width
45+
R_RX_PAYLOAD = const(0x61) # read RX payload
46+
W_TX_PAYLOAD = const(0xa0) # write TX payload
47+
FLUSH_TX = const(0xe1) # flush TX FIFO
48+
FLUSH_RX = const(0xe2) # flush RX FIFO
49+
NOP = const(0xff) # use to read STATUS register
50+
51+
class NRF24L01:
52+
def __init__(self, spi, cs, ce, channel=46, payload_size=16):
53+
assert payload_size <= 32
54+
55+
# init the SPI bus and pins
56+
spi.init(spi.MASTER, baudrate=4000000, polarity=0, phase=1, firstbit=spi.MSB)
57+
cs.init(cs.OUT_PP, cs.PULL_NONE)
58+
ce.init(ce.OUT_PP, ce.PULL_NONE)
59+
60+
# store the pins
61+
self.spi = spi
62+
self.cs = cs
63+
self.ce = ce
64+
65+
# reset everything
66+
self.ce.low()
67+
self.cs.high()
68+
self.payload_size = payload_size
69+
self.pipe0_read_addr = None
70+
pyb.delay(5)
71+
72+
# set address width to 5 bytes
73+
self.reg_write(SETUP_AW, 0b11)
74+
75+
# disable dynamic payloads
76+
self.reg_write(DYNPD, 0)
77+
78+
# auto retransmit delay: 1750us
79+
# auto retransmit count: 8
80+
self.reg_write(SETUP_RETR, (6 << 4) | 8)
81+
82+
# set rf power and speed
83+
self.set_power_speed(POWER_3, SPEED_1M)
84+
85+
# init CRC
86+
self.set_crc(2)
87+
88+
# clear status flags
89+
self.reg_write(STATUS, RX_DR | TX_DS | MAX_RT)
90+
91+
# set channel
92+
self.set_channel(channel)
93+
94+
# flush buffers
95+
self.flush_rx()
96+
self.flush_tx()
97+
98+
def reg_read(self, reg):
99+
self.cs.low()
100+
self.spi.send_recv(reg)
101+
buf = self.spi.recv(1)
102+
self.cs.high()
103+
return buf[0]
104+
105+
def reg_read_ret_status(self, reg):
106+
self.cs.low()
107+
status = self.spi.send_recv(reg)[0]
108+
buf = self.spi.recv(1)
109+
self.cs.high()
110+
return status
111+
112+
def reg_write(self, reg, buf):
113+
self.cs.low()
114+
status = self.spi.send_recv(0x20 | reg)[0]
115+
self.spi.send(buf)
116+
self.cs.high()
117+
return status
118+
119+
def flush_rx(self):
120+
self.cs.low()
121+
self.spi.send(FLUSH_RX)
122+
self.cs.high()
123+
124+
def flush_tx(self):
125+
self.cs.low()
126+
self.spi.send(FLUSH_TX)
127+
self.cs.high()
128+
129+
# power is one of POWER_x defines; speed is one of SPEED_x defines
130+
def set_power_speed(self, power, speed):
131+
setup = self.reg_read(RF_SETUP) & 0b11010001
132+
self.reg_write(RF_SETUP, setup | power | speed)
133+
134+
# length in bytes: 0, 1 or 2
135+
def set_crc(self, length):
136+
config = self.reg_read(CONFIG) & ~(CRCO | EN_CRC)
137+
if length == 0:
138+
pass
139+
elif length == 1:
140+
config |= EN_CRC
141+
else:
142+
config |= EN_CRC | CRCO
143+
self.reg_write(CONFIG, config)
144+
145+
def set_channel(self, channel):
146+
self.reg_write(RF_CH, min(channel, 127))
147+
148+
# address should be a bytes object 5 bytes long
149+
def open_tx_pipe(self, address):
150+
assert len(address) == 5
151+
self.reg_write(RX_ADDR_P0, address)
152+
self.reg_write(TX_ADDR, address)
153+
self.reg_write(RX_PW_P0, self.payload_size)
154+
155+
# address should be a bytes object 5 bytes long
156+
# pipe 0 and 1 have 5 byte address
157+
# pipes 2-5 use same 4 most-significant bytes as pipe 1, plus 1 extra byte
158+
def open_rx_pipe(self, pipe_id, address):
159+
assert len(address) == 5
160+
assert 0 <= pipe_id <= 5
161+
if pipe_id == 0:
162+
self.pipe0_read_addr = address
163+
if pipe_id < 2:
164+
self.reg_write(RX_ADDR_P0 + pipe_id, address)
165+
else:
166+
self.reg_write(RX_ADDR_P0 + pipe_id, address[0])
167+
self.reg_write(RX_PW_P0 + pipe_id, self.payload_size)
168+
self.reg_write(EN_RXADDR, self.reg_read(EN_RXADDR) | (1 << pipe_id))
169+
170+
def start_listening(self):
171+
self.reg_write(CONFIG, self.reg_read(CONFIG) | PWR_UP | PRIM_RX)
172+
self.reg_write(STATUS, RX_DR | TX_DS | MAX_RT)
173+
174+
if self.pipe0_read_addr is not None:
175+
self.reg_write(RX_ADDR_P0, self.pipe0_read_addr)
176+
177+
self.flush_rx()
178+
self.flush_tx()
179+
self.ce.high()
180+
pyb.udelay(130)
181+
182+
def stop_listening(self):
183+
self.ce.low()
184+
self.flush_tx()
185+
self.flush_rx()
186+
187+
# returns True if any data available to recv
188+
def any(self):
189+
return not bool(self.reg_read(FIFO_STATUS) & RX_EMPTY)
190+
191+
def recv(self):
192+
# get the data
193+
self.cs.low()
194+
self.spi.send(R_RX_PAYLOAD)
195+
buf = self.spi.recv(self.payload_size)
196+
self.cs.high()
197+
198+
# clear RX ready flag
199+
self.reg_write(STATUS, RX_DR)
200+
201+
return buf
202+
203+
def send(self, buf, timeout=500):
204+
# power up
205+
self.reg_write(CONFIG, (self.reg_read(CONFIG) | PWR_UP) & ~PRIM_RX)
206+
pyb.udelay(150)
207+
208+
# send the data
209+
self.cs.low()
210+
self.spi.send(W_TX_PAYLOAD)
211+
self.spi.send(buf)
212+
if len(buf) < self.payload_size:
213+
self.spi.send(b'\x00' * (self.payload_size - len(buf))) # pad out data
214+
self.cs.high()
215+
216+
# enable the chip so it can send the data
217+
self.ce.high()
218+
pyb.udelay(15) # needs to be >10us
219+
self.ce.low()
220+
221+
# blocking wait for tx complete
222+
start = pyb.millis()
223+
while pyb.millis() - start < timeout:
224+
status = self.reg_read_ret_status(OBSERVE_TX)
225+
if status & (TX_DS | MAX_RT):
226+
break
227+
228+
# get and clear all status flags
229+
status = self.reg_write(STATUS, RX_DR | TX_DS | MAX_RT)
230+
if not (status & TX_DS):
231+
raise OSError("send failed")
232+
233+
# power down
234+
self.reg_write(CONFIG, self.reg_read(CONFIG) & ~PWR_UP)

drivers/nrf24l01/nrf24l01test.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""Test for nrf24l01 module."""
2+
3+
import struct
4+
import pyb
5+
from pyb import Pin, SPI
6+
from nrf24l01 import NRF24L01
7+
8+
pipes = (b'\xf0\xf0\xf0\xf0\xe1', b'\xf0\xf0\xf0\xf0\xd2')
9+
10+
def master():
11+
nrf = NRF24L01(SPI(2), Pin('Y5'), Pin('Y4'), payload_size=8)
12+
13+
nrf.open_tx_pipe(pipes[0])
14+
nrf.open_rx_pipe(1, pipes[1])
15+
nrf.start_listening()
16+
17+
num_needed = 16
18+
num_successes = 0
19+
num_failures = 0
20+
led_state = 0
21+
22+
print('NRF24L01 master mode, sending %d packets...' % num_needed)
23+
24+
while num_successes < num_needed and num_failures < num_needed:
25+
# stop listening and send packet
26+
nrf.stop_listening()
27+
millis = pyb.millis()
28+
led_state = max(1, (led_state << 1) & 0x0f)
29+
print('sending:', millis, led_state)
30+
try:
31+
nrf.send(struct.pack('ii', millis, led_state))
32+
except OSError:
33+
pass
34+
35+
# start listening again
36+
nrf.start_listening()
37+
38+
# wait for response, with 250ms timeout
39+
start_time = pyb.millis()
40+
timeout = False
41+
while not nrf.any() and not timeout:
42+
if pyb.elapsed_millis(start_time) > 250:
43+
timeout = True
44+
45+
if timeout:
46+
print('failed, respones timed out')
47+
num_failures += 1
48+
49+
else:
50+
# recv packet
51+
got_millis, = struct.unpack('i', nrf.recv())
52+
53+
# print response and round-trip delay
54+
print('got response:', got_millis, '(delay', pyb.millis() - got_millis, 'ms)')
55+
num_successes += 1
56+
57+
# delay then loop
58+
pyb.delay(250)
59+
60+
print('master finished sending; succeses=%d, failures=%d' % (num_successes, num_failures))
61+
62+
def slave():
63+
nrf = NRF24L01(SPI(2), Pin('Y5'), Pin('Y4'), payload_size=8)
64+
65+
nrf.open_tx_pipe(pipes[1])
66+
nrf.open_rx_pipe(1, pipes[0])
67+
nrf.start_listening()
68+
69+
print('NRF24L01 slave mode, waiting for packets... (ctrl-C to stop)')
70+
71+
while True:
72+
pyb.wfi()
73+
if nrf.any():
74+
while nrf.any():
75+
buf = nrf.recv()
76+
millis, led_state = struct.unpack('ii', buf)
77+
print('received:', millis, led_state)
78+
for i in range(4):
79+
if led_state & (1 << i):
80+
pyb.LED(i + 1).on()
81+
else:
82+
pyb.LED(i + 1).off()
83+
pyb.delay(15)
84+
85+
nrf.stop_listening()
86+
try:
87+
nrf.send(struct.pack('i', millis))
88+
except OSError:
89+
pass
90+
print('sent response')
91+
nrf.start_listening()
92+
93+
print('NRF24L01 test module loaded')
94+
print('NRF24L01 pinout for test:')
95+
print(' CE on Y4')
96+
print(' CSN on Y5')
97+
print(' SCK on Y6')
98+
print(' MISO on Y7')
99+
print(' MOSI on Y8')
100+
print('run nrf24l01test.slave() on slave, then nrf24l01test.master() on master')

0 commit comments

Comments
 (0)