0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

stm32H7 HAL庫中存在的bug

魚鷹談單片機 ? 來源:魚鷹談單片機 ? 2024-08-12 17:37 ? 次閱讀

stm32H7 hal 庫里面的以太網(wǎng)代碼,坑了魚鷹很多次(不知道最新版是否已經(jīng)修復(fù)了這些bug),這里分享一篇網(wǎng)上的文章,因為魚鷹也遇到過,靠它解決了其中一個編譯優(yōu)化問題,在此感謝作者。不過hal庫里面遠不止這些bug(主要是項目環(huán)境太復(fù)雜了,一般情況很難觸發(fā)),還有更多bug沒在此文章描述,而魚鷹碰到了......

問題

最近在調(diào)試STM32H750+LAN8720,搞了大半天終于移植好LwIP了,ping也能ping通,TCP測試也成功。本來以為ST的HAL庫終于省心了,結(jié)果我將編譯優(yōu)化開到最大…

…直接ping都ping不通了。后來發(fā)現(xiàn)HAL庫有很大問題。(果然HAL庫還是不省心,生成的代碼只有初始化能用)

后面發(fā)現(xiàn),HAL庫有兩個隱患:

1、對描述符的處理有問題

2、因為單片機是Cortex-M7,有Cache和單片機會亂序執(zhí)行和亂序訪問內(nèi)存,亂序訪問對發(fā)送/接收描述符 操作有很大的隱患

后面對這些問題詳細(xì)描述

原因

問題1 HAL庫的隱患1 處理方式

我感覺HAL庫處理數(shù)據(jù)描述符的方式似乎很有問題,例如

SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_OWN);//居然在這里將描述符權(quán)限給了ETH


if(dmarxdesclist->ItMode != 0U)
{
  SET_BIT(dmarxdesc->DESC3,ETH_DMARXNDESCRF_IOC);//描述符都給了ETH居然還要修改
}

這是stm32h7xx_hal_eth.c中

HAL_StatusTypeDef HAL_ETH_BuildRxDescriptors(ETH_HandleTypeDef *heth)

的一段代碼,描述符的OWN在我看來應(yīng)該是最后才設(shè)置的,因為它是標(biāo)記描述符當(dāng)前是ETH所有還是用戶(CPU等其他玩意)所有,但從這段代碼看來,它把描述符歸還ETH后居然還對這個描述符進行修改,這是要趁ETH不注意嗎,如果真是這樣,ETH還真的能正常運行!但是這無疑是一個安全隱患。一個直接體現(xiàn)就是我把編譯優(yōu)化打開(-O3)以太網(wǎng)通信就出問題了。

并且這種操作在HAL庫里隨處可見,簡直恐怖如斯

估摸著ST的人以為寫入ETH->DMACRDTPR或者ETH->DMACTDTPR(用于告訴ETH描述符有更新)描述符才會生效,但我看了文檔的描述,如果應(yīng)用程序能一直更新描述符,即使不寫入這個寄存器,ETH還是會接著發(fā)送,即描述符的OWN位的設(shè)置為1代表著描述符歸ETH所有,送出去的描述符潑出去的水,用戶不應(yīng)該再進行修改,直到OWN被ETH清零。那ETH->DMACRDTPR或者ETH->DMACTDTPR的用處是什么呢?為了減少ETH對總線的占用,如果ETH檢測不到有效的描述符(OWN都為0),ETH將不再訪問內(nèi)存,所以需要一個機制來告訴ETH描述符有更新,該干活了。

而這個機制就是寫入ETH->DMACRDTPR(接收描述符)或者ETH->DMACTDTPR(發(fā)送描述符),這兩個寄存器除了標(biāo)記最后一個描述符的地址,還用于告訴ETH描述符有更新。

問題2 HAL庫的隱患2 沒有處理Cortex-M7的亂序訪問和Cache的問題

正常來說,CPU的亂序執(zhí)行是不會影響外設(shè)操作的。因為外設(shè)寄存器的內(nèi)存類型默認(rèn)為Device(還要一個類似的屬性為Strongly-ordered),這意味著亂序執(zhí)行無論在怎么亂,它都會保證對這些內(nèi)存正確的順序(在代碼中的執(zhí)行順序)訪問。

但巧了,描述符的存放位置不在這些內(nèi)存區(qū)域,它只能放在D2域的SRAM中(ETH的手只能夠著這些地方),而且這些SRAM的內(nèi)存類型默認(rèn)為Normal,而為了效率,CPU會對這些區(qū)域的內(nèi)存亂序訪問(這樣關(guān)鍵的OWN位可能提前也可能延后被寫入),這肯定不是我們希望的。

對于開啟了Cache的情況,這將變得更加復(fù)雜,可能寫入的數(shù)據(jù)都沒有實實在在的寫入內(nèi)存中。

解決方法

對于問題1,對描述符操作的代碼沒多少,我就自己手動修改了stm32h7xx_hal_eth.c中的相關(guān)代碼(修改過的部分在后面放出),保證代碼的順序是沒問題的。主要修改OWN相關(guān)的和ETH->DMACRDTPR和ETH->DMACTDTPR相關(guān)的。

對于問題2,我是這么處理的:

配置MPU,將描述符,數(shù)據(jù)緩存所在的內(nèi)存區(qū)域配置為不緩沖(Buffer),不緩存(Cache),TEX配置為LEVEL1(0x01),這樣這些內(nèi)存就配置為了Normal,但不再受Cache的影響(這保證了一個性能的平衡,因為對于CortexM7,CleanCache操作步驟太多,雖然就調(diào)用了一個內(nèi)聯(lián)的函數(shù),但其中是寫一次寄存器Clean一個Cache行,整個Cache需要512次寫操作還有其他相關(guān)的計算,還不如不Cache這些地方的內(nèi)存,而使用Normal屬性能加快內(nèi)存訪問速度(它似乎能把多個連續(xù)單字節(jié)的訪問打包成32Bit的訪問,應(yīng)該是亂序訪問的功勞,這個特性是我調(diào)試SDRAM測試它的速度時發(fā)現(xiàn)的,即使沒有使能Cache,配置成Normal的訪問速度比Device要快))

在設(shè)置/清零OWN操作的前后加入__DMB()內(nèi)存隔離指令(要大寫的,自帶編譯隔離,防止這個指令被編譯器重排,沒想到吧,編譯器也會把你的指令打亂)

LwIP的版本為2.1.0

修改完之后,單片機端使用socket API創(chuàng)建的tcp服務(wù)器,單向傳輸10MB/s,相當(dāng)于80Mbps帶寬,離物理能達到的100Mbps還差一點,但CPU占用率已經(jīng)達到了80,90多(TCP應(yīng)該比較消耗性能吧),估計協(xié)議和性能的損耗比較大。

修改的代碼

只需要修改stm32h7xx_hal_eth.c的代碼

發(fā)送的修改

發(fā)送相關(guān)的有三個函數(shù)被修改


static uint32_t ETH_Prepare_Tx_Descriptors(ETH_HandleTypeDef *heth, ETH_TxPacketConfig *pTxConfig, uint32_t ItMode)
{
  ETH_TxDescListTypeDef *dmatxdesclist = &heth->TxDescList;
  uint32_t descidx = dmatxdesclist->CurTxDesc;
  uint32_t firstdescidx = dmatxdesclist->CurTxDesc;
  uint32_t descnbr = 0, idx;
  ETH_DMADescTypeDef *dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];


  ETH_BufferTypeDef  *txbuffer = pTxConfig->TxBuffer;
  uint32_t           bd_count = 0;


  /* Current Tx Descriptor Owned by DMA: cannot be used by the application  */
  if((READ_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCWBF_OWN) == ETH_DMATXNDESCWBF_OWN) || (dmatxdesclist->PacketAddress[descidx] != NULL))
  {
    return HAL_ETH_ERROR_BUSY;
  }


  /***************************************************************************/
  /*****************    Context descriptor configuration (Optional) **********/
  /***************************************************************************/
  /* If VLAN tag is enabled for this packet */
  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_VLANTAG) != 0U)
  {
    /* Set vlan tag value */
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXCDESC_VT, pTxConfig->VlanTag);
    /* Set vlan tag valid bit */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_VLTV);
    /* Set the descriptor as the vlan input source */
    SET_BIT(heth->Instance->MACVIR, ETH_MACVIR_VLTI);


    /* if inner VLAN is enabled */
    if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_INNERVLANTAG) != 0U)
    {
      /* Set inner vlan tag value */
      MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXCDESC_IVT, (pTxConfig->InnerVlanTag << 16));
      /* Set inner vlan tag valid bit */
      SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_IVLTV);


      /* Set Vlan Tag control */
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXCDESC_IVTIR, pTxConfig->InnerVlanCtrl);


      /* Set the descriptor as the inner vlan input source */
      SET_BIT(heth->Instance->MACIVIR, ETH_MACIVIR_VLTI);
      /* Enable double VLAN processing */
      SET_BIT(heth->Instance->MACVTR, ETH_MACVTR_EDVLP);
    }
  }


  /* if tcp segmentation is enabled for this packet */
  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_TSO) != 0U)
  {
    /* Set MSS value */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXCDESC_MSS, pTxConfig->MaxSegmentSize);
    /* Set MSS valid bit */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_TCMSSV);
  }


  if((READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_VLANTAG) != 0U)|| (READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_TSO) != 0U))
  {
    /* Set as context descriptor */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_CTXT);
    /* Set own bit */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_OWN);
    /* Increment current tx descriptor index */
    INCR_TX_DESC_INDEX(descidx, 1U);
    /* Get current descriptor address */
    dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];


    descnbr += 1U;


    /* Current Tx Descriptor Owned by DMA: cannot be used by the application  */
    if(READ_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCWBF_OWN) == ETH_DMATXNDESCWBF_OWN)
    {
      dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[firstdescidx];
      /* Clear own bit */
      CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_OWN);


      return HAL_ETH_ERROR_BUSY;
    }
  }


  /***************************************************************************/
  /*****************    Normal descriptors configuration     *****************/
  /***************************************************************************/


  descnbr += 1U;


  /* Set header or buffer 1 address */
  WRITE_REG(dmatxdesc->DESC0, (uint32_t)txbuffer->buffer);
  /* Set header or buffer 1 Length */
  MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B1L, txbuffer->len);


  if(txbuffer->next != NULL)
  {
    txbuffer = txbuffer->next;
    /* Set buffer 2 address */
    WRITE_REG(dmatxdesc->DESC1, (uint32_t)txbuffer->buffer);
    /* Set buffer 2 Length */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, (txbuffer->len << 16));
  }
  else
  {
    WRITE_REG(dmatxdesc->DESC1, 0x0);
    /* Set buffer 2 Length */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, 0x0U);
  }


  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_TSO) != 0U)
  {
    /* Set TCP Header length */
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_THL, (pTxConfig->TCPHeaderLen << 19));
    /* Set TCP payload length */
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_TPL, pTxConfig->PayloadLen);
    /* Set TCP Segmentation Enabled bit */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_TSE);
  }
  else
  {
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_FL, pTxConfig->Length);


    if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_CSUM) != 0U)
    {
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CIC, pTxConfig->ChecksumCtrl);
    }


    if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_CRCPAD) != 0U)
    {
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CPC, pTxConfig->CRCPadCtrl);
    }
  }


  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_VLANTAG) != 0U)
  {
    /* Set Vlan Tag control */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_VTIR, pTxConfig->VlanCtrl);
  }


  /* Mark it as First Descriptor */
  SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_FD);
  /* Mark it as NORMAL descriptor */
  CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CTXT);


  /* If source address insertion/replacement is enabled for this packet */
  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_SAIC) != 0U)
  {
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_SAIC, pTxConfig->SrcAddrCtrl);
  }


  /* only if the packet is split into more than one descriptors > 1 */
  while (txbuffer->next != NULL)
  {
    /* Clear the LD bit of previous descriptor */
    CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_LD);
    __DMB();//修改!!
    /* set OWN bit of Last descriptor */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_OWN);
    __DMB();//修改!!


    /* Increment current tx descriptor index */
    INCR_TX_DESC_INDEX(descidx, 1U);
    /* Get current descriptor address */
    dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];


    /* Clear the FD bit of new Descriptor */
    CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_FD);


    /* Current Tx Descriptor Owned by DMA: cannot be used by the application  */
    if((READ_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_OWN) == ETH_DMATXNDESCRF_OWN) || (dmatxdesclist->PacketAddress[descidx] != NULL))
    {
      descidx = firstdescidx;
      dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];


      /* clear previous desc own bit */
      for(idx = 0; idx < descnbr; idx ++)
      {
        __DMB();//修改!!
        CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_OWN);
        __DMB();//修改!!


        /* Increment current tx descriptor index */
        INCR_TX_DESC_INDEX(descidx, 1U);
        /* Get current descriptor address */
        dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];
      }


      return HAL_ETH_ERROR_BUSY;
    }


    descnbr += 1U;


    /* Get the next Tx buffer in the list */
    txbuffer = txbuffer->next;


    /* Set header or buffer 1 address */
    WRITE_REG(dmatxdesc->DESC0, (uint32_t)txbuffer->buffer);
    /* Set header or buffer 1 Length */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B1L, txbuffer->len);


    if (txbuffer->next != NULL)
    {
      /* Get the next Tx buffer in the list */
      txbuffer = txbuffer->next;
      /* Set buffer 2 address */
      WRITE_REG(dmatxdesc->DESC1, (uint32_t)txbuffer->buffer);
      /* Set buffer 2 Length */
      MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, (txbuffer->len << 16));
    }
    else
    {
      WRITE_REG(dmatxdesc->DESC1, 0x0);
      /* Set buffer 2 Length */
      MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, 0x0U);
    }


    if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_TSO) != 0U)
    {
      /* Set TCP payload length */
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_TPL, pTxConfig->PayloadLen);
      /* Set TCP Segmentation Enabled bit */
      SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_TSE);
    }
    else
    {
      /* Set the packet length */
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_FL, pTxConfig->Length);


      if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_CSUM) != 0U)
      {
        /* Checksum Insertion Control */
        MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CIC, pTxConfig->ChecksumCtrl);
      }
    }


    bd_count += 1U;


    /* Mark it as NORMAL descriptor */
    CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CTXT);
    
  }


  if(ItMode != ((uint32_t)RESET))
  {
    /* Set Interrupt on completion bit */
    SET_BIT(dmatxdesc->DESC2, ETH_DMATXNDESCRF_IOC);
  }
  else
  {
    /* Clear Interrupt on completion bit */
    CLEAR_BIT(dmatxdesc->DESC2, ETH_DMATXNDESCRF_IOC);
  }


  /* Mark it as LAST descriptor */
  SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_LD);


  __DMB();//修改!!


  /* Set Own bit For End Desc */
  SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_OWN);
  
  /* Save the current packet address to expose it to the application */
  dmatxdesclist->PacketAddress[descidx] = dmatxdesclist->CurrentPacketAddress;


  dmatxdesclist->CurTxDesc = descidx;


  /* disable the interrupt */
  //__disable_irq();
  //我最看不慣的就是關(guān)中斷!!
  //dmatxdesclist->BuffersInUse += bd_count + 1U;


  /* Enable interrupts back */
  //__enable_irq();




  /* Return function status */
  return HAL_ETH_ERROR_NONE;
}




HAL_StatusTypeDef HAL_ETH_Transmit(ETH_HandleTypeDef *heth, ETH_TxPacketConfig *pTxConfig, uint32_t Timeout)
{
  uint32_t tickstart;
  const ETH_DMADescTypeDef *dmatxdesc;


  if(pTxConfig == NULL)
  {
    heth->ErrorCode |= HAL_ETH_ERROR_PARAM;
    return HAL_ERROR;
  }


  if(heth->gState == HAL_ETH_STATE_READY)
  {
    /* Config DMA Tx descriptor by Tx Packet info */
    if (ETH_Prepare_Tx_Descriptors(heth, pTxConfig, 0) != HAL_ETH_ERROR_NONE)
    {
      /* Set the ETH error code */
      heth->ErrorCode |= HAL_ETH_ERROR_BUSY;
      return HAL_ERROR;
    }


    dmatxdesc = (ETH_DMADescTypeDef *)(&heth->TxDescList)->TxDesc[heth->TxDescList.CurTxDesc];


    /* Incr current tx desc index */
    INCR_TX_DESC_INDEX(heth->TxDescList.CurTxDesc, 1U);


    /* Start transmission */
    /* issue a poll command to Tx DMA by writing address of next immediate free descriptor */
    //WRITE_REG(heth->Instance->DMACTDTPR, (uint32_t)(heth->TxDescList.TxDesc[heth->TxDescList.CurTxDesc]));
    __DMB();//修改!!
    WRITE_REG(heth->Instance->DMACTDTPR, ((uint32_t)(heth->Init.TxDesc + (uint32_t)(ETH_TX_DESC_CNT - 1))));


    tickstart = HAL_GetTick();


    /* Wait for data to be transmitted or timeout occurred */
    while((dmatxdesc->DESC3 & ETH_DMATXNDESCWBF_OWN) != (uint32_t)RESET)
    {
      if((heth->Instance->DMACSR & ETH_DMACSR_FBE) != (uint32_t)RESET)
      {
        heth->ErrorCode |= HAL_ETH_ERROR_DMA;
        heth->DMAErrorCode = heth->Instance->DMACSR;
        /* Set ETH HAL State to Ready */
        heth->gState = HAL_ETH_STATE_ERROR;
        /* Return function status */
        return HAL_ERROR;
      }


      /* Check for the Timeout */
      if(Timeout != HAL_MAX_DELAY)
      {
        if(((HAL_GetTick() - tickstart ) > Timeout) || (Timeout == 0U))
        {
          heth->ErrorCode |= HAL_ETH_ERROR_TIMEOUT;
          heth->gState = HAL_ETH_STATE_ERROR;
          return HAL_ERROR;
        }
      }
    }


    /* Return function status */
    return HAL_OK;
  }
  else
  {
    return HAL_ERROR;
  }
}


/**
  * @brief  Sends an Ethernet Packet in interrupt mode.
  * @param  heth: pointer to a ETH_HandleTypeDef structure that contains
  *         the configuration information for ETHERNET module
  * @param  pTxConfig: Hold the configuration of packet to be transmitted
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_ETH_Transmit_IT(ETH_HandleTypeDef *heth, ETH_TxPacketConfig *pTxConfig)
{
  if(pTxConfig == NULL)
  {
    heth->ErrorCode |= HAL_ETH_ERROR_PARAM;
    return HAL_ERROR;
  }


  if(heth->gState == HAL_ETH_STATE_READY)
  {
    /* Config DMA Tx descriptor by Tx Packet info */
    if (ETH_Prepare_Tx_Descriptors(heth, pTxConfig, 1) != HAL_ETH_ERROR_NONE)
    {
      heth->ErrorCode |= HAL_ETH_ERROR_BUSY;
      return HAL_ERROR;
    }


    /* Incr current tx desc index */
    INCR_TX_DESC_INDEX(heth->TxDescList.CurTxDesc, 1U);


    __DMB();//修改!!


    /* Start transmission */
    /* issue a poll command to Tx DMA by writing address of next immediate free descriptor */
    //WRITE_REG(heth->Instance->DMACTDTPR, (uint32_t)(heth->TxDescList.TxDesc[heth->TxDescList.CurTxDesc]));
    WRITE_REG(heth->Instance->DMACTDTPR, (uint32_t)(heth->TxDescList.TxDesc[ETH_TX_DESC_CNT-1]));


    return HAL_OK;


  }
  else
  {
    return HAL_ERROR;
  }
}

接收的修改

接收相關(guān)的函數(shù)有2個被修改

uint8_t HAL_ETH_IsRxDataAvailable(ETH_HandleTypeDef *heth)
{
  ETH_RxDescListTypeDef *dmarxdesclist = &heth->RxDescList;
  uint32_t descidx = dmarxdesclist->CurRxDesc;
  ETH_DMADescTypeDef *dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];
  uint32_t descscancnt = 0;
  uint32_t appdesccnt = 0, firstappdescidx = 0;


  if(dmarxdesclist->AppDescNbr != 0U)
  {
    /* data already received by not yet processed*/
    return 0;
  }


  /* Check if descriptor is not owned by DMA */
  while((READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_OWN) == (uint32_t)RESET) && (descscancnt < (uint32_t)ETH_RX_DESC_CNT))
  {
    descscancnt++;


    /* Check if last descriptor */
    if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_LD) != (uint32_t)RESET)
    {
      /* Increment the number of descriptors to be passed to the application */
      appdesccnt += 1U;


      if(appdesccnt == 1U)
      {
        WRITE_REG(firstappdescidx, descidx);
      }


      /* Increment current rx descriptor index */
      INCR_RX_DESC_INDEX(descidx, 1U);


      /* Check for Context descriptor */
      /* Get current descriptor address */
      dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];


      if(READ_BIT(dmarxdesc->DESC3,  ETH_DMARXNDESCWBF_OWN)  == (uint32_t)RESET)
      {
        if(READ_BIT(dmarxdesc->DESC3,  ETH_DMARXNDESCWBF_CTXT)  != (uint32_t)RESET)
        {
          /* Increment the number of descriptors to be passed to the application */
          dmarxdesclist->AppContextDesc = 1;
          /* Increment current rx descriptor index */
          INCR_RX_DESC_INDEX(descidx, 1U);
        }
      }
      /* Fill information to Rx descriptors list */
      dmarxdesclist->CurRxDesc = descidx;
      dmarxdesclist->FirstAppDesc = firstappdescidx;
      dmarxdesclist->AppDescNbr = appdesccnt;


      /* Return function status */
      return 1;
    }
    /* Check if first descriptor */
    else if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_FD) != (uint32_t)RESET)
    {
      WRITE_REG(firstappdescidx, descidx);
      /* Increment the number of descriptors to be passed to the application */
      appdesccnt = 1U;


      /* Increment current rx descriptor index */
      INCR_RX_DESC_INDEX(descidx, 1U);
      /* Get current descriptor address */
      dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];
    }
    /* It should be an intermediate descriptor */
    else
    {
      /* Increment the number of descriptors to be passed to the application */
      appdesccnt += 1U;


      /* Increment current rx descriptor index */
      INCR_RX_DESC_INDEX(descidx, 1U);
      /* Get current descriptor address */
      dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];
    }
  }


  /* Build Descriptors if an incomplete Packet is received */
  if(appdesccnt > 0U)
  {
    dmarxdesclist->CurRxDesc = descidx;
    dmarxdesclist->FirstAppDesc = firstappdescidx;
    descidx = firstappdescidx;
    dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];


    for(descscancnt = 0; descscancnt < appdesccnt; descscancnt++)
    {
      WRITE_REG(dmarxdesc->DESC0, dmarxdesc->BackupAddr0);
      WRITE_REG(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF1V);


      if (READ_REG(dmarxdesc->BackupAddr1) != ((uint32_t)RESET))
      {
        WRITE_REG(dmarxdesc->DESC2, dmarxdesc->BackupAddr1);
        SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF2V);
      }


      SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_OWN);


      if(dmarxdesclist->ItMode != ((uint32_t)RESET))
      {
        SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_IOC);
      }
      if(descscancnt < (appdesccnt - 1U))
      {
        /* Increment rx descriptor index */
        INCR_RX_DESC_INDEX(descidx, 1U);
        /* Get descriptor address */
        dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];
      }
    }


    /* Set the Tail pointer address to the last rx descriptor hold by the app */
    //WRITE_REG(heth->Instance->DMACRDTPR, (uint32_t)dmarxdesc);
  }


  /* Fill information to Rx descriptors list: No received Packet */
  dmarxdesclist->AppDescNbr = 0U;


  return 0;
}


HAL_StatusTypeDef HAL_ETH_BuildRxDescriptors(ETH_HandleTypeDef *heth)
{
  ETH_RxDescListTypeDef *dmarxdesclist = &heth->RxDescList;
  uint32_t descindex = dmarxdesclist->FirstAppDesc;
  __IO ETH_DMADescTypeDef *dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descindex];
  uint32_t totalappdescnbr = dmarxdesclist->AppDescNbr;
  uint32_t descscan;


  if(dmarxdesclist->AppDescNbr == 0U)
  {
    /* No Rx descriptors to build */
    return HAL_ERROR;
  }


  if(dmarxdesclist->AppContextDesc != 0U)
  {
    /* A context descriptor is available */
    totalappdescnbr += 1U;
  }


  for(descscan =0; descscan < totalappdescnbr; descscan++)
  {
    WRITE_REG(dmarxdesc->DESC0, dmarxdesc->BackupAddr0);
    WRITE_REG(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF1V);


    if (READ_REG(dmarxdesc->BackupAddr1) != 0U)
    {
      WRITE_REG(dmarxdesc->DESC2, dmarxdesc->BackupAddr1);
      SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF2V);
    }


    if(dmarxdesclist->ItMode != 0U){
      SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_IOC);
    }


    __DMB();//修改!!
    SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_OWN);


    if(descscan < (totalappdescnbr - 1U))
    {
      /* Increment rx descriptor index */
      INCR_RX_DESC_INDEX(descindex, 1U);
      /* Get descriptor address */
      dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descindex];
    }
  }


  __DMB();//修改!!
  /* Set the Tail pointer address to the last rx descriptor hold by the app */
  //WRITE_REG(heth->Instance->DMACRDTPR, (uint32_t)dmarxdesc);//修改!!
  WRITE_REG(heth->Instance->DMACRDTPR, ((uint32_t)(heth->Init.RxDesc + (uint32_t)(ETH_RX_DESC_CNT - 1))));


  /* reset the Application desc number */
  WRITE_REG(dmarxdesclist->AppDescNbr, 0);


  /*  reset the application context descriptor */
  WRITE_REG(heth->RxDescList.AppContextDesc, 0);


  return HAL_OK;
}

其他修改

其實下面的修改無關(guān)緊要,因為它會在后面的傳輸中被修正,但強迫癥不能忍啊。

static void ETH_DMATxDescListInit(ETH_HandleTypeDef *heth)
{
  ETH_DMADescTypeDef *dmatxdesc;
  uint32_t i;


  /* Fill each DMATxDesc descriptor with the right values */
  for(i=0; i < (uint32_t)ETH_TX_DESC_CNT; i++)
  {
    dmatxdesc = heth->Init.TxDesc + i;


    WRITE_REG(dmatxdesc->DESC0, 0x0);
    WRITE_REG(dmatxdesc->DESC1, 0x0);
    WRITE_REG(dmatxdesc->DESC2, 0x0);
    WRITE_REG(dmatxdesc->DESC3, 0x0);


    WRITE_REG(heth->TxDescList.TxDesc[i], (uint32_t)dmatxdesc);
  }


  heth->TxDescList.CurTxDesc = 0;


  /* Set Transmit Descriptor Ring Length */
  WRITE_REG(heth->Instance->DMACTDRLR, (ETH_TX_DESC_CNT -1));


  /* Set Transmit Descriptor List Address */
  WRITE_REG(heth->Instance->DMACTDLAR, (uint32_t) heth->Init.TxDesc);


  /* Set Transmit Descriptor Tail pointer *///修改!!
  WRITE_REG(heth->Instance->DMACTDTPR, ((uint32_t)(heth->Init.TxDesc + (uint32_t)(ETH_TX_DESC_CNT - 1))));//修改!!
}


/**
  * @brief  Initializes the DMA Rx descriptors in chain mode.
  *         called by HAL_ETH_Init() API.
  * @param  heth: pointer to a ETH_HandleTypeDef structure that contains
  *         the configuration information for ETHERNET module
  * @retval None
  */
static void ETH_DMARxDescListInit(ETH_HandleTypeDef *heth)
{
  ETH_DMADescTypeDef *dmarxdesc;
  uint32_t i;


  for(i = 0; i < (uint32_t)ETH_RX_DESC_CNT; i++)
  {
    dmarxdesc =  heth->Init.RxDesc + i;


    WRITE_REG(dmarxdesc->DESC0, 0x0);
    WRITE_REG(dmarxdesc->DESC1, 0x0);
    WRITE_REG(dmarxdesc->DESC2, 0x0);
    WRITE_REG(dmarxdesc->DESC3, 0x0);
    WRITE_REG(dmarxdesc->BackupAddr0, 0x0);
    WRITE_REG(dmarxdesc->BackupAddr1, 0x0);


    /* Set Rx descritors addresses */
    WRITE_REG(heth->RxDescList.RxDesc[i], (uint32_t)dmarxdesc);
  }


  WRITE_REG(heth->RxDescList.CurRxDesc, 0);
  WRITE_REG(heth->RxDescList.FirstAppDesc, 0);
  WRITE_REG(heth->RxDescList.AppDescNbr, 0);
  WRITE_REG(heth->RxDescList.ItMode, 0);
  WRITE_REG(heth->RxDescList.AppContextDesc, 0);


  /* Set Receive Descriptor Ring Length */
  WRITE_REG(heth->Instance->DMACRDRLR, ((uint32_t)(ETH_RX_DESC_CNT - 1)));


  /* Set Receive Descriptor List Address */
  WRITE_REG(heth->Instance->DMACRDLAR, (uint32_t) heth->Init.RxDesc);


  /* Set Receive Descriptor Tail pointer Address *///修改!!
  WRITE_REG(heth->Instance->DMACRDTPR, ((uint32_t)(heth->Init.RxDesc + (uint32_t)(ETH_RX_DESC_CNT - 1))));//修改!!
}

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 以太網(wǎng)
    +關(guān)注

    關(guān)注

    40

    文章

    5343

    瀏覽量

    170796
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4722

    瀏覽量

    68229
  • HAL庫
    +關(guān)注

    關(guān)注

    1

    文章

    114

    瀏覽量

    6144

原文標(biāo)題:hal 庫超好用?來看看以太網(wǎng)的bug

文章出處:【微信號:emOsprey,微信公眾號:魚鷹談單片機】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    使用LL生成STM32H7代碼時存在報錯是什么原因?qū)е碌模?/a>

    使用LL生成STM32H7代碼時存在BUG
    發(fā)表于 04-03 07:33

    STM32H7教程】第57章 STM32H7硬件JPEG編解碼基礎(chǔ)知識和HALAPI 精選資料分享

    完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980第57章 STM32H7硬件JPEG編解碼基礎(chǔ)知識和HAL
    發(fā)表于 08-03 07:28

    STM32H7的DAC基礎(chǔ)知識和HALAPI

    第59章 STM32H7的DAC基礎(chǔ)知識和HALAPI本章節(jié)為大家講解DAC,實際項目用到DAC的地方比較多,而且H7的DAC性能也比較給力。59.1 初學(xué)者重要提示59.2 DAC
    發(fā)表于 08-06 08:25

    STM32H7的TIM定時器基礎(chǔ)知識和HAL

    第32章 STM32H7的TIM定時器基礎(chǔ)知識和HALAPI本章節(jié)為大家講解TIM1 – TIM17(STM32H7沒有TIM9,TIM10和TIM11)共計14個定時器的基礎(chǔ)知識和
    發(fā)表于 08-19 06:53

    STM32H7的ADC基礎(chǔ)知識和HALAPI

    第44章 STM32H7的ADC基礎(chǔ)知識和HALAPI本章節(jié)為大家講解ADC(Analog-to-digital converters,模數(shù)轉(zhuǎn)換器),極具項目使用價值,因為STM32H7
    發(fā)表于 08-20 07:55

    STM32H7教程】第19章 STM32H7的GPIO應(yīng)用之按鍵FIFO

    STM32H7教程】第19章 STM32H7的GPIO應(yīng)用之按鍵FIFO
    發(fā)表于 11-23 18:21 ?9次下載
    【<b class='flag-5'>STM32H7</b>教程】第19章 <b class='flag-5'>STM32H7</b>的GPIO應(yīng)用之按鍵FIFO

    STM32H7學(xué)習(xí)繼續(xù)(STM32H7系列7)含外設(shè)的編程一般流程

    以<<STM32H7 開發(fā)指南(HAL 版)>>為導(dǎo)引學(xué)習(xí)SYSTEM 文件夾介紹本章包括如下 3 個小結(jié):5.1,delay 文件夾代碼
    發(fā)表于 11-23 18:21 ?28次下載
    <b class='flag-5'>STM32H7</b>學(xué)習(xí)繼續(xù)(<b class='flag-5'>STM32H7</b>系列<b class='flag-5'>7</b>)含外設(shè)的編程一般流程

    STM32H7學(xué)習(xí)之路繼續(xù)(stm32H7系列3) GPIO

    STM32H7學(xué)習(xí)之路繼續(xù)(stm32H7系列3) GPIO
    發(fā)表于 11-30 12:36 ?20次下載
    <b class='flag-5'>STM32H7</b>學(xué)習(xí)之路繼續(xù)(<b class='flag-5'>stm32H7</b>系列3)  GPIO

    STM32H7教程】第21章 STM32H7的NVIC中斷分組和配置(重要)

    STM32H7教程】第21章 STM32H7的NVIC中斷分組和配置(重要)
    發(fā)表于 12-04 14:36 ?13次下載
    【<b class='flag-5'>STM32H7</b>教程】第21章 <b class='flag-5'>STM32H7</b>的NVIC中斷分組和配置(重要)

    STM32H7 串口 空閑中斷 任意長接收 Hal IDLE

    今天主要記錄一下STM32H7系列串口的使用,正點原子、野火等各大家都有教程,當(dāng)然用起來也沒有問題。解決方法后邊有紅色大字提醒,直接看后邊就可以,如果你沒有時間想去了解HAL的接收思想。ST推
    發(fā)表于 12-04 17:21 ?17次下載
    <b class='flag-5'>STM32H7</b>  串口  空閑中斷   任意長接收  <b class='flag-5'>Hal</b><b class='flag-5'>庫</b>  IDLE

    "STM32H7學(xué)習(xí)繼續(xù)(STM32H7系列5)第十七章比較實用,以后寫程序的時候會用到"

    "STM32H7學(xué)習(xí)繼續(xù)(STM32H7系列5)第十七章比較實用,以后寫程序的時候會用到"
    發(fā)表于 12-05 11:21 ?9次下載
    "<b class='flag-5'>STM32H7</b>學(xué)習(xí)繼續(xù)(<b class='flag-5'>STM32H7</b>系列5)第十七章比較實用,以后寫程序的時候會用到"

    STM32H7教程】第8章 STM32H7的終極調(diào)試組件Event Recorder

    STM32H7教程】第8章 STM32H7的終極調(diào)試組件Event Recorder
    發(fā)表于 12-05 20:06 ?7次下載
    【<b class='flag-5'>STM32H7</b>教程】第8章 <b class='flag-5'>STM32H7</b>的終極調(diào)試組件Event Recorder

    STM32H7教程】第14章 STM32H7的電源,復(fù)位和時鐘系統(tǒng)

    STM32H7教程】第14章 STM32H7的電源,復(fù)位和時鐘系統(tǒng)
    發(fā)表于 12-09 11:21 ?36次下載
    【<b class='flag-5'>STM32H7</b>教程】第14章 <b class='flag-5'>STM32H7</b>的電源,復(fù)位和時鐘系統(tǒng)

    STM32H7教程】第57章 STM32H7硬件JPEG編解碼基礎(chǔ)知識和HALAPI

    完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980第57章 STM32H7硬件JPEG編解碼基礎(chǔ)知識和HAL
    發(fā)表于 12-27 18:45 ?14次下載
    【<b class='flag-5'>STM32H7</b>教程】第57章 <b class='flag-5'>STM32H7</b>硬件JPEG編解碼基礎(chǔ)知識和<b class='flag-5'>HAL</b><b class='flag-5'>庫</b>API

    STM32H7技術(shù)詳解

    電子發(fā)燒友網(wǎng)站提供《STM32H7技術(shù)詳解.pdf》資料免費下載
    發(fā)表于 08-01 14:49 ?4次下載
    <b class='flag-5'>STM32H7</b>技術(shù)詳解