Chinaunix首页 | 论坛 | 博客
  • 博客访问: 291535
  • 博文数量: 23
  • 博客积分: 3011
  • 博客等级: 中校
  • 技术积分: 662
  • 用 户 组: 普通用户
  • 注册时间: 2005-11-26 13:43
文章分类

全部博文(23)

文章存档

2009年(19)

2008年(4)

我的朋友

分类: 嵌入式

2009-08-01 11:28:03

模拟串口通信,在波特率9600时非常稳定,已经使用在我的MDB协议软件中.
特点是完全在定时器中中断中完成收/发动作,并且使用示波器进行了校准.
注意:当波特率过高时,可能会有问题,这是软件模拟串口的通病.
 

/**********************************************
IO 口模拟232串行异步通讯程序 (占用两个IO口及一个定时器)
本程序使用示波器校正,基本能保证在PC发过来的方波的中间位置进行取样
说明: 1位起始位,9位数据位,1位停止位
问题:1. 如果PC侧连续发数据,并且51侧收到一个字节就响应一个字节到PC,
     会出现总是少发一个数据给PC的情况,如PC发 123456789,则51回 13579
         但该问题不影响MDB协议的实现,故这里不讨论该问题的解决方法.
     2. 如果比指定的定时器中断优先级高的中断进入,并且处理时间比较长,可能会造成接收到的数据不正确(这个问题在硬件串口中不存在)
     3. 如果PC连续发数据,51收到一个数据,准备收下一个数据时,随便进入一个中断,并且处理时间过长,将会导致丢失数据或收到错误的数据(这个问题在硬件串口中也存在)
    
**********************************************/

#include <reg52.h>
#include "uart_sim.h"

sbit BT_REC =P3^0;
sbit BT_SND =P3^1;
sbit P1_0    =P1^0;
#define ETx ET1
#define TRx TR1
#define TFx     TF1
#define THx     TH1
#define TLx     TL1
#define PTx     PT1


#define StartBitOn() (BT_REC==0)
#define UART_SIM_THx (256-208)
#define UART_SIM_THx_HALF (256-22)
//9600bps 就是 1000000/9600=104.167微秒 执行的

//时间是104.167*11.0592/12        = 96 (补偿数= 原数/2 - (原数/2)*(5/12)=(96/2)-(96/2)*(4/5)=?)

//时间是104.167*24.0/12=208.33=208 (补偿数= 原数/2 - (原数/2)*(5/12)=(208/2)-(208/2)*(4/5)=22)    //如果有问题可以将4/5改为3/4或2/3

//时间是104.167*22.1184/12        =192 (补偿数= 原数/2 - (原数/2)*(5/12)=(192/2)-(192/2)*(4/5)=20)


#define UART_SIM_TIMER_ENABLE do{TLx=THx; TRx=1;}while(0)//TRx = 1,启动Timer

#define UART_SIM_TIMER_DISABLE TRx=0
#define UART_SIM_TIMER_THx(x) do{THx = (x);TLx=THx;}while(0)

#define TRUE 1
#define FALSE 0

#define F_SEND 1
#define F_RECV 0
typedef unsigned char UINT8;
typedef bit                 BOOL;
typedef bit                 BIT1;

//************** 全局变量 for uart_sim begin

BOOL fSendOrRecv;        //F_SEND F_RECV

BOOL fRecvSuccess;            //TRUE or FALSE, 如果没有正确的接收到停止位,则置 FALSE

BOOL fRecvStartbit;            //TRUE 表示接收起始位, false 表示接收数据位或停止位

UINT8 sendOrRecvLen;    //要发送/或接收的数据的BIT数

UINT8 sendOrRecvByte;    //要发送/或接收到的字符

BIT1 bitMode;
//************** 全局变量 for uart_sim end


//定时器计数器1的中断

void UART_SIM_TIMER_Interrupt() interrupt 3    //Timer1

{
     P1_0 = !P1_0;    //输出一个脉冲用于校正取样点


    if(fSendOrRecv==F_RECV){

         if(fRecvStartbit){
            UART_SIM_TIMER_THx(UART_SIM_THx);
            fRecvStartbit = FALSE;
            return;
        }
         if(sendOrRecvLen>1){
         sendOrRecvByte>>=1;
         if(BT_REC)
         {
             sendOrRecvByte|=0x80;
         }
            --sendOrRecvLen;
        } else if(sendOrRecvLen==0) {
            //接收 stop

            fRecvSuccess = BT_REC;
            //停止定时器

            UART_SIM_TIMER_DISABLE;
        } else {
            //接收 mode

            bitMode = BT_REC;
            --sendOrRecvLen;
        }

     } else {
         
         if(fRecvStartbit){
            fRecvStartbit=FALSE;
            //停止定时器

            UART_SIM_TIMER_DISABLE;
            return;
        }
        if(sendOrRecvLen>1){
             BT_SND = (bit)(sendOrRecvByte&0x01);
            sendOrRecvByte>>=1;
            --sendOrRecvLen;
       } else if(sendOrRecvLen==0){
            //发送 stop

            BT_SND = 1;
            fRecvStartbit=TRUE;
            
        } else {
            //发送 mode

            BT_SND = bitMode;
            --sendOrRecvLen;
        }
     }
}

void uart_sim_sendByte(UINT8 b, bit mode){
    UART_SIM_TIMER_THx(UART_SIM_THx);
    UART_SIM_TIMER_ENABLE; //记数器0启动

    BT_SND = 0;     //启动位

    fSendOrRecv = F_SEND;
    fRecvStartbit=FALSE;

    sendOrRecvLen = 9;
    sendOrRecvByte = b;
    bitMode = mode;

    while(sendOrRecvLen);    
    while(TRx);
}

BOOL uart_sim_recvByte(UINT8* pbyte, UINT8* pmode){
     while(!StartBitOn());
     fSendOrRecv = F_RECV;
     fRecvStartbit = TRUE;    //置跳过起始位

     UART_SIM_TIMER_THx(UART_SIM_THx_HALF);
     UART_SIM_TIMER_ENABLE; //记数器0启动


     sendOrRecvLen = 9;

     while(sendOrRecvLen);
     *pbyte = sendOrRecvByte;
     *pmode = bitMode;
     while(TRx);
     return fRecvSuccess;
}
void uart_sim_init(){
    //TMOD=0x22; /*定时器1为工作模式2(8位自动重装),0为模式2(8位自动重装) */

    //如果使用T0,则应该是 TMOD=(TMOD&0xf0)|0x02;

    //如果使用T1,则应该是 TMOD=(TMOD&0x0f)|0x20;


    TMOD=(TMOD&0x0f)|0x20;

    //PCON=00;

    PCON &=0x7F;    //SET SMOD=0;


    TRx=0; //在发送或接收才开始使用

    TFx=0;
    THx=UART_SIM_THx;
    TLx=THx;
    ETx=1;//定时器/记数器T1的溢出中断允许位,ET,允 许中断


    PTx = 1;        //设定定时器x为高优先级

}

void test_send_nostop(){
    while(1){
        uart_sim_sendByte(0x55,1);
        uart_sim_sendByte(0xaa,1);
        uart_sim_sendByte(0xff,1);
        uart_sim_sendByte(0x0f,1);
        uart_sim_sendByte(0xf0,1);
        uart_sim_sendByte(0x00,1);
    }
}

void test_send2(){
    UINT8 i;
    static const char code a[]="#define UART_SIM_TIMER_ENABLE do{TL0=TH0; TR0=1;}while(0);//TR0 = 1";
    for(i=0; i<sizeof(a)/sizeof(char); ++i)
        uart_sim_sendByte(a[i], 1);
}

UINT8 idata recvbuf[50];
UINT8 idata recvmodebuf[50];
void test_recv2(){
    UINT8 * p = recvbuf;
    UINT8 * pm = recvmodebuf;
    UINT8 i=10;
    while(i--){
        if(!uart_sim_recvByte(p, pm)){
            *p = 0xee;
        }
        ++p;
        ++pm;
    }

    i=10;
    p=recvbuf;
    pm=recvmodebuf;
    while(i--){
        uart_sim_sendByte(*p,1);
        uart_sim_sendByte(*pm,1);
        ++p;
        ++pm;
    }
}

void test_send_recv(){
    UINT8 Getch, mode;
    while(1)
    {
     if(uart_sim_recvByte(&Getch, &mode)){
                 uart_sim_sendByte(Getch, 1);
         } else {
             uart_sim_sendByte(0xee, 1);
         }
    }
}

char code c512[3] _at_ 0x3b;
void main()
{
    
    uart_sim_init();

    EA=1;

    test_send2();

    test_recv2();

    test_send_recv();

    test_send_nostop();
}

阅读(5049) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~