#include <REG52.H>
/*主程序*/
void main (void)
{
SCON = 0x50; /* SCON: 模式1, 8-bit UART, 使能接收*/
TMOD |= 0x20; /* TMOD: timer 1, mode 2,
8-bit reload*/
TH1 = 0xFD; /* TH1: reload value for 9600 baud @ 11.0592MHz */
TR1 = 1; /* TR1: timer 1 run */
EA = 1; /*打开总中断*/
ES = 1; /*打开串口中断*/
while (1) /*主循环不做任何动作*/
{
}
}
void UART_SER (void) interrupt 4 //串行中断服务程序
{
unsigned char Temp; //定义临时变量
if(RI) //判断是接收中断产生
{
RI=0; //标志位清零
Temp=SBUF; //读入缓冲区的值
P1=Temp; //把值输出到P1口,用于观察
SBUF=Temp; //把接收到的值再发回电脑端
}
if(TI) //如果是发送标志位,清零
TI=0;
}
2.51单片机与电脑串口通信的C程序,最好是中断方式的
#include <reg51.h>
#include <string.h>
unsigned char ch;
bit read_flag= 0 ;
void init_serialcom( void ) //串口通信初始设定
{
SCON = 0x50 ; //UART为模式1,8位数据,
允许接收
TMOD |= 0x20 ; //定时器1为模式2,8位自动重装
PCON |= 0x80 ; //SMOD=1;
TH1 = 0xFD ; //Baud:19200 fosc="11".0592MHz
IE |= 0x90 ; //Enable Serial Interrupt
TR1 = 1 ; // timer 1 run
TI=1;
}
//向串口发送一个字符
void send_char_com( unsigned char ch)
{
SBUF=ch;
while (TI== 0);
TI= 0 ;
}
void serial () interrupt 4 using 3 //串口接收中断函数
{
if (RI)
{
RI = 0 ;
ch=SBUF;
read_flag= 1 ; //就置位取数标志
}
}
main()
{
init_serialcom(); //初始化串口
while ( 1 )
{
if (read_flag) //如果取数标志已置位,就将读到的数从串口发出
{
read_flag= 0 ; //取数标志清0 send_char_com(ch);
}
}
}
3.// 单片机串行口发送/接收程序,每接收到字节即发送出去
// 和微机相接后键入的字符回显示在屏幕上
// 可用此程序测试
#include <reg51.h>
#define XTAL 11059200 // CUP 晶振频率
#define baudrate 9600 // 通信波特率
void main(void)
{
unsigned char c;
TMOD = 0x20; // 定时器1工作于8位自动重载模式, 用于产生波特率
TH1=(unsigned char)(256 - (XTAL / (32L * 12L * baudrate)));
TL1=(unsigned char)(256 - (XTAL / (32L * 12L * baudrate))); // 定时器0赋初值
SCON = 0x50;
PCON = 0x00;
TR1 = 1;
IE = 0x00; // 禁止任何中断
while(1)
{
while(RI == 0);
RI = 0;
c = SBUF; // 从缓冲区中把接收的字符放入c中
SBUF = c; // 要发送的字符放入缓冲区
while(TI == 0);
TI = 0;
}
}
4.//////////////// /////////////////////////////////////////////////////////
//E51Pro.c
//Easy 51Pro编程器主程序,负责通讯,管理编程操作
/////////////////////////////////////////////////////////////////////////
#include <E51Pro.h>
BYTE ComBuf[18];//串口通讯数据缓存,发送和接收都使用
UINT nAddress;//ROM中地址计数
UINT nTimeOut;//超时计数
ProWork pw;//编程器一般操作
void Delay_us(BYTE nUs)//微秒级延时<255us
{
TH0=0;
TL0=0;
TR0=1;
while(TL0<nUs)//利用T0做定时计数器,循环采样,直到达到定时值
{
}
TR0=0;
}
void Delay_ms(UINT nMs)//豪秒级的延时<65535ms
{
UINT n=0;
TR0=1;
while(n<nMs)////利用T0做定时计数器,循环采样,直到达到定时值
{
TH0=0;
TL0=20;
while(TH0<4)
{
}
n++;
}
TR0=0;
}
BOOL WaitComm()//等待上位机的命令,18字节
{
BYTE n=0;
RI=0;
while(!RI){}//等待第一个字节
ComBuf[n]=SBUF;
RI=0;
n++;
for(n;n<=17;n++)
{
nTimeOut=0;
while(!RI)
{
nTimeOut++;
if(nTimeOut>10000)//后17个字节都有超时限制
return 0;
}
ComBuf[n]=SBUF;
RI=0;
}
return 1;
}
BOOL WaitResp()//等待上位机回应,1字节,有超时限制
{
nTimeOut=0;
RI=0;
while(!RI)
{
nTimeOut++;
if(nTimeOut>50000)
{
return 0;
}
}
RI=0;
ComBuf[0]=SBUF;
return 1;
}
BOOL WaitData()//写器件时等待上位机数据,18字节,有超时限制
{
BYTE n;
RI=0;
for(n=0;n<=17;n++)
{
nTimeOut=0;
while(!RI)
{
nTimeOut++;
if(nTimeOut>10000)
{
return 0;
}
}
RI=0;
ComBuf[n]=SBUF;
}
return 1;
}
void SendData()//发送数据或回应操作完成,18字节
{
BYTE n=0;
for(n;n<=17;n++)
{
TI=0;
SBUF=ComBuf[n];
while(!TI){}
TI=0;
}
}
void SendResp()//回应上位机1个字节,在写器件函数中使用
{
TI=0;
SBUF=ComBuf[0];
while(!TI){}
TI=0;
}
void SetVpp5V()//设置Vpp为5v
{
P3_4=0;
P3_3=0;
}
void SetVpp0V()//设置Vpp为0v
{
P3_3=0;
P3_4=1;
}
void SetVpp12V()//设置Vpp为12v
{
P3_4=0;
P3_3=1;
}
void RstPro()//编程器复位
{
pw.fpProOver();//直接编程结束
SendData();//通知上位机,表示编程器就绪,可以直接用此函数因为协议号(ComBuf[0])还没被修改,下同
}
void ReadSign()//读特征字
{
pw.fpReadSign();
SendData();//通知上位机,送出读出器件特征字
}
void Erase()//擦除器件
{
pw.fpErase();
SendData();//通知上位机,擦除了器件
}
void Write()//写器件
{
BYTE n;
pw.fpInitPro();//编程前的准备工作
SendData();//回应上位机表示进入写器件状态,
可以发来数据
while(1)
{
if(WaitData())//如果等待数据成功
{
if(ComBuf[0]==0x07)//判断是否继续写
{
for(n=2;n<=17;n++)//ComBuf[2~17]为待写入数据块
{
if(!pw.fpWrite(ComBuf[n]))//<<< <<<<<<调用写该器件一个单元的函数
{
pw.fpProOver();//出错了就结束编程
ComBuf[0]=0xff;
SendResp();//回应上位机一个字节,
表示写数据出错了
WaitData();//等待上位机的回应后就结束
return;
}
nAddress++;//下一个单元
}
ComBuf[0]=1;//回应上位机一个字节,表示数据块顺利完成,请求继续
SendResp();
}
else if(ComBuf[0]==0x00)//写器件结束
break;
else//可能是通讯出错了
{
pw.fpProOver();
return;
}
}
else//等待数据失败
{
pw.fpProOver();
return;
}
}
pw.fpProOver();//编程结束后的工作
Delay_ms(50);//延时等待上位机写线程结束
ComBuf[0]=0;//通知上位机编程器进入就绪状态
SendData();
}
void Read()//读器件
{
BYTE n;
pw.fpInitPro();//先设置成编程状态
SendData();//回应上位机表示进入读状态
while(1)
{
if(WaitResp())//等待上位机回应1个字节
{
if(ComBuf[0]==0)//ComBuf[0]==0表示读结束
{
break;
}
else if(ComBuf[0]==0xff)//0xff表示重发
{
nAddress=nAddress-0x0010;
}
for(n=2;n<=17;n++)//ComBuf[2~17]保存读出的数据块
{
ComBuf[n]=pw.fpRead();//<<<<<<<<<<调用写该器件一个单元的函数
nAddress++;//下一个单元
}
ComBuf[0]=6;//向上位机发送读出的数据块
SendData();
}
else
break;//等待回应失败
}
pw.fpProOver();//操作结束设置为运行状态
ComBuf[0]=0;//通知上位机编程器进入就绪状态
SendData();
}
void Lock()//写锁定位
{
pw.fpLock();
SendData();
}
/////////////////////////////////////////////////////////////////////////////////
//所支持的FID,请在这里继续添加
/////////////////////////////////////////////////////////////////////////////
extern void PreparePro00();//FID=00:AT89C51编程器
extern void PreparePro01();//FID=01:AT89C2051编程器
extern void PreparePro02();//FID=02:AT89S51编程器
void main()
{
SP=0x60;
SetVpp5V();//先初始化Vpp为5v
SCON=0x00;
TCON=0x00;
//PCON=0x00;//波特率*2
IE=0x00;
//TMOD: GATE|C/!T|M1|M0|GATE|C/!T|M1|M0
// 0 0 1 0 0 0 0 1
TMOD=0x21;//T0用于延时程序
TH1=0xff;
TL1=0xff;//波特率28800*2,注意PCON
//SCON: SM0|SM1|SM2|REN|TB8|RB8|TI|RI
// 0 1 0 1 0 0 0 0
SCON=0x50;
TR1=1;
Delay_ms(1000);//延时1秒后编程器自举
ComBuf[0]=0;
SendData();
while(1)//串口通讯采用查询方式
{
if(!WaitComm())//如果超时,通讯出错
{
Delay_ms(500);
ComBuf[0]=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;
//case 4:
// break;
default: ComBuf[0]=0xff;
ComBuf[1]=0xff; //表示无效的操作
break;
}
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;
}
}
}
5.void InitSerial(void)
{
TMOD = 0x20; // T1 方式2
PCON=0x00; // PCON=00H,SMOD=0 PD = PCON.2 = 1 进入掉电模式
TH1 = TL1 = BAUD_9600; // BAUD: 9600
SCON = 0x50; // 串行通信方式1 REN=1 允许接收
ET1 = 0; // 不允许中断
TR1 = 1; // 开启定时器1
IE = 0; // 关闭所有中断允许位
memset(&SerialBuf, 0x00, SERIAL_BUF_LEN); // 初始化SerialBuf[SERIAL_BUF_LEN]
}
/********************************************************
**名称:SendByte()
**功能:串口发送一个字节
**输入:ucData
**返回:无
**说明:无
********************************************************/
void SendByte(unsigned char ucData)
{
SBUF = ucData;
while(!TI)
{
_CLRWDT_;
}
TI = 0;
}
RS232串口通信程序
#include <AT89X52.H>
unsigned char code dispcode1[]={" welcome! "};
unsigned char code dispcode2[]={"www.zhaojian.com"};
unsigned char i,j,k,l,DData;
sbit RS = P3^5;
sbit RW = P3^6;
sbit E = P3^7;
unsigned char m=0;
void delay()
{for(l=0;l<=100;l++){}}
void enable() //write order
{
RS=0;RW=0;E=0;delay();E=1;
}
void enable2() //write data
{
RS=1;RW=0;E=0;delay();E=1;
}
void initializtion() //lcd initializtion
{
for(i=0;i<=100;i++)
P0=0x01;enable();
P0=0x38;enable();
P0=0x0f;enable();
P0=0x06;enable();
}
void Display(m,DData) // display data
{
P0=0xC0+m; //write address
enable();
P0=DData; //write data
enable2();
}
void Esisr() interrupt 4 //串口接收中断服务程序
{
unsigned char temp;
ES=0;
if(RI == 1)
{
RI = 0;
temp = SBUF; //接收数据
SBUF=temp; //将接收到的数据发送至PC机
Display(m,temp); //将接收到的数据送LCD显示
while(!TI); //等待数据发送完成
TI=0;
m++;
if(m>16)
m=0;
}
ES=1;
}
void system_initial(void) //system initializtion
{
TMOD=0x21;// 定时器1工作方式2, 定时器0工作方式1
PCON=0x00;//数据传输率选择。
SCON=0x50;//串口工作方式选择, 并打开接收允许。
TH1=0xfd;//定时器赋初值。
TL1=0xfd;//波特率9600bit/s
TR1=1;//启动定时器。
EA = 1; //开总中断。
ES = 1;
}
void main()
{
initializtion();
P0=0x80;enable();
for(j=0;j<=15;j++)
{
P0=dispcode1[j];
enable2();
}
P0=0xC0;enable();
for(k=0;k<=15;k++)
{
P0=dispcode2[k];
enable2();
}
system_initial();
while(1);
}
#include <reg51.h>
#include <string.h>
#define length 4 //数据长度
unsigned char inbuf[length];
unsigned char checksum,counter;
bit flag = 0; //取数标记
main()
{
init_serial(); //串行口初始化
while (1)
{
if (flag!=0) //如果取数标志已置位,
就将读到的数从串口发出
{
flag= 0; //取数标志清0
send_string(inbuf,length); //向串口发送字符串
}
}
}
/* 串行口初始化 */
void init_serial( void )
{
SCON = 0x50; //串行工作方式1, 8位异步通信方式
TMOD |= 0x20; //定时器1, 方式 2, 8位自动重装
PCON |= 0x80; //SMOD=1,表示数据传输率加倍
TH1 = 0xF4; //数据传输率:4800 fosc=11.0592MHz
IE |= 0x90; //允许串行中断
TR1 = 1; //启动定时器1
}
/* 向串口发送一个字符 */
void send_char( unsigned char x)
{
SBUF=x;
while (TI== 0 );
TI= 0;
}
/* 向串口发送一个字符串,string_length为该字符串长度 */
void send_string( unsigned char *s, unsigned int string_length)
{
unsigned int i= 0;
do
{
send_char(*(s + i)); //向串口发送一个字符
i++;
}
while ( i<string_length);
}
/* 串口接收中断函数 */
void serial () interrupt 4 using 3
{
if (RI)
{
unsigned char x;
RI = 0;
x=SBUF; //接收字符
if ( x> 127 )
{
counter= 0;
inbuf[counter]=x;
checksum= x- 128;
}
else
{
counter++;
inbuf[counter]=x;
checksum ^= x;
if ((counter==(length- 1)) && (!checksum))
{
flag= 1; //如果串口接收的数据达到length个,
且校验没错,
//就置位取数标志
}
}
}
}
下面是我们完成本次实验的源程序代码,使用Keil编译软件,将其编译生成HEX文件,然后,通过A51编程器烧入AT89S51芯片即可。
#include "reg51.h"
#include <absacc.h>
Unsigned char code tab[]={0xc0,0xf9,0xa4,0xb0,
0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned char dat;
void Init_Com(void)
{
TMOD = 0x20; //定时器工作方式2,初值自动装入
PCON = 0x00; //波特率不增倍
SCON = 0x50; //串行工作方式设定
TH1 = 0xFd; //定时器初值高位
TL1 = 0xFd; //定时器初值低位
TR1 = 1; //启动定时器
}
/*函数功能:LED数码管延时程序*/
void delay(void)
{
int k;
for(k=0;k<600;k++);
}
/*函数功能:LED数码管显示程序*/
void display(int k)
{
P2=0xfe; //位选
P0=tab[k/1000]; //显示千位数字
delay(); //延时
P2=0xfd; //位选
P0=tab[k%1000/100]; //显示百位数字
delay(); //延时
P2=0xfb; //位选
P0=tab[k%100/10]; //显示十位数字
delay(); //延时
P2=0xf7; //位选
P0=tab[k%10]; //显示个位数字
delay(); //延时
P2=0xff; //位选
}
/*函数功能:主程序*/
void main()
{
P2=0xff; //端口初始化,关LED显示
P0=0xff;
Init_Com(); //调用串口初始化程序
while(1) //主循环
{
if ( RI ) //判断是否收到数据
{
dat = SBUF; //接收数据
RI = 0; //软件清除标志位
}
display(dat-48); //显示收到的数据
}
}
我也是第一次做AT89S52的串口通信实验,分享这个测试程序:
/***********************************************
程序名称:51串口通信测试、演示程序,晶振11.0592M
程序功能:单片机依次发送0~F这16个数至上位机,通过串口调试软件 进行hex观察
*******************************************/
#include <AT89X52.h>
#define uchar unsigned char
#define uint unsigned int
uchar dis[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
/*****n(ms)延时子程序*****/
void delayms(uint ms) //延时ms
{
uchar i;
while(ms--)
{
for(i=0;i<120;i++);
}
}
/*****主程序*****/
void main()
{
int i;
TMOD=0x20; //定时器1工作于8位自动重载模式, 用于产生波特率
TH1=0xFD; //波特率9600
TL1=0xFD;
SCON=0x50; //设定串行口工作方式
TR1=1; //启动定时器1
while(1)
{
for(i=0;i<16;i++)
{
SBUF=dis[i];
while(!TI); //等特数据传送
TI=0; //清除数据传送标志
delayms(500);
}
}
}
程序功能是依次向上位机发送0~F这16个数,用串口调试助手观察,需要勾选“HEX显示”选项,如图:
51串口实验:数码管显示电脑键盘按键ASCII值
这不仅可以通过一个按钮控制单片机向电脑发送字符串,而且还可以通过数码管显示电脑键盘上按键的ASCII值。
本实验除需要单片机最小系统外,还需用到自制多功能数码管显示实验板一文中的数码管实验板和简洁的RS232串口通信电路与串口测试程序文中的串口通信电路,另需一只轻触式按钮。
系统连接:P1.0接轻触式按钮K1,P0口输出数码管段码,P2口为数码管位选信号,在此实验中只用到了十位和个位显示。
操作说明:打开串口调试助手之类的软件(这次不要勾选“HEX显示”),当按下单片机系统的K1按钮时,单片机向主机发送字符串"欢迎光临万用电路板http://www.jiangx.net/",在串口调试软件中看得到;当按下电脑键盘上任一按键时,数码管则显示出该键的ASCII键值。
程序如下:
/**************************************************************
程序名称:51串口通信程序,晶振11.0592M
程序功能:单片机接收主机的数据,然后将数据通过数码管显示,再将数据发回主机
**************************************************************/
#include <AT89X52.h>
#define uchar unsigned char
uchar key_s, key_v, tmp;
char code str[] = "欢迎光临万用电路板http://www.jiangx.net/";
uchar code dis[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
sbit K1 = P1^0; //字符串发送按钮
/*****n(ms)延时子程序*****/
void delayms(uchar ms) //延时ms
{
uchar i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}
/*****串口传送一个字符*****/
void send_char(uchar txd)
{
SBUF = txd;
while(!TI); //等特数据传送
TI = 0; //清除数据传送标志
}
/*****串口传送字符串*****/
void send_str()
{
uchar i = 0;
while(str[i] != '\0')
{
SBUF = str[i];
while(!TI); //等特数据传送
TI = 0; //清除数据传送标志
i++; //下一个字符
}
}
/*****扫描按键*****/
bit scan_key()
{
key_s = 0x00;
key_s |= K1;
return(key_s ^ key_v);
}
/*****按键处理*****/
void proc_key()
{
if((key_v & 0x01) == 0) //K1按下
{
send_str(); //传送字串
}
}
/*****显示子程序*****/
void display()
{
P0=dis[tmp&0x0f];
P2=0x7f;
delayms(10); //个位显示
P0=dis[tmp/16];
P2=0xbf;
delayms(10); //十位显示
}
/*****主函数*****/
main()
{
TMOD = 0x20; //定时器1工作于8位自动重载模式, 用于产生波特率
TH1 = 0xFD; //波特率9600
TL1 = 0xFD;
SCON = 0x50; //设定串行口工作方式
PCON &= 0xef; //波特率不倍增
TR1 = 1; //启动定时器1
IE = 0x0; //禁止任何中断
while(1)
{
if(scan_key()) //扫描按键
{
delayms(10); //延时去抖动
if(scan_key()) //再次扫描
{
key_v = key_s; //保存键值
proc_key(); //键处理
}
}
if(RI) //是否有数据到来
{
RI = 0;
tmp = SBUF; //暂存接收到的数据
send_char(tmp); //回传接收到的数据
}
display();
}
}