COSA
An Object-Oriented Platform for Arduino Programming
NRF24L01P.cpp
Go to the documentation of this file.
1 
21 #include "NRF24L01P.hh"
22 #if !defined(BOARD_ATTINYX5)
23 
24 #include "Cosa/Power.hh"
25 #include "Cosa/RTT.hh"
26 #include <util/delay.h>
27 
28 NRF24L01P::NRF24L01P(uint16_t net, uint8_t dev,
32  SPI::Driver(csn, SPI::ACTIVE_LOW, SPI::DIV4_CLOCK, 0, SPI::MSB_ORDER, &m_irq),
33  Wireless::Driver(net, dev),
34  m_ce(ce, 0),
35  m_irq(irq, ExternalInterrupt::ON_FALLING_MODE, this),
36  m_status(0),
37  m_state(POWER_DOWN_STATE),
38  m_trans(0),
39  m_retrans(0),
40  m_drops(0)
41 {
42  channel(64);
43 }
44 
45 uint8_t
47 {
48  spi.acquire(this);
49  spi.begin();
50  m_status = spi.transfer(cmd);
51  uint8_t res = spi.transfer(0);
52  spi.end();
53  spi.release();
54  return (res);
55 }
56 
57 void
58 NRF24L01P::read(Command cmd, void* buf, size_t size)
59 {
60  spi.acquire(this);
61  spi.begin();
62  m_status = spi.transfer(cmd);
63  spi.read(buf, size);
64  spi.end();
65  spi.release();
66 }
67 
68 void
70 {
71  spi.acquire(this);
72  spi.begin();
73  m_status = spi.transfer(cmd);
74  spi.end();
75  spi.release();
76 }
77 
78 void
79 NRF24L01P::write(Command cmd, uint8_t data)
80 {
81  spi.acquire(this);
82  spi.begin();
83  m_status = spi.transfer(cmd);
84  spi.transfer(data);
85  spi.end();
86  spi.release();
87 }
88 
89 void
90 NRF24L01P::write(Command cmd, const void* buf, size_t size)
91 {
92  spi.acquire(this);
93  spi.begin();
94  m_status = spi.transfer(cmd);
95  spi.write(buf, size);
96  spi.end();
97  spi.release();
98 }
99 
102 {
103  spi.acquire(this);
104  spi.begin();
106  spi.end();
107  spi.release();
108  return (m_status);
109 }
110 
111 void
113 {
114  if (m_state != POWER_DOWN_STATE) return;
115  m_ce.clear();
116 
117  // Setup configuration for powerup and clear interrupts
118  write(CONFIG, (_BV(EN_CRC) | _BV(CRCO) | _BV(PWR_UP)));
119  _delay_ms(Tpd2stby_ms);
121 
122  // Flush status
123  write(STATUS, (_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT)));
124  write(FLUSH_TX);
125  write(FLUSH_RX);
126 }
127 
128 void
130 {
131  // Check already in receive mode
132  if (m_state == RX_STATE) return;
133 
134  // Configure primary receiver mode
135  write(CONFIG, (_BV(EN_CRC) | _BV(CRCO) | _BV(PWR_UP) | _BV(PRIM_RX)));
136  m_ce.set();
137  if (m_state == STANDBY_STATE) _delay_us(Tstby2a_us);
138  m_state = RX_STATE;
139 }
140 
141 void
143 {
144  // Setup primary transmit address
145  addr_t tx_addr(m_addr.network, dest);
146  write(TX_ADDR, &tx_addr, sizeof(tx_addr));
147 
148  // Trigger the transmitter mode
149  if (m_state != TX_STATE) {
150  m_ce.clear();
151  write(CONFIG, (_BV(EN_CRC) | _BV(CRCO) | _BV(PWR_UP)));
152  m_ce.set();
153  }
154 
155  // Wait for the transmitter to become active
156  if (m_state == STANDBY_STATE) _delay_us(Tstby2a_us);
157  m_state = TX_STATE;
158 }
159 
160 void
162 {
163  m_ce.clear();
164  _delay_us(Thce_us);
166 }
167 
168 void
170 {
171  delay(32);
172  m_ce.clear();
173  write(CONFIG, (_BV(EN_CRC) | _BV(CRCO)));
175 }
176 
177 bool
178 NRF24L01P::begin(const void* config)
179 {
180  UNUSED(config);
181 
182  // Check that a device is available
183  if (UNLIKELY(read_status().reserved)) return (false);
184 
185  // Setup hardware features, channel, bitrate, retransmission, dynamic payload
186  write(FEATURE, (_BV(EN_DPL) | _BV(EN_ACK_PAY) | _BV(EN_DYN_ACK)));
189  write(SETUP_RETR, ((DEFAULT_ARD << ARD) | (DEFAULT_ARC << ARC)));
190  write(DYNPD, DPL_PA);
191 
192  // Setup hardware receive pipes address; network (16-bit), device (8-bit)
193  // P0: auto-acknowledge (see transmit_mode)
194  // P1: node address<network:device> with auto-acknowledge
195  // P2: broadcast<network:0>
196  addr_t rx_addr = m_addr;
198  write(RX_ADDR_P1, &rx_addr, sizeof(rx_addr));
200  write(EN_RXADDR, (_BV(ERX_P2) | _BV(ERX_P1)));
201  write(EN_AA, (_BV(ENAA_P1) | _BV(ENAA_P0)));
202 
203  // Ready to go
204  powerup();
205  spi.attach(this);
206  m_irq.enable();
207  return (true);
208 }
209 
210 int
211 NRF24L01P::send(uint8_t dest, uint8_t port, const iovec_t* vec)
212 {
213  // Sanity check the payload size
214  if (UNLIKELY(vec == NULL)) return (EINVAL);
215  size_t len = iovec_size(vec);
216  if (UNLIKELY(len > PAYLOAD_MAX)) return (EMSGSIZE);
217 
218  // Setting transmit destination
219  transmit_mode(dest);
220 
221  // Write source address and payload to the transmit fifo
222  // Fix: Allow larger payload(30*3) with fragmentation
223  spi.acquire(this);
224  spi.begin();
225  uint8_t command = ((dest != BROADCAST)
226  ? W_TX_PAYLOAD
228  m_status = spi.transfer(command);
230  spi.transfer(port);
231  spi.write(vec);
232  spi.end();
233  spi.release();
234  m_trans += 1;
235 
236  // Check for auto-acknowledge pipe(0), and address setup and enable
237  if (dest != BROADCAST) {
238  addr_t tx_addr(m_addr.network, dest);
239  write(RX_ADDR_P0, &tx_addr, sizeof(tx_addr));
240  write(EN_RXADDR, (_BV(ERX_P2) | _BV(ERX_P1) | _BV(ERX_P0)));
241  }
242 
243  // Wait for transmission
244  do {
245  yield();
246  read_status();
247  } while (!m_status.tx_ds && !m_status.max_rt);
248  bool data_sent = m_status.tx_ds;
249 
250  // Check for auto-acknowledge pipe(0) disable
251  if (dest != BROADCAST) {
252  write(EN_RXADDR, (_BV(ERX_P2) | _BV(ERX_P1)));
253  }
254 
255  // Reset status bits and read retransmission counter and update
256  write(STATUS, _BV(MAX_RT) | _BV(TX_DS));
257  observe_tx_t observe = read_observe_tx();
258  m_retrans += observe.arc_cnt;
259 
260  // Check that the message was delivered
261  if (data_sent) return (len);
262 
263  // Failed to delivery
264  write(FLUSH_TX);
265  m_drops += 1;
266  return (EIO);
267 }
268 
269 int
270 NRF24L01P::send(uint8_t dest, uint8_t port, const void* buf, size_t len)
271 {
272  iovec_t vec[2];
273  iovec_t* vp = vec;
274  iovec_arg(vp, buf, len);
275  iovec_end(vp);
276  return (send(dest, port, vec));
277 }
278 
279 bool
281 {
282  // Check the receiver fifo
283  if (read_fifo_status().rx_empty) return (false);
284 
285  // Sanity check the size of the payload. Might require a flush
286  if (read(R_RX_PL_WID) <= DEVICE_PAYLOAD_MAX) return (true);
287  write(FLUSH_RX);
288  return (false);
289 }
290 
291 int
292 NRF24L01P::recv(uint8_t& src, uint8_t& port,
293  void* buf, size_t size,
294  uint32_t ms)
295 {
296  // Run in receiver mode
297  receiver_mode();
298 
299  // Check if there is data available on any pipe
300  uint32_t start = RTT::millis();
301  while (!available()) {
302  if ((ms != 0) && (RTT::since(start) > ms)) return (ETIME);
303  yield();
304  }
306  write(STATUS, _BV(RX_DR));
307 
308  // Check for payload error from device (Tab. 20, pp. 51, R_RX_PL_WID)
309  uint8_t count = read(R_RX_PL_WID) - 2;
310  if ((count > PAYLOAD_MAX) || (count > size)) {
311  write(FLUSH_RX);
312  return (EMSGSIZE);
313  }
314 
315  // Read the source address, port and payload
316  spi.acquire(this);
317  spi.begin();
319  src = spi.transfer(0);
320  port = spi.transfer(0);
321  spi.read(buf, count);
322  spi.end();
323  spi.release();
324  return (count);
325 }
326 
327 void
329 {
330  uint8_t pwr = RF_PWR_0DBM;
331  if (dBm < -12) pwr = RF_PWR_18DBM;
332  else if (dBm < -6) pwr = RF_PWR_12DBM;
333  else if (dBm < 0) pwr = RF_PWR_6DBM;
334  write(RF_SETUP, (RF_DR_2MBPS | pwr));
335 }
336 
337 // Output operators for bitfield status registers
339 {
340  outs << PSTR("RX_DR = ") << status.rx_dr
341  << PSTR(", TX_DS = ") << status.tx_ds
342  << PSTR(", MAX_RT = ") << status.max_rt
343  << PSTR(", RX_P_NO = ") << status.rx_p_no
344  << PSTR(", TX_FULL = ") << status.tx_full;
345  return (outs);
346 }
347 
349 {
350  outs << PSTR("PLOS_CNT = ") << observe.plos_cnt
351  << PSTR(", ARC_CNT = ") << observe.arc_cnt;
352  return (outs);
353 }
354 
356 {
357  outs << PSTR("RX_EMPTY = ") << fifo.rx_empty
358  << PSTR(", RX_FULL = ") << fifo.rx_full
359  << PSTR(", TX_EMPTY = ") << fifo.tx_empty
360  << PSTR(", TX_FULL = ") << fifo.tx_full
361  << PSTR(", TX_REUSE = ") << fifo.tx_reuse;
362  return (outs);
363 }
364 
365 #endif
#define EIO
Definition: Errno.h:32
uint8_t channel() const
Definition: Wireless.hh:77
addr_t m_addr
Current network and device address.
Definition: Wireless.hh:299
uint8_t tx_full
TX FIFO full.
Definition: NRF24L01P.hh:435
#define EINVAL
Definition: Errno.h:49
virtual void powerdown()
Definition: NRF24L01P.cpp:169
uint8_t transfer(uint8_t data)
Definition: SOFT_SPI.cpp:87
State m_state
Transceiver state.
Definition: NRF24L01P.hh:623
Setup of auto retransmission.
Definition: NRF24L01P.hh:296
Data send TX FIFO interrupt.
Definition: NRF24L01P.hh:421
NRF24L01P(uint16_t net, uint8_t dev, Board::DigitalPin csn=Board::D10, Board::DigitalPin ce=Board::D9, Board::ExternalInterruptPin irq=Board::EXT0)
Definition: NRF24L01P.cpp:28
RF channel.
Definition: NRF24L01P.hh:297
uint8_t plos_cnt
Count lost packets.
Definition: NRF24L01P.hh:468
Enable auto acknowledgement.
Definition: NRF24L01P.hh:293
static const uint16_t Thce_us
Definition: NRF24L01P.hh:594
uint16_t m_drops
Dropped messages.
Definition: NRF24L01P.hh:627
CRC encoding scheme (2/1 bytes CRC).
Definition: NRF24L01P.hh:328
Definition: SPI.hh:57
Default auto retransmit delay (500 us)
Definition: NRF24L01P.hh:380
Receive address data pipe 0.
Definition: NRF24L01P.hh:302
Power up/down.
Definition: NRF24L01P.hh:329
void acquire(Driver *dev)
Definition: SOFT_SPI.cpp:43
void receiver_mode()
Definition: NRF24L01P.cpp:129
IRQPin m_irq
Chip interrupt pin and handler.
Definition: NRF24L01P.hh:621
static const uint16_t Tpd2stby_ms
Definition: NRF24L01P.hh:592
#define NULL
Definition: Types.h:101
Configuration register.
Definition: NRF24L01P.hh:292
#define EMSGSIZE
Definition: Errno.h:117
Enable payload with ACK.
Definition: NRF24L01P.hh:536
virtual bool begin(const void *config=NULL)
Definition: NRF24L01P.cpp:178
uint8_t max_rt
Max number of TX retransmit interrupt.
Definition: NRF24L01P.hh:437
void set() const
Definition: OutputPin.hh:85
Enable CRC.
Definition: NRF24L01P.hh:327
Read RX payload width.
Definition: NRF24L01P.hh:245
uint16_t m_trans
Send count.
Definition: NRF24L01P.hh:625
#define PSTR(s)
Definition: Types.h:202
void read(void *buf, size_t count)
Definition: SPI.hh:308
uint8_t m_channel
Current channel (device dependent.
Definition: Wireless.hh:298
static const size_t PAYLOAD_MAX
Definition: NRF24L01P.hh:67
uint8_t m_dest
Latest message destination device address.
Definition: Wireless.hh:301
Read RX payload.
Definition: NRF24L01P.hh:240
friend IOStream & operator<<(IOStream &outs, status_t status)
Definition: NRF24L01P.cpp:338
Definition: Types.h:391
Maximum number of TX retransmits interrupt.
Definition: NRF24L01P.hh:422
#define ETIME
Definition: Errno.h:89
Status register.
Definition: NRF24L01P.hh:299
observe_tx_t read_observe_tx()
Definition: NRF24L01P.hh:649
Disable AUTOACK on this specific packet.
Definition: NRF24L01P.hh:248
static const size_t DEVICE_PAYLOAD_MAX
Definition: NRF24L01P.hh:61
uint8_t rx_empty
RX FIFO empty flag.
Definition: NRF24L01P.hh:499
uint8_t device
Device address (LSB).
Definition: Wireless.hh:41
static uint32_t since(uint32_t start)
Definition: RTT.hh:107
uint8_t rx_full
RX FIFO full flag.
Definition: NRF24L01P.hh:500
status_t read_status()
Definition: NRF24L01P.cpp:101
uint8_t tx_ds
Data send TX FIFO interrupt.
Definition: NRF24L01P.hh:438
void(* delay)(uint32_t ms)
int16_t network
Network address.
Definition: Wireless.hh:42
Enable dynamic payload length.
Definition: NRF24L01P.hh:535
void write(Command cmd)
Definition: NRF24L01P.cpp:69
static uint32_t millis()
Definition: RTT.cpp:121
void write(const void *buf, size_t count)
Definition: SPI.hh:321
static const uint16_t Tstby2a_us
Definition: NRF24L01P.hh:593
ExternalInterruptPin
Definition: ATmega1284P.hh:190
void(* yield)()
uint8_t arc_cnt
Count retransmitted packets.
Definition: NRF24L01P.hh:467
void powerup()
Definition: NRF24L01P.cpp:112
void begin()
Definition: SPI.hh:216
Setup of address width.
Definition: NRF24L01P.hh:295
fifo_status_t read_fifo_status()
Definition: NRF24L01P.hh:639
uint8_t rx_p_no
Data pipe number for available payload.
Definition: NRF24L01P.hh:436
Feature register.
Definition: NRF24L01P.hh:317
void end()
Definition: SPI.hh:226
RF setup register.
Definition: NRF24L01P.hh:298
#define UNUSED(x)
Definition: ATmega328P.hh:31
status_t m_status
Latest status.
Definition: NRF24L01P.hh:622
virtual bool available()
Definition: NRF24L01P.cpp:280
uint8_t tx_full
TX FIFO full flag.
Definition: NRF24L01P.hh:503
virtual int send(uint8_t dest, uint8_t port, const iovec_t *vec)
Definition: NRF24L01P.cpp:211
Enable dynamic payload length.
Definition: NRF24L01P.hh:316
bool attach(Driver *dev)
Definition: SOFT_SPI.cpp:78
Flush TX FIFO.
Definition: NRF24L01P.hh:242
static const uint8_t BROADCAST
Definition: Wireless.hh:58
uint16_t m_retrans
Retransmittion count.
Definition: NRF24L01P.hh:626
virtual int recv(uint8_t &src, uint8_t &port, void *buf, size_t count, uint32_t ms=0L)
Definition: NRF24L01P.cpp:292
uint8_t read(Command cmd)
Definition: NRF24L01P.cpp:46
OutputPin m_ce
Chip enable activity RX/TX select pin.
Definition: NRF24L01P.hh:620
Transmit address.
Definition: NRF24L01P.hh:308
SPI spi
Definition: SPI.cpp:29
Enable the W_TX_PAYLOAD_NOACK command.
Definition: NRF24L01P.hh:537
RX/TX control (PRX/PTX).
Definition: NRF24L01P.hh:330
void standby()
Definition: NRF24L01P.cpp:161
void release()
Definition: SOFT_SPI.cpp:64
Default auto retransmit count (15)
Definition: NRF24L01P.hh:383
void clear() const
Definition: OutputPin.hh:124
virtual void output_power_level(int8_t dBm)
Definition: NRF24L01P.cpp:328
Enable dynamic payload length on all pipes.
Definition: NRF24L01P.hh:528
Write TX payload.
Definition: NRF24L01P.hh:241
#define UNLIKELY(x)
Definition: Types.h:153
uint8_t tx_reuse
Reuse last transmitted data packat.
Definition: NRF24L01P.hh:504
void transmit_mode(uint8_t dest)
Definition: NRF24L01P.cpp:142
Flush RX FIFO.
Definition: NRF24L01P.hh:243
No operation, return status.
Definition: NRF24L01P.hh:249
Enable rx addresses.
Definition: NRF24L01P.hh:294
uint8_t tx_empty
TX FIFO empty flag.
Definition: NRF24L01P.hh:502
Data ready RX FIFO interrupt.
Definition: NRF24L01P.hh:420
void iovec_arg(iovec_t *&vp, const void *buf, size_t size)
Definition: Types.h:430
void iovec_end(iovec_t *&vp)
Definition: Types.h:449
uint8_t rx_dr
Data ready RX FIFO interrupt.
Definition: NRF24L01P.hh:439
size_t iovec_size(const iovec_t *vec)
Definition: Types.h:407