GPIO 模拟SMI协议
使用MCU GPIO引脚模拟SMI协议staticvoidmdc_high(void){gpio_bits_set(SMI_GPIO_PORT,SMI_MDC_PIN);}staticvoidmdc_low(void){gpio_bits_reset(SMI_GPIO_PORT,SMI_MDC_PIN);}voidmdio_output_mode(void){gpio_init_type gpio_init_struct;gpio_default_para_init(gpio_init_struct);gpio_init_struct.gpio_pinsSMI_MDIO_PIN;gpio_init_struct.gpio_modeGPIO_MODE_OUTPUT;gpio_init_struct.gpio_out_typeGPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_pullGPIO_PULL_UP;gpio_init_struct.gpio_drive_strengthGPIO_DRIVE_STRENGTH_STRONGER;gpio_init(SMI_GPIO_PORT,gpio_init_struct);// gpio_init_type gpio_init_struct;gpio_default_para_init(gpio_init_struct);gpio_init_struct.gpio_pinsSMI_MDC_PIN;gpio_init_struct.gpio_modeGPIO_MODE_OUTPUT;gpio_init_struct.gpio_out_typeGPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_pullGPIO_PULL_UP;gpio_init_struct.gpio_drive_strengthGPIO_DRIVE_STRENGTH_STRONGER;gpio_init(SMI_GPIO_PORT,gpio_init_struct);}staticvoidmdio_input_mode(void){gpio_init_type gpio_init_struct;gpio_default_para_init(gpio_init_struct);gpio_init_struct.gpio_pinsSMI_MDIO_PIN;gpio_init_struct.gpio_modeGPIO_MODE_INPUT;gpio_init_struct.gpio_pullGPIO_PULL_UP;gpio_init_struct.gpio_drive_strengthGPIO_DRIVE_STRENGTH_STRONGER;gpio_init(SMI_GPIO_PORT,gpio_init_struct);}staticvoidmdio_set(uint8_tval){if(val)gpio_bits_set(SMI_GPIO_PORT,SMI_MDIO_PIN);elsegpio_bits_reset(SMI_GPIO_PORT,SMI_MDIO_PIN);}staticuint8_tmdio_get(void){return(gpio_input_data_bit_read(SMI_GPIO_PORT,SMI_MDIO_PIN)SET)?1:0;}/* -------- SMI 时序基元时序修正 -------- *//** * brief 发送1个bitMDIO在MDC下降沿后改变上升沿被PHY采样 */staticvoidsend_bit(uint8_tbit){mdio_set(bit);SMI_DELAY_US(1);// 下降沿后保持稳定mdc_high();// 上升沿PHY采样SMI_DELAY_US(1);mdc_low();// 下降沿允许MDIO改变SMI_DELAY_US(1);}/** * brief 读取1个bitMDIO在MDC上升沿被采样 * note 标准 Clause 22 要求在 MDC 高电平期间读取 MDIO */staticuint8_tread_bit(void){uint8_tval;mdc_high();// 上升沿数据有效SMI_DELAY_US(1);valmdio_get();// 在高电平期间采样mdc_low();// 下降沿SMI_DELAY_US(1);returnval;}staticvoidsend_num(uint32_tvalue,uint8_tbits){for(intibits-1;i0;i--){send_bit((valuei)0x01);}}staticuint32_tread_num(uint8_tbits){uint32_tvalue0;for(intibits-1;i0;i--){value(value1)|read_bit();}returnvalue;}/** * brief 发送帧头前导码、起始码、操作码、地址 */staticvoidsend_cmd(uint8_top,uint8_tphy,uint8_treg){mdio_output_mode();/* 1. 32 个 1 前导码Preamble */for(inti0;i32;i)send_bit(1);/* 2. 起始码Start of Frame01 */send_bit(0);send_bit(1);/* 3. 操作码Opcode读10写01 */send_bit((op1)0x01);send_bit(op0x01);/* 4. PHY 地址5bit 寄存器地址5bit */send_num(phy,5);send_num(reg,5);}/* -------- 对外 API -------- */voidsmi_init(void){gpio_init_type gpio_init_struct;crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK,TRUE);/* MDC 推挽输出 */gpio_default_para_init(gpio_init_struct);gpio_init_struct.gpio_pinsSMI_MDC_PIN;gpio_init_struct.gpio_modeGPIO_MODE_OUTPUT;gpio_init_struct.gpio_out_typeGPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_pullGPIO_PULL_NONE;gpio_init_struct.gpio_drive_strengthGPIO_DRIVE_STRENGTH_STRONGER;gpio_init(SMI_GPIO_PORT,gpio_init_struct);/* MDIO 默认输出高电平空闲 */mdio_output_mode();mdio_set(1);mdc_low();rt_thread_mdelay(10);}/** * brief SMI 读寄存器Clause 22 * note 读操作 TurnaroundTA为 2 位标准规定必须为 00 * 第1位由PHY将总线拉低确认响应第2位保持0准备输出数据 */uint16_tsmi_reg_read(uint8_tphy_addr,uint8_treg_addr){uint16_tdata;send_cmd(MDIO_READ,phy_addr,reg_addr);/* 切换 MDIO 为输入准备读取 TA */mdio_input_mode();/* 读取完整的 2 位 Turnaround标准要求均为 0 */uint8_tta1read_bit();// 第1位PHY 应输出 0uint8_tta2read_bit();// 第2位PHY 应输出 0/* 校验 TA只要有一位不为 0说明 PHY 无响应或总线异常 */if(ta1!0||ta2!0){/* 丢弃剩余的 16 个数据位以恢复总线时钟对齐 */for(inti0;i16;i){read_bit();}/* 恢复总线空闲状态输出高电平 */mdio_output_mode();mdio_set(1);return0xFFFF;}/* TA 校验通过此时读取的 16 位即为标准寄存器数据 */data(uint16_t)read_num(16);/* 恢复总线空闲状态输出高电平 */mdio_output_mode();mdio_set(1);returndata;}/** * brief SMI 写寄存器Clause 22 * note 写操作 TurnaroundTA固定为 10由主机驱动 */voidsmi_reg_write(uint8_tphy_addr,uint8_treg_addr,uint16_tdata){send_cmd(MDIO_WRITE,phy_addr,reg_addr);/* 切换输出发送写操作的 Turnaround2b10 */mdio_output_mode();send_bit(1);// TA 第1位 1send_bit(0);// TA 第2位 0/* 发送 16 位寄存器数据 */send_num(data,16);/* 总线保持高电平空闲 */mdio_set(1);}/* -------- 以下为测试辅助函数无逻辑修改 -------- */voidsmi_detect_phys(void){uint16_tbmsr,id1,id2;rt_kprintf(\r\n PHY Address Scan (0~31) \r\n);rt_kprintf(Addr | BMSR | Link | PHY ID (High, Low)\r\n);rt_kprintf(-----|---------|------|--------------------\r\n);for(uint8_taddr0;addr32;addr){bmsrsmi_reg_read(addr,REG_BMSR);if(bmsr0xFFFF)continue;id1smi_reg_read(addr,REG_PHYID1);id2smi_reg_read(addr,REG_PHYID2);if(id10xFFFFid20xFFFF)continue;rt_kprintf( 0x%02X | 0x%04X | %s | 0x%04X, 0x%04X\r\n,addr,bmsr,(bmsrBMSR_LINK_STATUS)?UP:DOWN,id1,id2);}rt_kprintf(-----|---------|------|--------------------\r\n);}voidsmi_scan_all_ports(void){uint8_taddrs[]{PHY_ADDR_PORT0,PHY_ADDR_PORT1,PHY_ADDR_PORT2,PHY_ADDR_PORT3,PHY_ADDR_PORT4};constchar*name[]{P0,P1,P2,P3,P4};uint16_tbmsr;rt_kprintf(\r\n--- Port Link Status ---\r\n);for(inti0;i5;i){bmsrsmi_reg_read(addrs[i],REG_BMSR);rt_kprintf(%s (0x%02X): BMSR0x%04X, Link%s\r\n,name[i],addrs[i],bmsr,(bmsrBMSR_LINK_STATUS)?UP:DOWN);}}voidsmi_verify_port_mapping(void){uint8_tvalid_addrs[32];uint8_tvalid_count0;uint8_tlast_status[32]{0};uint8_tcurrent_status;uint16_tbmsr,id1,id2;rt_kprintf(\r\n Port Mapping Verification \r\n);rt_kprintf(Scanning for active PHY addresses...\r\n);for(uint8_taddr0;addr32;addr){bmsrsmi_reg_read(addr,REG_BMSR);if(bmsr0xFFFF)continue;id1smi_reg_read(addr,REG_PHYID1);id2smi_reg_read(addr,REG_PHYID2);if(id10xFFFFid20xFFFF)continue;valid_addrs[valid_count]addr;last_status[addr](bmsrBMSR_LINK_STATUS)?1:0;valid_count;rt_kprintf( Found PHY at address 0x%02X, Link%s, ID0x%04X%04X\r\n,addr,(bmsrBMSR_LINK_STATUS)?UP:DOWN,id1,id2);}if(valid_count0){rt_kprintf(No PHY found! Check hardware connection and pull-up resistor.\r\n);return;}rt_kprintf(\r\n Monitoring Link Status Changes \r\n);rt_kprintf(Now, plug/unplug Ethernet cables one by one.\r\n);rt_kprintf(Observe which address changes when you operate each port.\r\n);rt_kprintf(Addresses being monitored: );for(inti0;ivalid_count;i){rt_kprintf(0x%02X ,valid_addrs[i]);}rt_kprintf(\r\n);while(1){for(inti0;ivalid_count;i){uint8_taddrvalid_addrs[i];bmsrsmi_reg_read(addr,REG_BMSR);if(bmsr0xFFFF)continue;current_status(bmsrBMSR_LINK_STATUS)?1:0;if(current_status!last_status[addr]){rt_kprintf([%lu ms] Address 0x%02X changed from %s to %s\r\n,rt_tick_get()*1000/RT_TICK_PER_SECOND,addr,last_status[addr]?UP:DOWN,current_status?UP:DOWN);last_status[addr]current_status;}}rt_thread_mdelay(200);}}