Attempting to publish a tree
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
MicroBitRadio.cpp
00001 /* 00002 The MIT License (MIT) 00003 00004 Copyright (c) 2016 British Broadcasting Corporation. 00005 This software is provided by Lancaster University by arrangement with the BBC. 00006 00007 Permission is hereby granted, free of charge, to any person obtaining a 00008 copy of this software and associated documentation files (the "Software"), 00009 to deal in the Software without restriction, including without limitation 00010 the rights to use, copy, modify, merge, publish, distribute, sublicense, 00011 and/or sell copies of the Software, and to permit persons to whom the 00012 Software is furnished to do so, subject to the following conditions: 00013 00014 The above copyright notice and this permission notice shall be included in 00015 all copies or substantial portions of the Software. 00016 00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00023 DEALINGS IN THE SOFTWARE. 00024 */ 00025 00026 #include "MicroBitConfig.h" 00027 #include "MicroBitRadio.h" 00028 #include "MicroBitComponent.h" 00029 #include "EventModel.h" 00030 #include "MicroBitDevice.h" 00031 #include "ErrorNo.h" 00032 #include "MicroBitFiber.h" 00033 #include "MicroBitBLEManager.h" 00034 00035 /** 00036 * Provides a simple broadcast radio abstraction, built upon the raw nrf51822 RADIO module. 00037 * 00038 * The nrf51822 RADIO module supports a number of proprietary modes of operation oher than the typical BLE usage. 00039 * This class uses one of these modes to enable simple, point to multipoint communication directly between micro:bits. 00040 * 00041 * TODO: The protocols implemented here do not currently perform any significant form of energy management, 00042 * which means that they will consume far more energy than their BLE equivalent. Later versions of the protocol 00043 * should look to address this through energy efficient broadcast techbiques / sleep scheduling. In particular, the GLOSSY 00044 * approach to efficient rebroadcast and network synchronisation would likely provide an effective future step. 00045 * 00046 * TODO: Meshing should also be considered - again a GLOSSY approach may be effective here, and highly complementary to 00047 * the master/slave arachitecture of BLE. 00048 * 00049 * TODO: This implementation may only operated whilst the BLE stack is disabled. The nrf51822 provides a timeslot API to allow 00050 * BLE to cohabit with other protocols. Future work to allow this colocation would be benefical, and would also allow for the 00051 * creation of wireless BLE bridges. 00052 * 00053 * NOTE: This API does not contain any form of encryption, authentication or authorisation. Its purpose is solely for use as a 00054 * teaching aid to demonstrate how simple communications operates, and to provide a sandpit through which learning can take place. 00055 * For serious applications, BLE should be considered a substantially more secure alternative. 00056 */ 00057 00058 MicroBitRadio* MicroBitRadio::instance = NULL; 00059 00060 extern "C" void RADIO_IRQHandler(void) 00061 { 00062 // Move on to the next buffer, if possible. 00063 MicroBitRadio::instance->queueRxBuf(); 00064 NRF_RADIO->PACKETPTR = (uint32_t) MicroBitRadio::instance->getRxBuf(); 00065 00066 if(NRF_RADIO->EVENTS_READY) 00067 { 00068 NRF_RADIO->EVENTS_READY = 0; 00069 00070 // Start listening and wait for the END event 00071 NRF_RADIO->TASKS_START = 1; 00072 } 00073 00074 if(NRF_RADIO->EVENTS_END) 00075 { 00076 NRF_RADIO->EVENTS_END = 0; 00077 00078 if(NRF_RADIO->CRCSTATUS == 1) 00079 { 00080 uint8_t sample = NRF_RADIO->RSSISAMPLE; 00081 00082 MicroBitRadio::instance->setRSSI(sample); 00083 } 00084 00085 // Start listening and wait for the END event 00086 NRF_RADIO->TASKS_START = 1; 00087 } 00088 } 00089 00090 /** 00091 * Constructor. 00092 * 00093 * Initialise the MicroBitRadio. 00094 * 00095 * @note This class is demand activated, as a result most resources are only 00096 * committed if send/recv or event registrations calls are made. 00097 */ 00098 MicroBitRadio::MicroBitRadio(uint16_t id) : datagram(*this), event (*this) 00099 { 00100 this->id = id; 00101 this->status = 0; 00102 this->group = 0; 00103 this->queueDepth = 0; 00104 this->rssi = 0; 00105 this->rxQueue = NULL; 00106 this->rxBuf = NULL; 00107 00108 instance = this; 00109 } 00110 00111 /** 00112 * Change the output power level of the transmitter to the given value. 00113 * 00114 * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest. 00115 * 00116 * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range. 00117 */ 00118 int MicroBitRadio::setTransmitPower(int power) 00119 { 00120 if (power < 0 || power >= MICROBIT_BLE_POWER_LEVELS) 00121 return MICROBIT_INVALID_PARAMETER; 00122 00123 NRF_RADIO->TXPOWER = (uint32_t)MICROBIT_BLE_POWER_LEVEL[power]; 00124 00125 return MICROBIT_OK; 00126 } 00127 00128 /** 00129 * Change the transmission and reception band of the radio to the given channel 00130 * 00131 * @param band a frequency band in the range 0 - 100. Each step is 1MHz wide, based at 2400MHz. 00132 * 00133 * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range, 00134 * or MICROBIT_NOT_SUPPORTED if the BLE stack is running. 00135 */ 00136 int MicroBitRadio::setFrequencyBand(int band) 00137 { 00138 if (ble_running()) 00139 return MICROBIT_NOT_SUPPORTED; 00140 00141 if (band < 0 || band > 100) 00142 return MICROBIT_INVALID_PARAMETER; 00143 00144 NRF_RADIO->FREQUENCY = (uint32_t)band; 00145 00146 return MICROBIT_OK; 00147 } 00148 00149 /** 00150 * Retrieve a pointer to the currently allocated receive buffer. This is the area of memory 00151 * actively being used by the radio hardware to store incoming data. 00152 * 00153 * @return a pointer to the current receive buffer. 00154 */ 00155 FrameBuffer* MicroBitRadio::getRxBuf() 00156 { 00157 return rxBuf; 00158 } 00159 00160 /** 00161 * Attempt to queue a buffer received by the radio hardware, if sufficient space is available. 00162 * 00163 * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if a replacement receiver buffer 00164 * could not be allocated (either by policy or memory exhaustion). 00165 */ 00166 int MicroBitRadio::queueRxBuf() 00167 { 00168 if (rxBuf == NULL) 00169 return MICROBIT_INVALID_PARAMETER; 00170 00171 if (queueDepth >= MICROBIT_RADIO_MAXIMUM_RX_BUFFERS) 00172 return MICROBIT_NO_RESOURCES; 00173 00174 // Store the received RSSI value in the frame 00175 rxBuf->rssi = getRSSI(); 00176 00177 // Ensure that a replacement buffer is available before queuing. 00178 FrameBuffer *newRxBuf = new FrameBuffer(); 00179 00180 if (newRxBuf == NULL) 00181 return MICROBIT_NO_RESOURCES; 00182 00183 // We add to the tail of the queue to preserve causal ordering. 00184 rxBuf->next = NULL; 00185 00186 if (rxQueue == NULL) 00187 { 00188 rxQueue = rxBuf; 00189 } 00190 else 00191 { 00192 FrameBuffer *p = rxQueue; 00193 while (p->next != NULL) 00194 p = p->next; 00195 00196 p->next = rxBuf; 00197 } 00198 00199 // Increase our received packet count 00200 queueDepth++; 00201 00202 // Allocate a new buffer for the receiver hardware to use. the old on will be passed on to higher layer protocols/apps. 00203 rxBuf = newRxBuf; 00204 00205 return MICROBIT_OK; 00206 } 00207 00208 /** 00209 * Sets the RSSI for the most recent packet. 00210 * 00211 * @param rssi the new rssi value. 00212 * 00213 * @note should only be called from RADIO_IRQHandler... 00214 */ 00215 int MicroBitRadio::setRSSI(uint8_t rssi) 00216 { 00217 if (!(status & MICROBIT_RADIO_STATUS_INITIALISED)) 00218 return MICROBIT_NOT_SUPPORTED; 00219 00220 this->rssi = rssi; 00221 00222 return MICROBIT_OK; 00223 } 00224 00225 /** 00226 * Retrieves the current RSSI for the most recent packet. 00227 * 00228 * @return the most recent RSSI value or MICROBIT_NOT_SUPPORTED if the BLE stack is running. 00229 */ 00230 int MicroBitRadio::getRSSI() 00231 { 00232 if (!(status & MICROBIT_RADIO_STATUS_INITIALISED)) 00233 return MICROBIT_NOT_SUPPORTED; 00234 00235 return this->rssi; 00236 } 00237 00238 /** 00239 * Initialises the radio for use as a multipoint sender/receiver 00240 * 00241 * @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running. 00242 */ 00243 int MicroBitRadio::enable() 00244 { 00245 // If the device is already initialised, then there's nothing to do. 00246 if (status & MICROBIT_RADIO_STATUS_INITIALISED) 00247 return MICROBIT_OK; 00248 00249 // Only attempt to enable this radio mode if BLE is disabled. 00250 if (ble_running()) 00251 return MICROBIT_NOT_SUPPORTED; 00252 00253 // If this is the first time we've been enable, allocate out receive buffers. 00254 if (rxBuf == NULL) 00255 rxBuf = new FrameBuffer(); 00256 00257 if (rxBuf == NULL) 00258 return MICROBIT_NO_RESOURCES; 00259 00260 // Enable the High Frequency clock on the processor. This is a pre-requisite for 00261 // the RADIO module. Without this clock, no communication is possible. 00262 NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; 00263 NRF_CLOCK->TASKS_HFCLKSTART = 1; 00264 while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0); 00265 00266 // Bring up the nrf51822 RADIO module in Nordic's proprietary 1MBps packet radio mode. 00267 setTransmitPower(MICROBIT_RADIO_DEFAULT_TX_POWER); 00268 setFrequencyBand(MICROBIT_RADIO_DEFAULT_FREQUENCY); 00269 00270 // Configure for 1Mbps throughput. 00271 // This may sound excessive, but running a high data rates reduces the chances of collisions... 00272 NRF_RADIO->MODE = RADIO_MODE_MODE_Nrf_1Mbit; 00273 00274 // Configure the addresses we use for this protocol. We run ANONYMOUSLY at the core. 00275 // A 40 bit addresses is used. The first 32 bits match the ASCII character code for "uBit". 00276 // Statistically, this provides assurance to avoid other similar 2.4GHz protocols that may be in the vicinity. 00277 // We also map the assigned 8-bit GROUP id into the PREFIX field. This allows the RADIO hardware to perform 00278 // address matching for us, and only generate an interrupt when a packet matching our group is received. 00279 NRF_RADIO->BASE0 = MICROBIT_RADIO_BASE_ADDRESS; 00280 00281 // Join the default group. This will configure the remaining byte in the RADIO hardware module. 00282 setGroup(MICROBIT_RADIO_DEFAULT_GROUP); 00283 00284 // The RADIO hardware module supports the use of multiple addresses, but as we're running anonymously, we only need one. 00285 // Configure the RADIO module to use the default address (address 0) for both send and receive operations. 00286 NRF_RADIO->TXADDRESS = 0; 00287 NRF_RADIO->RXADDRESSES = 1; 00288 00289 // Packet layout configuration. The nrf51822 has a highly capable and flexible RADIO module that, in addition to transmission 00290 // and reception of data, also contains a LENGTH field, two optional additional 1 byte fields (S0 and S1) and a CRC calculation. 00291 // Configure the packet format for a simple 8 bit length field and no additional fields. 00292 NRF_RADIO->PCNF0 = 0x00000008; 00293 NRF_RADIO->PCNF1 = 0x02040000 | MICROBIT_RADIO_MAX_PACKET_SIZE; 00294 00295 // Most communication channels contain some form of checksum - a mathematical calculation taken based on all the data 00296 // in a packet, that is also sent as part of the packet. When received, this calculation can be repeated, and the results 00297 // from the sender and receiver compared. If they are different, then some corruption of the data ahas happened in transit, 00298 // and we know we can't trust it. The nrf51822 RADIO uses a CRC for this - a very effective checksum calculation. 00299 // 00300 // Enable automatic 16bit CRC generation and checking, and configure how the CRC is calculated. 00301 NRF_RADIO->CRCCNF = RADIO_CRCCNF_LEN_Two; 00302 NRF_RADIO->CRCINIT = 0xFFFF; 00303 NRF_RADIO->CRCPOLY = 0x11021; 00304 00305 // Set the start random value of the data whitening algorithm. This can be any non zero number. 00306 NRF_RADIO->DATAWHITEIV = 0x18; 00307 00308 // Set up the RADIO module to read and write from our internal buffer. 00309 NRF_RADIO->PACKETPTR = (uint32_t)rxBuf; 00310 00311 // Configure the hardware to issue an interrupt whenever a task is complete (e.g. send/receive). 00312 NRF_RADIO->INTENSET = 0x00000008; 00313 NVIC_ClearPendingIRQ(RADIO_IRQn); 00314 NVIC_EnableIRQ(RADIO_IRQn); 00315 00316 NRF_RADIO->SHORTS |= RADIO_SHORTS_ADDRESS_RSSISTART_Msk; 00317 00318 // Start listening for the next packet 00319 NRF_RADIO->EVENTS_READY = 0; 00320 NRF_RADIO->TASKS_RXEN = 1; 00321 while(NRF_RADIO->EVENTS_READY == 0); 00322 00323 NRF_RADIO->EVENTS_END = 0; 00324 NRF_RADIO->TASKS_START = 1; 00325 00326 // register ourselves for a callback event, in order to empty the receive queue. 00327 fiber_add_idle_component(this); 00328 00329 // Done. Record that our RADIO is configured. 00330 status |= MICROBIT_RADIO_STATUS_INITIALISED; 00331 00332 return MICROBIT_OK; 00333 } 00334 00335 /** 00336 * Disables the radio for use as a multipoint sender/receiver. 00337 * 00338 * @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running. 00339 */ 00340 int MicroBitRadio::disable() 00341 { 00342 // Only attempt to enable.disable the radio if the protocol is alreayd running. 00343 if (ble_running()) 00344 return MICROBIT_NOT_SUPPORTED; 00345 00346 if (!(status & MICROBIT_RADIO_STATUS_INITIALISED)) 00347 return MICROBIT_OK; 00348 00349 // Disable interrupts and STOP any ongoing packet reception. 00350 NVIC_DisableIRQ(RADIO_IRQn); 00351 00352 NRF_RADIO->EVENTS_DISABLED = 0; 00353 NRF_RADIO->TASKS_DISABLE = 1; 00354 while(NRF_RADIO->EVENTS_DISABLED == 0); 00355 00356 // deregister ourselves from the callback event used to empty the receive queue. 00357 fiber_remove_idle_component(this); 00358 00359 return MICROBIT_OK; 00360 } 00361 00362 /** 00363 * Sets the radio to listen to packets sent with the given group id. 00364 * 00365 * @param group The group to join. A micro:bit can only listen to one group ID at any time. 00366 * 00367 * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running. 00368 */ 00369 int MicroBitRadio::setGroup(uint8_t group) 00370 { 00371 if (ble_running()) 00372 return MICROBIT_NOT_SUPPORTED; 00373 00374 // Record our group id locally 00375 this->group = group; 00376 00377 // Also append it to the address of this device, to allow the RADIO module to filter for us. 00378 NRF_RADIO->PREFIX0 = (uint32_t)group; 00379 00380 return MICROBIT_OK; 00381 } 00382 00383 /** 00384 * A background, low priority callback that is triggered whenever the processor is idle. 00385 * Here, we empty our queue of received packets, and pass them onto higher level protocol handlers. 00386 */ 00387 void MicroBitRadio::idleTick() 00388 { 00389 // Walk the list of packets and process each one. 00390 while(rxQueue) 00391 { 00392 FrameBuffer *p = rxQueue; 00393 00394 switch (p->protocol) 00395 { 00396 case MICROBIT_RADIO_PROTOCOL_DATAGRAM: 00397 datagram.packetReceived(); 00398 break; 00399 00400 case MICROBIT_RADIO_PROTOCOL_EVENTBUS: 00401 event.packetReceived(); 00402 break; 00403 00404 default: 00405 MicroBitEvent(MICROBIT_ID_RADIO_DATA_READY, p->protocol); 00406 } 00407 00408 // If the packet was processed, it will have been recv'd, and taken from the queue. 00409 // If this was a packet for an unknown protocol, it will still be there, so simply free it. 00410 if (p == rxQueue) 00411 { 00412 recv(); 00413 delete p; 00414 } 00415 } 00416 } 00417 00418 /** 00419 * Determines the number of packets ready to be processed. 00420 * 00421 * @return The number of packets in the receive buffer. 00422 */ 00423 int MicroBitRadio::dataReady() 00424 { 00425 return queueDepth; 00426 } 00427 00428 /** 00429 * Retrieves the next packet from the receive buffer. 00430 * If a data packet is available, then it will be returned immediately to 00431 * the caller. This call will also dequeue the buffer. 00432 * 00433 * @return The buffer containing the the packet. If no data is available, NULL is returned. 00434 * 00435 * @note Once recv() has been called, it is the callers resposibility to 00436 * delete the buffer when appropriate. 00437 */ 00438 FrameBuffer* MicroBitRadio::recv() 00439 { 00440 FrameBuffer *p = rxQueue; 00441 00442 if (p) 00443 { 00444 rxQueue = rxQueue->next; 00445 queueDepth--; 00446 } 00447 00448 return p; 00449 } 00450 00451 /** 00452 * Transmits the given buffer onto the broadcast radio. 00453 * The call will wait until the transmission of the packet has completed before returning. 00454 * 00455 * @param data The packet contents to transmit. 00456 * 00457 * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running. 00458 */ 00459 int MicroBitRadio::send(FrameBuffer *buffer) 00460 { 00461 if (ble_running()) 00462 return MICROBIT_NOT_SUPPORTED; 00463 00464 if (buffer == NULL) 00465 return MICROBIT_INVALID_PARAMETER; 00466 00467 if (buffer->length > MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE - 1) 00468 return MICROBIT_INVALID_PARAMETER; 00469 00470 // Firstly, disable the Radio interrupt. We want to wait until the trasmission completes. 00471 NVIC_DisableIRQ(RADIO_IRQn); 00472 00473 // Turn off the transceiver. 00474 NRF_RADIO->EVENTS_DISABLED = 0; 00475 NRF_RADIO->TASKS_DISABLE = 1; 00476 while(NRF_RADIO->EVENTS_DISABLED == 0); 00477 00478 // Configure the radio to send the buffer provided. 00479 NRF_RADIO->PACKETPTR = (uint32_t) buffer; 00480 00481 // Turn on the transmitter, and wait for it to signal that it's ready to use. 00482 NRF_RADIO->EVENTS_READY = 0; 00483 NRF_RADIO->TASKS_TXEN = 1; 00484 while (NRF_RADIO->EVENTS_READY == 0); 00485 00486 // Start transmission and wait for end of packet. 00487 NRF_RADIO->TASKS_START = 1; 00488 NRF_RADIO->EVENTS_END = 0; 00489 while(NRF_RADIO->EVENTS_END == 0); 00490 00491 // Return the radio to using the default receive buffer 00492 NRF_RADIO->PACKETPTR = (uint32_t) rxBuf; 00493 00494 // Turn off the transmitter. 00495 NRF_RADIO->EVENTS_DISABLED = 0; 00496 NRF_RADIO->TASKS_DISABLE = 1; 00497 while(NRF_RADIO->EVENTS_DISABLED == 0); 00498 00499 // Start listening for the next packet 00500 NRF_RADIO->EVENTS_READY = 0; 00501 NRF_RADIO->TASKS_RXEN = 1; 00502 while(NRF_RADIO->EVENTS_READY == 0); 00503 00504 NRF_RADIO->EVENTS_END = 0; 00505 NRF_RADIO->TASKS_START = 1; 00506 00507 // Re-enable the Radio interrupt. 00508 NVIC_ClearPendingIRQ(RADIO_IRQn); 00509 NVIC_EnableIRQ(RADIO_IRQn); 00510 00511 return MICROBIT_OK; 00512 }
Generated on Tue Jul 12 2022 19:58:09 by 1.7.2