51IO口模拟串口通讯C源程Word下载.docx
《51IO口模拟串口通讯C源程Word下载.docx》由会员分享,可在线阅读,更多相关《51IO口模拟串口通讯C源程Word下载.docx(26页珍藏版)》请在冰豆网上搜索。
![51IO口模拟串口通讯C源程Word下载.docx](https://file1.bdocx.com/fileroot1/2022-11/23/aa831694-5894-4999-893d-51c93fbc6644/aa831694-5894-4999-893d-51c93fbc66441.gif)
TIMER0_DISABLE;
//停止timer
//接收一个字符
unsignedcharPGetChar()
unsignedcharrch,ii;
rch=0;
//等过起始位
rch>
if(BT_REC)
rch|=0x80;
F_TM)
break;
returnrch;
//检查是不是有起始位
bitStartBitOn()
return(BT_REC==0);
voidmain()
unsignedchargch;
TMOD=0x22;
/*定时器1为工作模式2(8位自动重装),0为模式2(8位自动重装)*/
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);
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则为
96x2=192,如为19200BPS则为48,别的波特率就不算了,都刚好为整数个指令周期,妙吧。
至于
别的晶振频率大家自已去算吧。
现在就以11.0592M的晶振为例,谈谈三种模拟串口的方法。
方法一:
延时法
通过上述计算大家知道,串口的每位需延时0.104秒,中间可执行96个指令周期。
#define
uchar
unsigned
char
sbit
P1_0
=
0x90;
P1_1
0x91;
P1_2
0x92;
RXD
P1_0
TXD
P1_1
WRDYN
44
//写延时
RDDYN
43
//读延时
//往串口写一个字节
void
WByte(uchar
input)
i=8;
TXD=(bit)0;
//发送启始
位
Delay2cp(39);
//发送8位数据位
while(i--)
TXD=(bit)(input&
0x01);
//先传低位
Delay2cp(36);
input=input>
1;
//发送校验位(无)
TXD=(bit)1;
//发送结束
Delay2cp(46);
//从串口读一个字节
RByte(void)
Output=0;
temp=RDDYN;
Delay2cp(RDDYN*1.5);
//此处注意,等过起始位
Output
if(RXD)
|=0x80;
//先收低位
Delay2cp(35);
//(96-26)/2,循环共
占用26个指令周期
while(--temp)
//在指定的
时间内搜寻结束位。
Delay2cp
(1);
if(RXD)break;
//收到结束位便退出
return
Output;
//延时程序*
Delay2cp(unsigned
char
i)
while(--i);
//刚好两个
指令周期。
此种方法在接收上存在一定的难度,主要是采样定位存在需较准确,另外还必须知道
每条语句的指令周期数。
此法可能模拟若干个串口,实际中采用它的人也很多,但如果你用Keil
C,本人不建议使用此种方法,上述程序在P89C52、AT89C52、W78E52三种单片机上实验通过。
方法二:
计数法
51的计数器在每指令周期加1,直到溢出,同时硬件置溢出标志位。
这样我们就可以
通过预置初值的方法让机器每96个指令周期产生一次溢出,程序不断的查询溢出标志来决定是否
发送或接收下一位。
//计数器初始化
S2INI(void)
TMOD
|=0x02;
//计数器0,方式2
TH0=0xA0;
//预值为256-96=140,十六进制A0
TR0=1;
//开始计数
//发送启始位
WaitTF0();
//发送结束位
}
//查询计数器溢出标志位
WaitTF0(
)
TF0);
接收的程序,可以参考下一种方法,不再写出。
这种办法个人感觉不错,接收和发送
都很准确,另外不需要计算每条语句的指令周期数。
方法三:
中断法
中断的方法和计数器的方法差不多,只是当计算器溢出时便产生一次中断,用户可以
在中断程序中置标志,程序不断的查询该标志来决定是否发送或接收下一位,当然程序中需对中
断进行初始化,同时编写中断程序。
本程序使用Timer0中断。
TM0_FLAG
//设传输标志位
//计数器及中断初始化
//在发送或
接收才开始使用
//允许定时
器0中断
//中断允许
总开关
RByte()
//启动Timer0
//等过起始
//位间延时
TM0_FLAG)
//停止
Timer0
//中断1处理程序
IntTimer0()
interrupt
1
TM0_FLAG=1;
//设置标志位。
//查询传输标志位
TM0_FLAG);
TM0_FLAG=0;
//清标志位
中断法也是我推荐的方法,和计数法大同小异。
发送程序参考计数法,相信是件很容
易的事。
另外还需注明的是本文所说的串口就是通常的三线制异步通信串口(UART),只用RXD、TXD、
GND。
通用的I/O模拟串口程序
//UART.C
//
//GenericsoftwareuartwritteninC,requiringatimersetto3times
//thebaudrate,andtwosoftwareread/writepinsforthereceiveand
//transmitfunctions.
//*Receivedcharactersarebuffered
//*putchar(),getchar(),kbhit()andflush_input_buffer()areavailable
//*Thereisafacilityforbackgroundprocessingwhilewaitingforinput
//ColinGittins,SoftwareEngineer,HalliburtonEnergyServices
//ThebaudratecanbeconfiguredbychangingtheBAUD_RATEmacroas
//follows:
//#defineBAUD_RATE19200.0
//Thefunctioninit_uart()mustbecalledbeforeanycommscantakeplace
//Interfaceroutinesrequired:
//1.get_rx_pin_status()
//Returns0or1dependentonwhetherthereceivepinishighorlow.
//2.set_tx_pin_high()
//Setsthetransmitpintothehighstate.
//3.set_tx_pin_low()
//Setsthetransmitpintothelowstate.
//4.idle()
//Backgroundfunctionstoexecutewhilewaitingforinput.
//5.timer_set(BAUD_RATE)
//Setsthetimerto3timesthebaudrate.
//6.set_timer_interrupt(timer_isr)
//Enablesthetimerinterrupt.
//Functionsprovided:
//1.voidflush_input_buffer(void)
//Clearsthecontentsoftheinputbuffer.
//2.charkbhit(void)
//Testswhetheraninputcharacterhasbeenreceived.
//3.chargetchar(void)
//Readsacharacterfromtheinputbuffer,waitingifnecessary.
//4.voidturn_rx_on(void)
//Turnsonthereceivefunction.
//5.voidturn_rx_off(void)
//Turnsoffthereceivefunction.
//6.voidputchar(char)
//Writesacharactertotheserialport.
stdio.h>
#defineBAUD_RATE19200.0
#defineIN_BUF_SIZE256
#defineTRUE1
#defineFALSE0
staticunsignedcharinbuf[IN_BUF_SIZE];
staticunsignedcharqin=0;
staticunsignedcharqout=0;
staticcharflag_rx_waiting_for_stop_bit;
staticcharflag_rx_off;
staticcharrx_mask;
staticcharflag_rx_ready;
staticcharflag_tx_ready;
staticchartimer_rx_ctr;
staticchartimer_tx_ctr;
staticcharbits_left_in_rx;
staticcharbits_left_in_tx;
staticcharrx_num_of_bits;
staticchartx_num_of_bits;
staticcharinternal_rx_buffer;
staticcharinternal_tx_buffer;
staticcharuser_tx_buffer;
voidtimer_isr(void)
charmask,start_bit,flag_in;
//TransmitterSection
if(flag_tx_ready)
if(--timer_tx_ctr<
=0)
mask=internal_tx_buffer&
internal_tx_buffer>
=1;
if(mask)
set_tx_pin_high();
set_tx_pin_low();
timer_tx_ctr=3;
if(--bits_left_in_tx<
flag_tx_ready=FALSE;
//ReceiverSection
if(flag_rx_off==FALSE)
if(flag_rx_waiting_for_stop_bit)
if(--timer_rx_ctr<
flag_rx_waiting_for_stop_bit=FALSE;
flag_rx_ready=FALSE;
internal_rx_buffer&
=0xFF;
if(internal_rx_buffer!
=0xC2)
inbuf[qin]=
internal_rx_buffer;
if(++qin>
=IN_BUF_SIZE)
qin=0;
else//rx_test_busy
if(flag_rx_ready==FALSE)
start_bit=get_rx_pin_status();
//TestforStartBit
if(start_bit==0)
flag_rx_ready=TRUE;
internal_rx_buffer=0;
timer_rx_ctr=4;
bits_left_in_rx=
rx_num_of_bits;
rx_mask=1;
else//rx_busy
{
//rcv
timer_rx_ctr=3;
flag_in=
get_rx_pin_status();
if(flag_in)
internal_rx_buffer|=rx_mask;
rx_mask<
<
if(--
bits_left_in_rx<
flag_rx_waiting_for_stop_bit=TRUE;
voidinit_uart(void)
flag_rx_off=FALSE;
rx_num_of_bits=10;
tx_num_of_bits=10;
timer_set(BAUD_RATE);
set_timer_interrupt(timer_isr);
//Enabletimerinterrupt
char_getchar(void)
charch;
do
while(qout==qin)
idle();
ch=inbuf[qout]&
0xFF;
if(++qout>
qout=0;
while(ch==0x0A||ch==0xC2);
return(ch);
void_putchar(charch)
while(flag_tx_ready);
user_tx_buffer=ch;
//invoke_UART_transmit
bits_left_in_tx=tx_num_of_bits;
internal_tx_buffer=(user_tx_buffer<
1)|0x200;
flag_tx_ready=TRUE;
voidflush_input_buffer(void)
charkbhit(void)
return(qin!
=qout);
voidturn_rx_on(void)
voidturn_rx_off(void)
flag_rx_off=TRUE;
MSC51汇编代码编写的模拟串口程序
T2作为波特率控制
UART_RXD是硬中断0或1口,如果能进入中断,说明该线有一个起始位