STM32F4 IAP BOOTLOADER YMODEM_stm32f4 iap ymodem-程序员宅基地

技术标签: 单片机ARM  

原文  http://www.51hei.com/bbs/dpj-32461-1.html

YModem协议简介 

http://ziye334.blog.163.com/blog/static/224306191201481010478799/

ymodem协议c实现

 


最常用的几种通信传输协议有:XModem、YModem、ZModem等。

    XModem是最早的协议之一,几乎所有的通讯程序支持的文件传输协议,它传输128字节信息块。    
    YModem协议是XModem的改进协议,它最用于调制解调器之间的文件传输的协议,具有快速,稳定传输的优点。它的传输速度比XModem快,这是由于它可以一次传输1024字节的信息块,同时它还支持传输多个文件,也就是常说的批文件传输。
    ZModem速度快于XModem和YModem,而且可以更好地断开后恢复传输。

    如今,XModem基本已经被淘汰,最常用的就是YModem与ZModem。为了后面YModem升级程序实现做铺垫,下面就简单介绍下YModem协议。YModem的说明手册在http://pauillac.inria.fr/~doligez/zmodem/ymodem.txt网站上可以查看。
    YModem分成YModem-1K与YModem-g。
    YModem-1K用1024字节信息块传输取代标准的128字节传输,数据的发送回使用CRC校验,保证数据传输的正确性。它每传输一个信息块数据时,就会等待接收端回应ACK信号,接收到回应后,才会继续传输下一个信息块,保证数据已经全部接收。
    YModem-g传输形式与YModem-1K差不多,但是它去掉了数据的CRC校验码,同时在发送完一个数据块信息后,它不会等待接收端的ACK信号,而直接传输下一个数据块。正是它没有涉及错误校验,才使得它的传输速度比YModem-1K来得块。

    一般都会选择YModem-1K传输,平时所说的YModem也是指的是YModem-1K。下面就讲讲它的传输协议。


st官方有demo可以参考

STM32F407的源代码 http://download.csdn.net/detail/liang890319/9901107

移植到其他板子https://www.dhlx.wang/STM32F411VET6/Simple_transplant_IAP_STSW-STM32067_to_STM32F411VET6.html

STM32F1XX相关代码资源

IAP+YMODEM+CRC16+AES256+PC端软件+hex合并

http://www.openedv.com/thread-78079-1-1.html



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位。

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、文件传输过程
    文件的传输过程,以具体的例子说明。把foo.c,大小为4196Byte(16进制为0x1064)的文件作为传输的对象,则它的传输过程如下:
         发送端                                                                             接收端
 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    C
SOH 00 FF "foo.c" "1064'' NUL[118] CRC CRC >>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<     ACK
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<     C
STX 01 FE data[1024] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>>      
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    ACK
STX 02 FD data[1024] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    ACK
STX 03 FC data[1024] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    ACK
STX 04 FB data[1024] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    ACK
SOH 05 FA data[100]  1A[28] CRC CRC>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    ACK
EOT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    NAK
EOT>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    ACK
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    C
SOH 00 FF NUL[128] CRC CRC >>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<    ACK

YModem的传输过程就是上面所示。但是上面传输过程中存在许多通信信号,它们的数值与意义如下表所示:
 符号     数值     含义
 SOH     0x01     128字节数据包
 STX     0x02     1024字节数据包
 EOT     0x04     结束传输
 ACK     0x06     回应
 NAK     0x15     不回应
 CA     0x18     传输中止
 C     0x43     请求数据包
   
还是有几点需要说明下:
    1)EOT信号由发送端发送
    2)CA中止传输信号也有发送端发送
    3)C的含义在英文的数据手册上的意思有点难以理解,我个人理解成请求数据包,如开始传输的发送C请求起始帧数据,然后再发送C请求文件数据帧,最后有发送一次C请求结束帧!

5、CRC的计算
    YModem的采用的是CRC16-CCITT欧洲版本的CRC校验,它的生成多项式为:x16+x12+x5+1,具体的CRC的计算算法见我的《CRC16校验的C代码实现》一文。


超级终端 SecureCRT 可以按照Ymoden 协议发送文件.目前在单片机IAP固件升级模式中常常利用超级终端的Ymodem 协议发送新固件包实现用户自定义Bootload 功能。

Ymodem协议是一种发送并等待的协议。即发送方发送一个数据包以后,都要等待接收方的确认。如果是ACK信号,则可以发送新的包。如果是NAK信号,则重发或者错误退出。

2、文件传输过程

文件传输过程的开启:

(1)开启是由接收方开启传输,它发一个大写字母C开启传输。然后进入等待(SOH)状态,如果没有回应,就会超时退出。然后继续发送大写字母C等待传输。

(2)发送方开始时处于等待过程中,等待C。收到C以后,发送(SOH)数据包开始信号,发送序号(00),补码(FF),“文件名”,“空格”“文件大小”“除去序号外,补满128字节”,CRC校验两个字节。进入等待(ACK)状态。

(3)接收方收到以后,CRC校验满足,则发送ACK。发送方接收到ACK,又进入等待“文件传输开启”信号,即重新进入等待“C”的状态。准备接收下一个包。

Ymodem 协议中第一包是传输文件信息包,一般采用128字节(1024字节,也可以实现,不过比较浪费资源)
一个完整包数据内容(SOH模式为例)为 包头(3字节)+ 数据段(128字节)+ CRC16校验码(2字节)。所以整包长度是128+5 =133字节,现举例如下(16进制):

01 00 FF 41 42 43 2E 62 69 6E 00 31 30 32 34 20 00 00 ...... 09 E8

第1字节 : 01  表示SOH模式,此包数据段大小为128字节,如果该位置是02,表示STX模式,此包数据段大小为1024字节。

第2字节: 00 包序列号为0,以后发送或接收的包序列号要依次递增。

第3字节 : FF 包序列号的补码(与第2字节00对应)。在Ymodem接收中会对第2,第3字节做检验。所以这两个字节数据要按照协议保持一致。

第4字节到第131字节:数据段内容, 41 42 43 2E 62 69 6E 表示文件名(ASCII码 ABC.bin), 00 为文件名结束标志。31 30 32 34 表示文件大小 (ASCII码 1024字节), 20(ASCII码  空格符 '\0')为文件大小结束标志。此后到数据段的128字节全部填 00.

第132,133字节 : 09 E8 为CRC16校验码,此校验码只计算数据段128字节内容,不计算包头的3字节。此处09 E8 只是举例。

(4)文件发送完以后,发送方发出一个“EOT”信号,接收方也以“ACK”回应。

然后接收方会再次发出“C”开启另一次传输,若接着发送方会发出一个“全0数据包”,接收方回应“ACK”后,本次通信正式结束。
注意:“全0数据包”,包头并不为 0, 包头数据可写 01 00 FF.


一、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;
}


/**
  ******************************************************************************
  * @file    ymodem_daxia.c
  * @author  [email protected]
  * @version V2.0.2
  * @date    2014.12.06
  * @brief   Y-Modem 协议文件接收 (适用于ARM及X86平台)
  ******************************************************************************
  * @copy
  *
  * Copyright (c) 2013-2014
  *
  */
extern "C" {
#include "crc.h"
#include "crc16.h"
}
#ifndef STM32Fxxx
#include <stdint.h>
#include <stdbool.h>
#endif

#ifdef STM32Fxxx
// 发送字符串到USB端点(USB虚拟串口)
void VCP_MI_02_IN(uint8_t *buf, uint8_t count);
void VCP_MI_00_IN(uint8_t *buf, uint8_t count);
#else
// 发送字符串到串口1(发送完毕关闭串口1)
void SendStringWithClose(unsigned char * buf, unsigned short len);
#endif
uint16_t Y_Modem_CRC(uint8_t * buf, uint16_t len);
#define     __IO    volatile
/* Includes ------------------------------------------------------------------*/
//#include "ymodem_daxia.h"
#ifdef STM32Fxxx
#include "stm32_flash_read_write.h"
#include "usb_lib.h"
#endif
/* Extern function -----------------------------------------------------------*/
#ifdef STM32Fxxx
extern void NVIC_Configuration(void);
extern void NVIC_DISABLE(void);
extern void ChipHalInit(void);
#endif
/* Extern variables ----------------------------------------------------------*/
extern __IO uint8_t  USART1_Tx_Buffer[];
extern __IO uint16_t USART1_TxLength;
extern __IO uint16_t USART1_TxCount;
extern __IO uint8_t  USART1_Rx_Buffer [];
extern __IO uint8_t  USART1_Rx_Buffer2[];
extern __IO uint32_t USART1_RxCount;
extern __IO uint16_t USART1_Rx_ptr_in;

extern __IO uint16_t Y_Modem_TMP1;
extern __IO uint16_t Over_Time_Cnt;
extern __IO uint32_t into_app_cnt;
extern uint16_t CRC16;
extern uint16_t CRC16_Tmp;
extern uint8_t  FileName[];
extern uint32_t FileLength;
extern uint32_t Write_Counter;
extern __IO bool Flag_App_Rece_OK;
extern __IO bool Flag1_1Ms;    // 系统定时器1MS标志1
extern __IO bool Flag2_1Ms;    // 系统定时器1MS标志2

/* Private define ------------------------------------------------------------*/
#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.启动空包发送)
#define TIME_OVER_SETUP1  10000
#define TIME_OVER_SETUP2  30000
#ifdef STM32Fxxx
/*
函数功能: 大于或等于2K的程序数据,写入Flash
    小于2048不写入,直接返回
*/
void Flash_Write_2K(void)
{
uint32_t tmp;
if(USART1_RxCount > Write_Counter){
  tmp = USART1_RxCount - Write_Counter;
  if(tmp >= 2048){
   iap_write_appbin(FLASH_APP1_ADDR + Write_Counter, (uint8_t *)USART1_Rx_Buffer2, tmp);
   Write_Counter += tmp;
  }
}
}
/*
函数功能: 写入小于或等于128字节的程序数据
    若最后一个包小于1024字节, 则发送128字节的数据包,以(SOH)开始
参数列表:

*/
void Flash_Write_128(void)
{
uint32_t tmp;
if(USART1_RxCount > Write_Counter){
  tmp = USART1_RxCount - Write_Counter;
  if(tmp <= 128){
           iap_write_appbin(FLASH_APP1_ADDR + Write_Counter, (uint8_t *)USART1_Rx_Buffer2, tmp);                           
           Write_Counter += tmp;
        }
    }
}
/* 虚拟串口MI_02接收监控 */
void VCP_MI_02_IN (uint8_t *buf, uint8_t count){
    USB_SIL_Write(EP4_IN, &buf[0], count);
    SetEPTxValid(ENDP4);
}
/* 虚拟串口MI_00接收监控 */
void VCP_MI_00_IN (uint8_t *buf, uint8_t count){
USB_SIL_Write(EP1_IN, &buf[0], count);
SetEPTxValid(ENDP1);
}
#endif

// 取消传输
void Cancel_Transmission(void)
{
uint8_t USB_IN_buf[5];
  // 取消传输
USB_IN_buf[0] = MODEM_CAN;
USB_IN_buf[1] = MODEM_CAN;
USB_IN_buf[2] = MODEM_CAN;
USB_IN_buf[3] = MODEM_CAN;
USB_IN_buf[4] = MODEM_CAN;
#ifdef STM32Fxxx
USB_SIL_Write(EP4_IN, &USB_IN_buf[0], 5);
SetEPTxValid(ENDP4);
#else
#endif
}
// 回复确认
void Send_ACK(void)
{
uint8_t USB_IN_buf[2];
// 回复确认
USB_IN_buf[0] = MODEM_ACK;
#ifdef STM32Fxxx
USB_SIL_Write(EP4_IN, &USB_IN_buf[0], 1);
SetEPTxValid(ENDP4);
#endif
}
// 回复继续
void Send_MODEM_C(void)
{
uint8_t USB_IN_buf[1];
USB_IN_buf[0] = MODEM_C;
#ifdef STM32Fxxx
USB_SIL_Write(EP4_IN, &USB_IN_buf[0], 1);
SetEPTxValid(ENDP4);
#endif
}
// 回复确认、继续
void Send_ACK_Continue(void)
{
uint8_t USB_IN_buf[2];
// 回复确认、继续
USB_IN_buf[0] = MODEM_ACK;
USB_IN_buf[1] = MODEM_C;
#ifdef STM32Fxxx
USB_SIL_Write(EP4_IN, &USB_IN_buf[0], 2);
SetEPTxValid(ENDP4);
#endif
}
/*
函数功能: 为实时响应, 需连续调用,以尽快处理Y_Modem协议接收的数据
参数列表:
Flag_Timer_1MS    系统1ms标志传入函数, 但函数不对该标志做改动
Y_Modem_TMP1      全局变量
USART1_Rx_Buffer  4K byte  4K文件内容接收缓冲
USART1_Rx_ptr_in  USART1_Rx_Buffer  数据指针当前位置
USART1_Rx_Buffer2 4K byte  4K文件内容写入Flash缓冲
USART1_RxCount    USART1_Rx_Buffer2 数据指针当前位置
*/
extern uint8_t Flag_App_Update;
static uint16_t temp_cnt;
static uint16_t temp_cnt2 = 0;
void Y_Modem_Receive(void)
{
uint32_t Y_Modem_TMP2;
uint32_t Y_Modem_TMP3;
uint32_t Y_Modem_TMP4;
uint32_t Y_Modem_TMP5;
uint8_t USB_IN_buf[5];
#ifndef STM32Fxxx
Flag2_1Ms = true;
#endif
switch(Y_Modem_TMP1){
  case 0:
   break;
  case 1:
    if(Flag2_1Ms == true){
     Flag2_1Ms = false;
     if(Flag_App_Update == 1){
      // 更新标志上电随机设置为1或从用户程序跳转过来
      // 上电随机设置为1的情况不能启动,所以存在bug
     }else{
      // 更新标志上电随机设置为非零值
      if (++temp_cnt2 > 200){
     temp_cnt2  = 0;
                    Flag_App_Update = 0;
                  }
              }
              if (++temp_cnt > 2000){
      temp_cnt = 0;
      Send_MODEM_C();
     #ifdef STM32Fxxx
      USB_IN_buf[0] = *((uint8_t *)0x20004000);
      VCP_MI_00_IN(&USB_IN_buf[0],1);
     #endif
                  // 4K 缓冲清零
                  for(Y_Modem_TMP2 = 0; Y_Modem_TMP2 < 0x1000; ++Y_Modem_TMP2) {
                      USART1_Rx_Buffer[Y_Modem_TMP2] = 0;
                      USART1_Rx_Buffer2[Y_Modem_TMP2] = 0;
                  }   
                  USART1_Rx_ptr_in = 0;
                  USART1_RxCount   = 0;
                  Write_Counter = 0;               
              }
          }            
          if(USART1_Rx_ptr_in > 0){
              if(USART1_Rx_Buffer[0] == MODEM_SOH){
                  if(USART1_Rx_ptr_in >= 133){
                      Y_Modem_TMP1 = 2;
                  }
              }
          }
         break;
     case 2:
          // 第2步,处理128字节数据块(传输文件名,文件大小信息)
   if(Flag2_1Ms == true){
    Flag2_1Ms = false;
    if (++Over_Time_Cnt > TIME_OVER_SETUP1){
     Cancel_Transmission(); // 取消传输
     Y_Modem_TMP1 = 0;
     Over_Time_Cnt = 0;
      #ifdef STM32Fxxx
     USB_IN_buf[0] = 0x01;
     VCP_MI_00_IN(&USB_IN_buf[0],1);
    #endif
    }
         }
         if(USART1_Rx_ptr_in > 0){
    // 第2步,如果有数据传输,则进行处理
             // MODEM_SOH 128字节数据块
             if(USART1_Rx_Buffer[0] == MODEM_SOH){
                 if(USART1_Rx_ptr_in >= 133){
                     CRC16 = Y_Modem_CRC((uint8_t *)&USART1_Rx_Buffer[3], 128);
                     CRC16_Tmp  = USART1_Rx_Buffer[131];
                     CRC16_Tmp  = CRC16_Tmp << 8;
                     CRC16_Tmp |= USART1_Rx_Buffer[132];                           
            
                     if(CRC16_Tmp == CRC16){
                         // 获取文件名
                         for(Y_Modem_TMP2 = 0; Y_Modem_TMP2 < 128; ++Y_Modem_TMP2) {
                             FileName[Y_Modem_TMP2] = 0;
                         }
                         Y_Modem_TMP3 = 0;
                         for(Y_Modem_TMP2 = 3; Y_Modem_TMP2 < 128; ) {
                             if(USART1_Rx_Buffer[Y_Modem_TMP2]!= 0){
                                 FileName[Y_Modem_TMP3] = USART1_Rx_Buffer[Y_Modem_TMP2];
                             } else {
                                 ++Y_Modem_TMP2;
                                 break;
                             }
                             ++Y_Modem_TMP3;
                             ++Y_Modem_TMP2;
                         }
                         // 获取文件长度
                         for(FileLength = 0; Y_Modem_TMP2 <= 133; ++Y_Modem_TMP2) {
                             if(USART1_Rx_Buffer[Y_Modem_TMP2]!= 0){
                                 if((USART1_Rx_Buffer[Y_Modem_TMP2] >= 0x30)
                                     && (USART1_Rx_Buffer[Y_Modem_TMP2] <= 0x39)){
                                     FileLength = FileLength * 10 + USART1_Rx_Buffer[Y_Modem_TMP2] - 0x30;
                                 } else {
                                     break;
                                 }
                             } else {
                                 break;
                             }
                         }
                          // STM32F103C8 只有64K 大于64K 则取消传输
                          if(FileLength > 1024 * 64){
                              Cancel_Transmission(); // 取消传输
                           
                              Y_Modem_TMP1 = 0;
                          } else {
                              USART1_Rx_ptr_in = 0;
                           
                              Send_ACK_Continue();
                           
                              Over_Time_Cnt = 0;
                              Y_Modem_TMP1 = 3;
                          }
                          into_app_cnt = 0; // 延迟计数清0
                     }                           
                 }
             }
         }
         break;
     case 3:
          // 第3步,接收并写入程序数据,以2K的字节数据块写入
   if(Flag2_1Ms == true){
          Flag2_1Ms = false;
    if (++Over_Time_Cnt > TIME_OVER_SETUP2){
     Cancel_Transmission(); // 取消传输
     Y_Modem_TMP1 = 0;
        into_app_cnt = 0; // 延迟计数清0
          }
         }      
         if(USART1_Rx_ptr_in > 0){
              // MODEM_STX 1029字节数据块
             if(USART1_Rx_Buffer[0] == MODEM_STX){
                  // 最后一包数据包大于128且小于1024字节时,补充以0x1A
                 if(USART1_Rx_ptr_in >= 1029){
                     CRC16 = Y_Modem_CRC((uint8_t *)&USART1_Rx_Buffer[3], 1024);
                     CRC16_Tmp  = USART1_Rx_Buffer[1027];
                     CRC16_Tmp  = CRC16_Tmp << 8;
                     CRC16_Tmp |= USART1_Rx_Buffer[1028];                           
            
                     if(CRC16_Tmp == CRC16){
                          Y_Modem_TMP5 = USART1_RxCount - Write_Counter;
                         for(Y_Modem_TMP2 = 0; Y_Modem_TMP2 < 1024; ++Y_Modem_TMP2) {
                              Y_Modem_TMP4 = USART1_RxCount;
                              Y_Modem_TMP4 = Y_Modem_TMP4 + Y_Modem_TMP2;
                             if(Y_Modem_TMP4 < FileLength){
                                 USART1_Rx_Buffer2[Y_Modem_TMP5++] = USART1_Rx_Buffer[Y_Modem_TMP2+3];
                             }else{
                                  // 最后一个数据包大于128且小于1024字节时,补充的0x1A不复制,退出循环
                                 break;
                             }
                             USART1_Rx_Buffer[Y_Modem_TMP2] = 0;
                         }
       USART1_RxCount += Y_Modem_TMP2;
      #ifdef STM32Fxxx
       Flash_Write_2K(); // USART1_RxCount - Write_Counter < 2048, 调用该函数无效
      #endif
       for(; Y_Modem_TMP2 <= 1029; ++Y_Modem_TMP2) {
         USART1_Rx_Buffer[Y_Modem_TMP2] = 0;
       }
       USART1_Rx_ptr_in = 0;
       Send_ACK(); // 回复确认
       into_app_cnt = 0; // 延迟计数清0
      }else{
        // 出现CRC错误
       }
     }
    }
     // MODEM_SOH 133字节数据块
    if(USART1_Rx_Buffer[0] == MODEM_SOH){
      // 最后一包数据不足128字节时,补充以0x1A,总计补满133字节
     if(USART1_Rx_ptr_in >= 133){
      CRC16 = Y_Modem_CRC((uint8_t *)&USART1_Rx_Buffer[3], 128);
      CRC16_Tmp  = USART1_Rx_Buffer[131];
      CRC16_Tmp  = CRC16_Tmp << 8;
      CRC16_Tmp |= USART1_Rx_Buffer[132];
      if(CRC16_Tmp == CRC16){
       Y_Modem_TMP5 = USART1_RxCount - Write_Counter;
       for(Y_Modem_TMP2 = 0; Y_Modem_TMP2 < 128; ++Y_Modem_TMP2) {
        Y_Modem_TMP4  = USART1_RxCount;
        Y_Modem_TMP4 += Y_Modem_TMP2;
        if(Y_Modem_TMP4 < FileLength){
         USART1_Rx_Buffer2[Y_Modem_TMP5++] = USART1_Rx_Buffer[Y_Modem_TMP2+3];
        }else{
         break;
        }
        USART1_Rx_Buffer[Y_Modem_TMP2] = 0;
       }
       USART1_RxCount += Y_Modem_TMP2;
      #ifdef STM32Fxxx
       Flash_Write_128();
      #endif
       for(; Y_Modem_TMP2 <= 133; ++Y_Modem_TMP2) {
        USART1_Rx_Buffer[Y_Modem_TMP2] = 0;
       }
       USART1_Rx_ptr_in = 0;
       Send_ACK(); // 回复确认
       into_app_cnt = 0; // 延迟计数清0
       }
                 }
             }
              // 文件传输结束命令
             if(USART1_Rx_Buffer[0] == MODEM_EOT){
     // 文件传输结束
     if(FileLength == USART1_RxCount){
      Flag_App_Rece_OK = true;
     }
     USART1_Rx_ptr_in = 0;
     Send_ACK_Continue(); // 回复确认、继续
     Y_Modem_TMP1 = 4;
     into_app_cnt = 0;    // 延迟计数清0
    }
              // 取消传输命令
             if(USART1_Rx_Buffer[0] == MODEM_CAN){
                 // 取消传输
                 for(Y_Modem_TMP2 = 0; Y_Modem_TMP2 <= 1029; ++Y_Modem_TMP2) {
                     USART1_Rx_Buffer[Y_Modem_TMP2] = 0;
                 }
                 USART1_Rx_ptr_in = 0;
                 Y_Modem_TMP1 = 4;
                 into_app_cnt = 0; // 延迟计数清0
             }
         }
         break;
     case 4:
         if(Flag2_1Ms == true){
             Flag2_1Ms = false;
          if (++Over_Time_Cnt > TIME_OVER_SETUP1){
     Cancel_Transmission(); // 取消传输
     Y_Modem_TMP1 = 0;
     into_app_cnt = 0; // 延迟计数清0
          }
          }
          // MODEM_SOH 133字节数据块
          if(USART1_Rx_Buffer[0] == MODEM_SOH){
              // 最后一包数据不足128字节时,补充以0x1A,总计补满133字节
              if(USART1_Rx_ptr_in >= 133){
                  // 最后一个空数据包,133字节,前3个字节是01 00 FF,其余是0
                  CRC16_Tmp = 0;
                  for(Y_Modem_TMP2 = 3; Y_Modem_TMP2 <= 133; ++Y_Modem_TMP2) {
                      CRC16_Tmp += USART1_Rx_Buffer[Y_Modem_TMP2];
                  }
                  if(CRC16_Tmp==0){
                      if(Flag_App_Rece_OK){
                          // 未写完的程序数据写入,[1-128) OR [1024,2048)
                          if(FileLength > Write_Counter){
                              Y_Modem_TMP4 = FileLength - Write_Counter;
         if(Y_Modem_TMP4){
         #ifdef STM32Fxxx
          iap_write_appbin(FLASH_APP1_ADDR + Write_Counter, (uint8_t *)USART1_Rx_Buffer2, Y_Modem_TMP4);
         #endif
                                  Write_Counter += Y_Modem_TMP4;
                              }
                          }
                      }
       Send_ACK();  // 回复确认,结束文件传输
       Y_Modem_TMP1 = 5;     // 文件传输结束
      }
      into_app_cnt = 0; // 延迟计数清0
              }
          }
         break;
     case 5:
   if(Flag2_1Ms == true){
    Flag2_1Ms = false;
   #ifndef STM32Fxxx
    if (++Over_Time_Cnt > 500){
   #else
    if (++Over_Time_Cnt > 5000){
   #endif
     Over_Time_Cnt = 0;
     Y_Modem_TMP1 = 0;
     into_app_cnt = 0; // 延迟5秒
    }
   }
   break;
  default:
  #ifdef STM32Fxxx
   __NOP();
   __NOP();
   __NOP();
  #endif
   break;
}   
}



版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/liang890319/article/details/75248144

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签