第五章 AT89S52 下载器的制作
5.1 LuckyProg S52 概述
ATMEL 推出的89S 系列单片机具有类似AVR 的ISP 编程功能,单片机ISP 接口为用户提供了一种串行编程方法。ISP 功能就象操作串行EEPROM 存储器那样使单片机的编程变得简单方便。
本章将介绍一种用AVR(AT90S2313)实现的AT89S52 单片机ISP 编程器:LuckyProg S52。
LuckyProg S52 工作原理:
如图5-1 所示,编程单片机AT90S2313 与计算机用RS232 串行接口通信,2312 从串行口获得编程命令和数据后用ISP 程序下载接口对AT89S52 编程。

图5-1 LuckyProg S52 功能框图:
图5-2 为LuckyProg S52 的电中原理图,图中AT90S2313 的UART 口与计算机RS232标准串行接口之间的电平转换被省略,可参考图4-1。
用ISP 口下载程序时AT89S52 必需有时钟源,为此在XTAL0 与XTAL1 间接一个6MHz晶振,ISP 数据通信口MOSI、MISO 和SCK 均接在AT90S2313 的普通I/O 口上,而RST脚由I/O 口通过一个三极管控制。

图5-2 LuckyProg S52 ISP 下载电路原理图
5.2 AT89S52 ISP 功能简介
串行数据的输入与输出时序
数据在SCK 的上升沿输入到52,SCK 的下降沿输出。另外必须保证串行时钟SCK 的周期至少大于是6 个CPU 时钟(XTAL1 上的)周期。
串行编程算法
1. 上电过程
在VCC 和GND 间加上电源的同时RST 脚加高电平。至少等待时10ms。
2.发送串行编程使能命令
如果通信失步则串行编程失败。如果同步则在编程时钟的第四个字节器件响应0X69,表示编程使能命令成功。不论响应正确与否,必需保证四字节的时钟周期。
3.写程序
通过写指令可对程序存储器的每一字节进行编程。一个写指令使单片机进入自定时的编程模式,在5V 编程电压下典型编程时间少于1ms。
4.读程序
任意位置的程序数据可通过读指令从引脚步MISO/P1.6 读出,实现定写入数据的校验。
5.编程操作结束后将RST 引脚拉低,使器件进入正常工作模式。
编程指令

表 5-1 AT89S52 ISP 下载命令
注:1.锁定位与模式对应
模式1(B1=0、B2=0):无锁定保护
模式2(B1=0、B2=1):内部锁定位1 有效
模式3(B1=1、B2=0):内部锁定位2 有效
模式4(B1=1、B2=1):内部锁定位3 有效
1.在模式3 和4 下不能读厂标
2.将Reset 拉高后SCK 至少保持64 个时钟周期才可执行编程允许命令,在页读写中命令和地址后数据由0到255 的顺序传送,只有接收完这256 字节的数据后下一个指令才能就绪。
5.3 程序设计
延时功能函数
通常在单片机C 程序里的延时模块为一个计数循环,延时时间的长短往往是先估计,后通过实验或仿真等方法来验证。avr-libc 提供了两个延时API 函数,利用这两个函数我们可以较精确的产生所需的延时函数。
第一个函数声明如下:
void _delay_loop_1(unsigned char count);
它的延时时间为count × 3 个系统时钟周期,计数器count 为8 位无符号整数,第二个函数声明格式为:
void _delay_loop_2(unsigned int count);
它将延时count × 4 个系统时钟周期,计数器count 为16 位无符号整数。
若要调用这两个函数,需先包函头文件 delay.h,实际上这两个函数的实现就在此文件里,我们可以从WINAVR安装目录里的\AVR\INCLUDE\AVR子目录里找到并查看它的内容,以下为_delay_loop_2 的源程序:
static inline void
_delay_loop_2(unsigned int __count)
{
asm volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" (__count)
: "0" (__count)
);
}
首先要说明的是,由于函数的实现写在了头文件里,所以被多个源文件包含是可能的,为此有必要将它声明成局部函数(static)。函数内容为内嵌汇编方式,有关内嵌汇编看第8章,这里我们只需知道它的执行需要count * 4 个时钟周期。要注意的是,inline 关键字说明了函数是内连函数,内连函数如同汇编程序里的宏,编译结果是在每一个调用的地方插入一次函数的内容。为此有程序空间要求且调用频率高的应用中再写一个延时函数是有必要的。
以下是为编程器主控单片机AT90S2313 写的延时程序,它以毫秒为单位执行延时任务。
void DelayMs(unsigned int t)
{
unsigned int i;
for(i=0;i_delay_loop_2(FEQ * 250 - 1);
}
其中FEQ 为系统振荡频率(以M 为单位)。
AT90S2313 程序清单
#include
#include
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define SETLED PORTB&=0xF7
#define CLRLED PORTB|=0X80
#define FREQ 6 //时钟 6MHz
#define MOSI 4
#define MISO 6
#define SCK 0
#define RST 1
#define ACK 0xaa
#define ERR 0XBB
#define CMOD 0XCC;
uchar g_bTxdPos=0; //UART 发送定位数
uchar g_bTxdLen=0; //发送长度设置绶冲
uchar g_bRxdPos=0; //UART 接收定位数
uchar g_bRxdLen=0; //接收长度设置绶冲
uchar g_aMemBuf[32]; //数据绶冲
void DelayMs(uint t)
{
uint i;
for(i=0;i{
_delay_loop_2(250*FREQ-1);//delay 1ms
wdt_reset();
}
}
void DelayBus(void)
{
_delay_loop_1(4);
wdt_reset();
}
void ISP_WriteByte(uchar dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(dat&0x80)
sbi(PORTB,MOSI);
else
cbi(PORTB,MOSI);
sbi(PORTB,SCK);
DelayBus();
cbi(PORTB,SCK);
DelayBus();
dat<<=1;
}
}
uchar ISP_ReadByte(void)
{
uchar ret=0;
uchar i;
for(i=0;i<8;i++)
{
ret<<=1;
sbi(PORTB,SCK);
DelayBus();
if(PIND&0x40)
ret|=1;
cbi(PORTB,SCK);
DelayBus();
}
return ret;
}
///////////////////////串口处理/////////////////////////////
//接收中断
SIGNAL(SIG_UART_RECV)
{
uchar c=UDR;
if(g_bRxdLen>0)
{
g_aMemBuf[g_bRxdPos++]=c;
g_bRxdLen--;
}
}
//发送中断
SIGNAL (SIG_UART_TRANS)
{
if(--g_bTxdLen>0)
UDR=g_aMemBuf[++g_bTxdPos];
}
//等待接收完成
void WaitRecv(void)
{
while(g_bRxdLen>0)
DelayBus();
}
//发送指定字节
void SendToUart(uchar size)
{
g_bTxdPos=0;
g_bTxdLen=size;
UDR=g_aMemBuf[0];
while(g_bTxdLen>0)
DelayBus();
}
//接收指定字节
void RecvFromUart(uchar size,uchar bwait)
{
g_bRxdPos=0;
g_bRxdLen=size;
if(bwait)
WaitRecv();
}
//////////////////////////////////////////////////////////
//S52 编程允许
uchar PrgEn(void)
{
//MOSI、SCK 设为输出
cbi(PORTB,SCK);
cbi(PORTB,MOSI);
sbi(DDRB,MOSI);
sbi(DDRB,SCK);
cbi(PORTB,RST);
DelayMs(100);
ISP_WriteByte(0xac);
ISP_WriteByte(0x53);
ISP_WriteByte(0);
if(ISP_ReadByte()==0x69)
return 1;
else
return 0;
}
//S52 复位
void PrgDs(void)
{
//MOSI、SCK 设为输入高阻
cbi(PORTB,MOSI);
cbi(PORTB,SCK);
cbi(DDRB,MOSI);
cbi(DDRB,SCK);
cbi(PORTB,RST);
DelayMs(500);
sbi(PORTB,RST);
}
//读FLASH
void ReadDevice(void)// CMD : 1
{
uchar i,j,k;
uchar pageaddress=g_aMemBuf[1];
uchar pagecount=g_aMemBuf[2];
if(!PrgEn())
{
g_aMemBuf[0]=ERR;
return ;
}
g_aMemBuf[0]=ACK;
SendToUart(1);
for(k=0;k{
ISP_WriteByte(0x30);
ISP_WriteByte(pageaddress++);//Write address
for(i=0;i<8;i++)
{
for(j=0;j<32;j++)
{
g_aMemBuf[j]=ISP_ReadByte();
}
SendToUart(32);
}
if(k&0x1)
SETLED;
else
CLRLED;
}
PrgDs();
CLRLED;
g_aMemBuf[0]=ACK;
}
//写FLASH
void WriteDevice(void)//CMD : 3
{
uchar i,j,k;
if(PrgEn()==0)
{
g_aMemBuf[0]=ERR;
return ;
}
uchar pageaddress=g_aMemBuf[1];
uchar pagecount=g_aMemBuf[2];
for(k=0;k{
ISP_WriteByte(0x50);
ISP_WriteByte(pageaddress++); //Write address
SETLED;
for(i=0;i<8;i++)
{
g_aMemBuf[0]=3;
SendToUart(1);
RecvFromUart(32,1);
for(j=0;j<32;j++)
{
ISP_WriteByte(g_aMemBuf[j]);
DelayMs(1);
}
}
CLRLED;
//DelayMs(256);
}
PrgDs();
g_aMemBuf[0]=ACK;
}
//擦除
void EraseDevice(void) // CMD : 2
{
if(PrgEn()==0)
{
g_aMemBuf[0]=ERR;
return ;
}
ISP_WriteByte(0xac);
ISP_WriteByte(0x80);
ISP_WriteByte(0x0);
ISP_WriteByte(0x0);
DelayMs(1000);
PrgDs();
g_aMemBuf[0]=ACK;
}
//写锁定位
void WriteLockBits(void) // CMD: 4
{
uchar temp;
if(PrgEn()==0)
{
g_aMemBuf[0]=ERR;
return ;
}
temp=0xe0;
if(g_aMemBuf[1])
temp|=0x02;
if(g_aMemBuf[2])
temp|=0x1;
ISP_WriteByte(0xac);
ISP_WriteByte(temp);
ISP_WriteByte(0);
ISP_WriteByte(0);
g_aMemBuf[0]=ACK;
PrgDs();
}
//读锁定位
void ReadLockBits(void)//CMD :5
{
if(PrgEn()==0)
{
g_aMemBuf[0]=ERR;
return ;
}
ISP_WriteByte(0x24);
ISP_WriteByte(0);
ISP_WriteByte(0);
g_aMemBuf[0]=ISP_ReadByte();
g_aMemBuf[0]>>=2;
g_aMemBuf[0]&=7;
PrgDs();
}
//////////////////////////////////
//入口////////////////////////////
int main( void )
{
DelayMs(1000);
//i/o 口初始化
PORTB=0X08;
DDRB=0X09;
DDRD=0;
PORTD=0XFF;//上拉开
//uart 初始化
UCR=(1<UBRR=38; //UBRR=FCK/(9600*16) -1
wdt_enable(WDTO_1S);
//复位目标板
PrgDs();
//复位延时
DelayMs(1000);
//开中断
sei ();
//主盾环
while(1)
{
RecvFromUart(3,1);
SETLED;
switch(g_aMemBuf[0])
{
case 0:
PrgDs();
g_aMemBuf[0]=ACK;
break;
case 1:
ReadDevice();
break;
case 2:
EraseDevice();
break;
case 3:
WriteDevice();
break;
case 4:
WriteLockBits();
break;
case 5:
ReadLockBits();
break;
case ACK:
g_aMemBuf[0]=ACK;
break;
default:
break;
}//switch
SendToUart(1);
CLRLED;
}//main loop
}//main
在写FLASH 存储器时先从计算机读取程序数据到g_aMemBuf 缓冲区,然后用页模式(查表5-1)写入,由于2313 内部RAM 有限,缓冲区g_aMemBuf 的大小定义为32 字节,为此写一页时必需从计算机读程序数据8 次。
上位机程序
上位机程序界面如图5-3 所示,它是由VisualBasic6.0 编写。可执行文件或VisualBasic 源代码可到 http://bitfu.zj.com 下载。
单片机教程,五系列(55讲)电子书全集下载论坛精选:
单片机教程,MCS51单片机从零开始 第一讲:初识单片机
http://bbs.huazhoucn.com/Topic.aspx?id=2539
单片机教程,MCS51单片机从零开始 第二讲:MCS-51单片机简述
http://bbs.huazhoucn.com/Topic.aspx?id=2540
单片机教程,MCS51单片机从零开始 第三讲:单片机相关常用名词解释
http://bbs.huazhoucn.com/Topic.aspx?id=2541
单片机教程,MCS51单片机从零开始 第四讲:计算机中数的表示及运算
http://bbs.huazhoucn.com/Topic.aspx?id=2542
单片机教程,MCS51单片机从零开始 第五讲:常用逻辑电路
http://bbs.huazhoucn.com/Topic.aspx?id=2544
单片机教程,MCS51单片机从零开始 第六讲:51单片机的结构及其组成
http://bbs.huazhoucn.com/Topic.aspx?id=2545
单片机教程,MCS51单片机从零开始 第七讲:51单片机的引脚
http://bbs.huazhoucn.com/Topic.aspx?id=2546
单片机教程,MCS51单片机从零开始 第八讲:8051单片机I/O引脚工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2547
单片机教程,MCS51单片机从零开始 第九讲:8051单片机的存储器结构
http://bbs.huazhoucn.com/Topic.aspx?id=2548
单片机教程,MCS51单片机从零开始 第十讲:编码及译码器工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2549
单片机教程,MCS51单片机从零开始 第十一讲:存储器的存储原理
http://bbs.huazhoucn.com/Topic.aspx?id=2550
单片机教程,MCS51单片机从零开始 第十二讲:51单片机的特殊功能寄存器
http://bbs.huazhoucn.com/Topic.aspx?id=2551
单片机教程,MCS51单片机从零开始 第十三讲:51单片机CPU的内部结构
http://bbs.huazhoucn.com/Topic.aspx?id=2552
单片机教程,MCS51单片机从零开始 第十四讲:定时器/计数器的基本结构及工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2553
单片机教程,MCS51单片机从零开始 第十五讲:51单片机的中断系统
http://bbs.huazhoucn.com/Topic.aspx?id=2554
单片机教程,MCS51单片机从零开始 第十六讲:51单片机的复位
http://bbs.huazhoucn.com/Topic.aspx?id=2555
单片机教程,MCS51单片机从零开始 第十七讲:51单片机执行指令的过程
http://bbs.huazhoucn.com/Topic.aspx?id=2556
单片机教程,MCS51单片机从零开始 第十八讲:51单片机的延时及时序分析
http://bbs.huazhoucn.com/Topic.aspx?id=2557
单片机教程,MCS51单片机从零开始 第十九讲:汇编语言基础
http://bbs.huazhoucn.com/Topic.aspx?id=2558
单片机教程,MCS51单片机从零开始 第二十讲:汇编语言及汇编过程
http://bbs.huazhoucn.com/Topic.aspx?id=2556
单片机教程,MCS51单片机从零开始 第二十一讲:汇编程序的基本结构
http://bbs.huazhoucn.com/Topic.aspx?id=2560
单片机教程,MCS51单片机从零开始 第二十二讲:51单片机的寻址方式
http://bbs.huazhoucn.com/Topic.aspx?id=2561
单片机教程,MCS51单片机从零开始 第二十三讲:数据传送类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2562
单片机教程,MCS51单片机从零开始 第二十四讲:算术运算类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2563
单片机教程,MCS51单片机从零开始 第二十五讲:逻辑运算及移位指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2564
单片机教程,MCS51单片机从零开始 第二十六讲:控制转移类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2565
单片机教程,MCS51单片机从零开始 第二十七讲:布尔变量操作指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2566
单片机教程,MCS51单片机从零开始 第二十八讲:伪指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2567