對 YModem 的接觸,只是在無網絡的情況下,使用SecureCRT軟件輸入loady命令進行串口傳輸,燒寫內核和文件系統。
參看:Hi3516A開發-- 板卡串口燒寫
但你要問我 YModem 是個什么東西,我還真說不上來。
接下來,就開始對YModem的一步步剖析詳解!!
一、YModem簡介
參看:XMODEM/YMODEM PROTOCOL REFERENCE
。。。。 網上查了老半天,怎么感覺沒有一篇講的特別清楚的。
1、YMODEM最低要求
聲稱支持YMODEM的所有程序必須滿足以下最低要求
1) 發送程序應在塊0中發送路徑名(文件名)。
2) 路徑名應為空終止的ASCII字符串,如下所述。
對于那些懶得閱讀整篇文檔的人:
3) 除非特別要求,否則僅發送文件名部分。
4) 沒有發送驅動器號。
5) 不區分大小寫的系統,文件名中的字母只能以小寫形式發送路徑名。
6) 接收程序應使用此路徑名作為接收文件名稱,除非明確覆蓋。
7) 當接收程序成功接收到此塊時打開輸出文件,它應該用ACK確認該塊字符,然后繼續正常的XMODEM文件傳輸以接收器發送的“C”或NAK開頭。
8) 發送程序應使用CRC-16響應“C”路徑名nak,否則使用8位校驗和。
9) 接收程序必須接受128和1024字節的任何混合。它接收的每個文件中的塊。發送節目可能會任意切換1024和128字節塊。
10) 發送程序不得更改未確認的長度塊。
11) 在每個文件的末尾,發送程序應發送EOT最多十個直到收到ACK字符為止。 (這是其中的一部分XMODEM規范。)
12) 傳輸會話的結束應由null(空)表示pathname,此路徑名塊應與其他路徑名相同路徑名塊。
1、簡介
參看:YModem協議簡介
最常用的幾種通信傳輸協議有:XModem、YModem、ZModem等。
XModem是最早的協議之一,幾乎所有的通訊程序支持的文件傳輸協議,它傳輸128字節信息塊。
YModem協議是XModem的改進協議,它最用于調制解調器之間的文件傳輸的協議,具有快速,穩定傳輸的優點。它的傳輸速度比XModem快,這是由于它可以一次傳輸1024字節的信息塊,同時它還支持傳輸多個文件,也就是常說的批文件傳輸。
ZModem速度快于XModem和YModem,而且可以更好地斷開后恢復傳輸。
如今,XModem基本已經被淘汰,最常用的就是YModem與ZModem。為了后面YModem升級程序實現做鋪墊,下面就簡單介紹下YModem協議。
YModem分成YModem-1K與YModem-g。
YModem-1K用1024字節信息塊傳輸取代標準的128字節傳輸,數據的發送回使用CRC校驗,保證數據傳輸的正確性。它每傳輸一個信息塊數據時,就會等待接收端回應ACK信號,接收到回應后,才會繼續傳輸下一個信息塊,保證數據已經全部接收。
YModem-g傳輸形式與YModem-1K差不多,但是它去掉了數據的CRC校驗碼,同時在發送完一個數據塊信息后,它不會等待接收端的ACK信號,而直接傳輸下一個數據塊。正是它沒有涉及錯誤校驗,才使得它的傳輸速度比YModem-1K來得塊。
一般都會選擇YModem-1K傳輸,平時所說的YModem也是指的是YModem-1K。
二、YModem傳輸協議
1、起始幀的數據格式
YModem的起始幀并不直接傳輸文件的數據,而是將文件名與文件的大小放在數據幀中傳輸,它的幀長=3字節數據首部+128字節數據+2字節CRC16校驗碼=33字節。它的數據結構如下:
SOH 00 FF filename[ ] filezise[ ] NUL[ ] CRCH CRCL
其中SOH=0x01,表示這個數據幀中包含著128字節的數據部分;在SOH后面的00 FF,00表示數據幀序號,因為是起始幀,所以它的幀序為00,至于FF,它是幀序的取反,YModem特地這么做是為了給數據是否正確提供一種判斷依據,通過判斷這兩個字節是否為取反關系,就可以知道數據是否傳輸出錯;filename[ ]就是文件名,如文件名foo.c,它在數據幀中存放格式為:66 6F 6F 2E 63 00,一定要在文件名最后跟上一個00,表示文件名結束;filesize[ ]就是文件大小,如上面的foo.c的大小為1KByte,即1024Byte,需要先將它轉化成16進制,即0x400,所以它在數據幀的存放格式為:34 30 30 00,即“400”,同樣的文件大小最后需要跟上00,表示結束;NUL[ ]表示剩下的字節都用00填充,數據部分大小為128字節,除去文件名與文件大小占用的空間外,剩余的字節全部用00填充;CRCH CRCL分別表示16位CRC校驗碼的高8位與低8位。
擴展,什么是補碼?
參看:C語言再學習 – 負數
2、數據幀的數據格式
YModem的數據幀中會預留1024字節空間用來傳輸文件數據,它跟起始幀接收差不多,如下:
STX 01 FE data[1024] CRCH CRCL
其中STX=0x02,表示這幀數據幀后面包含著1024字節的數據部分;STX后面的01 FE,01表示第一幀數據幀,FE則是它的取反,當然如果是第二幀數據的話就是:01 FD;data[1024]表示存放著1024字節的文件數據;CRCH與CRCL是CRC16檢驗碼的高8位與低8位。
如果文件數據的最后剩余的數據在128~1024之前,則還是使用STX的1024字節傳輸,但是剩余空間全部用0x1A填充,如下結構:
STX [num] [~num] data[ ] 1A …1A CRCH CRCL
有一種特殊的情況:如果文件大小小于等于128字節或者文件數據最后剩余的數據小于128字節,則YModem會選擇SOH數據幀用128字節來傳輸數據,如果數據不滿128字節,剩余的數據用0x1A填充這是數據正的結構就變成了:
文件大小小于128字節:
SOH 01 FE data[ ] 1A …1A CRCH CRCL
文件最后剩余數據小于128字節:
SOH [num] [~~num] data[ ] 1A…1A CRCH CRCL
3、結束幀數據結構
YModem的結束幀數據也采用SOH的128字節數據幀,它的結構如下:
SOH 00 FF NUL[128] CRCH CRCL
結束幀同樣以SOH開頭,表示后面跟著128字節大小的數據;結束幀的幀序也認為是00 FF;結束幀的128字節的數據部分不存放任何信息,即NUL[128]全部用00填充。
4、文件傳輸過程
YMODEM批量傳輸會話(1個文件)
YMODEM批量傳輸會話(2個文件)
YMODEM批量傳輸Session-1k塊
YMODEM-g傳輸會話(擴展)
上面傳輸過程中存在許多通信信號,它們的數值與意義如下表所示:
CRC的計算:
參看:STM32開發 – CRC校驗碼
手冊CRC代碼:
/* update CRC */
unsigned short
updcrc(c, crc)
register c;
register unsigned crc;
{
register count;
for (count=8; --count>=0;) {
if (crc & 0x8000) {
crc <<= 1;
crc += (((c<<=1) & 0400) != 0);
crc ^= 0x1021;
}
else {
crc <<= 1;
crc += (((c<<=1) & 0400) != 0);
}
}
return crc;
}
傳輸過程文字描述
接收方發送信號C啟動傳輸會話,然后進入等待(SOH)狀態,如果沒有回應,就會超時退出。
發送方開始時處于等待C過程中。收到C以后,發送攜帶文件名和文件長度的起始幀(SOH)數據包開始信號。進入等待(ACK)狀態
接收方收到SOH起始幀以后,CRC校驗滿足,則發送ACK。發送方接收到ACK,又進入等待“文件傳輸開啟”信號,即重新進入等待“C”的狀態
接收方發送C,表示可以開始數據的傳輸
于是發送方發送數據幀、接收方接收到后回復ACK,如此循環進行數據接收(過程中雙方因為任何異常,如人工終端、通訊故障等都可能造成傳輸中斷).
文件傳輸完畢后,發送方發送EOT信號,接收方收到后,回應NAK
發送方再次發送EOT,接收方回應ACK。
接收方發送C,準備再次文件傳輸
如果是單次文件傳輸,發送方發送傳輸結束幀,接收方回應ACK后,整個傳輸會話結束
接收方和發送方應實現的處理策略
參看:通信協議之YMODEM
共通策略
出錯后需要重復嘗試10次
協議流程應由接收方驅動
使用CAN或ASCII的^X字符來取消傳輸
接收方策略
接收方應該有10s超時機制
只要接收到數據開始,接收方使用1s超時機制來接收每個數據,直到該數據包接收完成。
同步:1.接收到想要的那個數據包并且沒有任何錯誤后才算接收成功,并返回ACK; 2.能夠處理兩條重復的數據包(ACK丟失引起的問題); 3.因為一些特殊原因造成同步出錯應終止傳輸,發送CAN
發送方策略
等待傳輸開始的這段時間,發送方應該設置一個比較長的超時時間,也可以不設置超時時間
文件傳輸結束應該發送EOT到接收方,直到收到一條ACK響應,否則重復發送
三、C語言實現YModem傳輸協議
上面簡單的介紹了一下YModem的傳輸協議。那么問題來了,我想在STM32上用實現,代碼該怎么寫呢?
參看:ST官方代碼——YModem協議部分c代碼分析
參看:STM32F103代碼遠程升級(三)基于YModem協議串口升級程序的實現
下載工程:相關參考工程
網上相關的工程有很多的,接下來就逐一分析一下相關代碼。
1、接收數據
可恥的,直接復制了。。。該博主注釋的相當不錯。我沒什么可說的了。。。
/*******************************************************************************
* @函數名稱 Receive_Packet
* @函數說明 從發送端接收一個數據包
* @輸入參數 data :數據指針
length:長度
timeout :超時時間
* @輸出參數 無
* @返回參數 接收的結果
0: 正常返回
-1: 超時或者數據包錯誤
1: 用戶取消
*******************************************************************************/
static int32_t Receive_Packet (uint8_t *data, int32_t *length, uint32_t timeout)
{
uint16_t i, packet_size;
uint8_t c;
*length = 0;
if (Receive_Byte(&c, timeout) != 0)
{
return -1;//超時返回-1
}
switch (c) //c表示接收到的數據的第一個字節
{
case SOH: //數據包開始
packet_size = PACKET_SIZE;
break;
case STX: //正文開始
packet_size = PACKET_1K_SIZE;
break;
case EOT: //數據包結束
return 0;
case CA: //發送方中止傳輸
if ((Receive_Byte(&c, timeout) == 0) && (c == CA))
{
*length = -1;
return 0;
}
else
{
return -1; //中止傳輸返回-1
}
case ABORT1: //A
case ABORT2: //a
return 1;
default:
return -1;
}
*data = c;
for (i = 1; i < (packet_size + PACKET_OVERHEAD); i ++) //獲取剩下的數據(以字節為單位)
{
if (Receive_Byte(data + i, timeout) != 0)
{
return -1; //接收數據超時
}
}
if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff))
{
return -1;
}
*length = packet_size;
return 0;
}
/*******************************************************************************
* @函數名稱 Ymodem_Receive
* @函數說明 通過 ymodem協議接收一個文件
* @輸入參數 buf: 首地址指針
* @輸出參數 無
* @返回參數 文件長度
*******************************************************************************/
int32_t Ymodem_Receive (uint8_t *buf)
{
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
int32_t i, j, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
//初始化Flash地址變量
FlashDestination = ApplicationAddress;
for (session_done = 0, errors = 0, session_begin = 0; ;)
{
for (packets_received = 0, file_done = 0, buf_ptr = buf; ;)
{
switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
{
case 0:
errors = 0;
switch (packet_length)
{
//發送端終止
case - 1:
Send_Byte(ACK);
return 0;
//結束傳輸
case 0: /* 數據包中序號和補碼不匹配,終止數據發送*/
Send_Byte(ACK);
file_done = 1;
break;
//正常的數據包
default:
if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
{
Send_Byte(NAK); //發送應答NAK,接收失敗要求重發
}
else
{
if (packets_received == 0) //第一包,包含文件名,文件大小
{
//文件名數據包
if (packet_data[PACKET_HEADER] != 0) //去除3個字節的首部,讀取128B的數據包
{
//文件名數據包有效數據區域
//取出文件名--32B用于存儲
上一篇:STM32開發 -- Ublox GPS之設置PUBX
下一篇:STM32開發 -- 看門狗詳解
推薦閱讀最新更新時間:2025-05-01 07:57





設計資源 培訓 開發板 精華推薦
- 新年狂歡盛宴,TI開發板秒不停!??!
- Follow me第二季第3期來啦!與得捷一起解鎖高性能開發板【EK-RA6M5】超能力!
- 你評論,我送禮!《玩轉TI MSP430 Launchpad》TI社區與EEWORLD聯合首發!
- 【社區大講堂】揭秘ADI實驗室電路!
- 嵌入式工程師AI挑戰營(進階):基于RV1106部署InsightFace算法,實現多人的實時人臉識別
- 報名直播贏【手環、攝像頭、雨傘、手機支架】羅德與施瓦茨USB 3.2一致性測試
- 【喚新】微信寄語2018年STM32峰會,贏取ST精美板卡
- 力源帶你了解安森美半導體開發神器——高度靈活的物聯網開發套件( IDK )!看視頻輕松答題享好禮!
- EE團--TI 原裝DK-LM3S9B96開發板 348元(包郵)!