Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.
Dependents: mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more
Enc28j60Eth.cpp
00001 /* 00002 Enc28J60Network.cpp 00003 UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. 00004 00005 Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de> 00006 All rights reserved. 00007 00008 based on enc28j60.c file from the AVRlib library by Pascal Stang. 00009 For AVRlib See http://www.procyonengineering.com/ 00010 00011 Modified (ported to mbed) by Zoltan Hudak <hudakz@inbox.com> 00012 00013 This program is free software: you can redistribute it and/or modify 00014 it under the terms of the GNU General Public License as published by 00015 the Free Software Foundation, either version 3 of the License, or 00016 (at your option) any later version. 00017 00018 This program is distributed in the hope that it will be useful, 00019 but WITHOUT ANY WARRANTY; without even the implied warranty of 00020 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00021 GNU General Public License for more details. 00022 00023 You should have received a copy of the GNU General Public License 00024 along with this program. If not, see <http://www.gnu.org/licenses/>. 00025 */ 00026 #include "Enc28j60Eth.h" 00027 #include "mbed.h" 00028 #include "mbed_version.h" 00029 00030 extern "C" 00031 { 00032 #include "enc28j60.h" 00033 #include "uip.h" 00034 } 00035 00036 // Static member initialization 00037 uint16_t Enc28j60Eth::nextPacketPtr; 00038 uint8_t Enc28j60Eth::bank = 0xff; 00039 struct memblock Enc28j60Eth::receivePkt; 00040 00041 /** 00042 * @brief 00043 * @note 00044 * @param 00045 * @retval 00046 */ 00047 Enc28j60Eth::Enc28j60Eth(PinName mosi, PinName miso, PinName sclk, PinName cs) : 00048 MemPool(), 00049 _spi(mosi, miso, sclk), 00050 _cs(cs) 00051 { } 00052 00053 /** 00054 * @brief 00055 * @note 00056 * @param 00057 * @retval 00058 */ 00059 void Enc28j60Eth::init(uint8_t* macaddr) 00060 { 00061 MemPool::init(); // 1 byte in between RX_STOP_INIT and pool to allow prepending of controlbyte 00062 00063 // initialize SPI interface 00064 _cs = 1; 00065 _spi.format(8, 0); // 8-bit, mode 0 00066 _spi.frequency(10000000); // 10 Mbit/s 00067 #if MBED_MAJOR_VERSION == 2 00068 wait_ms(100); 00069 #else 00070 thread_sleep_for(100); 00071 #endif 00072 00073 // perform system reset 00074 writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); 00075 00076 // check CLKRDY bit to see if reset is complete 00077 // while(!(readReg(ESTAT) & ESTAT_CLKRDY)); 00078 // The CLKRDY does not work. See Rev. B4 Silicon Errata point. 00079 // Just wait. 00080 #if MBED_MAJOR_VERSION == 2 00081 wait_ms(50); 00082 #else 00083 thread_sleep_for(50); 00084 #endif 00085 00086 // do bank 0 stuff 00087 // initialize receive buffer 00088 // 16-bit transfers, must write low byte first 00089 // set receive buffer start address 00090 nextPacketPtr = RXSTART_INIT; 00091 00092 // Rx start 00093 writeRegPair(ERXSTL, RXSTART_INIT); 00094 00095 // set receive pointer address 00096 writeRegPair(ERXRDPTL, RXSTART_INIT); 00097 00098 // RX end 00099 writeRegPair(ERXNDL, RXEND_INIT); 00100 00101 //All memory which is not used by the receive buffer is considered the transmission buffer. 00102 // No explicit action is required to initialize the transmission buffer. 00103 // TX start 00104 //writeRegPair(ETXSTL, TXSTART_INIT); 00105 // TX end 00106 //writeRegPair(ETXNDL, TXEND_INIT); 00107 // However, he host controller should leave at least seven bytes between each 00108 // packet and the beginning of the receive buffer. 00109 00110 // do bank 1 stuff, packet filter: 00111 // For broadcast packets we allow only ARP packtets 00112 // All other packets should be unicast only for our mac (MAADR) 00113 // 00114 // The pattern to match is therefore 00115 // Type ETH.DST 00116 // ARP BROADCAST 00117 // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 00118 // in binary these poitions are:11 0000 0011 1111 00119 // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 00120 //TODO define specific pattern to receive dhcp-broadcast packages instead of setting ERFCON_BCEN! 00121 writeReg(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN | ERXFCON_BCEN); 00122 writeRegPair(EPMM0, 0x303f); 00123 writeRegPair(EPMCSL, 0xf7f9); 00124 00125 // 00126 // 00127 // do bank 2 stuff, 00128 // enable MAC receive 00129 // and bring MAC out of reset (writes 0x00 to MACON2) 00130 writeRegPair(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); 00131 00132 // enable automatic padding to 60bytes and CRC operations 00133 writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); 00134 00135 // set inter-frame gap (non-back-to-back) 00136 writeRegPair(MAIPGL, 0x0C12); 00137 00138 // set inter-frame gap (back-to-back) 00139 writeReg(MABBIPG, 0x12); 00140 00141 // Set the maximum packet size which the controller will accept 00142 // Do not send packets longer than MAX_FRAMELEN: 00143 writeRegPair(MAMXFLL, MAX_FRAMELEN); 00144 00145 // do bank 3 stuff 00146 // write MAC address 00147 // NOTE: MAC address in ENC28J60 is byte-backward 00148 writeReg(MAADR5, macaddr[0]); 00149 writeReg(MAADR4, macaddr[1]); 00150 writeReg(MAADR3, macaddr[2]); 00151 writeReg(MAADR2, macaddr[3]); 00152 writeReg(MAADR1, macaddr[4]); 00153 writeReg(MAADR0, macaddr[5]); 00154 00155 // no loopback of transmitted frames 00156 phyWrite(PHCON2, PHCON2_HDLDIS); 00157 00158 // switch to bank 0 00159 setBank(ECON1); 00160 00161 // enable interrutps 00162 writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE); 00163 00164 // enable packet reception 00165 writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); 00166 00167 //Configure leds 00168 phyWrite(PHLCON, 0x476); 00169 } 00170 00171 /** 00172 * @brief 00173 * @note 00174 * @param 00175 * @retval 00176 */ 00177 memhandle Enc28j60Eth::receivePacket() 00178 { 00179 uint8_t rxstat; 00180 uint16_t len; 00181 // check if a packet has been received and buffered 00182 //if( !(readReg(EIR) & EIR_PKTIF) ){ 00183 // The above does not work. See Rev. B4 Silicon Errata point 6. 00184 if (readReg(EPKTCNT) != 0) { 00185 uint16_t readPtr = nextPacketPtr + 00186 6 > RXEND_INIT ? nextPacketPtr + 00187 6 - 00188 RXEND_INIT + 00189 RXSTART_INIT : nextPacketPtr + 00190 6; 00191 // Set the read pointer to the start of the received packet 00192 writeRegPair(ERDPTL, nextPacketPtr); 00193 00194 // read the next packet pointer 00195 nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); 00196 nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; 00197 00198 // read the packet length (see datasheet page 43) 00199 len = readOp(ENC28J60_READ_BUF_MEM, 0); 00200 len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; 00201 len -= 4; //remove the CRC count 00202 // read the receive status (see datasheet page 43) 00203 rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); 00204 00205 //rxstat |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; 00206 #ifdef ENC28J60DEBUG 00207 printf 00208 ( 00209 "receivePacket [%d-%d], next: %d, stat: %d, count: %d -> ", 00210 readPtr, 00211 (readPtr + len) % (RXEND_INIT + 1), 00212 nextPacketPtr, 00213 rxstat, 00214 readReg(EPKTCNT) 00215 ); 00216 (rxstat & 0x80) != 0 ? printf("OK") : printf("failed"); 00217 printf("\r\n"); 00218 #endif 00219 // decrement the packet counter indicate we are done with this packet 00220 00221 writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); 00222 00223 // check CRC and symbol errors (see datasheet page 44, table 7-3): 00224 // The ERXFCON.CRCEN is set by default. Normally we should not 00225 // need to check this. 00226 if ((rxstat & 0x80) != 0) { 00227 receivePkt.begin = readPtr; 00228 receivePkt.size = len; 00229 return UIP_RECEIVEBUFFERHANDLE; 00230 } 00231 00232 // Move the RX read pointer to the start of the next received packet 00233 // This frees the memory we just read out 00234 setERXRDPT(); 00235 } 00236 00237 return(NOBLOCK); 00238 } 00239 00240 /** 00241 * @brief 00242 * @note 00243 * @param 00244 * @retval 00245 */ 00246 void Enc28j60Eth::setERXRDPT() 00247 { 00248 writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXEND_INIT : nextPacketPtr - 1); 00249 } 00250 00251 /** 00252 * @brief 00253 * @note 00254 * @param 00255 * @retval 00256 */ 00257 size_t Enc28j60Eth::blockSize(memhandle handle) 00258 { 00259 return handle == NOBLOCK ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; 00260 } 00261 00262 /** 00263 * @brief 00264 * @note 00265 * @param 00266 * @retval 00267 */ 00268 void Enc28j60Eth::sendPacket(memhandle handle) 00269 { 00270 memblock* packet = &blocks[handle]; 00271 uint16_t start = packet->begin - 1; 00272 uint16_t end = start + packet->size; 00273 00274 // backup data at control-byte position 00275 uint8_t data = readByte(start); 00276 // write control-byte (if not 0 anyway) 00277 if (data) 00278 writeByte(start, 0); 00279 00280 #ifdef ENC28J60DEBUG 00281 printf("sendPacket(%d) [%d-%d]: ", handle, start, end); 00282 for (uint16_t i = start; i <= end; i++) { 00283 printf("%d ", readByte(i)); 00284 } 00285 00286 printf("\r\n"); 00287 #endif 00288 // TX start 00289 00290 writeRegPair(ETXSTL, start); 00291 00292 // Set the TXND pointer to correspond to the packet size given 00293 writeRegPair(ETXNDL, end); 00294 00295 // send the contents of the transmit buffer onto the network 00296 writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); 00297 00298 // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. 00299 if ((readReg(EIR) & EIR_TXERIF)) { 00300 writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); 00301 } 00302 00303 //restore data on control-byte position 00304 if (data) 00305 writeByte(start, data); 00306 } 00307 00308 /** 00309 * @brief 00310 * @note 00311 * @param 00312 * @retval 00313 */ 00314 uint16_t Enc28j60Eth::setReadPtr(memhandle handle, memaddress position, uint16_t len) 00315 { 00316 memblock* packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; 00317 memaddress start = handle == UIP_RECEIVEBUFFERHANDLE && 00318 packet->begin + 00319 position > RXEND_INIT ? packet->begin + 00320 position - 00321 RXEND_INIT + 00322 RXSTART_INIT : packet->begin + 00323 position; 00324 00325 writeRegPair(ERDPTL, start); 00326 00327 if (len > packet->size - position) 00328 len = packet->size - position; 00329 return len; 00330 } 00331 00332 /** 00333 * @brief 00334 * @note 00335 * @param 00336 * @retval 00337 */ 00338 uint16_t Enc28j60Eth::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) 00339 { 00340 len = setReadPtr(handle, position, len); 00341 readBuffer(len, buffer); 00342 return len; 00343 } 00344 00345 /** 00346 * @brief 00347 * @note 00348 * @param 00349 * @retval 00350 */ 00351 uint16_t Enc28j60Eth::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) 00352 { 00353 memblock* packet = &blocks[handle]; 00354 uint16_t start = packet->begin + position; 00355 00356 writeRegPair(EWRPTL, start); 00357 00358 if (len > packet->size - position) 00359 len = packet->size - position; 00360 writeBuffer(len, buffer); 00361 return len; 00362 } 00363 00364 /** 00365 * @brief 00366 * @note 00367 * @param 00368 * @retval 00369 */ 00370 uint8_t Enc28j60Eth::readByte(uint16_t addr) 00371 { 00372 uint8_t result; 00373 00374 writeRegPair(ERDPTL, addr); 00375 00376 _cs = 0; 00377 00378 // issue read command 00379 _spi.write(ENC28J60_READ_BUF_MEM); 00380 00381 // read data 00382 result = _spi.write(0x00); 00383 00384 _cs = 1; 00385 00386 return(result); 00387 } 00388 00389 /** 00390 * @brief 00391 * @note 00392 * @param 00393 * @retval 00394 */ 00395 void Enc28j60Eth::writeByte(uint16_t addr, uint8_t data) 00396 { 00397 writeRegPair(EWRPTL, addr); 00398 00399 _cs = 0; 00400 00401 // issue write command 00402 _spi.write(ENC28J60_WRITE_BUF_MEM); 00403 00404 // write data 00405 _spi.write(data); 00406 00407 _cs = 1; 00408 } 00409 00410 /** 00411 * @brief 00412 * @note 00413 * @param 00414 * @retval 00415 */ 00416 void Enc28j60Eth::copyPacket 00417 ( 00418 memhandle dest_pkt, 00419 memaddress dest_pos, 00420 memhandle src_pkt, 00421 memaddress src_pos, 00422 uint16_t len 00423 ) 00424 { 00425 memblock* dest = &blocks[dest_pkt]; 00426 memblock* src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; 00427 memaddress start = src_pkt == UIP_RECEIVEBUFFERHANDLE && 00428 src->begin + 00429 src_pos > RXEND_INIT ? src->begin + 00430 src_pos - 00431 RXEND_INIT + 00432 RXSTART_INIT : src->begin + 00433 src_pos; 00434 enc28j60_mempool_block_move_callback(dest->begin + dest_pos, start, len); 00435 00436 // Move the RX read pointer to the start of the next received packet 00437 // This frees the memory we just read out 00438 //setERXRDPT(); 00439 } 00440 00441 /** 00442 * @brief 00443 * @note 00444 * @param 00445 * @retval 00446 */ 00447 void Enc28j60Eth::freePacket() 00448 { 00449 setERXRDPT(); 00450 } 00451 00452 /** 00453 * @brief 00454 * @note 00455 * @param 00456 * @retval 00457 */ 00458 uint8_t Enc28j60Eth::readOp(uint8_t op, uint8_t address) 00459 { 00460 uint8_t result; 00461 00462 _cs = 0; 00463 00464 // issue read command 00465 _spi.write(op | (address & ADDR_MASK)); 00466 00467 // read data 00468 result = _spi.write(0x00); 00469 00470 // do dummy read if needed (for mac and mii, see datasheet page 29) 00471 if (address & 0x80) 00472 result = _spi.write(0x00); 00473 00474 _cs = 1; 00475 return(result); 00476 } 00477 00478 /** 00479 * @brief 00480 * @note 00481 * @param 00482 * @retval 00483 */ 00484 void Enc28j60Eth::writeOp(uint8_t op, uint8_t address, uint8_t data) 00485 { 00486 _cs = 0; 00487 00488 // issue write command 00489 _spi.write(op | (address & ADDR_MASK)); 00490 00491 // write data 00492 _spi.write(data); 00493 00494 _cs = 1; 00495 } 00496 00497 /** 00498 * @brief 00499 * @note 00500 * @param 00501 * @retval 00502 */ 00503 void Enc28j60Eth::readBuffer(uint16_t len, uint8_t* data) 00504 { 00505 _cs = 0; 00506 00507 // issue read command 00508 _spi.write(ENC28J60_READ_BUF_MEM); 00509 00510 // read data 00511 while (len) { 00512 len--; 00513 *data = _spi.write(0x00); 00514 data++; 00515 } 00516 00517 *data = '\0'; 00518 00519 _cs = 1; 00520 } 00521 00522 /** 00523 * @brief 00524 * @note 00525 * @param 00526 * @retval 00527 */ 00528 void Enc28j60Eth::writeBuffer(uint16_t len, uint8_t* data) 00529 { 00530 _cs = 0; 00531 00532 // issue write command 00533 _spi.write(ENC28J60_WRITE_BUF_MEM); 00534 00535 // write data 00536 while (len) { 00537 len--; 00538 _spi.write(*data); 00539 data++; 00540 } 00541 00542 _cs = 1; 00543 } 00544 00545 /** 00546 * @brief 00547 * @note 00548 * @param 00549 * @retval 00550 */ 00551 void Enc28j60Eth::setBank(uint8_t address) 00552 { 00553 // set the bank (if needed) 00554 if ((address & BANK_MASK) != bank) { 00555 // set the bank 00556 writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0)); 00557 writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5); 00558 bank = (address & BANK_MASK); 00559 } 00560 } 00561 00562 /** 00563 * @brief 00564 * @note 00565 * @param 00566 * @retval 00567 */ 00568 uint8_t Enc28j60Eth::readReg(uint8_t address) 00569 { 00570 // set the bank 00571 setBank(address); 00572 00573 // do the read 00574 return readOp(ENC28J60_READ_CTRL_REG, address); 00575 } 00576 00577 /** 00578 * @brief 00579 * @note 00580 * @param 00581 * @retval 00582 */ 00583 void Enc28j60Eth::writeReg(uint8_t address, uint8_t data) 00584 { 00585 // set the bank 00586 setBank(address); 00587 00588 // do the write 00589 writeOp(ENC28J60_WRITE_CTRL_REG, address, data); 00590 } 00591 00592 /** 00593 * @brief 00594 * @note 00595 * @param 00596 * @retval 00597 */ 00598 void Enc28j60Eth::writeRegPair(uint8_t address, uint16_t data) 00599 { 00600 // set the bank 00601 setBank(address); 00602 00603 // do the write 00604 writeOp(ENC28J60_WRITE_CTRL_REG, address, (data & 0xFF)); 00605 writeOp(ENC28J60_WRITE_CTRL_REG, address + 1, (data) >> 8); 00606 } 00607 00608 /** 00609 * @brief 00610 * @note 00611 * @param 00612 * @retval 00613 */ 00614 void Enc28j60Eth::phyWrite(uint8_t address, uint16_t data) 00615 { 00616 // set the PHY register address 00617 writeReg(MIREGADR, address); 00618 00619 // write the PHY data 00620 writeRegPair(MIWRL, data); 00621 00622 // wait until the PHY write completes 00623 while (readReg(MISTAT) & MISTAT_BUSY) { 00624 wait_us(15); 00625 } 00626 } 00627 00628 /** 00629 * @brief 00630 * @note 00631 * @param 00632 * @retval 00633 */ 00634 uint16_t Enc28j60Eth::phyRead(uint8_t address) 00635 { 00636 writeReg(MIREGADR, address); 00637 writeReg(MICMD, MICMD_MIIRD); 00638 00639 // wait until the PHY read completes 00640 while (readReg(MISTAT) & MISTAT_BUSY) { 00641 wait_us(15); 00642 } //and MIRDH 00643 00644 writeReg(MICMD, 0); 00645 return(readReg(MIRDL) | readReg(MIRDH) << 8); 00646 } 00647 00648 /** 00649 * @brief 00650 * @note 00651 * @param 00652 * @retval 00653 */ 00654 void Enc28j60Eth::clkout(uint8_t clk) 00655 { 00656 //setup clkout: 2 is 12.5MHz: 00657 writeReg(ECOCON, clk & 0x7); 00658 } 00659 00660 // read the revision of the chip: 00661 uint8_t Enc28j60Eth::getrev() 00662 { 00663 return(readReg(EREVID)); 00664 } 00665 00666 /** 00667 * @brief 00668 * @note 00669 * @param 00670 * @retval 00671 */ 00672 uint16_t Enc28j60Eth::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) 00673 { 00674 uint8_t spdr; 00675 uint16_t t; 00676 uint16_t i; 00677 00678 len = setReadPtr(handle, pos, len) - 1; 00679 _cs = 0; 00680 00681 // issue read command 00682 spdr = _spi.write(ENC28J60_READ_BUF_MEM); 00683 for (i = 0; i < len; i += 2) { 00684 // read data 00685 spdr = _spi.write(0x00); 00686 t = spdr << 8; 00687 spdr = _spi.write(0x00); 00688 t += spdr; 00689 sum += t; 00690 if (sum < t) { 00691 sum++; /* carry */ 00692 } 00693 } 00694 00695 if (i == len) { 00696 spdr = _spi.write(0x00); 00697 t = (spdr << 8) + 0; 00698 sum += t; 00699 if (sum < t) { 00700 sum++; /* carry */ 00701 } 00702 } 00703 00704 _cs = 1; 00705 00706 /* Return sum in host byte order. */ 00707 return sum; 00708 } 00709 00710 /** 00711 * @brief 00712 * @note 00713 * @param 00714 * @retval 00715 */ 00716 void Enc28j60Eth::powerOff() 00717 { 00718 writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); 00719 #if MBED_MAJOR_VERSION == 2 00720 wait_ms(50); 00721 #else 00722 thread_sleep_for(50); 00723 #endif 00724 writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); 00725 #if MBED_MAJOR_VERSION == 2 00726 wait_ms(50); 00727 #else 00728 thread_sleep_for(50); 00729 #endif 00730 writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); 00731 } 00732 00733 /** 00734 * @brief 00735 * @note 00736 * @param 00737 * @retval 00738 */ 00739 void Enc28j60Eth::powerOn() 00740 { 00741 writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); 00742 #if MBED_MAJOR_VERSION == 2 00743 wait_ms(50); 00744 #else 00745 thread_sleep_for(50); 00746 #endif 00747 writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); 00748 #if MBED_MAJOR_VERSION == 2 00749 wait_ms(50); 00750 #else 00751 thread_sleep_for(50); 00752 #endif 00753 } 00754 00755 /** 00756 * @brief 00757 * @note 00758 * @param 00759 * @retval 00760 */ 00761 bool Enc28j60Eth::linkStatus() 00762 { 00763 return(phyRead(PHSTAT2) & 0x0400) > 0; 00764 }
Generated on Tue Jul 12 2022 18:48:00 by 1.7.2