首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用51的定时器模拟串口

使用51的定时器模拟串口

对于一部分坑爹的51单片机来说,压根就米有串口啊!!特别是用来做小东西的利器:STC15F100系列的,坑爹到串口都米有的地步。当然,我们可以使用软件来模拟串口。可惜,软件模拟的串口速度那个慢(12T单片机),误码率比硬件串口那个高……但是总比没有的好。这个玩意不像硬件一样能支持全双工(12T的。1T的可以,毕竟12T的定时器中断频率快了主程序直接会被阻塞了……)。
另外STC的手册上面说的8位自动重载定时器竟然TMOD等于6!导致我搞了一下午都调不通,没想到老妖写错了……定时器重载模式应该是=2.我现在想把TMOD寄存器的O去掉,再用这个词问候他们全家!!
顺便说一句,1T的51单片机足够在定时器里实现全双工,这就是为什么老妖说真实波特率要/3的原因。有RCNT=3这个语句在,所以可以实现同时接收发送……老妖写程序不写注释是出了名的……但是至于普通的12T单片机。如果也用老妖得的程序,要注意定时器模式没有16位自动重装模式。如果/3的话,9600就成3200的波特率了……还是非标准的,这个程序在89C52上最高波特率只能到9600,继续往上加就严重误码……所以模拟串口还是用1T单片机或者12T降3倍速吧,(但是速度能卡的可怕,我感觉9600都卡)。要不然误码率能搞死人。
对了,顺便说明一下,这个程序在keil c51上通不过,因为Keil内置putchar……会提示重复定义。能过keil的程序请参考 http://www.51hei.com/mcu/1541.html, 而且似乎keil的printf函数有毛病……
#include <hwconfig.h>#include <type-def.h>#include <stdio.h>void WaitTF0(){        while(!TF0);        TF0=0;}void WByte(BYTE out){    //发送启始位    BYTE i=8;    BYTE tmp=out;    TR0=1;//开定时器    TX1=0;    WaitTF0();    //发送8位数据位    while(i--)    {        TX1=(tmp&0x01);     //先传低位        tmp=tmp>>1;        WaitTF0();    }    //发送校验位(无)    //发送结束位    TX1=1;    WaitTF0();    TR0=0;}    void putchar(char ch){        WByte(ch);}BYTE RByte(){        BYTE in=0;        BYTE cnt;        while(RX1==1);//等待RXD变低,启动定时器,这个是阻塞模式        TR0=1;//同步开定时器        WaitTF0();//等到周期过去        for(cnt=0;cnt<8;cnt++)        {                in=in >>1;//从高移到低                if(RX1==1) in = in | 0x80;//如果RXD=1,则最高置位                WaitTF0();//等待一位过去        }        //等待停止位        //WaitTF0();        TR0=0;//关闭定时器        return in;}void Init_VSer(){        //指令周期=(1000000/Baud)/(12/SysClock)        //定时器值=0x100-指令周期    TMOD |=0x02;  //计数器0,方式2        TH0=0xa0;                        TL0=0xa0;            TR0=0;       //停止计数    //TF0=0;                //ET0=0;                //T1中断关        //EA=0;                //总中断关}void main(){        BYTE a;        Init_VSer();        printf("Hello,world!\n");        while(1)        {                a=RByte();//阻塞模式                putchar(a);        }}
顺便附上老妖的减掉三倍速的1T单片机模拟串口程序。稳定得多,当然效率也低的多。那个是靠定时器中断实现的。不过至少不会象在12T上到主程序几乎跑不动的地步。强烈要求老妖出有串口的C版本!
/*----------------------------------------------------*//* --- STC MCU International Limited -----------------*//* --- 演示STC 15 系列单片机利用定时器1实现模拟串口功能-----*//* --- Mobile: (86)13922809991 -----------------------*//* --- Fax: 86-755-82905966 --------------------------*//* --- Tel: 86-755-82948412 --------------------------*//* --- Web: www.STCMCU.com ---------------------------*//* 如果要在程序中使用或在文章中引用该程序, -----------------*//* 请在程序中或文章中注明使用了STC的资料及程序 -------------*//*----------------------------------------------------*/#include "reg51.h"//define baudrate const//BAUD = 256 - SYSclk/3/BAUDRATE/M (1T:M=1; 12T:M=12)//NOTE: (SYSclk/3/BAUDRATE) must be greater than 98, (RECOMMEND GREATER THAN 110)//开始一直看不懂为什么要除三//#define BAUD         0xF400          // 1200bps @ 11.0592MHz//#define BAUD         0xFA00         // 2400bps @ 11.0592MHz//#define BAUD         0xFD00         // 4800bps @ 11.0592MHz//#define BAUD         0xFE80         // 9600bps @ 11.0592MHz//#define BAUD         0xFF40          //19200bps @ 11.0592MHz//#define BAUD         0xFFA0                 //38400bps @ 11.0592MHz//#define BAUD         0xEC00         // 1200bps @ 18.432MHz//#define BAUD         0xF600          // 2400bps @ 18.432MHz//#define BAUD         0xFB00         // 4800bps @ 18.432MHz//#define BAUD         0xFD80         // 9600bps @ 18.432MHz//#define BAUD         0xFEC0         //19200bps @ 18.432MHz#define BAUD         0xFF60          //38400bps @ 18.432MHz//#define BAUD         0xE800          // 1200bps @ 22.1184MHz//#define BAUD         0xF400          // 2400bps @ 22.1184MHz//#define BAUD         0xFA00         // 4800bps @ 22.1184MHz//#define BAUD         0xFD00         // 9600bps @ 22.1184MHz//#define BAUD         0xFE80         //19200bps @ 22.1184MHz//#define BAUD         0xFF40          //38400bps @ 22.1184MHz//#define BAUD         0xFF80          //57600bps @ 22.1184MHz//define UART TX/RX porttypedef bit BOOL;typedef unsigned char BYTE;typedef unsigned int WORD;BYTE TBUF,RBUF;BYTE TDAT,RDAT;BYTE TCNT,RCNT;BYTE TBIT,RBIT;BOOL TING,RING;BOOL TEND,REND;void UART_INIT();BYTE t, r;BYTE buf[16];void main(){    TMOD = 0x00;//timer1 in 16-bit auto reload mode    AUXR = 0x40;//timer1 working at 1T mode    TL1 = BAUD;//initial timer1 and set reload value    TH1 = BAUD>>8;    TR1 = 1;//timer1 start running[原文有错误],感觉这个代码不像老妖写的。注释都英文……    ET1 = 1;//enable timer1 interrupt    PT1 = 1;//improve timer1 interrupt priority    EA = 1;//open global interrupt switch    UART_INIT();    while (1)    {//user's function        if (REND)        {            REND = 0;            buf[r++ & 0x0f] = RBUF;        }        if (TEND)        {            if (t != r)            {                TEND = 0;                TBUF = buf[t++ & 0x0f];                TING = 1;            }        }    }}//-----------------------------------------//Timer interrupt routine for UARTvoid tm1() interrupt 3 using 1{    if (RING)    {        if (--RCNT == 0)        {            RCNT = 3;//直到我看到了这里……//reset send baudrate counter            if (--RBIT == 0)            {                RBUF = RDAT;          //save the data to RBUF                RING = 0;//stop receive                REND = 1;//set receive completed flag            }            else//这TM不是坑爹吧            {                RDAT >>= 1;                if (RXB) RDAT |= 0x80;//shift RX data to RX buffer            }        }    }    else if (!RXB)    {        RING = 1;//set start receive flag        RCNT = 4;//initial receive baudrate counter        RBIT = 9;//initial receive bit number (8 data bits + 1 stop bit)    }    if (--TCNT == 0)    {        TCNT = 3;//reset send baudrate counter        if (TING)//judge whether sending        {            if (TBIT == 0)            {                TXB = 0;//send start bit                TDAT = TBUF;          //load data from TBUF to TDAT                TBIT = 9;//initial send bit number (8 data bits + 1 stop bit)            }            else            {                TDAT >>= 1;//shift data to CY                if (--TBIT == 0)                {                    TXB = 1;                    TING = 0;//stop send                    TEND = 1;//set send completed flag                }                else                {                    TXB = CY;//write CY to TX port                }            }        }    }}//-----------------------------------------//initial UART module variablevoid UART_INIT(){    TING = 0;    RING = 0;    TEND = 1;    REND = 0;    TCNT = 0;    RCNT = 0;}唉,串口之争告一段落。我还是希望有硬件串口。
另外就是我实在不知道诡异的STC ISP串口是怎么在11.0592下面跑到115200的。本来加倍是溢出/16,那么它就算加倍的加倍,也就是/8……既然软件模拟不可能实现如此高的波特率。而且6T模式能到230400,我就认为他的SFR有串口四倍速选项。可惜老妖没有拿出来吹,或者说他也不知道。ISP程序也读不出。实在不知道是为啥……
顺便说一句,STC15系列的片子只要RAM越界就复位……



顺便提一个老妖的坑爹之处:
ISP定时常数不在表格中写出来……
而是:

/*Define ISP/IAP/EEPROM operation const for IAP_CONTR*///#define         ENABLE_IAP          0x80//if SYSCLK<30MHz//#define         ENABLE_IAP          0x81//if SYSCLK<24MHz#define          ENABLE_IAP          0x82//if SYSCLK<20MHz//#define         ENABLE_IAP          0x83//if SYSCLK<12MHz//#define         ENABLE_IAP          0x84//if SYSCLK<6MHz//#define         ENABLE_IAP          0x85//if SYSCLK<3MHz//#define         ENABLE_IAP          0x86//if SYSCLK<2MHz//#define         ENABLE_IAP          0x87//if SYSCLK<1MHz
晕死……但是我为什么发现似乎0×83这个ISP常数到处可以用,1M的RC设置照样0×83下……
本文转自:http://www.rwzy.co.cc
返回列表