Easy 51Pro 的原理与扩充

这篇文章将让你很快掌握如何扩充 Easy 51Pro 对器件的支持。当然首先你必须先了解 Keil C（如果扩充 Easy 51Pro 串行编程器）或 VC(如果扩充 Easy Isp 下载线)。本文只介绍基本原理，至于串口通讯的细节和程序界面的制作请看源代码，里面有详细的注释。如果你正在学习单片机与 PC 的串口通讯，那么这套资料将对你非常有用。

本人编程水平有限，如果你发现了任何问题，欢迎指正。可以 Email 通知我(nie_zq@163.net)。常见问题我会贴在留言版。如果你是真心的想扩充器件，那么我们可以用 QQ 讨论(QQ：3813420)。如果你想用它作为商业用途，请先与本人联系。

首先介绍一下我常挂在嘴变的“FID”。FID=“Function IDentification”，我 E 文不是很好，所以只能取出个这样的名字。它标识一种器件的编程方法（包括读，写，擦等编程操作）。AT89C51 和 AT89C52,AT89C55 的编程方法是一样的，所以可以用同一个 FID，我把他们的 FID 定义为 0，还有 AT89CS51 与 AT89S52 的 FID=2，AT89C2051 的 FID=1。编程方法实际上就是控制编程过程的程序吧，C 语言里面的程序不是叫 Function 吗?

Easy 51Pro 串行编程器的扩充

编程器和上位机能协调的工作肯定是要有个通讯协议的。Easy 51Pro 的通讯协议很简单。每次编程操作，上位机发给编程器 18 个字节，编程器完成编程操作后再回应上位机 18 个字节（读，写过程略有不同，请看源程序）。这 18 个字节是什么东西呢？

0 | 1 | 2-17
编程操作的标识 | FID | 该操作相关的数据

编程操作标识是如何定义的？可以从源程序找到答案。

switch( ComBuf[0] ) //根据操作 ID 跳到不同的操作函数

{

case 0x00:

RstPro(); //编程器复位

break;

case 0x01:

ReadSign(); //读特征字

break;

case 0x02:

Erase(); //擦除器件

break;

case 0x03:

Write(); //写器件

break;

case 0x04:

Read(); //读器件

break;

case 0x05:

Lock(); //写锁定位

break;

default:

SendData();

break;

}

FID 已经解释过的，但是如何通过 FID 调用到控制器件编程操作的子程序呢？还是用程序说话：

在 e51pro.h 中定义了这样一个结构：

struct _prowork //定义编程器的一般操作

{

void (*fpInitPro)(); //编程前的准备工作

void (*fpReadSign)(); //读特征字

void (*fpErase)(); //擦除器件

BOOL (*fpWrite)(BYTE); //写器件

BYTE (*fpRead)(); //读器件

void (*fpLock)(); //写锁定位

void (*fpProOver)(); //编程结束后的工作

};

typedef struct _prowork ProWork;

ProWork pw; //编程器一般操作

看见没有，里面都是一堆函数的指针。没办法，Keil C 里面没有 class，在 struct 里面弄一堆函数指针也别有一方风味。

在调用编程操作（就是最上面那一堆程序 switch(ComBuf[0]){…}）之前，还要先进行这一堆程序：

switch( ComBuf[1] ) //根据 FID 设置 (ProWork)pw 中的函数指针

{

case 0: //at89c51 编程器

PreparePro00();//<<<<<<<<<<<<<<<<<<<<<<关键要看这里面的东西

break;

case 1: //at89c2051 编程器

PreparePro01();

break;

case 2: //at89s51 编程器

PreparePro02();

break;

//case 3: //扩充器件时，请继续向下添加

// break;

default:

ComBuf[0]=0xff;

ComBuf[1]=0xff; //表示无效的操作

break;

}

看看 AT89C51PRO.C 中的 PreparePro00()函数里面搞了些什么？

void PreparePro00() //设置 pw 中的函数指针，让主程序可以调用上面的函数

{

pw.fpInitPro=InitPro00;

pw.fpReadSign=ReadSign00;

pw.fpErase=Erase00;

pw.fpWrite=Write00;

pw.fpRead=Read00;

pw.fpLock=Lock00;

pw.fpProOver=ProOver00;

}

其中 InitPro00(),ReadSign00()…..ProiOver00()这些函数都写在了 AT89C51PRO.C 中，他们实现了控制编程器对 AT89C51 编程的具体细节。例如，再看看擦除 AT89C51 和插除 AT89S51 的 Flash Rom 是如何实现的。

void Erase00()//擦除 AT89C51

{

InitPro00();

//-----------------------------------------------------------------------------

//根据器件的 DataSheet，设置相应的编程控制信号

P2_6=1;

P2_7=0;

P3_6=0;

P3_7=0;

Delay_ms(1);

SetVpp12V();

Delay_ms(1);

P3_2=0;

Delay_ms(10);

P3_2=1;

Delay_ms(1);

//-----------------------------------------------------------------------------

ProOver00();

}

就是根据这个来的：

再看看 AT89S51PRO.C 中的：

void Erase02()//擦除 AT89S51

{

InitPro02();

//-----------------------------------------------------------------------------

//根据器件的 DataSheet，设置相应的编程控制信号

OutBuf[0]=0xac;//<<<<<<<<<<<<<<<看下表中的红框

OutBuf[1]=0x80;//<<<<<<<<<<<<<<<看下表中的红框

SendInstrc(4);

Delay_ms(500);

//-----------------------------------------------------------------------------

ProOver02();

}

根据这个来的：

在 Erase02()中看到调用了 SendInstrc(4)这样一个函数，有必要解释一下

void SendInstrc(BYTE nByte) //用 MOSI 串行发送命令的同时用 MISO 接收相关数据

{

BYTE n;

for(n=0;n<nByte;n++) //发送 nByte 个字节

{

ACC=OutBuf[n];

SCK=0;

MOSI=A_7; //SCK 低电平时输出一位

SCK=1;

B_7=MISO; //SCK 高电平时接收一位

SCK=0;

MOSI=A_6;

…

…

MOSI=A_1;

SCK=1;

B_1=MISO;

SCK=0;

MOSI=A_0;

SCK=1;

B_0=MISO;

SCK=0;

InBuf[n]=B;

}

}

贴个图来解释：

最后来看一个扩充器件的全过程：

1． 看懂器件手册上 Programming 的原理和过程细节。

2． 评估一下 Easy 51Pro 的硬件是否可以实现，包括编程电压，编程控制信号。一般可以用 Isp 编程的都没问题。

3． 让软件能识别出器件，这就要用传说中的“ChipManager”编辑了。用这个程序打开程序目录下的 ChipList.chip 文件，然后“增加器件”。也可以在空白列表上直接“增加器件”，然后保存为*.chip 文件，再把这个文件合并到 ChipList.chip，这就是使用“从文件导入”。

如果你发现编程方法和列表中的某 FID 一样，那么就直接用这个 FID 吧，这是最幸福的。注意：如果特征字没有 3 个字节，可以用 ff 补充。

4． 最关键的部分，要你编程解决问题了。如果器件能 ISP 的尽量用 ISP 吧。建一个*.c 文件，里面实现这些函数：

#include <e51pro.h>

void InitProXX() //编程前的准备工作

{

…

}

void ProOverXX() //编程结束后的工作，设置合适的引脚电平

{

…

}

void ReadSignXX() //读特征字

{

…

}

void EraseXX() //擦除器件

{

…

}

BOOL WriteXX(BYTE Data) //向器件写一个字节

{

…

}

BYTE ReadXX() //从器件读一个字节

{

…

}

void LockXX() //写锁定位

{

…

}

void PrepareProXX() //设置 pw 中的函数指针，让主程序可以调用上面的函数

{

pw.fpInitPro=InitProXX;

pw.fpReadSign=ReadSignXX;

pw.fpErase=EraseXX;

pw.fpWrite=WriteXX;

pw.fpRead=ReadXX;

pw.fpLock=LockXX;

pw.fpProOver=ProOverXX;

}

“XX”就是 FID。其实你不必写太多程序，如果发现它的编程方法与 AT89C51 相似，那就直接复制 AT89C51PRO.C 中的源程序，修改一下即可。如果与 AT89S51 相似呢，那就更好办了：

void Erase02()//擦除器件

{

InitPro02();

//-----------------------------------------------------------------------------

//根据器件的 DataSheet，设置相应的编程控制信号

OutBuf[0]=0xac;//<<<<<<<<<<<<<<<<<<修改这个

OutBuf[1]=0x80;//<<<<<<<<<<<<<<<<<<修改这个

SendInstrc(4);//<<<<<<<<<<<<<<<<<<<<注意要用几个字节

Delay_ms(500);

//-----------------------------------------------------------------------------

ProOver02();

}

最后把 PrepareProXX()添交到 e51Pro.C 中：

///////////////////////////////////////////////////////////////////////////////////////////////////

//所支持的 FID,请在这里继续添加

///////////////////////////////////////////////////////////////////////////////////////////////////

extern void PreparePro00();//FID=00:AT89C51 编程器

extern void PreparePro01();//FID=01:AT89C2051 编程器

extern void PreparePro02();//FID=02:AT89S51 编程器

extern void PrepareProXX();//<<<<<<<<<<<<<<<<<<<<<<<<<<<添加到末尾

还有：

switch(ComBuf[1]) //根据 FID 设置(ProWork)pw 中的函数指针

{

case 0: //at89c51 编程器

PreparePro00();

break;

case 1: //at89c2051 编程器

PreparePro01();

break;

case 2: //at89s51 编程器

PreparePro02();

break;

//case 3: //扩充器件时，请继续向下添加

// PrepareProXX();<<<<<<<<<<<<<<<<<<<<<<<<添加到末尾

// break;

default:

ComBuf[0]=0xff;

ComBuf[1]=0xff;//表示无效的操作

break;

}

5．测试你的程序，成功后把它贴出来与大家分享，可以先发个 Email 给我。