freeelectron 发表于 2022-3-17 18:01

【AT-START-F425测评】11、IAP程序升级——基于YMODEM协议

本帖最后由 freeelectron 于 2022-3-17 18:05 编辑


1、什么是IAPIAP是In Application Programming的缩写,即在应用编程,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。可以通过串口、USB、网络、无线等方式进行升级数据的传输。
2、IAP要点(1)程序分两部分,BOOT和APP分别编写;
(2)flash空间划分(flash空间足够大的情况下,可以分成APP1和APP2进行备份升级),本应用中前14k用于boot程序,后面50k用于app程序;(3)flash可编程(读、写、擦除),AT32f425 flash编程,可查看【AT-START-F425测评】10、flash读写——使用内部flash存储数据,里面有关于flash的的读、写、擦除操作;(4)从BOOT跳转到APP的时候需要,关闭中断,判断栈顶地址、设置栈指针;(5)APP程序编译设置,flash起始地址;(6)App程序中设置,中断开和中断向量位置。
3、YMODEM协议本文基于YMODEM协议实现升级,关于YMODEM协议此次不展开,可自行百度。
4、YMODEM工具
本文使用SecureCRT,其他工具亦可。
5、核心代码(1)操作菜单,通过输入代码,选择执行的操作void Main_Menu(void)
{
uint8_t key = 0;

while (1)
{
    SerialPutString("\r\n================== Main Menu ===============================\r\n\n");
    SerialPutString("Download Image To the AT32F425 Internal Flash ----------- 1\r\n\n");
    SerialPutString("Upload Image From the AT32F425 Internal Flash ----------- 2\r\n\n");
    SerialPutString("Execute The New Program --------------------------------- 3\r\n\n");
    SerialPutString("=============================================================\r\n\n");
   
    key = GetKey();

    if (key == 0x31)
    {
      /* Download user application in the Flash */
      SerialDownload();
    }
    else if (key == 0x32)
    {
      /* Upload user application from the Flash */
      SerialUpload();
    }
    else if (key == 0x33)
    {
                if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) //判断栈顶地址是否在合法范围内
                {
                  __disable_irq();/* 禁止全局中断*/

                  JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);

                  /* Jump to user application */
                  Jump_To_Application = (pFunction) JumpAddress;
                  /* Initialize user application's Stack Pointer */
                  __set_MSP(*(__IO uint32_t*) ApplicationAddress);
                  Jump_To_Application();
                }
                else
                {
                        SerialPutString("\r\nJump app fail!\r\n");
                }
    }
      else if(key==0x34)
      {
                SerialPutString("\r\nEnter 4,use debug!\r\n");
      }
    else
    {
       SerialPutString("Invalid Number ! ==> The number should be either 1, 2 or 3\r");
    }
}
}(2)ymodem发送uint8_t Ymodem_Transmit (uint8_t *buf, const uint8_t* sendFileName, uint32_t sizeFile)
{

uint8_t packet_data;
uint8_t FileName;
uint8_t *buf_ptr, tempCheckSum ;
uint16_t tempCRC, blkNumber;
uint8_t receivedC, CRC16_F = 0, i;
uint32_t errors, ackReceived, size = 0, pktSize;

errors = 0;
ackReceived = 0;
for (i = 0; i < (FILE_NAME_LENGTH - 1); i++)
{
    FileName = sendFileName;
}
CRC16_F = 1;      
   
/* Prepare first block */
Ymodem_PrepareIntialPacket(&packet_data, FileName, &sizeFile);

do
{
    /* Send Packet */
    Ymodem_SendPacket(packet_data, PACKET_SIZE + PACKET_HEADER);
    /* Send CRC or Check Sum based on CRC16_F */
    if (CRC16_F)
    {
       tempCRC = Cal_CRC16(&packet_data, PACKET_SIZE);
       Send_Byte(tempCRC >> 8);
       Send_Byte(tempCRC & 0xFF);
    }
    else
    {
       tempCheckSum = CalChecksum (&packet_data, PACKET_SIZE);
       Send_Byte(tempCheckSum);
    }

    /* Wait for Ack and 'C' */
    if (Receive_Byte(&receivedC, 10000) == 0)
    {
      if (receivedC == ACK)
      {
      /* Packet transfered correctly */
      ackReceived = 1;
      }
    }
    else
    {
      errors++;
    }
}while (!ackReceived && (errors < 0x0A));

if (errors >=0x0A)
{
    return errors;
}
buf_ptr = buf;
size = sizeFile;
blkNumber = 0x01;
/* Here 1024 bytes package is used to send the packets */


/* Resend packet if NAKfor a count of 10 else end of commuincation */
while (size)
{
    /* Prepare next packet */
    Ymodem_PreparePacket(buf_ptr, &packet_data, blkNumber, size);
    ackReceived = 0;
    receivedC= 0;
    errors = 0;
    do
    {
      /* Send next packet */
      if (size >= PACKET_1K_SIZE)
      {
      pktSize = PACKET_1K_SIZE;
      
      }
      else
      {
      pktSize = PACKET_SIZE;
      }
      Ymodem_SendPacket(packet_data, pktSize + PACKET_HEADER);
      /* Send CRC or Check Sum based on CRC16_F */
      /* Send CRC or Check Sum based on CRC16_F */
      if (CRC16_F)
      {
         tempCRC = Cal_CRC16(&packet_data, pktSize);
         Send_Byte(tempCRC >> 8);
         Send_Byte(tempCRC & 0xFF);
      }
      else
      {
      tempCheckSum = CalChecksum (&packet_data, pktSize);
      Send_Byte(tempCheckSum);
      }
      
      /* Wait for Ack */
      if ((Receive_Byte(&receivedC, 0xffffff) == 0)&& (receivedC == ACK))
      {
      ackReceived = 1;
      if (size > pktSize)
      {
         buf_ptr += pktSize;
         size -= pktSize;
         if (blkNumber == (APP_IMAGE_SIZE/1024))
         {
             return 0xFF; /*error */
         }
         else
         {
            blkNumber++;
         }
      }
      else
      {
          buf_ptr += pktSize;
          size = 0;
      }
      }
      else
      {
      errors++;
      }
    }while(!ackReceived && (errors < 0x0A));
    /* Resend packet if NAKfor a count of 10 else end of commuincation */
   
    if (errors >=0x0A)
    {
      return errors;
    }
   
}
ackReceived = 0;
receivedC = 0x00;
errors = 0;
do
{
    Send_Byte(EOT);
    /* Send (EOT); */
    /* Wait for Ack */
      if ((Receive_Byte(&receivedC, 0xffffff) == 0)&& receivedC == ACK)
      {
      ackReceived = 1;
      }
      else
      {
      errors++;
      }
}while (!ackReceived && (errors < 0x0A));
   
if (errors >=0x0A)
{
    return errors;
}

/* Last packet preparation */
ackReceived = 0;
receivedC = 0x00;
errors = 0;

packet_data = SOH;
packet_data = 0;
packet_data = 0xFF;

for (i = PACKET_HEADER; i < (PACKET_SIZE + PACKET_HEADER); i++)
{
   packet_data = 0x00;
}

do
{
    /* Send Packet */
    Ymodem_SendPacket(packet_data, PACKET_SIZE + PACKET_HEADER);
    /* Send CRC or Check Sum based on CRC16_F */
    tempCRC = Cal_CRC16(&packet_data, PACKET_SIZE);
    Send_Byte(tempCRC >> 8);
    Send_Byte(tempCRC & 0xFF);

    /* Wait for Ack and 'C' */
    if (Receive_Byte(&receivedC, 0xffffff) == 0)
    {
      if (receivedC == ACK)
      {
      /* Packet transfered correctly */
      ackReceived = 1;
      }
    }
    else
    {
      errors++;
    }

}while (!ackReceived && (errors < 0x0A));
/* Resend packet if NAKfor a count of 10else end of commuincation */
if (errors >=0x0A)
{
    return errors;
}

do
{
    Send_Byte(EOT);
    /* Send (EOT); */
    /* Wait for Ack *///0xffffff
      if ((Receive_Byte(&receivedC, 0xfff) == 0)&& receivedC == ACK)
      {
      ackReceived = 1;
      }
      else
      {
      errors++;
      }
}while (!ackReceived && (errors < 0x0A));
   
if (errors >=0x0A)
{
    return errors;
}
return 0; /* file trasmitted successfully */
}
(3)ymodem接收int32_t Ymodem_Receive (uint8_t *buf)
{
uint8_t packet_data, file_size, *file_ptr, *buf_ptr;
int32_t i, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;

/* Initialize FlashDestination variable */
FlashDestination = ApplicationAddress;

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)
          {
            /* Abort by sender */
            case - 1:
            Send_Byte(ACK);
            return 0;
            /* End of transmission */
            case 0:
            Send_Byte(ACK);
            file_done = 1;
            break;
            /* Normal packet */
            default:
            if ((packet_data & 0xff) != (packets_received & 0xff))
            {
                Send_Byte(NAK);
            }
            else
            {
                if (packets_received == 0) //第一个包
                {
                  /* Filename packet */
                  if (packet_data != 0)
                  {
                  /* Filename packet has valid data */
                  for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
                  {
                      file_name = *file_ptr++;
                  }
                  file_name = '\0';
                  for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
                  {
                      file_size = *file_ptr++;
                  }
                  file_size = '\0';
                  Str2Int(file_size, &size);

                  /* Test the size of the image to be sent */
                  /* Image size is greater than Flash size */
                  if (size > (FLASH_SIZE - 1))//文件大小,大于flash容量
                  {
                      /* End session */
                      Send_Byte(CA);
                      Send_Byte(CA);
                      return -1;
                  }
                                        FlashErase(FlashDestination,FlashDestination+size);
                              
                  Send_Byte(ACK);
                  Send_Byte(CRC16);
                  }
                  /* Filename packet is empty, end session */
                  else
                  {
                  Send_Byte(ACK);
                  file_done = 1;
                  session_done = 1;
                  break;
                  }
                }
                /* Data packet */
                else
                {
                  memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
                  RamSource = (uint32_t)buf;
                                       
                                  {
                                       FlashWrite(packet_length,buf_ptr,FlashDestination);
                                       FlashDestination+=packet_length;
                                  }
                              
                  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;
}6、现象(1)上电复位,选择操作可以输入1,2,3选择操作,如果都没有输入,那么会在10s后自动跳转到app。
(2)升级程序输入1,选择ymodem发送,选择要升级的文件即可,升级完成之后,输入3,跳转到APP执行。
(3)读取mcu中的app部分flash输入1,然后选择ymodem接收,这里我们提前选择了接收文件存放的位置为桌面,接收完成之后,输入3,直接跳转到APP执行。

注:这里从mcu读取了app所有空间的flash。




muyichuan2012 发表于 2022-3-18 07:34

感谢分享

freeelectron 发表于 2022-3-18 08:38

muyichuan2012 发表于 2022-3-18 07:34
感谢分享

{:handshake:}

1021256354 发表于 2023-10-23 21:44

有源码吗?

HeTui 发表于 2024-8-20 16:11

求源码

muyichuan2012 发表于 2024-8-20 17:38

这里有一份apnote
目的是提供在AT32微控制器上创建IAP by Ymodem应用程序的方法。
https://www.arterytek.com/file/download/1340
页: [1]
查看完整版本: 【AT-START-F425测评】11、IAP程序升级——基于YMODEM协议