/*
*********************************************************************************************************
*
*	模块名称 : DM9KAEP 底层驱动模块(For STM32F4XX， uip)
*	文件名称 : dm9k_uip.c
*	版    本 : V1.1
*	说    明 : 这是硬件底层驱动程序的主文件。每个c文件可以 #include "bsp.h" 来包含所有的外设驱动模块。
*			   bsp = Borad surport packet 板级支持包
*	修改记录 :
*		版本号  日期        作者     说明
*		V1.0    2013-03-01  armfly   正式发布
*		V1.1    2013-06-20  armfly   规范注释，添加必要说明
*
*	Copyright (C), 2013-2014, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/

#include "dm9k.h"
#include "lwip_dm9k.h"

/****************************************************************************
 * Copyright (C), 2009-2010, www.armfly.com  安富莱电子
 *
 * 【本驱动在安富莱STM32F103ZE-EK开发板上调试通过             】
 * 【QQ: 1295744630, 旺旺：armfly, Email: armfly@qq.com       】
 *
 * 文件名: dm9k_uip.c
 * 内容简述: Davicom DM9KA uP NIC fast Ethernet driver for uIP.
 *
 * 文件历史:
 * 版本号  日期       作者    说明
 * v0.1    2010-01-18 armfly  创建该文件
 *
 */

#include "Lwip_init.h"

/* DM9KA 接收函数设置宏 */
// #define Rx_Int_enable
#define Max_Int_Count 1

#define Broadcast_Jump
#define Max_Broadcast_Lenth 500

/* DM9KA 传送函数设置宏 */
#define Max_Send_Pack 2

// #define FifoPointCheck
// #define DM9KA_FLOW_CONTROL
// #define DM9KA_UPTO_100M
// #define Fifo_Point_Check
// #define Point_Error_Reset
#define Fix_Note_Address
/*=============================================================================
  系统全域的变量
  =============================================================================*/
uint8_t SendPackOk = 0;

// uint8_t s_FSMC_Init_Ok = 0; /* 用于指示FSMC是否初始化 */

// #define printk(...)
#define printk myprintf

/*******************************************************************************
 *	函数名: dm9k_udelay
 *	参  数: time ： 延迟时间，不精确，us级别
 *	返  回: 无
 *	功  能: 延迟函数
 */
void dm9k_udelay(uint16_t time)
{
    us_delay(time);
}

/*******************************************************************************
 *	函数名: ior
 *	参  数: reg ：寄存器地址
 *	返  回: 无
 *	功  能: 读出寄存器的值
 */
uint8_t dm9k_ReadReg(uint8_t reg)
{
    NET_REG_ADDR = reg;
    return (NET_REG_DATA);
}

/*******************************************************************************
 *	函数名: iow
 *	参  数: reg ：寄存器地址
 *			writedata : 写入的数据
 *	返  回: 无
 *	功  能: 写DM9KAE寄存器的值
 */
void dm9k_WriteReg(uint8_t reg, uint8_t writedata)
{
    NET_REG_ADDR = reg;
    NET_REG_DATA = writedata;
}

/*******************************************************************************
 *	函数名: dm9k_hash_table
 *	参  数: 无
 *	返  回: 无
 *	功  能: 设置 DM9KA MAC 、 广播 、 多播 寄存器
 */
void dm9k_hash_table(uint8_t *MacAdd)
{
    uint8_t i;

    /* 设置 网卡 MAC 位置，来自於 MyHardware */
    for (i = 0; i < 6; i++)
        dm9k_WriteReg(DM9K_REG_PAR + i, MacAdd[i]);

    for (i = 0; i < 8; i++) /* 清除 网卡多播设置 */
        dm9k_WriteReg(DM9K_REG_MAR + i, 0xff);
}

/*******************************************************************************
 *	函数名: dm9k_reset
 *	参  数: 无
 *	返  回: 无
 *	功  能: 对DM9KAE进行软件复位
 */
void dm9k_reset(void)
{
    dm9k_WriteReg(DM9K_REG_NCR, DM9K_REG_RESET); /* 对 DM9KA 进行软件重置 */
    dm9k_udelay(10);                             /* delay 10us */
    dm9k_WriteReg(DM9K_REG_NCR, DM9K_REG_RESET); /* 对 DM9KA 进行软件重置 */
    dm9k_udelay(10);                             /* delay 10us */

    /* 基本记存器相关设置 */
    dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_OFF);   /* 开启内存自环模式 */
    dm9k_WriteReg(DM9K_REG_TCR2, DM9K_TCR2_SET); /* 设置 LED 显示模式1:全双工亮，半双工灭 */

    /* 清除多余资讯 */
    dm9k_WriteReg(DM9K_REG_NSR, 0x2c);
    dm9k_WriteReg(DM9K_REG_TCR, 0x00);
    dm9k_WriteReg(DM9K_REG_ISR, 0x3f);

#ifdef DM9KA_FLOW_CONTROL
    dm9k_WriteReg(DM9K_REG_BPTR, DM9K_BPTR_SET); /* 半双工流控设置 */
    dm9k_WriteReg(DM9K_REG_FCTR, DM9K_FCTR_SET); /* 全双工流控设置 */
    dm9k_WriteReg(DM9K_REG_FCR, DM9K_FCR_SET);   /* 开启流控设置 */
#endif

#ifdef DM9KA_UPTO_100M
    /* DM9KA无此寄存器 */
    dm9k_WriteReg(DM9K_REG_OTCR, DM9K_OTCR_SET); /* 工作频率到 100Mhz 设置 */
#endif

#ifdef Rx_Int_enable
    dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_SET); /* 开启 中断模式 */
#else
    dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_OFF); /* 关闭 中断模式 */
#endif

    dm9k_WriteReg(DM9K_REG_RCR, DM9K_RCR_SET); /* 开启 接收工能 */

    SendPackOk = 0;
}

/*******************************************************************************
 *	函数名: dm9k_phy_write
 *	参  数: phy_reg ： 寄存器地址
 *			writedata ： 写入的数据
 *	返  回: 无
 *	功  能: 写DM9KA PHY 寄存器
 */
void dm9k_phy_write(uint8_t phy_reg, uint16_t writedata)
{
    /* 设置写入 PHY 寄存器的位置 */
    dm9k_WriteReg(DM9K_REG_EPAR, phy_reg | DM9K_PHY);

    /* 设置写入 PHY 寄存器的值 */
    dm9k_WriteReg(DM9K_REG_EPDRH, (writedata >> 8) & 0xff);
    dm9k_WriteReg(DM9K_REG_EPDRL, writedata & 0xff);

    dm9k_WriteReg(DM9K_REG_EPCR, 0x0a); /* 将资料写入 PHY 寄存器 */

    while (dm9k_ReadReg(DM9K_REG_EPCR) & 0x01)
        ; /* 查寻是否执行结束 */

    dm9k_WriteReg(DM9K_REG_EPCR, 0x08); /* 清除写入命令 */
}

/*******************************************************************************
 *	函数名: dm9k_phy_read
 *	参  数: phy_reg ： 寄存器地址
 *	返  回: 无
 *	功  能: 读DM9KA PHY 寄存器
 */
uint16_t dm9k_phy_read(uint8_t phy_reg)
{
    uint16_t temp;

    /* 设置写入 PHY 寄存器的位置 */
    dm9k_WriteReg(DM9K_REG_EPAR, phy_reg | DM9K_PHY);

    /* 设置写入 PHY 寄存器的值 */
    dm9k_WriteReg(DM9K_REG_EPCR, 0x0C); // 选中PHY，发送读命令

    while (dm9k_ReadReg(DM9K_REG_EPCR) & 0x01)
        ; /* 查寻是否执行结束 */

    dm9k_WriteReg(DM9K_REG_EPCR, 0x00); // 清除读命令
    temp = (dm9k_ReadReg(DM9K_REG_EPDRH) << 8) | (dm9k_ReadReg(DM9K_REG_EPDRL));

    return temp;
}

/*******************************************************************************
 *	函数名: dm9k_linkstat
 *	参  数: 无
 *	返  回: 0,未连接
 *			1,已连接
 *	功  能: 获取DM9K的连接状态
 */
uint8_t dm9k_linkstat(void)
{
    uint8_t linkchanged = 0;

    linkchanged = (dm9k_ReadReg(DM9K_PHY_BMSR) >> 6) & 0x01;

    return linkchanged;
}

/*******************************************************************************
 *	函数名: DM9K_Get_SpeedAndDuplex
 *	参  数: 无
 *	返  回: 0,100M半双工
 *			1,100M全双工
 *			2,10M半双工
 *			3,10M全双工
 *			0XFF,连接失败！
 *	功  能: 获取DM9K的连接速度和双工模式
 */
uint8_t DM9K_Get_SpeedAndDuplex(void)
{
    uint8_t temp;
    uint8_t i = 0;

    while (!(dm9k_phy_read(DM9K_PHY_BMSR) & 0x0020)) // 等待自动协商完成
    {
        ms_delay(100);
        i++;

        if (i > 100)
            return 0XFF; // 自动协商失败
    }

    temp = ((dm9k_ReadReg(DM9K_REG_NSR) >> 6) & 0x02);  // 获取DM9K的连接速度 0:100M 1:10M
    temp |= ((dm9k_ReadReg(DM9K_REG_NCR) >> 3) & 0x01); // 获取DM9K的双工状态

    return temp;
}

/*******************************************************************************
 *	函数名: dm9k_initnic
 *	参  数: 无
 *	返  回: 无
 *	功  能: 初始化DM9KAE
 */
void dm9k_initnic(void)
{
    uint8_t temp;

    // if (DM9KA_ID != dm9k_ReadID()) /* 进行 设备ID读取判断 */
    //     return;

    dm9k_WriteReg(DM9K_REG_GPCR, 0x01); /* 设置GPCR bit[0]=1，使DM9K为GPIO0为输出 */
    dm9k_WriteReg(DM9K_REG_GPR, 0x00);  /* GPR bit[0]=0，使GPIO0输出低电平以激活内部PHY */
    ms_delay(5);

    dm9k_WriteReg(DM9K_REG_NCR, DM9K_REG_RESET); /* 对 DM9KA 进行软件重置 */
    dm9k_udelay(100);                            /* delay 100us */
    dm9k_WriteReg(DM9K_REG_NCR, DM9K_REG_RESET); /* 对 DM9KA 进行软件重置 */
    dm9k_udelay(100);                            /* delay 100us */

    /* 清除多余资讯 */
    dm9k_WriteReg(DM9K_REG_NCR, 0x00); /* 复位完成，设置正常工作模式 */
    dm9k_WriteReg(DM9K_REG_NSR, 0x2c); /* 清除各种状态标志位 */
    dm9k_WriteReg(DM9K_REG_TCR, 0x00); /* 清除 发送控制寄存器 */
    dm9k_WriteReg(DM9K_REG_ISR, 0x3f); /* 清除 中断状态寄存器 */

    /* 基本记存器相关设置 */
    dm9k_WriteReg(DM9K_REG_IMR, 0x80);  /* 开启内存自环模式 */
    dm9k_WriteReg(DM9K_REG_TCR2, 0x80); /* 设置 LED 显示模式1:全双工亮，半双工灭 */

    dm9k_WriteReg(DM9K_REG_BPTR, 0x3F); /* 半双工流控设置 默认 0x37 */
    dm9k_WriteReg(DM9K_REG_FCTR, 0x38); /* 全双工流控设置 默认 0x38 */

    dm9k_WriteReg(DM9K_REG_RCR, DM9K_RCR_SET); /* 开启 接收工能 */

    dm9k_hash_table(dm9kdev.mac); /* 设置 DM9KA MAC 及 多播*/

    dm9k_WriteReg(DM9K_REG_GPR, DM9K_PHY_OFF); /* 关闭 PHY ，进行 PHY 设置*/
    dm9k_phy_write(DM9K_PHY_BMCR, 0x8000);     /* 重置 PHY 的寄存器 */

    /* 连接模式设置
      0x0000 : 固定10M半双工	ANAR_Value：0x21
      0x0100 : 固定10M全双工	ANAR_Value：0x41
      0x2000 : 固定100M半双工	ANAR_Value：0x81
      0x2100 : 固定100M全双工	ANAR_Value：0x0101
      0x1000 : 自适应模式		ANAR_Value：0x01E1
    */
    dm9k_phy_write(DM9K_PHY_BMCR, 0x1000); /* 设置 基本连接模式 */
    dm9k_phy_write(DM9K_PHY_ANAR, 0x01E1); /* 设置 自适应模式相容表 */

    dm9k_WriteReg(DM9K_REG_GPR, DM9K_PHY_ON); /* 结束 PHY 设置, 开启 PHY */

    temp = DM9K_Get_SpeedAndDuplex(); /* 获取DM9K的连接速度和双工状态 */
    if (temp != 0xFF)                 /* 连接成功，通过串口显示连接速度和双工状态 */
    {
        printf("DM9K Speed:%dMbps,Duplex:%s duplex mode\r\n", (temp & 0x02) ? 10 : 100, (temp & 0x01) ? "Full" : "Half");
    }
    else
    {
        printf("DM9K Establish Link Failed!\r\n");
    }

    dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_SET); /* 设置中断 */

    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, GPIO_PinSource15);
    /* 配置 EXTI LineXXX */
    EXTI_InitStructure.EXTI_Line    = EXTI_Line15;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    EXTI_ClearITPendingBit(EXTI_Line15);

    NVIC_InitStructure.NVIC_IRQChannel                   = EXTI15_10_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    SendPackOk = 0;
}

/*******************************************************************************
 *	函数名: dm9k_receive_packet
 *	参  数: _uip_buf : 接收缓冲区
 *	返  回: > 0 表示接收的数据长度, 0表示没有数据
 *	功  能: 读取一包数据
 */
uint16_t dm9k_receive_packet(uint8_t *_uip_buf)
{
    uint16_t  ReceiveLength;
    uint16_t *ReceiveData;
    uint8_t   rx_int_count = 0;
    uint8_t   rx_checkbyte;
    uint16_t  rx_status, rx_length;
    uint8_t   jump_packet;
    uint16_t  i;
    uint16_t  calc_len;
    uint16_t  calc_MRR;

    do
    {
        ReceiveLength = 0; /* 清除接收的长度 */
        ReceiveData   = (uint16_t *)_uip_buf;
        jump_packet   = 0;             /* 清除跳包动作 */
        dm9k_ReadReg(DM9K_REG_MRCMDX); /* 读取内存数据，地址不增加 */
        /* 计算内存数据位置 */
        calc_MRR     = (dm9k_ReadReg(DM9K_REG_MRRH) << 8) + dm9k_ReadReg(DM9K_REG_MRRL);
        rx_checkbyte = dm9k_ReadReg(DM9K_REG_MRCMDX); /*  */

        if (rx_checkbyte == DM9K_PKT_RDY) /* 取 */
        {
            /* 读取封包相关资讯 及 长度 */
            NET_REG_ADDR = DM9K_REG_MRCMD;
            rx_status    = NET_REG_DATA;
            rx_length    = NET_REG_DATA;

            /* 若收到超过系统可承受的封包，此包跳过 */
            if (rx_length > Max_Ethernet_Lenth)
                jump_packet = 1;

#ifdef Broadcast_Jump
            /* 若收到的广播或多播包超过特定长度，此包跳过 */
            if (rx_status & 0x4000)
            {
                if (rx_length > Max_Broadcast_Lenth)
                    jump_packet = 1;
            }
#endif
            /* 计算下一个包的指针位 , 若接收长度为奇数，需加一对齐偶字节。*/
            /* 若是超过 0x3fff ，则需回归绕到 0x0c00 起始位置 */
            calc_MRR += (rx_length + 4);

            if (rx_length & 0x01)
                calc_MRR++;

            if (calc_MRR > 0x3fff)
                calc_MRR -= 0x3400;

            if (jump_packet == 0x01)
            {
                /* 将指针移到下一个包的包头位置 */
                dm9k_WriteReg(DM9K_REG_MRRH, (calc_MRR >> 8) & 0xff);
                dm9k_WriteReg(DM9K_REG_MRRL, calc_MRR & 0xff);
                continue;
            }

            /* 开始将内存的资料搬到到系统中，每次移动一个 word */
            calc_len = (rx_length + 1) >> 1;

            for (i = 0; i < calc_len; i++)
                ReceiveData[i] = NET_REG_DATA;

            /* 将包长回报给 TCP/IP 上层，并减去最後 4 BYTE 的 CRC-32 检核码 */
            ReceiveLength = rx_length - 4;

            rx_int_count++; /* 累计收包次数 */

#ifdef FifoPointCheck

            if (calc_MRR != ((dm9k_ReadReg(DM9K_REG_MRRH) << 8) + dm9k_ReadReg(DM9K_REG_MRRL)))
            {
#ifdef Point_Error_Reset
                dm9k_reset(); /* 若是指针出错，重置 */
                return ReceiveLength;
#endif
                /*若是指针出错，将指针移到下一个包的包头位置  */
                dm9k_WriteReg(DM9K_REG_MRRH, (calc_MRR >> 8) & 0xff);
                dm9k_WriteReg(DM9K_REG_MRRL, calc_MRR & 0xff);
            }

#endif
            return ReceiveLength;
        }
        else
        {
            if (rx_checkbyte == DM9K_PKT_NORDY) /* 未收到包 */
            {
                dm9k_WriteReg(DM9K_REG_ISR, 0x3f); /*  */
            }
            else
            {
                dm9k_initnic(); /* 接收指针出错，重置 */
            }

            return (0);
        }
    } while (rx_int_count < Max_Int_Count); /* 是否超过最多接收封包计数 */

    return 0;
}

/*******************************************************************************
 *	函数名: dm9k_send_packet
 *	参  数: p_char : 发送数据缓冲区
 *			length : 数据长度
 *	返  回: 无
 *	功  能: 发送一包数据
 */
void dm9k_send_packet(uint8_t *p_char, uint16_t length)
{
    uint16_t      SendLength = length;
    uint16_t     *SendData   = (uint16_t *)p_char;
    uint16_t      i;
    uint16_t      calc_len;
    __IO uint16_t calc_MWR;

#ifdef FifoPointCheck
    /* 计算下一个传送的指针位 , 若接收长度为奇数，需加一对齐偶字节。*/
    /* 若是超过 0x0bff ，则需回归绕到 0x0000 起始位置 */
    calc_MWR = (dm9k_ReadReg(DM9K_REG_MWRH) << 8) + dm9k_ReadReg(DM9K_REG_MWRL);
    calc_MWR += SendLength;

    if (SendLength & 0x01)
        calc_MWR++;

    if (calc_MWR > 0x0bff)
        calc_MWR -= 0x0c00;

#endif

    dm9k_WriteReg(DM9K_REG_TXPLH, (SendLength >> 8) & 0xff); /* 设置传送封包的长度 */
    dm9k_WriteReg(DM9K_REG_TXPLL, SendLength & 0xff);

    /* 开始将系统的资料搬到到内存中，每次移动一个 word */
    NET_REG_ADDR = DM9K_REG_MWCMD;
    calc_len     = (SendLength + 1) >> 1;

    for (i = 0; i < calc_len; i++)
    {
        NET_REG_DATA = SendData[i];
    }

    dm9k_WriteReg(DM9K_REG_TCR, DM9K_TCR_SET); /* 进行传送 */

#ifdef FifoPointCheck

    if (calc_MWR != ((dm9k_ReadReg(DM9K_REG_MWRH) << 8) + dm9k_ReadReg(DM9K_REG_MWRL)))
    {
#ifdef Point_Error_Reset

        /* 若是指针出错，等待此一封包送完 , 之後进行重置 */
        while (dm9k_ReadReg(DM9K_REG_TCR) & DM9K_TCR_SET)
            dm9k_udelay(5);

        dm9k_reset();
        return;
#endif
        /*若是指针出错，将指针移到下一个传送包的包头位置  */
        dm9k_WriteReg(DM9K_REG_MWRH, (calc_MWR >> 8) & 0xff);
        dm9k_WriteReg(DM9K_REG_MWRL, calc_MWR & 0xff);
    }

#endif
    return;
}

/*******************************************************************************
 *	函数名: dm9k_init
 *	参  数: 无
 *	返  回: 无
 *	功  能: uIP 接口函数,初始化网卡
 */
void dm9k_init(void)
{
    dm9k_fsmc();

    // s_FSMC_Init_Ok = 1;

    dm9k_initnic();
}

/*******************************************************************************
 *	函数名: etherdev_send
 *	参  数: p_char : 数据缓冲区
 *			length : 数据长度
 *	返  回: 无
 *	功  能: uIP 接口函数,发送一包数据
 */
void etherdev_send(uint8_t *p_char, uint16_t length)
{
    dm9k_send_packet(p_char, length);
}

uint16_t etherdev_read(uint8_t *p_char)
{
    return dm9k_receive_packet(p_char);
}

/*******************************************************************************
 *	函数名: etherdev_chkmedia
 *	参  数: p_char : 数据缓冲区
 *			length : 数据长度
 *	返  回: 无
 *	功  能: uIP 接口函数, 检测网络连接状态
 */
void etherdev_chkmedia(void)
{
    //	uint8_t status;

    while (!(dm9k_ReadReg(DM9K_REG_NSR) & DM9K_PHY))
    {
        dm9k_udelay(2000);
    }
}

/*******************************************************************************
 *	函数名: dm9k_ReadID
 *	参  数: 无
 *	返  回: 无
 *	功  能: 读取芯片ID
 */
uint32_t dm9k_ReadID(void)
{
    uint8_t vid1, vid2, pid1, pid2;

    vid1 = dm9k_ReadReg(DM9K_REG_VID_L) & 0xFF;
    vid2 = dm9k_ReadReg(DM9K_REG_VID_H) & 0xFF;
    pid1 = dm9k_ReadReg(DM9K_REG_PID_L) & 0xFF;
    pid2 = dm9k_ReadReg(DM9K_REG_PID_H) & 0xFF;

    return (pid2 << 24) | (pid1 << 16) | (vid2 << 8) | vid1;
}

void dm9k_fsmc(void)
{
    FSMC_NORSRAMInitTypeDef       FSMC_NORSRAMInitStructure;
    FSMC_NORSRAMTimingInitTypeDef p;

    p.FSMC_AddressSetupTime      = 6;
    p.FSMC_AddressHoldTime       = 2;
    p.FSMC_DataSetupTime         = 4;
    p.FSMC_BusTurnAroundDuration = 1;
    p.FSMC_CLKDivision           = 0;
    p.FSMC_DataLatency           = 0;
    p.FSMC_AccessMode            = FSMC_AccessMode_A;

    FSMC_NORSRAMInitStructure.FSMC_Bank                  = FSMC_Bank1_NORSRAM3;
    FSMC_NORSRAMInitStructure.FSMC_DataAddressMux        = FSMC_DataAddressMux_Disable;
    FSMC_NORSRAMInitStructure.FSMC_MemoryType            = FSMC_MemoryType_SRAM;
    FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth       = FSMC_MemoryDataWidth_16b;
    FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode       = FSMC_BurstAccessMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait      = FSMC_AsynchronousWait_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity    = FSMC_WaitSignalPolarity_Low;
    FSMC_NORSRAMInitStructure.FSMC_WrapMode              = FSMC_WrapMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive      = FSMC_WaitSignalActive_BeforeWaitState;
    FSMC_NORSRAMInitStructure.FSMC_WriteOperation        = FSMC_WriteOperation_Enable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignal            = FSMC_WaitSignal_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ExtendedMode          = FSMC_ExtendedMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WriteBurst            = FSMC_WriteBurst_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
    FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct     = &p;

    FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);
    FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM3, ENABLE);
}