基于VB设计的YModem协议下载器

STM32支持IAP功能,官方提供一个基于Ymodem协议来升级,我们可以借助window超级终端里的Ymodem来传送文件.

这个超级终端用起来相当痛苦,撇开每次下载时候重新加载文件不说,对于win7的电脑每次都得重新设置下串口

解决的办法只有在网上找或者自己编写一个工具了;

我找了很久,只看见一个家伙用C++写了一个工具,但是他没有发布,所以也跟没发一样;

我们来看看基于VB是怎么设计的,首先简单了解下Ymodem协议:

一、Ymodem通信发送数据包的说明
  1 Ymodem通信数据包的格式
    数据包的格式是: 类型 + 序号 + 序号反码 + 数据区(128或1024) + 校验和(两字节)
  2 Ymodem通信数据包的序号增长规律
    数据包的序号从00开始直到255,然后又从00开始。不大于255K字节的数据包,所有发送数据包的
    序号是唯一的。大于255K的数据包,按照1K字节分包,第一个数据包序号是1,第二个数据包的序号是2
    。。。第255个数据包的序号是255,第256个数据包的序号,又从0开始。
  3 Ymodem通信的大小数据包
    Ymodem通信有128字节和1024字节两种类型的数据包,数据包开头有3字节(类型+ 序号+序号反码),
    末尾crc16是2个字节,所以可以说有133字节的小数据包和1029字节的大数据包。
    (1)133字节数据包的特点、字符填充规则
      以SOH(0x01)开始的数据包,数据区是128字节。发送端第一个含有文件信息的数据包是3+128+2 = 133字节。
      发送最后一个数据包时,剩余数据字节数若小于128,则以0x1A填充,仍发送133字节数据包。
    (2)1029字节数据包的特点、字符填充规则
      以STX(0x02)开始的数据包,数据区是1024字节。若发送的文件大于1024字节,文件信息包之后的第一个
      数据包则为1029字节,随后剩余的数据若不小于1024字节,则均以1029大数据包发送。
  4 处理Ymodem通信最后一个数据包时,需要考虑的情况
    Ymodem通信按照1024字节分包,最后一个数据包的大小不会超过1024字节。编程时需要考虑以下几种情况:
    (1)数据个数等于1024字节,按1029字节发送。
    (2)数据个数小于1024字节,但大于128字节,按1029字节发送,无效数据区域以0x1A字符填充。
    (3)数据个数等于128字节,按133字节发送。
    (4)数据个数小于128字节,按133字节发送,无效数据区域以0x1A字符填充。


二、Ymodem通信crc16计算说明:
  1 小包3 + 128 字节,大包 3 + 1024字节,末尾2字节是crc16。
  2 crc16计算从第4字节开始(不包括SOH(0x01) 序号 序号反码三字节),
     小包计算长度128字节,大包计算长度1024字节
  3 最后一个空包,除前三字节外全是0,末尾2个字节也是0,不用计算crc16。


三、Ymodem通信命令说明
  #define MODEM_SOH 0x01 //133字节数据包类型,接收正常回应0x06(含文件信息的第一个包接收正常需回应0x06、0x43)
  #define MODEM_STX 0x02 //1029字节数据包类型,接收正常回应0x06
  #define MODEM_EOT 0x04 //发送文件传输结束命令,接收正常回应0x06、0x43(启动空包发送)
  #define MODEM_ACK 0x06 //发送确认应答,接收方crc校验成功或收到已定义的命令
  #define MODEM_NAK 0x15 //发送重传当前数据包请求,接收方crc校验出错
  #define MODEM_CAN 0x18 //发送取消传输命令,连续发送5个字符
  #define MODEM_C   0x43 //发送大写字母C(三种情况下发送该字符: 1.启动通信握手.2.启动数据包发送.3.启动空包发送)


*/
uint16_t Y_Modem_CRC(uint8_t * buf, uint16_t len)
{
    uint16_t chsum;
    uint16_t stat;
    uint16_t i;
    uint8_t * in_ptr; 
    
    //指向要计算CRC的缓冲区开头
    in_ptr = buf;
    chsum = 0;
    for (stat = len ; stat > 0; stat--) //len是所要计算的长度
    {
        chsum = chsum^(uint16_t)(*in_ptr++) << 8;
        for (i=8; i!=0; i--) {
            if (chsum & 0x8000){
                chsum = chsum << 1 ^ 0x1021;
            } else {
                chsum = chsum << 1;
            }
        }
    }
    return chsum;
}


这段是我直接照抄的,我仔细看了,写的没有问题;

我们来看下VB里的核心代码:

首当其冲的就是CRC16的写法:

Public Function YModemCRC16(ByRef Data() As Byte, ByVal offset As Integer, ByVal length As Integer) As Byte()
    Dim crc As Long
    Dim i As Byte, j As Integer
    Dim CRC16Hi As Byte
    Dim CRC16Lo As Byte
    
    crc = 0
    For j = offset To length + offset - 1 'For j = LBound(Data) To UBound(Data)
        i = &H80
        While (i <> 0)
            If (crc And &H8000) <> 0 Then
                crc = crc * 2
                crc = crc Xor &H1021
            Else
                crc = crc * 2
            End If
            If (Data(j) And i) <> 0 Then
                crc = crc Xor &H1021
            End If
            i = i / 2
            crc = crc Mod 65536
        Wend
    Next j
    
    Dim ReturnData(1) As Byte
    ReturnData(0) = crc \ 256             'CRC高位
    ReturnData(1) = crc Mod 256           'CRC低位
    YModemCRC16 = ReturnData
     
End Function

Const MODEM_SOH = &H1   '133字节数据包类型,接收正常回应0x06(含文件信息的第一个包接收正常需回应0x06、0x43)
Const MODEM_STX = &H2   '1029字节数据包类型,接收正常回应0x06
Const MODEM_EOT = &H4   '发送文件传输结束命令,接收正常回应0x06、0x43(启动空包发送)
Const MODEM_ACK = &H6   '发送确认应答,接收方crc校验成功或收到已定义的命令
Const MODEM_NAK = &H15  '发送重传当前数据包请求,接收方crc校验出错
Const MODEM_CAN = &H18  '发送取消传输命令,连续发送5个字符
Const MODEM_C = &H43    '发送大写字母C(三种情况下发送该字符: 1.启动通信握手.2.启动数据包发送.3.启动空包发送)
Const MODEM_DUMMY = &H1A    '空白填充字节

'发送第一帧数据:类型 + 序号 + 序号反码 + 数据区(128或1024) + 校验和(两字节)
Private Sub Send_FirstFrame()
    Dim send() As Byte
    
    ReDim send(0 To 132) As Byte
    Dim i%, j%
    
    Dim fileName As String, s() As String, b() As Byte
    s = Split(FilePath, "\")
    fileName = s(UBound(s))
    
    b = StrConv(fileName, vbFromUnicode)
    
    PackageNo = 0   '第一包是从0开始
    send(0) = MODEM_SOH
    send(1) = PackageNo
    send(2) = Not PackageNo
    
    '文件名
    For i = 0 To UBound(b)
        send(i + 3) = b(i)
    Next i
    
    send(i + 3) = &H0 '字符串结束符
    
    '文件大小
    b = StrConv(FileSize, vbFromUnicode)
    j = 0
    For i = i + 4 To i + 4 + UBound(b)
        send(i) = b(j)
        j = j + 1
    Next i
    send(i) = &H0 '字符串结束符
    
    Dim crc16() As Byte
    crc16 = YModemCRC16(send, 3, 128)
    send(131) = crc16(0)
    send(132) = crc16(1)
    
    If MSComm.PortOpen = True Then
        MSComm.OutBufferCount = 0   '清空输出寄存器
        MSComm.Output = send       '向输出 缓冲区输出指令
    End If
End Sub

Public Function SendAFrame(ByRef Data() As Byte, ByVal offset As Integer, ByVal length As Integer, ByVal pgno As Byte)
    Dim send() As Byte
    Dim sendlen As Integer
    Dim i%
  
    If length < 1024 Then
        sendlen = 133
    Else
        sendlen = 1029
    End If
    ReDim send(0 To sendlen - 1) As Byte
    
    If sendlen < 1024 Then
        send(0) = MODEM_SOH
    Else
        send(0) = MODEM_STX
    End If
    send(1) = pgno
    send(2) = Not pgno
    
    '数据
    
    If send(0) = MODEM_SOH Then '对于发128包的,要判断是否到文件尾巴了
        For i = 0 To sendlen - 6 'i=81 pgno=24 sendlen=133
            If i + offset < FileSize Then
                send(i + 3) = Data(i + offset)
            Else
                send(i + 3) = MODEM_DUMMY
                isLastPackage = True
            End If
        Next i
    Else
        For i = 0 To sendlen - 6
            send(i + 3) = Data(i + offset)
        Next i
    End If
       
    Dim crc16() As Byte
    crc16 = YModemCRC16(send, 3, sendlen - 5)
    send(sendlen - 2) = crc16(0)
    send(sendlen - 1) = crc16(1)
    
    If MSComm.PortOpen = True Then
        MSComm.OutBufferCount = 0   '清空输出寄存器
        MSComm.Output = send       '向输出 缓冲区输出指令
    End If
    '
    If sendlen < 1024 Then
        FileIdx = FileIdx + 128
    Else
        FileIdx = FileIdx + 1024
    End If
End Function



剩下的工作就交给感兴趣的同志们,或者可以直接下载使用我已经编写好的工具.


YModoem 升级工具下载



猜你喜欢

转载自blog.csdn.net/yunjie167/article/details/78959600