對于一部分坑爹的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://m.raoushi.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 port
typedef 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 UART
void 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 variable
void 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
