2016中国新兴城市排名:51单片机IO口模拟串口通讯C源程[机电之家行业下载站]

来源:百度文库 编辑:中财网 时间:2024/04/28 01:29:07
51单片机IO口模拟串口通讯C源程作者:jdzj868 来源:机电之家下载站 录入:jdzj868 更新时间:2009-9-11 16:43:13 点击数:0 【字体:

51 IO口模拟串口通讯C源程
#include
sbit BT_SND =P1^0;
sbit BT_REC =P1^1;
/**********************************************

IO 口模拟232通讯程序

使用两种方式的C程序 占用定时器0

**********************************************/

#define MODE_QUICK

#define F_TM F0

#define TIMER0_ENABLE TL0=TH0; TR0=1;
#define TIMER0_DISABLE TR0=0;

sbit ACC0= ACC^0;
sbit ACC1= ACC^1;
sbit ACC2= ACC^2;
sbit ACC3= ACC^3;
sbit ACC4= ACC^4;
sbit ACC5= ACC^5;
sbit ACC6= ACC^6;
sbit ACC7= ACC^7;

void IntTimer0() interrupt 1
{
F_TM=1;
}
//发送一个字符
void PSendChar(unsigned char inch)
{
#ifdef MODE_QUICK
ACC=inch;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

BT_SND=ACC0; //先送出低位
F_TM=0;
while(!F_TM);

BT_SND=ACC1;
F_TM=0;
while(!F_TM);

BT_SND=ACC2;
F_TM=0;
while(!F_TM);

BT_SND=ACC3;
F_TM=0;
while(!F_TM);

BT_SND=ACC4;
F_TM=0;
while(!F_TM);

BT_SND=ACC5;
F_TM=0;
while(!F_TM);

BT_SND=ACC6;
F_TM=0;
while(!F_TM);

BT_SND=ACC7;
F_TM=0;
while(!F_TM);

BT_SND=1;
F_TM=0;
while(!F_TM);


TIMER0_DISABLE; //停止timer
#else
unsigned char ii;

ii=0;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

while(ii<8)
{
if(inch&1)
{
BT_SND=1;
}
else
{
BT_SND=0;
}
F_TM=0;
while(!F_TM);
ii++;
inch>>=1;
}
BT_SND=1;
F_TM=0;
while(!F_TM);

#endif
TIMER0_DISABLE; //停止timer
}
//接收一个字符
unsigned char PGetChar()
{
#ifdef MODE_QUICK

TIMER0_ENABLE;
F_TM=0;
while(!F_TM); //等过起始位
ACC0=BT_REC;

TL0=TH0;

F_TM=0;
while(!F_TM);
ACC1=BT_REC;

F_TM=0;
while(!F_TM);
ACC2=BT_REC;

F_TM=0;
while(!F_TM);
ACC3=BT_REC;

F_TM=0;
while(!F_TM);
ACC4=BT_REC;

F_TM=0;
while(!F_TM);
ACC5=BT_REC;

F_TM=0;
while(!F_TM);
ACC6=BT_REC;

F_TM=0;
while(!F_TM);
ACC7=BT_REC;

F_TM=0;

while(!F_TM)
{
if(BT_REC)
{
break;
}
}
TIMER0_DISABLE; //停止timer
return ACC;
#else
unsigned char rch,ii;
TIMER0_ENABLE;
F_TM=0;
ii=0;
rch=0;
while(!F_TM); //等过起始位

while(ii<8)
{
rch>>=1;
if(BT_REC)
{
rch|=0x80;
}
ii++;
F_TM=0;
while(!F_TM);

}
F_TM=0;
while(!F_TM)
{
if(BT_REC)
{
break;
}

}
TIMER0_DISABLE; //停止timer
return rch;

#endif

}

AVR软件模拟串口程序



M48,8MHz
9600,1,8,1

输出:用定时器控制普通IO口输出位
输入:用外部中断+定时器,判断位的宽度

好几天没休息,利用闲暇写的,也没找到别人的参考程序,不过终于算是稳定了,其实还应该有很多其它的方法可以试一下,比如用PWM输出串行数据,用输入捕获接收数据,或定时查询,或用任意一个IO口中断,则每个引脚都有可能

现在还有些问题,全双工同时收发时发送偶尔出错,占用两个定时器有些浪费,以后再修改吧,最好加上各种波率

本程序为直接摘出部分,删了无关的部分,在此可能有些变量没用,或有段落遗漏,请谅

#include
#include
#include
#include

#define Sbit1() PORTD|=1<#define Sbit0() PORTD&=~(1<

volatile unsigned int
eep_ms,//毫秒计时
keytime, //等待时间
SoundOnTime; ////
volatile unsigned char
rdata,
key,
start=0,
keycode, //
*TxPoint,
rtime,
INT0_time, //中断次数

RxLength=0, //接收长度
RUDR, //摸拟串口接收的数据
TxLength, //串口发送数据长度
SUDR; //串口发送的数据

unsigned char arr[10],DispBuff[10];


void Initial_IO(void)//IO口初始化
{
DDRD = 0X82; //PD1串口输出,PD0串口输入,PD2模拟串口输入(INT0)
PORTD = 0X82; //PD1输出高电平
}

void Initial_INT0(void)
{
EICRA|=(1<EIMSK|=1<}

void Initial_timer0(void) //定时器0初始化
{
TCCR0B|=(1<TIMSK0|=(1<}

void Initial_timer1(void)
{
TCCR1A=(1<TCCR1B=(1<ICR1=1000;
TIMSK1|=(1<}

void Initial_timer2(void)
{
TCCR2B=(1<TIMSK2|=(1<}


void Initial_WDR(void) //看门狗初始化
{
wdt_enable(WDTO_1S);
wdt_reset();
}


void Initial(void)
{
Initial_IO();
Initial_timer0();
Initial_timer1();
Initial_timer2();
Initial_INT0();
Initial_WDR();
sei();
}


/*启动串口发送*/
void SendData(unsigned char *P,unsigned char DataLength)
{
TxLength=DataLength;
TxPoint=P;
start=0;
}


int main (void)
{

Initial();

while(1)
{
wdt_reset();
if((rdata)&&(eep_ms>10))//收到数据延时10mS后启动发送,回送验证数据
{
key=0;
SendData(&DispBuff[0],9);//发送DispBuff[0]的9位数据
while(TxLength);//等待发送完成
rdata=0;
eep_ms=0;
}
}
}


/*定时器0,100us溢出中断*/
SIGNAL(SIG_OVERFLOW0)
{
TCNT0=151;//重载数据,计时区间为151---255,共104uS,一个位的时间
if(TxLength)//
{
if(start==0)
{
Sbit0();//起始位
SUDR=*(TxPoint++);
}
else
{
if((start<=8))
{
if(SUDR&(1<<(start-1)))Sbit1();//数据1
else Sbit0();//数据0
}
else Sbit1();//停止位
}
if(start<10)start++;
else
{
TxLength--;//一字节 发送完成,字节数减1
start=0;
}//
}
}

/*定时器1,1ms溢出中断*/
SIGNAL(SIG_OVERFLOW1)
{
eep_ms++;
}
/*定时器2*/
SIGNAL(SIG_OVERFLOW2)
{
sei();
if(INT0_time)//有数据
{
INT0_time=0;//中断次数清0
rdata=1;//置有数据标志
eep_ms=0;
if(RxLength<10)DispBuff[RxLength++]=RUDR;

}
if(rtime<4)rtime++;//字节间隔时间,间隔3个字节重新开始一帧
else RxLength=0;
}

SIGNAL(SIG_INTERRUPT0)//INT0,边沿触发中断
{
unsigned char temp,temp2=0;
static unsigned char pre_TCNT2,j=0;
if(INT0_time==0)//一个字节第一个下降沿中断,起始位开始
{
TCNT2=130;
pre_TCNT2=130;
RUDR=0xff;//接收的数据初值
j=0; //位数清零
INT0_time++;//中断次数加一
}
else
{
temp=TCNT2;
if(temp>pre_TCNT2)temp2=temp-pre_TCNT2;//取一个高/低电平的宽度
if(temp2>10)//滤过窄电平(干扰信号)
{
pre_TCNT2=temp;//记录前一次的时间值
temp=0;
while(temp2>13)//计算位的个数,约13为一个位(8*13=104uS)
{
temp2-=13;//
temp++;
}
if(temp2>6)temp++;//计算位的个数,一般13为一个位
if(INT0_time==1)temp-=1;
if(INT0_time&1)//奇数次中断
{
while(temp)//位0的个数
{
RUDR&=~(1<temp--;
j++;
}
}
else j+=temp;//偶数,位1的个数,跳过
INT0_time++;//中断次数加一
}
}
rtime=0;
}

串囗模拟



;***********************************************************
;普通IO口模拟RS232串口 占用定时器0中断
;在TH0,TL0中设置波特率的大小 注意不要开定时器0的中断,这里用的是查询方式;***********************************************************

BT_REC BIT P1.2
BT_SND BIT P1.5
;占用定时器0
ORG 0000H
AJMP MAIN
main: MOV SP,#59H

RS232: MOV TMOD,#02 ;利用定时器方式0
MOV PCON,#80H
;****************************************************
;9600bps 就是 1000000/9600=104.167微秒
;执行的timer是104.167*11.0592/12= 96 256-96=160(0A0H)
;执行的timer是104.167*22.1184/12= 192 256-192=64(040H)

;19200bps 就是 1000000/19200=52.083微秒
;执行的timer是52.083*11.0592/12= 48 256-48=208(0D0H)
;执行的timer是52.083*22.1184/12= 96 256-96=160(0A0H)

;115200bps 就是 1000000/115200=8.6806微秒 (可能出错,达不到那么高)
;执行的timer是8.6806*11.0592/12= 8 256- 8=248(0F8H)
;执行的timer是8.6806*22.1184/12= 16 256-16=240(0F0H)

;所以,改变TH0,TL0的处置就是改变了波特率
;****************************************************
MOV TH0,#0A0H
MOV TL0,#0A0H
SETB TR0 ;开定时器0
;============发送==============

MOV A,#55H
LCALL S_Char
MOV A,#56H
LCALL S_Char
MOV A,#77H
LCALL S_Char
AJMP JIESHU

;============接收=============
STAR: JB BT_REC,$ ;检测是否有开始位
LCALL G_Char
MOV 30H,A ;存储接受来的数据
STAR0: JB BT_REC,$ ;检测是否有开始位
LCALL G_Char
MOV 31H,A
JIESHU: NOP
AJMP JIESHU

;==============子程序=======================


;----------接收1个字节-------
;接收来的数在A中
;用到的资源:TF0,ACC,R7,TR0,TL0,TR0
G_Char: MOV TL0,TR0
SETB TR0
MOV R7,#08H
R_LOOP: CLR TF0 ;等待TIMER0的中断TIMER0中断的时间定了那么波特率也就定了
JNB TF0,$
MOV C,BT_REC
RRC A
DJNZ R7,R_LOOP
CLR TF0
ST_BIT: JB TF0,SADA ;等待停止位
JNB BT_REC,ST_BIT
SADA: CLR TR0
RET

;---------发送1个字节------------
;要发送的数存在R7中
;用到的资源:R7,A,TF0,TRO,TL0,TR0
S_Char: CLR TF0
CLR BT_SND
MOV R7,#08H
MOV TL0,TR0
SETB TR0
LOOP: JNB TF0,$ ;判断定时器是否溢出
RRC A
MOV BT_SND,C
CLR TF0
DJNZ R7,LOOP
JNB TF0,$
SETB BT_SND
CLR TF0
JNB TF0,$
CLR TR0
RET

51单片机IO口模拟串口通讯C源程作者:jdzj868 来源:机电之家下载站 录入:jdzj868 更新时间:2009-9-11 16:43:13 点击数:0 【字体:

51 IO口模拟串口通讯C源程
#include
sbit BT_SND =P1^0;
sbit BT_REC =P1^1;
/**********************************************

IO 口模拟232通讯程序

使用两种方式的C程序 占用定时器0

**********************************************/

#define MODE_QUICK

#define F_TM F0

#define TIMER0_ENABLE TL0=TH0; TR0=1;
#define TIMER0_DISABLE TR0=0;

sbit ACC0= ACC^0;
sbit ACC1= ACC^1;
sbit ACC2= ACC^2;
sbit ACC3= ACC^3;
sbit ACC4= ACC^4;
sbit ACC5= ACC^5;
sbit ACC6= ACC^6;
sbit ACC7= ACC^7;

void IntTimer0() interrupt 1
{
F_TM=1;
}
//发送一个字符
void PSendChar(unsigned char inch)
{
#ifdef MODE_QUICK
ACC=inch;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

BT_SND=ACC0; //先送出低位
F_TM=0;
while(!F_TM);

BT_SND=ACC1;
F_TM=0;
while(!F_TM);

BT_SND=ACC2;
F_TM=0;
while(!F_TM);

BT_SND=ACC3;
F_TM=0;
while(!F_TM);

BT_SND=ACC4;
F_TM=0;
while(!F_TM);

BT_SND=ACC5;
F_TM=0;
while(!F_TM);

BT_SND=ACC6;
F_TM=0;
while(!F_TM);

BT_SND=ACC7;
F_TM=0;
while(!F_TM);

BT_SND=1;
F_TM=0;
while(!F_TM);


TIMER0_DISABLE; //停止timer
#else
unsigned char ii;

ii=0;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

while(ii<8)
{
if(inch&1)
{
BT_SND=1;
}
else
{
BT_SND=0;
}
F_TM=0;
while(!F_TM);
ii++;
inch>>=1;
}
BT_SND=1;
F_TM=0;
while(!F_TM);

#endif
TIMER0_DISABLE; //停止timer
}
//接收一个字符
unsigned char PGetChar()
{
#ifdef MODE_QUICK

TIMER0_ENABLE;
F_TM=0;
while(!F_TM); //等过起始位
ACC0=BT_REC;

TL0=TH0;

F_TM=0;
while(!F_TM);
ACC1=BT_REC;

F_TM=0;
while(!F_TM);
ACC2=BT_REC;

F_TM=0;
while(!F_TM);
ACC3=BT_REC;

F_TM=0;
while(!F_TM);
ACC4=BT_REC;

F_TM=0;
while(!F_TM);
ACC5=BT_REC;

F_TM=0;
while(!F_TM);
ACC6=BT_REC;

F_TM=0;
while(!F_TM);
ACC7=BT_REC;

F_TM=0;

while(!F_TM)
{
if(BT_REC)
{
break;
}
}
TIMER0_DISABLE; //停止timer
return ACC;
#else
unsigned char rch,ii;
TIMER0_ENABLE;
F_TM=0;
ii=0;
rch=0;
while(!F_TM); //等过起始位

while(ii<8)
{
rch>>=1;
if(BT_REC)
{
rch|=0x80;
}
ii++;
F_TM=0;
while(!F_TM);

}
F_TM=0;
while(!F_TM)
{
if(BT_REC)
{
break;
}

}
TIMER0_DISABLE; //停止timer
return rch;

#endif

}

AVR软件模拟串口程序



M48,8MHz
9600,1,8,1

输出:用定时器控制普通IO口输出位
输入:用外部中断+定时器,判断位的宽度

好几天没休息,利用闲暇写的,也没找到别人的参考程序,不过终于算是稳定了,其实还应该有很多其它的方法可以试一下,比如用PWM输出串行数据,用输入捕获接收数据,或定时查询,或用任意一个IO口中断,则每个引脚都有可能

现在还有些问题,全双工同时收发时发送偶尔出错,占用两个定时器有些浪费,以后再修改吧,最好加上各种波率

本程序为直接摘出部分,删了无关的部分,在此可能有些变量没用,或有段落遗漏,请谅

#include
#include
#include
#include

#define Sbit1() PORTD|=1<#define Sbit0() PORTD&=~(1<

volatile unsigned int
eep_ms,//毫秒计时
keytime, //等待时间
SoundOnTime; ////
volatile unsigned char
rdata,
key,
start=0,
keycode, //
*TxPoint,
rtime,
INT0_time, //中断次数

RxLength=0, //接收长度
RUDR, //摸拟串口接收的数据
TxLength, //串口发送数据长度
SUDR; //串口发送的数据

unsigned char arr[10],DispBuff[10];


void Initial_IO(void)//IO口初始化
{
DDRD = 0X82; //PD1串口输出,PD0串口输入,PD2模拟串口输入(INT0)
PORTD = 0X82; //PD1输出高电平
}

void Initial_INT0(void)
{
EICRA|=(1<EIMSK|=1<}

void Initial_timer0(void) //定时器0初始化
{
TCCR0B|=(1<TIMSK0|=(1<}

void Initial_timer1(void)
{
TCCR1A=(1<TCCR1B=(1<ICR1=1000;
TIMSK1|=(1<}

void Initial_timer2(void)
{
TCCR2B=(1<TIMSK2|=(1<}


void Initial_WDR(void) //看门狗初始化
{
wdt_enable(WDTO_1S);
wdt_reset();
}


void Initial(void)
{
Initial_IO();
Initial_timer0();
Initial_timer1();
Initial_timer2();
Initial_INT0();
Initial_WDR();
sei();
}


/*启动串口发送*/
void SendData(unsigned char *P,unsigned char DataLength)
{
TxLength=DataLength;
TxPoint=P;
start=0;
}


int main (void)
{

Initial();

while(1)
{
wdt_reset();
if((rdata)&&(eep_ms>10))//收到数据延时10mS后启动发送,回送验证数据
{
key=0;
SendData(&DispBuff[0],9);//发送DispBuff[0]的9位数据
while(TxLength);//等待发送完成
rdata=0;
eep_ms=0;
}
}
}


/*定时器0,100us溢出中断*/
SIGNAL(SIG_OVERFLOW0)
{
TCNT0=151;//重载数据,计时区间为151---255,共104uS,一个位的时间
if(TxLength)//
{
if(start==0)
{
Sbit0();//起始位
SUDR=*(TxPoint++);
}
else
{
if((start<=8))
{
if(SUDR&(1<<(start-1)))Sbit1();//数据1
else Sbit0();//数据0
}
else Sbit1();//停止位
}
if(start<10)start++;
else
{
TxLength--;//一字节 发送完成,字节数减1
start=0;
}//
}
}

/*定时器1,1ms溢出中断*/
SIGNAL(SIG_OVERFLOW1)
{
eep_ms++;
}
/*定时器2*/
SIGNAL(SIG_OVERFLOW2)
{
sei();
if(INT0_time)//有数据
{
INT0_time=0;//中断次数清0
rdata=1;//置有数据标志
eep_ms=0;
if(RxLength<10)DispBuff[RxLength++]=RUDR;

}
if(rtime<4)rtime++;//字节间隔时间,间隔3个字节重新开始一帧
else RxLength=0;
}

SIGNAL(SIG_INTERRUPT0)//INT0,边沿触发中断
{
unsigned char temp,temp2=0;
static unsigned char pre_TCNT2,j=0;
if(INT0_time==0)//一个字节第一个下降沿中断,起始位开始
{
TCNT2=130;
pre_TCNT2=130;
RUDR=0xff;//接收的数据初值
j=0; //位数清零
INT0_time++;//中断次数加一
}
else
{
temp=TCNT2;
if(temp>pre_TCNT2)temp2=temp-pre_TCNT2;//取一个高/低电平的宽度
if(temp2>10)//滤过窄电平(干扰信号)
{
pre_TCNT2=temp;//记录前一次的时间值
temp=0;
while(temp2>13)//计算位的个数,约13为一个位(8*13=104uS)
{
temp2-=13;//
temp++;
}
if(temp2>6)temp++;//计算位的个数,一般13为一个位
if(INT0_time==1)temp-=1;
if(INT0_time&1)//奇数次中断
{
while(temp)//位0的个数
{
RUDR&=~(1<temp--;
j++;
}
}
else j+=temp;//偶数,位1的个数,跳过
INT0_time++;//中断次数加一
}
}
rtime=0;
}

串囗模拟



;***********************************************************
;普通IO口模拟RS232串口 占用定时器0中断
;在TH0,TL0中设置波特率的大小 注意不要开定时器0的中断,这里用的是查询方式;***********************************************************

BT_REC BIT P1.2
BT_SND BIT P1.5
;占用定时器0
ORG 0000H
AJMP MAIN
main: MOV SP,#59H

RS232: MOV TMOD,#02 ;利用定时器方式0
MOV PCON,#80H
;****************************************************
;9600bps 就是 1000000/9600=104.167微秒
;执行的timer是104.167*11.0592/12= 96 256-96=160(0A0H)
;执行的timer是104.167*22.1184/12= 192 256-192=64(040H)

;19200bps 就是 1000000/19200=52.083微秒
;执行的timer是52.083*11.0592/12= 48 256-48=208(0D0H)
;执行的timer是52.083*22.1184/12= 96 256-96=160(0A0H)

;115200bps 就是 1000000/115200=8.6806微秒 (可能出错,达不到那么高)
;执行的timer是8.6806*11.0592/12= 8 256- 8=248(0F8H)
;执行的timer是8.6806*22.1184/12= 16 256-16=240(0F0H)

;所以,改变TH0,TL0的处置就是改变了波特率
;****************************************************
MOV TH0,#0A0H
MOV TL0,#0A0H
SETB TR0 ;开定时器0
;============发送==============

MOV A,#55H
LCALL S_Char
MOV A,#56H
LCALL S_Char
MOV A,#77H
LCALL S_Char
AJMP JIESHU

;============接收=============
STAR: JB BT_REC,$ ;检测是否有开始位
LCALL G_Char
MOV 30H,A ;存储接受来的数据
STAR0: JB BT_REC,$ ;检测是否有开始位
LCALL G_Char
MOV 31H,A
JIESHU: NOP
AJMP JIESHU

;==============子程序=======================


;----------接收1个字节-------
;接收来的数在A中
;用到的资源:TF0,ACC,R7,TR0,TL0,TR0
G_Char: MOV TL0,TR0
SETB TR0
MOV R7,#08H
R_LOOP: CLR TF0 ;等待TIMER0的中断TIMER0中断的时间定了那么波特率也就定了
JNB TF0,$
MOV C,BT_REC
RRC A
DJNZ R7,R_LOOP
CLR TF0
ST_BIT: JB TF0,SADA ;等待停止位
JNB BT_REC,ST_BIT
SADA: CLR TR0
RET

;---------发送1个字节------------
;要发送的数存在R7中
;用到的资源:R7,A,TF0,TRO,TL0,TR0
S_Char: CLR TF0
CLR BT_SND
MOV R7,#08H
MOV TL0,TR0
SETB TR0
LOOP: JNB TF0,$ ;判断定时器是否溢出
RRC A
MOV BT_SND,C
CLR TF0
DJNZ R7,LOOP
JNB TF0,$
SETB BT_SND
CLR TF0
JNB TF0,$
CLR TR0
RET

51单片机IO口模拟串口通讯C源程作者:jdzj868 来源:机电之家下载站 录入:jdzj868 更新时间:2009-9-11 16:43:13 点击数:0 【字体:

51 IO口模拟串口通讯C源程
#include
sbit BT_SND =P1^0;
sbit BT_REC =P1^1;
/**********************************************

IO 口模拟232通讯程序

使用两种方式的C程序 占用定时器0

**********************************************/

#define MODE_QUICK

#define F_TM F0

#define TIMER0_ENABLE TL0=TH0; TR0=1;
#define TIMER0_DISABLE TR0=0;

sbit ACC0= ACC^0;
sbit ACC1= ACC^1;
sbit ACC2= ACC^2;
sbit ACC3= ACC^3;
sbit ACC4= ACC^4;
sbit ACC5= ACC^5;
sbit ACC6= ACC^6;
sbit ACC7= ACC^7;

void IntTimer0() interrupt 1
{
F_TM=1;
}
//发送一个字符
void PSendChar(unsigned char inch)
{
#ifdef MODE_QUICK
ACC=inch;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

BT_SND=ACC0; //先送出低位
F_TM=0;
while(!F_TM);

BT_SND=ACC1;
F_TM=0;
while(!F_TM);

BT_SND=ACC2;
F_TM=0;
while(!F_TM);

BT_SND=ACC3;
F_TM=0;
while(!F_TM);

BT_SND=ACC4;
F_TM=0;
while(!F_TM);

BT_SND=ACC5;
F_TM=0;
while(!F_TM);

BT_SND=ACC6;
F_TM=0;
while(!F_TM);

BT_SND=ACC7;
F_TM=0;
while(!F_TM);

BT_SND=1;
F_TM=0;
while(!F_TM);


TIMER0_DISABLE; //停止timer
#else
unsigned char ii;

ii=0;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

while(ii<8)
{
if(inch&1)
{
BT_SND=1;
}
else
{
BT_SND=0;
}
F_TM=0;
while(!F_TM);
ii++;
inch>>=1;
}
BT_SND=1;
F_TM=0;
while(!F_TM);

#endif
TIMER0_DISABLE; //停止timer
}
//接收一个字符
unsigned char PGetChar()
{
#ifdef MODE_QUICK

TIMER0_ENABLE;
F_TM=0;
while(!F_TM); //等过起始位
ACC0=BT_REC;

TL0=TH0;

F_TM=0;
while(!F_TM);
ACC1=BT_REC;

F_TM=0;
while(!F_TM);
ACC2=BT_REC;

F_TM=0;
while(!F_TM);
ACC3=BT_REC;

F_TM=0;
while(!F_TM);
ACC4=BT_REC;

F_TM=0;
while(!F_TM);
ACC5=BT_REC;

F_TM=0;
while(!F_TM);
ACC6=BT_REC;

F_TM=0;
while(!F_TM);
ACC7=BT_REC;

F_TM=0;

while(!F_TM)
{
if(BT_REC)
{
break;
}
}
TIMER0_DISABLE; //停止timer
return ACC;
#else
unsigned char rch,ii;
TIMER0_ENABLE;
F_TM=0;
ii=0;
rch=0;
while(!F_TM); //等过起始位

while(ii<8)
{
rch>>=1;
if(BT_REC)
{
rch|=0x80;
}
ii++;
F_TM=0;
while(!F_TM);

}
F_TM=0;
while(!F_TM)
{
if(BT_REC)
{
break;
}

}
TIMER0_DISABLE; //停止timer
return rch;

#endif

}

AVR软件模拟串口程序



M48,8MHz
9600,1,8,1

输出:用定时器控制普通IO口输出位
输入:用外部中断+定时器,判断位的宽度

好几天没休息,利用闲暇写的,也没找到别人的参考程序,不过终于算是稳定了,其实还应该有很多其它的方法可以试一下,比如用PWM输出串行数据,用输入捕获接收数据,或定时查询,或用任意一个IO口中断,则每个引脚都有可能

现在还有些问题,全双工同时收发时发送偶尔出错,占用两个定时器有些浪费,以后再修改吧,最好加上各种波率

本程序为直接摘出部分,删了无关的部分,在此可能有些变量没用,或有段落遗漏,请谅

#include
#include
#include
#include

#define Sbit1() PORTD|=1<#define Sbit0() PORTD&=~(1<

volatile unsigned int
eep_ms,//毫秒计时
keytime, //等待时间
SoundOnTime; ////
volatile unsigned char
rdata,
key,
start=0,
keycode, //
*TxPoint,
rtime,
INT0_time, //中断次数

RxLength=0, //接收长度
RUDR, //摸拟串口接收的数据
TxLength, //串口发送数据长度
SUDR; //串口发送的数据

unsigned char arr[10],DispBuff[10];


void Initial_IO(void)//IO口初始化
{
DDRD = 0X82; //PD1串口输出,PD0串口输入,PD2模拟串口输入(INT0)
PORTD = 0X82; //PD1输出高电平
}

void Initial_INT0(void)
{
EICRA|=(1<EIMSK|=1<}

void Initial_timer0(void) //定时器0初始化
{
TCCR0B|=(1<TIMSK0|=(1<}

void Initial_timer1(void)
{
TCCR1A=(1<TCCR1B=(1<ICR1=1000;
TIMSK1|=(1<}

void Initial_timer2(void)
{
TCCR2B=(1<TIMSK2|=(1<}


void Initial_WDR(void) //看门狗初始化
{
wdt_enable(WDTO_1S);
wdt_reset();
}


void Initial(void)
{
Initial_IO();
Initial_timer0();
Initial_timer1();
Initial_timer2();
Initial_INT0();
Initial_WDR();
sei();
}


/*启动串口发送*/
void SendData(unsigned char *P,unsigned char DataLength)
{
TxLength=DataLength;
TxPoint=P;
start=0;
}


int main (void)
{

Initial();

while(1)
{
wdt_reset();
if((rdata)&&(eep_ms>10))//收到数据延时10mS后启动发送,回送验证数据
{
key=0;
SendData(&DispBuff[0],9);//发送DispBuff[0]的9位数据
while(TxLength);//等待发送完成
rdata=0;
eep_ms=0;
}
}
}


/*定时器0,100us溢出中断*/
SIGNAL(SIG_OVERFLOW0)
{
TCNT0=151;//重载数据,计时区间为151---255,共104uS,一个位的时间
if(TxLength)//
{
if(start==0)
{
Sbit0();//起始位
SUDR=*(TxPoint++);
}
else
{
if((start<=8))
{
if(SUDR&(1<<(start-1)))Sbit1();//数据1
else Sbit0();//数据0
}
else Sbit1();//停止位
}
if(start<10)start++;
else
{
TxLength--;//一字节 发送完成,字节数减1
start=0;
}//
}
}

/*定时器1,1ms溢出中断*/
SIGNAL(SIG_OVERFLOW1)
{
eep_ms++;
}
/*定时器2*/
SIGNAL(SIG_OVERFLOW2)
{
sei();
if(INT0_time)//有数据
{
INT0_time=0;//中断次数清0
rdata=1;//置有数据标志
eep_ms=0;
if(RxLength<10)DispBuff[RxLength++]=RUDR;

}
if(rtime<4)rtime++;//字节间隔时间,间隔3个字节重新开始一帧
else RxLength=0;
}

SIGNAL(SIG_INTERRUPT0)//INT0,边沿触发中断
{
unsigned char temp,temp2=0;
static unsigned char pre_TCNT2,j=0;
if(INT0_time==0)//一个字节第一个下降沿中断,起始位开始
{
TCNT2=130;
pre_TCNT2=130;
RUDR=0xff;//接收的数据初值
j=0; //位数清零
INT0_time++;//中断次数加一
}
else
{
temp=TCNT2;
if(temp>pre_TCNT2)temp2=temp-pre_TCNT2;//取一个高/低电平的宽度
if(temp2>10)//滤过窄电平(干扰信号)
{
pre_TCNT2=temp;//记录前一次的时间值
temp=0;
while(temp2>13)//计算位的个数,约13为一个位(8*13=104uS)
{
temp2-=13;//
temp++;
}
if(temp2>6)temp++;//计算位的个数,一般13为一个位
if(INT0_time==1)temp-=1;
if(INT0_time&1)//奇数次中断
{
while(temp)//位0的个数
{
RUDR&=~(1<temp--;
j++;
}
}
else j+=temp;//偶数,位1的个数,跳过
INT0_time++;//中断次数加一
}
}
rtime=0;
}

串囗模拟



;***********************************************************
;普通IO口模拟RS232串口 占用定时器0中断
;在TH0,TL0中设置波特率的大小 注意不要开定时器0的中断,这里用的是查询方式;***********************************************************

BT_REC BIT P1.2
BT_SND BIT P1.5
;占用定时器0
ORG 0000H
AJMP MAIN
main: MOV SP,#59H

RS232: MOV TMOD,#02 ;利用定时器方式0
MOV PCON,#80H
;****************************************************
;9600bps 就是 1000000/9600=104.167微秒
;执行的timer是104.167*11.0592/12= 96 256-96=160(0A0H)
;执行的timer是104.167*22.1184/12= 192 256-192=64(040H)

;19200bps 就是 1000000/19200=52.083微秒
;执行的timer是52.083*11.0592/12= 48 256-48=208(0D0H)
;执行的timer是52.083*22.1184/12= 96 256-96=160(0A0H)

;115200bps 就是 1000000/115200=8.6806微秒 (可能出错,达不到那么高)
;执行的timer是8.6806*11.0592/12= 8 256- 8=248(0F8H)
;执行的timer是8.6806*22.1184/12= 16 256-16=240(0F0H)

;所以,改变TH0,TL0的处置就是改变了波特率
;****************************************************
MOV TH0,#0A0H
MOV TL0,#0A0H
SETB TR0 ;开定时器0
;============发送==============

MOV A,#55H
LCALL S_Char
MOV A,#56H
LCALL S_Char
MOV A,#77H
LCALL S_Char
AJMP JIESHU

;============接收=============
STAR: JB BT_REC,$ ;检测是否有开始位
LCALL G_Char
MOV 30H,A ;存储接受来的数据
STAR0: JB BT_REC,$ ;检测是否有开始位
LCALL G_Char
MOV 31H,A
JIESHU: NOP
AJMP JIESHU

;==============子程序=======================


;----------接收1个字节-------
;接收来的数在A中
;用到的资源:TF0,ACC,R7,TR0,TL0,TR0
G_Char: MOV TL0,TR0
SETB TR0
MOV R7,#08H
R_LOOP: CLR TF0 ;等待TIMER0的中断TIMER0中断的时间定了那么波特率也就定了
JNB TF0,$
MOV C,BT_REC
RRC A
DJNZ R7,R_LOOP
CLR TF0
ST_BIT: JB TF0,SADA ;等待停止位
JNB BT_REC,ST_BIT
SADA: CLR TR0
RET

;---------发送1个字节------------
;要发送的数存在R7中
;用到的资源:R7,A,TF0,TRO,TL0,TR0
S_Char: CLR TF0
CLR BT_SND
MOV R7,#08H
MOV TL0,TR0
SETB TR0
LOOP: JNB TF0,$ ;判断定时器是否溢出
RRC A
MOV BT_SND,C
CLR TF0
DJNZ R7,LOOP
JNB TF0,$
SETB BT_SND
CLR TF0
JNB TF0,$
CLR TR0
RET

51单片机IO口模拟串口通讯C源程作者:jdzj868 来源:机电之家下载站 录入:jdzj868 更新时间:2009-9-11 16:43:13 点击数:0 【字体:

51 IO口模拟串口通讯C源程
#include
sbit BT_SND =P1^0;
sbit BT_REC =P1^1;
/**********************************************

IO 口模拟232通讯程序

使用两种方式的C程序 占用定时器0

**********************************************/

#define MODE_QUICK

#define F_TM F0

#define TIMER0_ENABLE TL0=TH0; TR0=1;
#define TIMER0_DISABLE TR0=0;

sbit ACC0= ACC^0;
sbit ACC1= ACC^1;
sbit ACC2= ACC^2;
sbit ACC3= ACC^3;
sbit ACC4= ACC^4;
sbit ACC5= ACC^5;
sbit ACC6= ACC^6;
sbit ACC7= ACC^7;

void IntTimer0() interrupt 1
{
F_TM=1;
}
//发送一个字符
void PSendChar(unsigned char inch)
{
#ifdef MODE_QUICK
ACC=inch;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

BT_SND=ACC0; //先送出低位
F_TM=0;
while(!F_TM);

BT_SND=ACC1;
F_TM=0;
while(!F_TM);

BT_SND=ACC2;
F_TM=0;
while(!F_TM);

BT_SND=ACC3;
F_TM=0;
while(!F_TM);

BT_SND=ACC4;
F_TM=0;
while(!F_TM);

BT_SND=ACC5;
F_TM=0;
while(!F_TM);

BT_SND=ACC6;
F_TM=0;
while(!F_TM);

BT_SND=ACC7;
F_TM=0;
while(!F_TM);

BT_SND=1;
F_TM=0;
while(!F_TM);


TIMER0_DISABLE; //停止timer
#else
unsigned char ii;

ii=0;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

while(ii<8)
{
if(inch&1)
{
BT_SND=1;
}
else
{
BT_SND=0;
}
F_TM=0;
while(!F_TM);
ii++;
inch>>=1;
}
BT_SND=1;
F_TM=0;
while(!F_TM);

#endif
TIMER0_DISABLE; //停止timer
}
//接收一个字符
unsigned char PGetChar()
{
#ifdef MODE_QUICK

TIMER0_ENABLE;
F_TM=0;
while(!F_TM); //等过起始位
ACC0=BT_REC;

TL0=TH0;

F_TM=0;
while(!F_TM);
ACC1=BT_REC;

F_TM=0;
while(!F_TM);
ACC2=BT_REC;

F_TM=0;
while(!F_TM);
ACC3=BT_REC;

F_TM=0;
while(!F_TM);
ACC4=BT_REC;

F_TM=0;
while(!F_TM);
ACC5=BT_REC;

F_TM=0;
while(!F_TM);
ACC6=BT_REC;

F_TM=0;
while(!F_TM);
ACC7=BT_REC;

F_TM=0;

while(!F_TM)
{
if(BT_REC)
{
break;
}
}
TIMER0_DISABLE; //停止timer
return ACC;
#else
unsigned char rch,ii;
TIMER0_ENABLE;
F_TM=0;
ii=0;
rch=0;
while(!F_TM); //等过起始位

while(ii<8)
{
rch>>=1;
if(BT_REC)
{
rch|=0x80;
}
ii++;
F_TM=0;
while(!F_TM);

}
F_TM=0;
while(!F_TM)
{
if(BT_REC)
{
break;
}

}
TIMER0_DISABLE; //停止timer
return rch;

#endif

}

AVR软件模拟串口程序



M48,8MHz
9600,1,8,1

输出:用定时器控制普通IO口输出位
输入:用外部中断+定时器,判断位的宽度

好几天没休息,利用闲暇写的,也没找到别人的参考程序,不过终于算是稳定了,其实还应该有很多其它的方法可以试一下,比如用PWM输出串行数据,用输入捕获接收数据,或定时查询,或用任意一个IO口中断,则每个引脚都有可能

现在还有些问题,全双工同时收发时发送偶尔出错,占用两个定时器有些浪费,以后再修改吧,最好加上各种波率

本程序为直接摘出部分,删了无关的部分,在此可能有些变量没用,或有段落遗漏,请谅

#include
#include
#include
#include

#define Sbit1() PORTD|=1<#define Sbit0() PORTD&=~(1<

volatile unsigned int
eep_ms,//毫秒计时
keytime, //等待时间
SoundOnTime; ////
volatile unsigned char
rdata,
key,
start=0,
keycode, //
*TxPoint,
rtime,
INT0_time, //中断次数

RxLength=0, //接收长度
RUDR, //摸拟串口接收的数据
TxLength, //串口发送数据长度
SUDR; //串口发送的数据

unsigned char arr[10],DispBuff[10];


void Initial_IO(void)//IO口初始化
{
DDRD = 0X82; //PD1串口输出,PD0串口输入,PD2模拟串口输入(INT0)
PORTD = 0X82; //PD1输出高电平
}

void Initial_INT0(void)
{
EICRA|=(1<EIMSK|=1<}

void Initial_timer0(void) //定时器0初始化
{
TCCR0B|=(1<TIMSK0|=(1<}

void Initial_timer1(void)
{
TCCR1A=(1<TCCR1B=(1<ICR1=1000;
TIMSK1|=(1<}

void Initial_timer2(void)
{
TCCR2B=(1<TIMSK2|=(1<}


void Initial_WDR(void) //看门狗初始化
{
wdt_enable(WDTO_1S);
wdt_reset();
}


void Initial(void)
{
Initial_IO();
Initial_timer0();
Initial_timer1();
Initial_timer2();
Initial_INT0();
Initial_WDR();
sei();
}


/*启动串口发送*/
void SendData(unsigned char *P,unsigned char DataLength)
{
TxLength=DataLength;
TxPoint=P;
start=0;
}


int main (void)
{

Initial();

while(1)
{
wdt_reset();
if((rdata)&&(eep_ms>10))//收到数据延时10mS后启动发送,回送验证数据
{
key=0;
SendData(&DispBuff[0],9);//发送DispBuff[0]的9位数据
while(TxLength);//等待发送完成
rdata=0;
eep_ms=0;
}
}
}


/*定时器0,100us溢出中断*/
SIGNAL(SIG_OVERFLOW0)
{
TCNT0=151;//重载数据,计时区间为151---255,共104uS,一个位的时间
if(TxLength)//
{
if(start==0)
{
Sbit0();//起始位
SUDR=*(TxPoint++);
}
else
{
if((start<=8))
{
if(SUDR&(1<<(start-1)))Sbit1();//数据1
else Sbit0();//数据0
}
else Sbit1();//停止位
}
if(start<10)start++;
else
{
TxLength--;//一字节 发送完成,字节数减1
start=0;
}//
}
}

/*定时器1,1ms溢出中断*/
SIGNAL(SIG_OVERFLOW1)
{
eep_ms++;
}
/*定时器2*/
SIGNAL(SIG_OVERFLOW2)
{
sei();
if(INT0_time)//有数据
{
INT0_time=0;//中断次数清0
rdata=1;//置有数据标志
eep_ms=0;
if(RxLength<10)DispBuff[RxLength++]=RUDR;

}
if(rtime<4)rtime++;//字节间隔时间,间隔3个字节重新开始一帧
else RxLength=0;
}

SIGNAL(SIG_INTERRUPT0)//INT0,边沿触发中断
{
unsigned char temp,temp2=0;
static unsigned char pre_TCNT2,j=0;
if(INT0_time==0)//一个字节第一个下降沿中断,起始位开始
{
TCNT2=130;
pre_TCNT2=130;
RUDR=0xff;//接收的数据初值
j=0; //位数清零
INT0_time++;//中断次数加一
}
else
{
temp=TCNT2;
if(temp>pre_TCNT2)temp2=temp-pre_TCNT2;//取一个高/低电平的宽度
if(temp2>10)//滤过窄电平(干扰信号)
{
pre_TCNT2=temp;//记录前一次的时间值
temp=0;
while(temp2>13)//计算位的个数,约13为一个位(8*13=104uS)
{
temp2-=13;//
temp++;
}
if(temp2>6)temp++;//计算位的个数,一般13为一个位
if(INT0_time==1)temp-=1;
if(INT0_time&1)//奇数次中断
{
while(temp)//位0的个数
{
RUDR&=~(1<temp--;
j++;
}
}
else j+=temp;//偶数,位1的个数,跳过
INT0_time++;//中断次数加一
}
}
rtime=0;
}

串囗模拟



;***********************************************************
;普通IO口模拟RS232串口 占用定时器0中断
;在TH0,TL0中设置波特率的大小 注意不要开定时器0的中断,这里用的是查询方式;***********************************************************

BT_REC BIT P1.2
BT_SND BIT P1.5
;占用定时器0
ORG 0000H
AJMP MAIN
main: MOV SP,#59H

RS232: MOV TMOD,#02 ;利用定时器方式0
MOV PCON,#80H
;****************************************************
;9600bps 就是 1000000/9600=104.167微秒
;执行的timer是104.167*11.0592/12= 96 256-96=160(0A0H)
;执行的timer是104.167*22.1184/12= 192 256-192=64(040H)

;19200bps 就是 1000000/19200=52.083微秒
;执行的timer是52.083*11.0592/12= 48 256-48=208(0D0H)
;执行的timer是52.083*22.1184/12= 96 256-96=160(0A0H)

;115200bps 就是 1000000/115200=8.6806微秒 (可能出错,达不到那么高)
;执行的timer是8.6806*11.0592/12= 8 256- 8=248(0F8H)
;执行的timer是8.6806*22.1184/12= 16 256-16=240(0F0H)

;所以,改变TH0,TL0的处置就是改变了波特率
;****************************************************
MOV TH0,#0A0H
MOV TL0,#0A0H
SETB TR0 ;开定时器0
;============发送==============

MOV A,#55H
LCALL S_Char
MOV A,#56H
LCALL S_Char
MOV A,#77H
LCALL S_Char
AJMP JIESHU

;============接收=============
STAR: JB BT_REC,$ ;检测是否有开始位
LCALL G_Char
MOV 30H,A ;存储接受来的数据
STAR0: JB BT_REC,$ ;检测是否有开始位
LCALL G_Char
MOV 31H,A
JIESHU: NOP
AJMP JIESHU

;==============子程序=======================


;----------接收1个字节-------
;接收来的数在A中
;用到的资源:TF0,ACC,R7,TR0,TL0,TR0
G_Char: MOV TL0,TR0
SETB TR0
MOV R7,#08H
R_LOOP: CLR TF0 ;等待TIMER0的中断TIMER0中断的时间定了那么波特率也就定了
JNB TF0,$
MOV C,BT_REC
RRC A
DJNZ R7,R_LOOP
CLR TF0
ST_BIT: JB TF0,SADA ;等待停止位
JNB BT_REC,ST_BIT
SADA: CLR TR0
RET

;---------发送1个字节------------
;要发送的数存在R7中
;用到的资源:R7,A,TF0,TRO,TL0,TR0
S_Char: CLR TF0
CLR BT_SND
MOV R7,#08H
MOV TL0,TR0
SETB TR0
LOOP: JNB TF0,$ ;判断定时器是否溢出
RRC A
MOV BT_SND,C
CLR TF0
DJNZ R7,LOOP
JNB TF0,$
SETB BT_SND
CLR TF0
JNB TF0,$
CLR TR0
RET