/* Enc28J60NetworkClass.cpp — Bitbang-SPI version for non-hardware-SPI pins Based on UIPEthernet by Norbert Truchsess, GPL V2 Modification: Hardware SPI replaced with bitbang SPI. Target: STM32F103RBT6 with ENC28J60 on PC6(SCK)/PC7(MISO)/PC8(CS)/PC9(MOSI) */ #include "Enc28J60Network.h" #include #include "config.h" // ETH_SCK_PIN / ETH_MOSI_PIN / ETH_MISO_PIN // Note: intentionally NOT included — this version uses bitbang SPI extern "C" { #include "enc28j60.h" #include "uip.h" } // ============================================================ // Bitbang SPI — SPI Mode 0 (CPOL=0, CPHA=0) // Pin definitions come from project include/config.h via build system // ============================================================ #ifndef ETH_SCK_PIN #error "ETH_SCK_PIN not defined — check include/config.h" #endif static bool _spi_ready = false; static void bitbang_init() { if (_spi_ready) return; pinMode(ETH_SCK_PIN, OUTPUT); digitalWrite(ETH_SCK_PIN, LOW); pinMode(ETH_MOSI_PIN, OUTPUT); digitalWrite(ETH_MOSI_PIN, LOW); pinMode(ETH_MISO_PIN, INPUT); _spi_ready = true; } static inline uint8_t bitbang_transfer(uint8_t out) { uint8_t in = 0; for (int8_t i = 7; i >= 0; i--) { digitalWrite(ETH_MOSI_PIN, (out >> i) & 1); digitalWrite(ETH_SCK_PIN, HIGH); in = (in << 1) | digitalRead(ETH_MISO_PIN); digitalWrite(ETH_SCK_PIN, LOW); } return in; } // ============================================================ // set CS to 0 = active #define CSACTIVE digitalWrite(csPin, LOW) // set CS to 1 = passive #define CSPASSIVE digitalWrite(csPin, HIGH) bool Enc28J60Network::spiInitialized = false; uint8_t Enc28J60Network::csPin = SS; uint16_t Enc28J60Network::nextPacketPtr; uint8_t Enc28J60Network::bank = 0xff; struct memblock Enc28J60Network::receivePkt; void Enc28J60Network::initSPI() { if (spiInitialized) return; bitbang_init(); pinMode(csPin, OUTPUT); CSPASSIVE; spiInitialized = true; } bool Enc28J60Network::init(uint8_t* macaddr) { MemoryPool::init(); initSPI(); // perform system reset writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); delay(50); nextPacketPtr = RXSTART_INIT; writeRegPair(ERXSTL, RXSTART_INIT); writeRegPair(ERXRDPTL, RXSTART_INIT); writeRegPair(ERXNDL, RXSTOP_INIT); writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN); writeRegPair(EPMM0, 0x303f); writeRegPair(EPMCSL, 0xf7f9); writeRegPair(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); writeRegPair(MAIPGL, 0x0C12); writeReg(MABBIPG, 0x12); writeRegPair(MAMXFLL, MAX_FRAMELEN); writeReg(MAADR5, macaddr[0]); writeReg(MAADR4, macaddr[1]); writeReg(MAADR3, macaddr[2]); writeReg(MAADR2, macaddr[3]); writeReg(MAADR1, macaddr[4]); writeReg(MAADR0, macaddr[5]); phyWrite(PHCON2, PHCON2_HDLDIS); setBank(ECON1); writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); phyWrite(PHLCON, 0x476); return getrev(); } memhandle Enc28J60Network::receivePacket() { uint8_t rxstat; uint16_t len; if (readReg(EPKTCNT) != 0) { uint16_t readPtr = nextPacketPtr+6 > RXSTOP_INIT ? nextPacketPtr+6-((RXSTOP_INIT + 1)-RXSTART_INIT) : nextPacketPtr+6; writeRegPair(ERDPTL, nextPacketPtr); nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; len = readOp(ENC28J60_READ_BUF_MEM, 0); len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; len -= 4; rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); if ((rxstat & 0x80) != 0) { receivePkt.begin = readPtr; receivePkt.size = len; return UIP_RECEIVEBUFFERHANDLE; } setERXRDPT(); } return (NOBLOCK); } void Enc28J60Network::setERXRDPT() { writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr-1); } memaddress Enc28J60Network::blockSize(memhandle handle) { return handle == NOBLOCK ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; } bool Enc28J60Network::sendPacket(memhandle handle) { memblock *packet = &blocks[handle]; uint16_t start = packet->begin; uint16_t end = start + packet->size - 1 - UIP_SENDBUFFER_PADDING; writeByte(start, 0); writeRegPair(ETXSTL, start); writeRegPair(ETXNDL, end); bool success = false; for (uint8_t retry = 0; retry < TX_COLLISION_RETRY_COUNT; retry++) { writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF | EIR_TXIF); writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); uint8_t eir; while (((eir = readReg(EIR)) & (EIR_TXIF | EIR_TXERIF)) == 0); writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); success = ((eir & EIR_TXERIF) == 0); if (success) break; uint8_t tsv4 = readByte(end + 4); if (!(tsv4 & 0b00100000)) break; } return success; } uint16_t Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) { memblock *packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; memaddress start = handle == UIP_RECEIVEBUFFERHANDLE && packet->begin + position > RXSTOP_INIT ? packet->begin + position-((RXSTOP_INIT + 1)-RXSTART_INIT) : packet->begin + position; writeRegPair(ERDPTL, start); if (len > packet->size - position) len = packet->size - position; return len; } uint16_t Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) { len = setReadPtr(handle, position, len); readBuffer(len, buffer); return len; } uint16_t Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) { memblock *packet = &blocks[handle]; uint16_t start = packet->begin + position; if (len > packet->size - position) len = packet->size - position; if (len == 0) return 0; writeRegPair(EWRPTL, start); writeBuffer(len, buffer); return len; } uint8_t Enc28J60Network::readByte(uint16_t addr) { writeRegPair(ERDPTL, addr); CSACTIVE; bitbang_transfer(ENC28J60_READ_BUF_MEM); uint8_t c = bitbang_transfer(0x00); CSPASSIVE; return c; } void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) { writeRegPair(EWRPTL, addr); CSACTIVE; bitbang_transfer(ENC28J60_WRITE_BUF_MEM); bitbang_transfer(data); CSPASSIVE; } void Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) { memblock *dest = &blocks[dest_pkt]; memblock *src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; memaddress start = src_pkt == UIP_RECEIVEBUFFERHANDLE && src->begin + src_pos > RXSTOP_INIT ? src->begin + src_pos - ((RXSTOP_INIT + 1) - RXSTART_INIT) : src->begin + src_pos; enc28J60_mempool_block_move_callback(dest->begin + dest_pos, start, len); } void enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) { if (len == 1) { Enc28J60Network::writeByte(dest, Enc28J60Network::readByte(src)); } else { len += src - 1; Enc28J60Network::writeRegPair(EDMASTL, src); Enc28J60Network::writeRegPair(EDMADSTL, dest); if ((src <= RXSTOP_INIT) && (len > RXSTOP_INIT)) len -= ((RXSTOP_INIT + 1)-RXSTART_INIT); Enc28J60Network::writeRegPair(EDMANDL, len); Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); while (Enc28J60Network::readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); } } void Enc28J60Network::freePacket() { setERXRDPT(); } uint8_t Enc28J60Network::readOp(uint8_t op, uint8_t address) { CSACTIVE; bitbang_transfer(op | (address & ADDR_MASK)); if (address & 0x80) bitbang_transfer(0x00); // dummy read for MAC/MII uint8_t c = bitbang_transfer(0x00); CSPASSIVE; return c; } void Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) { CSACTIVE; bitbang_transfer(op | (address & ADDR_MASK)); bitbang_transfer(data); CSPASSIVE; } void Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) { CSACTIVE; bitbang_transfer(ENC28J60_READ_BUF_MEM); while (len--) *data++ = bitbang_transfer(0x00); CSPASSIVE; } void Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) { CSACTIVE; bitbang_transfer(ENC28J60_WRITE_BUF_MEM); while (len--) bitbang_transfer(*data++); CSPASSIVE; } void Enc28J60Network::setBank(uint8_t address) { if ((address & BANK_MASK) != 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); } } uint8_t Enc28J60Network::readReg(uint8_t address) { setBank(address); return readOp(ENC28J60_READ_CTRL_REG, address); } void Enc28J60Network::writeReg(uint8_t address, uint8_t data) { setBank(address); writeOp(ENC28J60_WRITE_CTRL_REG, address, data); } void Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) { setBank(address); writeOp(ENC28J60_WRITE_CTRL_REG, address, (data & 0xFF)); writeOp(ENC28J60_WRITE_CTRL_REG, address+1, (data >> 8)); } void Enc28J60Network::phyWrite(uint8_t address, uint16_t data) { writeReg(MIREGADR, address); writeRegPair(MIWRL, data); while (readReg(MISTAT) & MISTAT_BUSY) delayMicroseconds(15); } uint16_t Enc28J60Network::phyRead(uint8_t address) { writeReg(MIREGADR, address); writeReg(MICMD, MICMD_MIIRD); while (readReg(MISTAT) & MISTAT_BUSY) delayMicroseconds(15); writeReg(MICMD, 0); return (readReg(MIRDL) | readReg(MIRDH) << 8); } void Enc28J60Network::clkout(uint8_t clk) { writeReg(ECOCON, clk & 0x7); } uint8_t Enc28J60Network::getrev(void) { initSPI(); return readReg(EREVID); } uint16_t Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) { uint16_t t; len = setReadPtr(handle, pos, len) - 1; CSACTIVE; bitbang_transfer(ENC28J60_READ_BUF_MEM); uint16_t i; for (i = 0; i < len; i += 2) { t = bitbang_transfer(0x00) << 8; t += bitbang_transfer(0x00); sum += t; if (sum < t) sum++; } if (i == len) { t = (bitbang_transfer(0x00) << 8) + 0; sum += t; if (sum < t) sum++; } CSPASSIVE; return sum; } void Enc28J60Network::powerOff() { writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); delay(50); writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); delay(50); writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); } void Enc28J60Network::powerOn() { writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); delay(50); writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); delay(50); } bool Enc28J60Network::linkStatus() { return (phyRead(PHSTAT2) & 0x0400) > 0; }