rt thread smart适配野火imx6ull开发板的KSZ8081网卡

原理图

野火和nxp的imx6ull都采用KSZ8081网卡,电路图一致。
在这里插入图片描述
ENET1_nRST复位引脚连接在了74lv595上,因此需要先驱动74lv595。
在这里插入图片描述
74lv595利用BOOT_MODE0、BOOT_MODE1、SNVS_TAMPER7、SNVS_TAMPER8四个引脚最多可以驱动8个引脚,但是野火的输出端只连接了两个。

引脚配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
BOOT_MODE0、BOOT_MODE1、SNVS_TAMPER8四个引脚的复用功能寄存器如上图。需要将它们复用成如下引脚:
BOOT_MODE0——> GPIO5_IO10、
BOOT_MODE1——> GPIO5_IO11、
SNVS_TAMPER7——> GPIO5_IO07、
SNVS_TAMPER8——> GPIO5_IO08
关于这四个引脚是如何复位的,我参考了正点原子“蜂鸣器实验”章节,其原理图如下:
在这里插入图片描述
这里需要将SNVS_TAMPER1复用为——> GPIO5_IO01,其源码如下:

void beep_init(void)
{
	/* 1、初始化IO复用,复用为GPIO5_IO01 */
	IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0);		
	
	
	/* 2、、配置GPIO1_IO03的IO属性	
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 00 默认下拉
	 *bit [13]: 0 kepper功能
	 *bit [12]: 1 pull/keeper使能
	 *bit [11]: 0 关闭开路输出
	 *bit [7:6]: 10 速度100Mhz
	 *bit [5:3]: 110 R0/6驱动能力
	 *bit [0]: 0 低转换率
	 */
	IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0X10B0);
	
	/* 3、初始化GPIO,GPIO5_IO01设置为输出 */
	GPIO5->GDIR |= (1 << 1);	 

	/* 4、设置GPIO5_IO01输出高电平,关闭蜂鸣器 */
	GPIO5->DR |= (1 << 1);		
}

/*
 * @description		: 蜂鸣器控制函数,控制蜂鸣器打开还是关闭
 * @param - status	: 0,关闭蜂鸣器,1 打开蜂鸣器
 * @return 			: 无
 */
void beep_switch(int status)
{	
	if(status == ON)
		GPIO5->DR &= ~(1 << 1);	/* 打开蜂鸣器 */
	else if(status == OFF)
		GPIO5->DR |= (1 << 1);	/* 关闭蜂鸣器 */
}

这里顺带讲一下DR 寄存器和GDIR 寄存器。
在这里插入图片描述
此寄存器是 32 位的,一个 GPIO 组最大只有 32 个 IO,因此 DR 寄存器中的每个位都对应 一个 GPIO。当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相 应的高低电平,比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1。当GPIO被配置为输入模式以后,此寄存器就保存着对应 IO 的电平值,每个位对对应一个 GPIO,例如, 当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR 的 bit0 就是 0。
看完 DR 寄存器,接着看 GDIR 寄存器,这是方向寄存器,用来设置某个 GPIO 的工作方向的,即输入/输出,GDIR 寄存器结构如图 8.1.5.3 所示:
在这里插入图片描述
GDIR 寄存器也是 32 位的,此寄存器用来设置某个 IO 的工作方向,是输入还是输出。同样的,每个 IO 对应一个位,如果要设置 GPIO 为输入的话就设置相应的位为 0,如果要设置为
输出的话就设置为 1。比如要设置 GPIO1_IO00 为输入,那么 GPIO1.GDIR=0;

IOMUXC_SetPinMux & IOMUXC_SetPinConfig

/*!
 * @brief Sets the IOMUXC pin mux mode.
 * @note The first five parameters can be filled with the pin function ID macros.
 *
 * This is an example to set the ENET1_RX_DATA0 Pad as FLEXCAN1_TX:
 * @code
 * IOMUXC_SetPinMux(IOMUXC_ENET1_RX_DATA0_FLEXCAN1_TX, 0);
 * @endcode
 *
 * This is an example to set the GPIO1_IO02 Pad as I2C1_SCL:
 * @code
 * IOMUXC_SetPinMux(IOMUXC_GPIO1_IO02_I2C1_SCL, 0);
 * @endcode
 *
 * @param muxRegister  The pin mux register.
 * @param muxMode      The pin mux mode.
 * @param inputRegister The select input register.
 * @param inputDaisy   The input daisy.
 * @param configRegister  The config register.
 * @param inputOnfield   Software input on field.
 */
static inline void IOMUXC_SetPinMux(uint32_t muxRegister,
                                    uint32_t muxMode,
                                    uint32_t inputRegister,
                                    uint32_t inputDaisy,
                                    uint32_t configRegister,
                                    uint32_t inputOnfield)
{
    *((volatile uint32_t *)muxRegister) =
        IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(muxMode) | IOMUXC_SW_MUX_CTL_PAD_SION(inputOnfield);

    if (inputRegister)
    {
        *((volatile uint32_t *)inputRegister) = IOMUXC_SELECT_INPUT_DAISY(inputDaisy);
    }
}

/*!
 * @brief Sets the IOMUXC pin configuration.
 * @note The previous five parameters can be filled with the pin function ID macros.
 *
 * This is an example to set pin configuration for IOMUXC_GPIO1_IO02_I2C1_SCL:
 * @code
 * IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO02_I2C1_SCL, IOMUXC_SW_PAD_CTL_PAD_PUE_MASK | IOMUXC_SW_PAD_CTL_PAD_PUS(2U));
 * @endcode
 *
 * @param muxRegister  The pin mux register.
 * @param muxMode      The pin mux mode.
 * @param inputRegister The select input register.
 * @param inputDaisy   The input daisy.
 * @param configRegister  The config register.
 * @param configValue   The pin config value.
 */
static inline void IOMUXC_SetPinConfig(uint32_t muxRegister,
                                       uint32_t muxMode,
                                       uint32_t inputRegister,
                                       uint32_t inputDaisy,
                                       uint32_t configRegister,
                                       uint32_t configValue)
{
    if (configRegister)
    {
        *((volatile uint32_t *)configRegister) = configValue;
    }
}

for example

IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0X10B0);

这 里 使 用 了 两 个 函 数 IOMUXC_SetPinMux 和 IOMUXC_SetPinConfig , 其 中 函 数 IOMUXC_SetPinMux 是 用 来 设 置 IO 复 用 功 能 的 , 最 终 肯 定 设 置 的 是 寄 存 器 “IOMUXC_SW_MUX_CTL_PAD_XX”。函数 IOMUXC_SetPinConfig 设置的是 IO 的上下拉、 速度等的,也就是寄存器“IOMUXC_SW_PAD_CTL_PAD_XX”。
函数 IOMUXC_SetPinMux 有 6 个参数,这 6 个参数的函数如下:
muxRegister : IO 的 复 用 寄 存 器 地 址 , 比 如 GPIO1_IO03 的 IO 复 用 寄 存 器 SW_MUX_CTL_PAD_GPIO1_IO03 的地址为 0X020E0068。
muxMode: IO 复用值,也就是 ALT0~ALT8,对应数字 0~8,比如要将 GPIO1_IO03 设置 为 GPIO 功能的话此参数就要设置为 5。
inputRegister:外设输入 IO 选择寄存器地址,有些 IO 在设置为其他的复用功能以后还需 要设置 IO 输入寄存器,比如 GPIO1_IO03 要复用为 UART1_RX 的话还需要设置寄存器 UART1_RX_DATA_SELECT_INPUT,此寄存器地址为 0X020E0624。
inputDaisy:寄存器 inputRegister 的值,比如 GPIO1_IO03 要作为 UART1_RX 引脚的话此 参数就是 1。
configRegister:未使用,函数 IOMUXC_SetPinConfig 会使用这个寄存器。
inputOnfield : IO 软 件 输 入 使 能 , 以 GPIO1_IO03 为 例 就 是 寄 存 器 SW_MUX_CTL_PAD_GPIO1_IO03 的 SION 位(bit4)。如果需要使能 GPIO1_IO03 的软件输入功能的话此参数应该为 1,否则的话就为 0。
在这里插入图片描述
函数 IOMUXC_SetPinConfig 有 6 个参数,其中前五个参数和函数 IOMUXC_SetPinMux 一 样,但是此函数只使用了参数 configRegister 和 configValue,cofigRegister 参数是 IO 配置寄存 器地址,比如 GPIO1_IO03 的 IO 配置寄存器为 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03, 其地址为 0X020E02F4,参数 configValue 就是要写入到寄存器 configRegister 的值。同理,“示例代码 12.3.3.1”的 43 行展开以后就是:
IOMUXC_SetPinConfig(0x020E0068U, 0x5U, 0x00000000U, 0x0U, 0x020E02F4U, 0X10B0);
根据函数 IOMUXC_SetPinConfig 的源码可以知道,上面函数就是将寄存器 0x020E02F4 的值设置为 0X10B0。函数 IOMUXC_SetPinMux 和 IOMUXC_SetPinConfig 就讲解到这里,我们
以后就可以使用这两个函数来方便的配置 IO 的复用功能和 IO 配置。

imx6ull_iomuxc

art-pi smart里的imx6ull_iomuxc和上面两个函数类似,但也存在一些差别:

struct imx6ull_iomuxc
{
    rt_uint32_t muxRegister;
    rt_uint32_t muxMode;
    rt_uint32_t inputRegister;
    rt_uint32_t inputDaisy;
    rt_uint32_t configRegister;

    rt_uint32_t inputOnfield;

    rt_uint32_t configValue;
};

void imx6ull_gpio_init(const struct imx6ull_iomuxc *gpio)
{
    rt_uint32_t mux_reg_vaddr    = 0;
    rt_uint32_t input_reg_vaddr  = 0;
    rt_uint32_t config_reg_vaddr = 0;

    mux_reg_vaddr    = (rt_uint32_t)(gpio->muxRegister    ? (rt_uint32_t)imx6ull_get_periph_vaddr(gpio->muxRegister) : gpio->muxRegister);
    input_reg_vaddr  = (rt_uint32_t)(gpio->inputRegister  ? (rt_uint32_t)imx6ull_get_periph_vaddr(gpio->inputRegister) : gpio->inputRegister);
    config_reg_vaddr = (rt_uint32_t)(gpio->configRegister ? (rt_uint32_t)imx6ull_get_periph_vaddr(gpio->configRegister) : gpio->configRegister);

    IOMUXC_SetPinMux(mux_reg_vaddr, gpio->muxMode, input_reg_vaddr, gpio->inputDaisy, config_reg_vaddr, gpio->inputOnfield);
    IOMUXC_SetPinConfig(mux_reg_vaddr, gpio->muxMode, input_reg_vaddr, gpio->inputDaisy, config_reg_vaddr, gpio->configValue);
}

#define IOMUXC_GPIO1_IO06_ENET1_MDIO                         0x020E0074U, 0x0U, 0x020E0578U, 0x0U, 0x020E0300U
#define IOMUXC_GPIO1_IO07_ENET1_MDC                          0x020E0078U, 0x0U, 0x00000000U, 0x0U, 0x020E0304U

static struct imx6ull_iomuxc mdio_gpio[2] = 
{
    {IOMUXC_GPIO1_IO06_ENET1_MDIO,0U,0xB029},
    {IOMUXC_GPIO1_IO07_ENET1_MDC,0U,0xB0E9}
};

完全展开后是:
static struct imx6ull_iomuxc mdio_gpio[2] = 
{
    {0x020E0074U, 0x0U, 0x020E0578U, 0x0U, 0x020E0300U,0U,0xB029},
    {0x020E0078U, 0x0U, 0x00000000U, 0x0U, 0x020E0304U,0U,0xB0E9}
};

注意点:0xB029(configValue)是用于设置0x020E0300U(configRegister)寄存器的。

NXP官方源码IAR

nxp官方也提供了以太网的IAR工程,里面正是用到了74lv595,可以在官方的sdk里找到代码。其74lv595相关的代码如下:

#define NXP74LV595_INPUT_STCP_GPIO GPIO5
#define NXP74LV595_INPUT_STCP_PIN 7U
#define NXP74LV595_INPUT_OE_GPIO GPIO5
#define NXP74LV595_INPUT_OE_PIN 8U
#define NXP74LV595_INPUT_SDI_GPIO GPIO5
#define NXP74LV595_INPUT_SDI_PIN 10U
#define NXP74LV595_INPUT_SHCP_GPIO GPIO5
#define NXP74LV595_INPUT_SHCP_PIN 11U

typedef enum _NXP74LV595_signal
{
    kSignal_NXP74LV595_Low = 0U,
    kSignal_NXP74LV595_High = 1U
} NXP74LV595_signal_t;

typedef enum _NXP74LV595_parOutputPins
{
    kNXP74LV595_HDMI_nRST = 0U,
    kNXP74LV595_ENET1_nRST = 1U,
    kNXP74LV595_ENET2_nRST = 2U,
    kNXP74LV595_CAN1_2_STBY = 3U,
    kNXP74LV595_BT_nPWD = 4U,
    kNXP74LV595_CSI_RST = 5U,
    kNXP74LV595_CSI_PWDN = 6U,
    kNXP74LV595_LCD_nPWREN = 7U
} NXP74LV595_parOutputPins_t;

/*Initial output value of NXP74LV595*/
static uint8_t s_NXP74LV595Output = 0U;

void Delay(uint32_t ticks)
{
    while (ticks--)
    {
        __NOP();
    }
}

/* Set parallel pins output value of 74LV595 */
void BOARD_NXP74LV595_SetValue(NXP74LV595_parOutputPins_t pin, NXP74LV595_signal_t value)
{
    uint8_t shiftNumber;
    uint8_t mask;

    /*Init GPIO pins and open output gate of 74LV595*/
    NXP74LV595_INPUT_OE_GPIO->DR &= ~(1U << NXP74LV595_INPUT_OE_PIN);
    NXP74LV595_INPUT_OE_GPIO->GDIR |= (1U << NXP74LV595_INPUT_OE_PIN);
    NXP74LV595_INPUT_STCP_GPIO->GDIR |= (1U << NXP74LV595_INPUT_STCP_PIN);
    NXP74LV595_INPUT_SDI_GPIO->GDIR |= (1U << NXP74LV595_INPUT_SDI_PIN);
    NXP74LV595_INPUT_SHCP_GPIO->GDIR |= (1U << NXP74LV595_INPUT_SHCP_PIN);

    /*Calculate all parallel pins output value that will be set*/
    s_NXP74LV595Output = (s_NXP74LV595Output & (~(1U << pin))) | (value << pin);

    for (shiftNumber = 0; shiftNumber < 8; shiftNumber++)
    {
        /*High data bits transfer first*/
        mask = (s_NXP74LV595Output >> (7 - shiftNumber)) & 1U;
        if (0 == mask)
        {
            NXP74LV595_INPUT_SDI_GPIO->DR &= ~(1U << NXP74LV595_INPUT_SDI_PIN);
        }
        else
        {
            NXP74LV595_INPUT_SDI_GPIO->DR |= (1U << NXP74LV595_INPUT_SDI_PIN);
        }
        /*Contents of shift register shifted.*/
        NXP74LV595_INPUT_SHCP_GPIO->DR &= ~(1U << NXP74LV595_INPUT_SHCP_PIN);
        Delay(1000);
        NXP74LV595_INPUT_SHCP_GPIO->DR |= (1U << NXP74LV595_INPUT_SHCP_PIN);
        Delay(1000);
    }
    /*Contents of shift register stages are transferred to the storage register and parallel output stages*/
    NXP74LV595_INPUT_STCP_GPIO->DR &= ~(1U << NXP74LV595_INPUT_STCP_PIN);
    Delay(1000);
    NXP74LV595_INPUT_STCP_GPIO->DR |= (1U << NXP74LV595_INPUT_STCP_PIN);
    Delay(1000);
}

NXP的uboot源码

uboot里也有以太网驱动的代码,其中74lv595相关的代码如下:
我找的是野火的uboot代码,源码路径为board\freescale\mx6ullevk\mx6ullevk.c

#define IOX_SDI IMX_GPIO_NR(5, 10)
#define IOX_STCP IMX_GPIO_NR(5, 7)
#define IOX_SHCP IMX_GPIO_NR(5, 11)
#define IOX_OE IMX_GPIO_NR(5, 8)

static iomux_v3_cfg_t const iox_pads[] = {
	/* IOX_SDI */
	MX6_PAD_BOOT_MODE0__GPIO5_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
	/* IOX_SHCP */
	MX6_PAD_BOOT_MODE1__GPIO5_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),
	/* IOX_STCP */
	MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
	/* IOX_nOE */
	MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
};


static enum qn_level seq[3][2] = {
	{0, 1}, {1, 1}, {0, 0}
};

static enum qn_func qn_output[8] = {
	qn_reset, qn_reset, qn_reset, qn_enable, qn_disable, qn_reset,
	qn_disable, qn_disable
};

static void iox74lv_init(void)
{
	int i;

	gpio_direction_output(IOX_OE, 0);

	for (i = 7; i >= 0; i--) {
		gpio_direction_output(IOX_SHCP, 0);
		gpio_direction_output(IOX_SDI, seq[qn_output[i]][0]);
		udelay(500);
		gpio_direction_output(IOX_SHCP, 1);
		udelay(500);
	}

	gpio_direction_output(IOX_STCP, 0);
	udelay(500);
	/*
	 * shift register will be output to pins
	 */
	gpio_direction_output(IOX_STCP, 1);

	for (i = 7; i >= 0; i--) {
		gpio_direction_output(IOX_SHCP, 0);
		gpio_direction_output(IOX_SDI, seq[qn_output[i]][1]);
		udelay(500);
		gpio_direction_output(IOX_SHCP, 1);
		udelay(500);
	}
	gpio_direction_output(IOX_STCP, 0);
	udelay(500);
	/*
	 * shift register will be output to pins
	 */
	gpio_direction_output(IOX_STCP, 1);
};

void iox74lv_set(int index)
{
	int i;

	for (i = 7; i >= 0; i--) {
		gpio_direction_output(IOX_SHCP, 0);

		if (i == index)
			gpio_direction_output(IOX_SDI, seq[qn_output[i]][0]);
		else
			gpio_direction_output(IOX_SDI, seq[qn_output[i]][1]);
		udelay(500);
		gpio_direction_output(IOX_SHCP, 1);
		udelay(500);
	}

	gpio_direction_output(IOX_STCP, 0);
	udelay(500);
	/*
	  * shift register will be output to pins
	  */
	gpio_direction_output(IOX_STCP, 1);

	for (i = 7; i >= 0; i--) {
		gpio_direction_output(IOX_SHCP, 0);
		gpio_direction_output(IOX_SDI, seq[qn_output[i]][1]);
		udelay(500);
		gpio_direction_output(IOX_SHCP, 1);
		udelay(500);
	}

	gpio_direction_output(IOX_STCP, 0);
	udelay(500);
	/*
	  * shift register will be output to pins
	  */
	gpio_direction_output(IOX_STCP, 1);
};

KSZ8081驱动

74lv595驱动

#ifndef __DRV_74LV595_H__
#define __DRV_74LV595_H__

#include <rtthread.h>
#include "fsl_phy.h"
#include "imx6ull.h"
#include "drv_common.h"

typedef enum _NXP74LV595_signal
{
    kSignal_NXP74LV595_Low = 0U,
    kSignal_NXP74LV595_High = 1U
} NXP74LV595_signal_t;

typedef enum _NXP74LV595_parOutputPins
{
    kNXP74LV595_HDMI_nRST = 0U,
    kNXP74LV595_ENET1_nRST = 1U,
    kNXP74LV595_ENET2_nRST = 2U,
    kNXP74LV595_CAN1_2_STBY = 3U,
    kNXP74LV595_BT_nPWD = 4U,
    kNXP74LV595_CSI_RST = 5U,
    kNXP74LV595_CSI_PWDN = 6U,
    kNXP74LV595_LCD_nPWREN = 7U
} NXP74LV595_parOutputPins_t;

void BOARD_NXP74LV595_SetValue_Init(void);
void BOARD_NXP74LV595_SetValue(NXP74LV595_parOutputPins_t pin, NXP74LV595_signal_t value);
#endif /* __DRV_74LV595_H__ */


#include "drv_74lv595.h"

#define NXP74LV595_INPUT_STCP_GPIO GPIO5
#define NXP74LV595_INPUT_STCP_PIN 7U
#define NXP74LV595_INPUT_OE_GPIO GPIO5
#define NXP74LV595_INPUT_OE_PIN 8U
#define NXP74LV595_INPUT_SDI_GPIO GPIO5
#define NXP74LV595_INPUT_SDI_PIN 10U
#define NXP74LV595_INPUT_SHCP_GPIO GPIO5
#define NXP74LV595_INPUT_SHCP_PIN 11U

/*Initial output value of NXP74LV595*/
static uint8_t s_NXP74LV595Output = 0U;



static struct imx6ull_iomuxc _74lv595_gpio[4] = 
{
    {IOMUXC_SNVS_BOOT_MODE0_GPIO5_IO10,0U,0x110B0},
    {IOMUXC_SNVS_BOOT_MODE1_GPIO5_IO11,0U,0x110B0},
    {IOMUXC_SNVS_SNVS_TAMPER7_GPIO5_IO07,0U,0x110B0},
    {IOMUXC_SNVS_SNVS_TAMPER8_GPIO5_IO08,0U,0x110B0},
};


void Delay(uint32_t ticks)
{
    while (ticks--)
    {
        __NOP();
        //__ASM("nop");
    }
}

void BOARD_NXP74LV595_SetValue_Init(void)
{
    imx6ull_gpio_init(&_74lv595_gpio[0]);
    imx6ull_gpio_init(&_74lv595_gpio[1]);
    imx6ull_gpio_init(&_74lv595_gpio[2]);
    imx6ull_gpio_init(&_74lv595_gpio[3]);
}

/* Set parallel pins output value of 74LV595 */
//GDIR寄存器输入0,输出1;
void BOARD_NXP74LV595_SetValue(NXP74LV595_parOutputPins_t pin, NXP74LV595_signal_t value)
{
    uint8_t shiftNumber;
    uint8_t mask;

    /*Init GPIO pins and open output gate of 74LV595*/
    GPIO_Type *OE_GPIO = (GPIO_Type *)rt_ioremap((void *)GPIO5,0x1000);
    GPIO_Type *STCP_GPIO = (GPIO_Type *)rt_ioremap((void *)GPIO5,0x1000);
    GPIO_Type *SDI_GPIO = (GPIO_Type *)rt_ioremap((void *)GPIO5,0x1000);
    GPIO_Type *SHCP_GPIO = (GPIO_Type *)rt_ioremap((void *)GPIO5,0x1000);

    OE_GPIO->DR &= ~(1U << NXP74LV595_INPUT_OE_PIN);
    OE_GPIO->GDIR |= (1U << NXP74LV595_INPUT_OE_PIN);
    STCP_GPIO->GDIR |= (1U << NXP74LV595_INPUT_STCP_PIN);
    SDI_GPIO->GDIR |= (1U << NXP74LV595_INPUT_SDI_PIN);
    SHCP_GPIO->GDIR |= (1U << NXP74LV595_INPUT_SHCP_PIN);

    /*Calculate all parallel pins output value that will be set*/
    s_NXP74LV595Output = (s_NXP74LV595Output & (~(1U << pin))) | (value << pin);

    for (shiftNumber = 0; shiftNumber < 8; shiftNumber++)
    {
        /*High data bits transfer first*/
        mask = (s_NXP74LV595Output >> (7 - shiftNumber)) & 1U;
        if (0 == mask)
        {
            SDI_GPIO->DR &= ~(1U << NXP74LV595_INPUT_SDI_PIN);
        }
        else
        {
            SDI_GPIO->DR |= (1U << NXP74LV595_INPUT_SDI_PIN);
        }
        /*Contents of shift register shifted.*/
        SHCP_GPIO->DR &= ~(1U << NXP74LV595_INPUT_SHCP_PIN);
        Delay(1000);
        SHCP_GPIO->DR |= (1U << NXP74LV595_INPUT_SHCP_PIN);
        Delay(1000);
    }
    /*Contents of shift register stages are transferred to the storage register and parallel output stages*/
    STCP_GPIO->DR &= ~(1U << NXP74LV595_INPUT_STCP_PIN);
    Delay(1000);
    STCP_GPIO->DR |= (1U << NXP74LV595_INPUT_STCP_PIN);
    Delay(1000);
}



drv_eth驱动

在这里插入图片描述
在这里插入图片描述

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-06-16     songchao   support emac driver
 * 2021-06-29     songchao   add phy link detect
 * 2021-08-13     songchao   support dual mac and reduse copy
 */
#include "drv_74lv595.h"
#include "drv_eth.h"
#define DBG_TAG "drv.enet"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

// #define BSP_USING_IMX6ULL_ART_PI

#if (defined(RT_USING_ENET1)) || (defined(RT_USING_ENET2))

#ifdef BSP_USING_IMX6ULL_ART_PI

static struct imx6ull_iomuxc mdio_gpio[2] = 
{
    {IOMUXC_GPIO1_IO06_ENET1_MDIO,0U,0xB029},
    {IOMUXC_GPIO1_IO07_ENET1_MDC,0U,0xB0E9}
};
#else
static struct imx6ull_iomuxc mdio_gpio[2] = 
{
    {IOMUXC_GPIO1_IO06_ENET2_MDIO,0U,0xB029},
    {IOMUXC_GPIO1_IO07_ENET2_MDC,0U,0xB0E9},
};
#endif
enum 
{
#ifdef RT_USING_ENET1
    DEV_ENET1,
#endif

#ifdef RT_USING_ENET2
    DEV_ENET2,
#endif

    DEV_ENET_MAX,
};

static struct rt_imx6ul_ethps _imx6ul_eth_device[DEV_ENET_MAX] = 
{
#ifdef RT_USING_ENET1
    {
        .dev_addr = {0xa8,0x5e,0x45,0x91,0x92,0x93},
        .mac_name = "e1",
        .irq_name = "emac1_intr",
        .enet_phy_base_addr = ENET1,
        .irq_num = IMX_INT_ENET1,
        .phy_num = 2,
        .mac_num = 1,
        .phy_id = 0x22,
        .buffConfig =
        {
            ENET_RXBD_NUM,
            ENET_TXBD_NUM,
            ENET_RXBUFF_ALIGN_SIZE,
            ENET_TXBUFF_ALIGN_SIZE,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            ENET_RXBUFF_TOTAL_SIZE,
            ENET_TXBUFF_TOTAL_SIZE
        },
        .gpio = 
        {
            {IOMUXC_ENET1_RX_DATA0_ENET1_RDATA00,0U,0xB0E9},
            {IOMUXC_ENET1_RX_DATA1_ENET1_RDATA01,0U,0xB0E9},
            {IOMUXC_ENET1_RX_EN_ENET1_RX_EN,0U,0xB0E9},
            {IOMUXC_ENET1_RX_ER_ENET1_RX_ER,0U,0xB0E9},
            {IOMUXC_ENET1_TX_CLK_ENET1_REF_CLK1,1U,0x00F0},
            {IOMUXC_ENET1_TX_DATA0_ENET1_TDATA00,0U,0xB0E9},
            {IOMUXC_ENET1_TX_DATA1_ENET1_TDATA01,0U,0xB0E9},
            {IOMUXC_ENET1_TX_EN_ENET1_TX_EN,0U,0xB0E9}
        }
    },
#endif

#ifdef RT_USING_ENET2
    {
        .dev_addr = {0xa8,0x5e,0x45,0x01,0x02,0x03},
        .mac_name = "e2",
        .irq_name = "emac2_intr",
        .enet_phy_base_addr = ENET2,
        .irq_num = IMX_INT_ENET2,
        .phy_num = 1,
        .mac_num = 2,
        .phy_id = 0x22,
        .buffConfig =
        {
            ENET_RXBD_NUM,
            ENET_TXBD_NUM,
            ENET_RXBUFF_ALIGN_SIZE,
            ENET_TXBUFF_ALIGN_SIZE,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            RT_NULL,
            ENET_RXBUFF_TOTAL_SIZE,
            ENET_TXBUFF_TOTAL_SIZE
        },
        .gpio = 
        {
            {IOMUXC_ENET2_RX_DATA0_ENET2_RDATA00,0U,0xB0E9},
            {IOMUXC_ENET2_RX_DATA1_ENET2_RDATA01,0U,0xB0E9},
            {IOMUXC_ENET2_RX_EN_ENET2_RX_EN,0U,0xB0E9},
            {IOMUXC_ENET2_RX_ER_ENET2_RX_ER,0U,0xB0E9},
            {IOMUXC_ENET2_TX_CLK_ENET2_REF_CLK2,1U,0x00F0},
            {IOMUXC_ENET2_TX_DATA0_ENET2_TDATA00,0U,0xB0E9},
            {IOMUXC_ENET2_TX_DATA1_ENET2_TDATA01,0U,0xB0E9},
            {IOMUXC_ENET2_TX_EN_ENET2_TX_EN,0U,0xB0E9}
        }

    },
#endif
};

void imx6ul_eth_link_change(struct rt_imx6ul_ethps *imx6ul_device,rt_bool_t up)
{
    if(up)
    {
        LOG_D("enet%d link up\n",imx6ul_device->mac_num);
        eth_device_linkchange(&imx6ul_device->parent, RT_TRUE);
        imx6ul_device->phy_link_status = RT_TRUE;
    }
    else
    {
        LOG_D("enet%d link down\n",imx6ul_device->mac_num);
        eth_device_linkchange(&imx6ul_device->parent, RT_FALSE);
        imx6ul_device->phy_link_status = RT_FALSE;
    }
}

void ENET_InitModuleClock(void)
{
    const clock_enet_pll_config_t config = {true, true, false, 1, 1};
    CLOCK_InitEnetPll(&config);
}

rt_err_t enet_buffer_init(enet_buffer_config_t *buffConfig)
{
    void *tx_buff_addr = RT_NULL;
    void *rx_buff_addr = RT_NULL;
    void *tx_bd_addr = RT_NULL;
    void *rx_bd_addr = RT_NULL;

    if(((SYS_PAGE_SIZE<<RX_BUFFER_INDEX_NUM)<buffConfig->rxBufferTotalSize)||
       ((SYS_PAGE_SIZE<<TX_BUFFER_INDEX_NUM)<buffConfig->txBufferTotalSize))
    {
        LOG_E("ERROR: alloc mem not enough for enet driver\n");
        return RT_ERROR;
    }
    rx_buff_addr = rt_pages_alloc(RX_BUFFER_INDEX_NUM);
    if(!rx_buff_addr)
    {
        LOG_E("ERROR: rx buff page alloc failed\n");
        return RT_ERROR;
    }
    buffConfig->rxBufferAlign = (void *)rt_ioremap_nocache(virtual_to_physical(rx_buff_addr), (SYS_PAGE_SIZE<<RX_BUFFER_INDEX_NUM));
    buffConfig->rxPhyBufferAlign = (void *)virtual_to_physical(rx_buff_addr);

    tx_buff_addr = rt_pages_alloc(TX_BUFFER_INDEX_NUM);
    if(!tx_buff_addr)
    {
        LOG_E("ERROR: tx buff page alloc failed\n");
        return RT_ERROR;
    }
    buffConfig->txBufferAlign = (void *)rt_ioremap_nocache(virtual_to_physical(tx_buff_addr), (SYS_PAGE_SIZE<<TX_BUFFER_INDEX_NUM));
    buffConfig->txPhyBufferAlign = (void *)virtual_to_physical(tx_buff_addr);

    rx_bd_addr = rt_pages_alloc(RX_BD_INDEX_NUM);
    if(!rx_bd_addr)
    {
        LOG_E("ERROR: rx bd page alloc failed\n");
        return RT_ERROR;
    }
    buffConfig->rxBdStartAddrAlign = (void *)rt_ioremap_nocache(virtual_to_physical(rx_bd_addr), (SYS_PAGE_SIZE<<RX_BD_INDEX_NUM));
    buffConfig->rxPhyBdStartAddrAlign = virtual_to_physical(rx_bd_addr);

    tx_bd_addr = rt_pages_alloc(TX_BD_INDEX_NUM);
    if(!tx_bd_addr)
    {
        LOG_E("ERROR: tx bd page alloc failed\n");
        return RT_ERROR;
    }
    buffConfig->txBdStartAddrAlign = (void *)rt_ioremap_nocache(virtual_to_physical(tx_bd_addr), (SYS_PAGE_SIZE<<TX_BD_INDEX_NUM));
    buffConfig->txPhyBdStartAddrAlign = virtual_to_physical(tx_bd_addr);

    return RT_EOK;
}

/* EMAC initialization function */
static rt_err_t rt_imx6ul_eth_init(rt_device_t dev)
{
    rt_err_t state;
    struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)dev;
    ENET_Type *base_addr = RT_NULL;
    enet_config_t *config;
    enet_handle_t *handle;
    enet_buffer_config_t *buffConfig;
    rt_uint32_t reg_value;

    imx6ul_device->enet_virtual_base_addr = (ENET_Type *)rt_ioremap((void *)imx6ul_device->enet_phy_base_addr,SYS_PAGE_SIZE);
    base_addr = imx6ul_device->enet_virtual_base_addr;
    config = &imx6ul_device->config;
    handle = &imx6ul_device->handle;
    buffConfig = &imx6ul_device->buffConfig;

    for (int i=0; i<GET_ARRAY_NUM(imx6ul_device->gpio); i++)
    {
        imx6ull_gpio_init(&imx6ul_device->gpio[i]);
    }
    
    IOMUXC_GPR_Type *GPR1 = (IOMUXC_GPR_Type *)rt_ioremap((void *)IOMUXC_GPR,0x1000);
    if(imx6ul_device->mac_num == 1)
    {
        reg_value = GPR1->GPR1;
        reg_value &= ~(IOMUXC_GPR_GPR1_ENET1_CLK_SEL_MASK
                    | IOMUXC_GPR_GPR1_ENET1_CLK_SEL_MASK);
        reg_value |=  IOMUXC_GPR_GPR1_ENET1_TX_CLK_DIR(1);
        reg_value |=  IOMUXC_GPR_GPR1_ENET1_CLK_SEL(0);
        GPR1->GPR1 = reg_value;
    }
    else if(imx6ul_device->mac_num == 2)
    {
        reg_value = GPR1->GPR1;
        reg_value &= ~(IOMUXC_GPR_GPR1_ENET2_CLK_SEL_MASK
                    | IOMUXC_GPR_GPR1_ENET2_CLK_SEL_MASK);
        reg_value |=  IOMUXC_GPR_GPR1_ENET2_TX_CLK_DIR(1);
        reg_value |=  IOMUXC_GPR_GPR1_ENET2_CLK_SEL(0);
        GPR1->GPR1 = reg_value;
    } 

    ENET_InitModuleClock();
    ENET_GetDefaultConfig(config);
    config->interrupt |= (ENET_RX_INTERRUPT);
    state = enet_buffer_init(buffConfig);
    if(state != RT_EOK)
    {
        return state;
    }
    ENET_Init(base_addr, handle, config, buffConfig, &imx6ul_device->dev_addr[0], SYS_CLOCK_HZ);
    ENET_ActiveRead(base_addr);
    rt_hw_interrupt_install(imx6ul_device->irq_num, (rt_isr_handler_t)ENET_DriverIRQHandler, (void *)base_addr,imx6ul_device->irq_name);
    rt_hw_interrupt_umask(imx6ul_device->irq_num);

    return RT_EOK;
}

static rt_err_t rt_imx6ul_eth_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t rt_imx6ul_eth_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_size_t rt_imx6ul_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    return 0;
}

static rt_size_t rt_imx6ul_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    return 0;
}

static rt_err_t rt_imx6ul_eth_control(rt_device_t dev, int cmd, void *args)
{
    struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)dev;
    switch (cmd)
    {
    case NIOCTL_GADDR:
        /* get MAC address */
        if (args)
        {
            rt_memcpy(args, imx6ul_device->dev_addr, MAX_ADDR_LEN);
        }
        else
        {
            return -RT_ERROR;
        }
        break;

    default :
        break;
    }
    return RT_EOK;
}

static status_t read_data_from_eth(rt_device_t dev,void *read_data,uint16_t *read_length)
{
    status_t status = 0;
    uint16_t length = 0;
    ENET_Type *base_addr = RT_NULL;
    enet_config_t *config;
    enet_handle_t *handle;
    enet_buffer_config_t *buffConfig;
    struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)dev;
    base_addr = imx6ul_device->enet_virtual_base_addr;
    config = &imx6ul_device->config;
    handle = &imx6ul_device->handle;
    buffConfig = &imx6ul_device->buffConfig;
    /* Get the Frame size */
    status = ENET_ReadFrame(base_addr,handle,config,read_data,&length);
    if((status == kStatus_ENET_RxFrameEmpty)||(status == kStatus_ENET_RxFrameError))
    {
        ENET_EnableInterrupts(base_addr,ENET_RX_INTERRUPT);
        if(status == kStatus_ENET_RxFrameError)
        {
            /*recv error happend reinitialize mac*/
            ENET_Init(base_addr, handle, config, buffConfig, &imx6ul_device->dev_addr[0], SYS_CLOCK_HZ);
            ENET_ActiveRead(base_addr);
            return kStatus_ENET_RxFrameError;
        }
        else if(status == kStatus_ENET_RxFrameEmpty)
        {
            return kStatus_ENET_RxFrameEmpty;
        }
    }
    *read_length = length;
    return status;
}

/* transmit data*/
rt_err_t rt_imx6ul_eth_tx(rt_device_t dev, struct pbuf *p)
{
    rt_err_t ret = RT_ERROR;
    struct pbuf *q = RT_NULL;
    uint16_t offset = 0;
    uint32_t last_flag = 0;
    status_t status;
    ENET_Type *base_addr = RT_NULL;
    enet_handle_t *handle;
    struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)dev;
    base_addr = imx6ul_device->enet_virtual_base_addr;
    handle = &imx6ul_device->handle;
    RT_ASSERT(p);

    for(q = p;q != RT_NULL;q=q->next)
    {
        if(q->next == RT_NULL)
        {
            last_flag = 1;
        }
        else
        {
            last_flag = 0;
        }
        status = ENET_SendFrame(base_addr, handle, q->payload, q->len,last_flag);
        offset = offset + q->len;
        if(status == kStatus_Success)
        {
        }
        else
        {
            return RT_ERROR;
        }
    }
    if(offset > ENET_FRAME_MAX_FRAMELEN)
    {
        LOG_E("net error send length %d exceed max length\n",offset);
    }
    return ret;
}

struct pbuf *rt_imx6ul_eth_rx(rt_device_t dev)
{
    static struct pbuf *p_s = RT_NULL;
    struct pbuf *p = RT_NULL;
    status_t status;
    uint16_t length = 0;

    if(p_s == RT_NULL)
    {
        p_s = pbuf_alloc(PBUF_RAW, ENET_FRAME_MAX_FRAMELEN, PBUF_POOL);
        if(p_s == RT_NULL)
        {
            return RT_NULL;
        }
    }
    p = p_s;
    status = read_data_from_eth(dev,p->payload,&length);
    if(status == kStatus_ENET_RxFrameEmpty)
    {
        return RT_NULL;
    }
    else if(status == kStatus_ENET_RxFrameError)
    {
        return RT_NULL;
    }
    if(length > ENET_FRAME_MAX_FRAMELEN)
    {
        LOG_E("net error recv length %d exceed max length\n",length);
        return RT_NULL;
    }
    pbuf_realloc(p, length);
    p_s = RT_NULL;
    return p;
}

int32_t get_instance_by_base(void *base)
{
    int32_t i = 0;
    int32_t instance = 0;
    for(i = 0; i < DEV_ENET_MAX; i ++)
    {
        if((void *)_imx6ul_eth_device[i].enet_virtual_base_addr == base)
        {
            break;
        }
    }
    if(i == DEV_ENET_MAX)
    {
        return -1;
    }
    return instance;
    
}
void rx_enet_callback(void *base)
{
    int32_t instance = 0;
    instance = get_instance_by_base(base);
    if(instance == -1)
    {
        LOG_E("interrput match base addr error \n");
        return;
    }
    eth_device_ready(&(_imx6ul_eth_device[instance].parent));
    ENET_DisableInterrupts(base,ENET_RX_INTERRUPT);
}

void tx_enet_callback(void *base)
{
    ENET_DisableInterrupts(base,ENET_TX_INTERRUPT);
}

/*phy link detect thread*/
static void phy_detect_thread_entry(void *param)
{
    bool link = false;
    phy_speed_t speed;
    phy_duplex_t duplex;
    ENET_Type *base_addr = RT_NULL;
    struct rt_imx6ul_ethps *imx6ul_device = (struct rt_imx6ul_ethps *)param;
    base_addr = imx6ul_device->enet_virtual_base_addr;

    // phy_reset(imx6ul_device->phy_base_addr,imx6ul_device->phy_gpio_pin);
    if(imx6ul_device->mac_num == 1)
    {
        BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET1_nRST,kSignal_NXP74LV595_Low);
        rt_thread_delay(50);
        BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET1_nRST,kSignal_NXP74LV595_High);
    }
    else if(imx6ul_device->mac_num == 2)
    {
        BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET2_nRST,kSignal_NXP74LV595_Low);
        rt_thread_delay(50);
        BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET2_nRST,kSignal_NXP74LV595_High);
    }
    rt_thread_mdelay(100);

    PHY_Init(base_addr, imx6ul_device->phy_num, SYS_CLOCK_HZ,imx6ul_device->phy_id);

    int i = 0;
    for(i = 0;i<100;i++)
    {
        PHY_GetLinkStatus(base_addr, imx6ul_device->phy_num, &link);
        LOG_D("phy %s , link status %d",imx6ul_device->mac_name,link);
        if (link)
        {
            /* Get the actual PHY link speed. */
            PHY_GetLinkSpeedDuplex(base_addr, imx6ul_device->phy_num, &speed, &duplex);
            /* Change the MII speed and duplex for actual link status. */
            imx6ul_device->config.miiSpeed = (enet_mii_speed_t)speed;
            imx6ul_device->config.miiDuplex = (enet_mii_duplex_t)duplex;
            break;
        }
        else
        {
            LOG_E("PHY : %s , Link down, please check the cable connection and link partner setting.\r\n",imx6ul_device->mac_name);
        }
    }

    // PHY_GetLinkStatus(base_addr, imx6ul_device->phy_num, &link);
    // if (link)
    // {
    //     /* Get the actual PHY link speed. */
    //     PHY_GetLinkSpeedDuplex(base_addr, imx6ul_device->phy_num, &speed, &duplex);
    //     /* Change the MII speed and duplex for actual link status. */
    //     imx6ul_device->config.miiSpeed = (enet_mii_speed_t)speed;
    //     imx6ul_device->config.miiDuplex = (enet_mii_duplex_t)duplex;
    // }
    // else
    // {
    //     LOG_E("\r\nPHY Link down, please check the cable connection and link partner setting.\r\n");
    // }

    while(1)
    {
        PHY_GetLinkStatus(base_addr, imx6ul_device->phy_num, &link);
        if(link != imx6ul_device->phy_link_status)
        {
            if(link == true)
            {
                PHY_StartNegotiation(base_addr,imx6ul_device->phy_num);
                
            }            
            imx6ul_eth_link_change(imx6ul_device,link);
        }
        rt_thread_delay(DETECT_DELAY_ONE_SECOND);
    }
}

_internal_ro struct rt_device_ops _k_enet_ops =
{
    rt_imx6ul_eth_init,
    rt_imx6ul_eth_open,
    rt_imx6ul_eth_close,
    rt_imx6ul_eth_read,
    rt_imx6ul_eth_write,
    rt_imx6ul_eth_control,
};

static int imx6ul_eth_init(void)
{
    rt_err_t state = RT_EOK;
    char link_detect[10];

    #if 1
    imx6ull_gpio_init(&mdio_gpio[0]);
    imx6ull_gpio_init(&mdio_gpio[1]);
    #endif
    
    BOARD_NXP74LV595_SetValue_Init();
    BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET1_nRST,kSignal_NXP74LV595_High);
    BOARD_NXP74LV595_SetValue(kNXP74LV595_ENET2_nRST,kSignal_NXP74LV595_High);

    for (int idx=0; idx<GET_ARRAY_NUM(_imx6ul_eth_device); idx++)
    {
        _imx6ul_eth_device[idx].parent.parent.ops = &_k_enet_ops;
        _imx6ul_eth_device[idx].parent.eth_rx     = rt_imx6ul_eth_rx;
        _imx6ul_eth_device[idx].parent.eth_tx     = rt_imx6ul_eth_tx;
        _imx6ul_eth_device[idx].phy_link_status   = RT_FALSE;

        /* register eth device */
        state = eth_device_init(&(_imx6ul_eth_device[idx].parent), _imx6ul_eth_device[idx].mac_name);
        if (RT_EOK == state)
        {
            LOG_E("emac device init success\n");
        }
        else
        {
            LOG_E("emac device init faild: %d", state);
            state = -RT_ERROR;
        }
        
        rt_sprintf(link_detect,"link_d%d",_imx6ul_eth_device[idx].mac_num);
        /* start phy link detect */
        rt_thread_t phy_link_tid;
        phy_link_tid = rt_thread_create(link_detect,
                                phy_detect_thread_entry,
                                &_imx6ul_eth_device[idx],
                                4096,
                                RT_THREAD_PRIORITY_MAX - 2,
                                2);
        if (phy_link_tid != RT_NULL)
        {
            rt_thread_startup(phy_link_tid);
        }
        memset(link_detect,0,sizeof(link_detect));
    }

    return state;
}
INIT_DEVICE_EXPORT(imx6ul_eth_init);
#endif

kernel\bsp\yh\libraries\sdk\devices\MCIMX6Y2\drivers\fsl_phy.c添加如下宏定义
在这里插入图片描述

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值