通过计算大家知道,串口的每位需延时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)//刚好两个
指令周期。
}
此种方法在接收上存在一定的难度,主要是采样定位存在需较准确,另外还必须知道
每条语句的指令周期数。
方法二:计数法
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()//位间延时
}
return Output
}
//中断1处理程序
void IntTimer0() interrupt 1
{
TM0_FLAG=1//设置标志位。
}
//查询传输标志位
void WaitTF0( void )
{
while(!TM0_FLAG)
TM0_FLAG=0//清标志位
}
一、 IO模拟UART发送
串口通信属于 串行 异步 半双工 的通信模式
1、 最近在调试一个IO模拟UART的程序,把调试过程中遇到的问题总结一下。对于UART的发送部分(主机模式)还是比较容易实现的。比较麻烦的做从机时,UART接收还在调试,可以接收数据,但还存在很多问题。
(1) 起始位 :总线没通信是高电平状态,要进行通信时,总线拉低发出“逻辑0”信号,表示开始传输数据
(2) 数据位 :通常以1byte数据为标准,从低位开始传输,通过时钟频率来定位数据的传输
(3) 奇偶校验位 :通过在数据末尾加上1位,使得数据包中的1的个数为偶数(偶校验)或者是奇数(奇校验),来确定数据的准确性。
(4) 停止位 :在通信结束时,将总线拉高发出“逻辑1”信号,表示传输结束,也给下次进行通信提供了校准时间的机会
2、波特率
波特率表示每秒钟传送的码元符号的个数,是 衡量数据传输速率的指标 ,它用单位时间内载波调制状态改变的次数来表示,1波特即指每秒传输1个符号。
若 波特率为9600bps,那么传输一位数据的时间是在1000ms/9600bps=0.104ms
3、串口发送数据组成
数据由:1bit起始位+8bit数据+1bit停止位构成;
所以,搞懂这些原理,串口发送并不难,主要把单片机的定时器配置在104us左右溢出中断,保证每一位时间间隔在104us 左右即可。
我用的是51单片机,51本身自带一个串口,但自己想用IO模拟一个串口出来,便于自己更好的理解串口,也作为自己的一个技术储备。
/**********************************************51单片机IO模拟UART实验************************************************************************************/
/****************************************************
*文件名:uart.c
****************************************************/
#include "reg52.h"
#include "type.h"
#include "uart.h"
#include "timer.h"
void UART_INIT(void)
{
TX_D=1
Timer1Init() //T1 初始化
}
void WAIT_TF1(void)
{
while(!TF1) //查询计数器溢出标志位
TF1=0
}
//写数据很快的话,定时器不稳定而导致发送的数据会有错码,发送时应适当延时降低错码概率
void Write_DATA(uint8_t input)
{
uint8_t i=8 //写入1byte数据
TR1=1 //开始计时
TX_D=0 //拉低信号线准备发送数据 起始信号
WAIT_TF1() //106us溢出一次,溢出进行下一位的传输
while(i--) //开始写数据
{
TX_D=input&0X01
WAIT_TF1()
input>>=1
}
TX_D=1 //8位数据发完拉高信号线 停止信号
WAIT_TF1()
TR1=0 //停止计时
}
//发送字符串
void Send_Char(uint8_t *buf)
{
while(*buf != '\0')
{
Write_DATA(*buf)
buf++
}
}
//发送数字
void Send_Num(uint8_t *buf,uint16_t s)
{
while(s--)
{
Write_DATA(*buf+48) //ASCII 将字符转换成数字
buf++
}
}
//发送多个数据
void Send_ND(uint8_t *buf,uint16_t len)
{
while(len--)
{
Write_DATA(*buf)
buf++
}
}
#include "type.h"
#ifndef __UART_H__
#define __UART_H__
sbit TX_D=P0^6
extern uint8_t flag2
extern void Send_Char(uint8_t *buf) //发送字符
extern void Send_ND(uint8_t *buf,uint16_t len) //发送N个数据
extern void Send_Num(uint8_t *buf,uint16_t s) //发送数字
extern void UART_INIT(void) //串口初始化
extern void WAIT_TF1(void) //等待TF1溢出
extern void Write_DATA(uint8_t input) //写数据
#endif
/*****************************************************************
文件名:timer.c
*******************************************************************/
#include "reg52.h"
#include "type.h"
#include "timer.h"
uint8_t flag1=0
void Timer1Init()
{
TMOD = 0x10 //T1 方式1
TH1 = 0xFF //波特率9600bps 1000ms/9600bps=0.104ms
TL1 = 0xA0 //定时器误差2us左右 106us
EA = 1
ET1 = 1
TR1 = 1
TF1=0
}
void Time1() interrupt 3
{
if(TR1==1)
{
TH1=0XFF
TL1=0xA0
flag1=1
}
}
#include "type.h"
#ifndef __TIMER_H__
#define __TIMER_H__
extern uint8_t flag1
extern void Timer1Init(void)
#endif
#ifndef __TYPE_H__
#define __TYPE_H__
#define uint8_t unsigned char
#define uint16_t unsigned int
#endif
/*******************************************************主程序--测试部分****************************************************************/
#include "reg52.h"
#include "type.h"
#include "timer.h"
#include "uart.h"
#include "delay.h"
uint8_t buffer[5]
uint8_t Buff[5]={1,2,3,4,5}
void main(void)
{
UART_INIT()
Write_DATA('A')
Send_Char("testuart")
Send_Num(Buff,5)
while(1)
}
如果发送发在while(1)中跑的话,需要加延时来降低由于查询过快导致的发送乱码的概率,事实证明加延时效果更好,结果更准确。
二、 IO模拟UART接收
1、模拟串口接收部分是可以接收,但存在很多问题
2、我对问题进行了总结以便后期改进
3、读数据 接收数据的效果不是很好,需要连续的点击发送字符才会成功
问题解析 :估计是等到单片机扫描时,串口helper已经把数据发送完了,单片机这边对不上起始信号从而导致错峰,所以串口helper需要不停的点击发送在这个期间对上了就进去了。
最近调试了一下,现在接收部分已经可以正常接收了。
接收部分
uint8_t Receive_Data(void)
{
uint8_t receive=0,t=0
uint8_t i=8
while(RX_D) //等待起始信号,超时自动退出
TR1=1 //开始计时
WAIT_TF1()
while(i--)
{
receive>>=1
if(RX_D)receive|=0x80
WAIT_TF1()
}
TR1=0
return receive
}
void Recevice(uint8_t *temp,uint8_t data_size)
{
while(data_size--)
{
*temp++=Receive_Data()
}
}
TEST
uint8_t recv[20]={0}
void main(void)
{
UART_INIT()
while(1)
{
Recevice(recv,5)
Send_ND(recv,5)
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)