|
/**
******************************************************************************
* @file ymodem_daxia.c
* @author wjandsq@163.com
* @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;
}
}
/*
一、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;
}
|
|