Enables fast realtime communication over Ethernet by using plain Ethernet frames (overhead of TCP/IP network layers is stripped away).
RawEthernet.cpp
- Committer:
- hudakz
- Date:
- 2018-08-20
- Revision:
- 0:ca476e0a1a28
File content as of revision 0:ca476e0a1a28:
//------------------------------------------------------------------------------ // Functions for using an ENC28J60 ethernet controller, optimized for // communication speed. // // Written by Rogier Schouten http://www.rogiershikes.tk // Based on code by Guido Socher http://www.tuxgraphics.org // Idea modified and further updated by Guido Socher // Ported to MBED by Zoltan Hudak hudakz@outlook.com // License: GPL V2 // // Enables to read sensor data or control IO-ports with less than a millisecond delay. // The trick is in removing the TCP/IP overhead and use raw Ethernet frames. // A Linux application using raw network sockets is controlling this slave device. // // Assumptions: // - Max. payload data: 255 bytes per packet // - The network consists of a master PC and one or more slave devices. // Only plain hubs and switches are allowed between PC and ethernet device. // Note that some wlan routers have internal switches which switch only // IP packets. They will discard plain ethernet frames. A normal 100Mbit // office/workgroup switch will however work. // // Based on these assumptions, we can optimize the protocol for faster communication: // - Master and slave send unicast packets. // - We use raw ethernet and no higher-level protocol such as UDP or TCP/IP // - We use the EtherType field of a packet as a length field. Actually, we only // use one byte of it since we have max 255 length packets. // // Furthermore, there are a few code optimizations: // - Minimize communication between ENC and MCU. // - No unnecessary memory bank checks // #include "RawEthernet.h" // ENC28J60 Control Registers // Control register definitions are a combination of address, // bank number, and Ethernet/MAC/PHY indicator bits. // - Register address (bits 0-4) // - Bank number (bits 5-6) // - MAC/PHY indicator (bit 7) #define ADDR_MASK 0x1F #define BANK_MASK 0x60 #define SPRD_MASK 0x80 // All-bank registers #define EIE 0x1B #define EIR 0x1C #define ESTAT 0x1D #define ECON2 0x1E #define ECON1 0x1F // Bank 0 registers #define ERDPTL (0x00 | 0x00) #define ERDPTH (0x01 | 0x00) #define EWRPTL (0x02 | 0x00) #define EWRPTH (0x03 | 0x00) #define ETXSTL (0x04 | 0x00) #define ETXSTH (0x05 | 0x00) #define ETXNDL (0x06 | 0x00) #define ETXNDH (0x07 | 0x00) #define ERXSTL (0x08 | 0x00) #define ERXSTH (0x09 | 0x00) #define ERXNDL (0x0A | 0x00) #define ERXNDH (0x0B | 0x00) #define ERXRDPTL (0x0C | 0x00) #define ERXRDPTH (0x0D | 0x00) #define ERXWRPTL (0x0E | 0x00) #define ERXWRPTH (0x0F | 0x00) #define EDMASTL (0x10 | 0x00) #define EDMASTH (0x11 | 0x00) #define EDMANDL (0x12 | 0x00) #define EDMANDH (0x13 | 0x00) #define EDMADSTL (0x14 | 0x00) #define EDMADSTH (0x15 | 0x00) #define EDMACSL (0x16 | 0x00) #define EDMACSH (0x17 | 0x00) // Bank 1 registers #define EHT0 (0x00 | 0x20) #define EHT1 (0x01 | 0x20) #define EHT2 (0x02 | 0x20) #define EHT3 (0x03 | 0x20) #define EHT4 (0x04 | 0x20) #define EHT5 (0x05 | 0x20) #define EHT6 (0x06 | 0x20) #define EHT7 (0x07 | 0x20) #define EPMM0 (0x08 | 0x20) #define EPMM1 (0x09 | 0x20) #define EPMM2 (0x0A | 0x20) #define EPMM3 (0x0B | 0x20) #define EPMM4 (0x0C | 0x20) #define EPMM5 (0x0D | 0x20) #define EPMM6 (0x0E | 0x20) #define EPMM7 (0x0F | 0x20) #define EPMCSL (0x10 | 0x20) #define EPMCSH (0x11 | 0x20) #define EPMOL (0x14 | 0x20) #define EPMOH (0x15 | 0x20) #define EWOLIE (0x16 | 0x20) #define EWOLIR (0x17 | 0x20) #define ERXFCON (0x18 | 0x20) #define EPKTCNT (0x19 | 0x20) // Bank 2 registers #define MACON1 (0x00 | 0x40 | 0x80) #define MACON2 (0x01 | 0x40 | 0x80) #define MACON3 (0x02 | 0x40 | 0x80) #define MACON4 (0x03 | 0x40 | 0x80) #define MABBIPG (0x04 | 0x40 | 0x80) #define MAIPGL (0x06 | 0x40 | 0x80) #define MAIPGH (0x07 | 0x40 | 0x80) #define MACLCON1 (0x08 | 0x40 | 0x80) #define MACLCON2 (0x09 | 0x40 | 0x80) #define MAMXFLL (0x0A | 0x40 | 0x80) #define MAMXFLH (0x0B | 0x40 | 0x80) #define MAPHSUP (0x0D | 0x40 | 0x80) #define MICON (0x11 | 0x40 | 0x80) #define MICMD (0x12 | 0x40 | 0x80) #define MIREGADR (0x14 | 0x40 | 0x80) #define MIWRL (0x16 | 0x40 | 0x80) #define MIWRH (0x17 | 0x40 | 0x80) #define MIRDL (0x18 | 0x40 | 0x80) #define MIRDH (0x19 | 0x40 | 0x80) // Bank 3 registers #define MAADR1 (0x00 | 0x60 | 0x80) #define MAADR0 (0x01 | 0x60 | 0x80) #define MAADR3 (0x02 | 0x60 | 0x80) #define MAADR2 (0x03 | 0x60 | 0x80) #define MAADR5 (0x04 | 0x60 | 0x80) #define MAADR4 (0x05 | 0x60 | 0x80) #define EBSTSD (0x06 | 0x60) #define EBSTCON (0x07 | 0x60) #define EBSTCSL (0x08 | 0x60) #define EBSTCSH (0x09 | 0x60) #define MISTAT (0x0A | 0x60 | 0x80) #define EREVID (0x12 | 0x60) #define ECOCON (0x15 | 0x60) #define EFLOCON (0x17 | 0x60) #define EPAUSL (0x18 | 0x60) #define EPAUSH (0x19 | 0x60) // PHY registers #define PHCON1 0x00 #define PHSTAT1 0x01 #define PHHID1 0x02 #define PHHID2 0x03 #define PHCON2 0x10 #define PHSTAT2 0x11 #define PHIE 0x12 #define PHIR 0x13 #define PHLCON 0x14 // ENC28J60 ERXFCON Register Bit Definitions #define ERXFCON_UCEN 0x80 #define ERXFCON_ANDOR 0x40 #define ERXFCON_CRCEN 0x20 #define ERXFCON_PMEN 0x10 #define ERXFCON_MPEN 0x08 #define ERXFCON_HTEN 0x04 #define ERXFCON_MCEN 0x02 #define ERXFCON_BCEN 0x01 // ENC28J60 EIE Register Bit Definitions #define EIE_INTIE 0x80 #define EIE_PKTIE 0x40 #define EIE_DMAIE 0x20 #define EIE_LINKIE 0x10 #define EIE_TXIE 0x08 #define EIE_WOLIE 0x04 #define EIE_TXERIE 0x02 #define EIE_RXERIE 0x01 // ENC28J60 EIR Register Bit Definitions #define EIR_PKTIF 0x40 #define EIR_DMAIF 0x20 #define EIR_LINKIF 0x10 #define EIR_TXIF 0x08 #define EIR_WOLIF 0x04 #define EIR_TXERIF 0x02 #define EIR_RXERIF 0x01 // ENC28J60 ESTAT Register Bit Definitions #define ESTAT_INT 0x80 #define ESTAT_LATECOL 0x10 #define ESTAT_RXBUSY 0x04 #define ESTAT_TXABRT 0x02 #define ESTAT_CLKRDY 0x01 // ENC28J60 ECON2 Register Bit Definitions #define ECON2_AUTOINC 0x80 #define ECON2_PKTDEC 0x40 #define ECON2_PWRSV 0x20 #define ECON2_VRPS 0x08 // ENC28J60 ECON1 Register Bit Definitions #define ECON1_TXRST 0x80 #define ECON1_RXRST 0x40 #define ECON1_DMAST 0x20 #define ECON1_CSUMEN 0x10 #define ECON1_TXRTS 0x08 #define ECON1_RXEN 0x04 #define ECON1_BSEL1 0x02 #define ECON1_BSEL0 0x01 // ENC28J60 MACON1 Register Bit Definitions #define MACON1_LOOPBK 0x10 #define MACON1_TXPAUS 0x08 #define MACON1_RXPAUS 0x04 #define MACON1_PASSALL 0x02 #define MACON1_MARXEN 0x01 // ENC28J60 MACON2 Register Bit Definitions #define MACON2_MARST 0x80 #define MACON2_RNDRST 0x40 #define MACON2_MARXRST 0x08 #define MACON2_RFUNRST 0x04 #define MACON2_MATXRST 0x02 #define MACON2_TFUNRST 0x01 // ENC28J60 MACON3 Register Bit Definitions #define MACON3_PADCFG2 0x80 #define MACON3_PADCFG1 0x40 #define MACON3_PADCFG0 0x20 #define MACON3_TXCRCEN 0x10 #define MACON3_PHDRLEN 0x08 #define MACON3_HFRMLEN 0x04 #define MACON3_FRMLNEN 0x02 #define MACON3_FULDPX 0x01 // ENC28J60 MACON4 Register Bit Definitions #define MACON4_DEFER 0x40 #define MACON4_BPEN 0x20 #define MACON4_NOBKOFF 0x10 // ENC28J60 MICMD Register Bit Definitions #define MICMD_MIISCAN 0x02 #define MICMD_MIIRD 0x01 // ENC28J60 MISTAT Register Bit Definitions #define MISTAT_NVALID 0x04 #define MISTAT_SCAN 0x02 #define MISTAT_BUSY 0x01 // ENC28J60 PHY PHCON1 Register Bit Definitions #define PHCON1_PRST 0x8000 #define PHCON1_PLOOPBK 0x4000 #define PHCON1_PPWRSV 0x0800 #define PHCON1_PDPXMD 0x0100 // ENC28J60 PHY PHSTAT1 Register Bit Definitions #define PHSTAT1_PFDPX 0x1000 #define PHSTAT1_PHDPX 0x0800 #define PHSTAT1_LLSTAT 0x0004 #define PHSTAT1_JBSTAT 0x0002 // ENC28J60 PHY PHSTAT2H Register Bit Definitions #define PHSTAT2H_LSTAT 0x04 // ENC28J60 PHY PHCON2 Register Bit Definitions #define PHCON2_FRCLINK 0x4000 #define PHCON2_TXDIS 0x2000 #define PHCON2_JABBER 0x0400 #define PHCON2_HDLDIS 0x0100 // ENC28J60 Packet Control Byte Bit Definitions #define PKTCTRL_PHUGEEN 0x08 #define PKTCTRL_PPADEN 0x04 #define PKTCTRL_PCRCEN 0x02 #define PKTCTRL_POVERRIDE 0x01 // SPI operation codes #define ENC28J60_READ_CTRL_REG 0x00 #define ENC28J60_READ_BUF_MEM 0x3A #define ENC28J60_WRITE_CTRL_REG 0x40 #define ENC28J60_WRITE_BUF_MEM 0x7A #define ENC28J60_BIT_FIELD_SET 0x80 #define ENC28J60_BIT_FIELD_CLR 0xA0 #define ENC28J60_SOFT_RESET 0xFF // Buffer memory allocation within controlller. // Assuming the controller is slower than the network, we allocate one transmit // packet and leave the rest for receiving. // The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata #define RXSTART_INIT 0x0 #define RXSTOP_INIT (0x1FFF - 0x0600 - 2) // note: MUST be odd (see errata point 13) #define TXSTART_INIT (0x1FFF - 0x0600) // Maximum packet length: this software has a limit of 255 payload data bytes // and then there is some ethernet overhead (srd, dst, len, fcs) #define ENC28J60_MAX_PACKET_LEN ((uint16_t) 273) // field or packet lengths #define ETH_HEADER_LEN 14 #define ETH_CHECKSUM_LEN 4 #define ETH_ENVELOPE_LEN (ETH_HEADER_LEN + ETH_CHECKSUM_LEN) #define ETH_PAYLOAD_MIN 46 #define ETH_PAYLOAD_MAX 1500 #define ETH_PACKET_MIN 64 #define ETH_PACKET_MAX 1518 // field locations in ethernet packet #define ETH_DST_MAC_P 0 #define ETH_SRC_MAC_P 6 #define ETH_TYPE_H_P 12 #define ETH_TYPE_L_P 13 #define ENC28J60_HAS_PENDING_TRANSMIT_ON_TRANSMIT /* Static member initialization */ char RawEthernet::_arpReqHdr[10] = { 8, 6, 0, 1, 8, 0, 6, 4, 0, 1 }; /** * @brief Constructor * @note * @param mosi SPI master-out slave-in pin # * @param miso SPI master-in slave-out pin # * @param sclk SPI serial clock pin # * @param cs SPI chip select pin # * @retval */ RawEthernet::RawEthernet(PinName mosi, PinName miso, PinName sclk, PinName cs, uint8_t myMac[6], uint8_t myIp[4]) : _spi(mosi, miso, sclk), _cs(cs) { int i; for (i = 0; i < 6; i++) _mac[i] = myMac[i]; for (i = 0; i < 4; i++) _ip[i] = myIp[i]; } /** * @brief Read operation * @note Reads from ENC28J60 register * @param op operation code * @param address register address * @retval Register value */ uint8_t RawEthernet::readOp(uint8_t op, uint8_t address) { uint8_t result; _cs = 0; // issue read command _spi.write(op | (address & ADDR_MASK)); // read data result = _spi.write(0x00); // do dummy read if needed (for mac and mii, see datasheet page 29) if (address & 0x80) result = _spi.write(0x00); // release CS _cs = 1; return(result); } /** * @brief Write operation * @note Writes to ENC28J60 register * @param op operation code * @param address register address * @retval data data to be written */ void RawEthernet::writeOp(uint8_t op, uint8_t address, uint8_t data) { _cs = 0; // issue write command _spi.write(op | (address & ADDR_MASK)); // write data _spi.write(data); _cs = 1; } /** * @brief * @note * @param * @retval */ void RawEthernet::setBank(uint8_t address) { // set the bank (if needed) if ((address & BANK_MASK) != _bank) { // set the bank writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0)); writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5); _bank = (address & BANK_MASK); } } /** * @brief * @note * @param * @retval */ uint8_t RawEthernet::readReg(uint8_t address) { setBank(address); readOp(ENC28J60_READ_CTRL_REG, address); } /** * @brief * @note * @param * @retval */ void RawEthernet::writeReg(uint8_t address, uint8_t data) { setBank(address); writeOp(ENC28J60_WRITE_CTRL_REG, address, data); } /** * @brief * @note * @param * @retval */ void RawEthernet::readBuffer(uint16_t len, uint8_t* data) { _cs = 0; // issue read command _spi.write(ENC28J60_READ_BUF_MEM); while (len) { len--; // read data *data = _spi.write(0x00); data++; } //*data = '\0'; _cs = 1; } /** * @brief * @note * @param * @retval */ void RawEthernet::writeBuffer(uint16_t len, uint8_t* data) { _cs = 0; // issue write command _spi.write(ENC28J60_WRITE_BUF_MEM); while (len) { len--; // write data _spi.write(*data); data++; } _cs = 1; } /** * @brief * @note * @param * @retval */ uint16_t RawEthernet::phyReadH(uint8_t address) { // set the right address and start the register read operation writeReg(MIREGADR, address); writeReg(MICMD, MICMD_MIIRD); wait_us(15); // wait until the PHY read completes while (readReg(MISTAT) & MISTAT_BUSY); // reset reading bit setBank(2); writeReg(MICMD, 0x00); return(readReg(MIRDH)); } /** * @brief * @note * @param * @retval */ void RawEthernet::phyWrite(uint8_t address, uint16_t data) { setBank(2); // set the PHY register address writeReg(MIREGADR, address); // write the PHY data writeReg(MIWRL, data); writeReg(MIWRH, data >> 8); // wait until the PHY write completes setBank(3); while (readReg(MISTAT) & MISTAT_BUSY) { wait_us(15); } } /** * @brief * @note * @param * @retval */ void RawEthernet::linkTo(uint8_t remoteMac[6]) { for (int i = 0; i < 6; i++) _remoteMac[i] = remoteMac[i]; _spi.format(8, 0); // 8bit, mode 0 _spi.frequency(7000000); // 7MHz wait(1); // 1 second for stable state // initialize I/O _cs = 1; // perform system reset writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); wait_ms(50); // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait. //while(!(read(ESTAT) & ESTAT_CLKRDY)); // do bank 0 stuff // initialize receive buffer // 16-bit transfers, must write low byte first // set receive buffer start address _nextPacketPtr = RXSTART_INIT; // Rx start writeReg(ERXSTL, RXSTART_INIT & 0xFF); writeReg(ERXSTH, RXSTART_INIT >> 8); // set receive pointer address writeReg(ERXRDPTL, RXSTART_INIT & 0xFF); writeReg(ERXRDPTH, RXSTART_INIT >> 8); // RX end writeReg(ERXNDL, RXSTOP_INIT & 0xFF); writeReg(ERXNDH, RXSTOP_INIT >> 8); // TX start writeReg(ETXSTL, TXSTART_INIT & 0xFF); writeReg(ETXSTH, TXSTART_INIT >> 8); // TX end (initialize for a packet with a payload of 1 byte) uint16_t address = (TXSTART_INIT + ETH_HEADER_LEN + 1); writeReg(ETXNDL, address & 0xFF); writeReg(ETXNDH, address >> 8); // prepare the parts of the transmit packet that never change // write per-packet control byte (0x00 means use macon3 settings) writeReg(EWRPTL, (TXSTART_INIT) & 0xFF); writeReg(EWRPTH, (TXSTART_INIT) >> 8); writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); // write broadcast address as DST MAC uint8_t i = 0; while (i < 6) { writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0xFF); i++; } // set our MAC address as the SRC MAC into the transmit buffer // set the write pointer to start of transmit buffer area writeBuffer(6, const_cast<uint8_t*>(_mac)); // First EtherType/length byte is always 0, initialize second byte to 1 writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x01); // do bank 1 stuff, packet filter: setBank(1); // only allow unicast packets destined for us and that have a correct CRC writeReg(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN); // do bank 2 stuff setBank(2); // enable MAC receive, disable flow control (only needed in full-duplex) writeReg(MACON1, MACON1_MARXEN); // bring MAC out of reset writeReg(MACON2, 0x00); // enable automatic padding to 60bytes and CRC operations // also, force half-duplex operation writeReg(MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); // half-duplex only: back-off settings writeReg(MACON4, MACON4_DEFER | MACON4_BPEN | MACON4_NOBKOFF); // set the maximum packet size which the controller will accept // do not send packets longer than MAX_FRAMELEN: writeReg(MAMXFLL, ENC28J60_MAX_PACKET_LEN & 0xFF); writeReg(MAMXFLH, ENC28J60_MAX_PACKET_LEN >> 8); // set inter-frame gap (non-back-to-back) writeReg(MAIPGL, 0x12); writeReg(MAIPGH, 0x0C); // set inter-frame gap (back-to-back) writeReg(MABBIPG, 0x12); // do bank 3 stuff // write MAC address // NOTE: MAC address in ENC28J60 is byte-backward writeReg(MAADR5, _mac[0]); writeReg(MAADR4, _mac[1]); writeReg(MAADR3, _mac[2]); writeReg(MAADR2, _mac[3]); writeReg(MAADR1, _mac[4]); writeReg(MAADR0, _mac[5]); // no loopback of transmitted frames phyWrite(PHCON2, PHCON2_HDLDIS); // switch to bank 0 setBank(ECON1); // enable interrutps writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE); // enable packet reception writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); // change clkout from 6.25MHz to 12.5MHz writeReg(ECOCON, 2 & 0x7); wait_us(60); /* magjack leds configuration, see enc28j60 datasheet, page 11 */ // LEDA=green, LEDB=yellow, // LEDA=links status, LEDB=receive/transmit // phyWrite(PHLCON,0b0000 0100 0111 0110); phyWrite(PHLCON, 0x476); // wait until the link is up, then send a gratuitous ARP request // to inform any conected switch about my existence while (!isLinkUp()); wait_ms(50); // send gratuitous ARP request gratuitousArp(); } /** * @brief * @note * @param * @retval */ bool RawEthernet::isLinkUp(void) { return(phyReadH(PHSTAT2) & PHSTAT2H_LSTAT); } /** * @brief * @note * @param * @retval */ uint8_t RawEthernet::receive(uint8_t* buf, uint8_t maxlen) { uint16_t len; uint16_t currentPacketPtr = _nextPacketPtr; uint16_t address; uint16_t framelen; // check if a packet has been received and buffered setBank(1); if (readReg(EPKTCNT) == 0) return(0); setBank(0); // Somehow, the read pointer is NOT already at the start of the next packet // even though we leave it in that state writeReg(ERDPTL, (_nextPacketPtr & 0xff)); writeReg(ERDPTH, (_nextPacketPtr) >> 8); // Read the next packet pointer _nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); _nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; // read the frame length framelen = readOp(ENC28J60_READ_BUF_MEM, 0); framelen |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; if (maxlen > framelen - 14) { // subtract eth source, dest and length fields maxlen = framelen - 14; } // Read EtherType (we use this as a length field) (note +6 for receive vectors) // Set read pointer (taking care of wrap-around) address = currentPacketPtr + ETH_TYPE_H_P + 6; if (address > RXSTOP_INIT) { address -= (RXSTOP_INIT - RXSTART_INIT + 1); } writeReg(ERDPTL, (address & 0xff)); writeReg(ERDPTH, (address) >> 8); readBuffer(6, _remoteMac); // A value of less than 0x05dc in the EtherType has to be interpreted as length. // This is what we do here. // The length is 16 bit. The upper 8 bits must be zero // otherwise it is not our packet. len = readOp(ENC28J60_READ_BUF_MEM, 0); if (len == 0) { // read the lower byte of the length field len = readOp(ENC28J60_READ_BUF_MEM, 0); // limit retrieve length to maxlen, ignoring anything else if (len > maxlen) { len = maxlen; } // copy payload data from the receive buffer readBuffer(len, buf); } else len = 0; // Move the RX read pointer to the start of the next received packet // This frees the memory we just read out. // However, compensate for the errata point 13, rev B4: enver write an even address! if ((_nextPacketPtr - 1 < RXSTART_INIT) || (_nextPacketPtr - 1 > RXSTOP_INIT)) { writeReg(ERXRDPTL, (RXSTOP_INIT) & 0xFF); writeReg(ERXRDPTH, (RXSTOP_INIT) >> 8); } else { writeReg(ERXRDPTL, (_nextPacketPtr - 1) & 0xFF); writeReg(ERXRDPTH, (_nextPacketPtr - 1) >> 8); } // Decrement the packet counter indicate we are done with this packet writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); return(len); } // sends gratuitous ARP request (spontaneous arp request) to teach // switches what our mac is. void RawEthernet::gratuitousArp() { uint8_t i = 0; uint16_t address; setBank(0); // (control byte, and SRC MAC have already been set during init) #ifdef ENC28J60_HAS_PENDING_TRANSMIT_ON_TRANSMIT // Check no transmit in progress while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) { // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. if ((readReg(EIR) & EIR_TXERIF)) { writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); } } #else // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. if ((readReg(EIR) & EIR_TXERIF)) { writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); } #endif // Set the write pointer to start of transmit buffer area // +1 to skip the per packet control byte and write directly the mac // The control byte was set to zero during initialisation and remains like that. writeReg(EWRPTL, (TXSTART_INIT + 1) & 0xFF); writeReg(EWRPTH, (TXSTART_INIT + 1) >> 8); // write a broadcase destination mac (all ff): while (i < 6) { writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0xFF); i++; } // The mac in the ethernet field does not need to be changed. // Set the write pointer to the first byte of the EtherType field address = TXSTART_INIT + 1 + ETH_TYPE_H_P; writeReg(EWRPTL, address & 0xFF); writeReg(EWRPTH, address >> 8); // there are 10 fixed bytes in the arp request i = 0; while (i < 10) { writeOp(ENC28J60_WRITE_BUF_MEM, 0, (_arpReqHdr[i])); i++; } i = 0; while (i < 6) { writeOp(ENC28J60_WRITE_BUF_MEM, 0, _mac[i]); i++; } i = 0; while (i < 4) { writeOp(ENC28J60_WRITE_BUF_MEM, 0, _ip[i]); i++; } // target data: i = 0; while (i < 6) { writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0xff); i++; } // to self, for gratuitous arp: i = 0; while (i < 4) { writeOp(ENC28J60_WRITE_BUF_MEM, 0, _ip[i]); i++; } // Set the TXND pointer to correspond to the payload size given address = (TXSTART_INIT + 42); writeReg(ETXNDL, address & 0xFF); writeReg(ETXNDH, address >> 8); // Send the contents of the transmit buffer onto the network writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); } /** * @brief * @note * @param * @retval */ void RawEthernet::send(uint8_t* buf, uint8_t len) { uint16_t address; setBank(0); // (control byte, and SRC MAC have already been set during init) #ifdef ENC28J60_HAS_PENDING_TRANSMIT_ON_TRANSMIT // Check no transmit in progress while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) { // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. if ((readReg(EIR) & EIR_TXERIF)) { writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); } } #else // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. if ((readReg(EIR) & EIR_TXERIF)) { writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); } #endif // Set the write pointer to start of transmit buffer area // +1 to skip the per packet control byte and write directly the mac // The control byte was set to zero during initialisation and remains like that. writeReg(EWRPTL, (TXSTART_INIT + 1) & 0xFF); writeReg(EWRPTH, (TXSTART_INIT + 1) >> 8); writeBuffer(6, _remoteMac); // Set the write pointer to the first byte of the EtherType field // (field after the mac address). This is the 802.3 length field. address = TXSTART_INIT + 1 + ETH_TYPE_H_P; writeReg(EWRPTL, address & 0xFF); writeReg(EWRPTH, address >> 8); // write the length of the data in the ethernet type field. // The type field to be interpreted by the receiver as ieee802.3 length if // the value is less than 0x05dc (see e.g. http://www.cavebear.com/archive/cavebear/Ethernet/type.html ): writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0); writeOp(ENC28J60_WRITE_BUF_MEM, 0, len); // Copy the payload into the transmit buffer writeBuffer(len, buf); // remove dest mac and write the rest // Set the TXND pointer to correspond to the payload size given address = (TXSTART_INIT + ETH_HEADER_LEN + len); writeReg(ETXNDL, address & 0xFF); writeReg(ETXNDH, address >> 8); // Send the contents of the transmit buffer onto the network writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); } /** * @brief * @note * @param * @retval */ uint8_t RawEthernet::getRev(void) { uint8_t rev; rev = readReg(EREVID); // microchip forgott to step the number on the silcon when they // released the revision B7. 6 is now rev B7. We still have // to see what they do when they release B8. At the moment // there is no B8 out yet if (rev > 5) rev++; return(rev); }