Library for HopeRF RFM22 transceiver module ported to mbed. Original Software from Mike McCauley (mikem@open.com.au) . See http://www.open.com.au/mikem/arduino/RF22/

Fork of RF22 by Karl Zweimüller

Revision:
8:d9e2ca137f2e
Parent:
7:b86825b9d74b
Child:
9:46fb41f4259d
--- a/RF22.cpp	Wed Mar 13 20:37:19 2013 +0000
+++ b/RF22.cpp	Sun Apr 07 11:42:36 2013 +0000
@@ -16,11 +16,10 @@
 //RF22* RF22::_RF22ForInterrupt[2] = {0, 0};
 
 // These are indexed by the values of ModemConfigChoice
-// Canned modem configurations generated with 
+// Canned modem configurations generated with
 // http://www.hoperf.com/upload/rf/RF22B%2023B%2031B%2042B%2043B%20Register%20Settings_RevB1-v5.xls
 // Stored in flash (program) memory to save SRAM
-/*PROGMEM */ static const RF22::ModemConfig MODEM_CONFIG_TABLE[] =
-{
+/*PROGMEM */ static const RF22::ModemConfig MODEM_CONFIG_TABLE[] = {
     { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x00, 0x08 }, // Unmodulated carrier
     { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x33, 0x08 }, // FSK, PN9 random modulation, 2, 5
 
@@ -59,7 +58,7 @@
 };
 
 RF22::RF22(PinName slaveSelectPin, PinName mosi, PinName miso, PinName sclk, PinName interrupt)
-          : _slaveSelectPin(slaveSelectPin),  _spi(mosi, miso, sclk), _interrupt(interrupt), led1(LED1), led2(LED2), led3(LED3), led4(LED4)
+    : _slaveSelectPin(slaveSelectPin),  _spi(mosi, miso, sclk), _interrupt(interrupt), led1(LED1), led2(LED2), led3(LED3), led4(LED4)
 {
 
 
@@ -68,8 +67,8 @@
     _rxGood = 0;
     _rxBad = 0;
     _txGood = 0;
-    
-    
+
+
 }
 
 boolean RF22::init()
@@ -82,9 +81,9 @@
     //pinMode(_slaveSelectPin, OUTPUT);
     //digitalWrite(_slaveSelectPin, HIGH);
     _slaveSelectPin = 1;
-    
+
     wait_ms(100);
-  
+
     // start the SPI library:
     // Note the RF22 wants mode 0, MSB first and default to 1 Mbps
     /*SPI.begin();
@@ -105,27 +104,27 @@
     // This also tests whether we are really connected to a device
     _deviceType = spiRead(RF22_REG_00_DEVICE_TYPE);
     if (   _deviceType != RF22_DEVICE_TYPE_RX_TRX
-        && _deviceType != RF22_DEVICE_TYPE_TX)
-    return false;
+            && _deviceType != RF22_DEVICE_TYPE_TX)
+        return false;
 
     // Set up interrupt handler
 //    if (_interrupt == 0)
 //    {
     //_RF22ForInterrupt[0] = this;
-    //attachInterrupt(0, RF22::isr0, LOW);  
+    //attachInterrupt(0, RF22::isr0, LOW);
     _interrupt.fall(this, &RF22::isr0);
-/*    }
-    else if (_interrupt == 1)
-    {
-    _RF22ForInterrupt[1] = this;
-    attachInterrupt(1, RF22::isr1, LOW);  
-    }
-    else
-    return false;
-*/ 
+    /*    }
+        else if (_interrupt == 1)
+        {
+        _RF22ForInterrupt[1] = this;
+        attachInterrupt(1, RF22::isr1, LOW);
+        }
+        else
+        return false;
+    */
     clearTxBuf();
     clearRxBuf();
-  
+
     // Most of these are the POR default
     spiWrite(RF22_REG_7D_TX_FIFO_CONTROL2, RF22_TXFFAEM_THRESHOLD);
     spiWrite(RF22_REG_7E_RX_FIFO_CONTROL,  RF22_RXFFAFULL_THRESHOLD);
@@ -149,7 +148,7 @@
     setPreambleLength(8);
     uint8_t syncwords[] = { 0x2d, 0xd4 };
     setSyncWords(syncwords, sizeof(syncwords));
-    setPromiscuous(false); 
+    setPromiscuous(false);
     // Check the TO header against RF22_DEFAULT_NODE_ADDRESS
     spiWrite(RF22_REG_3F_CHECK_HEADER3, RF22_DEFAULT_NODE_ADDRESS);
     // Set the default transmit header values
@@ -165,9 +164,15 @@
     spiWrite (RF22_REG_0C_GPIO_CONFIGURATION1, 0x15) ; // RX state
 
     // Enable interrupts
-    spiWrite(RF22_REG_05_INTERRUPT_ENABLE1, RF22_ENTXFFAEM | RF22_ENRXFFAFULL | RF22_ENPKSENT | RF22_ENPKVALID | RF22_ENCRCERROR | RF22_ENFFERR);
+    // this is original from arduion, which crashes on mbed after some hours
+    //see https://groups.google.com/forum/?fromgroups#!topic/rf22-arduino/Ezkw256yQI8
+    //spiWrite(RF22_REG_05_INTERRUPT_ENABLE1, RF22_ENTXFFAEM | RF22_ENRXFFAFULL | RF22_ENPKSENT | RF22_ENPKVALID | RF22_ENCRCERROR | RF22_ENFFERR);
+    //without RF22_ENFFERR it works - Charly
+    spiWrite(RF22_REG_05_INTERRUPT_ENABLE1, RF22_ENTXFFAEM |RF22_ENRXFFAFULL | RF22_ENPKSENT |RF22_ENPKVALID| RF22_ENCRCERROR);
+
     spiWrite(RF22_REG_06_INTERRUPT_ENABLE2, RF22_ENPREAVAL);
 
+
     // Set some defaults. An innocuous ISM frequency, and reasonable pull-in
     setFrequency(434.0, 0.05);
 //    setFrequency(900.0);
@@ -178,10 +183,7 @@
     setTxPower(RF22_TXPOW_8DBM);
 //    setTxPower(RF22_TXPOW_17DBM);
 
-// Set the AFC for receiver to max. 0,1MHz
-// Other AFC-Registers have PowerOnValues which enable AFC
-// RF22_AFC_LIMIT                          0x50   =0,1MHz
-//   spiWrite(RF22_REG_2A_AFC_LIMITER, RF22_AFC_LIMIT);  // POR=0x00 = OFF
+
 
     return true;
 }
@@ -190,9 +192,9 @@
 void RF22::handleInterrupt()
 {
     uint8_t _lastInterruptFlags[2];
-    
-led1 = 1;
-    
+
+    led1 = 1;
+
     // Read the interrupt flags which clears the interrupt
     spiBurstRead(RF22_REG_03_INTERRUPT_STATUS1, _lastInterruptFlags, 2);
 
@@ -203,110 +205,126 @@
     Serial.print(" ");
     Serial.println(_lastInterruptFlags[1], HEX);
     if (_lastInterruptFlags[0] == 0 && _lastInterruptFlags[1] == 0)
-    Serial.println("FUNNY: no interrupt!");
+        Serial.println("FUNNY: no interrupt!");
 #endif
 
 #if 0
     // TESTING: fake an RF22_IFFERROR
     static int counter = 0;
-    if (_lastInterruptFlags[0] & RF22_IPKSENT && counter++ == 10)
-    {
-    _lastInterruptFlags[0] = RF22_IFFERROR;
-    counter = 0;
+    if (_lastInterruptFlags[0] & RF22_IPKSENT && counter++ == 10) {
+        _lastInterruptFlags[0] = RF22_IFFERROR;
+        counter = 0;
     }
 #endif
 
-    if (_lastInterruptFlags[0] & RF22_IFFERROR)
-    {
-//    Serial.println("IFFERROR");  
-    resetFifos(); // Clears the interrupt
-    if (_mode == RF22_MODE_TX)
-        restartTransmit();
-    else if (_mode == RF22_MODE_RX)
-        clearRxBuf();
-    }
-    // Caution, any delay here may cause a FF underflow or overflow
-    if (_lastInterruptFlags[0] & RF22_ITXFFAEM)
-    {
-    // See if more data has to be loaded into the Tx FIFO 
-    sendNextFragment();
-//  Serial.println("ITXFFAEM");  
+
+    if (_lastInterruptFlags[0] & RF22_IFFERROR) {
+//    Serial.println("IFFERROR");
+        led4 = !led4;
+        resetFifos(); // Clears the interrupt
+        if (_mode == RF22_MODE_TX)
+            restartTransmit();
+        else if (_mode == RF22_MODE_RX){
+            clearRxBuf();
+            //stop and start Rx
+            setModeIdle();
+            setModeRx();
+        }
+        // stop handling the remaining interruppts as something went wrong here
+        return;
     }
-    if (_lastInterruptFlags[0] & RF22_IRXFFAFULL)
-    {
-    // Caution, any delay here may cause a FF overflow
-    // Read some data from the Rx FIFO
-    readNextFragment();
-//    Serial.println("IRXFFAFULL"); 
+    
+    // Caution, any delay here may cause a FF underflow or overflow
+    if (_lastInterruptFlags[0] & RF22_ITXFFAEM) {
+        // See if more data has to be loaded into the Tx FIFO
+        //led2 = !led2;
+        sendNextFragment();
+//  Serial.println("ITXFFAEM");
     }
-    if (_lastInterruptFlags[0] & RF22_IEXT)
-    {
-    // This is not enabled by the base code, but users may want to enable it
-    handleExternalInterrupt();
-//    Serial.println("IEXT"); 
-    }
-    if (_lastInterruptFlags[1] & RF22_IWUT)
-    {
-    // This is not enabled by the base code, but users may want to enable it
-    handleWakeupTimerInterrupt();
-//    Serial.println("IWUT"); 
+  
+    if (_lastInterruptFlags[0] & RF22_IRXFFAFULL) {
+        // Caution, any delay here may cause a FF overflow
+        // Read some data from the Rx FIFO
+        //led4 = !led4;
+        readNextFragment();
+//    Serial.println("IRXFFAFULL");
+    }   
+    if (_lastInterruptFlags[0] & RF22_IEXT) {
+        // This is not enabled by the base code, but users may want to enable it
+        //led2 = !led2;
+        handleExternalInterrupt();
+//    Serial.println("IEXT");
     }
-    if (_lastInterruptFlags[0] & RF22_IPKSENT)
-    {
-//  Serial.println("IPKSENT");   
-    _txGood++; 
-    led4 = !led4;
-    // Transmission does not automatically clear the tx buffer.
-    // Could retransmit if we wanted
-    // RF22 transitions automatically to Idle
-    _mode = RF22_MODE_IDLE;
+    if (_lastInterruptFlags[1] & RF22_IWUT) {
+        // This is not enabled by the base code, but users may want to enable it
+        //led2 = !led2;
+        handleWakeupTimerInterrupt();
+//    Serial.println("IWUT");
+    }    
+    if (_lastInterruptFlags[0] & RF22_IPKSENT) {
+//  Serial.println("IPKSENT");
+        _txGood++;
+        //led4 = !led4;
+        // Transmission does not automatically clear the tx buffer.
+        // Could retransmit if we wanted
+        // RF22 transitions automatically to Idle
+        _mode = RF22_MODE_IDLE;
     }
-    if (_lastInterruptFlags[0] & RF22_IPKVALID)
-    {
-    uint8_t len = spiRead(RF22_REG_4B_RECEIVED_PACKET_LENGTH);
-//  Serial.println("IPKVALID");   
-//  Serial.println(len);   
-//  Serial.println(_bufLen);   
+   
+    if (_lastInterruptFlags[0] & RF22_IPKVALID) {
+        uint8_t len = spiRead(RF22_REG_4B_RECEIVED_PACKET_LENGTH);
+//  Serial.println("IPKVALID");
+//  Serial.println(len);
+//  Serial.println(_bufLen);
 
-    // May have already read one or more fragments
-    // Get any remaining unread octets, based on the expected length
-    // First make sure we dont overflow the buffer in the case of a stupid length
-    // or partial bad receives
-    if (   len >  RF22_MAX_MESSAGE_LEN
-        || len < _bufLen)
-    {
-        _rxBad++;
+        // May have already read one or more fragments
+        // Get any remaining unread octets, based on the expected length
+        // First make sure we dont overflow the buffer in the case of a stupid length
+        // or partial bad receives
+
+        if (   len >  RF22_MAX_MESSAGE_LEN
+                || len < _bufLen) {
+            _rxBad++;
+            led2 = !led2;
+            _mode = RF22_MODE_IDLE;
+            clearRxBuf();
+            return; // Hmmm receiver buffer overflow.
+        }
+
+        spiBurstRead(RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, len - _bufLen);
+        //__disable_irq();    // Disable Interrupts
+        _rxGood++;
+        _bufLen = len;
         _mode = RF22_MODE_IDLE;
-        clearRxBuf();
-        return; // Hmmm receiver buffer overflow. 
-    }
+        _rxBufValid = true;
+        // reset the fifo for next packet??
+        //resetRxFifo();
+        //__enable_irq();     // Enable Interrupts
 
-    spiBurstRead(RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, len - _bufLen);
-    __disable_irq();    // Disable Interrupts
-    _rxGood++;
-    _bufLen = len;
-    _mode = RF22_MODE_IDLE;
-    _rxBufValid = true;
-    __enable_irq();     // Enable Interrupts
-
-    led3 = !led3;
+        led3 = !led3;
 
     }
-    if (_lastInterruptFlags[0] & RF22_ICRCERROR)
-    {
-//    Serial.println("ICRCERR");  
-    _rxBad++;
-    led2 = !led2;
-    clearRxBuf();
-    resetRxFifo();
-    _mode = RF22_MODE_IDLE;
-    setModeRx(); // Keep trying
+    
+    if (_lastInterruptFlags[0] & RF22_ICRCERROR) {
+//    Serial.println("ICRCERR");
+        _rxBad++;
+        led2 = !led2;
+        clearRxBuf();
+        resetRxFifo();
+        _mode = RF22_MODE_IDLE;
+        setModeRx(); // Keep trying
     }
-    if (_lastInterruptFlags[1] & RF22_IPREAVAL)
-    {
-//  Serial.println("IPREAVAL");  
-    _lastRssi = spiRead(RF22_REG_26_RSSI);
-    clearRxBuf();
+    
+    if (_lastInterruptFlags[1] & RF22_IPREAVAL) {
+//  Serial.println("IPREAVAL");
+        
+        _lastRssi = spiRead(RF22_REG_26_RSSI);
+
+
+        // why clear the rx-buf here? charly
+        clearRxBuf();
+
+
     }
     led1 = 0;
 }
@@ -367,7 +385,7 @@
     _slaveSelectPin = 0;
     _spi.write(reg & ~RF22_SPI_WRITE_MASK); // Send the start address with the write mask off
     while (len--)
-    *dest++ = _spi.write(0);
+        *dest++ = _spi.write(0);
     //digitalWrite(_slaveSelectPin, HIGH);
     _slaveSelectPin = 1;
 }
@@ -378,7 +396,7 @@
     _slaveSelectPin = 0;
     _spi.write(reg | RF22_SPI_WRITE_MASK); // Send the start address with the write mask on
     while (len--)
-    _spi.write(*src++);
+        _spi.write(*src++);
     //digitalWrite(_slaveSelectPin, HIGH);
     _slaveSelectPin = 1;
 }
@@ -390,7 +408,7 @@
 
 uint8_t RF22::adcRead(uint8_t adcsel,
                       uint8_t adcref ,
-                      uint8_t adcgain, 
+                      uint8_t adcgain,
                       uint8_t adcoffs)
 {
     uint8_t configuration = adcsel | adcref | (adcgain & RF22_ADCGAIN);
@@ -400,8 +418,8 @@
     // Conversion time is nominally 305usec
     // Wait for the DONE bit
     while (!(spiRead(RF22_REG_0F_ADC_CONFIGURATION) & RF22_ADCDONE))
-    ;
-    // Return the value  
+        ;
+    // Return the value
     return spiRead(RF22_REG_11_ADC_VALUE);
 }
 
@@ -409,7 +427,7 @@
 {
     spiWrite(RF22_REG_12_TEMPERATURE_SENSOR_CALIBRATION, tsrange | RF22_ENTSOFFS);
     spiWrite(RF22_REG_13_TEMPERATURE_VALUE_OFFSET, tvoffs);
-    return adcRead(RF22_ADCSEL_INTERNAL_TEMPERATURE_SENSOR | RF22_ADCREF_BANDGAP_VOLTAGE); 
+    return adcRead(RF22_ADCSEL_INTERNAL_TEMPERATURE_SENSOR | RF22_ADCREF_BANDGAP_VOLTAGE);
 }
 
 uint16_t RF22::wutRead()
@@ -438,20 +456,17 @@
     uint8_t fbsel = RF22_SBSEL;
     uint8_t afclimiter;
     if (centre < 240.0 || centre > 960.0) // 930.0 for early silicon
-    return false;
-    if (centre >= 480.0)
-    {
-    if (afcPullInRange < 0.0 || afcPullInRange > 0.318750)
         return false;
-    centre /= 2;
-    fbsel |= RF22_HBSEL;
-    afclimiter = afcPullInRange * 1000000.0 / 1250.0;
-    }
-    else
-    {
-    if (afcPullInRange < 0.0 || afcPullInRange > 0.159375)
-        return false;
-    afclimiter = afcPullInRange * 1000000.0 / 625.0;
+    if (centre >= 480.0) {
+        if (afcPullInRange < 0.0 || afcPullInRange > 0.318750)
+            return false;
+        centre /= 2;
+        fbsel |= RF22_HBSEL;
+        afclimiter = afcPullInRange * 1000000.0 / 1250.0;
+    } else {
+        if (afcPullInRange < 0.0 || afcPullInRange > 0.159375)
+            return false;
+        afclimiter = afcPullInRange * 1000000.0 / 625.0;
     }
     centre /= 10.0;
     float integerPart = floor(centre);
@@ -502,35 +517,32 @@
 
 void RF22::setModeIdle()
 {
-    if (_mode != RF22_MODE_IDLE)
-    {
-    setMode(_idleMode);
-    _mode = RF22_MODE_IDLE;
+    if (_mode != RF22_MODE_IDLE) {
+        setMode(_idleMode);
+        _mode = RF22_MODE_IDLE;
     }
 }
 
 void RF22::setModeRx()
 {
-    if (_mode != RF22_MODE_RX)
-    {
-    setMode(_idleMode | RF22_RXON);
-    _mode = RF22_MODE_RX;
+    if (_mode != RF22_MODE_RX) {
+        setMode(_idleMode | RF22_RXON);
+        _mode = RF22_MODE_RX;
     }
 }
 
 void RF22::setModeTx()
 {
-    if (_mode != RF22_MODE_TX)
-    {
-    setMode(_idleMode | RF22_TXON);
-    _mode = RF22_MODE_TX;
-    // Hmmm, if you dont clear the RX FIFO here, then it appears that going
-    // to transmit mode in the middle of a receive can corrupt the
-    // RX FIFO
-    resetRxFifo();
-    clearRxBuf();
+    if (_mode != RF22_MODE_TX) {
+        setMode(_idleMode | RF22_TXON);
+        _mode = RF22_MODE_TX;
+        // Hmmm, if you dont clear the RX FIFO here, then it appears that going
+        // to transmit mode in the middle of a receive can corrupt the
+        // RX FIFO
+        resetRxFifo();
+//        clearRxBuf();
     }
-    }
+}
 
 uint8_t  RF22::mode()
 {
@@ -591,7 +603,7 @@
 boolean RF22::available()
 {
     if (!_rxBufValid)
-    setModeRx(); // Make sure we are receiving
+        setModeRx(); // Make sure we are receiving
     return _rxBufValid;
 }
 
@@ -599,7 +611,7 @@
 void RF22::waitAvailable()
 {
     while (!available())
-    ;
+        ;
 }
 
 // Blocks until a valid message is received or timeout expires
@@ -610,15 +622,15 @@
     t.start();
     unsigned long endtime = t.read_ms() + timeout;
     while (t.read_ms() < endtime)
-    if (available())
-        return true;
+        if (available())
+            return true;
     return false;
 }
 
 void RF22::waitPacketSent()
 {
     while (_mode == RF22_MODE_TX)
-    ; // Wait for any previous transmit to finish
+        ; // Wait for any previous transmit to finish
 }
 
 // Diagnostic help
@@ -628,15 +640,13 @@
     uint8_t i;
 
     Serial.println(prompt);
-    for (i = 0; i < len; i++)
-    {
-    if (i % 16 == 15)
-        Serial.println(buf[i], HEX);
-    else
-    {
-        Serial.print(buf[i], HEX);
-        Serial.print(' ');
-    }
+    for (i = 0; i < len; i++) {
+        if (i % 16 == 15)
+            Serial.println(buf[i], HEX);
+        else {
+            Serial.print(buf[i], HEX);
+            Serial.print(' ');
+        }
     }
     Serial.println(' ');
 #endif
@@ -645,11 +655,13 @@
 boolean RF22::recv(uint8_t* buf, uint8_t* len)
 {
     if (!available())
-    return false;
+        return false;
+    __disable_irq();    // Disable Interrupts
     if (*len > _bufLen)
-    *len = _bufLen;
+        *len = _bufLen;
     memcpy(buf, _buf, *len);
     clearRxBuf();
+    __enable_irq();     // Enable Interrupts
 //    printBuffer("recv:", buf, *len);
 //    }
     return true;
@@ -685,9 +697,9 @@
     waitPacketSent();
 //    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
     {
-    if (!fillTxBuf(data, len))
-        return false;
-    startTransmit();
+        if (!fillTxBuf(data, len))
+            return false;
+        startTransmit();
     }
 //    printBuffer("send:", data, len);
     return true;
@@ -697,17 +709,19 @@
 {
     clearTxBuf();
     if (!len)
-    return false; 
+        return false;
     return appendTxBuf(data, len);
 }
 
 boolean RF22::appendTxBuf(const uint8_t* data, uint8_t len)
 {
     if (((uint16_t)_bufLen + len) > RF22_MAX_MESSAGE_LEN)
-    return false;
+        return false;
+    __disable_irq();    // Disable Interrupts
     memcpy(_buf + _bufLen, data, len);
     _bufLen += len;
-    
+    __enable_irq();     // Enable Interrupts
+
 //    printBuffer("txbuf:", _buf, _bufLen);
     return true;
 }
@@ -715,15 +729,14 @@
 // Assumption: there is currently <= RF22_TXFFAEM_THRESHOLD bytes in the Tx FIFO
 void RF22::sendNextFragment()
 {
-    if (_txBufSentIndex < _bufLen)
-    {
-    // Some left to send?
-    uint8_t len = _bufLen - _txBufSentIndex;
-    // But dont send too much
-    if (len > (RF22_FIFO_SIZE - RF22_TXFFAEM_THRESHOLD - 1))
-        len = (RF22_FIFO_SIZE - RF22_TXFFAEM_THRESHOLD - 1);
-    spiBurstWrite(RF22_REG_7F_FIFO_ACCESS, _buf + _txBufSentIndex, len);
-    _txBufSentIndex += len;
+    if (_txBufSentIndex < _bufLen) {
+        // Some left to send?
+        uint8_t len = _bufLen - _txBufSentIndex;
+        // But dont send too much
+        if (len > (RF22_FIFO_SIZE - RF22_TXFFAEM_THRESHOLD - 1))
+            len = (RF22_FIFO_SIZE - RF22_TXFFAEM_THRESHOLD - 1);
+        spiBurstWrite(RF22_REG_7F_FIFO_ACCESS, _buf + _txBufSentIndex, len);
+        _txBufSentIndex += len;
     }
 }
 
@@ -732,7 +745,7 @@
 void RF22::readNextFragment()
 {
     if (((uint16_t)_bufLen + RF22_RXFFAFULL_THRESHOLD) > RF22_MAX_MESSAGE_LEN)
-    return; // Hmmm receiver overflow. Should never occur
+        return; // Hmmm receiver overflow. Should never occur
 
     // Read the RF22_RXFFAFULL_THRESHOLD octets that should be there
     spiBurstRead(RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, RF22_RXFFAFULL_THRESHOLD);