我自己做了一个 针对stm32的isp 离线编程器,
设计时候 采用了 W25Q64 作为要烧录文件的存储,
直接使用 Ymodem 协议,将文件下载到 flash中,免得再写一个上位机软件,
在ucos2的加持下,整个代码的逻辑比较简单易懂
Ymodem 协议 参考 https://www.cnblogs.com/dwj411024/p/7717084.html
贴上我的代码
头文件:
#ifndef __Y_MODEM_H__
#define __Y_MODEM_H__
enum{
eYmSOH=0x01,
eYmSTX=0x02,
eYmEOT=0x04,
eYmACK=0x06,
eYmNAK=0x15,
eYmCANCEL=0x18,
eYmC=0x43,
};
enum{
eYmFileBegin,
eYmFileData,
eYmFileEnd0,
eYmFileEnd1,
};
typedef unsigned char (*pYmdCb)(unsigned char *pdata,unsigned char state);
unsigned char RecYModem(pYmdCb cb,unsigned char *recbuf);
#endif
代码:
#include "includes.h"
/*******************************************************************************************
符号 数值 含义
SOH 0x01 128字节数据帧,协议类型
STX 0x02 1024字节数据帧,协议类型
EOT 0x04 结束传输,发送者发送
ACK 0x06 接收者处理成功回应,发送者发现下一包数据(1024或者128)
NAK 0x15 接收者处理失败回应,发送者需要重发此1024或者128数据,或者接收到第一个EOT的回复包
CA 0x18 传输中止
C 0x43 接收者准备接收时会发连续的C,发送者接收到C开始发送
**********************************************************************************************************************/
//这两个代码要重写,放到应用端
extern unsigned char RecYModemData(unsigned char *timeout);
extern void SendYModemData(unsigned char data);
/*---- Y _ M O D E M _ C R C ----
【功能】:crc校验
【参数】:
****
【返回】:****
【说明】:****
--------------作者:卢杰西 2020年12月1日17:15:24--------------------------------*/
unsigned short int Y_Modem_CRC(unsigned char * buf, unsigned short int len)
{
unsigned short int chsum;
unsigned short int stat;
unsigned short int i;
unsigned char * in_ptr;
//指向要计算CRC的缓冲区开头
in_ptr = buf;
chsum = 0;
for (stat = len ; stat > 0; stat--) //len是所要计算的长度
{
chsum = chsum^(unsigned short int)(*in_ptr++) << 8;
for (i=8; i!=0; i--) {
if (chsum & 0x8000){
chsum = chsum << 1 ^ 0x1021;
} else {
chsum = chsum << 1;
}
}
}
return chsum;
}
/*---- R E C Y M P A C K E T ----
【功能】:接收一个包
【参数】:****
【返回】:****
【说明】:****
--------------作者:卢杰西 2020年12月1日17:15:39--------------------------------*/
signed char RecYmPacket(unsigned char *pdata)
{
unsigned char err;
unsigned short int cnt,len,crcrec,crc;
pdata[0]=RecYModemData(&err);
if(err==OS_TIMEOUT) return -1;
if(pdata[0]==eYmEOT) return 0;
pdata[1]=RecYModemData(&err);
if(err==OS_TIMEOUT) return -1;
pdata[2]=RecYModemData(&err);
if(err==OS_TIMEOUT) return -1;
if((pdata[1]^pdata[2])!=0xff) return -2;
if(pdata[0]==eYmSOH)
len=128;
else
len=1024;
for (cnt=0;cnt<len;cnt++)
{
pdata[3+cnt]=RecYModemData(&err);
if(err==OS_TIMEOUT)
{
return -1;
}
}
crcrec=RecYModemData(&err)<<8;
if(err==OS_TIMEOUT) return -1;
crcrec|=RecYModemData(&err);
if(err==OS_TIMEOUT) return -1;
crc=Y_Modem_CRC(pdata+3,len);
if(crc!=crcrec)
{
if(pdata[0]!=eYmSOH) return -3;
else return -4;
}
return 0;
}
/*---- R E C Y M O D E M ----
【功能】:Ymodem的协议流程,
【参数】:
****
【返回】:****
【说明】:每接收到一个包 回调 cb 进行接收处理
--------------作者:卢杰西 2020年12月1日17:16:27--------------------------------*/
unsigned char RecYModem(pYmdCb cb,unsigned char *recbuf)
{
unsigned char cntEot=0,cnt,errcnt=0;
signed char ret;
do{
SendYModemData(eYmC);
ret=RecYmPacket(recbuf);
if(ret==0 && recbuf[0]==eYmSOH)
{
cb(recbuf,eYmFileBegin); //文件初始化
break;
}
else
{
OSTimeDly(OS_TICKS_PER_SEC); //接收 启动 失败
}
}while(++errcnt<100);
if(errcnt<100)
{
SendYModemData(eYmACK);
SendYModemData(eYmC);
do{
ret=RecYmPacket(recbuf);
if(ret==0 || ret==-4)
{
errcnt=0;
if( (recbuf[0]==eYmSOH && cntEot<2) ||recbuf[0]==eYmSTX )
{
cntEot=0;
cb(recbuf,eYmFileData); //文件下载中
SendYModemData(eYmACK);
}
else if(recbuf[0]==eYmSOH && cntEot==2)
{
printf("rec oc\r\n");
//cb(recbuf,eYmFileEnd0); //文件结束
SendYModemData(eYmACK);
break;
}
else if(recbuf[0]==eYmEOT )
{
if(cntEot==0)
{
SendYModemData(eYmNAK);
printf("rec eot 1\r\n");
++cntEot;
}
else
{
cb(recbuf,eYmFileEnd1); //文件结束
printf("rec eot 1\r\n");
SendYModemData(eYmACK);
SendYModemData(eYmC);
break;
}
}
}
else
{
SendYModemData(eYmNAK);
}
}while(++errcnt<100);
}
if(errcnt>=100)
{
//终止传输
for(cnt=0;cnt<10;cnt++)
SendYModemData(eYmCANCEL);
}
return (errcnt<100);
}
串口接口的代码:
OS_EVENT *OSQModemRec;
unsigned char RecYModemData(unsigned char *timeout)
{
unsigned char rec;
rec=(INT32U)OSQPend(OSQModemRec,OS_TICKS_PER_SEC,timeout);
return rec;
}
void SendYModemData(unsigned char ch)
{
USART_SendData(USART2, ch);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET) OSTimeDly(1);
}