您的位置 首页 新能源

单片机一般I/O模仿串口

#includereg52h>sfr16DPTR=0x82;typedefunsignedcharINT8U;typedefunsignedintINT16U;defineYES1defineN

#i nclude
sfr16 DPTR = 0x82;

typedef unsigned char INT8U;
typedef unsigned int INT16U;

#define YES 1
#define NO 0

//界说运用哪个守时器, 只可界说一个
//#define TIMER_0
#define TIMER_1

//界说串口收、发送管脚。
sbit rs_TXD = P2^1;
sbit rs_RXD = P2^0;

//依据守时器确认参数
#ifdef TIMER_0
#define TMOD_AND_WORD 0xF0;
#define TMOD_TIME_MODE 0x01;
#define TMOD_COUNT_MODE 0x05; //设置计数模式位
sbit TCON_ENABLE_TIMER = TCON^4;
sbit TCON_TFx = TCON^5; //中止标志位
sbit IE_ETx = IE^1; //中止答应位为 ET0
sbit IP_PTx = IP^1; //中止优先级

sfr rs_timerL = 0x8A; //TL0
sfr rs_timerH = 0x8C; //TH0
#endif

#ifdef TIMER_1
#define TMOD_AND_WORD 0x0F;
#define TMOD_TIME_MODE 0x10;
#define TMOD_COUNT_MODE 0x50; //设置计数形式位
sbit TCON_ENABLE_TIMER = TCON^6; //
sbit TCON_TFx = TCON^7; //中止标志位
sbit IE_ETx = IE^3; //中止答应位为 ET1
sbit IP_PTx = IP^4; //中止优先级

sfr rs_timerL = 0x8B; //TL1
sfr rs_timerH = 0x8D; //TH1
#endif

INT8U bdata rs_BUF; //串行收、发时用的移位暂存器。
sbit rs_BUF_bit7 = rs_BUF^7; //移位暂存器的最高位。
INT8U rs_shift_count; //移位计数器。

INT8U bdata rsFlags;
sbit rs_f_TI = rsFlags^0; //0:正在发送; 1: 一个字符完毕
sbit rs_f_RI_enable = rsFlags^1; //0:制止接纳; 1:答应接纳
sbit rs_f_TI_enable = rsFlags^2; //0:制止发送; 1:答应发送

//挑选以下一个晶体频率
//#define Fosc 6000000 //6MHz
#define Fosc 11059200 //11.059MHz
//#define Fosc 12000000
//#define Fosc 18432000
//#define Fosc 20000000
//#define Fosc 24000000
//#define Fosc 30000000
//#define Fosc 40000000

//挑选以下一个波特率:
//#efine Baud 300 //11.059MHz时,baud 最低为 300
//#define Baud 1200
//#define Baud 2400
//#define Baud 4800
#define Baud 9600
//#define Baud 14400
//#define Baud 19200
//#define Baud 28800
//#define Baud 38400
//#define Baud 57600

//收、发一位所需守时器计数
#define rs_FULL_BIT0 ((Fosc/12) / Baud)
#define rs_FULL_BIT (65536 – rs_FULL_BIT0)
#define rs_FULL_BIT_H rs_FULL_BIT >> 8 //收、发一位所需守时器计数高位
#define rs_FULL_BIT_L (rs_FULL_BIT & 0x00FF) //收、发一位所需守时器计数低位

//检测开端位的时刻距离所需守时器计数
#define rs_TEST0 rs_FULL_BIT0 / 4 //波特率较低时能够除以 3 或除以 2
#define rs_TEST ((~rs_TEST0))
#define rs_TEST_H rs_TEST >> 8 //高位
#define rs_TEST_L rs_TEST & 0x00FF //低位

//发送开端位所需守时器总计数
#define rs_START_BIT 0xFFFF – (Fosc/12/Baud) + 0x28
#define rs_START_BIT_H rs_START_BIT >> 8 //发送开端位所需守时器计数高位
#define rs_START_BIT_L rs_START_BIT & 0x00FF //发送开端位所需守时器计数低位

#define rs_RECEIVE_MAX 128 //最大接纳长度
INT8U idata rs232buffer[rs_RECEIVE_MAX]; //收、发缓冲区
INT16U ReceivePoint; //接纳数据存储指针

void soft_rs232_interrupt( void );

#ifdef TIMER_0
void timer0 (void) interrupt 1 using 3
{
if (rs_RXD == 0 | rs_shift_count > 0)
{ soft_rs232_interrupt(); }
else
{
rs_timerH = rs_TEST_H;
rs_timerL = rs_TEST_L;
}
}
#endif

#ifdef TIMER_1
void timer1 (void) interrupt 3 using 3
{
if (rs_RXD == 0 | rs_shift_count > 0)
{ soft_rs232_interrupt(); }
else
{
rs_timerH = rs_TEST_H;
rs_timerL = rs_TEST_L;
}
}
#endif

void soft_rs232_init (void) //串口初始化
{
TCON_ENABLE_TIMER = 0; //中止守时器
TMOD &= TMOD_AND_WORD;
TMOD |= TMOD_TIME_MODE;
rs_RXD = 1; //接纳脚置成高电平
rs_TXD = 1; //发射脚置成高电平
IP_PTx = 1; //置中止优先级为高
IE_ETx = 1; //答应守时器中止
}

void soft_receive_init() //监测开端位
{
TCON_ENABLE_TIMER = 0; //中止守时器
rs_timerH = rs_TEST_H;
rs_timerL = rs_TEST_L;
rs_shift_count = 0;
TCON_ENABLE_TIMER = 1; //发动守时器
}

void soft_receive_enable() //答应接纳
{
rs_f_RI_enable = 1; //答应接纳
rs_f_TI_enable = 0; //制止发送
soft_receive_init(); //监测开端位, RXD 下降沿触发接纳字节进程.
}

void soft_send_enable (void) //答应发送
{
TCON_ENABLE_TIMER = 0; //中止守时器
rs_f_TI_enable = 1; //答应发送
rs_f_RI_enable = 0; //制止接纳

rs_shift_count = 0; //清移位计数器
rs_f_TI = 1; //发送一个字符完毕标志
TCON_ENABLE_TIMER = 1; //发动守时器
}

void soft_rs232_interrupt( void )
{

if (rs_f_RI_enable == 1)
{
if (rs_shift_count == 0) //移位计数器==0, 表明检测到开端位的起点
{
if ( rs_RXD == 1 )
{
soft_receive_enable (); //开端位错, 重新开端
}
else
{
//下次中止在数据位或中止位中的某时刻发生
rs_timerL += rs_FULL_BIT_L + 0x10;
rs_timerH = rs_FULL_BIT_H;
rs_shift_count++;
rs_BUF = 0; //清移位缓冲变量
}
}
else
{
rs_timerL += rs_FULL_BIT_L; //下次中止在数据位或中止位中发生
rs_timerH = rs_FULL_BIT_H;

rs_shift_count++; //2–9:数据位 10:中止位

if ( rs_shift_count == 9)
{
rs_BUF = rs_BUF >> 1; //接纳第8位
rs_BUF_bit7 = rs_RXD;
if( ReceivePoint < rs_RECEIVE_MAX)
{ //保存收到的字节
rs232buffer[ReceivePoint++] = rs_BUF;
}
else
{
rs_f_RI_enable = 0; //缓冲区满, 制止接纳
}
}
else
{
if (rs_shift_count < 9 ) //收到的是数据位 1 -- 7
{
rs_BUF = rs_BUF >> 1;
rs_BUF_bit7 = rs_RXD;
}
else
{ //收到中止位,继续检测 PC 机宣布的下一个开端位
soft_receive_init();
}
}
}
TCON_TFx = 0; //清守时器中止标志
}
else
{

if (rs_f_TI_enable == 1)
{
rs_timerL += rs_FULL_BIT_L;//下次中止在数据位的结尾时刻
rs_timerH = rs_FULL_BIT_H;

rs_shift_count–; //0:中止位结尾时刻到
//1:发送中止位
//2–9:发送数据位
if (rs_shift_count > 9) //过错状况
{
rs_shift_count = 9;
rs_BUF = 0xFF;
}

if (rs_shift_count > 1) //2–9:发送数据位
{
ACC = rs_BUF;
ACC = ACC >> 1;
rs_TXD = CY;
rs_BUF = ACC;
}
else
{
if (rs_shift_count == 0) //0:中止位结尾时刻到
{
rs_TXD = 1;
rs_f_TI = 1; //已发送完毕一个字节
}
else
{
rs_TXD = 1; //1:发送中止位
}
}
}
}
}

//由收转到发时,要先调用 soft_send_enable ()
void rs_send_byte(INT8U SendByte) //发送一个字节
{
while ( rs_f_TI == 0); //等候发送完毕前一个字节
rs_TXD = 1;
rs_timerL = rs_START_BIT_L; //下次中止在开端位的结尾时刻
rs_timerH = rs_START_BIT_H;
rs_BUF = SendByte;
rs_shift_count = 10;
rs_TXD = 0; //发送开端位
rs_f_TI = 0; //清已发送完毕一个字节的标志
}

void initiate_MCU (void) //体系初始化
{
soft_rs232_init(); //串口初始化
EA = 1; //开中止
}

void main (void)
{
//首要发送 128 个字节 00H–7FH, 然后等候 PC 机发送的数据。当收到 128
//个字节后,马上将收到的 128 个数据回发送给 PC 机,然后继续等候下一个
//数据块。

INT8U i;
initiate_MCU(); //体系初始化

soft_send_enable (); //答应发送,制止接纳
for (i=0; i < rs_RECEIVE_MAX; i++ )
{
rs_send_byte(i);
}
while ( rs_f_TI == 0) ; // 等候最终一个字节发送完毕

while(1)
{
soft_receive_enable (); //发动并开端接纳,制止发送
while (ReceivePoint < rs_RECEIVE_MAX); // 等候接纳缓冲区满

soft_send_enable (); //答应发送,制止接纳
for (i=0; i < rs_RECEIVE_MAX; i++ )
{
rs_send_byte(rs232buffer[i]);
}
while ( rs_f_TI == 0) ; //等候最终一个字节发送完毕
ReceivePoint = 0;
}
}

单片机:LPC932

void INT_SERIAL(void) interrupt 4
{
unsigned char i,j,k,l;
ES=0;
j=0;
k=0;
l=SBUF;
//数据做偶校验
for(i=0;i<8;i++)
{
if(l & 0x80)
{ //bytedata和0x80作AND逻辑运算等于0x80
j++;
if(j==2)
{
j=0;
} //即表明位7等于1则条件建立
}
l <<=1;
} //制止串口中止
if(j==0)
{
if(RB8)
{
k=0;
}
else
{
k=1;
}
}
else
{
if(RB8)
{
k=1;
}
}
if(k)
{
if(ComEn)
{
SeriesSt=0;
if(InfraredSt)
{
if(InfraredBufDptr<60)
{
if(InfraredBufDptr==0) //数据缓冲区地址指针是否为零
{
if(SBUF==0x68) //缓冲区的第一个数据是否等于0x68*/
{
ComDataBuf[0]=SBUF; //数据保存到缓冲区*/
InfraredBufDptr++; //缓冲区地址累加*/
}
else
{
InfraredBufDptr=0;
}
}
else
{
ComDataBuf[InfraredBufDptr]=SBUF; //数据保存到缓冲区*/
InfraredBufDptr++; //缓冲区地址累加*/
}

}
else
{
InfraredBufDptr=0;
}
if(ComDataBuf[ComDataBuf][9]+11]==0x16)
{
InfraredBufDptr=0;
InfraredFg=1;//红外占用数据通道有用
ComBit=1;
}
else
{
InfraredFg=0;//红外占用数据通道失效标志
}
}
}
else
{
ComEn=1;
InfraredSt=1;
}
}
RI=0;
ES=1; //接纳标志位清零*
}
void INT_INT0(void) interrupt 0
{
unsigned char BitData;
bit BitChk;
EA=0;
EX0=0;
WDT();
EX0=0;
BitData=0;
BitChk=1;
SeriesInData=0;
Delay(130); //等过开端位
while(BitData<9)
{
if(BitData==8)
{
SeriesBitNine=RXD;
}
if(RXD==1)
{
SeriesInData|=0x0080;
BitChk=~BitChk;
}
BitData++;
if(BitData<8)
{
SeriesInData>>=1;
}
Delay(90);
}
if(BitChk)
{
if(ComEn)
{

InfraredSt=0;
if(SeriesSt)
{
if(SeriesBufDptr<60)
{
if(SeriesBufDptr==0) //数据缓冲区地址指针是否为零
{
if(SeriesInData==0x68) //缓冲区的第一个数据是否等于0x68*/
{
ComDataBuf[0]=SeriesInData; //数据保存到缓冲区*/
SeriesBufDptr++; //缓冲区地址累加*/
}
else
{
SeriesBufDptr=0;
}
}
else
{
ComDataBuf[SeriesBufDptr]=SeriesInData; //数据保存到缓冲区*/
SeriesBufDptr++; //缓冲区地址累加*/
}
}
else
{
SeriesBufDptr=0;
}
if(ComDataBuf[ComDataBuf][9]+11]==0x16)
{
SeriesBufDptr=0;
SeriesFg=1;//串口占用数据通道有标志效
ComBit=1;
}
else//指令过错开释串口占用数据通道标志
{
SeriesFg=0;//串口占用数据通道标志失效
}
}
}
else
{
ComEn=1;
SeriesSt=1;
}
}
EX0=1;
EA=1;
}
void DataCom(void)
{
if(ComBit)
{
//串口数据调集校验
if(ComEn)
{
if(SeriesSt)
{
if(SendOverFg)
{
CmdParseFg=1;
ComFg=1;
ComTimeA=0;
ComTimeB=0;
ComBit=0;
ComEn=0;
SeriesSt=0;
}
else//占用标志等候数据发送完毕
{
SeriesFg=1;//串口占用数据通道标志有用
}
}
else
{
if(InfraredSt)
{
if(SendOverFg)
{
CmdParseFg=1;
ComFg=1;
ComTimeA=0;
ComTimeB=0;
ComBit=0;
ComEn=0;
InfraredSt=0;
}
else
{
InfraredFg=1;//占用标志等候数据发送完毕
}
}
else
{
SeriesFg=0;
InfraredFg=0;
ComEn=0;
}
}
}
}
}

单片机一般IO模仿串口的办法介绍(转载) [资源共享] 发布时刻:2010-12-17 16:51:23 51单片机模仿串口的三种办法

跟着单片机的运用日益频繁,用其作前置机进行收集和通讯也常见于各种运用,一般是运用前置
机收集各种终端数据后进行处理、存储,再自动或被迫上报给管理站。这种状况下下,收聚会需
要一个串口,上报又需求另一个串口,这就要求单片机具有双串口的功用,但咱们知道一般的51
系列只提供一个串口,那么另一个串口只能靠程序模仿。
本文所说的模仿串口, 便是运用51的两个输入输出引脚如P1.0和P1.1,置1或0别离代表凹凸电
平,也便是串口通讯中所说的位,如开端位用低电平,则将其置0,中止位为高电平,则将其置
1,各种数据位和校验位则依据状况置1或置0。至于串口通讯的波特率,说到底仅仅每位电平继续
的时刻,波特率越高,继续的时刻越短。如波特率为9600BPS,即每一位传送时刻为
1000ms/9600=0.104ms,即位与位之间的延时为为0.104毫秒。单片机的延时是经过履行若干条
指令来到达意图的,由于每条指令为1-3个指令周期,可便是经过若干个指令周期来进行延时的,
单片机常用11.0592M的的晶振,现在我要告知你这个古怪数字的来历。用此频率则每个指令周期
的时刻为(12/11.0592)us,那么波特率为9600BPS每位要间融多少个指令周期呢?
指令周期s=(1000000/9600)/(12/11.0592)=96,刚好为一整数,假如为4800BPS则为
96×2=192,如为19200BPS则为48,其他波特率就不算了,都刚好为整数个指令周期,妙吧。至于
其他晶振频率咱们自已去算吧。
现在就以11.0592M的晶振为例,谈谈三种模仿串口的办法。

办法一:延时法

经过上述核算咱们知道,串口的每位需延时0.104秒,中心可履行96个指令周期。
#define uchar unsigned char
sbit P1_0 = 0x90;
sbit P1_1 = 0x91;
sbit P1_2 = 0x92;
#define RXD P1_0
#define TXD P1_1
#define WRDYN 44 //写延时
#define RDDYN 43 //读延时

//往串口写一个字节
void WByte(uchar input)
{
uchar i=8;
TXD=(bit)0; //发送启始

Delay2cp(39);
//发送8位数据位
while(i–)
{
TXD=(bit)(input&0x01); //先传低位
Delay2cp(36);
input=input>>1;
}
//发送校验位(无)
TXD=(bit)1; //发送完毕

Delay2cp(46);
}

//从串口读一个字节
uchar RByte(void)
{
uchar Output=0;
uchar i=8;
uchar temp=RDDYN;
//发送8位数据位
Delay2cp(RDDYN*1.5); //此处留意,等过开端位
while(i–)
{
Output >>=1;
if(RXD) Output |=0x80; //先收低位
Delay2cp(35); //(96-26)/2,循环共
占用26个指令周期
}
while(–temp) //在指定的
时刻内搜索完毕位。
{
Delay2cp(1);
if(RXD)break; //收到完毕位便退出
}
return Output;
}

//延时程序*
void Delay2cp(unsigned char i)
{
while(–i); //刚好两个
指令周期。
}

此种办法在接纳上存在必定的难度,主要是采样定位存在需较精确,别的还必须知道
每条句子的指令周期数。此法或许模仿若干个串口,实践中采用它的人也许多,但假如你用Keil
C,自己不主张运用此种办法,上述程序在P89C52、AT89C52、W78E52三种单片机上试验经过。

办法二:计数法

51的计数器在每指令周期加1,直到溢出,一起硬件置溢出标志位。这样咱们就能够
经过预置初值的办法让机器每96个指令周期发生一次溢出,程序不断的查询溢出标志来决议是否
发送或接纳下一位。

//计数器初始化
void S2INI(void)
{
TMOD |=0x02; //计数器0,办法2
TH0=0xA0; //预值为256-96=140,十六进制A0
TL0=TH0;
TR0=1; //开端计数
TF0=0;
}

void WByte(uchar input)
{
//发送启始位
uchar i=8;
TR0=1;
TXD=(bit)0;
WaitTF0();
//发送8位数据位
while(i–)
{
TXD=(bit)(input&0x01); //先传低位
WaitTF0();
input=input>>1;
}
//发送校验位(无)
//发送完毕位
TXD=(bit)1;
WaitTF0();
TR0=0;
}
//查询计数器溢出标志位
void WaitTF0( void )
{
while(!TF0);
TF0=0;
}
接纳的程序,能够参阅下一种办法,不再写出。这种办法个人感觉不错,接纳和发送
都很精确,别的不需求核算每条句子的指令周期数。

办法三:中止法

中止的办法和计数器的办法差不多,仅仅当核算器溢出时便发生一次中止,用户能够
在中止程序中置标志,程序不断的查询该标志来决议是否发送或接纳下一位,当然程序中需对中
断进行初始化,一起编写中止程序。本程序运用Timer0中止。
#define TM0_FLAG P1_2 //设传输标志位
//计数器及中止初始化
void S2INI(void)
{
TMOD |=0x02; //计数器0,办法2
TH0=0xA0; //预值为256-96=140,十六进制A0
TL0=TH0;
TR0=0; //在发送或
接纳才开端运用
TF0=0;
ET0=1; //答应守时
器0中止
EA=1; //中止答应
总开关
}

//接纳一个字符
uchar RByte()
{
uchar Output=0;
uchar i=8;
TR0=1; //发动Timer0
TL0=TH0;
WaitTF0(); //等过开端

//发送8位数据位
while(i–)
{
Output >>=1;
if(RXD) Output |=0x80; //先收低位
WaitTF0(); //位间延时
}
while(!TM0_FLAG) if(RXD) break;
TR0=0; //中止
Timer0
return Output;
}
//中止1处理程序
void IntTimer0() interrupt 1
{
TM0_FLAG=1; //设置标志位。
}
//查询传输标志位
void WaitTF0( void )
{
while(!TM0_FLAG);
TM0_FLAG=0; //清标志位
}
中止法也是我引荐的办法,和计数法迥然不同。发送程序参阅计数法,信任是件很容
易的事。
别的还需注明的是本文所说的串口便是一般的三线制异步通讯串口(UART),只用RXD、TXD、
GND。

附:51 IO口模仿串口通讯C源程序(守时器计数法)

#include
sbit BT_SND =P1^0;
sbit BT_REC =P1^1;

#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

}
//查看是不是有开端位
bit StartBitOn()
{
return (BT_REC==0);

}
void main()
{
unsigned char gch;

TMOD=0x22;
PCON=00;

TR0=0; //在发送或接纳才开端运用
TF0=0;
TH0=(256-96); //9600bps 便是 1000000/9600=104.167微秒 履行的
timer是
//
104.167*11.0592/12= 96
TL0=TH0;
ET0=1;
EA=1;

PSendChar(0x55);
PSendChar(0xaa);
PSendChar(0x00);
PSendChar(0xff);

while(1)
{
if(StartBitOn())
{
gch=PGetChar();
PSendChar(gch);
}
}

}

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qiche/xinnengyuan/260861.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部