#include "c8051f020.h"
unsigned char data1
void SYSCLK_Init()
void PORT_Init()
void UART0_Init()
void SYSCLK_Init()
{
unsigned int i
OSCXCN=0X67//0X67=0110,0111
for(i=0i<256i++)//等待>1ms
while(!(OSCXCN&0X80)) //等待XTLVLD变为1
OSCICN=0X88//时钟失效监测器,选择外部时钟源作为系统时钟
}
void PORT_Init()
{
XBR0 = 0x27/*交叉开关配置,URAT总线TX0置到P0.0口,RX0置到P0.1口, SPI总线SCK配置到P0.2口,
MOSI为P0.4口, NSS为P0.5,MISO配置到P0.3口,IIC总线SDA置到P0.6口�SCK置到P0.7 TX1,RX1配置到P1.0,P1.1,CEX0,CEX1,配置到P1.2,P1.3,外部中断int0配置到P1.4 */
XBR1 = 0x04
XBR2 = 0x44 /*允许功能选择开关有效*/
P0MDOUT = 0x1A/*SCK、MOSI和NSS为推拉式输出,MISO为开漏式.*/
P74OUT =0xff
}
void UART0_Init()
{
SCON0=0x50 //串口方式1
TMOD=0X20//选用定时器1作为波特率发生器
TH1=0xF4 //波特率为4800
TL1=0xF4
ES0=1 //开启串口中断0
TF1=0
TR1=1 //定时器启动
PCON=0X80//波特率加倍 波特率为9600
TI0=1
}
void UART0_ISR() interrupt 4 using 1
{
if(RI0)
{
RI0=0//中断接收标志清零
data1=SBUF0//接收数据
SBUF0=data1//发送数据
while(TI0==0)
TI0=0//发送标志清零
}
}
main()
{
WDTCN=0XDE
WDTCN=0XAD
SYSCLK_Init()
PORT_Init()
UART0_Init()
EA=1
while(1)
}
一、 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)
}
}
现像:开了接收中断,然后不断进入中断,而且P_UART_Command2寄存器读取值为0x00f1,而 P_UART_Data 值为0x0000。分析:如果出现这样的情况,首先确定问题的所在,另外一端的UART发送是否存在问题,UART传输通道是否正常?等…… 如除SPCE061A外的器件、外围都正常,则检查一下MCU的IO口设置状态,一般会由于IOB7与IOB10设置有误才导至问题的出现的;
解决:所以剩下的工作就是好好检查一下程序里面除了初始化程序外,是不是还有地方修改了IO的属性了?
现像:开或者没有开串行口发送使能时,对IOB口(特别是IOB10)进行输出 *** 作时,在PC端的接收程序中(串口测试程序)都可以收到一些串口数据,且多为0x00。
分析:这些问题与MCU无关,因为232的电平转换芯片没有使能端,所以,对IOB10口进行输出 *** 作时,特别是有高低电平的交叉输出时,也会把信号送到232的电平转换芯片中去,这样就送到了PC端的串行口,所以有时就会在PC端那边的串行口会接收到数据;
解决:所以当在用户的系统中会存在使用了UART也使用到IOB10口用作他用时,可以建议用户选用带使能的232电平转换芯片,或者通讯时采用数据包格式,同时作数据包的软校验处理。
现像:打开UART IRQ中断后,程序会不断进入UART IRQ中断。
分析:……
解决:
关于UART的IRQ使用方式,有以下几点需要注意:
1,一般我会建议在用UART IRQ中断时,初始化P_UART_Command1时只打开接收中断,而不打开发送中断;从教程或者DataSheet中可以得知,发送中断是由TxRDY信号触发的,而TxRDY信号的意义为:该标志位被置为“1”,表示发送器的数据缓存器为空,已准备好可以发送写入P_UART_Data单元的数据。问题就在这个解释里了,很多人没有注意到这个问题;串行口只要在闲置时,P_UART_Data寄存器里面是空的,肯定是随时准备好可以发送数据了,这时候TxRDY标志位应该为1的,也就会随时触发Tx IRQ中断(Tx中断打开时);所以就会出现打开串行发送、接收中断后,程序会不断地进UART IRQ中断里面去的现像了。所以建议在初始化串行口时,只打开Rx中断,而不打开Tx中断,当发送数据(需要用到Tx中断的话)后,再打开Tx中断,等全部数据发送完后,再关掉Tx中断,就不会出现这样的问题了。另外,这样的情况其实并不算是问题,本身MCU要发送数据就是可控的嘛!只要程序设计时考虑多一点就可以避免这样的麻烦了。
2,另外一点也是很重要的,关于Rx中断,其实很多人在做实验的时候关没有注意到,在避免第一种情况后,还是会出现不断进入UART IRQ中断的现像;这跟UART的寄存器设置没有太多关系,试一下,当有这样的现像时,把UART的Tx、Rx的管脚连上(当然前提是保证IO的设置是没有问题的),应该不会再有不断进入UART IRQ中断的现像。这个例子说明,在用UART IRQ中断时,要保证UART的通道有正确的连接,即IOB7、IOB10脚连到了UART的通讯通道上了(双机通讯连接也行、MCU和PC通讯连接也行,只要保证UART通讯管脚有效地连接上了且连对了),就可以避免这样的问题。
3,一般对UART IRQ应用的设置,我会如此建议:先设置好IOB的相应的端口,IOB7输出、IOB10输入——》设置正确的串行波特率——》打开Rx中断,而不打开Tx中断(原因前面已有述)——》使能发送、接收管脚——》读一次P_UART_Data,以清除之前的UART状态及错误——》再下面是用户自的程序了……。当然在用户的UART联IRQ中断里面要记得进中断后清中断标志了(读写P_UART_Data寄存器即可清除)。
PS:
unsigned int b
*P_IOB_Attrib |= 0x0480
*P_IOB_Dir |= 0x0400
*P_IOB_Data = 0x0000
*P_UART_BaudScalarLow = 0x0000
*P_UART_BaudScalarHigh = 0x0005
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)