stm32使用LWIP实现DHCP客户端

发布者:码梦小子最新更新时间:2017-01-12 来源: eefocus关键字:stm32  LWIP  DHCP客户端 手机看文章 扫描二维码
随时随地手机看文章

    LWIP是一款开源的嵌入式网络协议栈,支持的功能很多,而且能在多任务环境下和单任务裸机环境下跑,今天说说他的移植过程,芯片为STM32,网卡为ENC28J60,无操作系统

    首先下载LWIP的源代码,我下载的是1.4.1的源码,下载后解压,文件结构如图

    

将这四个目录中的文件全部拷贝到工程中,API是一些socket通讯的接口,需要在多任务的环境下实现,core里面存放的内核源码,我们主要使用IPV4,include目录下是需要包含的目录,lwip只要求我们包含include目录,里面的内层目录会自动找到,最后建立的工程目录如下

    

好了,此时源码已经做好,还有需要做的,在include目录下新建一个文件夹,必须叫arch,里面存放这几个文件,自己新建

文件的具体内容如下

cc.h

/*

 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.

 * All rights reserved. 

 * 

 * Redistribution and use in source and binary forms, with or without modification, 

 * are permitted provided that the following conditions are met:

 *

 * 1. Redistributions of source code must retain the above copyright notice,

 *    this list of conditions and the following disclaimer.

 * 2. Redistributions in binary form must reproduce the above copyright notice,

 *    this list of conditions and the following disclaimer in the documentation

 *    and/or other materials provided with the distribution.

 * 3. The name of the author may not be used to endorse or promote products

 *    derived from this software without specific prior written permission. 

 *

 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 

 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 

 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 

 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 

 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 

 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 

 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 

 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 

 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 

 * OF SUCH DAMAGE.

 *

 * This file is part of the lwIP TCP/IP stack.

 * 

 * Author: Adam Dunkels

 *

 */

#ifndef __CC_H__

#define __CC_H__


#include "cpu.h"


//编译器无关的数据类型定义

typedef unsigned   char    u8_t;

typedef signed     char    s8_t;

typedef unsigned   short   u16_t;

typedef signed     short   s16_t;

typedef unsigned   long    u32_t;

typedef signed     long    s32_t;

typedef u32_t mem_ptr_t;

typedef int sys_prot_t;


//lwip调试的时候数据类型定义

#define U16_F "hu"

#define S16_F "d"

#define X16_F "hx"

#define U32_F "u"

#define S32_F "d"

#define X32_F "x"

#define SZT_F "uz" 



//根据不同的编译器的符号定义

#if defined (__ICCARM__)


#define PACK_STRUCT_BEGIN

#define PACK_STRUCT_STRUCT 

#define PACK_STRUCT_END

#define PACK_STRUCT_FIELD(x) x

#define PACK_STRUCT_USE_INCLUDES


#elif defined (__CC_ARM)


#define PACK_STRUCT_BEGIN __packed

#define PACK_STRUCT_STRUCT 

#define PACK_STRUCT_END

#define PACK_STRUCT_FIELD(x) x


#elif defined (__GNUC__)


#define PACK_STRUCT_BEGIN

#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))

#define PACK_STRUCT_END

#define PACK_STRUCT_FIELD(x) x


#elif defined (__TASKING__)


#define PACK_STRUCT_BEGIN

#define PACK_STRUCT_STRUCT

#define PACK_STRUCT_END

#define PACK_STRUCT_FIELD(x) x


#endif


#define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0)


#endif /* __CC_H__ */


cpu.h



/*

 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.

 * All rights reserved. 

 * 

 * Redistribution and use in source and binary forms, with or without modification, 

 * are permitted provided that the following conditions are met:

 *

 * 1. Redistributions of source code must retain the above copyright notice,

 *    this list of conditions and the following disclaimer.

 * 2. Redistributions in binary form must reproduce the above copyright notice,

 *    this list of conditions and the following disclaimer in the documentation

 *    and/or other materials provided with the distribution.

 * 3. The name of the author may not be used to endorse or promote products

 *    derived from this software without specific prior written permission. 

 *

 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 

 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 

 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 

 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 

 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 

 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 

 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 

 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 

 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 

 * OF SUCH DAMAGE.

 *

 * This file is part of the lwIP TCP/IP stack.

 * 

 * Author: Adam Dunkels

 *

 */

#ifndef __CPU_H__

#define __CPU_H__


//定义cpu的数据模式,大端小端

#define BYTE_ORDER LITTLE_ENDIAN


#endif /* __CPU_H__ */


perf.h



#ifndef __PERF_H__

#define __PERF_H__


//用于lwip内置的统计功能

//不使能定义为空就可以了

#define PERF_START    /* null definition */

#define PERF_STOP(x)  /* null definition */


#endif /* __PERF_H__ */


sys_arch.h



#ifndef __SYS_RTXC_H__

#define __SYS_RTXC_H__



void init_lwip_timer(void);  //初始化LWIP定时器


u8_t timer_expired(u32_t *last_time,u32_t tmr_interval);    //定时器超时判断


#endif /* __SYS_RTXC_H__ */


sya_arch.c--注意该文件要加入源文件列表中,这是c文件哦



#include "lwip/debug.h"

#include "lwip/def.h"

#include "lwip/sys.h"

#include "lwip/mem.h"

#include "timerx.h"


//初始化LWIP定时器

void init_lwip_timer(void)

{

     TIM6_Int_Init(1000,719);//100Khz计数频率,计数到100为10ms

}


//为LWIP提供计时

extern u32_t lwip_timer;//lwip 计时器,每10ms增加1.


u32_t sys_now(void)

{

    return lwip_timer;

}


//定时器超时判断

//last_time:最近时间

//tmr_interval:定时器溢出周期

u8_t timer_expired(u32_t *last_time,u32_t tmr_interval)

{

    u32_t time;

    time = *last_time;    

    if((lwip_timer-time)>=tmr_interval){

        *last_time = lwip_timer;

        return 1;

    }

    return 0;

}


可以看到我们定义了定时器,那么就要修改相关的定时器文件,文件如下


timerx.c



#include "timerx.h"



u32 lwip_timer=0;//lwip 计时器,每10ms增加1.


//定时器6中断服务程序     

void TIM6_IRQHandler(void)

{     


    if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 

    {

        TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源   

        lwip_timer++;//lwip计时器增加1            

    }     


}

 





//基本定时器6中断初始化

//这里时钟选择为APB1的2倍,而APB1为36M

//arr:自动重装值。

//psc:时钟预分频数

//这里使用的是定时器3!

void TIM6_Int_Init(u16 arr,u16 psc)

{    

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    NVIC_InitTypeDef NVIC_InitStructure;


    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); //时钟使能


    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值     计数到5000为500ms

    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  

    TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

    TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

 

    TIM_ITConfig( TIM6,TIM_IT_Update|TIM_IT_Trigger,ENABLE);//使能定时器6更新触发中断

 

    TIM_Cmd(TIM6, ENABLE);  //使能TIMx外设

     

      NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;  //TIM3中断

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级0级

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //从优先级3级

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能

    NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器                                  

}


timerx.h



#ifndef __TIMER_H_

#define __TIMER_H_


#include "sys.h"





void TIM6_Int_Init(u16 arr,u16 psc);



#endif


好了,这个时候移植就基本完成了,但是编译是过不了的,因为我们缺少一个配置文件.h,这个文件放哪都可以,只要工程包含这个文件的目录,另外,该文件名称不能改动


lwipopts.h



#ifndef __LWIPOPTS_H__

#define __LWIPOPTS_H__




//回环模式

//#define LWIP_HAVE_LOOPIF  1


/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */

#define NO_SYS                          1    //是否使用操作系统

#define LWIP_NETCONN                    0

#define LWIP_SOCKET                     0



#define LWIP_DHCP                       1     //使能DHCP模块

#define MEM_ALIGNMENT                   4      //必须4字节对齐 曾出现在memset的时候hardfault

#define LWIP_DNS                        1




#define MEM_SIZE                        16000        //使用多大一块内存做lwip内存

#define TCP_SND_QUEUELEN                40            //tcp发送序列长度

#define MEMP_NUM_TCP_SEG                TCP_SND_QUEUELEN


#define TCP_MSS                     1460            

#define TCP_WND                     (4 * TCP_MSS)

#define TCP_SND_BUF                 (8 * TCP_MSS)




#define ETHARP_SUPPORT_STATIC_ENTRIES   1




#endif /* __LWIPOPTS_H__ */


可以看到,我们没有使用操作系统并且使能了dhcp功能


这时候就可以编译通过了,但是还有一个必须做的工作,移植网卡的驱动到lwip中,


也就是netif文件夹中的ethernetif.c文件,该文件修改为如下内容



//网卡驱动层文件


#if 1 /* don't build, this is only a skeleton, see previous comment */


#include "lwip/def.h"

#include "lwip/mem.h"

#include "lwip/pbuf.h"

#include

#include

#include "netif/etharp.h"

#include "netif/ppp_oe.h"

#include "enc28j60.h"

#include "netif/ethernetif.h"

#include "string.h"

#include "delay.h"


/* Define those to better describe your network interface. */

#define IFNAME0 'e'

#define IFNAME1 'n'


//MAC地址

const u8 mymac[6]={0x99,0x02,0x35,0x04,0x45,0x61};    //MAC地址

//定义发送接受缓冲区

u8 lwip_buf[1500*2];



//返回网卡地址

struct ethernetif 

{

  struct eth_addr *ethaddr;

  /* Add whatever per-interface state that is needed here. */

};



//网卡的初始化

static err_t low_level_init(struct netif *netif)

{

  //mac地址

  netif->hwaddr_len = ETHARP_HWADDR_LEN;

  /* set MAC hardware address */

  netif->hwaddr[0] = mymac[0];

  netif->hwaddr[1] = mymac[1];

  netif->hwaddr[2] = mymac[2];

  netif->hwaddr[3] = mymac[3];

  netif->hwaddr[4] = mymac[4];

  netif->hwaddr[5] = mymac[5];

    

  //最大传输单元

  netif->mtu = MAX_FRAMELEN; 

  if(ENC28J60_Init((u8*)mymac))    //初始化ENC28J60    

  {

    return ERR_IF;            //底层网络接口错误

  }

  //指示灯状态:0x476 is PHLCON LEDA(绿)=links status, LEDB(红)=receive/transmit

  //PHLCON:PHY 模块LED 控制寄存器        

  ENC28J60_PHY_Write(PHLCON,0x0476);      

    

  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

   

  return ERR_OK;

}


//从指定网卡输出一部分数据

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

{

  struct pbuf *q;

  int send_len=0;

  

#if ETH_PAD_SIZE

  pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */

#endif


  for(q = p; q != NULL; q = q->next) {

    /* Send the data from the pbuf to the interface, one pbuf at a

       time. The size of the data in each pbuf is kept in the ->len

       variable. */

    //send data from(q->payload, q->len);

    memcpy((u8_t*)&lwip_buf[send_len], (u8_t*)q->payload, q->len);

    send_len +=q->len;

  }

   // signal that packet should be sent();

  ENC28J60_Packet_Send(send_len,lwip_buf);



#if ETH_PAD_SIZE

  pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */

#endif

  

  LINK_STATS_INC(link.xmit);


  return ERR_OK;

}



//从指定网卡读取一帧数据回来

static struct pbuf *low_level_input(struct netif *netif)

{

//  struct ethernetif *ethernetif = netif->state;

  struct pbuf *p, *q;

  u16_t len;

  int rev_len=0;

    

  /* Obtain the size of the packet and put it into the "len"

     variable. */

  len = ENC28J60_Packet_Receive(MAX_FRAMELEN,lwip_buf);


#if ETH_PAD_SIZE

  len += ETH_PAD_SIZE; /* allow room for Ethernet padding */

#endif


  /* We allocate a pbuf chain of pbufs from the pool. */

  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

  

  if (p != NULL) {


#if ETH_PAD_SIZE

    pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */

#endif


    /* We iterate over the pbuf chain until we have read the entire

     * packet into the pbuf. */

    for(q = p; q != NULL; q = q->next) {

      /* Read enough bytes to fill this pbuf in the chain. The

       * available data in the pbuf is given by the q->len

       * variable.

       * This does not necessarily have to be a memcpy, you can also preallocate

       * pbufs for a DMA-enabled MAC and after receiving truncate it to the

       * actually received size. In this case, ensure the tot_len member of the

       * pbuf is the sum of the chained pbuf len members.

       */

      //read data into(q->payload, q->len);

        memcpy((u8_t*)q->payload, (u8_t*)&lwip_buf[rev_len],q->len);

        rev_len +=q->len;

        

    }

   // acknowledge that packet has been read();


#if ETH_PAD_SIZE

    pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */

#endif


    LINK_STATS_INC(link.recv);

  } else {

    //drop packet();

    LINK_STATS_INC(link.memerr);

    LINK_STATS_INC(link.drop);

  }


  return p;  

}



/**

 * This function should be called when a packet is ready to be read

 * from the interface. It uses the function low_level_input() that

 * should handle the actual reception of bytes from the network

 * interface. Then the type of the received packet is determined and

 * the appropriate input function is called.

 *

 * @param netif the lwip network interface structure for this ethernetif

 */

void ethernetif_input(struct netif *netif)

{

//  struct ethernetif *ethernetif;

  struct eth_hdr *ethhdr;

  struct pbuf *p;


 // ethernetif = netif->state;


  /* move received packet into a new pbuf */

  p = low_level_input(netif);

  /* no packet could be read, silently ignore this */

  if (p == NULL) return;

  /* points to packet payload, which starts with an Ethernet header */

  ethhdr = p->payload;


  switch (htons(ethhdr->type)) {

  /* IP or ARP packet? */

  case ETHTYPE_IP:

  case ETHTYPE_ARP:

#if PPPOE_SUPPORT

  /* PPPoE packet? */

  case ETHTYPE_PPPOEDISC:

  case ETHTYPE_PPPOE:

#endif /* PPPOE_SUPPORT */

    /* full packet send to tcpip_thread to process */

    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;

  }

}




/**

 * Should be called at the beginning of the program to set up the

 * network interface. It calls the function low_level_init() to do the

 * actual setup of the hardware.

 *

 * This function should be passed as a parameter to netif_add().

 *

 * @param netif the lwip network interface structure for this ethernetif

 * @return ERR_OK if the loopif is initialized

 *         ERR_MEM if private data couldn't be allocated

 *         any other err_t on error

 */

err_t ethernetif_init(struct netif *netif)

{

  struct ethernetif *ethernetif;


  LWIP_ASSERT("netif != NULL", (netif != NULL));

    

  ethernetif = mem_malloc(sizeof(struct ethernetif));

  if (ethernetif == NULL) {

    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));

    return ERR_MEM;

  }


#if LWIP_NETIF_HOSTNAME

  /* Initialize interface hostname */

  netif->hostname = "lwip";

#endif /* LWIP_NETIF_HOSTNAME */


  /*

   * Initialize the snmp variables and counters inside the struct netif.

   * The last argument should be replaced with your link speed, in units

   * of bits per second.

   */

  NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);


  netif->state = ethernetif;

  netif->name[0] = IFNAME0;

  netif->name[1] = IFNAME1;

  /* We directly use etharp_output() here to save a function call.

   * You can instead declare your own function an call etharp_output()

   * from it if you have to do some checks before sending (e.g. if link

   * is available...) */

  netif->output = etharp_output;

  netif->linkoutput = low_level_output;

  

  ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);

  

  /* initialize the hardware */

  return low_level_init(netif);


}


#endif 


主要是三个功能,网卡数据输入,网卡数据输入,网卡初始化,而且我们可以看到,lwip的配置是可以支持多个网卡滴


要是没有网卡驱动的可以看我之前的例程,里面有


这时候我们算是完整完成了lwip的移植,现在到了使用阶段了


这是我们的main函数



int main(void)

{

    NVIC_Group_Init();//系统默认中断分组

    Debug_Serial_Init(115200);

    Delay_Init();

    Led_Init();

    Key_Exti_Init();

    LCD_Init();

    LCD_Clear(LCD_BLACK);

    

    ipaddr.addr = 0;

    netmask.addr = 0;

    gw.addr = 0;

    init_lwip_timer();  //初始化LWIP定时器

    //初始化LWIP协议栈,执行检查用户所有可配置的值,初始化所有的模块

    lwip_init();

    

    //添加网络接口

    while((netif_add(&enc28j60_netif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input)==NULL))

    {

        LCD_ShowString(0,0,240,320,(u8*)"ENC28J60 Init Failed             ",LCD_BLACK);

        Delay_Ms(200);

        LCD_ShowString(0,0,240,320,(u8*)"                                 ",LCD_BLACK);

        Delay_Ms(200);

    }

    LCD_ShowString(0,0,240,320,(u8*)"ENC28J60 Init OK                     ",LCD_BLACK);

    //注册默认的网络接口

    netif_set_default(&enc28j60_netif);

    //建立网络接口用于处理通信

    netif_set_up(&enc28j60_netif); 

    

    dhcp_start(&enc28j60_netif);             //为网卡创建一个新的DHCP客户端

    

    while(1)

    {

        LWIP_Polling();

        Led_Set(0,0);

        Delay_Ms(100);

        Led_Set(0,1);

        Delay_Ms(500);

    }

}


初始化定时器,初始化协议栈,添加网络接口(该函数同时完成网卡的初始化,mac地址在之前的文件中),注册网络接口,建立网络通讯接口,因为是dhcp,所以完全不用管网络的地址是多少,直接给0,后面他会自己获得到的


接下来启动dhcp服务,因为是裸机程序,没有多任务再跑,所以我们必须轮询系统事件并处理,也就是LWIP_Polling(),该函数如下



#define CLOCKTICKS_PER_MS 10    //定义时钟节拍


static ip_addr_t ipaddr, netmask, gw;     //定义IP地址

struct netif enc28j60_netif;              //定义网络接口

u32_t input_time;

u32_t last_arp_time;            

u32_t last_tcp_time;    

u32_t last_ipreass_time;


u32_t last_dhcp_fine_time;            

u32_t last_dhcp_coarse_time;  

u32 dhcp_ip=0;


//LWIP查询

void LWIP_Polling(void)

{

    if(timer_expired(&input_time,5)) //接收包,周期处理函数

    {

        ethernetif_input(&enc28j60_netif); 

    }

    if(timer_expired(&last_tcp_time,TCP_TMR_INTERVAL/CLOCKTICKS_PER_MS))//TCP处理定时器处理函数

    {

        tcp_tmr();

    }

    if(timer_expired(&last_arp_time,ARP_TMR_INTERVAL/CLOCKTICKS_PER_MS))//ARP处理定时器

    {

        etharp_tmr();

    }

    if(timer_expired(&last_ipreass_time,IP_TMR_INTERVAL/CLOCKTICKS_PER_MS))//IP重新组装定时器

    { 

        ip_reass_tmr();

    }                                   

    if(timer_expired(&last_dhcp_fine_time,DHCP_FINE_TIMER_MSECS/CLOCKTICKS_PER_MS))//dhcp服务

    {

        dhcp_fine_tmr();

    }

    if(timer_expired(&last_dhcp_coarse_time,DHCP_COARSE_TIMER_MSECS/CLOCKTICKS_PER_MS))//dhcp服务

    {

        dhcp_coarse_tmr();

    }  

}


在循环中处理好相应的事件,因为我们没建立tcp链接所以tcp其实可以不要,另外dhcp服务使用的是udp,端口使用的是udp的57,58


到这里编译连接,系统启动就能查看到路由器自动分配的IP了




具体工程在下面下载


http://download.csdn.net/detail/dengrengong/8548851


关键字:stm32  LWIP  DHCP客户端 引用地址:stm32使用LWIP实现DHCP客户端

上一篇:STM32音乐播放器,文件查找的实现
下一篇:stm32-ucos移植lwip-1(raw)

推荐阅读最新更新时间:2024-03-16 15:30

怎么使用ST的库开发STM32
我使用的芯片是 STM32F103VET 和编译器是 IAR ARM V5.5,调试器用 JLINK V8 1,下载ST的库,现在的版本是 STM32F10x_StdPeriph_Lib_V3.3.0,解压缩,然后将 Libraries整个拷贝到你的工作目录,Project 目录是很多演示代码,可以参考怎么怎么编程,很容易的,readme里面都有说明。 2,建立一个 IAR 的project 选择 C 的空项目,为了方便管理我分了多个文件夹,分别是 建立一个 CMSIS目录 放内核启动代码添加两个系统启动文件,分别是在 STM32F10x_StdPeriph_Lib_V3.3.0\Libraries\CMSIS\CM3\Devi
[单片机]
基于STM32单片机的DLP驱动电路的研究
DLP投影技术是应用美国德州仪器公司开发的数字微镜元件——DMD(Digital Micromirror Device)作为主要关键处理元件以实现数字光学处理过程的技术。DLP显示的色彩清晰度高、艳丽、细腻、逼真,且为全数字显示即可靠性极高,能在各类产品(如大屏幕数字电视、公司/家庭/专业会议投影机和数码相机(DLP Cinema))中提供最佳图像效果。目前,大部分的家用或商用DLP投影机都采用了单片结构,使得其便于移动携带,因而得到越来越广泛的应用。在目前应用发展的基础上,又对其结构的精简性、携带的方便性提出了更高的要求。传统的DLP投影仪是通过DVI接口接收外部信号,并且经过信号转换传送给DLP控制器来控制DLP的显示,占
[单片机]
基于<font color='red'>STM32</font>单片机的DLP驱动电路的研究
STM32之Systick定时器解析
在ARM Cortex-M3内核中有一个Systick定时器,它是一个24位的倒计数定时器,当计数到0时,它就会从Load寄存器中自动重装定时初值,只要不把CTRL寄存器中的ENABLE清0,它就永不停。对于滴答定时器的理解主要分为下面几项: 1.滴答定时器的时钟来源 看上面的图会有一个错觉,以为滴答定时器是系统时钟的1/8,其实不是,滴答定时器的时钟既可以是HCLK/8,也可以是HCLK,这个是通过CTRL寄存器进行设定的,了解这一点,对于操作系统的时钟计算很重要,因为要精确计算时钟时间。 2.滴答定时器的寄存器 从这里就能看出,时钟源有两种选择 3.滴答定时器的库函数 (1)寄存器定义在哪?————在core
[单片机]
STM32的can现场总线的特点及工作流程分析
最近在搞stm32实验板的can现场总线实验,之前只是搞过STC51的串口通信,相比之下,发觉can总线都挺复杂的。开始时,知道自己是新手,只知道can总线跟串行通信,485通信,I2C通信一样都是用来传输数据通信的,对其工作原理一窍不通,还是从基础开始看书看资料,先了解它的基本原理吧。 原来can总线有以下特点: 主要特点 支持CAN协议2.0A和2.0B主动模式 波特率最高可达1兆位/秒 支持时间触发通信功能 发送 3个发送邮箱 发送报文的优先级特性可软件配置 记录发送SOF时刻的时间戳 接收 3级深度的2个接收FIFO 14个位宽可变的过滤器组-由整个CAN共享 标识符列表 FIFO溢出处理方式可配置 记录接
[单片机]
<font color='red'>STM32</font>的can现场总线的特点及工作流程分析
STM32系统芯片,加快LoRa IoT智能设备开发
单片集成STM32微控制器 IP和增强版Semtech射频模块 支持LoRa®等全球低功耗广域网接入 意法半导体工业产品10年生命周期滚动保证 通过智能基础设施及物流、智能工业和智能生活促进世界可持续发展,横跨多重电子应用领域的全球领先的半导体供应商意法半导体(STMicroelectronics,简称ST; 纽约证券交易所代码:STM)展示了全球首款通过长距离无线技术将智能设备连接到物联网(IoT)的LoRa®系统芯片(SoC)。 STM32WLE5 系统芯片使产品开发人员能够创建远程环境传感器、仪表、跟踪器和过程控制器等设备,帮助企业有效地管理能源和资源的使用情况。 该系统芯片在一个易于使用的单片产品内整合
[单片机]
<font color='red'>STM32</font>系统芯片,加快LoRa IoT智能设备开发
STM32单片机学习总结之------位操作
储备知识: 与(&)运算 0&0=0,0&1=0,1&0=0,1&1=1 非(~)运算 在二进制中1变0,0变1 或(|)运算 0|0=0,0|1=1,1|0=1,1|1=1 异或(^)运算,同为假,异为真 0^0=0 0^1=1 1^0=1 1^1=0 学习内容: 库函数的实现涉及到不少位操作,下面为几个常用的位操作方法,可以排除阅读代码的障碍。 char型只占一个字节,取值范围为-128 ~ +127 1、将char型变量a的第七位(bit6)清0,其它位不变。 2、同理,将变量a的第七位(bit6)置1,其它位不变的方法如下 3、将变量a的第七位(bit6)取反,其它位不变。 学习总结: 以上
[单片机]
<font color='red'>STM32</font>单片机学习总结之------位操作
STM32开发笔记66: 移植看门狗驱动程序到STM32L053R8T6
单片机型号:STM32L053R8T6 在文章STM32开发笔记45:看门狗驱动程序的移植已经介绍了看门狗驱动程序的移植方法,其实现的是将STM32CubeMX中的驱动程序,移植到自己已有的工程中。本文将介绍将STM32F070F6P6上的看门狗驱动程序移植到STM32L053R8T6的方法,步骤如下: 1、在1_bsp层引入STM32官方的驱动程序stm32l0xx_hal_iwdg.c,并修改stm32l0xx_hal_conf.h中的内容,使能内部看门狗,相关程序如下,然后进行编译,形成bsp.lib #define HAL_MODULE_ENABLED /*#define HAL_ADC_MODULE_ENA
[单片机]
意法STM32系列获ARM RealView微控制器开发工具包支持
ARM公司日前宣布RealView微控制器开发工具包将支持意法半导体基于ARM Cortex-M3处理器的全新STM32F1xx系列器件。 STM32F101(接入行)和STM32F103(性能行)将是意法半导体首个基于ARM Cortex-M3处理器的器件系列,兼具卓越的高性能和低功耗。该系列器件拥有高达72MHz的CPU时钟速度、128Kbyte片上闪存ROM及20Kbyte片上RAM,还包括A/D、CAN、USB、SPI、I2C等众多外设及多达80个GPIO。 RealView微控制器开发工具包3.1可为新器件提供支持。这一最新版本保留了Keil Vision 3集成开发环境(IDE)易于使用的特性,并增加了针对STM3
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved