STM32移植lwip之官方源码解析

2023-11-18

本篇目标:分析stm32的ETH(MAC控制器)初始化及lwip是如何与stm32底层连接的

材料准备:


ETH初始化

按照程序的思路,从main函数开始看:

int main(void)
{
  /* 配置中断优先级为4位,16个抢占级优先级,基于STM32库函数 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

  /* configure ethernet (GPIOs, clocks, MAC, DMA) */
  /* 配置有关以太网接口的控制器(包括IO口,时钟,MAC,DMA)
  基于STM32_ETH库函数 */
  ETH_BSP_Config();

  /* Initilaize the LwIP stack */
  /* lwip初始化函数,基于lwip协议栈 */
  LwIP_Init();

  /* Infinite loop */
  /* 主循环 */
  while (1)
  {  
    /* check if any packet received */
    /* 判断是否接收到数据,基于STM32_ETH库函数 */
    if (ETH_CheckFrameReceived())
    { 
      /* process received ethernet packet*/
      /* 接受缓存的数据,发送到lwip层进行处理,基于lwip协议栈 */
      LwIP_Pkt_Handle();
    }
    /* handle periodic timers for LwIP*/
    /* 轮询函数,定期询问lwip的一些状态 */
    LwIP_Periodic_Handle(LocalTime);
  } 
}

函数总结:main函数没有太多的注释,主要就是包含了很多内部函数的调用,包括stm32寄存器的初始化,lwip协议栈的初始化,以及循环while(1)中对接受数据的判断等。


进入 ETH_BSP_Config() 函数:

void ETH_BSP_Config(void)
{
  RCC_ClocksTypeDef RCC_Clocks;

  /***************************************************************************
    NOTE: 
         When using Systick to manage the delay in Ethernet driver, the Systick
         must be configured before Ethernet initialization and, the interrupt 
         priority should be the highest one.
    摘要 :
         当使用系统滴答定时器时钟来驱动以太网设备,系统滴答定时器时钟必须在以太网初始化之前配置,而且中断优先级必须是最高的。
      *****************************************************************************/

  /* Configure Systick clock source as HCLK */
  /* 配置滴答定时器时钟源为HCLK,因为之前没有对时钟进行任何配置,所以  
  频率为内部高速时钟HSI为8MHz */
  SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);

  /* SystTick configuration: an interrupt every 10ms */
  /* 读取时钟信息 */
  RCC_GetClocksFreq(&RCC_Clocks);
  /* 计算滴答定时器计数值以配置定时器,计数值/HCLK = 10/1000,即计数
  值 = HCLK/100 ,滴答定时器中断时间为10ms*/
  SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);

  /* Set Systick interrupt priority to 0*/
  /* 配置滴答定时器中断优先级为抢占优先级0 */
  NVIC_SetPriority (SysTick_IRQn, 0);

  /* Configure the GPIO ports for ethernet pins */
  /* 以太网PHY相关引脚配置 */
  ETH_GPIO_Config();

  /* Configure the Ethernet MAC/DMA */
  /* 配置以太网MAC和DMA相关寄存器 */
  ETH_MACDMA_Config();

  /* Get Ethernet link status*/iwang
  /* 获取以太网芯片的连接状态 
   * EthStatus变量要获取两个状态,一个是连接状态 ETH_LINK_FLAG,另
   * 一个是以太网初始化成功状态 ETH_INIT_FLAG ;必须在这两个状态都
   * 完成后,才能使lwip正常工作
   */
  if(ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_SR) & 1)
  {
    EthStatus |= ETH_LINK_FLAG;
  }

  /* Configure the PHY to generate an interrupt on change of link status */
  /* 配置PHY在改变连接状态时,产生中断 */
  Eth_Link_PHYITConfig(DP83848_PHY_ADDRESS);

  /* Configure the EXTI for Ethernet link status. */
  /* 配置以太网连接的退出 */
  Eth_Link_EXTIConfig(); 
}

上面都是一些对函数基本功能的注释,这里挑出重要的函数来做一个总结:

  1. ETH_GPIO_Config() 函数:连接PHY芯片的相关GPIO口初始化,STM32F4系列GPIO口初始化与STM32F1系列有点区别,需要注意一下,初始化顺序包括
    (1)使能相关外设时钟,包括GPIOA、GPIOB、GPIOC、SYSCFG;
    (2)MCO引脚输出时钟并配置接口为RMII模式;
    (3)复用相关GPIO口;
  2. ETH_MACDMA_Config() 函数:函数中主要就是对结构体变量
    ETH_InitStructure的值进行初始化,其中包括相关MAC和DMA的参数;最主要的还是最后一个函数 ETH_Init() ,这个函数中对PHY芯片进行了所有需要的配置,包括全双工通讯和100M网卡等等。
  3. PHY物理地址确定:在官方代码中PHY的物理地址有一个宏定义为
    #define DP83848_PHY_ADDRESS 0x00
    这个物理地址怎么确定,网上关于lan9303的物理地址讲解的比较少,而dp83848比较多,可以度娘,因为硬件搭建的是lan9303,所以这里就分析一下lan9303的物理地址:
    在lan9303数据手册里面找到关于 PHYADDR_LED5P 引脚的说明,这个引脚确定初始的物理地址;当reset之后,这个引脚为低电平则虚拟PHY物理地址为0,反之则为1,由于硬件搭建的是下拉,所以是低电平,则把物理地址的宏定义改成了0x00。(关于 PHYADDR_LED5P 引脚,数据手册还有很多的介绍,慢慢挖掘吧)

上面也就是简单浅层次提了ETH的初始化以及对PHY芯片的配置过程,现在来到lwip协议栈层,来挖掘lwip是怎么与STM32低层数据进行传递处理的。

lwip的底层连接

就像之前刚刚拿到lwip程序的自己,不能一下子就找到重要的底层函数,那就从main函数的 while 循环开始,循环肯定做了对数据的查询和处理。

 while (1)
  {  
    /* 看到下面这个函数,就有点头绪了,从函数名字分析可以确定,这个函
        数是用来判断是否有接收到数据,进入函数可以发现里面就有很多DMA
        接收结构体的处理 */
    if (ETH_CheckFrameReceived())
    { 
      /* 下面的这个函数肯定包含了对数据的接收 */
      LwIP_Pkt_Handle();
    }
    LwIP_Periodic_Handle(LocalTime);
  } 

进入LwIP_Pkt_Handle()函数:发现只调用了一个函数ethernetif_input();

err_t ethernetif_input(struct netif *netif)
{
  err_t err;
  struct pbuf *p;

  /* move received packet into a new pbuf */
  /* 将接收的数据存到pbuf结构体中 */
  p = low_level_input(netif);

  /* no packet could be read, silently ignore this */
  /* 若接收的数据为空,即没有数据,那就直接返回,扔掉数据 */
  if (p == NULL) return ERR_MEM;

  /* entry point to the LwIP stack */
  /* LwIP 协议栈入口点,即将接收到的数据放入lwip,lwip会对数据进行一
  系列的处理,所以称为入口点 */
  err = netif->input(p, netif);

  if (err != ERR_OK)
  {
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
    pbuf_free(p);
  }
  return err;
}

上面还是没有看到最底层的数据接收,那就再进入low_level_input()函数:

static struct pbuf * low_level_input(struct netif *netif)
{
  struct pbuf *p, *q;
  uint32_t len;
  FrameTypeDef frame;
  u8 *buffer;
  __IO ETH_DMADESCTypeDef *DMARxDesc;
  uint32_t bufferoffset = 0;
  uint32_t payloadoffset = 0;
  uint32_t byteslefttocopy = 0;
  uint32_t i=0;  

  /* get received frame */
  /* 获取接收数据,进入这个函数就可以发现数据是在这个函数里面接收的 */
  frame = ETH_Get_Received_Frame();

  len = frame.length;
  buffer = (u8 *)frame.buffer;

  /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
  /* 分配内存 */
  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

  if (p != NULL)
  {
    DMARxDesc = frame.descriptor;
    bufferoffset = 0;
    for(q = p; q != NULL; q = q->next)
    {
      byteslefttocopy = q->len;
      payloadoffset = 0;

      /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
      /* 比较接收数据的长度, 若大于buffer的长度,则进行分批依次处理
      byteslefttocopy为剩余数据的长度
      bufferoffset为上一组最后一个比buffer长度短的数据的长度 */
      while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
      {
        /* Copy data to pbuf*/
        /* 拷贝buffer指向的内存地址(代表为接收数据存放的地址)到q指
        向的内存地址 */
        memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));

        /* Point to next descriptor */
        /* 指向下一个数据 */
        DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
        buffer = (unsigned char *)(DMARxDesc->Buffer1Addr);

        byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
        payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
        bufferoffset = 0;
      }
      /* Copy remaining data in pbuf */
      /* 最后一个小于buffer长度的数据拷贝 */
      memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy);
      bufferoffset = bufferoffset + byteslefttocopy;
    }
  }

  /* 下面就是清除标志位,重新启动DMA的一些配置,与串口相似 */
  /* Release descriptors to DMA */
  DMARxDesc =frame.descriptor;

  /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
  for (i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++)
  {  
    DMARxDesc->Status = ETH_DMARxDesc_OWN;
    DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
  }

  /* Clear Segment_Count */
  DMA_RX_FRAME_infos->Seg_Count =0;

  /* When Rx Buffer unavailable flag is set: clear it and resume reception */
  if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)  
  {
    /* Clear RBUS ETHERNET DMA flag */
    ETH->DMASR = ETH_DMASR_RBUS;
    /* Resume DMA reception */
    ETH->DMARPDR = 0;
  }
  return p;
}

接收的过程过程已经略知12了,那找找发送的:直接找到 ethernetif.c 文件中的 low_level_output 函数;

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
  /* ···省略一段不重要的程序 */

  /* copy frame from pbufs to driver buffers */
  /* 拷贝数据到发送的buffer */
  for(q = p; q != NULL; q = q->next)
    {
      /* Is this buffer available? If not, goto error */
      /* buffer是非准备好可用,如果没有,那就会报错 */
      if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
      {
        errval = ERR_BUF;
        goto error;
      }

      /* Get bytes in current lwIP buffer */
      /* 获得传送数据的长度 */
      byteslefttocopy = q->len;
      payloadoffset = 0;

      /* Check if the length of data to copy is bigger than Tx buffer size*/
      /* 比较发送数据是否比发送buffer长度大,是则需要进行分批处理 */
      while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
      {
        /* Copy data to Tx buffer*/
        /* 拷贝数据到发送buffer */
        memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );

        /* Point to next descriptor */
        /* 指向下一个数据 */
        DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);

        /* Check if the buffer is available */
        /* 重新确认buffer可用 */
        if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
        {
          errval = ERR_USE;
          goto error;
        }

        buffer = (u8 *)(DmaTxDesc->Buffer1Addr);

        byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
        payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
        framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
        bufferoffset = 0;
      }

      /* Copy the remaining bytes */
      /* 拷贝剩下的最后一个数据 */
      memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), byteslefttocopy );
      bufferoffset = bufferoffset + byteslefttocopy;
      framelength = framelength + byteslefttocopy;
    }

  ETH_Prepare_Transmit_Descriptors(framelength);

  errval = ERR_OK;

  /* ···省略一段不重要的程序 */
}

可以看出发送函数 low_level_output() 和接收 low_level_input() 相辅相成,作为与stm32低层连接的两个重要的函数,那剩下还有两个次要的函数,找到 ethernetif.c 中的 ethernetif_init() 与 low_level_init():

err_t ethernetif_init(struct netif *netif)
{
  LWIP_ASSERT("netif != NULL", (netif != NULL));

  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;

  /* 网络结构体netif指向了两个函数,而两个函数最终调用的都是发送函数
  low_level_output() ,所以在lwip协议栈里面处理完数据后,会通过netif
  网络结构体进行调用发送函数*/
  netif->output = etharp_output;
  netif->linkoutput = low_level_output;

  /* initialize the hardware */
  /* 初始化硬件 */
  low_level_init(netif);

  return ERR_OK;
}
static void low_level_init(struct netif *netif)
{
#ifdef CHECKSUM_BY_HARDWARE
  int i; 
#endif
  /* set MAC hardware address length */
  /* 配置MAC物理地址长度 */
  netif->hwaddr_len = ETHARP_HWADDR_LEN;

  /* set MAC hardware address */
  /* 配置MAC物理地址 */
  netif->hwaddr[0] =  MAC_ADDR0;
  netif->hwaddr[1] =  MAC_ADDR1;
  netif->hwaddr[2] =  MAC_ADDR2;
  netif->hwaddr[3] =  MAC_ADDR3;
  netif->hwaddr[4] =  MAC_ADDR4;
  netif->hwaddr[5] =  MAC_ADDR5;

  /* initialize MAC address in ethernet MAC */ 
  /* 将MAC地址写入MAC控制器 */
  ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); 

  /* maximum transfer unit */
  /* 最大传输单元 */
  netif->mtu = 1500;

  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  /* 器件功能配置,如果不是只有一个以太网器件,不要配置成 
  NETIF_FLAG_ETHARP ,这里将器件配置开启广播功能和使用ARP */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;

  /* Initialize Tx Descriptors list: Chain Mode */
  /* 配置Tx为连续模式 */
  ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
  /* Initialize Rx Descriptors list: Chain Mode  */
  /* 配置Rx为连续模式 */
  ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);

#ifdef CHECKSUM_BY_HARDWARE
  /* Enable the TCP, UDP and ICMP checksum insertion for the Tx frames */
  /* 使能TCP/UDP/ICMP校验 */
  for(i=0; i<ETH_TXBUFNB; i++)
    {
      ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
    }
#endif

   /* Note: TCP, UDP, ICMP checksum checking for received frame are enabled in DMA config */

  /* Enable MAC and DMA transmission and reception */
  /* 开启MAC转换和DMA接收 */
  ETH_Start();

}

小结:上面整理了这么多,看起来也有点累,用一张图来捋一捋

这里写图片描述


ps:代码的解析远远比移植困难的多,花了几天时间看了很多网上大神关于移植lwip和lwip源码的解析,然后就照着自己的理解粗略的描述了一些自己认为比较重要的几个点,当然肯定见解不周,希望自己以后学的更深刻的时候再一点一点修改不当的地方,共勉~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32移植lwip之官方源码解析 的相关文章

  • 处理器指令周期执行时间

    我的猜测是 no operation 内在 ARM 指令应花费 1 168 MHz 来执行 前提是每个NOP在一个时钟周期内执行 我想通过文档验证这一点 有关处理器指令周期执行时间的信息是否有标准位置 我试图确定 STM32f407IGh6
  • 初始化 ST-Link 设备时出错 - 无法连接到设备

    我目前正在使用 ST Link 调试器对我的 STM32F3 Discovery 板进行编程 我使用的IDE是Atollic TrueStudio 5 5 2 现在我面临一个非常奇怪的问题 那就是我不断收到消息 初始化 ST Link 设备
  • 如何让printf在STM32F103上工作?

    我是 STM32F103 世界的新手 我有一个STM32F103的演示代码 我正在使用arm none eabi来编译它 我尝试了在谷歌上可以找到的内容 但到目前为止没有任何效果 我已经花了三天时间来解决这个问题 任何人都可以给我一个运行良
  • 138-基于stm32单片机汽车多功能仪表盘显示系统Proteus仿真+源程序

    资料编号 138 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 ds1302时钟 LED灯 蜂鸣器 电位器 制作一个基于stm32单片机汽车多功能仪表盘显示系统Proteus仿真 2 通过DHT1
  • Push_back() 导致程序在进入 main() 之前停止

    我正在为我的 STM32F3 Discovery 板使用 C 进行开发 并使用 std deque 作为队列 在尝试调试我的代码 直接在带有 ST link 的设备上或在模拟器中 后 代码最终在 main 中输入我的代码之前在断点处停止 然
  • STM32F4 通过软复位跳转到引导加载程序,无需 BOOT0 和 BOOT1 引脚

    我问这个问题是因为可以在这里找到类似问题的答案 通过应用程序跳转到 STM32 中的引导加载程序 即从用户闪存在引导模式下使用引导 0 和引导 1 引脚 用户 JF002 JF002回答 当我想跳转到引导加载程序时 我在其中一个备份寄存器中
  • 匹配 STM32F0 和 zlib 中的 CRC32

    我正在研究运行 Linux 的计算机和 STM32F0 之间的通信链路 我想对我的数据包使用某种错误检测 并且由于 STM32F0 有 CRC32 硬件 并且我在 Linux 上有带有 CRC32 的 zlib 所以我认为在我的项目中使用
  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • STM32超声波——HC_SR04

    文章目录 一 超声波图片 二 时序图 三 超声波流程 四 单位换算 五 取余计算 六 换算距离 七 超声波代码 一 超声波图片 测量距离 2cm 400cm 二 时序图 1 以下时序图要先提供一个至少10us的脉冲触发信号 告诉单片机我准备
  • 在 Atollic TrueStudio、STM32CubeMX 中导入 C 库

    我目前正在开发 STM32F767ZI Nucleo 板和一个小安全芯片 microchip atecc508a 通过 i2c 连接进行连接 该芯片有一个可用的库加密验证库 https github com MicrochipTech cr
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • STM32 暂停调试器时冻结外设

    当到达断点或用户暂停代码执行时 调试器可以停止 Cortex 中代码的执行 但是 当皮质停止在暂停状态下执行代码时 调试器是否会冻结其他外设 例如 DMA UART 和定时器 您只能保留时间 r 取决于外围设备 我在进入主函数时调用以下代码
  • STM32H5 Nucleo-144 board开箱

    文章目录 开发板资料下载 目标 点亮LD1 绿 LD2 黄 和LD3 红 三个LED灯 开箱过程 博主使用的是STM32CubeMX配置生成代码 具体操作如下 打开STM32CubeMX File gt New project 选择开发板型
  • STM32 上的位置无关代码 - 指针

    我已成功在 STM32 上构建并运行位置无关的代码 向量表和 GOT 已修补 一切正常 但我对这样的代码有问题 double myAdd double x return x 0 1 double ptrmyAdd double myAdd
  • STM32内部时钟

    我对 STM32F7 设备 意法半导体的 Cortex M7 微控制器 上的时钟系统感到困惑 参考手册没有充分阐明这些时钟之间的差异 SYSCLK HCLK FCLK 参考手册中阅读章节 gt RCC 为 Cortex 系统定时器 SysT
  • 在 Contiki 程序中使用 malloc

    考虑以下 Contiki 程序 include
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能
  • GCC 变量映射和 MISRA-C

    我主要知道两种使用 GCC 声明内存映射寄存器的方法 有许多变体 使用双字段 每个外设的数据结构等 要么使用初始化为正确地址的指针 例如volatile uint32 t pMyRegister uint32 t 0xDEADBEEFUL
  • 读取STM32 MCU SPI数据寄存器的值

    有很多类似的问题 但似乎没有一个问题完全相同 我正在将 STML4 MCU 连接到 6 轴传感器 LSM6DS3 我已经成功地在 I2C 中实现了所有内容 但想要 SPI 的额外速度 和 DMA 如果我能让这些第一步工作起来的话 因此 第一
  • STM32F4 定时器 - 计算周期和预分频,以生成 1 ms 延迟

    我在用STM32F407VGT6 with CubeMX 因此 我从通用定时器开始 但我被预分频值和周期值所困扰 基本上我想每隔一段时间生成一个定时器中断n 其中 n 1 2 3 ms 并执行一些任务 计算周期和预分频值的公式有很多变化 公

随机推荐

  • jquery-ui sortable详解

    该插件的用途 使用鼠标重新排列列表或网格中的元素 helper 这个小伙子 总结的不错 Note In order to sort table rows the tbody must be made sortable not the tab
  • Log4j2自定义插件实现自定义日志打印功能(脱敏/加密)

    文章目录 1 自定义appender插件 2 log4j2 xml配置 3 参数说明 1 定义标签 Plugin 2 定义标签参数或子元素 PluginFactory 4 获取容器中对象 5 Mybatis 设置日志打印实现为Log4j2I
  • RuoYi-spring-boot修改项目名称

    前言 若依是个不错的框架 想用若依框架来做项目 需要修改下项目名称为自定义的名称 比如school 1 找到硬盘对应的位置把名称改成我自定义的名称school 同时把路径也换成school 2 修改所有pom xml文件 1 总pom xm
  • 2.3.3 Servlet, 生命周期, 体系结构, xml配置, 请求和响应对象, 转发与重定向, ServletContext共享数据

    目录 一 Servlet概述 二 Servlet快速入门 2 1 案例需求 编写一个普通的java类 通过浏览器可以访问 2 2 servlet执行原理 三 Servlet生命周期 3 1 生命周期相关 3 1 1 思想介绍 3 1 2 代
  • osgEarth的Rex引擎原理分析(一一六)地理数据坐标系分类

    目标 一一五 中的问题203 地理数据的坐标系一般有两大类 一是地理坐标系 GCS 是经纬度单位的椭球坐标系 二是投影坐标系 PCS 是平面直角坐标系 投影坐标系 PCS 的定义一般会包含两方面的定义信息 1 基准面 Datum 与GCS相
  • 各种系统框架图简介(转载)

    原文出处 http space itpub net 6517 viewspace 609654 1 Spring 架构图 Spring 是一个开源 框架 是为了解决企业 应用程序开发复杂性而创建的 框架的主要优势之一就是其分层架构 分层架构
  • dubbo整合nacos没有注册成功

    这里大家整合的时候一定要注意dubbo3 0的版本 nacos整合的话要是2 0以上的版本 不然就会出现服务注册不上的情况 下面是nacos的下载地址 推荐大家使用这一个 还是比较主流的 Nacos 快速开始
  • 红帽6虚拟机安装流程

    红帽虚拟机的安装流程 redhat6 本篇文章将会带大家完整的安装linux红帽虚拟机 傻瓜式配置红帽虚拟机 超详细安装流程 1 下载好所需要的镜像文件 并且放在一个固定的文件夹中 如果没有iso文件 评论一下 发给你 2 打开虚拟机创建新
  • Typora软件的安装以及Typora中相应主题的拷贝和安装(网页下载到安装目录,安装Typora必看)

    1 首先 进入Typora官方中文网站 Typora官方中文网站 https typoraio cn 2 下载相应版本的Typora软件 直接进行安装即可 3 进入Typora软件 打开 主题 可以看到基本的主题 4 下载更多好看的Typo
  • MongoDB的安装与基本使用

    首先 得从MongoDB官网下载到对应操作系统的数据库安装包 MongoDB官网 https www mongodb com MongoDB下载地址 https www mongodb com download center communi
  • python中idx函数_python 常用函数、内置函数清单

    文章内容摘自 http www cnblogs com vamei 1 type 查询变量的类型 例 gt gt gt a 10 gt gt gt print a 10 gt gt gt print type a 2 dir 查询一个类或者
  • maven本地仓库配置

    来配置一下maven本地仓库 第一步 下载到官网下载maven包 下载地址 http maven apache org download cgi 第二步 找个盘符创建个文件夹将将下载的maven报放进去 然后将下载的包解压了 第三步 配置M
  • 树莓派3B+使用镜像烧录安装系统与配置教程(入门向)

    设备 Raspberry 3B 开发板一块 显示屏 台式计算机或笔记本电脑 显示屏 HDMI转VGA线或HDMI线或USB视频采集卡 千万不要用HDMI线从树莓派直接连接到显卡的HDMI口或者笔记本的HDMI口 轻则平安无事 重则电脑CPU
  • React移动端项目-02

    一些不实装功能的静态页面 底部导航栏 问答 页面 目标 实现问答页面的静态结构和样式 操作步骤 将资源包的样式拷贝到 pages Question 目录下 然后在该目录下的 index js中编写组件代码 import NavBar fro
  • UVA 1601 The Morning after Halloween - Japan 2007

    include
  • 创建任意程序为系统服务

    网上流传的创建系统服务的方法我看着真麻烦 把我自己弄的很简单的代码共享下 create SERVICENAME bat ECHO OFF sc delete SERVICENAME sc create SERVICENAME start a
  • 51单片机按键识别与LED显示(显示0-9的数字)

    实验内容 单片机外接10个按键 编号为0 9 编程实现任意按键则LED显示对应数字 一 硬件电路原理图 1 共阴共阳两种不同的方式 2 数码管显示表 3 矩阵键盘介绍 矩阵键盘是 单片机 外部设备中所使用的排布类似于矩阵的键盘组 矩阵式结构
  • WIN10+VS2013+CUDA10安装方法

    1 先安装VS 先安装VS 先安装VS 安装CUDA会配置VS文件 反向的话VS中找不到文件 VS安装参考 https blog csdn net m0 37477061 article details 83447773 2 安装CUDA
  • E-R模型应用示例

    E R模型应用示例 例1 1 设有某计算机系统集成制造公司需要建立一个零配件物资管理系统 该公司组装不同型号计算机所用的零配件由不同供货商供给 存放在多个仓库中 由多名仓库管理员管理 试用E R模型对该公司的零配件管理工作进行分析 根据该公
  • STM32移植lwip之官方源码解析

    本篇目标 分析stm32的ETH MAC控制器 初始化及lwip是如何与stm32底层连接的 材料准备 官方资料 包含代码和移植手册 stm32官方移植lwip资料 修改代码 包含移植后的代码 STM32官方移植lwip修改代码 修改参考