Wixel SDK
radio_mac.c
1 /* NOTE: Calibration of the frequency synthesizer and other RF hardware takes about 800 us and
2  * must be done regularly. There are several options for when to do the calibration and not.
3  * We configured the radio to automatically calibrate whenever going from the IDLE state to TX
4  * or RX (MCSM0.FS_AUTOCAL = 01). The radio will go into the idle state whenever the is an RX
5  * timeout. However, to enable a quick turnaround between TX and RX, we configured the radio to
6  * automatically go into the FSTXON mode after it is done with RX or TX mode. FSTXON means that
7  * the frequency synthesizer is on and the radio is ready to go into RX or TX mode quickly
8  * (but it goes to TX mode faster).
9  *
10  * So basically we are depending on the RX timeout feature to schedule our calibrations for us,
11  * instead of doing it manually. This should work, but it will probaby hinder our ability to
12  * quickly recover from lost packets (the RX timeout event is what happens when a packet is lost).
13  * An easy alternative would be to only calibrate after every 10th RX timeout or something like
14  * that.
15  */
16 
17 /* The definition of the maximum packet size (and the code that sets the PKTLEN register) is not
18  * in this layer. That is up to the higher-level code (radio_link.c) to decide. When this
19  * layer needs to know the packet size (for setting up the DMA), it reads it from PKTLEN. This
20  * makes the code a little less efficient because it takes longer to access an XDATA register
21  * than a hardcoded constant, but makes this MAC layer much more reusable.
22  */
23 
24 #include <radio_mac.h>
25 #include <cc2511_map.h>
26 #include <dma.h>
27 #include <radio_registers.h>
28 
29 #include <random.h>
30 
31 #define MAX_LATENCY_OF_STROBE 10
32 
33 // The RFST register is how we tell the radio to do something, and these are the
34 // command strobes we can write to it:
35 #define SFSTXON 0
36 #define SCAL 1
37 #define SRX 2
38 #define STX 3
39 #define SIDLE 4
40 
41 static void radioMacEvent(uint8 event);
42 
43 // Bits for sending commands to the MAC in an interrupt safe way.
44 static volatile BIT strobe = 0;
45 
46 // Error reporting
49 
50 // Radio MAC states
51 #define RADIO_MAC_STATE_OFF 0
52 #define RADIO_MAC_STATE_IDLE 1
53 #define RADIO_MAC_STATE_RX 2
54 #define RADIO_MAC_STATE_TX 3
55 volatile uint8 DATA radioMacState = RADIO_MAC_STATE_OFF;
56 
57 ISR(RF, 0)
58 {
59  S1CON = 0; // Clear the general RFIF interrupt registers
60 
61  if (RFIF & 0x10) // Check IRQ_DONE
62  {
63  if (radioMacState == RADIO_MAC_STATE_TX)
64  {
65  // We just sent a packet.
66  radioMacEvent(RADIO_MAC_EVENT_TX);
67  }
68  else if (radioMacState == RADIO_MAC_STATE_RX)
69  {
70  // We just received a packet, but it might have an invalid CRC or be irrelevant
71  // for other reasons.
72  radioMacEvent(RADIO_MAC_EVENT_RX);
73  }
74  }
75 
76  if (RFIF & 0x20) // Check IRQ_TIMEOUT
77  {
78  // We were listening for packets but we didn't receive anything
79  // and the timeout period expired.
80  radioMacEvent(RADIO_MAC_EVENT_RX_TIMEOUT);
81  }
82 
83  if (strobe)
84  {
85  // Some other code has set the strobe bit, which means he wants the radioMacEventHandler to
86  // run soon, typically because new data is available for it to send.
87  if (radioMacState == RADIO_MAC_STATE_TX)
88  {
89  // We are currently transmitting, so we will wait for the end of that packet.
90  // Then, we will issue a RADIO_MAC_EVENT_TX.
91  return;
92  }
93 
94  if (radioMacState == RADIO_MAC_STATE_RX && MARCSTATE == 0x0D)
95  {
96  if (PKTSTATUS & (1<<3)) // Check SFD bit (Start of Frame Delimiter)
97  {
98  // We are currently receiving a packet, so we will wait for the end of that
99  // packet and then issue a RADIO_MAC_EVENT_RX.
100  // ASSUMPTION: There is no automatic address filtering, and packets with
101  // bad CRCs still result in a RAIDO_MAC_EVENT_RX.
102  return;
103  }
104  if ((MCSM2&7) != 7 && WOREVT1 < MAX_LATENCY_OF_STROBE)
105  {
106  // We are currently listening for a packet and the timeout is going to happen
107  // soon (within 10 ms if MSCM2==1 or 20 ms if MCSMS2==0), so we will not actually issue a RADIO_MAC_EVENT_STROBE
108  // right now.
109  return;
110  }
111  }
112 
113 
114  /* The code below is necessary because we found that if the radio is in the
115  process of calibrating itself to go into RX mode, it won't respond
116  correctly to an STX strobe (it goes into RX mode instead of TX).
117  We only need to worry about that here, and not in the other events,
118  because those other events only happen at times when the radio should not
119  be in the middle of calibrating itself. */
120  if (MARCSTATE != 0x0D)
121  {
122  RFST = SIDLE;
123  }
124 
125  // We are not currently transmitting and nothing is being received at the
126  // moment, so we should stop and issue a RADIO_MAC_EVENT_STROBE now.
127  radioMacEvent(RADIO_MAC_EVENT_STROBE);
128  }
129 
130  if (RFIF & 0x80) // Check IRQ_TXUNF
131  {
132  // We were not sending data to the radio fast enough, so there was a
133  // TX underflow. This should not happen because we use DMA to send
134  // the data. Report it as an error.
136  RFIF = ~0x80;
137  }
138 
139  if (RFIF & 0x40) // Check IRQ_RXOVF
140  {
141 
142  // We were not reading data from the radio fast enough, so there was
143  // a RX overflow. This should not happen. Report it as an error.
145  RFIF = ~0x40;
146 
147  // The radio module is probably now in the RX_OVERFLOW state where it can not
148  // receive packets. The way to get out of this state is:
149  //RFST = SIDLE; (and you probably should put NOPs here, though in my tests it was not necessary)
150  //RFST = SRX;
151  // We never expect an RX overflow to happen though, so we won't do that.
152  }
153 }
154 
155 void radioMacEvent(uint8 event)
156 {
158  /* This is necessary because David has observed that sometimes (maybe every
159  * time?) when a packet with a bad CRC is received, the radio stays in RX
160  * instead of going to FSTXON mode the way it should (RXOFF_MODE=01).
161  * If we allow the radio to stay in RX mode then an RX overflow error could
162  * happen later after we disarm the DMA channel. */
163  if (MARCSTATE != 0x12 && MARCSTATE != 0x01 && MARCSTATE != 0x00 && MARCSTATE != 0x15)
164  {
165  // Fix the bad state by telling the radio to go to the SFSTXON state.
166  RFST = SFSTXON;
167  }
168 
170  DMAARM = 0x80 | (1<<DMA_CHANNEL_RADIO); // Abort any ongoing radio DMA transfer.
171  DMAIRQ &= ~(1<<DMA_CHANNEL_RADIO); // Clear any pending radio DMA interrupt
172 
174  radioMacState = RADIO_MAC_STATE_RX; // Default next state: RX
175  MCSM2 = 0x07; // Default next timeout: infinite.
176  radioMacEventHandler(event);
177 
179  // We want to do it before restarting the radio (to avoid accidentally missing
180  // an event) but we want to do it as long as possible AFTER turning off the
181  // radio.
182  RFIF = ~0x30; // Clear IRQ_DONE and IRQ_TIMEOUT if they are set.
183 
185  switch(radioMacState)
186  {
187  case RADIO_MAC_STATE_RX:
188  DMAARM = (1<<DMA_CHANNEL_RADIO); // Arm DMA channel.
189  RFST = SRX; // Switch radio to RX.
190  break;
191  case RADIO_MAC_STATE_TX:
192  DMAARM = (1<<DMA_CHANNEL_RADIO); // Arm DMA channel.
193  RFST = STX; // Switch radio to TX.
194  break;
195  }
196 
197  // Clear the strobe bit because we just ran the radioMacEventHandler.
198  strobe = 0;
199 }
200 
202 {
203  strobe = 1;
204  S1CON |= 3;
205 }
206 
210 {
212 
213  // MCSM.FS_AUTOCAL = 1: Calibrate freq when going from IDLE to RX or TX (or FSTXON).
214  MCSM0 = 0x14; // Main Radio Control State Machine Configuration
215  MCSM1 = 0x05; // Disable CCA. After RX, go to FSTXON. After TX, go to FSTXON.
216  MCSM2 = 0x07; // NOTE: MCSM2 also gets set every time we go into RX mode.
217 
218  IEN2 |= 0x01; // Enable RF general interrupt
219  RFIM = 0xF0; // Enable these interrupts: DONE, RXOVF, TXUNF, TIMEOUT
220 
221  EA = 1; // Enable interrupts in general
222 
223  dmaConfig.radio.DC6 = 19; // WORDSIZE = 0, TMODE = 0, TRIG = 19
224 }
225 
226 void radioMacRx(uint8 XDATA * packet, uint8 timeout)
227 {
228  if (timeout)
229  {
230  MCSM2 = 0x00; // RX_TIME = 0. Helps determine the units of the RX timeout period.
231  WORCTRL = 0; // WOR_RES = 0. Helps determine the units of the RX timeout period.
232  WOREVT1 = timeout;
233  WOREVT0 = 0;
234  }
235  else
236  {
237  MCSM2 = 0x07; // RX_TIME = 7: No timeout.
238  }
239 
240  dmaConfig.radio.SRCADDRH = XDATA_SFR_ADDRESS(RFD) >> 8;
241  dmaConfig.radio.SRCADDRL = XDATA_SFR_ADDRESS(RFD);
242  dmaConfig.radio.DESTADDRH = (unsigned int)packet >> 8;
243  dmaConfig.radio.DESTADDRL = (unsigned int)packet;
244  dmaConfig.radio.LENL = 1 + PKTLEN + 2;
245  dmaConfig.radio.VLEN_LENH = 0b10000000; // Transfer length is FirstByte+3
246  // Assumption: DC6 is set correctly
247  dmaConfig.radio.DC7 = 0x10; // SRCINC = 0, DESTINC = 1, IRQMASK = 0, M8 = 0, PRIORITY = 0
248 
249  radioMacState = RADIO_MAC_STATE_RX;
250 }
251 
252 // Called by the user during RADIO_MAC_STATE_IDLE or RADIO_MAC_STATE_RX to tell the Mac
253 // that it should start trying to send a packet.
254 void radioMacTx(uint8 XDATA * packet)
255 {
256  dmaConfig.radio.SRCADDRH = (unsigned int)packet >> 8;
257  dmaConfig.radio.SRCADDRL = (unsigned int)packet;
258  dmaConfig.radio.DESTADDRH = XDATA_SFR_ADDRESS(RFD) >> 8;
259  dmaConfig.radio.DESTADDRL = XDATA_SFR_ADDRESS(RFD);
260  dmaConfig.radio.LENL = 1 + PKTLEN;
261  dmaConfig.radio.VLEN_LENH = 0b00100000; // Transfer length is FirstByte+1
262  // Assumption: DC6 is set correctly
263  dmaConfig.radio.DC7 = 0x40; // SRCINC = 1, DESTINC = 0, IRQMASK = 0, M8 = 0, PRIORITY = 0
264 
265  radioMacState = RADIO_MAC_STATE_TX;
266 }
void radioMacInit(void)
Definition: radio_mac.c:209
#define DATA
Definition: cc2511_types.h:52
void radioMacStrobe(void)
Definition: radio_mac.c:201
unsigned char DC6
Definition: cc2511_map.h:391
#define DMA_CHANNEL_RADIO
Definition: dma.h:21
#define XDATA
Definition: cc2511_types.h:65
volatile BIT radioRxOverflowOccurred
Definition: radio_mac.c:47
volatile BIT radioTxUnderflowOccurred
Definition: radio_mac.c:48
void radioRegistersInit()
unsigned char VLEN_LENH
Definition: cc2511_map.h:387
void radioMacTx(uint8 XDATA *packet)
Definition: radio_mac.c:254
#define RADIO_MAC_EVENT_RX
Definition: radio_mac.h:34
#define RADIO_MAC_EVENT_STROBE
Definition: radio_mac.h:38
unsigned char DC7
Definition: cc2511_map.h:394
#define RADIO_MAC_EVENT_RX_TIMEOUT
Definition: radio_mac.h:36
void radioMacEventHandler(uint8 event)
Definition: radio_link.c:262
DMA14_CONFIG XDATA dmaConfig
Definition: dma.c:5
__bit BIT
Definition: cc2511_types.h:32
#define ISR(source, bank)
Definition: cc2511_map.h:71
#define XDATA_SFR_ADDRESS(sfr)
Definition: cc2511_map.h:369
void radioMacRx(uint8 XDATA *packet, uint8 timeout)
Definition: radio_mac.c:226
unsigned char uint8
Definition: cc2511_types.h:9
#define RADIO_MAC_EVENT_TX
Definition: radio_mac.h:32
volatile DMA_CONFIG radio
Definition: dma.h:30