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之官方源码解析 的相关文章

  • STM32在Debug时程序运行时间不对,Debug时定时器中断每次进入断点时间不对,定时器一开启就进入中断的解决办法

    一 Debug时程序时间不对解决办法 1 点击魔术棒 2 点击Debug 点击Settings 3 点击Trace 在Core Clock里修改为你的系统时钟 二 Debug时定时器中断每次进入断点时间不对 在Debug时 程序停下来 定时
  • 如何在 Cortex-M3 (STM32) 上从 RAM 执行函数?

    我正在尝试从 Cortex M3 处理器 STM32 上的 RAM 执行函数 该函数会擦除并重写内部闪存 所以我肯定需要在 RAM 中 但我该怎么做呢 我尝试过的是 使用 memcpy 将函数复制到 RAM 中的字节数组 检查它是否正确对齐
  • GCC - 如何停止链接 malloc?

    我正在努力将我的代码缩减到最小的骨架大小 我使用的是只有 32k 闪存的 STM32F0 需要很大一部分闪存用于数据存储 我的代码已经有大约 20k 闪存大小 其中一些是由于使用了 STM32 HAL 函数 我可以在以后需要时对其进行解释和
  • STM32F4 通过软复位跳转到引导加载程序,无需 BOOT0 和 BOOT1 引脚

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

    物联网网关是 连接物联网设备和互联网的重要桥梁 它负责将物联网设备采集到的数据进行处理 存储和转发 使其能够与云端或其它设备进行通信 物联网网关的作用是实现物联网设备与云端的无缝连接和数据交互 物联网网关功能 数据采集 物联网网关可以从物联
  • 毕业设计 江科大STM32的智能温室控制蓝牙声光报警APP系统设计

    基于STM32的智能温室控制蓝牙声光报警APP系统设计 1 项目简介 1 1 系统构成 1 2 系统功能 2 部分电路设计 2 1 stm32f103c8t6单片机最小系统电路设计 2 2 LCD1602液晶显示电路设计 2 2 风
  • [屏驱相关]【SWM166-SPI-Y1.28C1测评】+ 有点惊艳的开箱

    耳闻华芯微特许久了 看到论坛得评测活动赶紧上了末班车 毕竟对有屏幕得板子也是很喜欢得 京东快递小哥客客气气 微笑着把快递给了我 好评 直接拆了包 在此之前没看过视频号 所以这个圆盘盘得模具还是有点惊喜的 正面照如下 开机有灯光秀 还有动画
  • SHT10温湿度传感器——STM32驱动

    实验效果 硬件外观 接线 3 3V供电 IIC通讯 代码获取 查看下方 END
  • HAL 锁定和解锁函数如何使用以及为什么?

    我试图理解另一位程序员编写的代码 它使用了I C http en wikipedia org wiki I C2 B2C通信以将数据写入 STM32 微控制器的 EEPROM 一般来说 我理解他的代码是如何工作的 但我不明白他为什么使用HA
  • 硬件基础-电容

    电容 本质 电容两端电压不能激变 所以可以起到稳定电压作用 充放电 电容量的大小 想使电容容量大 使用介电常数高的介质 增大极板间的面积 减小极板间的距离 品牌 国外 村田 muRata 松下 PANASONIC 三星 SAMSUNG 太诱
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 在 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测试 再慢慢使用
  • CMSIS & STM32,如何开始? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想在 STM32 上使用 CMSIS 启动项目 网上一搜 没找到具体的教程 有些使用 SPL 开始项
  • for循环延时时间计算

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 之前做led点亮的实验 好像是被delay函数影响了 因为delay参数设置的不对
  • 库函数点亮Led

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 例如 随着人工智能的不断发展 机器学习这门
  • STM32H5 Nucleo-144 board开箱

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

    特殊寄存器 文章目录 前言 一 背景 二 2 1 2 2 总结 前言 前期疑问 STM32特殊寄存器到底是什么 特殊寄存器怎么查看和调试代码 本文目标 记录和理解特殊寄存器 一 背景 最近在看ucosIII文章是 里面提到特殊寄存器 这就进
  • 通过JTAG恢复STM32 MCU磨掉的标记

    我有一块可能带有 STM32 MCU 的板 我想为该板制作定制固件 因为库存板有很多问题 不幸的是 电路板制造商很友善地磨掉了所有标记 有没有办法通过 jtag 获取设备 系列 ID 并将其交叉引用到型号 我能找到的一切都是关于获取芯片的唯
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt

随机推荐

  • 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修改代码 修改参考