• STM32F103VET6基于ENC28J60移植LWIP1.4.1(标准库,无RTOS)



    本文用于记录STM32F103VET6基于ENC28J60移植LWIP1.4.1。

    有了ENC28J60与LWIP之后,网络5层里除了应用层都完成了。本文使用ping测试移植结果,不进行应用层开发。

    按照本文移植成功的话,使用网线连接网卡与电脑,将电脑以太网IP设置在与网卡IP同一网段下,电脑应该可以ping通单片机。

    网上要么是用μCOS3,要么STM32F4,要么不用这个网卡。笔者自学被搞得哇哇大叫。因此写本文记录过程。

    笔者仅做移植记录,详细原理不做讲解。

    环境

    1. STM32F103VET6(野火指南者)
    2. LWIP 1.4.1
    3. 网卡为ENC28J60
    4. 一根网线

    引脚连接

    PB1 — INT
    GND — GND
    PA4 — CS
    PA5 — SCL
    PA6 — SO
    PA7 — SI
    PE5 — RST
    5V — VCC

    1.准备LWIP

    准备一个标准库项目,下载LWIP1.4.1源码。
    源码下载好后,解压,复制其中的src目录,粘贴到项目路径下,更改路径名为LWIP。
    点我下载
    在这里插入图片描述

    2.新建arch

    在LWIP文件夹下,新建文件夹arch,并在其中新建三个文件:cc.h、lwipopts.h、perf.h备用。
    在这里插入图片描述

    在这里插入图片描述

    3.网卡驱动

    本文基于网卡ENC28J60。它的驱动代码我参考了 这位大佬的博客
    原来的驱动是HAL库编写的,我跑起来有问题。我把它改成标准库(其实也就改了SPI读写一字节那一块)却能直接跑了。

    //enc28j60.c
    #include "enc28j60.h"
    static void ENC28J60_GPIO_Init(){
    		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE,ENABLE);
    		GPIO_InitTypeDef gpio_init;
    		gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
    		gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
    		gpio_init.GPIO_Pin = ENC28J60_CS_PIN;
    		GPIO_Init(ENC28J60_CS_PORT,&gpio_init);
    	
    		gpio_init.GPIO_Pin = GPIO_Pin_5;
    		GPIO_Init(GPIOE,&gpio_init);
    		
    		gpio_init.GPIO_Pin = GPIO_Pin_0;
    		GPIO_Init(GPIOB,&gpio_init);
    		
    		gpio_init.GPIO_Pin = GPIO_Pin_5;
    		gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
    		GPIO_Init(GPIOA,&gpio_init);
    		
    		gpio_init.GPIO_Pin = GPIO_Pin_6;
    		GPIO_Init(GPIOA,&gpio_init);
    		
    		gpio_init.GPIO_Pin = GPIO_Pin_7;
    		GPIO_Init(GPIOA,&gpio_init);
    		
    		GPIO_SetBits(ENC28J60_CS_PORT,ENC28J60_CS_PIN);
    		GPIO_SetBits(GPIOB,GPIO_Pin_0);
    }
    
    static void ENC28J60_SPI1_Init(){
    		RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
    		SPI_InitTypeDef spi_init;
    		spi_init.SPI_NSS = SPI_NSS_Soft;
    		spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    		spi_init.SPI_Mode = SPI_Mode_Master;
    		spi_init.SPI_DataSize = SPI_DataSize_8b;
    		spi_init.SPI_CPOL = SPI_CPOL_Low;
    		spi_init.SPI_CPHA = SPI_CPHA_1Edge;
    		spi_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
    		spi_init.SPI_FirstBit = SPI_FirstBit_MSB;
    		spi_init.SPI_CRCPolynomial = 7;
    		SPI_Init(SPI1,&spi_init);
    		SPI_Cmd(SPI1,ENABLE);
    }
    
    static void ENC28J60_EXTI_Init(){
    		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);
    		
    		NVIC_InitTypeDef nvic_init;
    		nvic_init.NVIC_IRQChannel = EXTI1_IRQn;
    		nvic_init.NVIC_IRQChannelPreemptionPriority = 1;
    		nvic_init.NVIC_IRQChannelSubPriority = 1;
    		nvic_init.NVIC_IRQChannelCmd = ENABLE;
    		NVIC_Init(&nvic_init);
    		
    		GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
    	
    		EXTI_InitTypeDef exti_init;
    		exti_init.EXTI_Line = EXTI_Line1;
    		exti_init.EXTI_Mode = EXTI_Mode_Interrupt;
    		exti_init.EXTI_Trigger = EXTI_Trigger_Falling;
    		exti_init.EXTI_LineCmd = ENABLE;
    		EXTI_Init(&exti_init);
    		
    		
    		GPIO_InitTypeDef gpio_init;
    		gpio_init.GPIO_Mode = GPIO_Mode_IPU;
    		gpio_init.GPIO_Pin = GPIO_Pin_1;
    		gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
    		GPIO_Init(GPIOB,&gpio_init);
    }
    
    static void ENC28J60_Reset(){
    		GPIO_ResetBits(GPIOE,GPIO_Pin_5);
    		uint16_t t = 0x1fff;
    		while(t--);
    		GPIO_SetBits(GPIOE,GPIO_Pin_5);
    }
    
    static unsigned char W25Q64_SendByte(uint8_t byte){
    			while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);
    			SPI_I2S_SendData(SPI1,byte);
    			while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) == RESET);
    			return SPI_I2S_ReceiveData(SPI1);
    }
    
    static unsigned char	SPI1_ReadWrite(unsigned char writedat){
    		unsigned char r = W25Q64_SendByte(writedat);
    		return r;
    }
    
    void Enc28j60_Init(void)
    {
    	ENC28J60_GPIO_Init();
    	ENC28J60_SPI1_Init();
    	ENC28J60_EXTI_Init();
    	ENC28J60_Reset();
    }
    
    static unsigned char Enc28j60Bank;
    static unsigned int NextPacketPtr;
    
    
    unsigned char enc28j60ReadOp(unsigned char op, unsigned char address)
    {
        unsigned char dat = 0;
        ENC28J60_CSL();
        dat = op | (address & ADDR_MASK);
        SPI1_ReadWrite(dat);
        dat = SPI1_ReadWrite(0xFF);
        // do dummy read if needed (for mac and mii, see datasheet page 29)
        if(address & 0x80)
        {
            dat = SPI1_ReadWrite(0xFF);
        }
        // release CS
        ENC28J60_CSH();
        return dat;
    }
    
    void enc28j60WriteOp(unsigned char op, unsigned char address, unsigned char data)
    {
        unsigned char dat = 0;
        ENC28J60_CSL();
        // issue write command
        dat = op | (address & ADDR_MASK);
        SPI1_ReadWrite(dat);
        // write data
        dat = data;
        SPI1_ReadWrite(dat);
        ENC28J60_CSH();
    }
    
    void enc28j60ReadBuffer(unsigned int len, unsigned char* data)
    {
        ENC28J60_CSL();
        // issue read command
        SPI1_ReadWrite(ENC28J60_READ_BUF_MEM);
        while(len)
        {
            len--;
            // read data
            *data = (unsigned char)SPI1_ReadWrite(0);
            data++;
        }
        *data='\0';
        ENC28J60_CSH();
    }
    
    void enc28j60WriteBuffer(unsigned int len, unsigned char* data)
    {
        ENC28J60_CSL();
        // issue write command
        SPI1_ReadWrite(ENC28J60_WRITE_BUF_MEM);
        
        while(len)
        {
            len--;
            SPI1_ReadWrite(*data);
            data++;
        }
        ENC28J60_CSH();
    }
    
    void enc28j60SetBank(unsigned char address)
    {
        // set the bank (if needed)
        if((address & BANK_MASK) != Enc28j60Bank)
        {
            // set the bank
            enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0));
            enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5);
            Enc28j60Bank = (address & BANK_MASK);
        }
    }
    
    unsigned char enc28j60Read(unsigned char address)
    {
        // set the bank
        enc28j60SetBank(address);
        // do the read
        return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address);
    }
    
    void enc28j60Write(unsigned char address, unsigned char data)
    {
        // set the bank
        enc28j60SetBank(address);
        // do the write
        enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data);
    }
    
    void enc28j60PhyWrite(unsigned char address, unsigned int data)
    {
        // set the PHY register address
        enc28j60Write(MIREGADR, address);
        // write the PHY data
        enc28j60Write(MIWRL, data);
        enc28j60Write(MIWRH, data>>8);
        // wait until the PHY write completes
        while(enc28j60Read(MISTAT) & MISTAT_BUSY)
        {
            //Del_10us(1);
            //_nop_();
        }
    }
    
    void enc28j60clkout(unsigned char clk)
    {
        //setup clkout: 2 is 12.5MHz:
        enc28j60Write(ECOCON, clk & 0x7);
    }
    
    void enc28j60Init(unsigned char* macaddr)
    {   
        ENC28J60_CSH();	      
    
        // perform system reset
        enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
       
        // check CLKRDY bit to see if reset is complete
        // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait.
        //while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY));
        // do bank 0 stuff
        // initialize receive buffer
        // 16-bit transfers, must write low byte first
        // set receive buffer start address	   
        NextPacketPtr = RXSTART_INIT;
        // Rx start    
        enc28j60Write(ERXSTL, RXSTART_INIT&0xFF);	 
        enc28j60Write(ERXSTH, RXSTART_INIT>>8);
        // set receive pointer address     
        enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF);
        enc28j60Write(ERXRDPTH, RXSTART_INIT>>8);
        // RX end
        enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF);
        enc28j60Write(ERXNDH, RXSTOP_INIT>>8);
        // TX start	  1500
        enc28j60Write(ETXSTL, TXSTART_INIT&0xFF);
        enc28j60Write(ETXSTH, TXSTART_INIT>>8);
        // TX end
        enc28j60Write(ETXNDL, TXSTOP_INIT&0xFF);
        enc28j60Write(ETXNDH, TXSTOP_INIT>>8);
        // do bank 1 stuff, packet filter:
        // For broadcast packets we allow only ARP packtets
        // All other packets should be unicast only for our mac (MAADR)
        //
        // The pattern to match on is therefore
        // Type     ETH.DST
        // ARP      BROADCAST
        // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9
        // in binary these poitions are:11 0000 0011 1111
        // This is hex 303F->EPMM0=0x3f,EPMM1=0x30
        
        enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN);
        enc28j60Write(EPMM0, 0x3f);
        enc28j60Write(EPMM1, 0x30);
        enc28j60Write(EPMCSL, 0xf9);
        enc28j60Write(EPMCSH, 0xf7);    
        enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
        // bring MAC out of reset 
        enc28j60Write(MACON2, 0x00);
        
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX);
        // set inter-frame gap (non-back-to-back)
    
        enc28j60Write(MAIPGL, 0x12);
        enc28j60Write(MAIPGH, 0x0C);
        // set inter-frame gap (back-to-back)
    
        enc28j60Write(MABBIPG, 0x15);
        // Set the maximum packet size which the controller will accept
        // Do not send packets longer than MAX_FRAMELEN:
      
        enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF);	
        enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8);
        // do bank 3 stuff
        // write MAC address
        // NOTE: MAC address in ENC28J60 is byte-backward
        enc28j60Write(MAADR5, macaddr[0]);	
        enc28j60Write(MAADR4, macaddr[1]);
        enc28j60Write(MAADR3, macaddr[2]);
        enc28j60Write(MAADR2, macaddr[3]);
        enc28j60Write(MAADR1, macaddr[4]);
        enc28j60Write(MAADR0, macaddr[5]);
    
        //配置PHY为全双工  LEDB为拉电流
        enc28j60PhyWrite(PHCON1, PHCON1_PDPXMD);    
        
        // no loopback of transmitted frames
        enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);
    
        // switch to bank 0    
        enc28j60SetBank(ECON1);
    
        // enable interrutps
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
    
        // enable packet reception
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
    }
    
    // read the revision of the chip:
    unsigned char enc28j60getrev(void)
    {
        //在EREVID 内也存储了版本信息。 EREVID 是一个只读控
        //制寄存器,包含一个5 位标识符,用来标识器件特定硅片
        //的版本号
        return(enc28j60Read(EREVID));
    }
    
    void enc28j60PacketSend(unsigned int len, unsigned char* packet)
    {
        // Set the write pointer to start of transmit buffer area
        enc28j60Write(EWRPTL, TXSTART_INIT&0xFF);
        enc28j60Write(EWRPTH, TXSTART_INIT>>8);
        
        // Set the TXND pointer to correspond to the packet size given
        enc28j60Write(ETXNDL, (TXSTART_INIT+len)&0xFF);
        enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8);
        
        // write per-packet control byte (0x00 means use macon3 settings)
        enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
        
        // copy the packet into the transmit buffer
        enc28j60WriteBuffer(len, packet);
        
        // send the contents of the transmit buffer onto the network
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
        
        // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12.
        if( (enc28j60Read(EIR) & EIR_TXERIF) )
        {
            enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
        }
    }
    
    // Gets a packet from the network receive buffer, if one is available.
    // The packet will by headed by an ethernet header.
    //      maxlen  The maximum acceptable length of a retrieved packet.
    //      packet  Pointer where packet data should be stored.
    // Returns: Packet length in bytes if a packet was retrieved, zero otherwise.
    unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet)
    {
        unsigned int rxstat;
        unsigned int len;
        
        // check if a packet has been received and buffered
        //if( !(enc28j60Read(EIR) & EIR_PKTIF) ){
        // The above does not work. See Rev. B4 Silicon Errata point 6.
        if( enc28j60Read(EPKTCNT) ==0 )  //收到的以太网数据包长度
        {
            return(0);
        }
        
        // Set the read pointer to the start of the received packet		 缓冲器读指针
        enc28j60Write(ERDPTL, (NextPacketPtr));
        enc28j60Write(ERDPTH, (NextPacketPtr)>>8);
        
        // read the next packet pointer
        NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
        NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
        
        // read the packet length (see datasheet page 43)
        len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
        len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
        
        len-=4; //remove the CRC count
        // read the receive status (see datasheet page 43)
        rxstat  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
        rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
        // limit retrieve length
        if (len>maxlen-1)
        {
            len=maxlen-1;
        }
        
        // check CRC and symbol errors (see datasheet page 44, table 7-3):
        // The ERXFCON.CRCEN is set by default. Normally we should not
        // need to check this.
        if ((rxstat & 0x80)==0)
        {
            // invalid
            len=0;
        }
        else
        {
            // copy the packet from the receive buffer
            enc28j60ReadBuffer(len, packet);
        }
        // Move the RX read pointer to the start of the next received packet
        // This frees the memory we just read out
        enc28j60Write(ERXRDPTL, (NextPacketPtr));
        enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8);
        
        // decrement the packet counter indicate we are done with this packet
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
        return(len);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    //enc28j60.h
    #ifndef __ENC28J60_H__
    #define __ENC28J60_H__
    #include "stm32f10x.h"
    
    #define ADDR_MASK        0x1F
    #define BANK_MASK        0x60
    #define SPRD_MASK        0x80
    // All-bank registers
    #define EIE              0x1B
    #define EIR              0x1C
    #define ESTAT            0x1D
    #define ECON2            0x1E
    #define ECON1            0x1F
    // Bank 0 registers
    #define ERDPTL           (0x00|0x00)
    #define ERDPTH           (0x01|0x00)
    #define EWRPTL           (0x02|0x00)
    #define EWRPTH           (0x03|0x00)
    #define ETXSTL           (0x04|0x00)
    #define ETXSTH           (0x05|0x00)
    #define ETXNDL           (0x06|0x00)
    #define ETXNDH           (0x07|0x00)
    #define ERXSTL           (0x08|0x00)
    #define ERXSTH           (0x09|0x00)
    #define ERXNDL           (0x0A|0x00)
    #define ERXNDH           (0x0B|0x00)
    //ERXWRPTH:ERXWRPTL 寄存器定义硬件向FIFO 中
    //的哪个位置写入其接收到的字节。 指针是只读的,在成
    //功接收到一个数据包后,硬件会自动更新指针。 指针可
    //用于判断FIFO 内剩余空间的大小。
    #define ERXRDPTL         (0x0C|0x00)
    #define ERXRDPTH         (0x0D|0x00)
    #define ERXWRPTL         (0x0E|0x00)
    #define ERXWRPTH         (0x0F|0x00)
    #define EDMASTL          (0x10|0x00)
    #define EDMASTH          (0x11|0x00)
    #define EDMANDL          (0x12|0x00)
    #define EDMANDH          (0x13|0x00)
    #define EDMADSTL         (0x14|0x00)
    #define EDMADSTH         (0x15|0x00)
    #define EDMACSL          (0x16|0x00)
    #define EDMACSH          (0x17|0x00)
    // Bank 1 registers
    #define EHT0             (0x00|0x20)
    #define EHT1             (0x01|0x20)
    #define EHT2             (0x02|0x20)
    #define EHT3             (0x03|0x20)
    #define EHT4             (0x04|0x20)
    #define EHT5             (0x05|0x20)
    #define EHT6             (0x06|0x20)
    #define EHT7             (0x07|0x20)
    #define EPMM0            (0x08|0x20)
    #define EPMM1            (0x09|0x20)
    #define EPMM2            (0x0A|0x20)
    #define EPMM3            (0x0B|0x20)
    #define EPMM4            (0x0C|0x20)
    #define EPMM5            (0x0D|0x20)
    #define EPMM6            (0x0E|0x20)
    #define EPMM7            (0x0F|0x20)
    #define EPMCSL           (0x10|0x20)
    #define EPMCSH           (0x11|0x20)
    #define EPMOL            (0x14|0x20)
    #define EPMOH            (0x15|0x20)
    #define EWOLIE           (0x16|0x20)
    #define EWOLIR           (0x17|0x20)
    #define ERXFCON          (0x18|0x20)
    #define EPKTCNT          (0x19|0x20)
    // Bank 2 registers
    #define MACON1           (0x00|0x40|0x80)
    #define MACON2           (0x01|0x40|0x80)
    #define MACON3           (0x02|0x40|0x80)
    #define MACON4           (0x03|0x40|0x80)
    #define MABBIPG          (0x04|0x40|0x80)
    #define MAIPGL           (0x06|0x40|0x80)
    #define MAIPGH           (0x07|0x40|0x80)
    #define MACLCON1         (0x08|0x40|0x80)
    #define MACLCON2         (0x09|0x40|0x80)
    #define MAMXFLL          (0x0A|0x40|0x80)
    #define MAMXFLH          (0x0B|0x40|0x80)
    #define MAPHSUP          (0x0D|0x40|0x80)
    #define MICON            (0x11|0x40|0x80)
    #define MICMD            (0x12|0x40|0x80)
    #define MIREGADR         (0x14|0x40|0x80)
    #define MIWRL            (0x16|0x40|0x80)
    #define MIWRH            (0x17|0x40|0x80)
    #define MIRDL            (0x18|0x40|0x80)
    #define MIRDH            (0x19|0x40|0x80)
    // Bank 3 registers
    #define MAADR1           (0x00|0x60|0x80)
    #define MAADR0           (0x01|0x60|0x80)
    #define MAADR3           (0x02|0x60|0x80)
    #define MAADR2           (0x03|0x60|0x80)
    #define MAADR5           (0x04|0x60|0x80)
    #define MAADR4           (0x05|0x60|0x80)
    #define EBSTSD           (0x06|0x60)
    #define EBSTCON          (0x07|0x60)
    #define EBSTCSL          (0x08|0x60)
    #define EBSTCSH          (0x09|0x60)
    #define MISTAT           (0x0A|0x60|0x80)
    #define EREVID           (0x12|0x60)
    #define ECOCON           (0x15|0x60)
    #define EFLOCON          (0x17|0x60)
    #define EPAUSL           (0x18|0x60)
    #define EPAUSH           (0x19|0x60)
    // PHY registers
    #define PHCON1           0x00
    #define PHSTAT1          0x01
    #define PHHID1           0x02
    #define PHHID2           0x03
    #define PHCON2           0x10
    #define PHSTAT2          0x11
    #define PHIE             0x12
    #define PHIR             0x13
    #define PHLCON           0x14
    
    // ENC28J60 ERXFCON Register Bit Definitions
    #define ERXFCON_UCEN     0x80
    #define ERXFCON_ANDOR    0x40
    #define ERXFCON_CRCEN    0x20
    #define ERXFCON_PMEN     0x10
    #define ERXFCON_MPEN     0x08
    #define ERXFCON_HTEN     0x04
    #define ERXFCON_MCEN     0x02
    #define ERXFCON_BCEN     0x01
    // ENC28J60 EIE Register Bit Definitions
    #define EIE_INTIE        0x80
    #define EIE_PKTIE        0x40
    #define EIE_DMAIE        0x20
    #define EIE_LINKIE       0x10
    #define EIE_TXIE         0x08
    #define EIE_WOLIE        0x04
    #define EIE_TXERIE       0x02
    #define EIE_RXERIE       0x01
    // ENC28J60 EIR Register Bit Definitions
    #define EIR_PKTIF        0x40
    #define EIR_DMAIF        0x20
    #define EIR_LINKIF       0x10
    #define EIR_TXIF         0x08
    #define EIR_WOLIF        0x04
    #define EIR_TXERIF       0x02
    #define EIR_RXERIF       0x01
    // ENC28J60 ESTAT Register Bit Definitions
    #define ESTAT_INT        0x80
    #define ESTAT_LATECOL    0x10
    #define ESTAT_RXBUSY     0x04
    #define ESTAT_TXABRT     0x02
    #define ESTAT_CLKRDY     0x01
    // ENC28J60 ECON2 Register Bit Definitions
    #define ECON2_AUTOINC    0x80
    #define ECON2_PKTDEC     0x40
    #define ECON2_PWRSV      0x20
    #define ECON2_VRPS       0x08
    // ENC28J60 ECON1 Register Bit Definitions
    #define ECON1_TXRST      0x80
    #define ECON1_RXRST      0x40
    #define ECON1_DMAST      0x20
    #define ECON1_CSUMEN     0x10
    #define ECON1_TXRTS      0x08
    #define ECON1_RXEN       0x04
    #define ECON1_BSEL1      0x02
    #define ECON1_BSEL0      0x01
    // ENC28J60 MACON1 Register Bit Definitions
    #define MACON1_LOOPBK    0x10
    #define MACON1_TXPAUS    0x08
    #define MACON1_RXPAUS    0x04
    #define MACON1_PASSALL   0x02
    #define MACON1_MARXEN    0x01
    // ENC28J60 MACON2 Register Bit Definitions
    #define MACON2_MARST     0x80
    #define MACON2_RNDRST    0x40
    #define MACON2_MARXRST   0x08
    #define MACON2_RFUNRST   0x04
    #define MACON2_MATXRST   0x02
    #define MACON2_TFUNRST   0x01
    // ENC28J60 MACON3 Register Bit Definitions
    #define MACON3_PADCFG2   0x80
    #define MACON3_PADCFG1   0x40
    #define MACON3_PADCFG0   0x20
    #define MACON3_TXCRCEN   0x10
    #define MACON3_PHDRLEN   0x08
    #define MACON3_HFRMLEN   0x04
    #define MACON3_FRMLNEN   0x02
    #define MACON3_FULDPX    0x01
    // ENC28J60 MICMD Register Bit Definitions
    #define MICMD_MIISCAN    0x02
    #define MICMD_MIIRD      0x01
    // ENC28J60 MISTAT Register Bit Definitions
    #define MISTAT_NVALID    0x04
    #define MISTAT_SCAN      0x02
    #define MISTAT_BUSY      0x01
    // ENC28J60 PHY PHCON1 Register Bit Definitions
    #define PHCON1_PRST      0x8000
    #define PHCON1_PLOOPBK   0x4000
    #define PHCON1_PPWRSV    0x0800
    #define PHCON1_PDPXMD    0x0100
    // ENC28J60 PHY PHSTAT1 Register Bit Definitions
    #define PHSTAT1_PFDPX    0x1000
    #define PHSTAT1_PHDPX    0x0800
    #define PHSTAT1_LLSTAT   0x0004
    #define PHSTAT1_JBSTAT   0x0002
    // ENC28J60 PHY PHCON2 Register Bit Definitions
    #define PHCON2_FRCLINK   0x4000
    #define PHCON2_TXDIS     0x2000
    #define PHCON2_JABBER    0x0400
    #define PHCON2_HDLDIS    0x0100
    
    // ENC28J60 Packet Control Byte Bit Definitions
    #define PKTCTRL_PHUGEEN  0x08
    #define PKTCTRL_PPADEN   0x04
    #define PKTCTRL_PCRCEN   0x02
    #define PKTCTRL_POVERRIDE 0x01
    
    // SPI operation codes
    #define ENC28J60_READ_CTRL_REG       0x00
    #define ENC28J60_READ_BUF_MEM        0x3A
    #define ENC28J60_WRITE_CTRL_REG      0x40
    #define ENC28J60_WRITE_BUF_MEM       0x7A
    #define ENC28J60_BIT_FIELD_SET       0x80
    #define ENC28J60_BIT_FIELD_CLR       0xA0
    #define ENC28J60_SOFT_RESET          0xFF
    
    // The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata
    // buffer boundaries applied to internal 8K ram
    // the entire available packet buffer space is allocated
    //
    // start with recbuf at 0/
    #define RXSTART_INIT     0x0
    // receive buffer end
    #define RXSTOP_INIT      (0x1FFF-0x0600-1)
    // start TX buffer at 0x1FFF-0x0600, pace for one full ethernet frame (~1500 bytes)
    #define TXSTART_INIT     (0x1FFF-0x0600)
    // stp TX buffer at end of mem
    #define TXSTOP_INIT      0x1FFF
    //
    // max frame length which the conroller will accept:
    #define        MAX_FRAMELEN        1500        // (note: maximum ethernet frame length would be 1518)
    //#define MAX_FRAMELEN     600
    
    
    
    
    
    #define 	ENC28J60_CS_PORT	GPIOA
    #define 	ENC28J60_CS_PIN		GPIO_Pin_4													/* ENC28J60片选线 */
    #define 	ENC28J60_CSL()		\
    	GPIO_ResetBits(ENC28J60_CS_PORT, ENC28J60_CS_PIN)				/* 拉低片选 */
    #define 	ENC28J60_CSH()		\
    	GPIO_SetBits(ENC28J60_CS_PORT, ENC28J60_CS_PIN)				/* 拉高片选 */
    
    void Enc28j60_Init(void);
    unsigned char enc28j60ReadOp(unsigned char op, unsigned char address);
    void 	enc28j60WriteOp(unsigned char op, unsigned char address, unsigned char data);
    void 	enc28j60ReadBuffer(unsigned int len, unsigned char* data);
    void 	enc28j60WriteBuffer(unsigned int len, unsigned char* data);
    void 	enc28j60SetBank(unsigned char address);
    unsigned char enc28j60Read(unsigned char address);
    void 	enc28j60Write(unsigned char address, unsigned char data);
    void 	enc28j60PhyWrite(unsigned char address, unsigned int data);
    void 	enc28j60clkout(unsigned char clk);
    void 	enc28j60Init(unsigned char* macaddr);
    unsigned char enc28j60getrev(void);
    void 	enc28j60PacketSend(unsigned int len, unsigned char* packet);
    unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet);
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265

    更加详细的说明请参考原博客。

    4.新建分组

    打开空STM32项目、新建组lwip-arch、lwip-netif、lwip-core、lwip-core-ipv4。
    lwip-arch添加cc.h、lwipopts.h、perf.h、网卡驱动文件
    lwip-netif添加ethernetif.c、etharp.c、slipif.c
    lwip-core添加core全部文件,lwip-core-ipv4添加core/ipv4全部文件

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

    5.项目头文件路径

    包含LWIP以及LWIP一些子文件夹路径。看图:
    在这里插入图片描述

    6.LWIIP头文件编写

    接下来编写前面提到的三个头文件cc.h、lwipopts.h、perf.h

    首先是perf.h,这个文件很简单

    //perf.h
    #ifndef __PERF_H__
    #define __PERF_H__
    
    #define PERF_START
    #define PERF_STOP(x)
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    下面是cc.h,该文件提供了变量类型声明与调试宏定义

    //cc.h
    #ifndef __CC_H__
    #define __CC_H__
    #include "stdio.h"
    
    typedef unsigned   char    	u8_t;    /* Unsigned 8 bit quantity         */
    typedef signed     char    	s8_t;    /* Signed    8 bit quantity        */
    typedef unsigned   short   	u16_t;   /* Unsigned 16 bit quantity        */
    typedef signed     short   	s16_t;   /* Signed   16 bit quantity        */
    typedef unsigned   int    	u32_t;   /* Unsigned 32 bit quantity        */
    typedef signed     int    	s32_t;   /* Signed   32 bit quantity        */
    typedef unsigned   int 		 	mem_ptr_t;            /* Unsigned 32 bit quantity        */
    typedef unsigned   int 		 	sys_prot_t;
    
    
    /* define compiler specific symbols */
    #define PACK_STRUCT_FIELD(x) x
    #define PACK_STRUCT_STRUCT 
    #define PACK_STRUCT_BEGIN __packed
    #define PACK_STRUCT_END
    
    /*--------------macros--------------------------------------------------------*/
    #define LWIP_DEBUG
    #define LWIP_PLATFORM_DIAG(x) {printf(x);}
    #define LWIP_PLATFORM_ASSERT(x) {printf(x);while(1);}
    #define LWIP_ERROR(message,expression,handler) \
    				do{\
    						if (!(expression)) {printf(message);handler;}\
    				}while(0)
    
    /*---define (sn)printf formatters for these lwip types, for lwip DEBUG/STATS--*/
    #define U16_F "u"
    #define S16_F "d"
    #define X16_F "x"
    #define U32_F "u"
    #define S32_F "d"
    #define X32_F "x"
    
    #define LWIP_PROVIDE_ERRNO
    #define BYTE_ORDER LITTLE_ENDIAN
    extern u32_t sys_now(void);;
    #endif /* __CC_H__ */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    最后是lwipopts.h,用于重定义opt.h中默认的宏。

    #ifndef __LWIPOPTS_H__
    #define __LWIPOPTS_H__
    //不使用RTOS
    #define NO_SYS                  1
    
    //不适用RTOS时,不使用这些API
    #define LWIP_SOCKET             0
    #define LWIP_NETCONN            0
    
    //LWIP的内存大小
    #define MEM_ALIGNMENT           4  
    #define MEM_SIZE                10*1024
    
    //TCP发送缓存与最长报文段长度
    #define TCP_SND_BUF             4000
    #define TCP_MSS                 1000
    
    //调试功能
    #define ETHARP_DEBUG 	LWIP_DBG_ON
    #define ICMP_DEBUG    LWIP_DBG_ON
    
    #endif /* __LWIPOPTS_H__ */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    7.ethernetif.c

    这个文件用于向LWIP封装网卡驱动。在前面的网卡驱动那一节已经实现了网卡的驱动,其中有三个函数:

    void 	enc28j60Init(unsigned char* macaddr);//网卡初始配置
    
    void 	enc28j60PacketSend(unsigned int len, unsigned char* packet);//发送
    
    unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet);//接收
    
    • 1
    • 2
    • 3
    • 4
    • 5

    本文件就是封装这三个函数,向LWIP实现以下几个函数:

    void low_level_init(struct netif *netif)
    err_t low_level_output(struct netif *netif, struct pbuf *p)
    struct pbuf *low_level_input(struct netif *netif)
    void ethernetif_input(struct netif *netif)
    err_t ethernetif_init(struct netif *netif)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    打开LWIP的ethernetif.c文件,发现官方已经为我们用0宏定义写好了这五个函数的框架其中ethernetif_init可以直接使用不修改,其他的要自己实现。
    其他函数的实现我参考了《嵌入式网络那些事——STM32物联实战-朱升林-2015年版》这本书。

    void low_level_init(struct netif *netif)

    static unsigned char MyMacID[6] = {'M','y','L','W','I','P'};
    static void low_level_init(struct netif *netif)
    {
      struct ethernetif *ethernetif = netif->state;
      netif->hwaddr_len = ETHARP_HWADDR_LEN;
    	//自定义网卡MAC地址
      for(int i = 0 ; i < ETHARP_HWADDR_LEN ; i++)
    		netif->hwaddr[i] = MyMacID[i];
    
      netif->mtu = 1500;
    
      netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; 
    	//在原来的框架上,调用网卡寄存器初始化函数
    	enc28j60Init(MyMacID);
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    err_t low_level_output(struct netif *netif, struct pbuf *p)`

    static unsigned char MySendbuf[1500];
    static err_t low_level_output(struct netif *netif, struct pbuf *p)
    {
      struct pbuf *q = NULL;
    	unsigned int templen = 0;
    	for (q = p ; q != NULL ; q = q->next){
    		memcpy(&MySendbuf[templen],q->payload,q->len);
    		templen += q->len;
    		if (templen > 1500 || templen > p->tot_len) return ERR_BUF;
    	}
    	if (templen == p->tot_len){
    		enc28j60PacketSend(templen,MySendbuf);
    		return ERR_OK;
    	}
    	return ERR_BUF;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    struct pbuf *low_level_input(struct netif *netif)

    static unsigned char MyRecvbuf[1500];
    static struct pbuf *low_level_input(struct netif *netif)
    {
      struct pbuf *p=NULL, *q=NULL;
      u16_t len = 0,i = 0;
      len = enc28j60PacketReceive(1500,MyRecvbuf);
    	if (!len) return NULL;
    	p = pbuf_alloc(PBUF_RAW,len,PBUF_RAM);
    	if (!p) return NULL;
    	q = p;
    	while(q != NULL){
    		memcpy(q->payload,&MyRecvbuf[i],q->len);
    		i += q->len;
    		q = q->next;
    		if(i>=len) break;
    	}
    	return p;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    void ethernetif_input(struct netif *netif)

    void ethernetif_input(struct netif *netif)
    {
      struct ethernetif *ethernetif;
      struct eth_hdr *ethhdr;
      struct pbuf *p;
    
      ethernetif = netif->state;
    
      p = low_level_input(netif);
    
      if (p == NULL) return;
    
      ethhdr = p->payload;
    
      switch (htons(ethhdr->type)) {
      case ETHTYPE_IP:
      case ETHTYPE_ARP:
    #if PPPOE_SUPPORT
      /* PPPoE packet? */
      case ETHTYPE_PPPOEDISC:
      case ETHTYPE_PPPOE:
    #endif /* PPPOE_SUPPORT */
        if (netif->input(p, netif)!=ERR_OK)
         { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
           pbuf_free(p);
           p = NULL;
         }
        break;
      default:
        pbuf_free(p);
        p = NULL;
        break;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    8.sys_now

    LWIP需要定时功能(ARP、TCP都有定时需求),而LWIP需要用户实现一个unsigned int sys_now(void)的函数来返回当前运行时间。

    有RTOS基础的都知道RTOS都会有一个计时变量,在各自的RTOS的SysTick中断函数中自增。

    现在我们没有用RTOS,但是也要提供这样的函数。因此我们在中断文件stm32f10x_it.c中,实现这些功能:

    #include "stm32f10x_it.h"
    unsigned int lwip_localtime;
    void SysTick_Handler(void)
    {
    		lwip_localtime++;
    }
    
    unsigned int sys_now(void){
    		return lwip_localtime;
    }
    
    void EXTI1_IRQHandler(){
    	if(EXTI_GetITStatus(EXTI_Line1) != RESET) {
    		EXTI_ClearITPendingBit(EXTI_Line1);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这里为什么会有一个外部中断函数?

    网卡驱动函数里面初始化了网卡的INT引脚,因此必须根据你的引脚连接,实现对应的外部中断函数

    否则测试的时候,一旦接收到数据,触发中断,系统会因为没有重定义外部中断函数而死循环(重要!笔者被弄过好几次)。

    9.初始化函数

    LWIP移植时要实现的函数与要修改的地方就这些。在开始使用LWIP前,就像其他很多东西一样,要进行LWIP初始化。
    写一个函数完成:

    //main.c
    struct netif enc28j60_netif;
    err_t ethernetif_init(struct netif* netif);
    void ethernetif_input(struct netif *netif);
    
    void LWIP_Init(void){
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    	
    	//网卡的GPIO、中断等初始化函数,你也可以放在enc28j60Init最前面
    	Enc28j60_Init();
    	//简单延时
    	uint32_t t = 0x001fffff;while(t--);
    	//开启Systick
    	SysTick_Config(SystemCoreClock/1000);
      //LWIP初始化,设置网关、IP(这里是192.168.1.114)、掩码
    	struct ip_addr ipaddr,netmask,gw;
    	lwip_init();
    	IP4_ADDR(&gw,192,168,1,1);
    	IP4_ADDR(&ipaddr,192,168,1,114);
    	IP4_ADDR(&netmask,255,255,255,0);
    	netif_add(&enc28j60_netif,&ipaddr,&netmask,&gw,NULL,ethernetif_init,ethernet_input);
    	netif_set_default(&enc28j60_netif);
    	netif_set_up(&enc28j60_netif);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    10.主函数

    本文仅实现PING功能,不实现应用层功能。因此这样写:

    int main(void){
    	//串口初始化,看这文章的应该没有不会用printf串口打印的吧(
    	Usart_1_Config();
    	printf("\r\n 这是一个无OS移植LWIP实验 \r\n现在可以ping试试");
    	//调用上面的初始化函数
    	LWIP_Init();
    	//循环调用ethernetif_input与sys_check_timeouts
    	while(1){
    		ethernetif_input(&enc28j60_netif);
    		sys_check_timeouts();
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    至此,代码部分全部编写完成。

    注意

    1.正如前文所述,网卡驱动初始化了中断。一旦收到数据,对应的引脚(看你连接)触发中断。如果不重写中断函数,触发中断时会进入死循环。

    2、直接编译时,KEIL5会报500多个警告(调试相关),因此这样做:
    点击魔术棒 -> C/C++ -> MiscControls,在里面输入下面这一段文字:

    --diag_suppress=1,1295,174,167,111,128,177,550
    
    • 1

    消除很多没用的警告。
    在这里插入图片描述
    3. ENC28J60模块的VCC引脚要接5V,3.3V经过测试无法正常运行。

    测试

    已知前文中,网卡初始化成这样:

    网关   192.168.1.1
    IP地址 192.168.1.114
    掩码   255.255.255.0
    
    • 1
    • 2
    • 3

    控制面板 -> 网络和Internet -> 网络连接 -> 右键以太网 -> 属性
    这样设置:
    以太网IP为192.168.1.x
    掩码为255.255.255.0
    网关192.168.1.1
    (总之和单片机在一个网段)
    在这里插入图片描述

    结果

    ping 192.168.1.114 正常情况可以PING通
    在这里插入图片描述

    源码

    点击获得源码

  • 相关阅读:
    思腾云计算
    AndroidSDK下载及安装(Windows)
    基于粒子群优化的BP神经网络(分类应用) - 附代码
    VS Code搭配code runnner编译时提示:g++: fatal error: no input files解决方法
    「Python 」面向对象三大特性之继承性——(概念、语法、特点、相关术语、注意事项、代码示例)
    机器学习几种常见凸函数的证明
    章鱼网络Louis:发现Web3商业价值
    NVMe开发——PCIe复位
    与图相关的一些算法
    MySQL里的查看操作
  • 原文地址:https://blog.csdn.net/weixin_54435584/article/details/132888808