Chapter Three
— 從“壁畫(huà)”記事到“甲骨文” —
不得不承認(rèn),到目前為止,似乎
如果我們要想做一個(gè)電話號(hào)碼記事本之類(lèi)的電子助手已經(jīng)萬(wàn)事俱備了,但真正開(kāi)始做的時(shí)候才發(fā)現(xiàn),我們還沒(méi)有教會(huì)AVR如何去寫(xiě)字。如果說(shuō),我們前面已經(jīng)能在
LCD上畫(huà)出“壁畫(huà)”的話,那么要想讓別人看懂你記錄的到底是什么鬼畫(huà)符還需要一點(diǎn)點(diǎn)關(guān)于“甲骨文”的掃盲。事實(shí)上,大家約定俗成的固定大小的圖片集或其
子集就是一個(gè)被尊稱(chēng)為字庫(kù)的神圣典籍。在這個(gè)圣經(jīng)里面記錄的是一種被稱(chēng)之為“字模碼”的東西,對(duì)于我們,這種信息可能相當(dāng)抽象,但是借助LCD,那么字模
碼就是一個(gè)我們能看懂的字符在顯存中存在的模式。
關(guān)于這些字模碼是如何排列的,自古以來(lái)就有數(shù)不清的模式。終于有一天,一群中國(guó)人伴隨著新中國(guó)
站了起來(lái),制定了一個(gè)叫做國(guó)標(biāo)的標(biāo)準(zhǔn)(GB),根據(jù)這個(gè)標(biāo)準(zhǔn),祖國(guó)大地的字模碼才有了統(tǒng)一的目錄,而查詢(xún)這個(gè)目錄的方法已經(jīng)逐漸被人們所淡忘,吹落那源自
電報(bào)碼的書(shū)籍紅色封皮上的滄桑,用手輕輕撫摸封皮上的的文字:“區(qū)位碼”,我們發(fā)現(xiàn),其實(shí)他包含了6000個(gè)漢字的一級(jí)字庫(kù)其他一些由非常用字組成的多級(jí)
字庫(kù)。
在西方,埃尼阿克的故鄉(xiāng),一群依靠技術(shù)侵略世界的瘋子根據(jù)自己半通不同的習(xí)慣制定了一個(gè)由128個(gè)字符組成的交換標(biāo)準(zhǔn)稱(chēng)之為ASCII碼,由于技術(shù)大潮的沖擊,世界妥協(xié)了。
……
這一切的一切都無(wú)法改變字庫(kù)只不過(guò)是圖片集和的本質(zhì)。所以,敢于抵抗強(qiáng)權(quán)的人們?cè)谧约旱念I(lǐng)土上堅(jiān)持著自己的信念——我們稱(chēng)之為“小字庫(kù)技術(shù)”。甚至有些人堅(jiān)持使用圖片記事,那么自然的被視作是“無(wú)字庫(kù)技術(shù)”。
世界在前進(jìn),即便后來(lái)世界技術(shù)的格局發(fā)生了怎樣的變化,即便一些曾經(jīng)約定的不合理的東西也會(huì)作為最底層的協(xié)議支持者新世界,就像是烏龜駝著的世界。任何觸動(dòng)這些底層的行為都會(huì)受到世界的背叛,所以,拋棄情感上的東西,我們來(lái)研究一下ACSII的構(gòu)成原理和實(shí)現(xiàn)方法。
對(duì)不起大家,我寫(xiě)這些東西的目的就是面向初學(xué)者。事實(shí)上,如各位所說(shuō),我并非高手,所以很多地方漏出了類(lèi)似“內(nèi)存
映射”之類(lèi)的馬腳。這里我只想做一點(diǎn)解釋?zhuān)挥形遗说臇|西,我才能用通俗的方法和大家解釋?zhuān)乙桓[不通的東西就只好原樣照搬打腫臉充胖子了。呵呵。還
請(qǐng)?jiān)?。事?shí)上,就拿“內(nèi)存映射”這個(gè)問(wèn)題來(lái)說(shuō),我使用的大段大段的文字來(lái)解釋這個(gè)概念,因?yàn)榧幢闶橇_嗦,我也最多只能用“屏幕的一塊區(qū)域?qū)?yīng)內(nèi)存的一塊
區(qū)域”這樣仍然抽象的話語(yǔ)來(lái)解釋?zhuān)炊@得我騙稿費(fèi)一般,所以不如先提出一個(gè)名詞,把解釋溶化在后面的文字中。
本來(lái),開(kāi)篇就說(shuō)得很清楚,我寫(xiě)這
些東西的目的不是等大家來(lái)喊牛,姑妄言之,姑妄聽(tīng)之,水平有限,沒(méi)有刻意去追求什么文本格式上的東西,自然可能不對(duì)大家胃口,我以后注意就是了。但是,說(shuō)
回來(lái),寫(xiě)這些東西的心情和大家寫(xiě)伯克的時(shí)候差不多,多半是吐吐心中不吐不快的東西罷了,所以,由著性子,演繹也罷,說(shuō)明書(shū)也罷,文檔整理稿也罷,那要看那
一陣子我正在看什么書(shū)了,如果哪天我不幸開(kāi)始看小說(shuō),來(lái)一個(gè)欲知后事請(qǐng)聽(tīng)下回分解也說(shuō)不定。
我的專(zhuān)業(yè)本來(lái)就是軟件工程,所以寫(xiě)出這些文字,非常自然。
--------------------------------
以上,就是本人借著大家的機(jī)會(huì),公然在技術(shù)論壇上灌的水。大家五味自知哈。
最近有點(diǎn)忙,第三章可能會(huì)推后幾天……請(qǐng)?jiān)?/font>
對(duì)不起大家,最近剛剛忙完“挑戰(zhàn)杯”創(chuàng)業(yè)大賽的商業(yè)計(jì)劃書(shū)、學(xué)校的一套試驗(yàn)系統(tǒng)剛剛設(shè)計(jì)交付使用、呵呵……對(duì)不住大家。我就盡力寫(xiě)一點(diǎn),可能不會(huì)向從前那樣一次寫(xiě)很多了,時(shí)間可能長(zhǎng)一點(diǎn),但是質(zhì)量絕對(duì)不減。??
3.1 ASCII字符集
ASCII(American Standard Code for Information
InterChange)——美國(guó)通用信息交換編碼。他是現(xiàn)在流行的眾多編碼的榜樣,雖然使用僅僅7位二進(jìn)制表示(通常用一個(gè)字節(jié)表示),但是卻是眾多編
碼系統(tǒng)的基礎(chǔ),比方說(shuō)16位二進(jìn)制為組成的Unicode編碼,證據(jù)就是,只要在ASCII碼前面加9個(gè)零就成完成了轉(zhuǎn)換。當(dāng)然,仍然有不聽(tīng)話的,比方說(shuō)
IBM老大的EBCDIC碼(大型計(jì)算機(jī)系統(tǒng)上用的)。
大家都注意到了7位二進(jìn)制表示的編碼顯然只能有128個(gè)字符的容量,那么,用一個(gè)字節(jié)
256個(gè)字符的容量豈不是造成了浪費(fèi)?于是,現(xiàn)在PC機(jī)上普遍通用的IBM擴(kuò)展ASCII碼從128~255開(kāi)始擴(kuò)展了128個(gè)字符——注意,這128個(gè)
字符并不是通用的,即便在我們能接觸的大部分場(chǎng)合他們都有效,但是記住他們的“非常任理事國(guó)”的身份是擁有重大意義的。
比方說(shuō),我們的顯示系統(tǒng)只
需要顯示E文字母和數(shù)字還有一些標(biāo)點(diǎn)符號(hào),那么,干什么要這些無(wú)用的字符充數(shù)呢?要知道,一個(gè)字母存儲(chǔ)起來(lái)需要至少8*7的點(diǎn)陣(7個(gè)字節(jié))?。∈聦?shí)上,
由于幾乎所有通用單片機(jī)內(nèi)部都不帶有ASCII字庫(kù)(字模碼),所以,我們必須把他們存儲(chǔ)起來(lái),并且還不能打破原有的存儲(chǔ)模式,不然通過(guò)ASCII碼作為
索引我們就找不到他們了。為了完成對(duì)字模庫(kù)的簡(jiǎn)化,我們需要知道他們的構(gòu)成方式,然后再考慮如何去獲取一個(gè)已知的標(biāo)準(zhǔn)字模庫(kù),并按照我們?cè)O(shè)計(jì)的方法去簡(jiǎn)化
他……可憐的AVR,存儲(chǔ)器又要吃緊了。
從古老的教科書(shū)上,很容易獲得一張ASCII編碼表。因?yàn)榇蠹叶际歉鉋mbedded System開(kāi)發(fā)的(為了顯示大家工作的高深程度,請(qǐng)?jiān)试S我掉書(shū)袋),所以,這里我更多的要講述一下ASCII編碼一些不太被人注意的特性,一些只有從二進(jìn)制角度才容易看出的特性。
1)ASCII碼由7位二進(jìn)制組成;
[6][5][4][3][2][1][0]
2)[6:5]為用來(lái)表示ASCII編碼的組分類(lèi)
控制字符組(顯示不需要顯示的東西)
數(shù)字字符和標(biāo)點(diǎn)符號(hào)組
大寫(xiě)字符和特殊字符
小寫(xiě)字符和特殊字符
3)只要把大寫(xiě)字母的第6位也就是[5]置位就是現(xiàn)了到小寫(xiě)字母的轉(zhuǎn)換,反之亦然;
4)數(shù)字的ASCII碼[3:0]位的值與它要表示的數(shù)值相同;
例如:
“0”? ?? ?? ? 0x30
“1”? ?? ?? ? 0x31
依照上面的編碼規(guī)則,ASCII碼的字模碼文件存儲(chǔ)的方式為:
char c;
……
fAddress = c * 8 * 16;//超級(jí)簡(jiǎn)單哈,這是計(jì)算機(jī)系統(tǒng)上標(biāo)準(zhǔn)8*16的ASCII字符集
fAddress = c * 8 * 8; //這是計(jì)算機(jī)系統(tǒng)使用的8*8小字符集
聰明的大家已經(jīng)知道如何在存儲(chǔ)器中獲得字模碼了吧?
就是訪問(wèn)存儲(chǔ)的“基地址+fAddress”就可以了。
為了便于后面大家實(shí)現(xiàn)漢字顯示時(shí)候的代碼移植(回避全角和半角問(wèn)題),我們使用8*16的大字符集,至于你想使用小字符集,那么就看你的愛(ài)好了。
3.2 How to get them?
這里我們來(lái)順手說(shuō)說(shuō)字模碼獲得的問(wèn)題。
不可否認(rèn),現(xiàn)在網(wǎng)上很多兄弟寫(xiě)的字
模制作軟件水平之高,已經(jīng)到了令人嘆為觀止的地步,只可惜當(dāng)時(shí)我學(xué)習(xí)字庫(kù)問(wèn)題的時(shí)候,尚且沒(méi)有解決溫飽,更不用說(shuō)上網(wǎng)了。而且,這些字模軟件無(wú)不在客觀上
支持了字模的“無(wú)政府主義”,小字庫(kù)和無(wú)字庫(kù)技術(shù)在一些不恰當(dāng)?shù)膱?chǎng)合也被大量濫用,嚴(yán)重影響了接著寫(xiě)你代碼同志的心情……鬼知道原來(lái)跳槽的家伙如何定義那
該死的字庫(kù)的,所以,提倡在何時(shí)的場(chǎng)合使用標(biāo)準(zhǔn)的字模庫(kù)還是非常有必要的。
首先說(shuō)說(shuō)一種簡(jiǎn)單的獲取字模的方法。
不知道還有多少人記得UCDOS,中國(guó)漢字操作系統(tǒng)的“希望”。金山WPS,CCED……TX.com,這些東西讓人難忘啊。我們的字模庫(kù)很容易從這樣具有我國(guó)獨(dú)立知識(shí)產(chǎn)權(quán)的系統(tǒng)中獲得。所有的東西都放在
UCDOS\
目錄下面。我們選取這次需要的文件“ASC16”。順手說(shuō)下,漢字庫(kù)也可以從里面獲得“HZK16”。其他的字模庫(kù)在
UCDOS\FNT
目錄下面。
還有一種BT的方法可以獲得8*8的字庫(kù)。大家記得BIOS吧……呵呵,利用TC寫(xiě)一個(gè)中斷程序,直接讀出來(lái)……哈哈。后面有機(jī)會(huì)我會(huì)附上代碼,如果我能記得地址的話。
------------------------------------------------------
(本章待續(xù))接下來(lái),我將來(lái)聊聊對(duì)這些字庫(kù)減肥。
Gorgon Meducer 來(lái)點(diǎn)實(shí)際的吧。
我們這些學(xué)生都等不急了。在這里讀帖子的電子愛(ài)好者都不是閑著沒(méi)事逛逛的。
我來(lái)加點(diǎn)東西這是鉆石生成圖:有大液晶的朋友可以看看。
//****************************************************************************
//測(cè)試橢圓的圖形驅(qū)動(dòng)函數(shù)
//***************************************************************************
//顯示漸漸形成的鉆石
void test_elli(void)
{
Uchar i,j;
for(j=0;j<0x5;j++)
{
ClrScr();
for(i=0;i<100;i=i+2)
{
ellispeMidpoint(220,120,100,i);
//ellispeMidpoint(220,120,i,100);
delay_nms(500);
}
}
}//******************************************************************************
//合并四分橢圓點(diǎn)
//入口參數(shù):中心點(diǎn)xc,yc和長(zhǎng)短軸 rx,ry??
//*****************************************************************************
void ellipsePlotPoints(int xc,int yc,int x,int y)
{
setPixel(xc+x,yc+y);
setPixel(xc-x,yc+y);
setPixel(xc+x,yc-y);
setPixel(xc-x,yc-y);
}
//******************************************************************************
//中點(diǎn)橢圓算法
//入口參數(shù):中心點(diǎn)xc,yc和長(zhǎng)短軸 rx,ry??
//*****************************************************************************
void ellispeMidpoint(int xc,int yc,int rx,int ry)
{
int rx2=rx*rx;
int ry2=ry*ry;
int tworx2=2*rx2;
int twory2=2*ry2;
int p;
int x=0;
int y=ry;
int px=0;
int py=tworx2*y;
void ellipsePlotPoints(int,int,int,int);
//plot the first set of points
ellipsePlotPoints(xc,yc,x,y);
//region1
p=round(ry2-(rx2*ry)+(0.25*rx2));
while(px<py)
{
x++;
px+=twory2;
if(p<0)
p+=ry2+px;
else
{
y--;
py-=tworx2;
p+=ry2+px-py;
}
ellipsePlotPoints(xc,yc,x,y);
}
//region2
p=round(ry2*(x+0.5)*(x+0.5)+rx2*(y-1)*(y-1)-rx2*ry2);
while(y>0)
{
y--;
py-=tworx2;
if(p>0)
p+=rx2-py;
else
{
x++;
px+=twory2;
p+=rx2-py+px;
}
ellipsePlotPoints(xc,yc,x,y);
}
}
__________________________
總是在東風(fēng)無(wú)力的時(shí)候...
16M晶振跑Gorgon Meducer 先生的窗口(windows)生成算法都顯太慢。填充->擦除->畫(huà)框在液晶上過(guò)程的足跡太明顯。
因?yàn)闆](méi)有實(shí)際的顯示緩沖區(qū),沒(méi)有辦法的事情啊,如果你使用SRAM大的AVR芯片,那么應(yīng)該可以避免畫(huà)圖過(guò)程明顯的弊端的。
我用Mega8L填充整個(gè)換面,沒(méi)有使用優(yōu)化算法,直接用點(diǎn)畫(huà)完只需要500ms左右,內(nèi)部8M,所以你說(shuō)16M還是慢,我不是很理解。呵呵,沒(méi)有別的意思,全當(dāng)交流。
還有,沒(méi)有任何人是學(xué)生。你只當(dāng)我在寫(xiě)blog好么?我沒(méi)有義務(wù)給大家做任何的免費(fèi)資料即便是垃圾——我只是在寫(xiě)B(tài)log一樣的東西。
如果我寫(xiě)得東西能起到拋磚引玉的作用,那是最好的。
chenbin0011能給大家談?wù)勀愕南敕??不然真的沒(méi)有交流可言了。
我用的320*240點(diǎn)陣液晶。mega128
那個(gè)窗口函數(shù)很通用,但重復(fù)擦寫(xiě)占去了很多時(shí)間。??
這樣會(huì)好點(diǎn)吧:
Box(X-4,Y-4,X+Width-4,Y,2,1);
Box(X-4,Y-4,X,Y+Height-4,2,1);
Box(X,Y,Width+X,Height+Y,1,1);
另外:做圖時(shí)只用了一個(gè)底層借口SetPix();這和RAM大小有什么聯(lián)系呢。Gorgon Meducer有什么好的處理方法呢?
SetPix函數(shù)只有反顯時(shí)需要知道像素的當(dāng)前狀態(tài),而擦初與顯示都不需理會(huì)。
基于這種考慮來(lái)實(shí)現(xiàn)圖形界面:針對(duì)擦除區(qū)域?yàn)?的整數(shù)倍的區(qū)域(字符一般為8的整數(shù)倍,這種情況也很多見(jiàn))重新編寫(xiě)函數(shù),速度又可提升x倍(取決于總線寬度,和讀取速度)。
擦除的時(shí)候也許要知道當(dāng)前狀態(tài)阿,不然會(huì)影響到同一個(gè)字節(jié)內(nèi)的其他點(diǎn)的。
您說(shuō):
“
這樣會(huì)好點(diǎn)吧:
Box(X-4,Y-4,X+Width-4,Y,2,1);
Box(X-4,Y-4,X,Y+Height-4,2,1);
Box(X,Y,Width+X,Height+Y,1,1);
”
我考慮到還要覆蓋下面有圖片的情況,不只是下面是空白的情況。
對(duì)非8的整數(shù)倍變換需要取出一個(gè)字節(jié),對(duì)相應(yīng)位變換后再寫(xiě)入,如果水平位置大于一個(gè)像素可同時(shí)變換多位。對(duì)垂直方向只要設(shè)光標(biāo)移動(dòng)方向向下,也可同樣處理。不用每次只變換一個(gè)Pix.
呵呵,通用和效率很難兼顧呀。
Gorgon Meducer :再請(qǐng)教一下啊,31樓的不畫(huà)邊框怎么理解呢?和什么都不執(zhí)行有什么區(qū)別呢,能給出函數(shù)嗎?
to chenbin0011
你說(shuō):“
對(duì)非8的整數(shù)倍變換需要取出一個(gè)字節(jié),對(duì)相應(yīng)位變換后再寫(xiě)入,如果水平位置大于一個(gè)像素可同時(shí)變換多位。對(duì)垂直方向只要設(shè)光標(biāo)移動(dòng)方向向下,也可同樣處理。不用每次只變換一個(gè)Pix.”
這種情況我在前文討論過(guò)了。你注意看一下。
“
還記得黑白點(diǎn)陣屏幕的顯存映射方式么?它簡(jiǎn)單的使用1個(gè)字節(jié)表示8個(gè)坐標(biāo)點(diǎn),同時(shí)這1
個(gè)字節(jié)是沿著屏幕的短邊方向映射的,所以當(dāng)我們想畫(huà)一條垂直的直線時(shí),對(duì)于每一個(gè)牽涉到的字節(jié)都有可能要重復(fù)的操作8次之多,這種操作不是簡(jiǎn)單的畫(huà)線,而
是要先讀取再計(jì)算最后再寫(xiě)這樣的復(fù)合操作,重復(fù)8次只是為了把整個(gè)字節(jié)變黑顯然是一種超級(jí)不可容忍的冗余——大家都知道,直接把這個(gè)字節(jié)讀取一次,計(jì)算一
次,再寫(xiě)一次就完成了?;谶@種思想,我們需要把這種特殊情況單獨(dú)提取出來(lái),重新優(yōu)化代碼……具體優(yōu)化代碼就留給大家做作業(yè)了哈。 ”
關(guān)于無(wú)邊框,是需要的。一下的就是部分偽碼。前面也是提供過(guò)的……chenbin0011可能太性急,沒(méi)有注意哈。
if (BoxModel == BoxModel_NoBox)
{
if (FillType == FillType_NoFill)
{
//運(yùn)行到這里說(shuō)明我們被程序員耍了……什么不做到這里干什么???
return ;
}
BoxModel = FillType;
}
Line(Xbegin,Ybegin,Xend,Ybegin,BoxModel);
Line(Xbegin,Ybegin,Xbegin,Yend,BoxModel);
Line(Xbegin,Yend,Xend,Yend,BoxModel);
Line(Xend,Ybegin,Xend,Yend,BoxModel);
3.3 字庫(kù)減肥
查看一下Mega8的Datasheet,上面赫然寫(xiě)著8K Flash,再看看ASC16的大小4K,我不知道有多少人不會(huì)打寒顫。更不用說(shuō)260多K的漢字庫(kù)了,看來(lái)要么外擴(kuò)存儲(chǔ)器,要么只能選擇西文顯示,并且對(duì)ASC16庫(kù)進(jìn)行減肥。
其
實(shí),減肥并不是一個(gè)可以稱(chēng)之為技術(shù)的行為,總原則就是丟棄無(wú)用的部分,同時(shí)修改索引方式以保證外界使用減肥前方文字魔窟文件的索引方式不至于出錯(cuò)就可以
了。就拿ASC16的減肥工作來(lái)說(shuō)吧,很顯然,我們并不需要擴(kuò)展字符128~255的那個(gè)部分,可以減小大小為16*128 =
2K的大小,再加上基本字符基的四個(gè)區(qū)中,控制字符區(qū)顯然也是可以舍棄的,所以,還可以減少16*32 =
512B的大小,也就是說(shuō),剩下的文件只有1.5K大小了,哈哈,總算可以接受了。當(dāng)然如果你選擇了8*8的字模,就更小了。
以上完成的只是第一步,借助類(lèi)似UltraEdit這樣的編輯工具都可以做到,然后我們?cè)侔炎帜?kù)寫(xiě)成數(shù)組的形式,比方說(shuō):
const char ASC_Lib[][16] = {
……
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
……
}
包含入頭文件,就可以了。
下面我們需要一個(gè)訪問(wèn)函數(shù),用來(lái)安全的有效的訪問(wèn)到我們需要的字模庫(kù):
char *getASCLIB(char ASC)
{
if ((ASC <32 ) || (ASC > 127))
{
return ASC_Lib[CharStringNULL]; //需要一個(gè)空字符串作為安全返回
}
return ASC_Lib[ASC];
}
配合12864頭文件中公版中都有的那個(gè)顯示圖片的函數(shù),就可以顯示字符了。例如:
void DispBITMap(const char *String,char StringLength,char X,char Y,char DispModel);
我們還可以再對(duì)這個(gè)函數(shù)包裝一下,實(shí)現(xiàn)16 * 4的文本模式。使用CLocate(x,y)來(lái)定位字符,用Print("")來(lái)顯示字符串,用PrintN()來(lái)顯示數(shù)字……哈哈。這一部分就留給大家自己來(lái)做了,不過(guò)是幾個(gè)函數(shù)加宏定義罷了,相信自己簡(jiǎn)單的。
至于如何實(shí)現(xiàn)任意位置顯示字符,還有一些字符特效的實(shí)現(xiàn),在下一節(jié)里面具體說(shuō)明。
這是我寫(xiě)的函數(shù)
/********************************************************
* 函數(shù)說(shuō)明:讀取顯示數(shù)據(jù)指令 *
* 輸出: 顯示數(shù)據(jù) *
********************************************************/
char getLCD12864Data(void)
{
char TempData = 0;
char a = 0;
LCD12864_SetModel_Data;
LCD12864_SetModel_Read;
LCD12864_SetEnable;
LCD12864_SetDisable;
LCD12864_SetEnable;
asm("nop");
TempData = ReadDataPORT;
return TempData;
}