51单片机模拟串口的三种方法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
51单片机模拟串口的三种方法
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,刚好为一整数,如果为4800BP S则为
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。
附: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);
{
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;
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;
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);
}
{
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);
}
}
}
I2C串行总线标准驱动程序(C51)-万能程序
发布时间:2008年2月25日 16时0分
/*------------------------------------------------------------------------------------------
[文件名] I2C.c
[版本] 1.1b
[作者] 鞠春阳
[最后修改时间] 2003年5月12日
[版权所有]
[资料] 请到下载
===================================================================== ======================*/</P><P>#include "reg51.h"
#include "intrins.h"
unsigned char SystemError;
sbit SCL= P1^6; //定义串行时钟线所在口使用时根据自己的需要来定义
sbit SDA= P1^7; //定义串行数据线所在口使用时根据自己的需要来定义
#define SomeNOP(); {_nop_();_nop_();_nop_();_nop_();}
/*--------------------------------------------------------------------------------
调用方式:void AD7416_I2CStart(void) 2003/05/04
函数说明:私有函数,I2C专用
---------------------------------------------------------------------------------*/
void I2CStart(void)
{
EA=0;
SDA=1; SCL=1; SomeNOP();//数据线保持高,时钟线从高到低一次跳变,I2C通信开始
SDA=0; SomeNOP();
SCL=0;
}
/*--------------------------------------------------------------------------------
调用方式:void AD7416_I2CStop(void) 2003/05/04
函数说明:私有函数,I2C专用
---------------------------------------------------------------------------------*/
void I2CStop(void)
{
SCL=0; SDA=0; SomeNOP(); //数据线保持低,时钟线从低到高一次跳变,I2C
通信停止
SCL=1; SomeNOP(); SDA=1;
EA=1;
}
/*--------------------------------------------------------------------------------
调用方式:I2CAck(void) 2003/05/13
函数说明:私有函数,I2C专用,等待从器件接收方的应答
ACK BY AD7416
---------------------------------------------------------------------------------*/
WaitAck(void)
{
unsigned char errtime=255;//因故障接收方无ACK,超时值为255。
SDA=1;
SCL=1;
SystemError=0x10;
while(SDA)
{ errtime--;
if(!errtime)
{ AD7416_I2CStop();
AD7416_SystemError=0x11; //出错后给全局变量赋值
return;
}
}
SCL=0;
}
/*--------------------------------------------------------------------------------
调用方式:void SendAck(void) ﹫2003/05/13
函数说明:私有函数,I2C专用,主器件为接收方,从器件为发送方时,应答信号。
---------------------------------------------------------------------------------*/
void SendAck(void)
{
SDA=0; SomeNOP(); //数据线保持低,时钟线发生一次从高低的跳变发送一个应答信号
SCL=1; SomeNOP();
SCL=0;
}
/*--------------------------------------------------------------------------------
调用方式:void SendAck(void) ﹫2003/05/13
函数说明:私有函数,I2C专用,主器件为接收方,从器件为发送方时,非应答信号。
--------------------------------------------------------------------------------*/
void SendNotAck(void)
{
SDA=1; SomeNOP(); //数据线保持高,时钟线发生一次从高低的跳变没有应答SCL=1; SomeNOP();
SCL=0;
}
/*--------------------------------------------------------------------------------
调用方式:void I2CSend(uchar ch) 2003/05/13
函数说明:私有函数,I2C专用
---------------------------------------------------------------------------------*/
void I2CSendByte(Byte ch)
{
unsigned char i=8;
while (i--)
{
SCL=0;_nop_();
SDA=(bit)(ch&0x80); ch<<=1; SomeNOP(); //时钟保持低可以发送数据
SCL=1; SomeNOP();
}
SCL=0;
}
/*--------------------------------------------------------------------------------
调用方式:uchar I2CReceive(void) 2003/05/13
函数说明:私有函数,I2C专用
---------------------------------------------------------------------------------*/
Byte I2CReceiveByte(void)
{
unsigned char i=8,data=0;
SDA=1;
while (i--)
{
data<<=1;
SCL=0;SomeNOP();
SCL=1;SomeNOP(); //时钟做一次从低到高的跳变可以接收数据
data|=SDA;
}
SCL=0;
return data;
}
标签:电源mc34063中文
MC34063 中文资料PDF及MC34063应用
1. MC34063 DC/DC变换器控制电路简介:
MC34063是一单片双极型线性集成电路,专用于直流-直流变换器控制部分。
片内包含有温度补偿带隙基准源、一个占空比周期控制振荡器、驱动器和大电流输出开关,能输出1.5A的开关电流。
它能使用最少的外接元件构成开关式升压变换器、降压式变换器和电源反向器。
特点:
*能在3.0-40V的输入电压下工作
*短路电流限制
*低静态电流
*输出开关电流可达1.5A(无外接三极管)
*输出电压可调
*工作振荡频率从100HZ到100KHZ
2.MC34063引脚图及原理框图
3 MC34063应用电路图:
3.1 MC34063大电流降压变换器电路
3.2 MC34063大电流升压变换器电路
3.3 MC34063反向变换器电路
3.4 MC34063降压变换器电路
3.5 MC34063升压变换器电路
=========================================================== =====
MC34063 电路原理
振荡器通过恒流源对外接在C T管脚(3 脚)上的定时电容不断地充电和放电以产生振荡波形。
充电和放电电流都是恒定的,振荡频率仅取决于外接定时电容的容量。
与门的C 输入端在振荡器对外充电时为高电平,D 输入端在比较器
的输入电平低于阈值电平时为高电平。
当C 和D输入端都变成高电平时触发器被置为高电平,输出开关管导通;反之当振荡器在放电期间,C 输入端为低电平,触发器被复位,使得输出开关管处于关闭状态。
电流限制通过检测连接在V CC
和5 脚之间电阻上的压降来完成功能。
当检测到电阻上的电压降接近超过300 mV 时,电流限制电路开始工作,这时通过C T管脚(3 脚) 对定时电容进行快速充电以减少充电时间和输出开关管的导通时间,结果是使得输出开关管的关闭时间延长。
带隔离输出的DC-DC变换电路方案
电路原理
电路原理如图2 所示,该电路是在MC34063典型的降压电路上,用开关变压器取代自感线圈实现的。
利用开关变压器以获取隔离直流电源的能量供给。
开关变压器的副边交变电压经BR1的全波整流,C19 、C20 的滤波,L2 、L3的高频遏制及U7 、U8 线性稳压器的稳压,便可获取稳定的直流输出。
在确定的硬件系统中,用于向数字系统供电的V CC电源负荷是稳定的,通过开关变压器的交变方波的占空比也是稳定的,因此,根据+ 5 V、- 5 V 的负荷情况,恰当的选择开关变压器的铁芯、骨架参数及原、副边匝数,便可获得与供电电源、数字电路电源V CC隔离的+ 5 V 、- 5 V直流输出。
图2 带隔离输出的DC-DC 变换电路原理图
电路参数及实验数据
开关变压器参数
铁芯形式: EI22B ,磁导率:2000 H/ m ,骨架参数: EI22210P ,原边线圈匝数: 60 匝,副边线圈匝数:40 匝。
电源拉偏实验参数
在规定范围内改变输入电压,用万用表测输出端电压,数据如表1 。
负荷调整实验参数
对3 组直流电源分别接入不同负载,用万用表测取输出电压,数据如表2 。
纹波系数
在一定负荷下用示波器观察输出电压,测取纹波电压(峰2峰值) < 5mV ,纹波系数= 5/5000×100 % = 0.1%。
满足一般硬件电路对电源的要求。
结论
该电路使用元件少,结构简单,可靠实用。
在数据采集器的14位A/ D转换器中,采用上述电源,实验室长期试运行表明:供电稳定,无跳码,检测精度不低于0.1%。
该电路适用于微处理器的模拟电路与数字电路的混合硬件系统中,需要低压、小功率、供电场合,如温度巡检仪、数据采集器等装置。
系统分类: 电源技术 | 用户分类: 电子电路 | 来源: 整理 | 【推荐给朋友】 | 【添加到收藏夹】。