After part 1 and part 2, I’d like to conclude with a description of how everything fits together.
The main functions in the public API are:
uint8_t rf12_initialize (uint8_t id, uint8_t band, uint8_t g)
Sets up the driver and hardware, with the RFM12B in idle mode and rxstate set to TXIDLE.uint8_t rf12_recvDone ()
If in receive mode, check whether a complete packet has been received and is intended for us. If so, set rxstate to TXIDLE, and return 1 to indicate there is a fresh new packet. Otherwise, if we were in TXIDLE mode, enable the RFM12B receiver and set rxstate to TXRECV.uint8_t rf12_canSend ()
This only returns true if we are in TXRECV mode, and no data has been received yet, and the RFM12B indicates that there is no signal in the air right now. If those are all true, set rxstate to TXIDLE and return 1.void rf12_sendStart (uint8_t hdr, ...)
This function may only be called right after rf12_recvDone() or rf12_canSend() returned true, indicating that we are allowed to start transmitting. This turns the RFM12B transmitter on and set rxstate to TXPRE1.void rf12_sendWait (uint8_t mode)
Can be called after rf12_sendStart() to wait for the completion of the transmission. The mode arg can be used to do this waiting in various low-power modes, to minimize the ATmega power consumption while the RFM12B module is drawing a relative large current (ca 25 mA at full power). This call is optional – either way, the RF12 driver will return to TXRECV mode after the transmission.
Note that this design places all time-critical code inside the RF12 driver. You don’t have to call rf12_recvDone() very often if you’re busy doing other things. Once a packet has been received, the RF12 driver will stop all further activity and prevent the current contents of the packet buffer from being overwritten.
Likewise, since you have to ask permission to send, the logic is such that the packet buffer will only be filled with outgoing data at a time when it is not being used for reception. Thus, a single packet buffer can be used for both.
The main thing to keep in mind, is that rf12_recvDone() needs to be called as main polling mechanism, even if you don’t care about incoming packets. You can’t simply call rf12_canSend() forever, hoping that it will return 1 at some point. Instead, use this logic:
while (!rf12_candSend())
rf12_recvDone();
rf12_sendStart(...)
This code ignores all incoming packets, i.e. when rf12_recvDone() returns true, but the call is still needed to keep the finite state machine in the driver going.
So much for the general structure and flow through the RF12 driver. To find out more about the protocol and how the different header fields are used, see this post and this post. And lastly there’s the RF12 library‘s home page.
Do I read correctly that once RF12 has receives a packet, it turns the receiver off until you call rf12_recvDone() to retrieve it so you will miss any further transmissions in that time?
Thanks,
Ian
Correct, there is a single packet buffer.