51单片机模拟串口的多种方法讲解

发布者:RadiantDusk最新更新时间:2015-08-26 来源: eefocus关键字: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;   
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。   [page]
 
   
//附: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   
   
}   
//检查是不是有起始位   
bit StartBitOn()   
{   
return  (BT_REC==0);   
   
}   
void main()   
{   
unsigned char gch;   
   
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单片机的数字音乐盒C语言驱动程序
下一篇:51单片机的315M接收头接收汇编程序(从机)

推荐阅读最新更新时间:2024-03-16 14:30

51单片机——74HC595、LED点阵、直流电动机C语言入门编程
74HC595: 是一个8位串行输入、并行输出的位移缓存器,其中并行输出为三态输出(即高电平、低电平和高阻抗)。移位寄存器和存储器是单独的时钟。数据放在SER中,在SCK的上升沿输入,在RCK 的上升沿进入到存储器中。移位寄存器有一个串行输入(DS),和一个串行输出(Q7非),和一个异步的低电平复位,存储寄存器有一个并行 8 位的,具有三态的总线输出,当 MR 为高电平,OE为低电平时,数据在SHCP上升沿进入移位寄存器,在STCP上升沿输 出到并行端口。(先发高位后发低位) 15和1到 7脚 QA--QH→并行数据输出;9脚QH非→串行数据输出;10脚SCLK非(MR)→低电平复位引脚;11脚SCK(SHCP)→ 移
[单片机]
<font color='red'>51单片机</font>——74HC595、LED点阵、直流电动机C语言入门编程
51单片机C语言学习 6
第六课 变量   上课所提到变量就是一种在程序执行过程中其值能不断变化的量。要在程序中使用变量必须先用标识符作为变量名,并指出所用的数据类型和存储模式,这样编译系统才能为变量分配相应的存储空间。定义一个变量的格式如下:       数据类型   变量名表   在定义格式中除了数据类型和变量名表是必要的,其它都是可选项。存储种类有四种:自动(auto),外部(extern),静态(static)和寄存器(register),缺省类型为自动(auto)。这些存储种类的具体含义和用法,将在第七课《变量的存储》中进一步进行学习。   而这里的数据类型则是和我们在第四课中学习到的名种数据类型的定义是一
[单片机]
如何编写51单片机音乐程序
正好做了一个音乐程序,贡献出来给你吧,希望你能满意。 电路连接很简单,在P3.0端口接个蜂鸣器,不过效果一般。如果想效果好点,就加个放大电路,接个小功率喇叭就行。因为这里发电路不方便,所以如果需要放大电路,可加我QQ:7468485。 音乐程序的设计原理和程序如下: 设计原理 ⑴ 总体原理: 乐曲中不同的音符,实质就是不同频率的声音。通过单片机产生不同的频率的脉冲信号,经过放大电路,由蜂鸣器放出,就产生了美妙和谐的乐曲。 ⑵ 单片机产生不同频率脉冲信号的原理: 1)要产生音频脉冲,只要算出某一音频的脉冲(1/频率),然后将此周期除以2,即为半周期的时间,利用定时器计时这个半周期的时间,每当计时到后就将输出脉冲的I/O反相,然后重复
[单片机]
基于51单片机的多任务机制及应用
1 引言 传统的单片机程序一般采用单任务机制,单任务系统具有简单直观、易于控制的优点。然而由于程序只能按顺序依次执行,缺乏灵活性,只能使用中断函数实时地处理一些较短的任务,在较复杂的应用中使用极为不便。嵌入式多任务操作系统的出现解决了这个问题。在多任务系统中可以同时执行多个并行任务,任务之间可以相互跳转。但是嵌入式操作系统在提供强大功能的同时,也带来了代码量大、结构复杂、对硬件要求较高、开发难度大且成本高等问题。而很多时候只需要实现简单的多任务操作就可以满足实际需要,本文设计的这种简单的多任务机制,在只增加极少量C语言代码的前提下,不需使用汇编,无需对原本的程序进行大改动,就可以实现多任务操作。 实时操作系统RTOS的核
[单片机]
51单片机-搞定矩阵键盘
矩阵按键貌似是学51单片机里面必备的一课,现在基本所有的51开发板都带这部分电路。因为它用8个IO口实现了16个按键的扫描检测,实现了IO口更充分的利用。 扫描矩阵按键的程序说简单也简单,说复杂也复杂。说它简单,是因为现在网络太发达了,随便搜索一下,都能找到可以直接用的源码。说它复杂,是因为如果不借助网络,全凭自己去摸索,确实要花一部分时间。 我当时是性子比较倔,只在网上看了矩阵按键的原理,没有看参考代码,完全凭借自己的理解,写了一段矩阵按键的扫描程序,花了三天时间才调试通。那三天,真的是折腾的死去活来,但是当程序调好的时候,真的感觉自己还是很拉风的。 这里,不是让大家学我那样去闭门造车,因为太
[单片机]
学<font color='red'>51单片机</font>-搞定矩阵键盘
C51单片机模拟I2C总线驱动程序设计(1)
/********************************** I2C 总线驱动 ****************************** 模块名:I2C 总线驱动 型号:I2C 功能描述: 此模块包括发送数据及接收数据,应答位发送,并提供了几个直接面对器件的操作函数,能 很方便的与用户程序进行连接并扩展。需要注意的是,函数是采用延时方法产生 SCL 脉冲, 对高晶振频率要做一定的修改!!在写 E2PROM 的时候一定要延时!!! 说明: 1us机器周期,晶振频率要小于12MHz 返回1:则操作成功,返回0:则操作失败。 sla为器件从地址,suba为器件子地址。 ********
[单片机]
51单片机写个电动小风扇方案
接下来我将为你介绍如何使用51单片机来实现一个简单的电动小风扇。 1、硬件准备 首先,我们需要准备好以下硬件: 51单片机主板 直流电机 驱动模块 电池盒或电源模块 一些导线和杜邦线 2、电路连接 接下来,我们需要将这些硬件进行连接。具体连接方式如下: 将直流电机连接到驱动模块上,驱动模块需要根据电机的电压和电流进行选择。 将驱动模块的输入端连接到单片机主板上的某个IO口,用于控制电机的开关。 将电池盒或电源模块连接到单片机主板上,用于提供电源。 连接完成后,检查一遍电路是否连接正确,然后进行下一步。 3、编写代码 现在,我们需要编写控制代码来控制电机的开关。以下是一个简单的示例代码: #include
[单片机]
用<font color='red'>51单片机</font>写个电动小风扇方案
采用MCS51单片机的大屏幕LED显示屏高速控制方案
引言   LED显示屏的基本工作原理是动态扫描。显示控制的过程是先从数据存储器读得字模数据,再通过单片机的串行口或并行口将数据写给LED点阵片,然后再行扫描。   动态扫描方案和静态显示方案相比节省驱动元件,但要求刷新频率高于50 Hz,以避免显示的图像或文字出现闪烁。由于刷新频率的限制,一片单片机能控制显示元件的片数是较少的。   现在大屏幕LED显示屏的应用已越来越广泛。为了对成百、上千片的LED点阵片实现有序的、快速的显示控制,人们动了许多脑筋,双CPU、双RAM的方案,FPGA的方案等都获得了成功的应用;但是这些方案的显示控制过程还是先读后写。   本方案另开思路:用一条读指令,将读和写合在一步完成,可大大
[单片机]
采用MCS<font color='red'>51单片机</font>的大屏幕LED显示屏高速控制方案
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多每日新闻
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved