cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 TFTP Server

jowakar122
Senior

I am trying to implement TFTP file transfer on my STM32 module (Nucleo-H723ZG). I have taken reference from:
https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects/STM324xG_EVAL/Applications/LwIP/LwIP_IAP
https://github.com/JoeMerten/Stm32-Tools-Evaluation/tree/master/STM32Cube_FW_F4_V1.9.0/Projects/STM324xG_EVAL/Applications/LwIP/LwIP_TFTP_Server

For file transfer I am using tftpd64 software. I used the software for PC-PC file transfer which were on the same local network and it was successful. But when I am trying to send file from PC-STM32 module I am only receiving write request from the client (PC). After that  I am suppose the get a block 0 acknowledgement from the server (STM32 module) which I am not receiving. 

I tried debugging the code but in the code the acknowledgement is being sent successfully. For more information:
- my ethernet is up

- I have successfully ping the IP address that I have assigned or configured for the module.

- Moreover, I am using UDP so I am able to send messages using Hercules software.

Until the block 0 acknowledgement is received by the client (PC), it will not send the data packets.

20 REPLIES 20
LL0258
Associate

从F4移植到H7的时候尤其需要注意内存问题,low_level_output接口里使用的HAL_ETH_Transmit这个函数是会通过DMA来数据发送的,ETH的DMA是无法访问DTCM-RAM和ITCM-RAM这俩个内存的?解决办法如下:
1、检查你的icf文件里配置的RAM起始地址是不是在这两区域,如果是,这将导致这个HAL_ETH_Transmit接口报总线错误;

2、如果你cf文件里的RAM起始地址不在DTCM-RAM和ITCM-RAM,比如在AXI SRAM,需要注意缓存一致性问题;否则,也会导致ETH无法发送数据,且底层和上层发送接口不会报错;

/**
  * @brief Sends TFTP ACK packet  
  * @PAram upcb: pointer on udp_pcb structure
  * @PAram to: pointer on the receive IP address structure
  * @PAram to_port: receive port number
  * @PAram block: block number
  * @retval: err_t: error code 
  */
static err_t IAP_tftp_send_ack_packet(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, int block)
{
  err_t err;
  struct pbuf *pkt_buf; /* Chain of pbuf's to be sent */

  /* create the maximum possible size packet that a TFTP ACK packet can be */
  char packet[TFTP_ACK_PKT_LEN];
	
	memset(packet, 0, TFTP_ACK_PKT_LEN *sizeof(char));

  /* define the first two bytes of the packet */
  IAP_tftp_set_opcode(packet, TFTP_ACK);

  /* Specify the block number being ACK'd.
   * If we are ACK'ing a DATA pkt then the block number echoes that of the DATA pkt being ACK'd (duh)
   * If we are ACK'ing a WRQ pkt then the block number is always 0
   * RRQ packets are never sent ACK pkts by the server, instead the server sends DATA pkts to the
   * host which are, obviously, used as the "acknowledgement".  This saves from having to sEndTransferboth
   * an ACK packet and a DATA packet for RRQs - see RFC1350 for more info.  */
  IAP_tftp_set_block(packet, block);

  /* PBUF_TRANSPORT - specifies the transport layer */
  //pkt_buf = pbuf_alloc(PBUF_TRANSPORT, TFTP_ACK_PKT_LEN, PBUF_POOL);//,PBUF_RAM);
  pkt_buf = pbuf_alloc(PBUF_TRANSPORT, TFTP_ACK_PKT_LEN, PBUF_RAM);

  if (!pkt_buf)      /*if the packet pbuf == NULL exit and EndTransfertransmission */
  {
#ifdef USE_LCD
    LCD_ErrTrace("Can not allocate pbuf\n");
#endif
    return ERR_MEM;
  }

  /* Copy the original data buffer over to the packet buffer's payload */
  memcpy(pkt_buf->payload, packet, TFTP_ACK_PKT_LEN);

  /* Sending packet by UDP protocol */
  err = udp_sendto(upcb, pkt_buf, to, to_port);

  /* free the buffer pbuf */
  pbuf_free(pkt_buf);

  return err;
}

这个示例里的tftpserver.c的IAP_tftp_send_ack_packet函数里使用的pkt_buf是从PBUF_POOL申请的,默认的lwip配置下,内存池是在静态RAM区域的也就是icf文件里指定的RAM起始地址区域;如果其位于DTCM-RAM和ITCM-RAM则HAL_ETH_Transmit会报总线错误,导致无法回复客户端应答;如果其位于AXI SRAM,而这个示例里并未维护缓冲一致的操作,所以也会导致low_level_output这个接口无法成功发送应答;你会发现,如果pkt_buf改是从PBUF_RAM申请,则可以正常发送应答包,那是因为这个示例里,PBUF_RAM这种申请方式申请的内存位于SRAM3这个区域在MPU配置的时候有配置成不可缓存、不可缓冲 且SRAM位于D2域,ETH的DMA可访问该区域;故可发送成功;
那难道就不能从PBUF_POOL申请pbuf吗?当然是可以的,但需要自行维护缓存一致性;

/**
 * @brief This function should do the actual transmission of the packet. The packet is
 * contained in the pbuf that is passed to the function. This pbuf
 * might be chained.
 *
 * @PAram netif the lwip network interface structure for this ethernetif
 * @PAram p the MAC packet to send (e.g. IP packet including MAC addresses and type)
 * @return ERR_OK if the packet could be sent
 *         an err_t value if the packet couldn't be sent
 *
 * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
 *       strange results. You might consider waiting for space in the DMA queue
 *       to become available since the stack doesn't retry to send a packet
 *       dropped because of memory failure (except for the TCP timers).
 */
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
    uint32_t i = 0;
    struct pbuf *q;
    err_t errval = ERR_OK;
    ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT];

    memset(Txbuffer, 0, ETH_TX_DESC_CNT * sizeof(ETH_BufferTypeDef));

    for (q = p; q != NULL; q = q->next)
    {
        if (i >= ETH_TX_DESC_CNT)
            return ERR_IF;

        SCB_CleanInvalidateDCache_by_Addr((uint32_t *)q->payload, q->len);
        Txbuffer[i].buffer = q->payload;
        Txbuffer[i].len = q->len;

        if (i > 0)
        {
            Txbuffer[i - 1].next = &Txbuffer[i];
        }

        if (q->next == NULL)
        {
            Txbuffer[i].next = NULL;
        }

        i++;
    }

    TxConfig.Length = p->tot_len;
    TxConfig.TxBuffer = Txbuffer;

    HAL_StatusTypeDef ret = HAL_ETH_Transmit(&EthHandle, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT);
    if (ret != 0)
    {
        __NOP();
    }

    return errval;
}

关键在205行的清除并使cache失效从而保证,dma取到了正确的数据;这里不建议直接不开启D-chche,因为这样无法发挥FTM32H7最大的性能,以上是我的调试心得,希望能帮到你,如有谬误之处,望批评指正,共勉之。