原文地址:点击打开链接
在之前的《STM32串口IAP》一文中,通过传输数据流来升级程序,但是这种"裸"数据的传输方式存在这许多的问题,比如它没有容错机制,不能保证数据的正确传输,还比如说它无法获知升级文件的信息,导致它在判断何时停止接收数据上“犹豫不决”。正式为了解决上面的问题,才引进了YModem协议。
/*************************************************************
Function : IAP_Init
Description: IAP初始化函数,初始化串口1
Input : none
return : none
*************************************************************/
void IAP_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(COM1_RCC, ENABLE);//使能 USART2 时钟
RCC_APB2PeriphClockCmd(COM1_GPIO_RCC, ENABLE);//使能串口2引脚时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//配置 USART2 的Tx 引脚类型为推挽式的
GPIO_InitStructure.GPIO_Pin = COM1_TX_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(COM1_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//配置 USART2 的Rx 为输入悬空
GPIO_InitStructure.GPIO_Pin = COM1_RX_PIN;
GPIO_Init(COM1_GPIO_PORT, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;//设置波特率为115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//设置数据位为8位
USART_InitStructure.USART_StopBits = USART_StopBits_1;//设置停止位为1位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //没有硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送与接收
USART_Init(COM1,&USART_InitStructure);//串口2相关寄存器的配置
USART_Cmd(COM1,ENABLE);//使能串口2
}
/*************************************************************
Function : IAP_SerialSendByte
Description: 串口发送字节
Input : c-要发送的字节
return : none
*************************************************************/
void IAP_SerialSendByte(u8 c)
{
USART_SendData(COM1, c);
while (USART_GetFlagStatus(COM1, USART_FLAG_TXE) == RESET) {}
}
/*************************************************************
Function : IAP_SerialSendStr
Description: 串口发送字符串
Input : none
return : none
*************************************************************/
void IAP_SerialSendStr(u8 *s)
{
while(*s != '\0')
{
IAP_SerialSendByte(*s);
s++;
}
}
/*************************************************************
Function : IAP_SerialGetByte
Description: 接收一个字节数据
Input : none
return : 返回结果值,0-没有接收到数据;1-接收到数据
*************************************************************/
u8 IAP_SerialGetByte(u8 *c)
{
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET)
{
*c = USART_ReceiveData(USART1);
return 1;
}
return 0;
}
/*************************************************************
Function : IAP_GetKey
Description: 获取键入值
Input : none
return : 返回键值
*************************************************************/
u8 IAP_GetKey(void)
{
u8 data;
while(!IAP_SerialGetByte(&data)){ }
return data;
}
/*************************************************************
Function : IAP_ShowMenu
Description: 显示菜单界面
Input : none
return : none
*************************************************************/
void IAP_ShowMenu(void)
{
IAP_SerialSendStr("\r\n+================(C) COPYRIGHT 2014 Ziye334 ================+");
IAP_SerialSendStr("\r\n| In-Application Programing Application (Version 1.0) |");
IAP_SerialSendStr("\r\n+----command----+-----------------function------------------+");
IAP_SerialSendStr("\r\n| 1: FWUPDATA | Update the firmware to flash by YModem |");
IAP_SerialSendStr("\r\n| 2: FWDWLOAD | Download the firmware from Flash by YModem|");
IAP_SerialSendStr("\r\n| 3: FWERASE | Erase the current firmware |");
IAP_SerialSendStr("\r\n| 4: BOOT | Excute the current firmware |");
IAP_SerialSendStr("\r\n| 5:REBOOT | Reboot |");
IAP_SerialSendStr("\r\n| ?: HELP | Display this help |");
IAP_SerialSendStr("\r\n+===========================================================+");
IAP_SerialSendStr("\r\n\r\n");
IAP_SerialSendStr("STM32-IAP>>");
}
/*************************************************************
Function : IAP_DisableFlashWPR
Description: 关闭flash的写保护
Input : none
return : none
*************************************************************/
void IAP_DisableFlashWPR(void)
{
u32 blockNum = 0, UserMemoryMask = 0;
blockNum = (IAP_ADDR - FLASH_BASE_ADDR) >> 12; //计算flash块
UserMemoryMask = ((u32)(~((1 << blockNum) - 1)));//计算掩码
if((FLASH_GetWriteProtectionOptionByte() & UserMemoryMask) != UserMemoryMask)//查看块所在区域是否写保护
{
FLASH_EraseOptionBytes ();//擦除选择位
}
}
s8 IAP_UpdataParam(s32 *param)
{
u32 i;
u32 flashptr = IAP_PARAM_ADDR;
FLASH_Unlock();//flash上锁
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);//清除flash相关标志位
for(i = 0; i < IAP_PARAM_SIZE; i++)
{
FLASH_ProgramWord(flashptr + 4 * i, *param);
if(*(u32 *)(flashptr + 4 * i) != *param)
{
return -1;
}
param++;
}
FLASH_Lock();//flash解锁
return 0;
}
/*************************************************************
Function : IAP_UpdataProgram
Description: 升级程序
Input : addr-烧写的地址 size-大小
return : 0-OK 1-error
*************************************************************/
s8 IAP_UpdataProgram(u32 addr, u32 size)
{
u32 i;
static u32 flashptr = IAP_ADDR;
FLASH_Unlock();//flash上锁
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);//清除flash相关标志位
for(i = 0; i < size; i += 4)
{
FLASH_ProgramWord(flashptr, *(u32 *)addr);//烧写1个字
if(*(u32 *)flashptr != *(u32 *)addr)//判断是否烧写成功
{
return -1;
}
flashptr += 4;
addr += 4;
}
FLASH_Lock();//flash解锁
return 0;
}
/*************************************************************
Function : IAP_FlashEease
Description: 擦除Flash
Input : size-擦除的大小
return : none
*************************************************************/
void IAP_FlashEease(u32 size)
{
u16 eraseCounter = 0;
u16 nbrOfPage = 0;
FLASH_Status FLASHStatus = FLASH_COMPLETE;
if(size % PAGE_SIZE != 0)//计算需要擦写的页数
{
nbrOfPage = size / PAGE_SIZE + 1;
}
else
{
nbrOfPage = size / PAGE_SIZE;
}
FLASH_Unlock();//解除flash擦写锁定
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);//清除flash相关标志位
for(eraseCounter = 0; (eraseCounter < nbrOfPage) && ((FLASHStatus == FLASH_COMPLETE)); eraseCounter++)//开始擦除
{
FLASHStatus = FLASH_ErasePage(IAP_ADDR + (eraseCounter * PAGE_SIZE));//擦除
IAP_SerialSendStr(".");//打印'.'以显示进度
}
FLASH_ErasePage(IAP_PARAM_ADDR);//擦除参数所在的flash页
FLASH_Lock();//flash擦写锁定
}
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
/*************************************************************
Function : IAP_JumpToApplication
Description: 跳转到升级程序处
Input : none
return : none
*************************************************************/
void IAP_JumpToApplication(void)
{
u32 JumpAddress;//跳转地址
if(((*(__IO u32 *)IAP_ADDR) & 0x2FFE0000) == 0x20000000)//有升级代码,IAP_ADDR地址处理应指向主堆栈区,即0x20000000
{
JumpAddress = *(__IO u32 *)(IAP_ADDR + 4);//获取复位地址
Jump_To_Application = (pFunction)JumpAddress;//函数指针指向复位地址
__set_MSP(*(__IO u32*)IAP_ADDR);//设置主堆栈指针MSP指向升级机制IAP_ADDR
Jump_To_Application();//跳转到升级代码处
}
}
/*************************************************************
Function : ShwHelpInfo
Description: 显示帮助信息
Input : none
return : none
*************************************************************/
static void ShwHelpInfo(void)
{
IAP_SerialSendStr("\r\nEnter '1' to updtate you apllication code!");
IAP_SerialSendStr("\r\nRnter '2' to download the firmware from interal flash!");
IAP_SerialSendStr("\r\nRnter '3' to erase the current application code!");
IAP_SerialSendStr("\r\nEnter '4' to go to excute the current application code!");
IAP_SerialSendStr("\r\nEnter '5' to restart the system!");
IAP_SerialSendStr("\r\nEnter '?' to show the help infomation!\r\n");
}
/*************************************************************
Function : IAP_WiatForChoose
Description: 功能选择
Input : none
return : none
*************************************************************/
void IAP_WiatForChoose(void)
{
u8 c = 0;
while (1)
{
c = IAP_GetKey();//获取键值
IAP_SerialSendByte(c);//串口返回键值
switch(c)
{
case '1'://FWUPDATA固件升级
if(IAP_GetKey() == '\r')//检测回车键
{
IAP_DisableFlashWPR();//关闭写保护
DownloadFirmware();//开始升级
Delay_ms(500);
NVIC_SystemReset();//重启
}
break;
case '2'://FWDWLOAD上传当前固件
if(IAP_GetKey() == '\r') //获取键值
{
UploadFirmware();//开始上传
return;//退出循环
}
break;
case '3'://FWERASE固件擦除
if(IAP_GetKey() == '\r')//检测回车键
{
IAP_SerialSendStr("\r\nErasing...");
IAP_FlashEease(FLASH_SIZE + FLASH_BASE_ADDR - IAP_ADDR);//擦除Flash,升级处后面的flash空间
IAP_SerialSendStr("\r\nErase done!\r\n");
return;//退出循环
}
break;
case '4'://BOOT执行升级程序
if(IAP_GetKey() == '\r')//获取键值
{
IAP_SerialSendStr("\r\nBotting...\r\n");
if(((*(__IO u32 *)IAP_ADDR) & 0x2FFE0000) != 0x20000000)//判断是否有应用程序
{
IAP_SerialSendStr("No user program! Please download a firmware!\r\n");
}
Delay_ms(500);
NVIC_SystemReset();
return;//退出循环
}
break;
case '5'://REBOOT系统重启
if(IAP_GetKey() == '\r')//检测回车键
{
IAP_SerialSendStr("\r\nRebooting...\r\n");
return;//退出循环
}
break;
case '?'://HELP帮助
if(IAP_GetKey() == '\r')
{
ShwHelpInfo();//显示帮助信息
return;//退出循环
}
break;
default:
IAP_SerialSendStr("\r\nInvalid Number! The number should be either 1、2、3、4or5\r\n");
return;//退出循环
}
}
}
#ifndef __IAP_H__
#define __IAP_H__
#include "stm32f10x.h"
#define FLASH_BASE_ADDR 0x8000000 //Flash基地址
#define IAP_ADDR 0x8005000 //升级代码地址
#define IAP_PARAM_SIZE 1
#define IAP_PARAM_ADDR (FLASH_BASE_ADDR + FLASH_SIZE - PAGE_SIZE) //Flash空间最后1页地址开始处存放参数
#if defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)
#define PAGE_SIZE (0x400) //页的大小1K
#define FLASH_SIZE (0x20000) //Flash空间128K
#elif defined STM32F10X_CL
#define PAGE_SIZE (0x800) //页的大小2K
#define FLASH_SIZE (0x40000) //Flash空间256K
#elif defined STM32F10X_HD || defined (STM32F10X_HD_VL)
#define PAGE_SIZE (0x800) //页的大小2K
#define FLASH_SIZE (0x80000) //Flash空间512K
#elif defined STM32F10X_XL
#define PAGE_SIZE (0x800) //页的大小2K
#define FLASH_SIZE (0x100000) //Flash空间1M
#else
#error "Please select first the STM32 device to be used (in stm32f10x.h)"
#endif
void IAP_Init(void);
void IAP_SerialSendByte(u8 c);
void IAP_SerialSendStr(u8 *s);
u8 IAP_SerialGetByte(u8 *c);
u8 IAP_GetKey(void);
s8 IAP_UpdataParam(s32 *param);
s8 IAP_UpdataProgram(u32 addr, u32 size);
void IAP_FlashEease(u32 size);
void IAP_ShowMenu(void);
void IAP_WiatForChoose(void);
void IAP_JumpToApplication(void);
#endif
extern u8 file_name[FILE_NAME_LENGTH];
u8 tab_1024[1024] = {0};
/*************************************************************
Function : DownloadFirmware
Description: 下载升级固件
Input : none
return : none
*************************************************************/
void DownloadFirmware(void)
{
u8 number[10]= " "; //文件的大小字符串
s32 size = 0;
IAP_SerialSendStr("\r\nWaiting for the file to be send...(press 'a' or 'A' to abort)\r\n");
size = YModem_Receive(&tab_1024[0]);//开始接收升级程序
Delay_ms(1000);//延时1s,让secureCRT有足够时间关闭ymodem对话,而不影响下面的信息打印
if(size > 0)
{
IAP_SerialSendStr("+-----------------------------------+\r\n");
IAP_SerialSendStr("Proramming completed successfully!\r\nName: ");
IAP_SerialSendStr(file_name);//显示文件名
YModem_Int2Str(number, size);
IAP_SerialSendStr("\r\nSize:");
IAP_SerialSendStr(number);//显示文件大小
IAP_SerialSendStr("Bytes\r\n");
IAP_SerialSendStr("+-----------------------------------+\r\n");
}
else if(size == -1)//固件的大小超出处理器的flash空间
{
IAP_SerialSendStr("The image size is higher than the allowed space memory!\r\n");
}
else if(size == -2)//程序烧写不成功
{
IAP_SerialSendStr("Verification failed!\r\n");
}
else if(size == -3)//用户终止
{
IAP_SerialSendStr("Aborted by user!\r\n");
}
else //其他错误
{
IAP_SerialSendStr("Failed to receive the file!\r\n");
}
}
#ifndef __DOWNLOAD_H__
#define __DOWNLOAD_H__
#include "Download.h"
void DownloadFirmware(void);
#endif
/*************************************************************
Function : UploadFirmware
Description: 向上位机上传固件
Input : none
return : none
*************************************************************/
void UploadFirmware(void)
{
u32 status = 0;
u32 imageSize = 0;
IAP_SerialSendStr("\r\nBeginning to receive file...(press any key to abort)\r\n");
if(IAP_GetKey() == CRC16)//收到字符'C',便是ymodem询问数据
{
imageSize = *(u32 *)IAP_PARAM_ADDR;//向参数IAP_PARAM_ADDR地址处读取固件的大小
status = YModem_Transmit((u8 *)IAP_ADDR, (u8 *)"Firmware.bin", imageSize);
if(status != 0) //接收错误
{
IAP_SerialSendStr("\r\nError Occured while transmitting file!\r\n");
}
else//接收正确
{
IAP_SerialSendStr("\r\nFile Transmitted successfully!\r\n");
}
}
else//终止接收
{
IAP_SerialSendStr("\r\nAbort by user!\r\n");
}
}
#ifndef __UPLOAD_H__
#define __UPLOAD_H__
#include "stm32f10x.h"
void UploadFirmware(void);
#endif
/*************************************************************
Function : Int2Str
Description: 整型转化成字符串
Input : str-字符串指针 intnum-转换值
return : none
*************************************************************/
void YModem_Int2Str(u8* str, s32 intnum)
{
u32 i, Div = 1000000000, j = 0, Status = 0;
for (i = 0; i < 10; i++)
{
str[j++] = (intnum / Div) + '0';//数字转化成字符
intnum = intnum % Div;
Div /= 10;
if ((str[j-1] == '0') & (Status == 0))//忽略最前面的'0'
{
j = 0;
}
else
{
Status++;
}
}
}
/*************************************************************
Function : YModem_RecvByte
Description: ymodem接收一个字节
Input : c-存放接收到的字节 timeout-超时时间
return : none
*************************************************************/
static s8 YModem_RecvByte(u8 *c, u32 timeout)
{
while(timeout-- > 0)
{
if(IAP_SerialGetByte(c) == 1)
{
return 0;
}
}
return -1;
}
/*************************************************************
Function : YModem_SendByte
Description: 发送一个字节
Input : c-要发送的字节
return : none
*************************************************************/
static void YModem_SendByte(u8 c)
{
IAP_SerialSendByte(c);
}
/*************************************************************
Function : YModem_RecvPacket
Description: 接收一个数据包
Input : data-存放接收到的数据
length-数据包的长度
timeout-超时时间
return : 0 -正常接收 -1 -接收错误
*************************************************************/
s8 YModem_RecvPacket(u8 *data, s32 *length, u32 timeout)
{
u16 i, packet_size;
u8 c;
*length = 0;
if(YModem_RecvByte(&c, timeout) != 0)//接收数据包的第一个字节
{
return -1;
}
switch(c)
{
case SOH: //128字节数据包
packet_size = PACKET_SIZE; /记录数据包的长度
break;
case STX: //1024字节数据包
packet_size = PACKET_1K_SIZE; //记录数据包的长度
break;
case EOT: //数据接收结束字符
return 0; //接收结束
case CA: //接收中止标志
if((YModem_RecvByte(&c, timeout) == 0) && (c == CA))//等待接收中止字符
{
*length = -1; //接收到中止字符
return 0;
}
else //接收超时
{
return -1;
}
case ABORT1: //用户终止,用户按下'A'
case ABORT2: //用户终止,用户按下'a'
return 1; //接收终止
default:
return -1; //接收错误
}
*data = c; //保存第一个字节
for(i = 1; i < (packet_size + PACKET_OVERHEAD); i++)//接收数据
{
if(YModem_RecvByte(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; //正常接收
}
/*************************************************************
Function : YModem_Receive
Description: ymodem接收
Input : buf-存放接收到的数据
return : 0 -发送端传输中止
-1 -固件过大
-2 -flash烧写错误
-3 -用户终止
*************************************************************/
s32 YModem_Receive(u8 *buf)
{
u8 packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH];
u8 session_done, file_done, session_begin, packets_received, errors;
u8 *file_ptr, *buf_ptr;
s32 packet_length = 0, size = 0;
u32 i = 0,RamSource = 0;
for (session_done = 0, errors = 0, session_begin = 0; ;)//死循环,一个ymodem连接
{
for (packets_received = 0, file_done = 0, buf_ptr = buf; ; )//死循环,不断接收数据
{
switch(YModem_RecvPacket(packet_data, &packet_length, NAK_TIMEOUT))//接收数据包
{
case 0:
errors = 0;
switch(packet_length)
{
case -1: //发送端中止传输
YModem_SendByte(ACK);//回复ACK
return 0;
case 0: //接收结束或接收错误
YModem_SendByte(ACK);
file_done = 1;//接收完成
break;
default: //接收数据中
if((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
{
YModem_SendByte(NAK);//接收错误的数据,回复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';//文件名以'\0'结束
for(i = 0, file_ptr++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH); )
{
file_size[i++] = *file_ptr++;//保存文件大小
}
file_size[i++] = '\0';//文件大小以'\0'结束
size = atoi((const char *)file_size);//将文件大小字符串转换成整型
if(size > (FLASH_SIZE -1))//升级固件过大
{
YModem_SendByte(CA);
YModem_SendByte(CA);//连续发送2次中止符CA
return -1;//返回
}
IAP_FlashEease(size);//擦除相应的flash空间
IAP_UpdataParam(&size);//将size大小烧写进Flash中Parameter区
YModem_SendByte(ACK);//回复ACk
YModem_SendByte(CRC16);//发送'C',询问数据
}
else//文件名数据包为空,结束传输
{
YModem_SendByte(ACK);//回复ACK
file_done = 1;/停止接收
session_done = 1;//结束对话
break;
}
}
else //收到数据包
{
memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);//拷贝数据
RamSource = (u32)buf;//8位强制转化成32为数据
if(IAP_UpdataProgram(RamSource, packet_length) != 0) //烧写升级数据
{
YModem_SendByte(CA);
YModem_SendByte(CA);//flash烧写错误,连续发送2次中止符CA
return -2;//烧写错误
}
YModem_SendByte(ACK);//flash烧写成功,回复ACK
}
packets_received++;//收到数据包的个数
session_begin = 1;//设置接收中标志
}
}
break;
case 1: //用户中止
YModem_SendByte(CA);
YModem_SendByte(CA); //连续发送2次中止符CA
return -3; //烧写中止
default:
if(session_begin > 0) //传输过程中发生错误
{
errors++;
}
if(errors > MAX_ERRORS) //错误超过上限
{
YModem_SendByte(CA);
YModem_SendByte(CA);//连续发送2次中止符CA
return 0; //传输过程发生过多错误
}
YModem_SendByte(CRC16); //发送'C',继续接收
break;
}
if(file_done != 0)//文件接收完毕,退出循环
{
break;
}
}
if(session_done != 0)//对话结束,跳出循环
{
break;
}
}
return (s32)size;//返回接收到文件的大小
}
/*************************************************************
Function : YModem_PrepareFirstPacket
Description: 准备第一个数据包,包括文件名与大小
Input : data-要发送的数据包
fileName-文件名
length-文件的大小
return : none
*************************************************************/
void YModem_PrepareFirstPacket(u8 *data, const u8 *fileName, u32 *length)
{
u16 i, j;
u8 file_size[10];
data[0] = SOH; //128字节数据包
data[1] = 0x00; //第一个数据包
data[2] = 0xFF; //data[2] = ~data[1]
for(i = 0; (fileName[i] != '\0') && (i < FILE_NAME_LENGTH); i++)
{
data[i + PACKET_HEADER] = fileName[i];//拷贝文件名
}
data[i + PACKET_HEADER] = '\0';//文件名以'\0'结束
YModem_Int2Str (file_size, *length);//将文件长度转化成字符串
for (j =0, i = i + PACKET_HEADER + 1; file_size[j] != '\0' ; )
{
data[i++] = file_size[j++];//拷贝文件长度
}
for(j = i; j < PACKET_SIZE + PACKET_HEADER; j++)
{
data[j] = 0; //0填充
}
}
/*************************************************************
Function : YModem_PrepareDataPacket
Description: 准备数据包
Input : sourceBuf-要发送的数据
data-要发送的数据包
ptkNo-数据包的序号
sizeBlk-要发送的数据长度
return : none
*************************************************************/
void YModem_PrepareDataPacket(u8 *sourceBuf, u8 *data, u8 pktNo, u32 sizeBlk)
{
u16 i, size, packetSize;
u8 *file_ptr;
packetSize = sizeBlk >= PACKET_1K_SIZE ? PACKET_1K_SIZE : PACKET_SIZE;//决定发送128字节数据包还是发送1024字节数据包
size = sizeBlk < packetSize ? sizeBlk : packetSize; //计算数据包中数据的长度
if(packetSize == PACKET_1K_SIZE)//1K字节数据
{
data[0] = STX; //设置数据包首部STX,1024字节数据包
}
else //128字节数据
{
data[0] = SOH; //设置数据包首部SOH,128字节数据
}
data[1] = pktNo; //数据包序号
data[2] = (~pktNo);
file_ptr = sourceBuf; //指向需要发送的数据
for(i = PACKET_HEADER; i < size + PACKET_HEADER; i++)
{
data[i] = *file_ptr++; //拷贝要发送的数据
}
if(size <= packetSize) //数据长度小于128字节
{
for(i = size + PACKET_HEADER; i < packetSize + PACKET_HEADER; i++)
{
data[i] = 0x1A; //数据不够,以0x1A填充
}
}
}
/*************************************************************
Function : YModem_PrepareLastPacket
Description: 准备最后一个数据包
Input : data-要发送搞得数据包
return : none
*************************************************************/
void YModem_PrepareLastPacket(u8 *data)
{
u8 i = 0;
data[0] = SOH; //128字节数据包
data[1] = 0; //序号
data[2] = 0xFF;
for(i = PACKET_HEADER; i < (PACKET_SIZE + PACKET_HEADER); i++)
{
data[i] = 0x00;//数据以0填充,即空数据包
}
}
/*************************************************************
Function : UpdateCRC16
Description: 计算一个字节的CRC16校验码(CRC16-CCITT欧洲标准)
Input : crcIn-上一次的CRC码
byte-一个字节
return : 返回crc码
*************************************************************/
u16 UpdateCRC16(uint16_t crcIn, uint8_t byte)
{
uint32_t crc = crcIn;
uint32_t in = byte|0x100;
do
{
crc <<= 1;
in <<= 1;
if (in&0x100)
++crc; //crc |= 0x01
if (crc&0x10000)
crc ^= 0x1021;
}
while (!(in&0x10000));
return crc&0xffffu;
}
/*************************************************************
Function : Cal_CRC16
Description: 计算数据的CRC码
Input : data-要计算的数据
size-数据的大小
return : 返回计算出的CRC码
*************************************************************/
u16 Cal_CRC16(const uint8_t* data, uint32_t size)
{
uint32_t crc = 0;
const uint8_t* dataEnd = data+size;
while (data<dataEnd)
crc = UpdateCRC16(crc,*data++);
crc = UpdateCRC16(crc,0);
crc = UpdateCRC16(crc,0);
return crc&0xffffu;
}
/*************************************************************
Function : YModem_SendPacket
Description: 发送数据包
Input : data-要发送的数据
length-发送的数据长度
return : none
*************************************************************/
void YModem_SendPacket(u8 *data, u16 length)
{
u16 i = 0;
while(i < length)
{
YModem_SendByte(data[i]);
i++;
}
}
/*************************************************************
Function : YModem_TransmitFirstPacket
Description: 传输第一个数据包
Input : sendFileName-文件名
fileSize-文件的大小
return : 0-successed -1 -failed
*************************************************************/
s8 YModem_TransmitFirstPacket(u8 *sendFileName, u32 fileSize)
{
u8 i, ackReceived = 0, errors = 0, receiveChar = 0;
u8 firstPacket[PACKET_SIZE + PACKET_HEADER];
u8 fileName[FILE_NAME_LENGTH];
u16 tempCRC = 0;
for(i = 0; i < (FILE_NAME_LENGTH - 1); i++)
{
fileName[i] = sendFileName[i]; //拷贝文件名
}
YModem_PrepareFirstPacket(firstPacket, fileName, &fileSize);//准备第一个数据包
do
{
YModem_SendPacket(firstPacket, PACKET_SIZE + PACKET_HEADER);//发送第一个数据包
tempCRC = Cal_CRC16(&firstPacket[3], PACKET_SIZE);//计算校验码
YModem_SendByte(tempCRC >> 8); //发送CRC高位
YModem_SendByte(tempCRC & 0xFF); //发送CRC低位
if(((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == ACK))
&& ((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == CRC16)))
{
ackReceived = 1; //先后收到ACK与C标志,才表示接收方接收成功
}
else
{
errors++;
}
}while(!ackReceived && (errors < 0x0A));
if(errors >= 0x0A)
{
return -1;
}
return 0;
}
/*************************************************************
Function : YModem_TransmitDataPacket
Description: 开始传输数据包
Input : buf-要传输的数据
fileSize-要发送文件的大小
return : 0-successed -1 -failed
*************************************************************/
s8 YModem_TransmitDataPacket(u8 *buf, u32 size)
{
u8 *buf_ptr = buf;
u8 blkNumber = 0x01;
u8 ackReceived, errors, receiveChar;
u8 packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
u16 tempCRC = 0;
u32 pktSize = 0;
u32 fileSize = size;
while(fileSize) //数据没有发送完
{
ackReceived = 0;
receiveChar = 0;
errors = 0;
YModem_PrepareDataPacket(buf_ptr, packet_data, blkNumber, fileSize);//准备数据包
do
{
if(fileSize >= PACKET_1K_SIZE) //1024字节数据包
{
pktSize = PACKET_1K_SIZE;
}
else //128字节数据包
{
pktSize = PACKET_SIZE;
}
YModem_SendPacket(packet_data, pktSize + PACKET_HEADER);//发送数据包
tempCRC = Cal_CRC16(&packet_data[3], pktSize);//计算校验码
YModem_SendByte(tempCRC >> 8); //发送CRC高位
YModem_SendByte(tempCRC & 0xFF); //发送CRC低位
if((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == ACK))
{
ackReceived = 1;//收到ACK
if(fileSize > pktSize)
{
buf_ptr += pktSize;//偏移要发送数据的位置
fileSize -= pktSize;//计算剩余的数据
if(blkNumber == (size/1024 + 3))//数据包是否还需发送
{
return -1;
}
else
{
blkNumber++;
}
}
else
{
buf_ptr += pktSize;
fileSize = 0;
}
}
else
{
errors++;
}
}while (!ackReceived && (errors < 0x0A));
if(errors >= 0x0A)
{
return -1;
}
}
return 0;
}
/*************************************************************
Function : YModem_TransmitLastPacket
Description: 传输最后一个数据包
Input : none
return : 0-successed -1 -failed
*************************************************************/
s8 YModem_TransmitLastPacket(void)
{
u8 ackReceived = 0, receiveChar = 0, errors = 0;
u8 lastPacket[PACKET_SIZE + PACKET_OVERHEAD];
u16 tempCRC = 0;
YModem_PrepareLastPacket(lastPacket);
do
{
YModem_SendPacket(lastPacket, PACKET_SIZE + PACKET_HEADER);//发送数据包
tempCRC = Cal_CRC16(&lastPacket[3], PACKET_SIZE);//计算CRC检验位
YModem_SendByte(tempCRC >> 8); //发送CRC高位
YModem_SendByte(tempCRC & 0xFF);//发送CRC低位
if((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == ACK))
{
ackReceived = 1; //收到ACK
}
else
{
errors++;
}
}while(!ackReceived && (errors < 0x0A));
if(errors >= 0x0A)
{
return -1;
}
return 0;
}
/*************************************************************
Function : YModem_TransmitFirstEOT
Description: 发送第一个EOT结束标志
Input : none
return : 0-successed -1 -failed
*************************************************************/
s8 YModem_TransmitFirstEOT(void)
{
u8 ackReceived = 0, receiveChar = 0, errors = 0;
do
{
YModem_SendByte(EOT);//发送结束标志
if((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == NAK))
{
ackReceived = 1;//收到ACK, 表示对方收到EOT标志
}
else
{
errors++;
}
}while(!ackReceived && (errors < 0x0A));
if(errors >= 0x0A)
{
return -1;
}
return 0;
}
/*************************************************************
Function : YModem_TransmitSecondEOT
Description: 发送第二个EOT结束标志
Input :
return : 0-successed -1 -failed
*************************************************************/
s8 YModem_TransmitSecondEOT(void)
{
u8 ackReceived = 0, receiveChar = 0, errors = 0;
do
{
YModem_SendByte(EOT); //发送结束标志
if(((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == ACK))
&& ((YModem_RecvByte(&receiveChar, NAK_TIMEOUT) == 0) && (receiveChar == CRC16)))
{
ackReceived = 1;//分别收到ACK与C标志,才表示对方同意结束
}
else
{
errors++;
}
}while(!ackReceived && (errors < 0x0A));
if(errors >= 0x0A)
{
return -1;
}
return 0;
}
/*************************************************************
Function : YModem_Transmit
Description: ymodem传输文件
Input : buf-要传输的数据
sendFileName-传传输的文件名
filesize-传输的数据大小
return : 0-successed -1 -failed
*************************************************************/
s8 YModem_Transmit(u8 *buf, u8 *sendFileName, u32 fileSize)
{
s8 result = 0;
result = YModem_TransmitFirstPacket(sendFileName, fileSize);//发送一个数据包
if(result != 0) return result;
result = YModem_TransmitDataPacket(buf, fileSize);//发送文件数据
if(result != 0) return result;
result = YModem_TransmitFirstEOT();//发送第一个结束标志
if(result != 0) return result;
result = YModem_TransmitSecondEOT();//发送第二个结束标志
if(result != 0) return result;
result = YModem_TransmitLastPacket();//发送最后一个数据包
if(result != 0) return result;
return 0;;
}
#ifndef __YMODEM_H__
#define __YMODEM_H__
#include "stm32f10x.h"
#define PACKET_SEQNO_INDEX (1) //数据包序号
#define PACKET_SEQNO_COMP_INDEX (2) //包序取反
#define PACKET_HEADER (3) //首部3位
#define PACKET_TRAILER (2) //CRC检验的2位
#define PACKET_OVERHEAD (PACKET_HEADER + PACKET_TRAILER)//3位首部+2位CRC
#define PACKET_SIZE (128) //128字节
#define PACKET_1K_SIZE (1024) //1024字节
#define FILE_NAME_LENGTH (256) //文件最大长度
#define FILE_SIZE_LENGTH (16) //文件大小
#define SOH (0x01) //128字节数据包开始
#define STX (0x02) //1024字节的数据包开始
#define EOT (0x04) //结束传输
#define ACK (0x06) //回应
#define NAK (0x15) //没回应
#define CA (0x18) //这两个相继中止转移
#define CRC16 (0x43) //'C'即0x43, 需要 16-bit CRC
#define ABORT1 (0x41) //'A'即0x41, 用户终止
#define ABORT2 (0x61) //'a'即0x61, 用户终止
#define NAK_TIMEOUT (0x100000)//最大超时时间
#define MAX_ERRORS (5) //错误上限
void YModem_Int2Str(uint8_t* str, int32_t intnum);
s32 YModem_Receive(u8 *buf);
s8 YModem_Transmit(u8 *buf, u8 *sendFileName, u32 fileSize);
#endif
/*************************************************************
Function : KeyInit
Description: 初始化按键
Input : none
return : none
*************************************************************/
void KeyInit (void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA , &GPIO_InitStructure);
}
/*************************************************************
Function : GetKey
Description: 获取按键状态
Input : none
return : none
*************************************************************/
u8 GetKey (void)
{
return (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8));
}
/*************************************************************
Function : main
Description: main入口
Input : none
return : none
*************************************************************/
int main(void)
{
BSP_Init(); //板子初始化
KeyInit(); //初始化按键
if(!GetKey ()) //按键按下,进入升级界面
{
set: IAP_Init(); //初始化串口
shw: IAP_ShowMenu(); //显示功能菜单
IAP_WiatForChoose(); //等待选择界面
goto shw; //重新显示界面
}
else
{
IAP_JumpToApplication();//跳转到升级出代码执行
goto set;//没有升级程序或者升级程序错误才会执行到这句,然后天转到升级界面
}
}