VS-MFC:LPC2000-uart-isp软件源码分析(含UU编码)
文章目录
源码:https://gitee.com/huangweide001/lpc_-uart_-isp
0.前言
这是一个基于MFC的LPC21xx系列ARM7单片机的烧录程序的源码。原来是用VC6.0开发,现在使用VS2017重新编译。年代比较久远,公开给有需要的人参考。
1. 几个关于LPC2000系列芯片UART ISP的基础知识
1.1 hex格式转化为bin格式
这个知识大家都知道,多数单片机都是由相应的编译器生成hex文件用于烧录器使用。
1.2 UU编码
UU编码:将3字节的数据编码成4字节的可打印的ASCII码,用于将二进制文件转换为可打印的ASCII码字符集。
下面以实例来说明编码原理:(使用0x14,0xFF,0xA7三个字节进行UU编码)
编码过程:
因此,UU编码后的数据范围是0x21~0x60的可打印字符,属于ASCII码字符集内;这个转换过程也意味着 uuencoded 文件要比原文件大三分之一。
UU编码的C代码:
void Uue (unsigned char chasc[3],unsigned char chuue[4])
{// chasc:未编码的二进制代码;chuue:编码过的Uue代码
int i,k=2;
unsigned char t=NULL;
for(i=0;i<3;i++)
{
*(chuue+i)=*(chasc+i)>>k;
*(chuue+i)|=t;
if(*(chuue+i)==NULL) *(chuue+i)+=96;
else *(chuue+i)+=32;
t=*(chasc+i)<<(8-k);
t>>=2;
k+=2;
}
*(chuue+3)=*(chasc+2)&63;
if(*(chuue+3)==NULL) *(chuue+3)+=96;
else *(chuue+3)+=32;
}
1.3 在NXP的LPC21XX、LPC22XX中,规定“中断向量表中所有数据32位累加和为0,否则程序不能脱机运行。”
flash地址 | 内容 |
---|---|
0x0000 0000 | LDR PC, ResetAddr |
0x0000 0004 | LDR PC, UndefinedAddr |
0x0000 0008 | LDR PC, SWI_Addr |
0x0000 000C | LDR PC, PrefetchAddr |
0x0000 0010 | LDR PC, DataAbortAddr |
0x0000 0014 | 检查字,使这个表中8个字的累加和为0 |
0x0000 0018 | LDR PC, IRQ_Addr |
0x0000 001C | LDR PC, FIQ_Addr |
由于这个检查字的存在,我们在读入hex文件后,要把其他7个字相加为sum,再用0-sum得到检查字。
// 必须将下面四个字节(检查字)改变,才能正确运行
data[0x14] = data[0x15] = data[0x16] = data[0x17] = 0;
UINT* pTemp=(UINT*)data;// data是由hex文件转换为bin数据
UINT Sum=0;
for (i=0;i<8;i++)
{
Sum += pTemp[i];
}
pTemp[5]=0-Sum;
2. ISP命令及应答
2.1 命令格式
命令名称 | 命令代码 | 命令格式 |
---|---|---|
ISP_SYNCHRONIZEd | ‘?’ | 自动波特率程序同步指令 |
CMD_UNLOCKED | ‘U’ | 解锁 <解锁代码> |
CMD_SET_BAUDRATE | ‘B’ | 置波特率 <波特率> <停止位> |
CMD_ECHO | ‘A’ | 回声 <设定> |
CMD_WRITE_RAM | ‘W’ | 写RAM <起始地址> <字节数> |
CMD_READ_FLASH | ‘R’ | 读存储器 <地址> <字节数> |
CMD_GO | ‘G’ | 运行 <地址> <模式> |
CMD_ERASE | ‘E’ | 擦除扇区 <起始扇区号> <结束扇区号> |
CMD_CHECK_BLANK | ‘I’ | 扇区查空 <起始扇区号> <结束扇区号> |
CMD_COMPARE | ‘M’ | 比较 <地址1> <地址2> <字节数> |
CMD_READ_CHIP_ID | ‘J’ | 读器件ID |
CMD_PREPARE_WRITE | ‘P’ | 准备写操作的扇区 <起始扇区号> <结束扇区号> |
CMD_RAM_COPY_TO_FLASH | ‘C’ | 将RAM 内容复制到Flash <Flash 地址> <RAM 地址> <字节数> |
CMD_READ_BOOT_VERSION | ‘K’ | 读Boot 代码版本 |
2.2 应答参数
应答名称 | 应答代码 | 含义 |
---|---|---|
RSP_SUCCESS | 0 | 命令被成功执行。只有当主机发出的命令被成功执行完毕后,才由ISP 处理程序发送。 |
RSP_INVALID_COMMAND | 1 | 无效命令 |
RSP_SRC_ADDR_ERROR | 2 | 源地址没有以字为边界 |
RSP_DST_ADDR_ERROR | 3 | 目标地址的边界错误 |
RSP_SRC_ADDR_NOT_MAPPED | 4 | 源地址没有位于存储器映射中。计数值必须考虑可用性。 |
RSP_DST_ADDR_NOT_MAPPED | 5 | 目的地址没有位于存储器映射中。计数值必须考虑可用性。 |
RSP_COUNT_ERROR | 6 | 字节计数值不是4 的倍数或是一个非法值。 |
RSP_INVALID_SECTOR | 7 | 扇区号无效或结束扇区号大于起始扇区号。 |
RSP_SECTOR_NOT_BLANK | 8 | 扇区非空 |
RSP_SECTOR_NOT_PREPARED_WRITE | 9 | 为写操作准备扇区命令未执行 。 |
RSP_COMPARE_ERROR | 10 | 源和目标数据不相等。 |
RSP_BUSY_FLASH | 11 | 编程硬件接口忙 |
RSP_PARAM_ERROR | 12 | 参数不足或无效参数 |
RSP_ADDR_ERROR | 13 | 地址没有以字为边界 |
RSP_ADDR_NOT_MAPPED | 14 | 地址没有位于存储器映射中。计数值必须考虑可用性。 |
RSP_CMD_LOCKED | 15 | 命令被锁定 |
RSP_INVALID_CODE | 16 | 解锁代码无效 |
RSP_INVALID_BAUD_RATE | 17 | 无效波特率设定 |
RSP_INVALID_STOP_BIT | 18 | 无效停止位设定 |
RSP_CODE_READ_PROTECTION_ENABLE | 19 | 代码读保护使能 |
3. 烧录主流程
源码:
void CArmISPV1_0Dlg::OnUploadFlash()
{
int i,nSendLen,nEndBase,nBaseCount;
CString strResult;
i = nSendLen = nEndBase =nBaseCount=0;
UpdateData(TRUE);
//复位状态
m_nPromgrammingStatus = enuStatus_NothingDone;
memset(binBuf,0,sizeof(binBuf));
if (g_hexFileName.Find(".bin") != -1){ // bin File
FILEReadByName(g_hexFileName,binBuf,&g_nBinTotalLen);
}else{ // hex File
if(!OpenHexFile(g_hexFileName,binBuf,g_nBinTotalLen)) {
strResult = "打开HEX文件出错!";
goto OVER;
}
}
i = g_nBinTotalLen%4;
if(i) g_nBinTotalLen += (4 - i);
nEndBase = g_nBinTotalLen/4096;
if(!GetFlashSector(m_nStartSector,m_nEndSector,g_nStartBase,g_nEndEraseSector,g_nBinTotalLen))
{
MessageBox("扇区号输入有问题,Sector Number wrong!");
strResult = "扇区号输入有问题,Sector Number wrong!";
goto OVER;
}
m_ProgressBar.SetRange ((unsigned short)0,(unsigned short)MAX_RANGE);
m_ProgressBar.SetPos (0);
if (!(ISPSynchroniz()))//m_nPromgrammingStatus||
{ strResult = "芯片握手失败"; goto OVER; }
if(!ChangeFlash(CMD_UNLOCKED,0,0))
{ strResult = "解锁失败!"; goto OVER; }
if(m_nPromgrammingStatus < enuStatus_Echo)
ChangeFlash(CMD_ECHO,0,0);
if (!ChangeFlash(CMD_PREPARE_WRITE,g_nStartBase,g_nEndEraseSector))
{ strResult = "准备擦除Flash失败!"; goto OVER; }
strResult = "正在擦除Flash...";
GetDlgItem(IDC_COM_OPEN_STATUS)->SetWindowText(strResult);
if(!ChangeFlash(CMD_ERASE,g_nStartBase,g_nEndEraseSector))
{ strResult = "擦除Flash失败!"; goto OVER; }
strResult = "擦除Flash成功,开始写Flash...";
GetDlgItem(IDC_COM_OPEN_STATUS)->SetWindowText(strResult);
g_dwStart = GetTickCount();
for (nBaseCount=0;nBaseCount<nEndBase;nBaseCount++)
{
i=3;
while (i)
{
i--;
if(Send2RamAndCopy2Flash(4096,binBuf+4096*nBaseCount,nBaseCount,TODO_COPY))
break;
}
if(i==0)
{ strResult = "下载Flash失败!"; goto OVER; }
}
i=3;
while (i)
{
i--;
if(Send2RamAndCopy2Flash(g_nBinTotalLen-4096*nBaseCount,
binBuf+4096*nBaseCount,
nBaseCount,TODO_COPY))
break;
}
if(i==0) { strResult = "下载Flash失败!"; goto OVER; }
else strResult = "下载Flash成功。";
m_nPromgrammingStatus = enuStatus_FlashLoad;
if (m_CheckAfterLoad.GetCheck ())
{
if(!m_bCheckComOpen.GetCheck())
OnComOpen();
OnCheckData ();
goto END_ALL;
}
OVER:
GetDlgItem(IDC_COM_OPEN_STATUS)->SetWindowText(strResult);
END_ALL:
OnComClose() ;
}
4. 编译方法
使用VS2017打开项目/解决方案 《armISPV1_0.sln》。
选择自己windows中的SDK和工具集。
5. 使用说明
和官方的烧写软件使用方法几乎相同。增加一个功能:将汉字库《hzk16.bin》写入到LPC2138偏移量为128kB起始的空间。《hzk16.bin》大小约为260kB。
7ee7da2d-dde0-4b21-aa41-37bc654fb190