精华内容
下载资源
问答
  • IOT物联网专用、SPI接口的百兆以太网芯片DM9051NP-DM9051NP相关资料.rar
  • DM9051驱动源码介绍

    2018-10-12 16:20:20
    DM9051是基于SPI接口的以太网MAC+PHY集成IC,本文档包括驱动源码及其源码解读,包括寄存器初始化,接口函数封装
  • DM9051 SPI转网卡设计电路参考
  • DM9051硬件资料.rar

    2020-08-13 11:32:09
    DM9051NP,SPI转100M网卡芯片电路图以及PCB资料;芯片资料以及周边BOM物料均有描述,支持光口和电口,适用于STM32单片机或者MTK手机平板平台。
  • DM9051NP 的AD pcblib文件

    2018-09-26 11:42:43
    DM9051NP 的AD pcblib文件 贴片封装 比较适合手工焊接
  • Raspberry pi zero or Raspberry pi 3 Model B DAVICOM DM9051 Linux Driver
  • 这是DAVICOM原厂的设计资料,DM9051NP电路图,保证最新+能用,如需相关咨询请站内联系我。
  • DM9051NP SPI接口以太网模块是联杰国际(DAVICOM)为了方便嵌入式ARM、MCU单片机系统进行以太网通信而开发出的解决方案。
  • lnx_dm9051_dts_Ver2.2zcd.rar

    2021-08-01 15:58:03
    DM9051网卡驱动(SPI转网卡芯片,支持DTS设备树的LINUX平台驱动源码)
  • NUVOTON NuTinyM051 + DAVICOM DM9051整合uIP 實現WEB Server
  • stm32-DM9051-uip

    2018-08-23 17:27:38
    stm32-dm9051最简单历程,用uip实现,实现最简单的tcp透传
  • DM9051_Linux_Driver.rar

    2020-08-13 11:40:39
    基于LINUX平台下的SPI网卡,DM9051的驱动,版本1.69.3 符合量产版本;目前大量MTK,RK,高通等产品批量使用中。
  • ESP32+SPI网卡DM9051NP的电路图
  • DM9051NP,多用于Linux平台的SPI转网口,实际速率可达9Mbps以上。 (已搭配MTK,高通,三星,ASR,展讯,云知声uni,TI,博通等平台,目前最高支持Linux4.13 安卓9.1系统。)
  • 基于STM32F103ZE处理器,采用SPI接口的DM9051(集成MAC+PHY)进行以太网接口,对整个开发过程进行指导。
  • 基于STM32F103C8+DM9051NP的SPI网卡 UIP协议栈,支持串口透传网口,UDP,TCP等功能。
  • DM9051_ESP32-S_Demo_测试说明.pdf
  • NUC_M051_uIP_DM9051_webserver实现串口服务器, 整个项目工程
  • stm32f103+dm9051 LwIP網路平台源代碼

    热门讨论 2014-08-25 10:39:22
    stm32f103採用SPI接口的乙太控制器DM9051, 在uCOS-ii+LwIP平台和Keil工具上開發Web, 手機控制LED, 內含simple iPerf.
  • SPI接口的10-100M网卡DM9051: 本驱动文件,适应于RK、MTK、高通、全志等安卓4.4-9.0平台,Linux3.1.X-Linux4.9适用;需要更详细资料,可站内联系。
  • 对于没有内置EMAC的处理器,如全志F1C100S等,如果需要连接有线网,一般只有2种办法:SPI接口扩展(DM9051NP)和USB接口扩展。本文提供SPI接口的DM9051的驱动,以及官方的驱动问题修正。 环境: 处理器:F1C100S ...

    对于没有内置EMAC的处理器,如全志F1C100S等,如果需要连接有线网,一般只有2种办法:SPI接口扩展(DM9051NP)和USB接口扩展。本文提供SPI接口的DM9051的驱动,以及官方的驱动问题修正。

    环境:

    处理器:F1C100S

    软件环境:Linux-4.15

    DM9051驱动,一般提供轮询(Poll)方式和中断(Interrupt)方式,对于现在网上能下载到的驱动,如1.69.3等,如果使用轮询方式,也可以工作,但CPU占用率非常高,很不实际。因此中断方式是首选。然而对于中断方式,1.69版本驱动及可收集到的驱动,要注意以下情况:

    1、对于IRQ引脚中断,原本驱动中设置成IRQF_TRIG_NONE。如果DTS设备树中未指定该引脚的中断方式,需要在驱动中进行指定。要注意的是,因为spi通讯不支持并发,因此必须带有IRQF_ONESHOT,以保证不会嵌套触发中断。同时,如果配置为下降沿触发(IRQF_TRIG_FALLING),当中断程序中处理较大数据帧较耗时的情况下,极有可能出现无法触发下一次中断,导致无法进行接收数据包。

    因此,中断方式驱动时,设置为电平触发是稳妥的。我的配置是低电平触发,即 IRQF_TRIG_LOW | IRQF_ONESHOT 。

    ret = request_threaded_irq(spi->irq, NULL, dm951_irq,
    	  			   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
    	  			   ndev->name, db);

    2、官方驱动中存在bug,导致的现象是 Ping延迟从100ms渐渐降低到1ms,然后再次循环。原因在于官方驱动在函数dm9051_start_xmit 中,虽然正确启动了任务 dm9051_tx_work ,但在函数dm9051_tx_work 中错误的使用了rx_work结构来获取当前db结构数据,导致dm9051_tx函数中判断db->bt.prob_cntStopped为0。  

    而官方驱动还是能工作的原因在于,在中断服务程序中,判断了是否需要发送数据,如果需要发送,则进行发送。因此导致了Ping的延迟不稳定。

    /* 系统发送数据包函数 */
    static netdev_tx_t 
    dm9051_start_xmit(struct sk_buff *skb, struct net_device *dev)
    {
    	board_info_t *db = netdev_priv(dev);
    
    	spin_lock(&db->statelock);//mutex_lock(&db->addr_lock);
    
    	toend_stop_queue1(dev, db->bt.prob_cntStopped++ );
    	skb_queue_tail(&db->txq, skb); // JJ: a skb add to the tail of the list '&db->txq'
    	driver_dtxt_step(db, '0'); //driver_dtxt_step(db, 'q'); // Normal
    	spin_unlock(&db->statelock);//mutex_unlock(&db->addr_lock);
    
    	schedule_work(&db->tx_work);
    
    	return NETDEV_TX_OK;
    }
    /* 实际向DM9051发送数据包函数 */
    static void dm9051_tx_work(struct work_struct *work)
    {
    	board_info_t *db = container_of(work, board_info_t, rxctrl_work); /* 这里不应该使用rxctrl_work,而应该是tx_work */
    	dm9051_tx(db);
    }
    
    static void dm9051_tx(board_info_t *db)
    {
    	struct net_device *dev = db->ndev;
    	if (db->bt.prob_cntStopped)  // This is more exactly right!!
    	{
    		  #if LOOP_XMIT
    		    mutex_lock(&db->addr_lock);
    		    dm9051_continue_xmit_inRX(db); //=dm9051_continue_poll_xmit
    		    opening_wake_queue1(dev); 
    		    mutex_unlock(&db->addr_lock);
    		  #endif //LOOP_XMIT
    	}
    }

     

    修正了这两个之后,驱动即可以正常工作。

    展开全文
  • 基于STM32F030C8T6 单片机控制DM9051以太网RJ45模块设计资料 包含原理图及PCB文件
  • 最近在某個论坛裡,看到这篇STM32F103+DM9051_UIP_SPI to 以太网,透过該版主拿到了DM9051NP 以太网卡,刚好手上有Nuvoton NuTiny M051 和 M451,就先用M051拿来做一个简单的web server,透过web server 来控制开发板...

           最近在某個论坛裡,看到这篇STM32F103+DM9051_UIP_SPI to 以太网,透过該版主拿到了DM9051NP 以太网卡,刚好手上有Nuvoton NuTiny M051 和 M451,就先用M051拿来做一个简单的web server,透过web server 来控制开发板上的LED灯亮灭。

        1. DM9051NP硬体相关描述

           DM9051NP SPI介面网卡芯片是聯傑國際(DAVICOM)为了方便MCU单片机系统进行乙太网通信而开发出的解决方案。DM9051NP晶片是带有行业标准串列外设介面(Serial Peripheral Interface,SPI)的独立乙太网控制器。DM9051NP符合IEEE 802.3 规范,它还支援以DMA 模式來传输,以实现资料传送快速。DM9051NP通过1个中断引脚和SPI介面來进行与主控制器/MCU单片机的通信,资料传输规格为10/100 M。相關介紹可以參考STM32F103+DM9051_UIP_SPI to 以太网。以下是DM9051NP 以太網卡特點:

     

     

          • Package:32支接脚封装,QFN.
          • IEEE 802.3az Energy Efficient Ethernet (EEE)
          • Built-in integrated 3.3V to 1.8V regulator
          • 远端唤醒 (WOL)
          • 平行线/交叉线自动切换 HP Auto-MDIX
          • Support 光口介面      
          • 具有16KB SRAM静态随机存取记忆
          • EMI (Class B) and HBM ESD Rating 8KV
          • 工业温度规范: –40℃ to +85℃
          • 功率:(100/10 M) => 429/561 mW
          • 连续工作温度<60℃  

     

     

            上图是聯傑國際(DAVICOM) DM9051NP 以太网路卡SPI Pin的排列。

     

            NuTinyM051使用的是SPI1与DM9051NP SPI脚位的硬体连接如下:

     

    M0516DM9051
    P0.7(Pin32)CLK   (Pin07)
    P0.6(Pin33)MISO (Pin05)
    P0.5(Pin34)MOSI (Pin03)
    P0.4(Pin35)CSS   (Pin01)

           上图为M051 + DM9051硬体连接示意图。
               

         2.DM9051网卡驱动

           SPI 设定和 R/W data 参考M0516 Library SPI_Loopback sample code 可到nuvoton 官网下载M0516 Library, 再参考STM32F103+DM9051_UIP_SPI to 以太网附件中的驱动,将DM9051_Configuration() SPI设定改为M0516 SPI1 Pin Group 设定和修改DM9051_Read_Reg(), DM9051_Write_Reg(), DM9051_Read_Mem(), DM9051_Writer_Mem() , 修改如下:

    (1) 首先配置M0516 SPI1 相关设定

    void DM9051_Configuration(void)
    {	
    	/* Enable SPI1 peripheral clock */
    	CLK_EnableModuleClock(SPI1_MODULE);
    	/* Select HCLK as the clock source of SPI1 */
    	CLK_SetModuleClock(SPI1_MODULE, CLK_CLKSEL1_SPI1_S_HCLK, MODULE_NoMsk);
    
    	/* Reset IP */
    	SYS_ResetModule(SPI1_RST);    
    
    	/* Setup SPI1 multi-function pins */
    	SYS->P0_MFP = SYS_MFP_P04_SPISS1 | SYS_MFP_P05_MOSI_1 | SYS_MFP_P06_MISO_1 | SYS_MFP_P07_SPICLK1;
    
    	/*---------------------------------------------------------------------------------------------------------*/
    	/* Init SPI                                                                                                */
    	/*---------------------------------------------------------------------------------------------------------*/
    	/* Configure SPI1 as a master, SPI clock rate 200 KHz,
    		 clock idle low, 32-bit transaction, drive output on falling clock edge and latch input on rising edge. */
    	SPI_Open(SPI1, SPI_MASTER, SPI_MODE_0, 8, 25000000);
    	
    	/* Disable the automatic hardware slave selection function. Select the SPI1_SS pin and configure as low-active. */
    	SPI_DisableAutoSS(SPI1);
    
    	SPI_EnableFIFO(SPI1, 3, 3);
    }
    
    

     

     

    (2) 透过read cmd 读出register

     

     

    /*------------------------------------------------------*/
    /* DM9051_Read_Reg()                                    */
    /* SPI read command: bit7 = 0,                          */
    /*                   bit[6:0] = Reg. offset addr        */
    /* Disable auto SPI slave chip select.                  */
    /*------------------------------------------------------*/
    uint8_t DM9051_Read_Reg(uint8_t Reg_Off)
    {
    		
        SPI_SET_SS_LOW(SPI1);
      
    	/* SPI transfer DM9051 Read-Command and Reg. offset. */
    	//SPI_WRITE_TX0(SPI1, (uint32_t)Reg_Off); //Read command + Register offset address
    	while(SPI_GET_TX_FIFO_FULL_FLAG(SPI1));
    	SPI_WRITE_TX0(SPI1, Reg_Off); //Read command + Register offset address
    	while(SPI_IS_BUSY(SPI1));
    	
    	SPI_WRITE_TX0(SPI1, 0x0); //Dummy for read register value.
    	while(SPI_IS_BUSY(SPI1));
    	
    	SPI_READ_RX0(SPI1); // dummy read, jump 1st byte.
    	
    	SPI_SET_SS_HIGH(SPI1);
    	
    	return (SPI_READ_RX0(SPI1) & 0xFF);
    }
    
    

     
     
     

    (3)透过write cmd 写入register

    /*------------------------------------------------------*/
    /* DM9051_Write_Reg()                                   */
    /* SPI write command: bit7 = 1,                         */
    /*                   bit[6:0] = Reg. offset addr        */
    /* Disable auto SPI slave chip select.                  */
    /*------------------------------------------------------*/
    void DM9051_Write_Reg(uint8_t Reg_Off, uint8_t spi_data)
    {
    	uint32_t cmdaddr;
    	
    	cmdaddr = (Reg_Off | 0x80);
    	
    	SPI_SET_SS_LOW(SPI1);
    	
    	/* SPI transfer DM9051 Read-Command and Reg. offset. */
    	SPI_WRITE_TX0(SPI1, cmdaddr); //Read command + Register offset address	
    	while(SPI_IS_BUSY(SPI1));
    	
    	SPI_WRITE_TX0(SPI1, (uint32_t)spi_data);
    	while(SPI_IS_BUSY(SPI1));
    	
    	/*Clear SPI TX FIFO*/
    	SPI_ClearRxFIFO(SPI1);
    	
    	SPI_SET_SS_HIGH(SPI1);
    	
    	return;
    }

     

    (4) 连续读出 data 从spi array

    /*------------------------------------------------------*/
    /* DM9051_Read_Mem()                                    */
    /*                                                      */
    /* DM9051 burst read command: SPI_RD_BURST = 0x72       */
    /* Disable auto SPI slave chip select.                  */
    /*------------------------------------------------------*/
    void DM9051_Read_Mem(uint8_t* pu8data, uint32_t datalen)
    {
    	uint32_t i;
    	
    	// Read SPI_Data_Array back from the slave
    	uint8_t burstcmd = SPI_RD_BURST;
    	
    	SPI_SET_SS_LOW(SPI1);
    	
    	SPI_WRITE_TX0(SPI1, (uint32_t)burstcmd);
    	while(SPI_IS_BUSY(SPI1));
    	SPI_READ_RX0(SPI1);
    	
    	for(i = 0 ; i < datalen; i++){
    		pu8data[i] = SPI_WRITE_TX0(SPI1, (uint32_t )0x0);
    		while(SPI_GET_RX_FIFO_EMPTY_FLAG(SPI1));
    		pu8data[i] = (uint8_t)SPI_READ_RX0(SPI1);
    	}
    	
    	while(SPI_IS_BUSY(SPI1));
    	/*Clear SPI TX FIFO*/
    	SPI_ClearTxFIFO(SPI1);
    	
    	SPI_SET_SS_HIGH(SPI1);
    }
    
    

     

     

     

     

     

     
     

    (5) 连续写入data到spi array

    /*------------------------------------------------------*/
    /* DM9051_Write_Mem()                                   */
    /*                                                      */
    /* DM9051 burst write command: SPI_WR_BURST = 0xF8      */
    /* Disable auto SPI slave chip select.                  */
    /*------------------------------------------------------*/
    void DM9051_Write_Mem(uint8_t* pu8data, uint32_t datalen)
    {
    	uint32_t i;
    	uint8_t burstcmd = SPI_WR_BURST; /* Send the array to the slave*/
    
    	SPI_SET_SS_LOW(SPI1);
    	
    	SPI_WRITE_TX0(SPI1, (uint32_t)burstcmd);
    	while(SPI_IS_BUSY(SPI1));
    	SPI_READ_RX0(SPI1);
    	
    	for(i = 0; i < datalen; i++){		
    		while(SPI_GET_TX_FIFO_FULL_FLAG(SPI1));
    		SPI_WRITE_TX0(SPI1, (uint32_t)pu8data[i]);
    	}
    	
    	while(SPI_IS_BUSY(SPI1));
    	/*Clear SPI RX FIFO*/
    	SPI_ClearRxFIFO(SPI1);
    	
    	SPI_SET_SS_HIGH(SPI1);
    }
    
    

     

     

     

     

     

     

    3. 简单的web server 控制 LED

           uIP就不多介绍了,网路上有许多相关资料可以参考,在上一部份将驱动设置好后,將tapdev_init()、tapdev_read()、tapdev_send()對應到DM9051_init()、DM9051_rx()、DM9051_tx() function即可附件可參考,在main()中加入在 http_init(); 然后在http.c 在handle_input中天加 控制 LED function,新增web_led.c 和web_led.h 如下:

     

    (1)首先先在 http.c handle_input()新增判斷:

     

    static PT_THREAD(handle_input(struct httpd_state *s))
    {
      PSOCK_BEGIN(&s->sin);
    
      PSOCK_READTO(&s->sin, ISO_space);
    
      if(strncmp(s->inputbuf, http_get, 4) != 0) {
        PSOCK_CLOSE_EXIT(&s->sin);
      }
    
      PSOCK_READTO(&s->sin, ISO_space);
    
      if(s->inputbuf[0] != ISO_slash) {
        PSOCK_CLOSE_EXIT(&s->sin);
      }
    
      if(s->inputbuf[1] == ISO_space) {		
        //strncpy(s->filename, http_index_html, sizeof(s->filename));
    	strncpy(s->filename, http_webMain_html, sizeof(s->filename));
      } 
    #if 1 //web control led
      /* Control led, 0 = OFF, 1 = ON, 2 = Flash */
      else if (s->inputbuf[3] == 'L','E','D' && ((s->inputbuf[4] == '0') || 
                (s->inputbuf[4] == '1') || (s->inputbuf[4] == '2'))){
        Set_LED_mode(s->inputbuf[4]);
        s->inputbuf[4]= 0;
        //strncpy(s->filename, "/home.html", 10);
        strncpy(s->filename, http_webMain_html, sizeof(s->filename));
      }
    #endif
      else {
     	s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
       	strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename));
      }
      /*  httpd_log_file(uip_conn->ripaddr, s->filename);*/
      s->state = STATE_OUTPUT;
    
      while(1) {
        PSOCK_READTO(&s->sin, ISO_nl);
        if(strncmp(s->inputbuf, http_referer, 8) == 0) {
          s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
          /* httpd_log(&s->inputbuf[9]);*/
        }
      }
      PSOCK_END(&s->sin);
    }
    

     

     
     

    (2)LED 设定和控制判断代码如下:

     

     

    void Set_LED_mode(char lkkcode)
    {
    	//int i;
    	
    	GPIO_SetMode(P3, BIT6, GPIO_PMD_OUTPUT);
    	
    	if(lkkcode == ('0')) // LED off
    	{
    		P36 = 1;
    	}else if (lkkcode == '1'){ // LED on
    		P36 = 0;
    	}else if(lkkcode == '2') // LED Flash
    	{
    		//for(i = 0 ; i< 30 ; ++i)
    		{
    			P36 = 0;
    			Delay(25);
    			P36 = 1;
    			Delay(25);
    		}
    	}
    }
    
    

     

     

     

     

     

    最后在网址列输入IP 192.168.XXX.XXX进入 uip web server控制LED,因为uip  有开DHCP 也可设定为固定IP ,看使用者设定输入正确的IP位址




    就可以透过web 控制LED,如下可以看到MCU +  DM9051相关讯息,最下面可以看到控制LED 选单,
    按下on 、off 、Flash后网址后面会分别显示XXX.XXX.XXX.XXX / LED0、1、2让使用者可以知道目前设定

     

    以上完成后就是一个简单webserver 控制LED应用了,最後附上程序:

     

     

     

    DM9051网卡 + NuTinyM051 + uIP实现web server

     

     

     

    展开全文
  • DM9051NP芯片SPI接口占用管脚数量比以太网PHY的RMII/MII少,PCBA版线布局可更精简。而DM9051NP驱动软件方面,由于源代码已集成在ESP IDF中,可见其可靠度以及网口性能得到乐鑫官方认可。 在ESP32既有SPI 时钟31.2MHz...

    前言

        DM9051NP芯片 SPI接口占用管脚数量比以太网PHYRMII/MII,PCBA版线局可更精简而DM9051NP驱动软件方面,由于源代码已集成在ESP IDF中,可见其可靠度以及网口性能得到乐鑫官方认可。 在ESP32既有SPI 时钟31.2MHz的条件下,DM9051NP能充分发挥网口资料送收性能,相关Iperf测试数据,请参考第5章节。

    1.DM9051NP + ESP32模块介绍

        此模块是基于安可信ESP-12K模块来设计,以DM9051NP SPI接口转以太网芯片为ESP-12K模块(核心模块为ESP32-S2)增加了有线网口功能,其中,DM9051NP只用了4个GPIO,原本ESP32有线网口参考设计为以太网PHY,在RMII模式下,需佔用10个GPIO ; MII模式下,GPIO占用更多,为16个。

    同样地,DM9051NP更适合用ESP32-WROOM32(核心模块为ESP32)的以太网口应用上,在有限GPIO接口的情况之下,使用DM9051NP SPI接口转以太网芯片,空出来的GPIO能让MCU更方便去扩展其他功能

    1-1.ESP 12K模组介绍

        ESP32-S2 提供丰富的外设接口,包括SPI,I2S,UART,I2C,LED PWM,LCD 接口, Camera 接口,ADC,DAC,触摸传感器,温度传感器和多达43 个GPIO。

        ESP-12K 是由安信可科技开发的Wi-Fi 模块,该模块核心处理器ESP32-S2 是一款 高集成度的低功耗Wi-Fi 系统级芯片(SoC),专为物联网(IoT)、移动设备、可穿戴电子 设备、智能家居等各种应用而设计。

    1-2. DM9051NP网卡芯片介绍

    DM9051NP SPI接口网卡芯片是为了方便物联网行业进行以太网通信而开发出的解决方案。DM9051NP芯片是带有行业标准串列外设接口(Serial Peripheral Interface,SPI)的独立以太网控制器。DM9051NP符合IEEE 802.3 规范,它还支持以DMA 模式來传输,以实现资料传送快速。DM9051NP通过1个中断引脚和SPI接口來进行与主控制器/MCU单片机的通信,资料传输规格为10/100 M。

    •Package:32支接脚封装,QFN.

    •IEEE 802.3az Energy Efficient Ethernet (EEE)

    •远程唤醒 (WOL)

    •Support 光口界面

    •EMI (Class B) and HBM ESD Rating 8KV

    • 工业温度规范: –40 to +85

    2.实验环境

    2-1.应用:访问网页

    2.1

    DM9051 ESP32 12K模块利用HTTP协议,完成无线Wi-Fi转传有线以太网的透传功能,而两端数据包进行双向即时转发

    在透传功能演示时,将模块当作一个小网关(无线热点Wi Fi AP),让手机或平板等透过无线WIFI连上线,模块会要求输入密码,待登入后,此时DM9051NP会释放出ESP32 AP access point(eth2ap的功能),当手机连线模块后,由路由的DHCP Server进行DHCP 的动态IP分配,让DM9051NP取得IP ,接著ESP32-DM9051-eth2ap进行数据透传,成功连线并访问网页。

    如果路由沒有DHCP server的话,使用者可对手机设置静态IP,也可访问网页。

    此应用可用于IPC监控、家庭物联网网关搭建等。

    3.硬件部分

    3-1 .硬件环境

    此DM9051 ESP 12K开发版规格如下:

    3.1

    主要组件

    功能描述

    P1

    DC5V电源插座

    J28

    J29

    选择 USB (默认设置) or DC电源插座来供电

    J28 ON: 5V 电源由 USB (J1)供应 

    J28 OFF: 5V电源由 DC插座 (P1)供应 

    J29 ON: 5V电源由 DC插座 (P1)供应

    J29 OFF: 5V电源由 USB (J1)供应

    J27

    J30

    选择 USB (默认设置) or DC电源插座来为DM9051 以及 ESP 12K (ESP32-S2)供电

    J27 ON: 5V电源由 USB (J1)供电给DM9051 

    J27 OFF: 5V电源由 DC插座 (P1)供电给DM9051  

    J30 ON: 5V电源由 DC插座 (P1)供电给ESP 12K(ESP32-S2)

    J30 OFF: 5V电源由 USB (J1)供电给ESP32-S2

    JP4

    LED1

    LED2

    RJ45 网络接口插座为10M以及/100Mbps Ethernet,插座有LED1 LED2灯号。

    (LED 灯号模式1: 默认设置)

    LED1 :绿灯表示 (Link / Active) 

    ON: 联机中 

    OFF: 不是联机中

    Flash: 数据传送中

    LED2 :黄灯(当联机中时,为网速灯号 ) 

    ON: 100M Full duplex 

    OFF: 10M Full duplex

    U3

    DM9051

    U2

    ESP32-S2 Module : ESP-12K

    SW1

    启动按键 

    SW2

    重启按键  

    LED4

    LED灯号自定义义(GPIO2)

    LED5

    电源灯号

    J3

    ESP32-S2 I/O

    J2

    ESP32-S2 I/O

    J1

    MicroUSB 插座( USB5V供电 以及 通信)

     表1

    3-2. SPI硬件接线部分

    ESP32 有线接入以太网方法:

    DM9051NP通过SPI接口控制内部寄存器,并有中断输出接口。ESP 12K(ESP32-S2)通过SPI1和DM9051NP相连。具体接口如下:

    DM9051NP

    ESP32 S2

    SPI_CS

    Pin17

    Pin13

    SPI_CLK

    Pin18

    Pin16

    SPI_MOSI

    Pin19

    Pin14

    SPI_MISO

    Pin20

    Pin15

    SPI_INT

    Pin24

    Pin18

     

    *在ESP IDF的Ethernet example DM9051NP module:

    在ESP32官方例程 examples/ethernet/中,介绍了DM9051NP SPI接口的接线方法,下面链结网址的说明文档README.md中,有说明接线方法。在這裡要提醒DM9051NP以及ESP32模块基于走线佈局考量,設計成如图3.2/3.3的变更

    ​​​​​​https://github.com/espressif/esp-idf/tree/1d7068e4be430edd92bb63f2d922036dcf5c3cc1/examples/ethernet

     

    4.软件部分

    4-1. ESP32 硬件开发的软件环境:ESP IDF

    此实作使用 ESP-IDF (Espressif IoT Development Framework) 环境来开发以太网口通讯,配置相关菜单,编译、下载固件至 ESP32 12K模块。对于如何搭建ESP32 硬件开发的软件环境,请各位同学参考上海乐鑫官方搭建教学,这里写得很详细,会手把手带着同学完成开发环境:

    快速入门 - ESP32 - — ESP-IDF 编程指南 latest 文档

     

    4-2. HTTP

     1.)HTTP简介

    HTTP协议是Hyper Text Transfer Protocol超文本传输协议的缩写,基于TCP传输层协议进行通信,采用Client端与Server端架构,属于应用层协议。

    ESP IDF有提供软件组件使用和设计文档,让同学运用ESP-IDF的各项功能,本实作即是在ESP IDF范例(Example)中构建HTTP的应用程序。

    2.)ESP IDF的HTTP接口:

        ESP IDF自带的HTTP接口的使用, ESP HTTP模块提供了完整的API以支持HTTP的应用,其源代码,请同学参考IDF目录下的test_http_client.c,其路径esp-idf\components\esp_http_client\test

    3.)HTTP请求

    HTTP请求格式是Client端往Server端发送请求动作,告知Server自己的要求。

    4.)HTTP报文

    HTTP报文是HTTP应用程序之间传输的数据块,HTTP报文分为HTTP请求报文和HTTP响应报文。

    对于HTTP请求与报文的详细内容,在这裡就先不谈了,已经有其他先进在各论坛做了详细介绍,有兴趣的同学可上网自行学习。

    5.)ESP32的HTTP接口介绍

    请同学直接参考ESP IDF的官方指南,那边介绍得更多更详细,相关网址如下:API 参考 - ESP32 - — ESP-IDF 编程指南 latest 文档

    4-3.有线以太网转Wi-Fi的AP路由功能:ETH2AP

    1.)介绍:

     

    ESP32有提供官方例程,这个例程(ETH2AP)功能是以太网+Wi-Fi热点AP(access point),各位同学可在ESP IDF范例(esp-ide/examples/Ethernet)找到,链结网址如下:

    esp-idf/examples/ethernet/eth2ap at 1d7068e4be430edd92bb63f2d922036dcf5c3cc1 · espressif/esp-idf · GitHub

    2.)如何使用ETH2AP example

     步骤1: 初始化有线以太网以及无线热点模式(Wi-Fi AP mode: Access point)

      步骤2: 有线以太网接入路由/交换器/PCE,开启DHCP server功能

      步骤3:开启 ESP32的Wi-Fi AP

      步骤4: 无线设备(智能手机)连接 ESP32的Wi-Fi

           详细内容,建议各位同学参考这位先进的文章:

    ESP32 单片机学习笔记 - 06 - (以太网)Ethernet转Wifi_Lovely_him的博客-CSDN博客_esp32 以太网

    4-4. DM9051NP驱动

    DM9051NP驱动源代码参考如下:

    static void emac_dm9051_task(void *arg)

    {

        emac_dm9051_t *emac = (emac_dm9051_t *)arg;

        uint8_t status = 0;

        uint8_t *buffer = NULL;

        uint32_t length = 0;

        while (1) {

            // block indefinitely until some task notifies me

            ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

            /* 清除中斷狀態 */

            dm9051_register_read(emac, DM9051_ISR, &status);

            dm9051_register_write(emac, DM9051_ISR, status);

            /* 收到封包 */

            if (status & ISR_PR) {

                do {

                    length = ETH_MAX_PACKET_SIZE;

                    buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);

                    if (!buffer) {

                        ESP_LOGE(TAG, "no mem for receive buffer");

                    } else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {

                        /*將緩衝buffter堆疊在TCP/IP層*/

                        if (length) {

                            emac->eth->stack_input(emac->eth, buffer, length);

                        } else {

                            free(buffer);

                        }

                    } else {

                        free(buffer);

                    }

                } while (emac->packets_remain);

            }

        }

        vTaskDelete(NULL);

    }

    *DM9051NP ESP32 完整驱动代码链结:

    esp-idf/esp_eth_mac_dm9051.c at master · espressif/esp-idf · GitHub

    4-5 其他笔记

       有些同学反应,之前他们从Github论坛下载DM9051NP sample code,执行后会出现连上线后,有断线情况。经过分析试验后,其原因是原本ESP32轮询的间隔时间为50ms,要改成10ms。相关源代码修改建议如下:

    1.)ESP32 轮询的间隔为改成10ms”, 将ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(50));改成Line 257  vTaskDelay(pdMS_TO_TICKS(10));

    2.)请删除原本example的这一行代码/components/esp_eth/src/esp_eth_mac_dm9051.c

    3.)增加此行代码  "dm9051_register_write(emac, DM9051_ISR, 0xFF);"到 emac_dm9051_task() function的部分。

    4.)增加此行代码  "dm9051_register_write(emac, DM9051_ISR, 0xFF);" 到emac_dm9051_receive() function. 的部分。

    5有线以太网转Wi-Fi的AP功能演示

    5-1)测试架构与需要设备如下:

    访问网页---路由---DM9051NP_ESP 12K模块 <<<--->>>手机

     注:依照当时的网路环境,同学需要对路由的WAN口以及LAN口做DHCP server配置。

    在图5.1中,手机经由模块,直接连线到左方的路由,此路由提供DHCP server。所以,手机向路由得到DHCP动态IP后,再经过路由连线到网际网路。

     5-2 ) ETH2AP功能演示相关讯息:

        以智能手机透过无线WiFi来连上模块,手机WiFi进行扫描后,可以找到模块的SSID 名称为“+++eth2ap_9051_s2_56_06”,表示模块的Wi-Fi已经准备好。点选后,输入Password : 12345678,成功连线后,手机可透过模块来上网。

     我们可以在调适终端上看到ETH-to-AP运行后,相关的设置讯息:

    模块上电后看到USB端口打印s2[e2ap]: Ethernet Link Up (如图5.2,ETH-to-AP’s message in the terminal)表示以太网路成功连线。模块的Wi-Fi AP配置了MAC地址:00:60:6e:90:56:06,模块的以太网也同样配置了MAC地址00:60:6e:90:56:06。这两个配置的MAC地址,在之前提到的透传功能中,于最底层硬件概念上,有它的意义,而且不会出现在数据包的收送内容中。接著,模块运行ETH2AP功能,进行收送数据包,从DM9051NP网口收到数据包,就原封不动地透过Wi-Fi口转送出去,从Wi-Fi口收到数据包也原封不动由网口转送出去,此为『透传』。

    5-3内网测试(Intranet)

    这裡以华硕路由器(产品型号:ASUS RX3041)来进行内网测试,图中手机经由模块直接连线左方的路由器,并且从路由的DHCP server得到DHCP动态IP。路由器在内网这一段区域,它本身是一个网络站点,而且是内网的IP支配者。所以,路由肯定有一个静态IP,由于它是内网的IP支配者(就是DHCP server),所以,会分配动态IP给DHCP客户端(手机)。经查看华硕路由器手册,找到预先配置的静态IP为192.168.6.1。

    此内网测试,在手机成功取得动态IP后,手机会出Wi-Fi AP已连上的状态图案。此时,同学开启网页流览器(Browser)来访问前述的192.168.6.1,也就是路由器的web server首页,路由器会要求同学登入帐户与密码,查看路由器手册后,得知帐户是admin,密码也是admin。登入后,手机即可访问路由器的web server首页,完成ETH-to-AP内网测试。

    5-4 Iperf测试

     

    以Iperf测试DM9051NP有线网口性能,在SPI clock 20MHz下,获得下面TCP传输数据:

    TX

    10.34 mbps

    RX

    9.58 mbps

    完。

     

     

     

     

     

     

     

     

    展开全文
  • STM32F103ZE uIP DM9051 SPI以太网移植指南

    千次阅读 2015-10-15 17:32:06
    DM9051NP SPI接口网卡芯片是为了方便MCU单片机系统进行以太网通信而开发出的解决方案。DM9051NP芯片是带有行业标准串列外设接口(Serial Peripheral Interface,SPI)的独立以太网控制器。DM9051NP符合IEEE 802....

    1.前言

    1-1网卡芯片介绍

    DM9051NP SPI接口网卡芯片是为了方便MCU单片机系统进行以太网通信而开发出的解决方案。DM9051NP芯片是带有行业标准串列外设接口(Serial Peripheral InterfaceSPI)的独立以太网控制器。DM9051NP符合IEEE 802.3 规范,它还支持以DMA 模式來传输,以实现资料传送快速。DM9051NP通过1个中断引脚和SPI接口來进行与主控制器/MCU单片机的通信,资料传输规格为10/100 M

    •Package:32支接脚封装,QFN.

    •IEEE 802.3az Energy Efficient Ethernet (EEE)

    •Built-in integrated 3.3V to 1.8V regulator

    远程唤醒 (WOL)

    并行线/交叉线自动切换 (HP Auto-MDIX)

    •Support 光口界面

    具有16KB SRAM静态随机存取记忆

    •EMI (Class B) and HBM ESD Rating 8KV

    • 工业温度规范: –40℃ to +85℃

    • 功率:(100/10 M) => 429/561 mW

    • 连续工作温度<60


    1. DM9051NP  SPI界面网卡介绍.Pdf

    2. DM9051NP datasheet.pdf

    1-2工程代码&demo原理图

    1. stm32_dm9051_uIP_SPIDMA.RAR

    2. DM9051_DEMO V2.1.DSN


    目錄

    1.前言 1

    1-1网卡芯片介绍 1

    1-2工程代码&demo原理图 1

    2.搭建实验环境 3

    3.硬件和软件说明 4

    3-1 硬件环境 4

    3-2 SPI配置 5

    3-2-1. 接线部分 5

    3-2-2. 软件配置部分 5

    4.网卡驱动 11

    4-1 网卡介绍 11

    4-2 Read/Write Register 11

    4-3 Read/Write Memory 13

    4-4 DM9051 Received Packet 18

    4-5 DM9051 Transmitted Packet 21

    5.uIP基本结构与配置 23

    5-1 首先介绍下uIP协议部分 23

    5-2 uIP基本结构 24

    5-3 uIP配置部分 29

    5-3-1. IP地址配置 29

    5-3-1. MAC地址配置 29

    5-4 uip-conf.h部分 30

    5-5一个简单有效的Clock Tick 31

    6.案例——最简单的TCP echo程序 32

    7.web页面控制 35

    8.测试结果 36

    9.移植相关 38

     


    2.搭建实验环境

    先讲一下如何搭建实验环境。我们STM32F103ZET6+DM9051作为一个网络终端通过网线跟PC直连(DM9051支持自动翻转功能(Auto-MDIX),不用区分双绞线并行线)。例如调试PC机的IP地址如下图所示。

     

    1 PCIP地址

        路由器的IP地址为192.168.1.1。那么开发板的IP地址可以设定为 192.168.1.2192.168.1.255。在这里,我们把开发板IP设为192.168.1.51

     

     


     

     

     图二三:物理连接图

     

     

     

    3.硬件和软件说明

    3-1 硬件环境

        【百为开发板】

         百为开发板上外接DM9051NP模块,DM9051NP通过SPI接口控制内部寄存器,并有中断输出接口。STM32通过SPI1DM9051NP相连。具体接口如下:

     

     

    DM9051

    STM32

    SPI_CS(Chip_Select)

    Pin01

    Pin26(PC0)

    SPI_MOSI

    Pin03

    Pin43(PA7)

    SPI_MISO

    Pin05

    Pin42(PA6)

    CPI_CLK

    Pin07

    Pin41(PA5)

    Interrpt

    Pin02

    Pin97(PC7)

     

        由于SPI上同时挂载其他SPI从设备,所有初始化的过程中需要通过操作CS端口禁止其他SPI从设备。CS片选将DM9051NP相连拉低,其他从设备拉高即可。

     


    3-2 SPI配置

    3-2-1. 接线部分


     

    SPI信号线说明,通常SPI通过4个引脚与外部器件相连:

    1. MISO : 主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。

    2. MOSI : 主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。

    3. SCK : 串口时钟,作为主设备的输出,从设备的输入

    4. NSS : 从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。


    3-2-2. 软件配置部分


    1. 首先开启SPI 1的时钟

    2. 配置SPI 1 GPIO PIN,使用的pin還有SPI接線參考3-1

    3. 配置DMA,簡單介绍一下: 

    (1). DMAAMBA的先进高性能总线(AHB)上的设备,它有2AHB端口:一个是从端口,用于配置DMA,另一个是主端口,使得DMA可以在不同的从设备之间传输数据。

    (2). DMA的作用是在没有Cortex-M3核心的干预下,在后台完成数据传输。在传输数据的过程中,主处理器可以执行其它任务,只有在整个数据块传输结束后,需要处理这些数据时才会中断主处理器的操作。它可以在对系统性能产生较小影响的情况下,实现大量数据的传输。

    4. SPI_DMA的通信过程

    (1). 设置外设地址

    (2). 设置存储器地址

    (3). 设置传输数据量

    (4). 设置通道的配置信息

    (5). 使能DMA通道,启动传输

    (6). 发送时,在每次TXE被设置为’1’时发出DMA请求,DMA控制器则写数据至SPI_DR寄存器,TXE标志因此而被清除。

    (7). 接收时,在每次RXNE被设置为’1’时发出DMA请求,DMA控制器则从SPI_DR寄存器读出数据,RXNE标志因此而被清除。

     

    DM9051 SPI1 DMA 配置如下:

    void DM9051_Configuration(void)

    {

      SPI_InitTypeDef  SPI_InitStructure;

      GPIO_InitTypeDef GPIO_InitStructure;

      EXTI_InitTypeDef EXTI_InitStructure;

      NVIC_InitTypeDef NVIC_InitStructure;

    DMA_InitTypeDef    DMA_InitStructure;

     

      /* Enable SPI1, DMA and GPIO clocks */

     

    #ifdef SPI_DMA //Enable DMA

    RCC_AHBPeriphClockCmd(SPI_MASTER_DMA_CLK, ENABLE); // if enable SPI_DMA  Configure DMA clock

    #endif//SPI_DMA

     

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOF, ENABLE);

     

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // enable SPI1 clock

     

    #ifdef DMA_INT

    /* NVIC configuration */

        NVIC_Configuration();

    #endif//DMA_INT

     

      /* Configure SPI1 pins: SCK, MISO and MOSI */

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      GPIO_Init(GPIOA, &GPIO_InitStructure);

     

      /* Configure I/O for DM9051 Chip select */

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_DM9051_CS;

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

      GPIO_Init(GPIO_DM9051_CS, &GPIO_InitStructure);

     

    #if 1

      /* Configure pins: SPI FLASH CS --------------------------------------------*/

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

      GPIO_Init(GPIOB, &GPIO_InitStructure);

      GPIO_SetBits(GPIOB, GPIO_Pin_2);

    #endif

     

    #if 1

      /* Configure pins: LCD TOUCH CS --------------------------------------------*/

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

      GPIO_Init(GPIOF, &GPIO_InitStructure);

      GPIO_SetBits(GPIOF, GPIO_Pin_10);

    #endif//0  

      /* Deselect the FLASH: Chip Select high */

      //SPI_DM9051_CS_HIGH();

     

      /* SPI1 configuration */

      SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

      SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

      SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

      SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;

      SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

      SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

      SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;

      SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

      SPI_InitStructure.SPI_CRCPolynomial = 7;

      SPI_Init(SPI1, &SPI_InitStructure);

     

    #ifdef SPI_DMA  

      /* SPI_MASTER_Rx_DMA_Channel configuration ---------------------------------*/

      DMA_DeInit(SPI_MASTER_Rx_DMA_Channel);

      DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SPI_MASTER_DR_Base;

      //DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI_MASTER_Buffer_Rx;

    #ifdef uIP_NOOS

      DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)uip_buf;

    #endif  

      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

      //DMA_InitStructure.DMA_BufferSize = BufferSize;//RX length

      DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

      DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

      DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

      DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

      DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

      DMA_InitStructure.DMA_Priority = DMA_Priority_High;

      DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

      DMA_Init(SPI_MASTER_Rx_DMA_Channel, &DMA_InitStructure);

     

      /* SPI_MASTER_Tx_DMA_Channel configuration ---------------------------------*/

      DMA_DeInit(SPI_MASTER_Tx_DMA_Channel);  

      DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SPI_MASTER_DR_Base;

      //DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI_MASTER_Buffer_Tx;

    #ifdef uIP_NOOS

      DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)uip_buf;

    #endif  

      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

      DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

      DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

      DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

      DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

      DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

      DMA_InitStructure.DMA_Priority = DMA_Priority_High;

      DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

      DMA_Init(SPI_MASTER_Tx_DMA_Channel, &DMA_InitStructure);

     

    #ifdef DMA_INT

    /* Enable DMA1 Channel2,3 Transfer Complete interrupt */

      DMA_ITConfig(SPI_MASTER_Rx_DMA_Channel, DMA_IT_TC, ENABLE);

      DMA_ITConfig(SPI_MASTER_Tx_DMA_Channel, DMA_IT_TC, ENABLE);  

    #endif//DMA_INT  

     

      //DMA_Cmd(SPI_MASTER_Rx_DMA_Channel, ENABLE);

      //DMA_Cmd(SPI_MASTER_Tx_DMA_Channel, ENABLE);

    #endif//SPI_DMA

     

      /* Enable SPI1  */

      SPI_Cmd(SPI1, ENABLE);

     

    #ifdef DM9051_INT  

      /* configure PC7 as external interrupt */

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

      GPIO_Init(GPIOC, &GPIO_InitStructure);

     

      /* Connect DM9051 EXTI Line to GPIOC Pin 7 */

      GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource7);

     

      /* Configure DM9051 EXTI Line to generate an interrupt on falling edge */

      EXTI_InitStructure.EXTI_Line = EXTI_Line7;

      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

      EXTI_InitStructure.EXTI_LineCmd = ENABLE;

      EXTI_Init(&EXTI_InitStructure);

     

      /* Clear DM9051 EXTI line pending bit */

      EXTI_ClearITPendingBit(EXTI_Line7);

     

      /* Enable the EXTI7 Interrupt */

      NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;

      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

      NVIC_Init(&NVIC_InitStructure);    

    #endif//DM9051_INT

    }

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    4.网卡驱动

    4-1 网卡介绍

    网卡采用DAVICOM  DM9051NPDM9051关特性与介绍可参考DM9051NP SPI卡介,在上一章节介绍如何配置STM32 SPI & DMA,在配置完成后,需配置DM9051通过SPI介面送收Date,相关代码如下:

     

    static __inline u8 SPI_DM9051_SendByte(u8 byte)

    {

      /* Loop while DR register in not emplty */

      while (SPI_I2S_GetFlagStatus(DM9051_SPI, SPI_I2S_FLAG_TXE) == 0);

     

      /* Send byte through the SPI1 peripheral */

      SPI_I2S_SendData(DM9051_SPI, byte);

     

      /* Wait to receive a byte */

    SPI_I2S_GetFlagStatus(DM9051_SPI, SPI_I2S_FLAG_RXNE);

      /* Return the byte read from the SPI bus */

      return SPI_I2S_ReceiveData(DM9051_SPI);

    }

     

    4-2 Read/Write Register

    在上一章配置好SPI read/write data function后,在此章节开始介绍如何通过SPI 读写DM9051 Register & Memory,介绍如下:

     

    A. 首先介绍Read Register,流程如下:

    1. Reset CS(Chip Select) , GPIO pin pull low (Falling edge)

    2. SPI send MOSI Reg Offset command.

    3. SPI send 0x0 ceate a clock for Slave send data .

    4. Set CS(Chip Select) GPIO pin pull High.

    5. return rx data

    相关代码參考如下:

    static u8 DM9051_Read_Reg(u8 Reg_Off)

    {

    u8 spi_data;

       SPI_DM9051_CS_LOW(); //读取之前先 Chip Select pull low

    /* SPI transfer DM9051 Read-Command and Reg. offset. */

    SPI_DM9051_SendByte(Reg_Off); //Read command + Register offset address

    spi_data = SPI_DM9051_SendByte(0x0);//Dummy for read register value.

    SPI_DM9051_CS_HIGH();//读取再把chip Select  pull High

    return spi_data;//返回Data

    }

     

    B. 再來介绍Write Register 流程部分,如下:

    1. Reset CS(Chip Select) ,GPIOC PIN pull low.

    2. SPI send MOSI  Reg_Offset | 0x80(disable DM9051 interrupt) value.

    3. SPI send MOSI spi_data value .

    4. Set CS(Chip Select) GPIOC PIN to high.

     

    相关代码參考如下:

    static void DM9051_Write_Reg(u8 Reg_Off, u8 spi_data)

    {

    u8 cmdaddr;

    cmdaddr = (Reg_Off | 0x80);

    SPI_DM9051_CS_LOW();

    /* SPI transfer DM9051 Read-Command and Reg. offset. */

    SPI_DM9051_SendByte(cmdaddr); //Read command + Register offset address

    SPI_DM9051_SendByte(spi_data); // 

    SPI_DM9051_CS_HIGH();

    return;

    }

     

    4-3 Read/Write Memory

    在上一章节介绍如何读写DM9051寄存器后,相信如何使用SPI读写有一定的了解了,在這章节将讲解如何读写DM9051 Memory,先簡單說明一下為何需要读写MemoryDM9051具有16K-byte SRAM這個特性,可以将要送收data暫存在SRAM,在通过SPI接口对DM9051command将存放在Memory裡的data 搬出來,读写Memory关流程与代码如下:

     

    A. 首先Read Memory流程:

    1. Reset GPIOPIN low

    2. Send SPI interrup SPI write buffer command(Read Memory Register = 0x72)。

    3. 此部分可以設定單一使用SPI SPI + DMA mode兩種設定如下:

    (1) 只使用SPI mode 通过SPI interface RX Memory data读取出來

       放到buffer

    (2) 使用SPI DMA mode 将data 丟到DMA CMAR 和 設定data length to CNDTR

    1. 启用DMA Channel

    2. 启用 SPI TX Request command 开始送收data

    3. Dummy TX 主要是產生一個SPI clock讓DM9051DataMaster, 因為DM9051 Chip 沒有DMA功能,通过送一個SPI Clock來触發SPI 接收RX Packet

    4. 判断等待data 完全送完,等待data完全送完后清除DMA Channel 等待標記。

    5. Set GPIO PIN to high

    6. 关閉DMA Channel

     

    相关代码參考如下:

    static void DM9051_Read_Mem(u8* pu8data, u16 datalen)

    {

    #ifndef SPI_DMA

    u32 i;

    #endif //SPI_DMA

    uint32_t mccr;

    #ifdef DMA_INT

    uint8_t err;

    #endif //DMA_INT

    // Read SPI_Data_Array back from the slave

    u8 burstcmd = SPI_RD_BURST;

       

    SPI_DM9051_CS_LOW();

    SPI_DM9051_SendByte(burstcmd);

     

    #ifndef SPI_DMA  //if no enable SPI DMA

    for(i=0; i<datalen; i++) {

    pu8data[i] = SPI_DM9051_SendByte(0x0);

     }

     SPI_DM9051_CS_HIGH();

    #else

    RxComplete = 0;

    ((DMA_Channel_TypeDef *)SPI_MASTER_Rx_DMA_Channel)->CMAR = (uint32_t)pu8data;

    ((DMA_Channel_TypeDef *)SPI_MASTER_Rx_DMA_Channel)->CNDTR = (uint32_t)datalen;

    DMA_Cmd(SPI_MASTER_Rx_DMA_Channel, ENABLE);

    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);//RX DMA request, start move data

     

    /* Dummy TX to generate SPI clock for RX*/

    mccr = ((DMA_Channel_TypeDef *)SPI_MASTER_Tx_DMA_Channel)->CCR; //Save DMA channel 3 configuration register

    ((DMA_Channel_TypeDef *)SPI_MASTER_Tx_DMA_Channel)->CCR = (mccr & 0xffffff31);// Peripheral & memory doesn't auto increment. Interrupt disable

    ((DMA_Channel_TypeDef *)SPI_MASTER_Tx_DMA_Channel)->CMAR = (uint32_t)pu8data;

    ((DMA_Channel_TypeDef *)SPI_MASTER_Tx_DMA_Channel)->CNDTR = (uint32_t)datalen;

    DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, DISABLE);//Diable Interrupt

    DMA_Cmd(SPI_MASTER_Tx_DMA_Channel, ENABLE);

    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);//TX DMA request, start move data

     

    #ifdef DMA_INT

    OSSemPend(DM9051_rxdma_flag, 0,&err);

    #else

    while(!DMA_GetFlagStatus(SPI_MASTER_Rx_DMA_FLAG));

    while(!DMA_GetFlagStatus(SPI_MASTER_Tx_DMA_FLAG));

    #endif//DMA_INT

     

    DMA_ClearFlag(SPI_MASTER_Rx_DMA_FLAG);

    DMA_ClearFlag(SPI_MASTER_Tx_DMA_FLAG);

     

    DMA_Cmd(SPI_MASTER_Rx_DMA_Channel, DISABLE);

     

    /* Restore Tx setting */

    DMA_Cmd(SPI_MASTER_Tx_DMA_Channel, DISABLE);

    ((DMA_Channel_TypeDef *)SPI_MASTER_Tx_DMA_Channel)->CCR = mccr;

    DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);

    #endif//SPI_DMA 

    SPI_DM9051_CS_HIGH();

    }

     

    B. Write Memory流程:

    1. Reset GPIO PIN to low

    2. Send SPI interface write memory buffer 

    3. 判断使用SPI modeSPI DMA mode

    4. 此部分可以設定單一使用SPI SPI + DMA mode兩種設定如下:

    (1) 假設只使用SPI mode 通过SPI interface send TX Packet to FIFO

    (2) 使用SPI DMA mode 将data 丟到DMA CMAR和 設定data length to CNDTR

          a. 启用DMA Channel

          b. 启用 SPI TX Request Channel send packet

         c. 断等待data 完全送完,等待data完全送完后清除DMA Channel 的等待標記。

          d. Set GPIO PIN to high

          e. 关閉DMA Channel

     

    相关代码參考如下:

    static void DM9051_Write_Mem(u8* pu8data, u16 datalen)

    {

    #ifndef SPI_DMA

    uint32_t i;

    #endif //SPI_DMA

     

    #ifdef DMA_INT

    uint8_t err;

    #endif //DMA_INT

     

      /* Send the array to the slave */

      u8 burstcmd = SPI_WR_BURST;

     

      SPI_DM9051_CS_LOW();

     

      SPI_DM9051_SendByte(burstcmd);

     

    #ifndef SPI_DMA   

    for(i=0; i< datalen; i++) {

    SPI_DM9051_SendByte(pu8data[i]);

    }

    SPI_DM9051_CS_HIGH();

    #else

    TxComplete = 0;

    ((DMA_Channel_TypeDef *)SPI_MASTER_Tx_DMA_Channel)->CMAR = (uint32_t)pu8data;

    ((DMA_Channel_TypeDef *)SPI_MASTER_Tx_DMA_Channel)->CNDTR = (uint32_t)datalen;

    DMA_Cmd(SPI_MASTER_Tx_DMA_Channel, ENABLE);

    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);//TX DMA request, start move data

     

    #ifdef DMA_INT

    OSSemPend(DM9051_txdma_flag, 0,&err);

    #else

    while(!DMA_GetFlagStatus(SPI_MASTER_Tx_DMA_FLAG));

    #endif//DMA_INT

    DMA_ClearFlag(SPI_MASTER_Tx_DMA_FLAG);

    DMA_Cmd(SPI_MASTER_Tx_DMA_Channel, DISABLE);

    #endif//SPI_DMA

    SPI_DM9051_CS_HIGH();

    }

    以上都完成后,接下來就是DM9051_Init部份了,此部分可直接參考project 裡的Driver 裡面有相关配置。

    如何使用DM9051,虽然DM9051使用复杂但是深入理解两点即可,第一点如何通过SPI发送命令和数据;第二点理解DM9051的缓冲区,在发送以太网和接受以太网数据报的过程中,DM9051会帮助用户做些额外的工作,例如发送时自动填充SFD,在读取接收缓冲区数据时会包含若干状态信息,包括数据报长度和CRC校验结果等。

     

     

     

     

    4-4 DM9051 Received Packet

    在上一章节介绍完read / write memory后,在這我們开始讲解DM9051是如何 送收packet ,首先先說明 RX packet 的流程,RX的部分最主要的是會将收進來的Date 返回在uip_len

    主要流程:

    1. 首先判断 DM9051 MRCMDX register 是不是為01,假如不為01則重置設備。

    2. 計算FIFO pointer,判断是否超過 0x3fff ,則需回歸繞到 0x0c00 起始位置

    3. 将收到的packet返回rx_statusrx_len,再将rx_len丟到uip_buf,完成后繼續判断rx_status 有無錯誤,沒有的話的就返回rx_len

     

    相关代码參考如下:

    uint16_t DM9051_rx(void)

    {

    uint8_t rxbyte;

      

    uint16_t calc_MRR;

    uint16_t rx_len = 0;

    /* Check packet ready or not                                                                              */

    rxbyte = DM9051_Read_Reg(DM9051_MRCMDX);

    rxbyte = DM9051_Read_Reg(DM9051_MRCMDX);

    /* if rxbyte != 1 or 0 reset device */

    if ((rxbyte != 1) && (rxbyte != 0))

    {

    printf("rxbyte = %02X.\r\n", rxbyte);

    DM9051_TRACE2("DM9051 rx: rx error, stop device \r\n");

     

    /* Reset RX FIFO pointer */

    DM9051_Write_Reg(DM9051_RCR, RCR_DEFAULT);//RX disable

    DM9051_Write_Reg(DM9051_MPCR, 0x01);//Reset RX FIFO pointer

    _DM9051_Delay(2);

     DM9051_Write_Reg(DM9051_RCR, (RCR_DEFAULT | RCR_RXEN));//RX Enable

        

    /* restore receive interrupt                                                                            */

    DM9051_device.imr_all |= IMR_PRM;

    //DM9051_device.imr_all = IMR_DEFAULT;

    DM9051_Write_Reg(DM9051_IMR, DM9051_device.imr_all);  

    return 0;

    }

     

    if (rxbyte)

    {

    uint16_t rx_status;

    //uint16_t* data = 0;

    uint8_t ReceiveData[4];

     

    #ifdef FifoPointCheck 

    calc_MRR = (DM9051_Read_Reg(DM9051_MRRH) << 8) + DM9051_Read_Reg(DM9051_MRRL);//Save RX SRAM pointer

    #endif//FifoPointCheck

    DM9051_Read_Reg(DM9051_MRCMDX);//dummy read

    DM9051_Read_Mem(ReceiveData, 4);

    rx_status = ReceiveData[0] + (ReceiveData[1] << 8);

    rx_len = ReceiveData[2] + (ReceiveData[3] << 8);

     

    DM9051_Read_Mem((u8_t *)uip_buf, rx_len);

    #ifdef FifoPointCheck

    /* if over 0x3fff, return 0x0c00 */

    calc_MRR += (rx_len + 4);

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

    if(calc_MRR != ((DM9051_Read_Reg(DM9051_MRRH) << 8) + DM9051_Read_Reg(DM9051_MRRL)))

    {

     

    printf("DM9K MRR Error!!\r\n");

    printf("Predicut RX Read pointer = 0x%X, Current pointer = 0x%X\r\n", calc_MRR, ((DM9051_Read_Reg(DM9051_MRRH) << 8) + DM9051_Read_Reg(DM9051_MRRL)));

    /* if pointer error, move pointer to next packet header */

    DM9051_Write_Reg(DM9051_MRRH, (calc_MRR >> 8) & 0xff);

    DM9051_Write_Reg(DM9051_MRRL, calc_MRR & 0xff);

    }

    #endif  

     

    if ((rx_status & 0xbf00) || (rx_len < 0x40) || (rx_len > DM9051_PKT_MAX) )

    {

      DM9051_TRACE2("rx error: status %04x, rx_len: %d \r\n", rx_status, rx_len);

     

      if (rx_status & 0x100)

      {

    DM9051_TRACE2("rx fifo error \r\n");

      }

      if (rx_status & 0x200)

      {

    DM9051_TRACE2("rx crc error \r\n");

      }

      if (rx_status & 0x8000)

      {

    DM9051_TRACE2("rx length error \r\n");

      }

      if (rx_len > DM9051_PKT_MAX)

      {

    DM9051_TRACE2("rx length too big \r\n");

    /* RESET device                                                                                     */

    //_DM9051_WriteReg(DM9051_NCR, NCR_RST);

    //_DM9051_Delay_ms(5);

      }

    }

    }

    return rx_len;

    }

     

    4-5 DM9051 Transmitted Packet

    讲解完RX后,接著說明 DM9051 TX流程:

    1. 等待DM9051 TX Control Register Ready

    2. 計算FIFO Pointer若是超過 0x0bff ,則需回歸繞到 0x0000 起始位置

    3. 写入data 至 FIFO Poniter

    4. 送出data ,在完成后清除 TX request

    5. 判断FIFO Pointer有無亂掉,有的話往前移8 bit

     

    相关代码參考如下:

    u32 DM9051_tx(void)

    {

      

    uint16_t calc_MWR;

    uint16_t SendLength;

     

    while(DM9051_Read_Reg(DM9051_TCR) & DM9051_TCR_SET) 

    {

    _DM9051_Delay(5);

    }

     

    #ifdef FifoPointCheck

    /* 計算下一個傳送的指針位 若接收長度為奇數,需加一对齊偶字节 */

    /* 若是超過 0x0bff ,則需回歸繞到 0x0000 起始位置 */

    calc_MWR = (DM9051_Read_Reg(DM9051_MWRH) << 8) + DM9051_Read_Reg(DM9051_MWRL);

    calc_MWR += SendLength;

    //printf("calc_MWR = 0x%X\r\n", calc_MWR);

    //if(SendLength & 0x01) calc_MWR++;

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

    #endif

     

    /* Move data to DM9051 TX RAM                                                                             */

    //_DM9051_WriteRegAddr(DM9051_MWCMD);

     

        //Write data to FIFO

        DM9051_Write_Reg(DM9051_TXPLL, uip_len & 0xff);

        DM9051_Write_Reg(DM9051_TXPLH, (uip_len >> 8) & 0xff);

        DM9051_Write_Mem((u8_t*)uip_buf, SendLength);

    #if 0   

        printf("Tx Read Pointer H = 0x%02X.\r\n", DM9051_Read_Reg(DM9051_TRPAH));

    printf("Tx Read Pointer L = 0x%02X.\r\n", DM9051_Read_Reg(DM9051_TRPAL));

    printf("DM9051_MWRH H = 0x%02X.\r\n", DM9051_Read_Reg(DM9051_MWRH));

    printf("DM9051_MWRL L = 0x%02X.\r\n", DM9051_Read_Reg(DM9051_MWRL));

    #endif  //0

     

        //Write data to FIFO

        DM9051_Write_Reg(DM9051_TXPLL, q->tot_len & 0xff);

        DM9051_Write_Reg(DM9051_TXPLH, (q->tot_len >> 8) & 0xff);

        DM9051_Write_Mem((u8_t*)q->payload, q->tot_len);

            

        /* Issue TX polling command                                                                             */

        DM9051_Write_Reg(DM9051_TCR, TCR_TXREQ);    /* Cleared after TX complete */

     

    #ifdef FifoPointCheck

    if(calc_MWR != ((DM9051_Read_Reg(DM9051_MWRH) << 8) + DM9051_Read_Reg(DM9051_MWRL)))

    {

    printf("DM9K MWR Error!! calc_MWR = 0x%X, SendLength = 0x%x\r\n", calc_MWR, SendLength);

    printf("MWRH = 0x%X, MWRL = 0x%X\r\n", DM9051_Read_Reg(DM9051_MWRH), DM9051_Read_Reg(DM9051_MWRL));

     

    /*Ι͏̼ѷƘ࠹Ҏ̼ѷҾɬŕŀΓׇѥƝʺƝYǬ٭  */

    DM9051_Write_Reg(DM9051_MWRH, (calc_MWR >> 8) & 0xff);

    DM9051_Write_Reg(DM9051_MWRL, calc_MWR & 0xff);

    }

    #endif //FifoPointCheck

     

      return 0;

    }



    5.uIP基本结构与配置

    5-1 首先介绍下uIP协议部分

    uIP协议栈提供了我们很多接口函数,这些函数在 uip.h 中定义,为了减少函数调用造成的额外支出,大部分接口函数以宏命令实现的,uIP提供的接口函数有:

    1. 初始化uIP协定栈:uip_init()

    2.处理输入包:uip_input()

    3.处理周期计时事件:uip_periodic()

    4.开始监听端口:uip_listen()

    5.连接到远程主机:uip_connect()

    6.接收到连接请求:uip_connected()

    7.主动关闭连接:uip_close()

    8.连接被关闭:uip_closed()

    9.发出去的数据被应答:uip_acked()

    10.在当前连接发送数据:uip_send()

    11.在当前连接上收到新的数据:uip_newdata()

    12.告诉对方要停止连接:uip_stop()

    13.连接被意外终止:uip_aborted()

     

    在使用uIP的时候,一般通过如下顺序:

    (1). 实现接口函数(回调函数)UIP_APPCALL

    该函数是我们使用uIP最关键的部分,它是uIP和应用程序的接口,我们必须根据自己的需要,在该函数做各种处理,而做这些处理的触发条件,就是前面提到的uIP提供的那些接口函数,如uip_newdatauip_ackeduip_closed等等。另外,如果是UDP,那么还需要实现UIP_UDP_APPCALL回调函数。

     

    (2). 调用tapdev_init函数,先初始化网卡。

    此步先初始化网卡,配置MAC地址、ethernet mode (10/100 rate ),为uIP和网络通信做好准备。

     

    (3). 调用uip_init函数,初始化uIP协定栈。

    此步主要用于uip自身的初始化,我们直接调用就是。

     

    (4). 等待DHCP 取得IP设置固定IP地址、网关以及屏蔽等。

    在初始化网卡完成后,DHCPsend discover packet 来取得IP,如果DHCP time out则会设定固定IP。这个和计算机上网差不多,只不过我们这里是通过uip_ipaddruip_sethostaddruip_setdraddruip_setnetmask等函数实现。

     

    (5). 设置Applocation监听端口

    uIP根据你设定的不同监听端口,实现不同的服务,比如我们实现Web Server就监听80端口(浏览器默认的端口是80端口),凡是发现80端口的数据,都通过Web ServerAPPCALL函数处理。根据自己的需要设置不同的监听端口。不过uIP有本地端口(lport)和远程端口(rport)之分,如果是做服务端,我们通过监听本地端口(lport)实现;如果是做客户端,则需要去连接远程端口(rport)。

     

    (6). 处理uIP事件

    最后,uIP通过uip_polling函数轮询处理uIP事件。该函数必须插入到用户的主循环里面(也就是必须每隔一定时间调用一次)。

     

    5-2 uIP基本结构

        uIP的代码编写需要遵守一定的结构,而且这种结构最好保持稳定(保持不变)。这个结构主要做以下几个部分任务。

    1. 初始化系統、驱动、uIP protocol

    2. 取的DHCP IP,或固定IP

    3. 获得以太网数据报

    4. 处理ARP报文

    5. 处理IP报文

    6. 定期处理TCPUDP连接

    7. 定期更新ARP缓冲区

     

    參考如下:

    int main(void)

    {

    #ifndef __DHCPC_H__

    uip_ipaddr_t ipaddr;

    #endif //__DHCPC_H__

     

      int i;

      struct timer periodic_timer, arp_timer;

     

      USART_Configuration();

      

      /* Setting polling and arp timer */

      timer_set(&periodic_timer, CLOCK_SECOND / 2);//500ms

      timer_set(&arp_timer, CLOCK_SECOND * 10);// 10sec

      

      /* Init ethernet driver */

      tapdev_init();

      /* Init uIP portocol */

      uip_init();

      uip_arp_init();// Clear arp table.

     

    #ifdef __DHCPC_H__

    /* setup the dhcp renew timer the make the first request */

    timer_set(&dhcp_timer, CLOCK_SECOND * 600);

    /* Init Dhcpc */

    dhcpc_init(&uip_ethaddr, 6);

    //dhcpc_request();

    #else

      uip_ipaddr(ipaddr, 192,168,7,51);//Setting Host IP address

      uip_sethostaddr(ipaddr);

      uip_ipaddr(ipaddr, 192,168,7,1);//Setting Default Gateway

      uip_setdraddr(ipaddr);

      uip_ipaddr(ipaddr, 255,255,255,0);//Setting Network Mask

      uip_setnetmask(ipaddr);

      

      /* Display system information */

      printf("\r\n---------------------------------------------\r\n");

      printf("Network chip: DAVICOM DM9051 \r\n");

      printf("MAC Address: %02X:%02X:%02X:%02X:%02X:%02X \r\n", uip_ethaddr.addr[0], uip_ethaddr.addr[1],

    uip_ethaddr.addr[2], uip_ethaddr.addr[3], uip_ethaddr.addr[4], uip_ethaddr.addr[5]);

      uip_gethostaddr(ipaddr); //get host IP

      printf("Host IP Address: %d.%d.%d.%d \r\n", uip_ipaddr1(ipaddr), uip_ipaddr2(ipaddr), uip_ipaddr3(ipaddr), uip_ipaddr4(ipaddr));

      uip_getnetmask(ipaddr); //get Get netmask 

      printf("Network Mask: %d.%d.%d.%d \r\n", uip_ipaddr1(ipaddr), uip_ipaddr2(ipaddr), uip_ipaddr3(ipaddr), uip_ipaddr4(ipaddr));

      uip_getdraddr(ipaddr); // get getway

      printf("Gateway IP Address: %d.%d.%d.%d \r\n", uip_ipaddr1(ipaddr), uip_ipaddr2(ipaddr), uip_ipaddr3(ipaddr), uip_ipaddr4(ipaddr));

      printf("---------------------------------------------\r\n");

    #endif //__DHCPC_H__

      

      httpd_init(); //Init TCP task 

     

      /*Initial and start system tick time = 1ms */

      SysTick_Config(SystemCoreClock / 1000);  

      

      while(1) {

        uip_len = tapdev_read(); //reveive packet, and return uip_len

        if(uip_len > 0) {

          if(BUF->type == htons(UIP_ETHTYPE_IP)) {

    //uip_arp_ipin();   //Removed by Spenser

    uip_input();// uip_process(UIP_DATA)

    /* If the above function invocation resulted in data that

       should be sent out on the network, the global variable

       uip_len is set to a value > 0. */

    if(uip_len > 0) {

      uip_arp_out();

      tapdev_send();

    }

          } else if(BUF->type == htons(UIP_ETHTYPE_ARP)) {

    uip_arp_arpin();

    /* If the above function invocation resulted in data that

       should be sent out on the network, the global variable

       uip_len is set to a value > 0. */

    if(uip_len > 0) {

      tapdev_send();

    }

          }

     

        } else if(timer_expired(&periodic_timer)) {

    timer_reset(&periodic_timer);

    for(i = 0; i < UIP_CONNS; i++) {

    uip_periodic(i);

    /* If the above function invocation resulted in data that

       should be sent out on the network, the global variable

       uip_len is set to a value > 0. */

    if(uip_len > 0) {

      uip_arp_out();

      tapdev_send();

    }

          }

    #if UIP_UDP

          for(i = 0; i < UIP_UDP_CONNS; i++) {

    uip_udp_periodic(i);

    /* If the above function invocation resulted in data that

       should be sent out on the network, the global variable

       uip_len is set to a value > 0. */

    if(uip_len > 0) {

      uip_arp_out();

      tapdev_send();

    }

          }

    #endif /* UIP_UDP */

          

          /* Call the ARP timer function every 10 seconds. */ 

          if(timer_expired(&arp_timer)) {

    timer_reset(&arp_timer);

    uip_arp_timer();

          }

        }

    #ifdef __DHCPC_H__

    else if (timer_expired(&dhcp_timer)) {

                // for now turn off the led when we start the dhcp process

                dhcpc_renew();

                timer_reset(&dhcp_timer);

        }

    #endif //__DHCPC_H__

      }

    }

     

    简单說明一下uip buffer和 调试驱动 :

        1.#define BUF ((struct uip_eth_hdr *)&uip_buf[0])

        指向uIP缓冲区,强制类型转化为uip_eth_hdr结构体,uip_eth_hdr即为以太网首部结构,6字节目标MAC地址 6字节源MAC地址 2字节类型。

        2. tapdev_init();tapdev_read();tapdev_send();

        三个函数为以太网操作函数,只有tapdev_read有返回值,其他函数即无输入参数也无返回参数。这三个函数便是DM9051操作的三个封装,DM9051发送或接收直接操作uIP的两个全局变量uip_bufuip_lentapdev.c具体代码如下:

    /* Init DM9051 */

    void tapdev_init(void)

    {

      DM9051_init();

    }

    /* received data return uip_len */

    unsigned int tapdev_read(void)

    {

      return DM9051_rx();

    }

    /* send data */

    void tapdev_send(void)

    {

      DM9051_tx();

    }

     

     

    5-3 uIP配置部分

    5-3-1. IP地址配置

    IP地址设置包括,本地IP地址,网关地址和子网掩码。可参考6-2

    5-3-1. MAC地址配置

    MAC的地址较为特殊,由于DM9051本身没有唯一的EUI-48(俗称MAC地址)地址,所以EUI-48地址需要手动配置。该地址不但应用于DM9051也应用于uIP。相关设定MAC Address代码如下介绍:

     

    首先在uip-conf.h,定义emacETHADDR 设定MAC address:

    #define emacETHADDR0   0x00

    #define emacETHADDR1   0x60

    #define emacETHADDR2   0x6e

    #define emacETHADDR3   0x90

    #define emacETHADDR4   0x51

    #define emacETHADDR5   0x02

     

    #if UIP_FIXEDETHADDR // if open UIP_FIXEDETHADDR

    const struct uip_eth_addr uip_ethaddr = {{emacETHADDR0,

      emacETHADDR1,

      emacETHADDR2,

      emacETHADDR3,

      emacETHADDR4,

      emacETHADDR5}};

    #else

    struct uip_eth_addr uip_ethaddr = {{0,0x6A,0x60,0x90,0x51,0}};

    #endif

     

    另外补充:DM9051 默认先从EEPROM读取MAC地址,如无挂载EEPROM,则通过软件设定。IP地址也是先透过DHCP获取,如获取不到则采取代码设定固定值。  

     

    5-4 uip-conf.h部分

    uip-conf部分说明三点

    1. 如果不熟悉请保留默认参数,例如UIP_CONF_MAX_CONNECTIONS

    2. 如果设置UIP_CONF_LOGGING1,请添加void uip_log(char *m){}

    3. 必须包含用户任务头文件,且放在该头文件的最后。例如添加#include "example1.h"。这样做的主要目的是定义uip_tcp_appstate_tUIP_APPCALL两个关键参数。具体代码如下:

     

    #ifndef __UIP_CONF_H  

    #define __UIP_CONF_H  

    #include <inttypes.h>  

    typedef uint8_t u8_t;  

    typedef uint16_t u16_t;  

    typedef unsigned short uip_stats_t;  

    /* 最大TCP连接数 */

    #define UIP_CONF_MAX_CONNECTIONS 10  

    /* 最大端口监听数 */

    #define UIP_CONF_MAX_LISTENPORTS 10  

    /* uIP 缓存大小*/

    #define UIP_CONF_BUFFER_SIZE 1500  

    /* CPU字节顺序 */

    #define UIP_CONF_BYTE_ORDER UIP_LITTLE_ENDIAN  

    /* 日志开关  */

    #define UIP_CONF_LOGGING 1  

    /* UDP支援开关*/

    #define UIP_CONF_UDP 0  

    /* UDP校验和开关 */

    #define UIP_CONF_UDP_CHECKSUMS 1  

    /* uIP统计开关 */

    #define UIP_CONF_STATISTICS 1

    // 加入用户任务头文件,请修改 

    #include "example1.h"

    #endif  

     

    5-5一个简单有效的Clock Tick

    uIP协议栈处理过程需要一个定时配合,该定时器实际为一个软件定时器,定时器说明uIP处理若干周期性任务,例如处理TCP连接重传,定时更新ARP缓冲表等。设计定时器的方法很多,在这里推荐uIP原作者的timer模块。timer模块的原理类似于MCU硬件中的比较匹配原理,timer模块中有一个全部变量counter,每次MCU发生某个定时器中断时累加1,如果某个任务需要使用定时器服务,在该任务中声明一个timer(在该任务中为全局变量),并记录此时的counter值。判断溢出可查询当前的counter和被记录的counter的差值,如果差值超过间隔值那么软件定时器timer溢出(类似于发生比较匹配中断)。软件定时器的主要作用有两个。第一,更新TCPUDP连接,第二,更新ARP缓冲区(ARP)虽然uIP在功能上比LwIP简单的多,但是LwIP也有类似的部分(或者说完全一样)。详细代码修改clock-arch.c如下:

     

    #include "clock-arch.h"

    #include "stm32f10x.h"

     

    extern __IO int32_t g_RunTime;

    /*---------------------------------------------------------------------------*/

    clock_time_t

    clock_time(void)

    {

    return g_RunTime;

    }

    /*---------------------------------------------------------------------------*/

     

    修改clock-arch.h如下

     

    typedef int clock_time_t;

    #define CLOCK_CONF_SECOND 100 

     

    作用是:clock_time 用于配置系统产生滴答的间隔,我的开发板系统时钟跑的是72MHz,即每隔720,000,000/100产生一次系统滴答,一个系统滴答是10ms, CLOCK_SECOND (这里定义为100) clock tick即为1S

     

     

    使用stm32 System Tick中断代码,stm32f10x_it.c修改如下:

     

    __IO int32_t g_RunTime = 0;

    void SysTick_Handler(void)

    {

      static uint8_t s_count = 0;

      if (++s_count >= 10)

       {

          s_count = 0;

          g_RunTime++;    /* every 10ms add 1 */

          if (g_RunTime == 0x80000000)

          {

             g_RunTime = 0;

          }

        }

    }

     

    6.案例——最简单的TCP echo程序

        先来一个最简单的TCP程序。uIP作为serverIP地址为192.168.1.15PC机做clientIP地址为192.168.1.10X

        1】在网络调试助手中,选择以太网通信种类为client(表示PC机为Client)IP地址输入192.168.1.15,端口号输入1234。最后点击连接。

        2】在发送区域输入任意内容,点击发送数据。

        3】观察返回结果,是否和发送数据相同。

        为了实现该功能新建example1.cexample1.两个文件。代码如下:

     

    #include "example1.h"  

    #include "uip.h"  

    #include <string.h>  

    #include <stdio.h>  

    #include <stdint.h>  

    void example1_init(void)  

    {  

        uip_listen(HTONS(1234));  

    }  

    void example1_appcall(void)  

    {  

        if( uip_newdata() )  

        {  

            // 输出远程IP端口号  

            printf("remote ip addr:%d.%d.%d.%d\r\n",  

                   (uip_conn->ripaddr[0]) & 0X00ff,  

                   (uip_conn->ripaddr[0]) >> 8,  

                   (uip_conn->ripaddr[1]) & 0X00ff,  

                   (uip_conn->ripaddr[1]) >> 8  

                       );  

            printf("remote ip port:%d\r\n",HTONS(uip_conn->rport));  

            // TCP ECHO  

            uip_send(uip_appdata,uip_len);  

        }  

    }   


    3 TCP Echo实验结果

     

    代码做如下分析:

    1. uip_listen(HTONS(1234));侦听1234端口,

    2. uip_newdata()即查询uip_buf中是否有新数据,如果返回1的话,表示接收到新数据。

    3. uip_send(uip_appdata,uip_len);uip_send为发送数据报函数

    4. uip_appdata指向用户数据,所谓用户数据即TCP负载数据,例如网络调试助手发送XX,那么uip_appdata指向xukai871105.

    5. uip_len为用户数据长度,若串口调试助手发送XX,那么uip_len11


    7.web页面控制

    1. 打开IE浏览器,地址栏输入DM9051IP地址:

       http://192.168.x.x/

     

    回车后打开下面控制页面:

     

    2. 网頁下可以看到MCUIP基本訊息(:MACIP address),最下面有LED Test可通过网页控制STM32板上的LED

    LED Test說明,在按下 ON 的時候,GPIOF6 腳位LED 燈會亮,按下OFF後會滅掉,Flash 代表閃爍,會從GPIOF6 ~9 LED順序亮滅,网址列上會顯示LED1LED01代表亮,0但表滅,2代表閃爍。

     


     

     

    3. 具体code如下介紹:

    (1). 稍微介绍一下web相关,另外标出端口、IPGPIO/LED的修改位置

    void main()中,系統初始化完成後加入httpd_init(); 來初始化webserver port,預設web 80 port

    (2). 在初始化完成後,在uip-conf.h最下面將#include "webserver.h"的注釋拿掉,這樣UIP_APPCALL 就能對應到 httpd_appcall

    (3). 首先我們先在main.c初始 LEDGPIO PIN,代码如下:


    void LedInit(void)

    {

    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOF, ENABLE);

    /*初始化 GPIOF的 Pin_6为推挽输出*/ 

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

    GPIO_Init(GPIOF,&GPIO_InitStructure);

    /*初始化 GPIOF的 Pin_7为推挽输出*/ 

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

    GPIO_Init(GPIOF,&GPIO_InitStructure);

    /*初始化 GPIOF的 Pin_8为推挽输出*/ 

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

    GPIO_Init(GPIOF,&GPIO_InitStructure);

    /*初始化 GPIOF的 Pin_9为推挽输出*/ 

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

    GPIO_Init(GPIOF,&GPIO_InitStructure);

    }

     

    初始化完成後,在webserver目錄下新增web_led.cweb_led.h檔,代码參考如下:

    (1). 首先設定LED 亮滅delay 時間:

    void Delay(uint32_t times)

    {

    while(times--)

    {

    uint32_t i;

    for (i=0; i<0xffff; i++);

    }

    }

    (2). 接下來設定LED 亮滅順序,我們使用ascii來判斷亮滅,

    void Set_LED_mode(char lkkcode)

    {

    //int i;

    /* 0代表LED6, 1代表LED6, 2代表LED6~9依序閃爍 */

    if(lkkcode == ('0'))

    {

    GPIO_ResetBits(GPIOF,GPIO_Pin_6);

    }else if (lkkcode == '1'){

    GPIO_SetBits(GPIOF,GPIO_Pin_6);

    }else if(lkkcode == '2')

    {

    //for(i = 0 ; i< 30 ; ++i)

    {

    GPIO_SetBits(GPIOF,GPIO_Pin_6); 

    Delay(25);

    GPIO_ResetBits(GPIOF,GPIO_Pin_6);

    Delay(25);

    GPIO_SetBits(GPIOF,GPIO_Pin_7); 

    Delay(25);

    GPIO_ResetBits(GPIOF,GPIO_Pin_7);

    Delay(25);

    GPIO_SetBits(GPIOF,GPIO_Pin_8);

    Delay(25);

    GPIO_ResetBits(GPIOF,GPIO_Pin_8);

    Delay(25);

    GPIO_SetBits(GPIOF,GPIO_Pin_9); 

    Delay(25);

    GPIO_ResetBits(GPIOF,GPIO_Pin_9);

    Delay(25);

    }

    }

    }

    (3). 設定好初始化LED GPIO後,在网址列輸入IP後會顯預設的示网頁,网頁內容為html放在app/webserver/ httpd-fs目錄下,主要為index.html,如想要修改顯示使用者网頁有兩種方式:

    (4). 修改代码參考如下

    1. 直接修改index.html

    2. 將使用者的html 放入httpd-fs目錄下,然後在handle_input修改

    3. 判斷LED亮滅,s->inputbuf array中填入ascii码或其他使用者想實現的判斷,如s->inputbuf[3] = 'L','E','D'

     

    static PT_THREAD(handle_input(struct httpd_state *s))

    {

      PSOCK_BEGIN(&s->sin);

     

      PSOCK_READTO(&s->sin, ISO_space);

     

      if(strncmp(s->inputbuf, http_get, 4) != 0) {

        PSOCK_CLOSE_EXIT(&s->sin);

      }

      PSOCK_READTO(&s->sin, ISO_space);

     

      if(s->inputbuf[0] != ISO_slash) {

        PSOCK_CLOSE_EXIT(&s->sin);

      }

     

      if(s->inputbuf[1] == ISO_space) {

        //strncpy(s->filename, http_index_html, sizeof(s->filename)); //修改成想要顯示的html檔,如下:

    strncpy(s->filename, http_webMain_html, sizeof(s->filename)); 

      } 

    /* Control led , 0 = OFF, 1 = ON, 2 = Flash */

    #if 1 //Joseph add

      else if (s->inputbuf[3] == 'L','E','D' && ((s->inputbuf[4] == '0') || 

    (s->inputbuf[4] == '1') || (s->inputbuf[4] == '2'))){

      

        Set_LED_mode(s->inputbuf[4]);

        s->inputbuf[4]= 0;

        //strncpy(s->filename, "/home.html", 10);

    strncpy(s->filename, http_webMain_html, sizeof(s->filename));

    }

    #endif

    else {

        s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;

        strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename));

      }

     

      /*  httpd_log_file(uip_conn->ripaddr, s->filename);*/  

      s->state = STATE_OUTPUT;

      while(1) {

        PSOCK_READTO(&s->sin, ISO_nl);

     

        if(strncmp(s->inputbuf, http_referer, 8) == 0) {

          s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;

          /*      httpd_log(&s->inputbuf[9]);*/

        }

      }

      PSOCK_END(&s->sin);

    }

     

    (5).上面介紹完网頁和控制LED燈後,接著說明如何在网頁上顯示目前的MACIP address等,在httpd-cgi.c檔下新增sys_ stats function,代參考如下:

     

    static unsigned short generate_sys_stats(void *arg)

    {

      struct httpd_state *s = (struct httpd_state *)arg;

     

      return snprintf((char *)uip_appdata, UIP_APPDATA_SIZE,

     "<tr> <td >MAC Address :</td> <td style=\"text-align: left\">%02x:%02x:%02x:%02x:%02x:%02x</td> </tr> \

    <tr> <td >IP Address : </td> <td style=\"text-align: left\"> %d.%d.%d.%d </td> </tr> \

    <tr> <td >Network Mask : </td> <td style=\"text-align: left\"> %d.%d.%d.%d </td> </tr> \

    <tr><td >Getway : </td> <td style=\"text-align: left\"> %d.%d.%d.%d </td> </tr>\r\n",

    /* MAC address */

    (unsigned int)uip_ethaddr.addr[0], (unsigned int)uip_ethaddr.addr[1], 

    (unsigned int)uip_ethaddr.addr[2], (unsigned int)uip_ethaddr.addr[3], 

    (unsigned int)uip_ethaddr.addr[4], (unsigned int)uip_ethaddr.addr[5],

    /* IP address */

    (uip_hostaddr[0]&0xff), ((uip_hostaddr[0] >> 8)&0xff),

    (uip_hostaddr[1]&0xff), ((uip_hostaddr[1] >> 8)&0xff),

    /* network mask */

    (uip_netmask[0]&0xff), ((uip_netmask[0] >> 8)&0xff),

    (uip_netmask[1]&0xff), ((uip_netmask[1] >> 8)&0xff),

    /* Getway address */

    (uip_draddr[0]&0xff), ((uip_draddr[0] >> 8)&0xff),

    (uip_draddr[1]&0xff), ((uip_draddr[1] >> 8)&0xff)

    );

    }

     

    static PT_THREAD(sys_stats(struct httpd_state *s, char *ptr))

    {

      

      PSOCK_BEGIN(&s->sout);

     

      //for(s->count = 0; s->count < UIP_CONNS; ++s->count) {

        if((uip_conns[s->count].tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) {

          PSOCK_GENERATOR_SEND(&s->sout, generate_sys_stats, s);

        }

      //}

     

      PSOCK_END(&s->sout);

     

     

     

     

     

     

    8.测试结果

    接上网线后:

    首先设置PCIPDM9051 在同一段内;

     

    1. 运行-cmd

    输入  ping 192.168.x.x          测试是否能ping通;

    输入  ping 192.168.x.x  -n 100     测试连通丢包率;ctrl+c停止;

     

     

     

    2. 查看确认物理地址

    输入 arp  -a 192.168.x.x 回车;显示物理地址。

     

     

     

     

     

     

     

    3. 速率测试结果:

    注:所用工具软件为:网络调试助手+IPOP

     


     

    A. 利用网络调试助手循环发送文档,让输出达到最大值从而测试DM9051的最大速率;

    B. 由结果可以看出速率,而平均235Kb/s的速率算是比较稳定的了。

    4. 使用iperf頻寬测试结果大約有8 . 5M多:

     

    9.移植相关

    移植第一步:实现在unix/tapdev.c里面的三个函数。首先是tapdev_init函数,该函数用于初始化网卡(也就是我们的DM9051),通过这个函数实现网卡初始化。其次,是tapdev_read函数,该函数用于从网卡读取一包数据,将读到的数据存放在uip_buf里面,数据长度返回给uip_len。最后,是tapdev_send函数,该函数用于向网卡发送一包数据,将全局缓存区uip_buf里面的数据发送出去(长度为uip_len)。其实这三个函数就是实现最底层的网卡操作。

     

    第二步,因为uIP协议栈需要使用时钟,为TCPARP的定时器服务,因此我们需要STM32提供一个定时器做时钟,提供10ms计时(假设clock-arch.h里面的CLOCK_CONF_SECOND100),通过clock-arch.c里面的clock_time函数返回给uIP使用。

     

    第三步,配置uip-conf.h里面的宏定义选项。主要用于设置TCP最大连接数、TCP监听端口数、CPU大小端模式等,这个大家根据自己需要配置即可。

    通过以上3步的修改,我们基本上就完成了uIP的移植。

     

    1,写网卡驱动程序,与具体硬件相关。这一步比较费点时间,不过好在大部分网卡芯片的驱动程序都有代码借鉴或移植。驱动需要提供三个函数:

     

    tapdev_init():网卡初始化函数,初始化网卡的工作模式。

    tapdev_read(void):读包函数。将网卡收到的数据放入全局缓存区uip_buf 中,返回包的长度,赋给uip_len

    void tapdev_send(void):发包函数。将全局缓存区uip_buf 里的数据(长度放在uip_len 中)发送出去。

     

    2.uipopt.h/uip-conf.h 是配置文件,用来设置本地的IP 地址、网关地址、MAC 地址、全局缓冲区的大小、支持的最大连接数、侦听数、ARP 表大小等。可以根据需要配置。

     

     

     

     

     

     

     

     

     

     


     

     


    展开全文
  • M4单片机+DM9051NP网卡项目总结

    千次阅读 2019-07-05 15:59:42
    M4单片机+DM9051NP网卡项目总结 1,云打印设备 云打印机技术需求 ·打印功能 ·联网功能 ·较复杂的控制算法 为何需要32-bit M4 MCU? ·高效能:M4F + 200MHz 主频 (AT32F403/413系列) ·大容量:程序...
  • STM32F030C8T6 单片机+DM9051以太网RJ45模块ALTIUM设计硬件原理图+PCB+封装库文件,采用4层板设计,板子大小为46x28mm,双面布局布线,包括完整的原理图和PCB文件,可以用Altium Designer(AD)软件打开或修改,可作为...
  • DM9051_SPI网卡驱动于EC200T4G模块有线网口驱动添加指导 目录 前言…………………………………………………………………………………………………3 应用简介……………………………………………………...
  • 适合MTK平台SPI总线网口驱动挂载,包含驱动文件,挂载步骤,参考设计原理图PCB文件!
  • STM32F103ZET+DM9051--SPI+以太网+web

    千次阅读 2015-10-27 10:55:22
    SPI转以太网芯片:DM9051NP 网络变压器:TRJ101 资料下载:http://pan.baidu.com/s/1o6nHuYm 里面有五个文件; 分别是UIP驱动协议栈、DM9051规格书、参考设计以及协议栈的分析文档。 正题...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 125
精华内容 50
关键字:

dm9051