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ā)送/接收描述符 操作有很大的隱患
后面對這些問題詳細描述
原因
問題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è)置的,因為它是標記描述符當前是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ā)送描述符),這兩個寄存器除了標記最后一個描述符的地址,還用于告訴ETH描述符有更新。
問題2 HAL庫的隱患2 沒有處理Cortex-M7的亂序訪問和Cache的問題
正常來說,CPU的亂序執(zhí)行是不會影響外設(shè)操作的。因為外設(shè)寄存器的內(nèi)存類型默認為Device(還要一個類似的屬性為Strongly-ordered),這意味著亂序執(zhí)行無論在怎么亂,它都會保證對這些內(nèi)存正確的順序(在代碼中的執(zhí)行順序)訪問。
但巧了,描述符的存放位置不在這些內(nèi)存區(qū)域,它只能放在D2域的SRAM中(ETH的手只能夠著這些地方),而且這些SRAM的內(nèi)存類型默認為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,相當于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))));//修改!!
}
-
以太網(wǎng)
+關(guān)注
關(guān)注
41文章
5995瀏覽量
180753 -
代碼
+關(guān)注
關(guān)注
30文章
4967瀏覽量
73937 -
HAL庫
+關(guān)注
關(guān)注
1文章
121瀏覽量
7626
原文標題:hal 庫超好用?來看看以太網(wǎng)的bug
文章出處:【微信號:emOsprey,微信公眾號:魚鷹談單片機】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
STM32H7使用HAL庫如何控制外置USB HS PHY進入低功耗?
【STM32H7教程】第57章 STM32H7硬件JPEG編解碼基礎(chǔ)知識和HAL庫API 精選資料分享
STM32H7的DAC基礎(chǔ)知識和HAL庫API
STM32H7的TIM定時器基礎(chǔ)知識和HAL庫
STM32H7的ADC基礎(chǔ)知識和HAL庫API
【STM32H7教程】第19章 STM32H7的GPIO應(yīng)用之按鍵FIFO
STM32H7學習繼續(xù)(STM32H7系列7)含外設(shè)的編程一般流程
【STM32H7教程】第21章 STM32H7的NVIC中斷分組和配置(重要)
STM32H7 串口 空閑中斷 任意長接收 Hal庫 IDLE
【STM32H7教程】第8章 STM32H7的終極調(diào)試組件Event Recorder
【STM32H7教程】第14章 STM32H7的電源,復(fù)位和時鐘系統(tǒng)
【STM32H7教程】第57章 STM32H7硬件JPEG編解碼基礎(chǔ)知識和HAL庫API
stm32H7 HAL庫中存在的bug
評論