我這個程序能夠測量串口的波特率,并且將自身的波特率設置到通訊波特率。
這個在STC89C52/STC15F104(其實主要針對STC15F104,因為它沒有串口)上面通過。不過因為是模擬串口,在11.0592 12T的情況下,最多到19200。
然后在STC15F104上面,要適量的減少補償值……
順便說一句,編譯器建議使用sdcc。keil的SB printf函數搞死我了……在22.1184 6T的情況下大約能到57600.
當然在STC15F系列到115200毫無壓力,可是是半雙工的。要做全雙工只能減倍波特率……
測量波特率的方法很簡單。校準他的波特率的方法就是發送沒有連續低電平的字符,類似0x55/0xff/0x7f之類的。使用這個特性還可以將它用在STC自動冷啟動下載器中。不過注意要限制校準波特率。否則你的正常串口通訊可能會被干擾,因為你比如115200通訊的時候,正好波形類似57600的7F,校準上去了把你的連接掐了,估計很多人都會看看程序是不是跑飛了……所以一定要把同步波特率降到4800以下,因為STC-ISP的默認最低波特率是從1200~4800.
這個也可以適用于不準晶振的單片機和計算機通訊。方法就是計算機以不同波特率發送校準信號,找出誤碼率最低的波特率,然后發送確認,讓單片機在這個重載值下運行,也適用于時鐘速度可能變化的單片機/懶得計算重載值的人使用。
然后低于4800的波特率在11.0592的速度下面只能分頻,這也是不得已的……
上代碼:
/*
* 自適應波特率模擬串口程序,
* BY 萬致遠@rwzy.co.cc
* CRYSTAL:任意
*/
#include <hwconfig.h>
#include <type-def.h>
#include <stdio.h>
#define MIS_0 0
#define MIS_2 1
#define MIS_4 2
#define MIS_8 3
#define MIS_16 4
#define TX1 P1_0 //發送數據端口
#define RX1 P1_1 //接收數據端口
BYTE min_mode;//減倍模式
void WaitTF1()
{
while(!TF1);
TF1=0;
if(min_mode==MIS_2)
{// /2
while(!TF1);
TF1=0;
}
else if(min_mode == MIS_4)
{// /4
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
}
else if(min_mode == MIS_8)
{// /8
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
}
else if(min_mode == MIS_16)
{// /16
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
while(!TF1);
TF1=0;
}
}
void WByte(BYTE out)
{
//發送啟始位
BYTE i=8;
BYTE tmp=out;
TR1=1;//開定時器
TX1=0;
WaitTF1();
//發送8位數據位
while(i--)
{
TX1=(tmp&0x01); //先傳低位
tmp=tmp>>1;
WaitTF1();
}
//發送校驗位(無)
//發送結束位
TX1=1;
WaitTF1();
TR1=0;
}
void putchar(char ch)
{
WByte(ch);
}
BYTE RByte()
{
BYTE in=0;
BYTE cnt;
while(RX1==1);//等待RXD變低,啟動定時器,這個是阻塞模式
TR1=1;//同步開定時器//這里……
//while(!TF1);
//TF1=0;
WaitTF1();
if(min_mode !=0)
{
while(!TF1);//注意這里的周期稍微長。要補償
TF1=0;
}
for(cnt=0;cnt<8;cnt++)
{
in=in >>1;//從高移到低
if(RX1==1) in = in | 0x80;//如果RXD=1,則最高置位
WaitTF1();//等待一位過去
}
while(!TF1);//注意這里的周期稍微長。要補償
TF1=0;
TR1=0;//關閉定時器
return in;
}
UINT f_Test(void)//測試脈寬
{
TMOD=0x10;//設置計數器1為方式一計數器模式
TH1=0;
TL1=0;//定時器CLR
while(!RX1);//等待頻率腳變高,這個是測低電平的
while(RX1);//等待腳變低,更換符號可以測正脈沖
TR1=1;//開啟定時器
while(!RX1);//等待變高
TR1=0;//停止計數
//cyc=TH0<<8;
//cyc=cyc+TL0;
return (TH1<<8)+TL1;
}
void baud_t()
{
BYTE k;//復用變量
ULONG frq=0; //周期變量
for(k=0;k<5;k++)// 變量復用大法
{
frq=frq+f_Test();//測試
}//測量5次取平均
frq=frq/5;
if(frq<0xff)
{
k=0x100-(frq&0xff);
min_mode=MIS_0;
}
else
{
if(frq / 2 < 0xff)
{//2400baud
k=0x100-((frq/2)&0xff); //2分頻
min_mode=MIS_2;
}
else if(frq / 4 < 0xff)
{//1200baud
k=0x100-((frq/4)&0xff);//4分頻
min_mode=MIS_4;
}
else if(frq / 8 < 0xff)
{//1200baud
k=0x100-((frq/8)&0xff);//8分頻
min_mode=MIS_8;
}
else if(frq / 16 < 0xff)
{//1200baud
k=0x100-((frq/16)&0xff);//16分頻
min_mode=MIS_16;
}
}
if(k > 0x50)
{
k=k+6;//加補償,因為if語句讓機器周期加長
//如果對于STC的新MCU,這里要按照情況調整
}
TMOD=0x20;//設置定時器1為自動裝載模式
TH1=k;//載入新波特率
TL1=k;
}
void main()
{
while(1)
{
baud_t();//測量波特率,阻塞模式
printf("Hello world!\n");
printf("Here:mode=%d,T1=0x%X\r\n",min_mode,TH1);//這里如果你要使用keil請自己寫字符串發送函數,和sprintf配合使用
printf("Could you please test another baudrate?\r\n");
printf("But I think that I couldn't to do.....\r\n");
}
}
萬致遠@rwzy.co.cc
求M~~~