#if 1//下面是参考案例 //RTL8305NB
#define PORT2_PHY_ADDR 0x05 // SFP Port2 PHY地址
#define STATUS_REG_ADDR 0x01 // 状态寄存器地址
#define MDC_PIN GPIO_Pin_13 //MDC (PC13)
#define MDIO_PIN GPIO_Pin_6 //MDIO (PE6)
#define MDIO_DELAY 10 // us
#define MDIO_READ_DELAY 10 // us
void smi_init(void);
u16 smi_reg_read(u16 phy, u16 reg);
void smi_reg_write(u16 phy, u16 reg, u16 val);
static void MDC_OUT(void);
static void MDIO_OUT(void);
static void MDIO_IN(void);
static void MDC_H(void);
static void MDC_L(void);
static uint8_t GET_MDIO(void);
static void SET_MDIO(uint8_t val);
extern void delay_us(u32 nus);
void LOS_UDelay(u32 nus)
{
delay_us(nus);
}
/* 设置MDC为输出引脚,在MDC输出时钟之前设置 */
static void MDC_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // 推挽输出,因为MDIO是双向的,但在这里我们先配置为输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//GPIO_PuPd (Pull-Up/Pull-Down)设置 GPIO 引脚的内部上拉/下拉电阻
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//GPIO_OType (Output Type)用于设置 GPIO 引脚在输出模式下的类型。
// MDC (PC13) 配置为输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/* 设置MDIO的gpio引脚为输出引脚 */
static void MDIO_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// MDIO (PE6) 配置为输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // 推挽输出,因为MDIO是双向的,但在这里我们先配置为输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//GPIO_PuPd (Pull-Up/Pull-Down)设置 GPIO 引脚的内部上拉/下拉电阻
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//GPIO_OType (Output Type)用于设置 GPIO 引脚在输出模式下的类型。
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
/* 设置MDIO的gpio引脚为输入引脚 */
static void MDIO_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// MDIO (PE6) 配置为输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//GPIO_PuPd (Pull-Up/Pull-Down)设置 GPIO 引脚的内部上拉/下拉电阻
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
/* MDC输出高电平,在MDC设置为输出后调用 */
static void MDC_H(void)
{
GPIO_SetBits(GPIOC, MDC_PIN);
}
/* MDC输出低电平,在MDC设置为输出后调用 */
static void MDC_L(void)
{
GPIO_ResetBits(GPIOC, MDC_PIN);
}
/* 获得MDIO的数据,只获得一个bit */
static uint8_t GET_MDIO(void)
{
return GPIO_ReadInputDataBit(GPIOE,MDIO_PIN);
}
/* 设置MDIO的数据,一个bit */
static void SET_MDIO(uint8_t val)
{
if (val != 0) {
GPIO_SetBits(GPIOE, MDIO_PIN);
} else {
GPIO_ResetBits(GPIOE, MDIO_PIN);
}
}
/* MDIO发送一个bit的数据,MDIO必须已经被配置为输出 */
static void mdio_bb_send_bit(uint8_t val)
{
MDC_OUT();
SET_MDIO(val);
LOS_UDelay(MDIO_DELAY);
MDC_H();
LOS_UDelay(MDIO_DELAY);
MDC_L();
// LOS_UDelay(MDIO_DELAY);
}
/* MDIO 获取一个bit的数据,MDIO必须已经被配置为输入. */
static uint8_t mdio_bb_get_bit(void)
{
uint8_t value;
MDC_OUT();
LOS_UDelay(MDIO_DELAY);
MDC_H();
LOS_UDelay(MDIO_READ_DELAY);
value = GET_MDIO();
// LOS_UDelay(MDIO_DELAY);
MDC_L();
return value;
}
/*
* MDIO发送一个数据,MDIO 必须被配置为输出模式.
* value:要发送的数据
* bits:数据的位数
*
* */
static void mdio_bb_send_num(u16 value, uint8_t bits)
{
int i;
MDIO_OUT();
for (i = bits - 1; i >= 0; i--)
mdio_bb_send_bit((value >> i) & 1);
}
/*
* MDIO获取一个数据,MDIO 必须被配置为输入模式.
* bits:获取数据的位数
*
* */
static u16 mdio_bb_get_num(u8 bits)
{
int i;
u16 ret = 0;
for (i = bits - 1; i >= 0; i--) {
ret <<= 1;
ret |= mdio_bb_get_bit();
}
return ret;
}
/* Utility to send the preamble, address, and
* register (common to read and write).
*/
static void mdio_bb_cmd(uint8_t op, u16 phy, u16 reg)
{
int i = 0;
MDIO_OUT(); //设置MDIO引脚为输出引脚
/*发送32bit的1,这个帧前缀域不是必须的,某些物理层芯片的MDIO操作就没有这个域*/
for (i = 0; i < 32; i++)
mdio_bb_send_bit(1);
mdio_bb_send_bit(0);
mdio_bb_send_bit(1);
mdio_bb_send_bit((op >> 1) & 1);
mdio_bb_send_bit((op >> 0) & 1);
mdio_bb_send_num(phy, 5);
mdio_bb_send_num(reg, 5);
}
void mdio_set_turnaround(void)
{
int i = 0;
MDIO_IN();
MDC_OUT();
for (i = 0; i < 1; i++) {
LOS_UDelay(MDIO_DELAY);
MDC_H();
LOS_UDelay(MDIO_DELAY);
MDC_L();
}
}
u16 smi_reg_read(u16 phy, u16 reg)
{
u16 ret, i;
mdio_bb_cmd(MDIO_READ, phy, reg);
MDIO_IN();
// mdio_set_turnaround();
#if 1
/* check the turnaround bit: the PHY should be driving it to zero */
if (mdio_bb_get_bit() != 0) {
/* PHY didn\'t driver TA low -- flush any bits it may be trying to send*/
for (i = 0; i < 32; i++)
mdio_bb_get_bit();
// bios_log("PHY didn\'t driver TA low! \r\n");
return 0xFFFF;
}
#endif
ret = mdio_bb_get_num(16);
mdio_bb_get_bit();
return ret;
}
void smi_reg_write(u16 phy, u16 reg, u16 val)
{
mdio_bb_cmd(MDIO_WRITE, phy, reg);
/* send the turnaround (10) */
mdio_bb_send_bit(1);
mdio_bb_send_bit(0);
mdio_bb_send_num(val, 16);
MDIO_IN();
// mdio_bb_get_bit();
}
void smi_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOE和GPIOC的时钟
RCC_APB2PeriphClockCmd(RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOC, ENABLE);
// MDIO (PE6) 配置为输出
GPIO_InitStructure.GPIO_Pin = MDIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // 推挽输出,因为MDIO是双向的,但在这里我们先配置为输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//GPIO_PuPd (Pull-Up/Pull-Down)设置 GPIO 引脚的内部上拉/下拉电阻
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//GPIO_OType (Output Type)用于设置 GPIO 引脚在输出模式下的类型。
GPIO_Init(GPIOE, &GPIO_InitStructure);
// MDC (PC13) 配置为输出
GPIO_InitStructure.GPIO_Pin = MDC_PIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void switch_fiber_port_init(void)
{
u16 data = 0;
smi_reg_write(0x08, 0x1F, 0x8000); // 切换Page至PHY page
smi_reg_write(PORT2_PHY_ADDR, 0x1F, 0x00); // 设置Port2为光口
data = smi_reg_read(PORT2_PHY_ADDR, 0x1C); // 读出寄存器28原始状态
data |= (0x01 << 5);// 将bit5置高
smi_reg_write(PORT2_PHY_ADDR, 0x1C, data); // 将修改后的值写入寄存器
smi_reg_write(0x08, 0x1F, 0x0); // 切回MAC Page
}
#endif
对于RTL8305NB,要从电口模式切换为光口模式,主要操作涉及到PHY page的切换和特定寄存器的配置。以下是详细的操作步骤:
- PHY Page切换:
- 首先,需要访问PHY地址8的寄存器31。这个寄存器用于Page的切换。
- 向PHY地址8的寄存器31写入值
0x8000
,这将使芯片切换到PHY page。
- 光口模式配置:
- 在切换到PHY page后,需要配置特定的寄存器以将端口设置为光口模式。
- 根据参考文章提供的信息,通常需要修改PHY page下的某个寄存器的某个位来配置光口模式。具体而言,是将寄存器28的bit5置高(设置为1)。
// 假设有一个函数用于向PHY寄存器写入值
void write_phy_register(uint8_t phy_address, uint8_t register_address, uint16_t value) {
// 实现向PHY寄存器写入的逻辑
// ...
}
// 切换到PHY Page
write_phy_register(8, 31, 0x8000); // 切换到PHY Page
// 假设有一个函数用于读取PHY寄存器的值
uint16_t read_phy_register(uint8_t phy_address, uint8_t register_address) {
// 实现从PHY寄存器读取值的逻辑
// ...
return value;
}
// 配置光口模式
uint16_t register28_value = read_phy_register(8, 28); // 读取寄存器28的当前值
register28_value |= (1 << 5); // 将bit5设置为1(光口模式)
write_phy_register(8, 28, register28_value); // 将修改后的值写回寄存器28
void switch_fiber_port_init(void)
{
unsigned short data = 0;
write_phy_register(0x08, 0x1F, 0x8000); // 切换Page至PHY page
write_phy_register(PORT2_PHY_ADDR, 0x1F, 0x00); // 设置Port2为光口
data = read_phy_register(PORT2_PHY_ADDR, 0x1C); // 读出寄存器28原始状态
data |= (0x01 << 5);// 将bit5置高
write_phy_register(PORT2_PHY_ADDR, 0x1C, data); // 将修改后的值写入寄存器
write_phy_register(0x08, 0x1F, 0x0); // 切回MAC Page
}
总结:
- 访问PHY地址8的寄存器31,并写入
0x8000
以切换到PHY page。 - 在PHY page下,将寄存器28的bit5置高,从而配置端口为光口模式。