【嵌入式】基于串口的IAP在线升级详解与实战2----移植Ymodem协议

目录

 

一 Ymodem协议简介:

二 Ymodem数据格式:

三 Ymodem协议移植:


一 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的传输过程就是上面所示。但是上面传输过程中存在许多通信信号,它们的数值与意义如下表所示:

还是有几点需要说明:

  1. EOT信号由发送端发送;
  2. CA中止传输信号也有发送端发送;
  3. 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移植完毕。

  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值