A Atmel RF2xx Radio Library for Mbed
Dependents: xBedRadio MxSniffer
MxRadio.cpp@0:5f1d66c85ae0, 2015-04-09 (annotated)
- Committer:
- fredqian
- Date:
- Thu Apr 09 16:42:51 2015 +0800
- Revision:
- 0:5f1d66c85ae0
init commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
fredqian | 0:5f1d66c85ae0 | 1 | /* Copyright (c) 2011 Frank Zhao |
fredqian | 0:5f1d66c85ae0 | 2 | All rights reserved. |
fredqian | 0:5f1d66c85ae0 | 3 | |
fredqian | 0:5f1d66c85ae0 | 4 | Redistribution and use in source and binary forms, with or without |
fredqian | 0:5f1d66c85ae0 | 5 | modification, are permitted provided that the following conditions |
fredqian | 0:5f1d66c85ae0 | 6 | are met: |
fredqian | 0:5f1d66c85ae0 | 7 | |
fredqian | 0:5f1d66c85ae0 | 8 | * Redistributions of source code must retain the above copyright |
fredqian | 0:5f1d66c85ae0 | 9 | notice, this list of conditions and the following disclaimer. |
fredqian | 0:5f1d66c85ae0 | 10 | * Redistributions in binary form must reproduce the above copyright |
fredqian | 0:5f1d66c85ae0 | 11 | notice, this list of conditions and the following disclaimer in the |
fredqian | 0:5f1d66c85ae0 | 12 | documentation and/or other materials provided with the distribution. |
fredqian | 0:5f1d66c85ae0 | 13 | * Neither the name of the authors nor the names of its contributors |
fredqian | 0:5f1d66c85ae0 | 14 | may be used to endorse or promote products derived from this software |
fredqian | 0:5f1d66c85ae0 | 15 | without specific prior written permission. |
fredqian | 0:5f1d66c85ae0 | 16 | |
fredqian | 0:5f1d66c85ae0 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
fredqian | 0:5f1d66c85ae0 | 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
fredqian | 0:5f1d66c85ae0 | 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
fredqian | 0:5f1d66c85ae0 | 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
fredqian | 0:5f1d66c85ae0 | 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
fredqian | 0:5f1d66c85ae0 | 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
fredqian | 0:5f1d66c85ae0 | 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
fredqian | 0:5f1d66c85ae0 | 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
fredqian | 0:5f1d66c85ae0 | 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
fredqian | 0:5f1d66c85ae0 | 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
fredqian | 0:5f1d66c85ae0 | 27 | POSSIBILITY OF SUCH DAMAGE. */ |
fredqian | 0:5f1d66c85ae0 | 28 | |
fredqian | 0:5f1d66c85ae0 | 29 | #include "MxRadio.h" |
fredqian | 0:5f1d66c85ae0 | 30 | #include "MxRadioEvents.h" |
fredqian | 0:5f1d66c85ae0 | 31 | //#define radio_set_state radio_force_state |
fredqian | 0:5f1d66c85ae0 | 32 | |
fredqian | 0:5f1d66c85ae0 | 33 | // the write function checks if beginTransmission was called prior to write |
fredqian | 0:5f1d66c85ae0 | 34 | // in order to determine whether to use immediate or non-immediate transmission |
fredqian | 0:5f1d66c85ae0 | 35 | |
fredqian | 0:5f1d66c85ae0 | 36 | const uint8_t HeadSize=9; |
fredqian | 0:5f1d66c85ae0 | 37 | |
fredqian | 0:5f1d66c85ae0 | 38 | // a busy indicator so transmit functions can wait until the last transmission has finished |
fredqian | 0:5f1d66c85ae0 | 39 | volatile uint8_t txIsBusy = 0; |
fredqian | 0:5f1d66c85ae0 | 40 | |
fredqian | 0:5f1d66c85ae0 | 41 | |
fredqian | 0:5f1d66c85ae0 | 42 | cMxRadio::~cMxRadio() |
fredqian | 0:5f1d66c85ae0 | 43 | { |
fredqian | 0:5f1d66c85ae0 | 44 | } |
fredqian | 0:5f1d66c85ae0 | 45 | // empty constructor, should not be called by user |
fredqian | 0:5f1d66c85ae0 | 46 | cMxRadio::cMxRadio(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst, PinName slp, PinName irq) |
fredqian | 0:5f1d66c85ae0 | 47 | :m_spi(mosi, miso, sclk), m_cs(cs), reset_pin(rst), sleep_pin(slp),irq_pin(irq) |
fredqian | 0:5f1d66c85ae0 | 48 | { |
fredqian | 0:5f1d66c85ae0 | 49 | // default event handlers |
fredqian | 0:5f1d66c85ae0 | 50 | |
fredqian | 0:5f1d66c85ae0 | 51 | zrEventTxDone=0; |
fredqian | 0:5f1d66c85ae0 | 52 | zrEventReceiveFrame=0; |
fredqian | 0:5f1d66c85ae0 | 53 | temprssi=0; |
fredqian | 0:5f1d66c85ae0 | 54 | setautotx=false; |
fredqian | 0:5f1d66c85ae0 | 55 | setautorx=false; |
fredqian | 0:5f1d66c85ae0 | 56 | needack=false; |
fredqian | 0:5f1d66c85ae0 | 57 | usedBeginTransmission = 0; |
fredqian | 0:5f1d66c85ae0 | 58 | hasAttachedTxEvent = 0; |
fredqian | 0:5f1d66c85ae0 | 59 | hasAttachedRxEvent = 0; |
fredqian | 0:5f1d66c85ae0 | 60 | lastLqi = 0; |
fredqian | 0:5f1d66c85ae0 | 61 | lastRssi = 0; |
fredqian | 0:5f1d66c85ae0 | 62 | txTmpBufferLength = 0; |
fredqian | 0:5f1d66c85ae0 | 63 | rxRingBufferHead = 0; |
fredqian | 0:5f1d66c85ae0 | 64 | rxRingBufferTail = 0; |
fredqian | 0:5f1d66c85ae0 | 65 | /* |
fredqian | 0:5f1d66c85ae0 | 66 | user_radio_tx_done=0; |
fredqian | 0:5f1d66c85ae0 | 67 | user_radio_receive_frame=0; |
fredqian | 0:5f1d66c85ae0 | 68 | user_radio_irq=0; |
fredqian | 0:5f1d66c85ae0 | 69 | user_radio_error=0; |
fredqian | 0:5f1d66c85ae0 | 70 | */ |
fredqian | 0:5f1d66c85ae0 | 71 | zr_attach_receive_frame(&cMxRadio::onReceiveFrame); |
fredqian | 0:5f1d66c85ae0 | 72 | zr_attach_tx_done(&cMxRadio::onTxDone); |
fredqian | 0:5f1d66c85ae0 | 73 | |
fredqian | 0:5f1d66c85ae0 | 74 | } |
fredqian | 0:5f1d66c85ae0 | 75 | |
fredqian | 0:5f1d66c85ae0 | 76 | /** |
fredqian | 0:5f1d66c85ae0 | 77 | * @brief Radio Initialization |
fredqian | 0:5f1d66c85ae0 | 78 | * |
fredqian | 0:5f1d66c85ae0 | 79 | * The function initializes all IO ressources, |
fredqian | 0:5f1d66c85ae0 | 80 | * needed for the usage of the radio and performs |
fredqian | 0:5f1d66c85ae0 | 81 | * a reset to the radio. |
fredqian | 0:5f1d66c85ae0 | 82 | * It prepares the frame header. |
fredqian | 0:5f1d66c85ae0 | 83 | * Then it sets the channel number and defaults to RX state. |
fredqian | 0:5f1d66c85ae0 | 84 | * |
fredqian | 0:5f1d66c85ae0 | 85 | * @param chan the channel number for the radio to use, 11 to 26 |
fredqian | 0:5f1d66c85ae0 | 86 | */ |
fredqian | 0:5f1d66c85ae0 | 87 | void cMxRadio::begin(channel_t chan) |
fredqian | 0:5f1d66c85ae0 | 88 | { |
fredqian | 0:5f1d66c85ae0 | 89 | begin(chan, 0); |
fredqian | 0:5f1d66c85ae0 | 90 | } |
fredqian | 0:5f1d66c85ae0 | 91 | |
fredqian | 0:5f1d66c85ae0 | 92 | void cMxRadio::begin(channel_t chan,uint16_t panid,uint16_t localaddress,bool needackval,bool autotxval,bool autorxval,char autoretrycount) |
fredqian | 0:5f1d66c85ae0 | 93 | { |
fredqian | 0:5f1d66c85ae0 | 94 | setautotx=autotxval; |
fredqian | 0:5f1d66c85ae0 | 95 | setautorx=autorxval; |
fredqian | 0:5f1d66c85ae0 | 96 | needack=needackval; |
fredqian | 0:5f1d66c85ae0 | 97 | radio_init(rxFrameBuffer, MAX_FRAME_SIZE); |
fredqian | 0:5f1d66c85ae0 | 98 | |
fredqian | 0:5f1d66c85ae0 | 99 | ////////////////////////////// |
fredqian | 0:5f1d66c85ae0 | 100 | #ifdef SR_MAX_FRAME_RETRES |
fredqian | 0:5f1d66c85ae0 | 101 | trx_bit_write(SR_MAX_FRAME_RETRES,autoretrycount & 0Xf);//for auto wake up |
fredqian | 0:5f1d66c85ae0 | 102 | ////////////////////////////// |
fredqian | 0:5f1d66c85ae0 | 103 | #endif |
fredqian | 0:5f1d66c85ae0 | 104 | |
fredqian | 0:5f1d66c85ae0 | 105 | |
fredqian | 0:5f1d66c85ae0 | 106 | if(!needack) |
fredqian | 0:5f1d66c85ae0 | 107 | txTmpBuffer[0] = 0x41; |
fredqian | 0:5f1d66c85ae0 | 108 | else |
fredqian | 0:5f1d66c85ae0 | 109 | txTmpBuffer[0] = 0x61; //ack required |
fredqian | 0:5f1d66c85ae0 | 110 | txTmpBuffer[1] = 0x88; |
fredqian | 0:5f1d66c85ae0 | 111 | txTmpBuffer[2] = 0; |
fredqian | 0:5f1d66c85ae0 | 112 | txTmpBuffer[3]=(uint8_t)(panid & 0xFF ); |
fredqian | 0:5f1d66c85ae0 | 113 | txTmpBuffer[4]=(uint8_t)((panid>>8) & 0xFF ); |
fredqian | 0:5f1d66c85ae0 | 114 | txTmpBuffer[5] = 0xFF; //dest address low byte |
fredqian | 0:5f1d66c85ae0 | 115 | txTmpBuffer[6] = 0xFF; //dest address hight byte |
fredqian | 0:5f1d66c85ae0 | 116 | txTmpBuffer[7] = (uint8_t)(localaddress & 0xFF ); |
fredqian | 0:5f1d66c85ae0 | 117 | txTmpBuffer[8] = (uint8_t)((localaddress>>8) & 0xFF ); |
fredqian | 0:5f1d66c85ae0 | 118 | setParam(phyPanId,(uint16_t)panid ); |
fredqian | 0:5f1d66c85ae0 | 119 | setParam(phyShortAddr,(uint16_t)localaddress ); |
fredqian | 0:5f1d66c85ae0 | 120 | |
fredqian | 0:5f1d66c85ae0 | 121 | radio_set_param(RP_CHANNEL(chan)); |
fredqian | 0:5f1d66c85ae0 | 122 | |
fredqian | 0:5f1d66c85ae0 | 123 | // default to receiver |
fredqian | 0:5f1d66c85ae0 | 124 | if (setautorx) |
fredqian | 0:5f1d66c85ae0 | 125 | radio_set_state(STATE_RXAUTO); |
fredqian | 0:5f1d66c85ae0 | 126 | else |
fredqian | 0:5f1d66c85ae0 | 127 | radio_set_state(STATE_RX); |
fredqian | 0:5f1d66c85ae0 | 128 | #ifdef ENABLE_DIG3_DIG4 |
fredqian | 0:5f1d66c85ae0 | 129 | trx_bit_write(SR_PA_EXT_EN, 1); |
fredqian | 0:5f1d66c85ae0 | 130 | #endif |
fredqian | 0:5f1d66c85ae0 | 131 | } |
fredqian | 0:5f1d66c85ae0 | 132 | void cMxRadio::begin(channel_t chan,uint16_t panid,uint16_t localaddress,bool needackval,bool autotxval,bool autorxval) |
fredqian | 0:5f1d66c85ae0 | 133 | { |
fredqian | 0:5f1d66c85ae0 | 134 | cMxRadio::begin(chan,panid,localaddress,needackval,autotxval,autorxval,4); |
fredqian | 0:5f1d66c85ae0 | 135 | |
fredqian | 0:5f1d66c85ae0 | 136 | } |
fredqian | 0:5f1d66c85ae0 | 137 | void cMxRadio::sendFrame(uint16_t destaddress,bool needackval,uint8_t* frm, uint8_t len) |
fredqian | 0:5f1d66c85ae0 | 138 | { |
fredqian | 0:5f1d66c85ae0 | 139 | uint8_t oldvalue=txTmpBuffer[0]; |
fredqian | 0:5f1d66c85ae0 | 140 | if(!needackval) |
fredqian | 0:5f1d66c85ae0 | 141 | txTmpBuffer[0] = 0x41; |
fredqian | 0:5f1d66c85ae0 | 142 | else |
fredqian | 0:5f1d66c85ae0 | 143 | txTmpBuffer[0] = 0x61; |
fredqian | 0:5f1d66c85ae0 | 144 | beginTransmission(destaddress); |
fredqian | 0:5f1d66c85ae0 | 145 | write(frm,len); |
fredqian | 0:5f1d66c85ae0 | 146 | endTransmission(); |
fredqian | 0:5f1d66c85ae0 | 147 | txTmpBuffer[0]=oldvalue; |
fredqian | 0:5f1d66c85ae0 | 148 | |
fredqian | 0:5f1d66c85ae0 | 149 | } |
fredqian | 0:5f1d66c85ae0 | 150 | /** |
fredqian | 0:5f1d66c85ae0 | 151 | * @brief Radio Initialization |
fredqian | 0:5f1d66c85ae0 | 152 | * |
fredqian | 0:5f1d66c85ae0 | 153 | * Overload for radio initalization that allows for the user to set a custom frame header |
fredqian | 0:5f1d66c85ae0 | 154 | * |
fredqian | 0:5f1d66c85ae0 | 155 | * @param chan channel number for the radio to use, 11 to 26 |
fredqian | 0:5f1d66c85ae0 | 156 | * @param frameHeader HeadSize byte custom frame header |
fredqian | 0:5f1d66c85ae0 | 157 | */ |
fredqian | 0:5f1d66c85ae0 | 158 | void cMxRadio::begin(channel_t chan, uint8_t* frameHeader) |
fredqian | 0:5f1d66c85ae0 | 159 | { |
fredqian | 0:5f1d66c85ae0 | 160 | radio_init(rxFrameBuffer, MAX_FRAME_SIZE); |
fredqian | 0:5f1d66c85ae0 | 161 | |
fredqian | 0:5f1d66c85ae0 | 162 | if (frameHeader) |
fredqian | 0:5f1d66c85ae0 | 163 | { |
fredqian | 0:5f1d66c85ae0 | 164 | // copy custom frame header |
fredqian | 0:5f1d66c85ae0 | 165 | int i; |
fredqian | 0:5f1d66c85ae0 | 166 | for (i = 0; i < HeadSize; i++) |
fredqian | 0:5f1d66c85ae0 | 167 | txTmpBuffer[i] = frameHeader[i]; |
fredqian | 0:5f1d66c85ae0 | 168 | } |
fredqian | 0:5f1d66c85ae0 | 169 | else |
fredqian | 0:5f1d66c85ae0 | 170 | { |
fredqian | 0:5f1d66c85ae0 | 171 | txTmpBuffer[0] = 0x41; |
fredqian | 0:5f1d66c85ae0 | 172 | txTmpBuffer[1] = 0x88; |
fredqian | 0:5f1d66c85ae0 | 173 | txTmpBuffer[2] = 0; |
fredqian | 0:5f1d66c85ae0 | 174 | txTmpBuffer[3]=0xCD; |
fredqian | 0:5f1d66c85ae0 | 175 | txTmpBuffer[4]=0xAB; |
fredqian | 0:5f1d66c85ae0 | 176 | txTmpBuffer[5] = 0xFF; //dest address low byte |
fredqian | 0:5f1d66c85ae0 | 177 | txTmpBuffer[6] = 0xFF; //dest address hight byte |
fredqian | 0:5f1d66c85ae0 | 178 | txTmpBuffer[7] = 0x01;; |
fredqian | 0:5f1d66c85ae0 | 179 | txTmpBuffer[8] = 0x00;; |
fredqian | 0:5f1d66c85ae0 | 180 | } |
fredqian | 0:5f1d66c85ae0 | 181 | |
fredqian | 0:5f1d66c85ae0 | 182 | // set the channel |
fredqian | 0:5f1d66c85ae0 | 183 | radio_set_param(RP_CHANNEL(chan)); |
fredqian | 0:5f1d66c85ae0 | 184 | |
fredqian | 0:5f1d66c85ae0 | 185 | // default to receiver |
fredqian | 0:5f1d66c85ae0 | 186 | if (setautorx) |
fredqian | 0:5f1d66c85ae0 | 187 | radio_set_state(STATE_RXAUTO); |
fredqian | 0:5f1d66c85ae0 | 188 | else |
fredqian | 0:5f1d66c85ae0 | 189 | radio_set_state(STATE_RX); |
fredqian | 0:5f1d66c85ae0 | 190 | |
fredqian | 0:5f1d66c85ae0 | 191 | #ifdef ENABLE_DIG3_DIG4 |
fredqian | 0:5f1d66c85ae0 | 192 | trx_bit_write(SR_PA_EXT_EN, 1); |
fredqian | 0:5f1d66c85ae0 | 193 | #endif |
fredqian | 0:5f1d66c85ae0 | 194 | |
fredqian | 0:5f1d66c85ae0 | 195 | } |
fredqian | 0:5f1d66c85ae0 | 196 | |
fredqian | 0:5f1d66c85ae0 | 197 | /** |
fredqian | 0:5f1d66c85ae0 | 198 | * @brief Set Frame Header |
fredqian | 0:5f1d66c85ae0 | 199 | * |
fredqian | 0:5f1d66c85ae0 | 200 | * changes the custom frame header |
fredqian | 0:5f1d66c85ae0 | 201 | * |
fredqian | 0:5f1d66c85ae0 | 202 | * @param frameHeader HeadSize byte custom frame header |
fredqian | 0:5f1d66c85ae0 | 203 | */ |
fredqian | 0:5f1d66c85ae0 | 204 | void cMxRadio::setFrameHeader(uint8_t* frameHeader) |
fredqian | 0:5f1d66c85ae0 | 205 | { |
fredqian | 0:5f1d66c85ae0 | 206 | // copy custom frame header |
fredqian | 0:5f1d66c85ae0 | 207 | int i; |
fredqian | 0:5f1d66c85ae0 | 208 | for (i = 0; i < HeadSize; i++) |
fredqian | 0:5f1d66c85ae0 | 209 | txTmpBuffer[i] = frameHeader[i]; |
fredqian | 0:5f1d66c85ae0 | 210 | } |
fredqian | 0:5f1d66c85ae0 | 211 | |
fredqian | 0:5f1d66c85ae0 | 212 | /** |
fredqian | 0:5f1d66c85ae0 | 213 | * @brief Attach Error Event Handler |
fredqian | 0:5f1d66c85ae0 | 214 | * |
fredqian | 0:5f1d66c85ae0 | 215 | * Allows the user to set a error handling function |
fredqian | 0:5f1d66c85ae0 | 216 | * An empty one is used if none is set |
fredqian | 0:5f1d66c85ae0 | 217 | * |
fredqian | 0:5f1d66c85ae0 | 218 | * @param funct the function pointer to the event handler |
fredqian | 0:5f1d66c85ae0 | 219 | */ |
fredqian | 0:5f1d66c85ae0 | 220 | void cMxRadio::attachError(void (*funct)(radio_error_t)) |
fredqian | 0:5f1d66c85ae0 | 221 | { |
fredqian | 0:5f1d66c85ae0 | 222 | user_radio_error = funct; |
fredqian | 0:5f1d66c85ae0 | 223 | } |
fredqian | 0:5f1d66c85ae0 | 224 | |
fredqian | 0:5f1d66c85ae0 | 225 | /** |
fredqian | 0:5f1d66c85ae0 | 226 | * @brief Attach IRQ Event Handler |
fredqian | 0:5f1d66c85ae0 | 227 | * |
fredqian | 0:5f1d66c85ae0 | 228 | * Allows the user to set a IRQ handling function |
fredqian | 0:5f1d66c85ae0 | 229 | * An empty one is used if none is set |
fredqian | 0:5f1d66c85ae0 | 230 | * |
fredqian | 0:5f1d66c85ae0 | 231 | * @param funct the function pointer to the event handler |
fredqian | 0:5f1d66c85ae0 | 232 | */ |
fredqian | 0:5f1d66c85ae0 | 233 | void cMxRadio::attachIrq(void (*funct)(uint8_t)) |
fredqian | 0:5f1d66c85ae0 | 234 | { |
fredqian | 0:5f1d66c85ae0 | 235 | user_radio_irq = funct; |
fredqian | 0:5f1d66c85ae0 | 236 | } |
fredqian | 0:5f1d66c85ae0 | 237 | |
fredqian | 0:5f1d66c85ae0 | 238 | /** |
fredqian | 0:5f1d66c85ae0 | 239 | * @brief Attach RX Event Handler |
fredqian | 0:5f1d66c85ae0 | 240 | * |
fredqian | 0:5f1d66c85ae0 | 241 | * Allows the user to set a RX handling function |
fredqian | 0:5f1d66c85ae0 | 242 | * The default handler will read in the received frame and place it into the RX FIFO ring buffer |
fredqian | 0:5f1d66c85ae0 | 243 | * If the user chooses to use this attach function, then the default handler will not be used |
fredqian | 0:5f1d66c85ae0 | 244 | * This means read/peek/available/flush will stop working |
fredqian | 0:5f1d66c85ae0 | 245 | * |
fredqian | 0:5f1d66c85ae0 | 246 | * @param funct the function pointer to the event handler |
fredqian | 0:5f1d66c85ae0 | 247 | */ |
fredqian | 0:5f1d66c85ae0 | 248 | void cMxRadio::attachReceiveFrame(uint8_t* (*funct)(uint8_t, uint8_t*, uint8_t,int8_t, uint8_t)) |
fredqian | 0:5f1d66c85ae0 | 249 | { |
fredqian | 0:5f1d66c85ae0 | 250 | zrEventReceiveFrame = funct; |
fredqian | 0:5f1d66c85ae0 | 251 | hasAttachedRxEvent = (funct == 0) ? 0 : 1; |
fredqian | 0:5f1d66c85ae0 | 252 | } |
fredqian | 0:5f1d66c85ae0 | 253 | |
fredqian | 0:5f1d66c85ae0 | 254 | /** |
fredqian | 0:5f1d66c85ae0 | 255 | * @brief Attach TX Complete Event Handler |
fredqian | 0:5f1d66c85ae0 | 256 | * |
fredqian | 0:5f1d66c85ae0 | 257 | * Allows the user to set a TX complete handling function |
fredqian | 0:5f1d66c85ae0 | 258 | * An empty one is used if none is set |
fredqian | 0:5f1d66c85ae0 | 259 | * The event will occur before the busy flag is reset and returning to RX state |
fredqian | 0:5f1d66c85ae0 | 260 | * |
fredqian | 0:5f1d66c85ae0 | 261 | * @param funct the function pointer to the event handler |
fredqian | 0:5f1d66c85ae0 | 262 | */ |
fredqian | 0:5f1d66c85ae0 | 263 | void cMxRadio::attachTxDone(void (*funct)(radio_tx_done_t)) |
fredqian | 0:5f1d66c85ae0 | 264 | { |
fredqian | 0:5f1d66c85ae0 | 265 | zrEventTxDone = funct; |
fredqian | 0:5f1d66c85ae0 | 266 | hasAttachedTxEvent = (funct == 0) ? 0 : 1; |
fredqian | 0:5f1d66c85ae0 | 267 | } |
fredqian | 0:5f1d66c85ae0 | 268 | |
fredqian | 0:5f1d66c85ae0 | 269 | /** |
fredqian | 0:5f1d66c85ae0 | 270 | * @brief Default RX Event Handler |
fredqian | 0:5f1d66c85ae0 | 271 | * |
fredqian | 0:5f1d66c85ae0 | 272 | * If the user has not attached a custom event handler, then the bytes are placed into the FIFO ring buffer |
fredqian | 0:5f1d66c85ae0 | 273 | * If the frame contains a header, then the frame header is ignored, only the payload is read |
fredqian | 0:5f1d66c85ae0 | 274 | * The above does not occur if a user handler is attached, which will be called instead |
fredqian | 0:5f1d66c85ae0 | 275 | * The LQI and RSSI is always remembered |
fredqian | 0:5f1d66c85ae0 | 276 | * |
fredqian | 0:5f1d66c85ae0 | 277 | * This should not be called by the user |
fredqian | 0:5f1d66c85ae0 | 278 | * |
fredqian | 0:5f1d66c85ae0 | 279 | * @param len length of the frame |
fredqian | 0:5f1d66c85ae0 | 280 | * @param frm array containing frame data |
fredqian | 0:5f1d66c85ae0 | 281 | * @param lqi link quality indicator |
fredqian | 0:5f1d66c85ae0 | 282 | * @param crc_fail boolean indicating whether the received frame failed FCS verification, not used |
fredqian | 0:5f1d66c85ae0 | 283 | */ |
fredqian | 0:5f1d66c85ae0 | 284 | uint8_t* cMxRadio::onReceiveFrame(uint8_t len, uint8_t* frm, uint8_t lqi, int8_t ed,uint8_t crc_fail) |
fredqian | 0:5f1d66c85ae0 | 285 | { |
fredqian | 0:5f1d66c85ae0 | 286 | if (crc_fail) |
fredqian | 0:5f1d66c85ae0 | 287 | return rxRingBuffer; |
fredqian | 0:5f1d66c85ae0 | 288 | lastLqi = lqi; |
fredqian | 0:5f1d66c85ae0 | 289 | //lastRssi=ed; |
fredqian | 0:5f1d66c85ae0 | 290 | if (hasAttachedRxEvent == 0) |
fredqian | 0:5f1d66c85ae0 | 291 | { |
fredqian | 0:5f1d66c85ae0 | 292 | // no event handler, so write it into the FIFO |
fredqian | 0:5f1d66c85ae0 | 293 | |
fredqian | 0:5f1d66c85ae0 | 294 | if (len >= HeadSize-1) |
fredqian | 0:5f1d66c85ae0 | 295 | { |
fredqian | 0:5f1d66c85ae0 | 296 | // frame header exists |
fredqian | 0:5f1d66c85ae0 | 297 | // copy only payload |
fredqian | 0:5f1d66c85ae0 | 298 | |
fredqian | 0:5f1d66c85ae0 | 299 | for (uint8_t i = HeadSize; i < len - 2; i++) |
fredqian | 0:5f1d66c85ae0 | 300 | { |
fredqian | 0:5f1d66c85ae0 | 301 | uint16_t j = ((uint16_t)((uint16_t)rxRingBufferHead + 1)) % ZR_FIFO_SIZE; |
fredqian | 0:5f1d66c85ae0 | 302 | if (j != rxRingBufferTail) |
fredqian | 0:5f1d66c85ae0 | 303 | { |
fredqian | 0:5f1d66c85ae0 | 304 | // push into FIFO |
fredqian | 0:5f1d66c85ae0 | 305 | rxRingBuffer[rxRingBufferHead] = frm[i]; |
fredqian | 0:5f1d66c85ae0 | 306 | rxRingBufferHead = j; |
fredqian | 0:5f1d66c85ae0 | 307 | } |
fredqian | 0:5f1d66c85ae0 | 308 | else |
fredqian | 0:5f1d66c85ae0 | 309 | { |
fredqian | 0:5f1d66c85ae0 | 310 | // FIFO full |
fredqian | 0:5f1d66c85ae0 | 311 | break; |
fredqian | 0:5f1d66c85ae0 | 312 | } |
fredqian | 0:5f1d66c85ae0 | 313 | } |
fredqian | 0:5f1d66c85ae0 | 314 | } |
fredqian | 0:5f1d66c85ae0 | 315 | else |
fredqian | 0:5f1d66c85ae0 | 316 | { |
fredqian | 0:5f1d66c85ae0 | 317 | // frame header does not exist |
fredqian | 0:5f1d66c85ae0 | 318 | // copy everything |
fredqian | 0:5f1d66c85ae0 | 319 | |
fredqian | 0:5f1d66c85ae0 | 320 | for (uint8_t i = 0; i < len; i++) |
fredqian | 0:5f1d66c85ae0 | 321 | { |
fredqian | 0:5f1d66c85ae0 | 322 | uint16_t j = ((uint16_t)((uint16_t)rxRingBufferHead + 1)) % ZR_FIFO_SIZE; |
fredqian | 0:5f1d66c85ae0 | 323 | if (j != rxRingBufferTail) |
fredqian | 0:5f1d66c85ae0 | 324 | { |
fredqian | 0:5f1d66c85ae0 | 325 | // push into FIFO |
fredqian | 0:5f1d66c85ae0 | 326 | rxRingBuffer[rxRingBufferHead] = frm[i]; |
fredqian | 0:5f1d66c85ae0 | 327 | rxRingBufferHead = j; |
fredqian | 0:5f1d66c85ae0 | 328 | } |
fredqian | 0:5f1d66c85ae0 | 329 | else |
fredqian | 0:5f1d66c85ae0 | 330 | { |
fredqian | 0:5f1d66c85ae0 | 331 | // FIFO full |
fredqian | 0:5f1d66c85ae0 | 332 | break; |
fredqian | 0:5f1d66c85ae0 | 333 | } |
fredqian | 0:5f1d66c85ae0 | 334 | } |
fredqian | 0:5f1d66c85ae0 | 335 | } |
fredqian | 0:5f1d66c85ae0 | 336 | |
fredqian | 0:5f1d66c85ae0 | 337 | return rxRingBuffer; |
fredqian | 0:5f1d66c85ae0 | 338 | } |
fredqian | 0:5f1d66c85ae0 | 339 | else |
fredqian | 0:5f1d66c85ae0 | 340 | { |
fredqian | 0:5f1d66c85ae0 | 341 | // user event is attached so call it |
fredqian | 0:5f1d66c85ae0 | 342 | return zrEventReceiveFrame(len, frm, lqi, ed,crc_fail); |
fredqian | 0:5f1d66c85ae0 | 343 | } |
fredqian | 0:5f1d66c85ae0 | 344 | } |
fredqian | 0:5f1d66c85ae0 | 345 | |
fredqian | 0:5f1d66c85ae0 | 346 | /** |
fredqian | 0:5f1d66c85ae0 | 347 | * @brief RX Buffer Flush |
fredqian | 0:5f1d66c85ae0 | 348 | * |
fredqian | 0:5f1d66c85ae0 | 349 | * Flush the RX FIFO ring buffer |
fredqian | 0:5f1d66c85ae0 | 350 | */ |
fredqian | 0:5f1d66c85ae0 | 351 | void cMxRadio::flush() |
fredqian | 0:5f1d66c85ae0 | 352 | { |
fredqian | 0:5f1d66c85ae0 | 353 | rxRingBufferHead = rxRingBufferTail; |
fredqian | 0:5f1d66c85ae0 | 354 | } |
fredqian | 0:5f1d66c85ae0 | 355 | |
fredqian | 0:5f1d66c85ae0 | 356 | /** |
fredqian | 0:5f1d66c85ae0 | 357 | * @brief RX Buffer Read |
fredqian | 0:5f1d66c85ae0 | 358 | * |
fredqian | 0:5f1d66c85ae0 | 359 | * pops and returns the next byte from the FIFO ring buffer |
fredqian | 0:5f1d66c85ae0 | 360 | * |
fredqian | 0:5f1d66c85ae0 | 361 | * @return the next byte, or -1 if buffer is empty |
fredqian | 0:5f1d66c85ae0 | 362 | */ |
fredqian | 0:5f1d66c85ae0 | 363 | int16_t cMxRadio::read() |
fredqian | 0:5f1d66c85ae0 | 364 | { |
fredqian | 0:5f1d66c85ae0 | 365 | // if the head isn't ahead of the tail, we don't have any characters |
fredqian | 0:5f1d66c85ae0 | 366 | if (rxRingBufferHead == rxRingBufferTail) |
fredqian | 0:5f1d66c85ae0 | 367 | { |
fredqian | 0:5f1d66c85ae0 | 368 | return -1; |
fredqian | 0:5f1d66c85ae0 | 369 | } |
fredqian | 0:5f1d66c85ae0 | 370 | else |
fredqian | 0:5f1d66c85ae0 | 371 | { |
fredqian | 0:5f1d66c85ae0 | 372 | uint8_t c = rxRingBuffer[rxRingBufferTail]; |
fredqian | 0:5f1d66c85ae0 | 373 | rxRingBufferTail = (rxRingBufferTail + 1) % ZR_FIFO_SIZE; // pop |
fredqian | 0:5f1d66c85ae0 | 374 | return c; |
fredqian | 0:5f1d66c85ae0 | 375 | } |
fredqian | 0:5f1d66c85ae0 | 376 | } |
fredqian | 0:5f1d66c85ae0 | 377 | |
fredqian | 0:5f1d66c85ae0 | 378 | /** |
fredqian | 0:5f1d66c85ae0 | 379 | * @brief RX Buffer Peek |
fredqian | 0:5f1d66c85ae0 | 380 | * |
fredqian | 0:5f1d66c85ae0 | 381 | * returns the next byte from the FIFO ring buffer without removing it |
fredqian | 0:5f1d66c85ae0 | 382 | * |
fredqian | 0:5f1d66c85ae0 | 383 | * @return the next byte, or -1 if buffer is empty |
fredqian | 0:5f1d66c85ae0 | 384 | */ |
fredqian | 0:5f1d66c85ae0 | 385 | int16_t cMxRadio::peek() |
fredqian | 0:5f1d66c85ae0 | 386 | { |
fredqian | 0:5f1d66c85ae0 | 387 | // if the head isn't ahead of the tail, we don't have any characters |
fredqian | 0:5f1d66c85ae0 | 388 | if (rxRingBufferHead == rxRingBufferTail) |
fredqian | 0:5f1d66c85ae0 | 389 | { |
fredqian | 0:5f1d66c85ae0 | 390 | return -1; |
fredqian | 0:5f1d66c85ae0 | 391 | } |
fredqian | 0:5f1d66c85ae0 | 392 | else |
fredqian | 0:5f1d66c85ae0 | 393 | { |
fredqian | 0:5f1d66c85ae0 | 394 | uint8_t c = rxRingBuffer[rxRingBufferTail]; |
fredqian | 0:5f1d66c85ae0 | 395 | return c; |
fredqian | 0:5f1d66c85ae0 | 396 | } |
fredqian | 0:5f1d66c85ae0 | 397 | } |
fredqian | 0:5f1d66c85ae0 | 398 | |
fredqian | 0:5f1d66c85ae0 | 399 | /** |
fredqian | 0:5f1d66c85ae0 | 400 | * @brief RX Buffer Size |
fredqian | 0:5f1d66c85ae0 | 401 | * |
fredqian | 0:5f1d66c85ae0 | 402 | * Shows how many bytes are in the RX FIFO ring buffer |
fredqian | 0:5f1d66c85ae0 | 403 | * |
fredqian | 0:5f1d66c85ae0 | 404 | * @return how many bytes are in the RX FIFO ring buffer |
fredqian | 0:5f1d66c85ae0 | 405 | */ |
fredqian | 0:5f1d66c85ae0 | 406 | int8_t cMxRadio::available() |
fredqian | 0:5f1d66c85ae0 | 407 | { |
fredqian | 0:5f1d66c85ae0 | 408 | return ((int16_t)((int16_t)ZR_FIFO_SIZE + (int16_t)rxRingBufferHead - (int16_t)rxRingBufferTail)) % ZR_FIFO_SIZE; |
fredqian | 0:5f1d66c85ae0 | 409 | } |
fredqian | 0:5f1d66c85ae0 | 410 | |
fredqian | 0:5f1d66c85ae0 | 411 | /** |
fredqian | 0:5f1d66c85ae0 | 412 | * @brief Raw Frame Transmit |
fredqian | 0:5f1d66c85ae0 | 413 | * |
fredqian | 0:5f1d66c85ae0 | 414 | * Transmits a frame |
fredqian | 0:5f1d66c85ae0 | 415 | * Warning: no frame header or FCS is added |
fredqian | 0:5f1d66c85ae0 | 416 | * |
fredqian | 0:5f1d66c85ae0 | 417 | * @param frm array containing frame data |
fredqian | 0:5f1d66c85ae0 | 418 | * @param len length of the frame |
fredqian | 0:5f1d66c85ae0 | 419 | */ |
fredqian | 0:5f1d66c85ae0 | 420 | void cMxRadio::txFrame(uint8_t* frm, uint8_t len) |
fredqian | 0:5f1d66c85ae0 | 421 | { |
fredqian | 0:5f1d66c85ae0 | 422 | #ifdef ZR_TXWAIT_BEFORE |
fredqian | 0:5f1d66c85ae0 | 423 | waitTxDone(0xFFFF); |
fredqian | 0:5f1d66c85ae0 | 424 | #endif |
fredqian | 0:5f1d66c85ae0 | 425 | txIsBusy = 1; |
fredqian | 0:5f1d66c85ae0 | 426 | if (setautotx) |
fredqian | 0:5f1d66c85ae0 | 427 | radio_set_state(STATE_TXAUTO); |
fredqian | 0:5f1d66c85ae0 | 428 | else |
fredqian | 0:5f1d66c85ae0 | 429 | radio_set_state(STATE_TX); |
fredqian | 0:5f1d66c85ae0 | 430 | ZR_RFTX_LED_ON(); |
fredqian | 0:5f1d66c85ae0 | 431 | radio_send_frame(len, frm, 0); |
fredqian | 0:5f1d66c85ae0 | 432 | #ifdef ZR_TXWAIT_AFTER |
fredqian | 0:5f1d66c85ae0 | 433 | waitTxDone(0xFFFF); |
fredqian | 0:5f1d66c85ae0 | 434 | if (setautorx) |
fredqian | 0:5f1d66c85ae0 | 435 | radio_set_state(STATE_RXAUTO); |
fredqian | 0:5f1d66c85ae0 | 436 | else |
fredqian | 0:5f1d66c85ae0 | 437 | radio_set_state(STATE_RX); |
fredqian | 0:5f1d66c85ae0 | 438 | txIsBusy = 0; |
fredqian | 0:5f1d66c85ae0 | 439 | #endif |
fredqian | 0:5f1d66c85ae0 | 440 | } |
fredqian | 0:5f1d66c85ae0 | 441 | |
fredqian | 0:5f1d66c85ae0 | 442 | /** |
fredqian | 0:5f1d66c85ae0 | 443 | * @brief Prepare for Trasmission |
fredqian | 0:5f1d66c85ae0 | 444 | * |
fredqian | 0:5f1d66c85ae0 | 445 | * Goes into non-immediate transmit mode, resets the transmit payload |
fredqian | 0:5f1d66c85ae0 | 446 | * Non-immediate mode sends multiple bytes per frame |
fredqian | 0:5f1d66c85ae0 | 447 | * |
fredqian | 0:5f1d66c85ae0 | 448 | */ |
fredqian | 0:5f1d66c85ae0 | 449 | void cMxRadio::beginTransmission() |
fredqian | 0:5f1d66c85ae0 | 450 | { |
fredqian | 0:5f1d66c85ae0 | 451 | usedBeginTransmission = 1; |
fredqian | 0:5f1d66c85ae0 | 452 | txTmpBuffer[5]= 0xFF; |
fredqian | 0:5f1d66c85ae0 | 453 | txTmpBuffer[6]= 0XFF; |
fredqian | 0:5f1d66c85ae0 | 454 | // add frame header |
fredqian | 0:5f1d66c85ae0 | 455 | txTmpBufferLength = HeadSize; |
fredqian | 0:5f1d66c85ae0 | 456 | } |
fredqian | 0:5f1d66c85ae0 | 457 | |
fredqian | 0:5f1d66c85ae0 | 458 | void cMxRadio::beginTransmission(uint16_t destaddress) |
fredqian | 0:5f1d66c85ae0 | 459 | { |
fredqian | 0:5f1d66c85ae0 | 460 | txTmpBuffer[5]=(uint8_t)(destaddress & 0xFF ); |
fredqian | 0:5f1d66c85ae0 | 461 | txTmpBuffer[6]=(uint8_t)((destaddress>>8) & 0xFF ); |
fredqian | 0:5f1d66c85ae0 | 462 | usedBeginTransmission = 1; |
fredqian | 0:5f1d66c85ae0 | 463 | |
fredqian | 0:5f1d66c85ae0 | 464 | // add frame header |
fredqian | 0:5f1d66c85ae0 | 465 | txTmpBufferLength = HeadSize; |
fredqian | 0:5f1d66c85ae0 | 466 | } |
fredqian | 0:5f1d66c85ae0 | 467 | |
fredqian | 0:5f1d66c85ae0 | 468 | /** |
fredqian | 0:5f1d66c85ae0 | 469 | * @brief Finalize Trasmission |
fredqian | 0:5f1d66c85ae0 | 470 | * |
fredqian | 0:5f1d66c85ae0 | 471 | * Finalize the payload and transmits it when ready |
fredqian | 0:5f1d66c85ae0 | 472 | * |
fredqian | 0:5f1d66c85ae0 | 473 | */ |
fredqian | 0:5f1d66c85ae0 | 474 | void cMxRadio::endTransmission() |
fredqian | 0:5f1d66c85ae0 | 475 | { |
fredqian | 0:5f1d66c85ae0 | 476 | usedBeginTransmission = 0; |
fredqian | 0:5f1d66c85ae0 | 477 | |
fredqian | 0:5f1d66c85ae0 | 478 | // empty FCS field |
fredqian | 0:5f1d66c85ae0 | 479 | txTmpBufferLength += 2; |
fredqian | 0:5f1d66c85ae0 | 480 | |
fredqian | 0:5f1d66c85ae0 | 481 | #ifdef ZR_TXWAIT_BEFORE |
fredqian | 0:5f1d66c85ae0 | 482 | waitTxDone(0xFFFF); |
fredqian | 0:5f1d66c85ae0 | 483 | #endif |
fredqian | 0:5f1d66c85ae0 | 484 | txIsBusy = 1; |
fredqian | 0:5f1d66c85ae0 | 485 | if (setautotx) |
fredqian | 0:5f1d66c85ae0 | 486 | radio_set_state(STATE_TXAUTO); |
fredqian | 0:5f1d66c85ae0 | 487 | else |
fredqian | 0:5f1d66c85ae0 | 488 | radio_set_state(STATE_TX); |
fredqian | 0:5f1d66c85ae0 | 489 | ZR_RFTX_LED_ON(); |
fredqian | 0:5f1d66c85ae0 | 490 | //if broadcase ,cant need ack |
fredqian | 0:5f1d66c85ae0 | 491 | if(txTmpBuffer[5]==0xff && txTmpBuffer[5]==0xff) |
fredqian | 0:5f1d66c85ae0 | 492 | txTmpBuffer[0]=txTmpBuffer[0]&0xdf; |
fredqian | 0:5f1d66c85ae0 | 493 | else |
fredqian | 0:5f1d66c85ae0 | 494 | { |
fredqian | 0:5f1d66c85ae0 | 495 | if(!needack) |
fredqian | 0:5f1d66c85ae0 | 496 | txTmpBuffer[0] = 0x41; |
fredqian | 0:5f1d66c85ae0 | 497 | else |
fredqian | 0:5f1d66c85ae0 | 498 | txTmpBuffer[0] = 0x61; //ack required |
fredqian | 0:5f1d66c85ae0 | 499 | } |
fredqian | 0:5f1d66c85ae0 | 500 | radio_send_frame(txTmpBufferLength, txTmpBuffer, 0); |
fredqian | 0:5f1d66c85ae0 | 501 | #ifdef ZR_TXWAIT_AFTER |
fredqian | 0:5f1d66c85ae0 | 502 | waitTxDone(0xFFFF); |
fredqian | 0:5f1d66c85ae0 | 503 | if (setautorx) |
fredqian | 0:5f1d66c85ae0 | 504 | radio_set_state(STATE_RXAUTO); |
fredqian | 0:5f1d66c85ae0 | 505 | else |
fredqian | 0:5f1d66c85ae0 | 506 | radio_set_state(STATE_RX); |
fredqian | 0:5f1d66c85ae0 | 507 | txIsBusy = 0; |
fredqian | 0:5f1d66c85ae0 | 508 | #endif |
fredqian | 0:5f1d66c85ae0 | 509 | } |
fredqian | 0:5f1d66c85ae0 | 510 | |
fredqian | 0:5f1d66c85ae0 | 511 | /** |
fredqian | 0:5f1d66c85ae0 | 512 | * @brief Cancel Trasmission |
fredqian | 0:5f1d66c85ae0 | 513 | * |
fredqian | 0:5f1d66c85ae0 | 514 | * Clears payload buffer |
fredqian | 0:5f1d66c85ae0 | 515 | * |
fredqian | 0:5f1d66c85ae0 | 516 | * Warning: does not actually cancel a transmission in progress |
fredqian | 0:5f1d66c85ae0 | 517 | * |
fredqian | 0:5f1d66c85ae0 | 518 | */ |
fredqian | 0:5f1d66c85ae0 | 519 | void cMxRadio::cancelTransmission() |
fredqian | 0:5f1d66c85ae0 | 520 | { |
fredqian | 0:5f1d66c85ae0 | 521 | usedBeginTransmission = 0; |
fredqian | 0:5f1d66c85ae0 | 522 | |
fredqian | 0:5f1d66c85ae0 | 523 | // add frame header |
fredqian | 0:5f1d66c85ae0 | 524 | txTmpBufferLength = HeadSize; |
fredqian | 0:5f1d66c85ae0 | 525 | } |
fredqian | 0:5f1d66c85ae0 | 526 | /** |
fredqian | 0:5f1d66c85ae0 | 527 | * @brief TX a Byte |
fredqian | 0:5f1d66c85ae0 | 528 | * |
fredqian | 0:5f1d66c85ae0 | 529 | * Wrapper for "write", since the "Wire" library uses "send" |
fredqian | 0:5f1d66c85ae0 | 530 | * |
fredqian | 0:5f1d66c85ae0 | 531 | */ |
fredqian | 0:5f1d66c85ae0 | 532 | void cMxRadio::send(uint8_t c) |
fredqian | 0:5f1d66c85ae0 | 533 | { |
fredqian | 0:5f1d66c85ae0 | 534 | write(c); |
fredqian | 0:5f1d66c85ae0 | 535 | } |
fredqian | 0:5f1d66c85ae0 | 536 | |
fredqian | 0:5f1d66c85ae0 | 537 | /** |
fredqian | 0:5f1d66c85ae0 | 538 | * @brief TX a Byte |
fredqian | 0:5f1d66c85ae0 | 539 | * |
fredqian | 0:5f1d66c85ae0 | 540 | * If "beginTrasmission" was used, then it writes into the transmit buffer for non-immediate mode |
fredqian | 0:5f1d66c85ae0 | 541 | * If "beginTrasmission" was not used, then it transmits the single byte immediately (slower for multiple bytes) |
fredqian | 0:5f1d66c85ae0 | 542 | * |
fredqian | 0:5f1d66c85ae0 | 543 | * @param c character to be sent |
fredqian | 0:5f1d66c85ae0 | 544 | */ |
fredqian | 0:5f1d66c85ae0 | 545 | void cMxRadio::write(uint8_t c) |
fredqian | 0:5f1d66c85ae0 | 546 | { |
fredqian | 0:5f1d66c85ae0 | 547 | if (usedBeginTransmission) |
fredqian | 0:5f1d66c85ae0 | 548 | { |
fredqian | 0:5f1d66c85ae0 | 549 | if (txTmpBufferLength < ZR_TXTMPBUFF_SIZE - 2) |
fredqian | 0:5f1d66c85ae0 | 550 | { |
fredqian | 0:5f1d66c85ae0 | 551 | txTmpBuffer[txTmpBufferLength] = c; |
fredqian | 0:5f1d66c85ae0 | 552 | txTmpBufferLength++; |
fredqian | 0:5f1d66c85ae0 | 553 | |
fredqian | 0:5f1d66c85ae0 | 554 | if (txTmpBufferLength >= ZR_TXTMPBUFF_SIZE - 2) |
fredqian | 0:5f1d66c85ae0 | 555 | { |
fredqian | 0:5f1d66c85ae0 | 556 | // buffer is now full |
fredqian | 0:5f1d66c85ae0 | 557 | // just send it all out so we have more room |
fredqian | 0:5f1d66c85ae0 | 558 | endTransmission(); |
fredqian | 0:5f1d66c85ae0 | 559 | beginTransmission(); |
fredqian | 0:5f1d66c85ae0 | 560 | } |
fredqian | 0:5f1d66c85ae0 | 561 | } |
fredqian | 0:5f1d66c85ae0 | 562 | } |
fredqian | 0:5f1d66c85ae0 | 563 | else |
fredqian | 0:5f1d66c85ae0 | 564 | { |
fredqian | 0:5f1d66c85ae0 | 565 | txTmpBuffer[HeadSize] = c; // set payload |
fredqian | 0:5f1d66c85ae0 | 566 | txTmpBuffer[HeadSize+1] = 0; // empty FCS |
fredqian | 0:5f1d66c85ae0 | 567 | txTmpBuffer[HeadSize+2] = 0; // empty FCS |
fredqian | 0:5f1d66c85ae0 | 568 | |
fredqian | 0:5f1d66c85ae0 | 569 | #ifdef ZR_TXWAIT_BEFORE |
fredqian | 0:5f1d66c85ae0 | 570 | waitTxDone(0xFFFF); |
fredqian | 0:5f1d66c85ae0 | 571 | #endif |
fredqian | 0:5f1d66c85ae0 | 572 | txIsBusy = 1; |
fredqian | 0:5f1d66c85ae0 | 573 | if (setautotx) |
fredqian | 0:5f1d66c85ae0 | 574 | radio_set_state(STATE_TXAUTO); |
fredqian | 0:5f1d66c85ae0 | 575 | else |
fredqian | 0:5f1d66c85ae0 | 576 | radio_set_state(STATE_TX); |
fredqian | 0:5f1d66c85ae0 | 577 | ZR_RFTX_LED_ON(); |
fredqian | 0:5f1d66c85ae0 | 578 | radio_send_frame(10, txTmpBuffer, 0); |
fredqian | 0:5f1d66c85ae0 | 579 | #ifdef ZR_TXWAIT_AFTER |
fredqian | 0:5f1d66c85ae0 | 580 | waitTxDone(0xFFFF); |
fredqian | 0:5f1d66c85ae0 | 581 | if (setautorx) |
fredqian | 0:5f1d66c85ae0 | 582 | radio_set_state(STATE_RXAUTO); |
fredqian | 0:5f1d66c85ae0 | 583 | else |
fredqian | 0:5f1d66c85ae0 | 584 | radio_set_state(STATE_RX); |
fredqian | 0:5f1d66c85ae0 | 585 | txIsBusy = 0; |
fredqian | 0:5f1d66c85ae0 | 586 | #endif |
fredqian | 0:5f1d66c85ae0 | 587 | } |
fredqian | 0:5f1d66c85ae0 | 588 | } |
fredqian | 0:5f1d66c85ae0 | 589 | /** |
fredqian | 0:5f1d66c85ae0 | 590 | * @brief TX a String |
fredqian | 0:5f1d66c85ae0 | 591 | * |
fredqian | 0:5f1d66c85ae0 | 592 | * A overload for "write" that sends a null-terminated string |
fredqian | 0:5f1d66c85ae0 | 593 | * |
fredqian | 0:5f1d66c85ae0 | 594 | * @param str null-terminated string to be sent |
fredqian | 0:5f1d66c85ae0 | 595 | */ |
fredqian | 0:5f1d66c85ae0 | 596 | void cMxRadio::write(char* str) |
fredqian | 0:5f1d66c85ae0 | 597 | { |
fredqian | 0:5f1d66c85ae0 | 598 | while (*str) |
fredqian | 0:5f1d66c85ae0 | 599 | write(*str++); |
fredqian | 0:5f1d66c85ae0 | 600 | } |
fredqian | 0:5f1d66c85ae0 | 601 | |
fredqian | 0:5f1d66c85ae0 | 602 | /** |
fredqian | 0:5f1d66c85ae0 | 603 | * @brief TX an Array |
fredqian | 0:5f1d66c85ae0 | 604 | * |
fredqian | 0:5f1d66c85ae0 | 605 | * A overload for "write" that sends an array |
fredqian | 0:5f1d66c85ae0 | 606 | * |
fredqian | 0:5f1d66c85ae0 | 607 | * @param arr data array to be sent |
fredqian | 0:5f1d66c85ae0 | 608 | * @param len length of data array |
fredqian | 0:5f1d66c85ae0 | 609 | */ |
fredqian | 0:5f1d66c85ae0 | 610 | void cMxRadio::write(uint8_t* arr, uint8_t len) |
fredqian | 0:5f1d66c85ae0 | 611 | { |
fredqian | 0:5f1d66c85ae0 | 612 | uint8_t i; |
fredqian | 0:5f1d66c85ae0 | 613 | for (i = 0; i < len; i++) |
fredqian | 0:5f1d66c85ae0 | 614 | write(arr[i]); |
fredqian | 0:5f1d66c85ae0 | 615 | } |
fredqian | 0:5f1d66c85ae0 | 616 | |
fredqian | 0:5f1d66c85ae0 | 617 | /** |
fredqian | 0:5f1d66c85ae0 | 618 | * @brief Default TX Complete Event Handler |
fredqian | 0:5f1d66c85ae0 | 619 | * |
fredqian | 0:5f1d66c85ae0 | 620 | * Calls the user event function if one is attached |
fredqian | 0:5f1d66c85ae0 | 621 | * Clear the TX busy status flag |
fredqian | 0:5f1d66c85ae0 | 622 | * Defaults back to RX state |
fredqian | 0:5f1d66c85ae0 | 623 | * |
fredqian | 0:5f1d66c85ae0 | 624 | * this should not be called by the user |
fredqian | 0:5f1d66c85ae0 | 625 | * |
fredqian | 0:5f1d66c85ae0 | 626 | * @param x one of the radio_tx_done_t enumerations indicating transmission success |
fredqian | 0:5f1d66c85ae0 | 627 | */ |
fredqian | 0:5f1d66c85ae0 | 628 | void cMxRadio::onTxDone(radio_tx_done_t x) |
fredqian | 0:5f1d66c85ae0 | 629 | { |
fredqian | 0:5f1d66c85ae0 | 630 | if (hasAttachedTxEvent) |
fredqian | 0:5f1d66c85ae0 | 631 | { |
fredqian | 0:5f1d66c85ae0 | 632 | zrEventTxDone(x); |
fredqian | 0:5f1d66c85ae0 | 633 | } |
fredqian | 0:5f1d66c85ae0 | 634 | |
fredqian | 0:5f1d66c85ae0 | 635 | txIsBusy = 0; |
fredqian | 0:5f1d66c85ae0 | 636 | } |
fredqian | 0:5f1d66c85ae0 | 637 | |
fredqian | 0:5f1d66c85ae0 | 638 | /** |
fredqian | 0:5f1d66c85ae0 | 639 | * @brief radio_set_param Wrapper |
fredqian | 0:5f1d66c85ae0 | 640 | * |
fredqian | 0:5f1d66c85ae0 | 641 | * set a radio parameter |
fredqian | 0:5f1d66c85ae0 | 642 | * |
fredqian | 0:5f1d66c85ae0 | 643 | * see radio.h for more info |
fredqian | 0:5f1d66c85ae0 | 644 | */ |
fredqian | 0:5f1d66c85ae0 | 645 | void cMxRadio::setParam(radio_attribute_t attr, radio_param_t parm) |
fredqian | 0:5f1d66c85ae0 | 646 | { |
fredqian | 0:5f1d66c85ae0 | 647 | radio_set_param(attr, parm); |
fredqian | 0:5f1d66c85ae0 | 648 | } |
fredqian | 0:5f1d66c85ae0 | 649 | |
fredqian | 0:5f1d66c85ae0 | 650 | /** |
fredqian | 0:5f1d66c85ae0 | 651 | * @brief radio_do_cca Wrapper |
fredqian | 0:5f1d66c85ae0 | 652 | * |
fredqian | 0:5f1d66c85ae0 | 653 | * perform CCA measure |
fredqian | 0:5f1d66c85ae0 | 654 | * |
fredqian | 0:5f1d66c85ae0 | 655 | * see radio.h for more info |
fredqian | 0:5f1d66c85ae0 | 656 | */ |
fredqian | 0:5f1d66c85ae0 | 657 | radio_cca_t cMxRadio::doCca() |
fredqian | 0:5f1d66c85ae0 | 658 | { |
fredqian | 0:5f1d66c85ae0 | 659 | return radio_do_cca(); |
fredqian | 0:5f1d66c85ae0 | 660 | } |
fredqian | 0:5f1d66c85ae0 | 661 | |
fredqian | 0:5f1d66c85ae0 | 662 | /** |
fredqian | 0:5f1d66c85ae0 | 663 | * @brief Set Radio State Wrapper |
fredqian | 0:5f1d66c85ae0 | 664 | * |
fredqian | 0:5f1d66c85ae0 | 665 | * sets or forces the radio into a state |
fredqian | 0:5f1d66c85ae0 | 666 | * |
fredqian | 0:5f1d66c85ae0 | 667 | * see radio.h for more info |
fredqian | 0:5f1d66c85ae0 | 668 | * |
fredqian | 0:5f1d66c85ae0 | 669 | * @param state requested radio state |
fredqian | 0:5f1d66c85ae0 | 670 | * @param force boolean indicating to force the state |
fredqian | 0:5f1d66c85ae0 | 671 | */ |
fredqian | 0:5f1d66c85ae0 | 672 | void cMxRadio::setState(radio_state_t state, uint8_t force) |
fredqian | 0:5f1d66c85ae0 | 673 | { |
fredqian | 0:5f1d66c85ae0 | 674 | if (force) |
fredqian | 0:5f1d66c85ae0 | 675 | radio_force_state(state); |
fredqian | 0:5f1d66c85ae0 | 676 | else |
fredqian | 0:5f1d66c85ae0 | 677 | radio_set_state(state); |
fredqian | 0:5f1d66c85ae0 | 678 | } |
fredqian | 0:5f1d66c85ae0 | 679 | |
fredqian | 0:5f1d66c85ae0 | 680 | /** |
fredqian | 0:5f1d66c85ae0 | 681 | * @brief radio_set_state Wrapper |
fredqian | 0:5f1d66c85ae0 | 682 | * |
fredqian | 0:5f1d66c85ae0 | 683 | * bring the the radio in the requested state |
fredqian | 0:5f1d66c85ae0 | 684 | * |
fredqian | 0:5f1d66c85ae0 | 685 | * see radio.h for more info |
fredqian | 0:5f1d66c85ae0 | 686 | */ |
fredqian | 0:5f1d66c85ae0 | 687 | void cMxRadio::setState(radio_state_t state) |
fredqian | 0:5f1d66c85ae0 | 688 | { |
fredqian | 0:5f1d66c85ae0 | 689 | radio_set_state(state); |
fredqian | 0:5f1d66c85ae0 | 690 | } |
fredqian | 0:5f1d66c85ae0 | 691 | |
fredqian | 0:5f1d66c85ae0 | 692 | /** |
fredqian | 0:5f1d66c85ae0 | 693 | * @brief radio_set_state to STATE_RX |
fredqian | 0:5f1d66c85ae0 | 694 | * |
fredqian | 0:5f1d66c85ae0 | 695 | * bring the the radio in the requested state of STATE_RX |
fredqian | 0:5f1d66c85ae0 | 696 | * |
fredqian | 0:5f1d66c85ae0 | 697 | * the radio state does not return to STATE_RX automatically if ZR_TXWAIT_AFTER is not used |
fredqian | 0:5f1d66c85ae0 | 698 | * thus why this is provided |
fredqian | 0:5f1d66c85ae0 | 699 | * |
fredqian | 0:5f1d66c85ae0 | 700 | * see radio.h for more info |
fredqian | 0:5f1d66c85ae0 | 701 | */ |
fredqian | 0:5f1d66c85ae0 | 702 | void cMxRadio::setStateRx() |
fredqian | 0:5f1d66c85ae0 | 703 | { |
fredqian | 0:5f1d66c85ae0 | 704 | if (setautorx) |
fredqian | 0:5f1d66c85ae0 | 705 | radio_set_state(STATE_RXAUTO); |
fredqian | 0:5f1d66c85ae0 | 706 | else |
fredqian | 0:5f1d66c85ae0 | 707 | radio_set_state(STATE_RX); |
fredqian | 0:5f1d66c85ae0 | 708 | } |
fredqian | 0:5f1d66c85ae0 | 709 | |
fredqian | 0:5f1d66c85ae0 | 710 | /** |
fredqian | 0:5f1d66c85ae0 | 711 | * @brief radio_force_state Wrapper |
fredqian | 0:5f1d66c85ae0 | 712 | * |
fredqian | 0:5f1d66c85ae0 | 713 | * force the radio to the requested state |
fredqian | 0:5f1d66c85ae0 | 714 | * |
fredqian | 0:5f1d66c85ae0 | 715 | * see radio.h for more info |
fredqian | 0:5f1d66c85ae0 | 716 | */ |
fredqian | 0:5f1d66c85ae0 | 717 | void cMxRadio::forceState(radio_state_t state) |
fredqian | 0:5f1d66c85ae0 | 718 | { |
fredqian | 0:5f1d66c85ae0 | 719 | radio_force_state(state); |
fredqian | 0:5f1d66c85ae0 | 720 | } |
fredqian | 0:5f1d66c85ae0 | 721 | |
fredqian | 0:5f1d66c85ae0 | 722 | /** |
fredqian | 0:5f1d66c85ae0 | 723 | * @brief Sets Radio Channel |
fredqian | 0:5f1d66c85ae0 | 724 | * |
fredqian | 0:5f1d66c85ae0 | 725 | * changes the radio channel by setting the radio channel state |
fredqian | 0:5f1d66c85ae0 | 726 | * |
fredqian | 0:5f1d66c85ae0 | 727 | * @param chan channel number, 11 to 26 |
fredqian | 0:5f1d66c85ae0 | 728 | */ |
fredqian | 0:5f1d66c85ae0 | 729 | void cMxRadio::setChannel(channel_t chan) |
fredqian | 0:5f1d66c85ae0 | 730 | { |
fredqian | 0:5f1d66c85ae0 | 731 | radio_set_param(RP_CHANNEL(chan)); |
fredqian | 0:5f1d66c85ae0 | 732 | } |
fredqian | 0:5f1d66c85ae0 | 733 | uint8_t cMxRadio::getChannel() |
fredqian | 0:5f1d66c85ae0 | 734 | { |
fredqian | 0:5f1d66c85ae0 | 735 | trx_param_t p; |
fredqian | 0:5f1d66c85ae0 | 736 | trx_parms_get(&p); |
fredqian | 0:5f1d66c85ae0 | 737 | channel_t chan= p.chan; |
fredqian | 0:5f1d66c85ae0 | 738 | return (uint8_t)chan; |
fredqian | 0:5f1d66c85ae0 | 739 | } |
fredqian | 0:5f1d66c85ae0 | 740 | |
fredqian | 0:5f1d66c85ae0 | 741 | |
fredqian | 0:5f1d66c85ae0 | 742 | /** |
fredqian | 0:5f1d66c85ae0 | 743 | * @brief Read Receiver Signal Strength Indicator Now |
fredqian | 0:5f1d66c85ae0 | 744 | * |
fredqian | 0:5f1d66c85ae0 | 745 | * returns the current RSSI |
fredqian | 0:5f1d66c85ae0 | 746 | * |
fredqian | 0:5f1d66c85ae0 | 747 | * range is between -91 and -9 dBm |
fredqian | 0:5f1d66c85ae0 | 748 | * where -9 is the best |
fredqian | 0:5f1d66c85ae0 | 749 | * |
fredqian | 0:5f1d66c85ae0 | 750 | * @return RSSI of the last transmission received |
fredqian | 0:5f1d66c85ae0 | 751 | */ |
fredqian | 0:5f1d66c85ae0 | 752 | int8_t cMxRadio::getRssiNow() |
fredqian | 0:5f1d66c85ae0 | 753 | { |
fredqian | 0:5f1d66c85ae0 | 754 | int16_t rssi = ((int16_t)(trx_reg_read(RG_PHY_RSSI) & 0x1F)); // mask only important bits |
fredqian | 0:5f1d66c85ae0 | 755 | rssi = (rssi == 0) ? (RSSI_BASE_VAL - 1) : ((rssi < 28) ? ((rssi - 1) * 3 + RSSI_BASE_VAL) : -9); |
fredqian | 0:5f1d66c85ae0 | 756 | // if 0, then rssi < RSSI_BASE_VAL, if 28, then >= -10 |
fredqian | 0:5f1d66c85ae0 | 757 | |
fredqian | 0:5f1d66c85ae0 | 758 | return rssi; |
fredqian | 0:5f1d66c85ae0 | 759 | } |
fredqian | 0:5f1d66c85ae0 | 760 | |
fredqian | 0:5f1d66c85ae0 | 761 | |
fredqian | 0:5f1d66c85ae0 | 762 | |
fredqian | 0:5f1d66c85ae0 | 763 | /** |
fredqian | 0:5f1d66c85ae0 | 764 | * @brief Read Last Receiver Signal Strength Indicator |
fredqian | 0:5f1d66c85ae0 | 765 | * |
fredqian | 0:5f1d66c85ae0 | 766 | * returns the RSSI of the last transmission |
fredqian | 0:5f1d66c85ae0 | 767 | * |
fredqian | 0:5f1d66c85ae0 | 768 | * range is between -91 and -9 dBm |
fredqian | 0:5f1d66c85ae0 | 769 | * where -9 is the best |
fredqian | 0:5f1d66c85ae0 | 770 | * |
fredqian | 0:5f1d66c85ae0 | 771 | * @return RSSI of the last transmission received |
fredqian | 0:5f1d66c85ae0 | 772 | */ |
fredqian | 0:5f1d66c85ae0 | 773 | int8_t cMxRadio::getLastRssi() |
fredqian | 0:5f1d66c85ae0 | 774 | { |
fredqian | 0:5f1d66c85ae0 | 775 | int16_t rssi = ((int16_t)(temprssi & 0x1F)); // mask only important bits |
fredqian | 0:5f1d66c85ae0 | 776 | rssi = (rssi == 0) ? (RSSI_BASE_VAL - 1) : ((rssi < 28) ? ((rssi - 1) * 3 + RSSI_BASE_VAL) : -9); |
fredqian | 0:5f1d66c85ae0 | 777 | // if 0, then rssi < RSSI_BASE_VAL, if 28, then >= -10 |
fredqian | 0:5f1d66c85ae0 | 778 | |
fredqian | 0:5f1d66c85ae0 | 779 | return rssi; |
fredqian | 0:5f1d66c85ae0 | 780 | } |
fredqian | 0:5f1d66c85ae0 | 781 | |
fredqian | 0:5f1d66c85ae0 | 782 | |
fredqian | 0:5f1d66c85ae0 | 783 | /** |
fredqian | 0:5f1d66c85ae0 | 784 | * @brief Read Link Quality Indicator |
fredqian | 0:5f1d66c85ae0 | 785 | * |
fredqian | 0:5f1d66c85ae0 | 786 | * returns the LQI of the last transmission received |
fredqian | 0:5f1d66c85ae0 | 787 | * |
fredqian | 0:5f1d66c85ae0 | 788 | * range is from 0 to 255 |
fredqian | 0:5f1d66c85ae0 | 789 | * 255 is the best |
fredqian | 0:5f1d66c85ae0 | 790 | * |
fredqian | 0:5f1d66c85ae0 | 791 | * @return LQI of the last transmission received |
fredqian | 0:5f1d66c85ae0 | 792 | */ |
fredqian | 0:5f1d66c85ae0 | 793 | uint8_t cMxRadio::getLqi() |
fredqian | 0:5f1d66c85ae0 | 794 | { |
fredqian | 0:5f1d66c85ae0 | 795 | return lastLqi; |
fredqian | 0:5f1d66c85ae0 | 796 | } |
fredqian | 0:5f1d66c85ae0 | 797 | |
fredqian | 0:5f1d66c85ae0 | 798 | /** |
fredqian | 0:5f1d66c85ae0 | 799 | * @brief Read Last Energy Detection Level |
fredqian | 0:5f1d66c85ae0 | 800 | * |
fredqian | 0:5f1d66c85ae0 | 801 | * returns the ED level of the last transmission received |
fredqian | 0:5f1d66c85ae0 | 802 | * |
fredqian | 0:5f1d66c85ae0 | 803 | * range is between -90 and -6 dBm |
fredqian | 0:5f1d66c85ae0 | 804 | * where -6 is the best |
fredqian | 0:5f1d66c85ae0 | 805 | * |
fredqian | 0:5f1d66c85ae0 | 806 | * @return ED level of the last transmission received |
fredqian | 0:5f1d66c85ae0 | 807 | */ |
fredqian | 0:5f1d66c85ae0 | 808 | int8_t cMxRadio::getLastEd() |
fredqian | 0:5f1d66c85ae0 | 809 | { |
fredqian | 0:5f1d66c85ae0 | 810 | int8_t ed = trx_reg_read(RG_PHY_ED_LEVEL); |
fredqian | 0:5f1d66c85ae0 | 811 | |
fredqian | 0:5f1d66c85ae0 | 812 | return ed == 0xFF ? 0 : (ed + RSSI_BASE_VAL); |
fredqian | 0:5f1d66c85ae0 | 813 | //return lastRssi == 0xFF ? 0 : (lastRssi + RSSI_BASE_VAL); |
fredqian | 0:5f1d66c85ae0 | 814 | |
fredqian | 0:5f1d66c85ae0 | 815 | } |
fredqian | 0:5f1d66c85ae0 | 816 | |
fredqian | 0:5f1d66c85ae0 | 817 | /** |
fredqian | 0:5f1d66c85ae0 | 818 | * @brief Read Energy Detection Level Now |
fredqian | 0:5f1d66c85ae0 | 819 | * |
fredqian | 0:5f1d66c85ae0 | 820 | * forces a reading of the ED level |
fredqian | 0:5f1d66c85ae0 | 821 | * |
fredqian | 0:5f1d66c85ae0 | 822 | * range is between -90 and -6 dBm |
fredqian | 0:5f1d66c85ae0 | 823 | * where -6 is the best |
fredqian | 0:5f1d66c85ae0 | 824 | * |
fredqian | 0:5f1d66c85ae0 | 825 | * @return ED level |
fredqian | 0:5f1d66c85ae0 | 826 | */ |
fredqian | 0:5f1d66c85ae0 | 827 | int8_t cMxRadio::getEdNow() |
fredqian | 0:5f1d66c85ae0 | 828 | { |
fredqian | 0:5f1d66c85ae0 | 829 | trx_reg_write(RG_PHY_ED_LEVEL, 0); // forces a reading |
fredqian | 0:5f1d66c85ae0 | 830 | |
fredqian | 0:5f1d66c85ae0 | 831 | return getLastEd(); |
fredqian | 0:5f1d66c85ae0 | 832 | } |
fredqian | 0:5f1d66c85ae0 | 833 | |
fredqian | 0:5f1d66c85ae0 | 834 | /** |
fredqian | 0:5f1d66c85ae0 | 835 | * @brief Wait for TX to Complete |
fredqian | 0:5f1d66c85ae0 | 836 | * |
fredqian | 0:5f1d66c85ae0 | 837 | * waits until the last transmission is complete, or timed out |
fredqian | 0:5f1d66c85ae0 | 838 | * |
fredqian | 0:5f1d66c85ae0 | 839 | * @param timeout iterations to countdown before timeout |
fredqian | 0:5f1d66c85ae0 | 840 | */ |
fredqian | 0:5f1d66c85ae0 | 841 | void cMxRadio::waitTxDone(uint16_t timeout) |
fredqian | 0:5f1d66c85ae0 | 842 | { |
fredqian | 0:5f1d66c85ae0 | 843 | volatile uint16_t cnt = timeout; |
fredqian | 0:5f1d66c85ae0 | 844 | while (txIsBusy && cnt--); |
fredqian | 0:5f1d66c85ae0 | 845 | } |
fredqian | 0:5f1d66c85ae0 | 846 | |
fredqian | 0:5f1d66c85ae0 | 847 |