Skip to content

Instantly share code, notes, and snippets.

@possan
Created August 2, 2020 20:39
Show Gist options
  • Save possan/f01fd83f0eecb1a5a68644b06cf94e9f to your computer and use it in GitHub Desktop.
Save possan/f01fd83f0eecb1a5a68644b06cf94e9f to your computer and use it in GitHub Desktop.
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