目录
一 Ymodem协议简介:
YModem协议是XModem的改进协议,它最常用于调制解调器之间的文件传输的协议,具有快速,稳定传输的优点。它的传输速度比XModem快,这是由于它可以一次传输1024字节的信息块,同时它还支持传输多个文件,也就是常说的批文件传输。
YModem分成YModem-1K与YModem-g。YModem-1K用1024字节信息块传输取代标准的128字节传输,数据使用CRC校验,保证数据传输的正确性。它每传输一个信息块数据时,就会等待接收端回应ACK信号,接收到回应后,才会继续传输下一个信息块,保证数据已经全部接收。
此次我用的就是YModem-1K传输。
Ymodem,数据包=128字节,速度=2~3KB/s
Ymodem-1K,数据包=1024字节,速度=7~8KB/s
二 Ymodem数据格式:
(1)起始帧的数据格式:
YModem的起始帧并不直接传输文件的数据,而是将文件名与文件的大小放在数据帧中传输,它的帧长=3字节数据首部+128字节数据+2字节CRC16校验码=33字节。它的数据结构如下:
SOH 00 FF filename filezise NUL CRCH CRCL
其中SOH=0x01,表示这个数据帧中包含着128个字节的数据(STX表示1024字节,初始帧只有128个),00表示数据帧序号,初始是0,依次向下排,FF是帧序号的取反,filename是要传输的文件名,如USTB_V3_1.0.1.26_NMEA.Bin,它在数据帧中的格式为:55 53 54 42 5F 56 33 5F 31 2E 30 2E 31 2E 32 36 5F 4E 4D 45 41 2E 42 69 6E 00,也就是把ASCII码转成十六进制,但是最后一定要在文件名后加上00,表示文件名的结束;filesize表示文件的大小,如上面的USTB_V3_1.0.1.26_NMEA.Bin大小是132KB,也就是135168Byte,转换成十六进制就是0x21000,它在数据帧中的格式就是32 31 30 30 30 00,也就是ASCII的“21000”,同样最后要加上00表示结束,NUL就是数据部分的128字节中除去文件名和文件大小占据的剩下的字节都用00填充,CRCH和CRCL分别表示16位CRC校验码的高8位与低8位。
(2)数据帧的数据格式:
YModem的数据帧中会预留1024字节空间用来传输文件数据,它跟起始帧接收差不多,如下:
STX 01 FEdata[1024] CRCH CRCL
其中STX=0x02,表示这帧数据帧后面包含着1024字节的数据部分;01是表示帧序号,FE是它的取反,再下一帧数据就是02 FD,以此类推;data[1024]表示存放着1024字节的文件数据;CRCH与CRCL是CRC16检验码的高8位与低8位。
如果文件数据的最后剩余的数据在128~1024之前,则还是使用STX的1024字节传输,但是剩余空间全部用0x1A填充,如下结构:
STX 01 FE data[1024] 1A 1A……… CRCH CRCL
有一种特殊的情况:如果文件大小小于等于128字节或者文件数据最后剩余的数据小于128字节,则YModem会选择SOH数据帧用128字节来传输数据,如果数据不满128字节,剩余的数据用0x1A填充这是数据帧的结构就变成了:
文件大小小于128字节: SOH 01 FE data[ ] 1A ...1A CRCH CRCL
文件最后剩余数据小于128字节: SOH 01 FE 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的传输过程就是上面所示。但是上面传输过程中存在许多通信信号,它们的数值与意义如下表所示:
还是有几点需要说明:
- EOT信号由发送端发送;
- CA中止传输信号也有发送端发送;
- C的含义在英文的数据手册上的意思有点难以理解,我个人理解成请求数据包,如开始传输的发送C请求起始帧数据,然后再发送C请求文件数据帧,最后有发送一次C请求结束帧;
(5)CRC的计算:
YModem的采用的是CRC16-CCITT欧洲版本的CRC校验,它的生成多项式为:x16+x12+x5+1。
三 Ymodem协议移植:
移植代码如下,其中包含【1】接收一个字节Receive_Byte---->【2】接收一个数据包Receive_Packet---->【3】接收一个文件Ymodem_Receive---->【4】通过串口烧写一个文件SerialDownload:
/*******************************************************************************
* @函数名称 Receive_Byte
* @函数说明 从发送端接收一个字节
* @输入参数 c: 接收字符
timeout: 超时时间
* @输出参数 无
* @返回参数 接收的结果
0:成功接收
1:时间超时
*******************************************************************************/
static int32_t Receive_Byte(uint8_t *c, uint32_t timeout)
{
while (timeout-- > 0)
{
if (SerialKeyPressed(c) == 1)
{
return 0;
}
}
return -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;
}
switch (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;
}
case ABORT1:
case ABORT2:
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 bank)
{
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
int32_t i, j, base_address, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
//初始化Flash地址变量
if(0 == bank)
{
FlashDestination = APP_BANK1_ADDRESS;
base_address = APP_BANK1_ADDRESS;
}
else
{
FlashDestination = APP_BANK2_ADDRESS;
base_address = APP_BANK2_ADDRESS;
}
iap_init(bank);
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);
}
else
{
if (packets_received == 0)
{
//文件名数据包
if (packet_data[PACKET_HEADER] != 0)
{
//文件名数据包有效数据区域
for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
{
file_name[i++] = *file_ptr++;
}
file_name[i++] = '\0';
for (i = 0, file_ptr++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
{
file_size[i++] = *file_ptr++;
}
file_size[i++] = '\0';
Str2Int(file_size, &size);
//测试数据包是否过大
if (size > (FLASH_SIZE - 1))
{
//结束
Send_Byte(CA);
Send_Byte(CA);
return -1;
}
iap_erase_flash(base_address, size); //清空flash扇区
Send_Byte(ACK);
Send_Byte(CRC16);
}
//文件名数据包空,结束传输
else
{
Send_Byte(ACK);
file_done = 1;
session_done = 1;
break;
}
}
//数据包
else
{
memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
RamSource = (uint32_t)buf;
if(packet_length == PAGE_SIZE) //1k整包
{
iap_write_flash(FlashDestination, (uint8_t *)RamSource, packet_length); //写flash
if (*(uint32_t *)FlashDestination != *(uint32_t *)RamSource) //错误
{
Send_Byte(CA);
Send_Byte(CA);
return -2;
}
FlashDestination += packet_length;
}
else //尾包
{
iap_write_flash(FlashDestination, (uint8_t *)RamSource, (size%PAGE_SIZE)); //写flash
}
Send_Byte(ACK);
}
packets_received++;
session_begin = 1;
}
}
break;
case 1:
Send_Byte(CA);
Send_Byte(CA);
return -3;
default:
if (session_begin > 0)
{
errors++;
}
if (errors > MAX_ERRORS)
{
Send_Byte(CA);
Send_Byte(CA);
return 0;
}
Send_Byte(CRC16);
break;
}
if (file_done != 0)
{
break;
}
}
if (session_done != 0)
{
break;
}
}
return (int32_t)size;
}
/*******************************************************************************
* @函数名称 SerialDownload
* @函数说明 通过串口接收一个文件
* @输入参数 烧录的片上flash块基地址:
bank=0为0x1A000000,bank=1为0x1B000000
* @输出参数 无
* @返回参数 无
*******************************************************************************/
void SerialDownload(uint8_t bank)
{
uint8_t Number[10] = " ";
int32_t Size = 0;
SerialPutString("Waiting for the file(press 'a' to abort)\r\n");
Size = Ymodem_Receive(&tab_1024[0], bank);
if (Size > 0)
{
SerialPutString("\r\nProgramming Successfully!\r\nName: ");
SerialPutString(file_name);
Int2Str(Number, Size);
SerialPutString("\r\nSize: ");
SerialPutString(Number);
SerialPutString("Bytes\r\n");
}
else if (Size == -1)
{
SerialPutString("\r\nThe image size is higher than the allowed space memory!\r\n");
}
else if (Size == -2)
{
SerialPutString("\r\n\rVerification failed!\r\n");
}
else if (Size == -3)
{
SerialPutString("\r\nAborted by user.\r\n");
}
else
{
SerialPutString("\r\nFailed to receive the file!\r\n");
}
}
当中最重要的就是读写Flash的底层驱动,这边一并列出:
#include <string.h>
#include "Flash_IAP.h"
#include "lpc18xx.h"
/*----------------------------------Mem Allocation Table---------------------------*/
/*---------------------------ROM1 512K(0x1A000000-0x0x1A07FFFF)--------------------*/
/*---Sector ID--------Size(KB)--------Start Addr--------End Addr--------Mem Role---*/
/*---0----------------8K--------------0x1A000000--------0x1A001FFF------IAP--------*/
/*---1----------------8K--------------0x1A002000--------0x1A003FFF------IAP--------*/
/*---2----------------8K--------------0x1A004000--------0x1A005FFF------IAP--------*/
/*---3----------------8K--------------0x1A006000--------0x1A007FFF------IAP--------*/
/*---4----------------8K--------------0x1A008000--------0x1A009FFF------APP--------*/
/*---5----------------8K--------------0x1A00A000--------0x1A00BFFF------APP--------*/
/*---6----------------8K--------------0x1A00C000--------0x1A00DFFF------APP--------*/
/*---7----------------8K--------------0x1A00E000--------0x1A00FFFF------APP--------*/
/*---8----------------64K-------------0x1A010000--------0x1A01FFFF------APP--------*/
/*---9----------------64K-------------0x1A020000--------0x1A02FFFF------APP--------*/
/*---10---------------64K-------------0x1A030000--------0x1A03FFFF------APP--------*/
/*---11---------------64K-------------0x1A040000--------0x1A04FFFF------APP--------*/
/*---12---------------64K-------------0x1A050000--------0x1A05FFFF------APP--------*/
/*---13---------------64K-------------0x1A060000--------0x1A06FFFF------APP--------*/
/*---14---------------64K-------------0x1A070000--------0x1A07FFFF------APP--------*/
/*---------------------------------------------------------------------------------*/
/*---------------------------ROM2 512K(0x1AB000000-0x0x1B07FFFF)-------------------*/
/*---Sector ID--------Size(KB)--------Start Addr--------End Addr--------Mem Role---*/
/*---0----------------8K--------------0x1B000000--------0x1B001FFF------APP--------*/
/*---1----------------8K--------------0x1B002000--------0x1B003FFF------APP--------*/
/*---2----------------8K--------------0x1B004000--------0x1B005FFF------APP--------*/
/*---3----------------8K--------------0x1B006000--------0x1B007FFF------APP--------*/
/*---4----------------8K--------------0x1B008000--------0x1B009FFF------APP--------*/
/*---5----------------8K--------------0x1B00A000--------0x1B00BFFF------APP--------*/
/*---6----------------8K--------------0x1B00C000--------0x1B00DFFF------APP--------*/
/*---7----------------8K--------------0x1B00E000--------0x1B00FFFF------APP--------*/
/*---8----------------64K-------------0x1B010000--------0x1B01FFFF------APP--------*/
/*---9----------------64K-------------0x1B020000--------0x1B02FFFF------APP--------*/
/*---10---------------64K-------------0x1B030000--------0x1B03FFFF------APP--------*/
/*---11---------------64K-------------0x1B040000--------0x1B04FFFF------APP--------*/
/*---12---------------64K-------------0x1B050000--------0x1B05FFFF------APP--------*/
/*---13---------------64K-------------0x1B060000--------0x1B06FFFF------APP--------*/
/*---14---------------64K-------------0x1B070000--------0x1B07FFFF------APP--------*/
/*---------------------------------------------------------------------------------*/
#define OPEN_CORE_INT() __enable_irq()
#define CLOSE_CORE_INT() __disable_irq()
//#define IAP_ERROR -1
unsigned char update_flag;
/*********************************************************************************************************
** define variable related to iap
*********************************************************************************************************/
typedef void (* IAP) (uint32_t *, uint32_t *);
#define IAP_LOCATION *(volatile unsigned int *)0x10400100
static IAP iap_entry;
static uint32_t paramin[5] = {100}; // IAP入口参数缓冲区
static uint32_t paramout[3] = {100}; // IAP出口参数缓冲区
//static uint32_t gSecStart = 0;
//static uint32_t gSecEnd = 0;
uint32_t gSecStart = 0;
uint32_t gSecEnd = 0;
/********************************************************************************************************
** 函数名称:init_iap()
** 函数功能:IAP初始化,命令代码49。
** 入口参数:无
** 出口参数:IAP操作状态码
** IAP返回值(paramout缓冲区)
********************************************************************************************************/
uint32_t init_iap(void)
{
paramin[0] = IAP_INIT; // 设置命令字
iap_entry(paramin, paramout); // 调用IAP服务程序
return(paramout[0]); // 返回状态码
}
/********************************************************************************************************
** 函数名称:pre_sector()
** 函数功能:IAP操作扇区选择,命令代码50。
** 入口参数:sec1 起始扇区
** sec2 终止扇区
** bank 0 = bank A, 1 = bank B
** 出口参数:IAP操作状态码
** IAP返回值(paramout缓冲区)
********************************************************************************************************/
uint32_t pre_sector(uint32_t sec1, uint32_t sec2, uint8_t bank)
{
paramin[0] = IAP_SELECTOR; // 设置命令字
paramin[1] = sec1; // 设置参数
paramin[2] = sec2;
paramin[3] = bank;
iap_entry(paramin, paramout); // 调用IAP服务程序
return(paramout[0]); // 返回状态码
}
/******************************************************************************************************
** 函数名称:copy_ram_to_flash()
** 函数功能:复制RAM的数据到FLASH,命令代码51。
** 入口参数:dst 目标地址,即FLASH起始地址。以512字节为分界
** src 源地址,即RAM地址。地址必须字对齐
** no 复制字节个数,为512/1024/4096
** 出口参数:IAP操作状态码
** IAP返回值(paramout缓冲区)
*******************************************************************************************************/
static uint32_t copy_ram_to_flash(uint32_t dst, uint32_t src, uint32_t no)
{
CLOSE_CORE_INT();
paramin[0] = IAP_RAMTOFLASH; // 设置命令字
paramin[1] = dst; // 设置参数
paramin[2] = src;
paramin[3] = no;
paramin[4] = IAP_FCCLK;
iap_entry(paramin, paramout); // 调用IAP服务程序
OPEN_CORE_INT();
return(paramout[0]); // 返回状态码
}
/******************************************************************************************************
** 函数名称:erase_sector()
** 函数功能:扇区擦除,命令代码52。
** 入口参数:sec1 起始扇区
** sec2 终止扇区
** bank 0 = bank A, 1 = bank B
** 出口参数:IAP操作状态码
** IAP返回值(paramout缓冲区)
*******************************************************************************************************/
uint32_t erase_sector(uint32_t sec1, uint32_t sec2, uint8_t bank)
{
paramin[0] = IAP_ERASESECTOR; // 设置命令字
paramin[1] = sec1; // 设置参数
paramin[2] = sec2;
paramin[3] = IAP_FCCLK;
paramin[4] = bank;
iap_entry(paramin, paramout); // 调用IAP服务程序
return(paramout[0]); // 返回状态码
}
/******************************************************************************************************
** 函数名称:blank_check_sector()
** 函数功能:扇区查空,命令代码53。
** 入口参数:sec1 起始扇区
** sec2 终止扇区
** bank 0 = bank A, 1 = bank B
** 出口参数:IAP操作状态码
** IAP返回值(paramout缓冲区)
*******************************************************************************************************/
uint32_t blank_check_sector(uint32_t sec1, uint32_t sec2, uint8_t bank)
{
paramin[0] = IAP_BLANKCHK; // 设置命令字
paramin[1] = sec1; // 设置参数
paramin[2] = sec2;
paramin[3] = bank;
iap_entry(paramin, paramout); // 调用IAP服务程序
return(paramout[0]); // 返回状态码
}
/******************************************************************************************************
** 函数名称:compare()
** 函数功能:校验数据,命令代码56。
** 入口参数:dst 目标地址,即RAM/FLASH起始地址。地址必须字对齐
** src 源地址,即FLASH/RAM地址。地址必须字对齐
** no 复制字节个数,必须能被4整除
** 出口参数:IAP操作状态码
** IAP返回值(paramout缓冲区)
*******************************************************************************************************/
uint32_t compare(uint32_t dst, uint32_t src, uint32_t no)
{
CLOSE_CORE_INT();
paramin[0] = IAP_COMPARE; // 设置命令字
paramin[1] = dst; // 设置参数
paramin[2] = src;
paramin[3] = no;
iap_entry(paramin, paramout); // 调用IAP服务程序
OPEN_CORE_INT();
return(paramout[0]); // 返回状态码
}
/******************************************************************************************************
** 函数名称:compare()
** 函数功能:校验数据,命令代码56。
** 入口参数:dst 目标地址,即RAM/FLASH起始地址。地址必须字对齐
** src 源地址,即FLASH/RAM地址。地址必须字对齐
** no 复制字节个数,必须能被4整除
** 出口参数:IAP操作状态码
** IAP返回值(paramout缓冲区)
*******************************************************************************************************/
uint32_t iap_compare(uint32_t dst, uint8_t *src, uint32_t no)
{
return compare(dst, (uint32_t)src, no);
}
static uint8_t g_bank_num = 0;
void iap_init (uint8_t bank)
{
if (bank > 1)
{
bank = 1;
}
g_bank_num = bank;
iap_entry = (IAP)IAP_LOCATION;
init_iap();
}
/******************************************************************************************************
** 函数名称:iap_erase_flash()
** 函数功能:flash 擦除操作
** 入口参数:addrDst 要擦除flash的起始地址
** length 要擦除flash的长度
** 出口参数:IAP操作状态码
** IAP返回值(paramout缓冲区)
*******************************************************************************************************/
uint32_t iap_erase_flash(uint32_t addrDst, uint32_t length)
{
uint32_t state;
uint32_t baseAddr;
if (length == 0)
{
return 101;
}
if (g_bank_num)
{
baseAddr = 0x1B000000;
} else
{
baseAddr = 0x1A000000;
}
if ((addrDst < baseAddr) || ((addrDst + length) > (0x7FFFF + baseAddr)))
{
return 100;
}
// 计算起始扇区号
if (addrDst < (0x10000 + baseAddr))
{
gSecStart = (addrDst - baseAddr) / (8 << 10);
if ((addrDst + length) < (0x10000 + baseAddr))
{
gSecEnd = (addrDst - baseAddr + length) / (8 << 10);
} else
{
gSecEnd = 8 + (addrDst - baseAddr - 0x10000 + length) / (64 << 10);
}
} else
{
gSecStart = 8 + (addrDst - baseAddr - 0x10000) / (64 << 10);
gSecEnd = 8 + (addrDst - baseAddr - 0x10000 + length) / (64 << 10);
}
// debugPrint("bank %d, len %d,sector start %d, end %d\r\n", g_bank_num, length, gSecStart, gSecEnd);
CLOSE_CORE_INT();
state = pre_sector(gSecStart, gSecEnd, g_bank_num);
if (state == CMD_SUCCESS)
{
state = erase_sector(gSecStart, gSecEnd, g_bank_num);
if (state == CMD_SUCCESS)
{
state = blank_check_sector(gSecStart, gSecEnd, g_bank_num);
}
}
OPEN_CORE_INT();
return state;
}
/******************************************************************************************************
** 函数名称:iap_write_flash()
** 函数功能:flash 写操作
** 入口参数:addrDst 写入flash的数据的目标地址
** addrSrc 要写入flash的数据的源地址
** length 数据长度
** 出口参数:写操作状态码
*******************************************************************************************************/
uint32_t iap_write_flash(uint32_t addrDst, uint8_t *addrSrc, uint32_t length)
{
uint32_t state;
uint8_t lastBuffer[1024];
uint32_t remainder = length;
uint32_t offset = 0;
/*
* 参数过滤
*/
if (length == 0)
{
return 100;
}
//if (addrDst % 256 != 0)
if (addrDst % 128 != 0)
{
return 101;
}
if (addrDst < 64)
{
return 102;
}
if ((uint32_t)addrSrc % 4 != 0)
{
return 103;
}
/*
* 每次都写512字节,不够用0xff填充
*/
while (remainder > 0)
{
CLOSE_CORE_INT();
state = pre_sector(gSecStart, gSecEnd, g_bank_num);
if (state != CMD_SUCCESS)
{
OPEN_CORE_INT();
return state;
}
if (remainder >= 1024)
{
state = copy_ram_to_flash(addrDst+offset, (uint32_t)(addrSrc+offset), 1024);
} else
{
memcpy(lastBuffer, addrSrc+offset, remainder);
memset(lastBuffer+remainder, 0xff, 1024-remainder);
state = copy_ram_to_flash(addrDst+offset, (uint32_t)lastBuffer, 1024);
}
OPEN_CORE_INT();
if (state != CMD_SUCCESS)
{
return state;
}
offset += 1024;
if (remainder >= 1024)
{
remainder -= 1024;
} else
{
remainder = 0;
}
}
return state;
}
/******************************************************************************************************
** iap_read_flash()
** 函数功能:flash 读操作
** 入口参数:addrDst 写保存的数据的目标地址
** addrSrc 要保存数据的源地址
** length 数据长度
** 出口参数:读取长度
*******************************************************************************************************/
uint32_t iap_read_flash(uint8_t *addrDst, uint8_t *addrSrc, uint32_t length)
{
uint32_t i = 0;
if ((addrDst == NULL) || (addrSrc == NULL) || (length == 0))
{
return 0;
}
while (length--)
{
addrDst[i] = addrSrc[i];
i++;
}
return i;
}
顺便附上头文件Flash_IAP.h:
#ifndef __FLASH_IAP_H_
#define __FLASH_IAP_H_
#include <stdint.h>
/* 定义IAP命令字 */
// 命令 参数
#define IAP_INIT 49 // 初始化IAP 【无】
#define IAP_SELECTOR 50 // 选择扇区 【起始扇区号、结束扇区号、bank号】
#define IAP_RAMTOFLASH 51 // 拷贝数据 【FLASH目标地址、RAM源地址、写入字节数、系统时钟频率】
#define IAP_ERASESECTOR 52 // 擦除扇区 【起始扇区号、结束扇区号、系统时钟频率】
#define IAP_BLANKCHK 53 // 查空扇区 【起始扇区号、结束扇区号】
#define IAP_READPARTID 54 // 读器件ID 【无】
#define IAP_BOOTCODEID 55 // 读Boot版本号 【无】
#define IAP_COMPARE 56 // 比较命令 【Flash起始地址、RAM起始地址、需要比较的字节数】
/* 定义IAP返回状态字 */
#define CMD_SUCCESS 0
#define INVALID_COMMAND 1
#define SRC_ADDR_ERROR 2
#define DST_ADDR_ERROR 3
#define SRC_ADDR_NOT_MAPPED 4
#define DST_ADDR_NOT_MAPPED 5
#define COUNT_ERROR 6
#define INVALID_SECTOR 7
#define SECTOR_NOT_BLANK 8
#define SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION 9
#define COMPARE_ERROR 10
#define BUSY 11
#define IAP_FCCLK 180000
#define APP_BANK1_ADDRESS 0x1A008000
#define APP_BANK2_ADDRESS 0x1B000000
#define IAP_ADDRESS 0x1A000000
#define FLAG_UPDATE_ADDRESS 0X1A007FFF
#define BANK0 0
#define BANK1 1
#define APP_CONFIG_SET_VALUE 0X55 //设置值
#define APP_CONFIG_CLEAR_VALUE 0XFF //清零值
extern void iap_init (uint8_t bank);
extern uint32_t iap_erase_flash(uint32_t addrDst, uint32_t length);
extern uint32_t iap_write_flash(uint32_t addrDst, uint8_t *addrSrc, uint32_t length);
extern uint32_t iap_read_flash(uint8_t *addrDst, uint8_t *addrSrc, uint32_t length);
extern uint32_t iap_compare(uint32_t dst, uint8_t *src, uint32_t no);
extern void runIap(void);
extern uint8_t Iap_Write_Config_Value(uint8_t value);
#endif
至此,Ymodem移植完毕。