Created
August 2, 2020 20:39
-
-
Save possan/f01fd83f0eecb1a5a68644b06cf94e9f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
uint8_t txpin1 = 4; | |
uint8_t txpin2 = 5; | |
uint8_t txpen = 6; | |
uint8_t powerpin = A3; | |
uint32_t counter = 0; | |
uint8_t phy_address = 1; // apparently hardcoded on board | |
uint8_t smi_clock = 8; | |
uint8_t smi_data = 9; | |
#define CLK_DELAY_MICROS 1 | |
//LAN8720 PHY registers | |
#define LAN8720_BMCR 0x00 | |
#define LAN8720_BMSR 0x01 | |
#define LAN8720_PHYID1 0x02 | |
#define LAN8720_PHYID2 0x03 | |
#define set 0x04 | |
#define LAN8720_ANLPAR 0x05 | |
#define LAN8720_ANER 0x06 | |
#define LAN8720_MCSR 0x11 | |
#define LAN8720_SMR 0x12 | |
#define LAN8720_SECR 0x1A | |
#define LAN8720_SCSIR 0x1B | |
#define LAN8720_ISR 0x1D | |
#define LAN8720_IMR 0x1E | |
#define LAN8720_PSCSR 0x1F | |
//Basic Control register | |
#define LAN8720_BMCR_RESET 0x8000 | |
#define LAN8720_BMCR_LOOPBACK 0x4000 | |
#define LAN8720_BMCR_SPEED_SEL 0x2000 | |
#define LAN8720_BMCR_AN_EN 0x1000 | |
#define LAN8720_BMCR_POWER_DOWN 0x0800 | |
#define LAN8720_BMCR_ISOLATE 0x0400 | |
#define LAN8720_BMCR_RESTART_AN 0x0200 | |
#define LAN8720_BMCR_DUPLEX_MODE 0x0100 | |
//Basic Status register | |
#define LAN8720_BMSR_100BT4 0x8000 | |
#define LAN8720_BMSR_100BTX_FD 0x4000 | |
#define LAN8720_BMSR_100BTX_HD 0x2000 | |
#define LAN8720_BMSR_10BT_FD 0x1000 | |
#define LAN8720_BMSR_10BT_HD 0x0800 | |
#define LAN8720_BMSR_100BT2_FD 0x0400 | |
#define LAN8720_BMSR_100BT2_HD 0x0200 | |
#define LAN8720_BMSR_EXTENDED_STATUS 0x0100 | |
#define LAN8720_BMSR_AN_COMPLETE 0x0020 | |
#define LAN8720_BMSR_REMOTE_FAULT 0x0010 | |
#define LAN8720_BMSR_AN_CAPABLE 0x0008 | |
#define LAN8720_BMSR_LINK_STATUS 0x0004 | |
#define LAN8720_BMSR_JABBER_DETECT 0x0002 | |
#define LAN8720_BMSR_EXTENDED_CAPABLE 0x0001 | |
//PHY Identifier 1 register | |
#define LAN8720_PHYID1_PHY_ID_MSB 0xFFFF | |
#define LAN8720_PHYID1_PHY_ID_MSB_DEFAULT 0x0007 | |
//PHY Identifier 2 register | |
#define LAN8720_PHYID2_PHY_ID_LSB 0xFC00 | |
#define LAN8720_PHYID2_PHY_ID_LSB_DEFAULT 0xC000 | |
#define LAN8720_PHYID2_MODEL_NUM 0x03F0 | |
#define LAN8720_PHYID2_MODEL_NUM_DEFAULT 0x00F0 | |
#define LAN8720_PHYID2_REVISION_NUM 0x000F | |
//Auto-Negotiation Advertisement register | |
#define LAN8720_ANAR_REMOTE_FAULT 0x2000 | |
#define LAN8720_ANAR_PAUSE 0x0C00 | |
#define LAN8720_ANAR_100BTX_FD 0x0100 | |
#define LAN8720_ANAR_100BTX_HD 0x0080 | |
#define LAN8720_ANAR_10BT_FD 0x0040 | |
#define LAN8720_ANAR_10BT_HD 0x0020 | |
#define LAN8720_ANAR_SELECTOR 0x001F | |
#define LAN8720_ANAR_SELECTOR_DEFAULT 0x0001 | |
//Auto-Negotiation Link Partner Ability register | |
#define LAN8720_ANLPAR_NEXT_PAGE 0x8000 | |
#define LAN8720_ANLPAR_ACK 0x4000 | |
#define LAN8720_ANLPAR_REMOTE_FAULT 0x2000 | |
#define LAN8720_ANLPAR_PAUSE 0x0400 | |
#define LAN8720_ANLPAR_100BT4 0x0200 | |
#define LAN8720_ANLPAR_100BTX_FD 0x0100 | |
#define LAN8720_ANLPAR_100BTX_HD 0x0080 | |
#define LAN8720_ANLPAR_10BT_FD 0x0040 | |
#define LAN8720_ANLPAR_10BT_HD 0x0020 | |
#define LAN8720_ANLPAR_SELECTOR 0x001F | |
#define LAN8720_ANLPAR_SELECTOR_DEFAULT 0x0001 | |
//Auto-Negotiation Expansion register | |
#define LAN8720_ANER_PAR_DETECT_FAULT 0x0010 | |
#define LAN8720_ANER_LP_NEXT_PAGE_ABLE 0x0008 | |
#define LAN8720_ANER_NEXT_PAGE_ABLE 0x0004 | |
#define LAN8720_ANER_PAGE_RECEIVED 0x0002 | |
#define LAN8720_ANER_LP_AN_ABLE 0x0001 | |
//Mode Control/Status register | |
#define LAN8720_MCSR_EDPWRDOWN 0x2000 | |
#define LAN8720_MCSR_FARLOOPBACK 0x0200 | |
#define LAN8720_MCSR_ALTINT 0x0040 | |
#define LAN8720_MCSR_ENERGYON 0x0002 | |
//Special Modes register | |
#define LAN8720_SMR_MODE 0x00E0 | |
#define LAN8720_SMR_PHYAD 0x001F | |
//Symbol Error Counter register | |
#define LAN8720_SECR_SYM_ERR_CNT 0xFFFF | |
//Special Control/Status Indication register | |
#define LAN8720_SCSIR_AMDIXCTRL 0x8000 | |
#define LAN8720_SCSIR_CH_SELECT 0x2000 | |
#define LAN8720_SCSIR_SQEOFF 0x0800 | |
#define LAN8720_SCSIR_XPOL 0x0010 | |
//Interrupt Source register | |
#define LAN8720_ISR_ENERGYON 0x0080 | |
#define LAN8720_ISR_AN_COMPLETE 0x0040 | |
#define LAN8720_ISR_REMOTE_FAULT 0x0020 | |
#define LAN8720_ISR_LINK_DOWN 0x0010 | |
#define LAN8720_ISR_AN_LP_ACK 0x0008 | |
#define LAN8720_ISR_PAR_DETECT_FAULT 0x0004 | |
#define LAN8720_ISR_AN_PAGE_RECEIVED 0x0002 | |
//Interrupt Mask register | |
#define LAN8720_IMR_ENERGYON 0x0080 | |
#define LAN8720_IMR_AN_COMPLETE 0x0040 | |
#define LAN8720_IMR_REMOTE_FAULT 0x0020 | |
#define LAN8720_IMR_LINK_DOWN 0x0010 | |
#define LAN8720_IMR_AN_LP_ACK 0x0008 | |
#define LAN8720_IMR_PAR_DETECT_FAULT 0x0004 | |
#define LAN8720_IMR_AN_PAGE_RECEIVED 0x0002 | |
//PHY Special Control/Status register | |
#define LAN8720_PSCSR_AUTODONE 0x1000 | |
#define LAN8720_PSCSR_HCDSPEED 0x001C | |
#define LAN8720_PSCSR_HCDSPEED_10BT_HD 0x0004 | |
#define LAN8720_PSCSR_HCDSPEED_100BTX_HD 0x0008 | |
#define LAN8720_PSCSR_HCDSPEED_10BT_FD 0x0014 | |
#define LAN8720_PSCSR_HCDSPEED_100BTX_FD 0x0018 | |
// Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/00002165b.pdf | |
// SMI Protocol: Section 3.5, Page 23 | |
// SMI Registers: Section 4.2, Page 42 | |
// More code: https://www.oryx-embedded.com/doc/lan8720__driver_8c_source.html | |
void inline smi_sendclock(uint8_t bit) { | |
digitalWrite(smi_clock, bit); | |
delayMicroseconds(CLK_DELAY_MICROS); | |
} | |
void inline smi_sendbit(uint8_t bit) { | |
digitalWrite(smi_data, bit); | |
delayMicroseconds(CLK_DELAY_MICROS); | |
smi_sendclock(1); | |
smi_sendclock(0); | |
} | |
void serial_print_bin(char *prefix, uint16_t value) { | |
Serial.print(prefix); | |
Serial.print("="); | |
for (int b = 15; b >= 0; b--) { | |
if (value & (1 << b)) { | |
Serial.print("1 "); | |
} else { | |
Serial.print("0 "); | |
} | |
} | |
// Serial.print(value, BIN); | |
// Serial.print(""); | |
} | |
uint8_t inline smi_readbit() { | |
delayMicroseconds(CLK_DELAY_MICROS); | |
uint8_t bit = digitalRead(smi_data); | |
smi_sendclock(1); | |
smi_sendclock(0); | |
return bit; | |
} | |
void smi_poke(uint8_t phy_address, uint8_t register_address, uint16_t value) { | |
pinMode(smi_data, OUTPUT); | |
digitalWrite(smi_data, 0); | |
digitalWrite(smi_clock, 0); | |
// Preamble | |
for (int i = 0; i < 32; i++) { | |
smi_sendbit(1); | |
} | |
// Start signal | |
smi_sendbit(0); | |
smi_sendbit(1); | |
// OP Code | |
smi_sendbit(0); | |
smi_sendbit(1); | |
// PHY Address | |
for (int b = 4; b >= 0; b--) { | |
smi_sendbit(phy_address & (1 << b)); | |
} | |
// Register Address | |
for (int b = 4; b >= 0; b--) { | |
smi_sendbit(register_address & (1 << b)); | |
} | |
digitalWrite(smi_data, 1); | |
// Turnaround | |
smi_sendclock(1); | |
smi_sendclock(0); | |
digitalWrite(smi_data, 0); | |
smi_sendclock(1); | |
smi_sendclock(0); | |
// Write data | |
for (int b = 15; b >= 0; b--) { | |
smi_sendbit(value & (1 << b)); | |
} | |
digitalWrite(smi_data, 0); | |
digitalWrite(smi_clock, 0); | |
pinMode(smi_data, OUTPUT); | |
delay(1); | |
} | |
uint16_t smi_peek(uint8_t phy_address, uint8_t register_address) { | |
uint16_t output = 0; | |
pinMode(smi_data, OUTPUT); | |
digitalWrite(smi_data, 0); | |
digitalWrite(smi_clock, 0); | |
// Preamble | |
for (int i = 0; i < 32; i++) { | |
smi_sendbit(1); | |
} | |
// Start signal | |
smi_sendbit(0); | |
smi_sendbit(1); | |
// OP Code | |
smi_sendbit(1); | |
smi_sendbit(0); | |
// PHY Address | |
for (int b = 4; b >= 0; b--) { | |
smi_sendbit(phy_address & (1 << b)); | |
} | |
// Register Address | |
for (int b = 4; b >= 0; b--) { | |
smi_sendbit(register_address & (1 << b)); | |
} | |
// Turnaround | |
smi_sendclock(1); | |
pinMode(smi_data, INPUT); | |
smi_sendclock(0); | |
smi_sendclock(1); | |
smi_sendclock(0); | |
// Write data | |
for (int b = 15; b >= 0; b--) { | |
uint8_t bit = smi_readbit(); | |
if (bit) output += (1 << b); | |
} | |
pinMode(smi_data, OUTPUT); | |
digitalWrite(smi_data, 0); | |
digitalWrite(smi_clock, 0); | |
delay(1); | |
return output; | |
} | |
// the setup routine runs once when you press reset: | |
void setup() { | |
uint16_t dummy; | |
// initialize the digital pin as an output. | |
pinMode(txpin1, OUTPUT); | |
pinMode(txpin2, OUTPUT); | |
pinMode(txpen, OUTPUT); | |
pinMode(powerpin, OUTPUT); | |
pinMode(smi_clock, OUTPUT); | |
pinMode(smi_data, OUTPUT); | |
Serial.begin(57600); | |
while (!Serial) {} | |
delay(1000); | |
Serial.print("Softresetting PHY\n"); | |
digitalWrite(powerpin, 1); | |
// Soft reset | |
smi_poke(phy_address, 0, 0x8000); | |
do { | |
dummy = smi_peek(phy_address, 0); | |
serial_print_bin("status", dummy); | |
Serial.print("\n"); | |
delay(10); | |
} while(dummy & 0x8000); | |
delay(100); | |
// Disable 10mbit | |
dummy = smi_peek(phy_address, 4); | |
serial_print_bin("speed change read #4", dummy); | |
Serial.print("\n"); | |
dummy &= ~(96); // bit 5 + 6 | |
serial_print_bin("speed change write #4", dummy); | |
Serial.print("\n"); | |
smi_poke(phy_address, 4, dummy); | |
do { | |
dummy = smi_peek(phy_address, 4); | |
serial_print_bin("speed change poll #4", dummy); | |
Serial.print("\n"); | |
delay(100); | |
} while ((dummy & 96) != 0); | |
dummy = smi_peek(phy_address, 0); | |
serial_print_bin("disable autoneg read #0", dummy); | |
Serial.print("\n"); | |
// dummy = 0; | |
dummy &= ~(1 << 12); // enable autonegotiaton | |
// dummy |= (1 << 9); // restart autoneg. | |
serial_print_bin("disable autoneg write #0", dummy); | |
Serial.print("\n"); | |
smi_poke(phy_address, 0, dummy); | |
// do { | |
dummy = smi_peek(phy_address, 0); | |
serial_print_bin("disable autoneg poll #0", dummy); | |
Serial.print("\n"); | |
// delay(100); | |
// } while (!(dummy & (1 << 13))); | |
a | |
dummy = smi_peek(phy_address, 0); | |
serial_print_bin("set speed read #0", dummy); | |
Serial.print("\n"); | |
// dummy = 0; | |
dummy |= (1 << 13); // 100mbit | |
dummy &= ~(1 << 10); // isolate? | |
dummy |= (1 << 8); // full duplex | |
// dummy |= (1 << 9); // restart autoneg. | |
serial_print_bin("set speed write #0", dummy); | |
Serial.print("\n"); | |
smi_poke(phy_address, 0, dummy); | |
// do { | |
dummy = smi_peek(phy_address, 0); | |
serial_print_bin("set speed poll #0", dummy); | |
Serial.print("\n"); | |
// delay(100); | |
// } while (!(dummy & (1 << 13))); | |
// | |
// dummy = smi_peek(phy_address, 0); | |
// serial_print_bin("restart autoneg read #0", dummy); | |
// Serial.print("\n"); | |
// dummy |= (1 << 9); // restart autoneg. | |
// serial_print_bin("restart autoneg write #0", dummy); | |
// Serial.print("\n"); | |
// smi_poke(phy_address, 0, dummy); | |
// do { | |
// dummy = smi_peek(phy_address, 0); | |
// serial_print_bin("restart autoneg poll #4", dummy); | |
// Serial.print("\n"); | |
// delay(100); | |
// } while (dummy & (1 << 9)); | |
// smi_poke(phy_address, LAN8720_IMR, LAN8720_IMR_AN_COMPLETE | | |
// LAN8720_IMR_LINK_DOWN); | |
// | |
// dummy = smi_peek(phy_address, 0); | |
// dummy &= ~(1 << 12); // disable autonegotiaton | |
// dummy |= (1 << 13); // 100mbit | |
// dummy &= ~(1 << 8); // half duplex | |
// smi_poke(phy_address, 0, dummy); | |
// restart auto-neg. | |
// dummy = 1 << 9; | |
// smi_poke(phy_address, 0, dummy); | |
// do { | |
// dummy = smi_peek(phy_address, 0); | |
// Serial.print("Waiting for reset status="); | |
// Serial.print(dummy); | |
// Serial.print("\n"); | |
// delay(100); | |
// } while(dummy & 0x8000); | |
for (int k = 0; k < 31; k++) { | |
dummy = smi_peek(phy_address, k); | |
Serial.print("register #"); | |
Serial.print(k, DEC); | |
serial_print_bin(" value", dummy); | |
Serial.print("\n"); | |
} | |
Serial.print("\n"); | |
} | |
uint16_t laststatus = 9999; | |
uint32_t nextcheck = 0; | |
uint8_t lastlinkstatus = 4; | |
// the loop routine runs over and over again forever: | |
void loop() { | |
uint16_t dummy; | |
uint16_t status; | |
uint8_t linkstatus; | |
for (int i = 0; i < 1000; i++) { | |
digitalWrite(txpin1, rand() & 1); // (counter & 2) == 2); | |
digitalWrite(txpin2, rand() & 1); // (counter & 2) == 2); | |
digitalWrite(txpen, 1); | |
digitalWrite(txpen, 0); | |
} | |
uint32_t T = millis(); | |
if (T > nextcheck) { | |
nextcheck = T + 200; | |
status = smi_peek(phy_address, 1); | |
status = smi_peek(phy_address, 1); | |
if (status != laststatus) { | |
laststatus = status; | |
// Serial.print("status="); | |
// Serial.print(status, BIN); | |
serial_print_bin("status", status); | |
Serial.print("\n"); | |
linkstatus = (status & LAN8720_BMSR_LINK_STATUS) != 0; | |
Serial.print("ext cap.="); | |
Serial.print((status & 1) != 0); | |
Serial.print("\n"); | |
Serial.print("jabber detect="); | |
Serial.print((status & 2) != 0); | |
Serial.print("\n"); | |
Serial.print("link status="); | |
Serial.print(linkstatus); | |
Serial.print("\n"); | |
Serial.print("auto negotiate ability="); | |
Serial.print((status & 8) != 0); | |
Serial.print("\n"); | |
Serial.print("remote fault="); | |
Serial.print((status & 16) != 0); | |
Serial.print("\n"); | |
Serial.print("auto negotiate complete="); | |
Serial.print((status & 32) != 0); | |
Serial.print("\n"); | |
// Serial.print("="); | |
// Serial.print((status & 64) != 0); | |
// Serial.print("\n"); | |
dummy = smi_peek(phy_address, LAN8720_PSCSR); | |
if (status & LAN8720_BMSR_LINK_STATUS) { | |
// Serial.print("LAN8720_PSCSR="); | |
// Serial.print(dummy, BIN); | |
serial_print_bin("LAN8720_PSCSR", dummy); | |
Serial.print("\n"); | |
switch (dummy & LAN8720_PSCSR_HCDSPEED) | |
{ | |
case LAN8720_PSCSR_HCDSPEED_10BT_HD: | |
Serial.print("10BASE-T half-duplex\n"); | |
break; | |
case LAN8720_PSCSR_HCDSPEED_10BT_FD: | |
Serial.print("10BASE-T full-duplex\n"); | |
break; | |
case LAN8720_PSCSR_HCDSPEED_100BTX_HD: | |
Serial.print("100BASE-TX half-duplex\n"); | |
break; | |
case LAN8720_PSCSR_HCDSPEED_100BTX_FD: | |
Serial.print("100BASE-TX full-duplex\n"); | |
break; | |
default: | |
Serial.print("Invalid mode\n"); | |
break; | |
} | |
} else { | |
Serial.print("No link\n"); | |
} | |
dummy = smi_peek(phy_address, 4); | |
serial_print_bin("#4", dummy); | |
Serial.print("\n"); | |
dummy = smi_peek(phy_address, 5); | |
serial_print_bin("#5", dummy); | |
Serial.print("\n"); | |
if (linkstatus != lastlinkstatus) { | |
lastlinkstatus = linkstatus; | |
// trigger autonegotiation | |
if (linkstatus) { | |
// Serial.print("Re-trigger autonegotiation?\n"); | |
// dummy = smi_peek(phy_address, 0); | |
// serial_print_bin("autoneg read #0", dummy); | |
// Serial.print("\n"); | |
// // dummy = 0; | |
// // dummy |= (1 << 12); // enable autonegotiaton | |
// // dummy |= (1 << 13); // 100mbit | |
// // dummy |= (1 << 10); // isolate? | |
// dummy |= (1 << 9); // restart autoneg. | |
// dummy |= (1 << 8); // full duplex | |
// serial_print_bin("autoneg write #0", dummy); | |
// Serial.print("\n"); | |
// smi_poke(phy_address, 0, dummy); | |
// do { | |
// dummy = smi_peek(phy_address, 0); | |
// serial_print_bin("autoneg poll #4", dummy); | |
// Serial.print("\n"); | |
// delay(100); | |
// } while(dummy & (1 << 9)); | |
} | |
} | |
Serial.print("\n"); | |
// } | |
} | |
} | |
// delayMicroseconds(10); | |
counter ++; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment