STM32物聯網之TFTP檔案傳輸

來源:互聯網
上載者:User

標籤:des   style   blog   http   io   ar   color   os   使用   

  • 感言:專註物聯網應用開發,分享物聯網技術經驗。
  • 軟體平台:IAR6.5
  • TCP/IP協議棧:LWIP1.4.1
  • 硬體平台:STM32F103C8T6有線通訊板(點擊這裡可以購買)

1、TCP/IP協議棧LWIP

1.1、LWIP認識

       LWIP是瑞典電腦科學院(SICS)的Adam Dunkels 開發的一個小型開源的TCP/IP協議棧,是Light Weight (輕型)IP協議,有無作業系統的支援都可以運行。LWIP提供三種API,分別是RAW API、LWIP API 、BSD API。其中RAW API把協議棧和應用程式放到一個進程裡邊,該介面基於函數回調技術來實現的,適合於無作業系統的場合運行,如單片機。本文使用的就是LWIP的RAW API來實現網路層的通訊的。

1.2、TFTP在LWIP中的實現

        關於LWIP的移植,就不在本文中多講,讀者可以在網上找到眾多資料或在另外的專題中再詳細講解,在這裡我們專註其應用。在LWIP中實現一個TFTP伺服器非常簡單,根據RAW API的編程方法,在初始化的時候建立一個UDP PCB(TFTP使用UDP協議通訊),且綁定69連接埠(TFTP預設通訊連接埠),最後指定該UDP PCB的資料接收回呼函數即可。

以上的建立TFTP伺服器的方法需要在LWIP初始化,並啟動網卡後進行:

 LwIP_Config();    printf("ipaddr:%d.%d.%d.%d\r\n", net_ip[0], net_ip[1], net_ip[2], net_ip[3]);    tftpd_init();
在tftpd_init函數中建立TFTP伺服器:

void tftpd_init(void){  err_t err;  unsigned port = 69;  /* create a new UDP PCB structure  */  UDPpcb = udp_new();  if (!UDPpcb)  {  /* Error creating PCB. Out of Memory  */    return;  }  /* Bind this PCB to port 69  */  err = udp_bind(UDPpcb, IP_ADDR_ANY, port);  if (err != ERR_OK)  {    /* Unable to bind to port  */    return;  }  /* TFTP server start  */  udp_recv(UDPpcb, recv_callback_tftp, NULL);}
OK,到這裡就完成了TFTP伺服器在LWIP中建立起來了,接下來的主要事情就是根據TFTP協議進行協議解釋、資料處理。

2、TFTP協議分析

2.1、TFTP通訊基本流程(摘自網路)


2.2、TFTP報文格式(摘自網路)


2.3、TFTP協議理解

     從以上兩張圖片,我們瞭解到什麼有用資訊呢?

  • 每一次檔案傳輸,首先需要發起一個請求,根據請求幀的作業碼判斷是讀檔案還是寫檔案。
  • 每一幀都有一個作業碼用來標識讀寫。
  • 資料包的長度有一個塊編號用來表示資料包的順序。
  • 資料包中的資料長度為512個位元組(在後面的軟體我們可以瞭解到這個長度是可以設定的)。

3、實現TFTP檔案傳輸

3.1、檔案傳輸通訊協定實現

有了第2節的協議分析,我們基本瞭解了TFTP通訊的協議,在這裡,我們來實現TFTP的伺服器端代碼。

在監聽的回呼函數被觸發調用時,首先從請求幀中擷取作業碼:

typedef enum {  TFTP_RRQ = 1,  TFTP_WRQ = 2,  TFTP_DATA = 3,  TFTP_ACK = 4,  TFTP_ERROR = 5} tftp_opcode;tftp_opcode tftp_decode_op(char *buf){  return (tftp_opcode)(buf[1]);}
根據作業碼進行相應的處理:

tftp_opcode op = tftp_decode_op(pkt_buf->payload);switch (op)  {    case TFTP_RRQ:    /* TFTP RRQ (read request) */      tftp_extract_filename(FileName, pkt_buf->payload);      tftp_process_read(upcb, addr, port, FileName);      break;    case TFTP_WRQ:    /* TFTP WRQ (write request) */       tftp_extract_filename(FileName, pkt_buf->payload);      //在這個加入擦FALSH       tftp_process_write(upcb, addr, port, FileName);      break;    default:      /* sEndTransfera generic access violation message */      tftp_send_error_message(upcb, addr, port, TFTP_ERR_ACCESS_VIOLATION);      /* TFTP unknown request op */      /* no need to use tftp_cleanup_wr because no "tftp_connection_args" struct has been malloc'd   */      udp_remove(upcb);      break;  }
這裡當STM32接收到寫操作請求時,通過tftp_extract_filename函數把檔案名稱讀出來。接下來通過tftp_process_write函數來完成檔案資料的傳輸:

int tftp_process_write(struct udp_pcb *upcb, struct ip_addr *to, int to_port, char *FileName){  ... ...  udp_recv(upcb, wrq_recv_callback, args);  tftp_send_ack_packet(upcb, to, to_port, args->block);  return 0;}
設定資料轉送回呼函數後,根據TFTP協議,回複一個ACK,之後TFTP用戶端開始傳輸檔案資料,從而觸發調用wrq_recv_callback
void wrq_recv_callback(void *_args, struct udp_pcb *upcb, struct pbuf *pkt_buf, struct ip_addr *addr, u16_t port){  tftp_connection_args *args = (tftp_connection_args *)_args;  int n = 0;  if (pkt_buf->len != pkt_buf->tot_len)  {    return;  }  /* Does this packet have any valid data to write? */  if ((pkt_buf->len > TFTP_DATA_PKT_HDR_LEN) &&      (tftp_extract_block(pkt_buf->payload) == (args->block + 1)))  {    /* 在這裡處理接收到的資料pkt_buf->payload */    /* update our block number to match the block number just received */    args->block++;    /* update total bytes  */    (args->tot_bytes) += (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);    /* This is a valid pkt but it has no data.  This would occur if the file being       written is an exact multiple of 512 bytes.  In this case, the args->block       value must still be updated, but we can skip everything else.    */  }  else if (tftp_extract_block(pkt_buf->payload) == (args->block + 1))  {    /* update our block number to match the block number just received  */    args->block++;  }  /* SEndTransferthe appropriate ACK pkt (the block number sent in the ACK pkt echoes   * the block number of the DATA pkt we just received - see RFC1350)   * NOTE!: If the DATA pkt we received did not have the appropriate block   * number, then the args->block (our block number) is never updated and   * we simply sEndTransfera "duplicate ACK" which has the same block number as the   * last ACK pkt we sent.  This lets the host know that we are still waiting   * on block number args->block+1. */  tftp_send_ack_packet(upcb, addr, port, args->block);  /* If the last write returned less than the maximum TFTP data pkt length,   * then we've received the whole file and so we can quit (this is how TFTP   * signals the EndTransferof a transfer!)   */  if (pkt_buf->len < TFTP_DATA_PKT_LEN_MAX)  {    tftp_cleanup_wr(upcb, args);    pbuf_free(pkt_buf);  }  else  {    pbuf_free(pkt_buf);    return;  }}
OK!至此STM32就完成了整個TFTP協議檔案的接收。

3.2、儲存檔案資料

接收到完整的檔案資料之後,我們需要把資料寫到STM32的FLASH中,儲存起來。

由於STM32記憶體較小,不可能開闢一個大的記憶體空間把檔案資料儲存起來再寫到FLASH,

所以需要邊接收邊寫FLASH。

首先在接收到寫操作請求後,把儲存地區的FLASH擦除:

    case TFTP_WRQ:    /* TFTP WRQ (write request) */ 。    ... ...      FlashDestination = HtmlDataAddress;  /* Erase the needed pages where the user application will be loaded */      /* Define the number of page to be erased */      NbrOfPage = FLASH_PagesMask(HtmlTotalSize);//擦除HTML地區      /* Erase the FLASH pages */      FLASH_Unlock();      for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)       {         FLASHStatus = FLASH_ErasePage(HtmlSizeAddress + (PageSize * EraseCounter));       }      FLASH_Lock();
在檔案資料轉送過程中,把<=512BYTE的資料寫到FLASH:
     filedata = (uint32_t)pkt_buf->payload + TFTP_DATA_PKT_HDR_LEN;     FLASH_Unlock();     for (n = 0;n < (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);n += 4)     {      /* Program the data received into STM32F10x Flash */      FLASH_ProgramWord(FlashDestination, *(uint32_t*)filedata);       if (*(uint32_t*)FlashDestination != *(uint32_t*)filedata)        {          /* End session */ tftp_send_error_message(upcb, addr, port, FLASH_VERIFICATION_FAILED);         /* close the connection */         tftp_cleanup_wr(upcb, args); /* close the connection */        }        FlashDestination += 4;        filedata += 4;     }     FLASH_Lock();
到這裡,就實現了STM32接收TFTP用戶端傳輸的檔案資料,並儲存到FLASH地址為FlashDestination的地區中。

3.3、示範操作

將通訊板串連到與電腦在同一局域的路由器,並正確配置好IP資訊。在電腦端開啟軟體Tftpd32.exe:


點擊“上傳”按鍵,就會把檔案html.bin檔案發送到STM32通訊板:



可以在STM32通訊板中把檔案內容讀出來使用,在下一篇部落格物聯網WEB開發中會使用TFTP傳輸HTML檔案。

4、TFTP的應用

    TFTP主要是實現檔案傳輸,在韌體升級、程式調試中極大提高效率,有重要的意義。

在WEB的應用開發中會體會到其強大的作用。歡迎關注下一篇關於STM32的WEB開發的部落格。

STM32物聯網之TFTP檔案傳輸

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.