久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 5245|回復: 7
打印 上一主題 下一主題
收起左側

帶字庫(GT20L16S1Y)LCD12864取字錯誤偏移問題的思考

  [復制鏈接]
跳轉到指定樓層
樓主
      最近買了一個LCD12864的屏幕,驅動采用的是UC1701,SPI接口,字庫是高通的GT20L16S1Y。這里吐槽一下我拿到的官方字庫手冊,居然沒有指明各種樣式字體的起始地址。不過在廠家給的例程里倒是能估計出來。在使用字庫例程修改加上我自己的SPI驅動以后,便可以簡單的顯示漢字了。一開始以為就這樣就好了,然后我打算將之前寫的一個電子鐘模塊移植過來時,卻發現年,月,日這樣的字符都無法正常顯示:年是亂碼,月的字符偏移到了字庫里面“(六)”’該字符的位置,日的字符則是直接顯示“查”字。我一開始覺得是驅動有問題,但是仔細想想又說不通,畢竟ascii字符也是可以讀取出來的。使用邏輯分析儀查看除了返回字模錯亂,其他也是一切正常。如果驅動有問題,也沒可能那么準確的返回另一個字的字模。然后我就考慮是不是其他地方在copy的時候出現了錯誤,類型不對溢出等。因為之前吃過這個虧,移植了一個stm32的程序到51上,結果MDK開發包與c51開發包的unsigned int類型有差異,導致了程序無緣無故的錯誤。

      萬萬沒想到這次又吃了這個虧。
      因為尋址算法是官方例程提供的,我也沒有多考慮,debug的時候直接排除了這部分。并且在網上查找對比發現這套GB2312的計算方法確實是可行的。我把精力一直放在字庫偏移的問題上,進行測試發現,GB2312庫使用我的程序,大約在“繭”這個字左右開始,之后的所有字,要么是亂碼的無法顯示,要么是直接偏移到了前面的區位段去。雖然出錯,但是讀出的又是完整的字。很明顯發生了類似的溢出行為,但我百思不得其解,我代碼段里變量都已經修改為unsigned long了,怎么還能溢出呢?該段代碼如下:

/***************************************************
16 點GB2312 標準點陣字庫
參數說明:
GBCode表示漢字內碼。
MSB 表示漢字內碼GBCode 的高8bits。
LSB 表示漢字內碼GBCode 的低8bits。
Address 表示漢字或ASCII字符點陣在芯片中的字節地址。
BaseAdd:說明點陣數據在字庫芯片中的起始地址。
r_dat_bat 是讀點陣數據函數。
DZ_Data是保存讀出的點陣數據的數組。

*****************************************************/
void gt_16_GetData (u8 MSB,u8 LSB)
{
u32 BaseAdd=0,Address;
if(MSB == 0xA9 && LSB >=0xA1)
   Address = (282 + (LSB - 0xA1))*32+BaseAdd;
else if(MSB >=0xA1 && MSB <= 0xA3 && LSB >=0xA1)
       Address =( (MSB - 0xA1) * 94 + (LSB - 0xA1))*32+ BaseAdd;
else if(MSB >=0xB0 && MSB <= 0xF7 && LSB >=0xA1)
        Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+ 846)*32+ BaseAdd;
r_dat_bat(Address,32,DZ_Data);
}


      在某一次搜索中,發現了中景園的字庫例程,我簡單的對比了一下,基本上是相同的,但是在燒寫程序以后,中景園給的例程完全正常,除了臭名昭著的0xfd bug(具體可以在論壇或者網絡上搜索),顯示完全沒問題。看了一眼程序注釋,恍然大悟。代碼如下(篇幅問題只放一部分作對比,具體可站內搜索):if(((text>=0xb0) &&(text<=0xf7))&&(text[i+1]>=0xa1))
                {                                                
                        /*國標簡體(GB2312)漢字在晶聯訊字庫IC中的地址由以下公式來計算:*/
                        /*Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+ 846)*32+ BaseAdd;BaseAdd=0*/
                        /*由于擔心8位單片機有乘法溢出問題,所以分三部取地址*/
                        fontaddr = (text- 0xb0)*94;
                        fontaddr += (text[i+1]-0xa1)+846;
                        fontaddr = (ulong)(fontaddr*32);
                        
                        addrHigh = (fontaddr&0xff0000)>>16;  /*地址的高8位,共24位*/
                        addrMid = (fontaddr&0xff00)>>8;      /*地址的中8位,共24位*/
                        addrLow = fontaddr&0xff;             /*地址的低8位,共24位*/
                        get_n_bytes_data_from_ROM(addrHigh,addrMid,addrLow,fontbuf,32 );/*取32個字節的數據,存到"fontbuf[32]"*/
                        display_graphic_16x16(y,x,fontbuf);/*顯示漢字到LCD上,y為頁地址,x為列地址,fontbuf[]為數據*/
                        i+=2;
                        x+=16;
                }
      這里提了一個關鍵詞 乘法溢出。之前在C語言,C++里面,相信大家都學習過不少相關的知識。例如整型提升,強制類型轉換等。uchar溢出的問題在剛寫程序的時候也經常會遇到,常常是定時器相關。這里引用一名為的“keil大常量計算問題”的博客來做解釋,我自己講的可能不太準確。
該博客提到:
      keil C51是與ANSI C兼容的編譯器,ANSI C規范規定十進制整數常量的默認數據類型是int、long int和unsigned long int的其中一種,對給定的常量是其中的哪一種要看這個常量的實際大小,如果常數在-32768~32767之間則按int類型處理,如果按int類型處理會溢出就考慮long int或更大的數據類型unsigned long int。總之,編譯器總是按盡可能的原則指定常量的類型。但這一原則并不總能奏效,當兩個常量做運算時就可能導致溢出。如:
    #define SYSCLK  22118400// SYSCLK in Hz (22.1184 MHz external crystal oscillator)
    #define SLIDER_REST_TIME  100// in ms,slider rest time
    #define REST_DELAY    SYSCLK * SLIDER_REST_TIME / (65536 * 1000)
    unsigned char i;
    i = REST_DELAY;
       在keil c51中運行i為0xE1,即225,并不是期望的結果22118400 * 100 / (65536 * 1000) = 33.75,取整為33。原因分析如下:
宏替換后為:i = 22118400 * 100 / (65536 * 1000);,編譯器首先為22118400定義類型,因為22118400不在int的表示范圍內,而在long int的范圍-2147483648~2147483647內,所以22118400按long int類型處理,在做乘積運算時100被自動按long int處理,22118400 * 100將按兩帶符號長整型常量進行運算,運算結果仍為帶符號長整型,結果寫成十六進制是0x83D60000,其十進制是-2083127296,顯然出現了溢出錯誤。keil編譯器并沒有給出任何錯誤或警告提示信息(VC++6.0還給出警告warning C4307: * : integral constant overflow),繼續進行下一個運算65536 * 1000,結果為帶符號長整型,十六進制為0x3E80000,十進制為65536000,最后按兩長整型除法計算-2083127296 / 65536000,結果為0xFFFFFFE1,由于i為字符類型,取0xFFFFFFE1的最低有效字節為0xE1賦值給i,i的最終值為0xE1。
      解決這種溢出錯誤的方法用C語言的一個術語就是“提升”(promotion),拿上例來說就是將22118400指定為無符號長整型,即:
         # define SYSCLK 22118400UL
       注:雖然只要將22118400、100、65536和1000四個常數中的一個指定為無符號長整型即可得到正確的結果,但考慮到可讀性及規范性,應選擇大整數指定其類型。-----------------------------
       通過以上說明我們可以看見,這一切起因是常量造成的整型提升。以我的字庫程序為例就是94,被默認成為了int,而繭對應的機內碼為BCEB,MSB為BC,LSB為EB,經過計算可以得到,該式子為(1128+74+846)*32,合并一下,即為2048*32,這個數字大家一定很熟悉吧,2的十五次,對應int類型,恰好發生溢出。因此,繭字以后發生溢出便得到了解釋。本身LSB和MSB屬于無符號char,但常數94等默認為有符號int,在一起計算的過程中uchar被提升,整體結果以int的形式呈現,而這時中間結果發生了溢出,無法在正常通過賦值提升為ulong類型提供正確地址。因此,字庫顯示范圍永遠被現在那個大小。再看中景園的程序,實際上是提前進行ulong轉化的步驟,提前存儲中間結果,防止其在進行乘法時溢出。同時也可以想到常量后綴的方法,將94等修改為94L,或者使用(unsigned long)進行強制轉化,則程序也可以正常運行。


      因此,大家以后在做這種容易涉及溢出的問題的時候,一定要多留個心眼,例如及時將常量加上后綴,以防止意料之外的溢出問題產生。我這個源程序其實是沒問題的,MDK環境下能正常運行,因為int的位數問題,導致了stm32使用該程序時,字庫無論怎么取也不會出現這個溢出。因此大家在移植程序時也要在這方面多加考慮。

      總體看其實是一個相當愚蠢的問題,卻困擾了我好多天。。一直沒想到乘法溢出的問題,C語言功底還是不夠扎實。總之吃一塹長一智,希望大家能吸取我的經驗教訓吧,也希望能幫到使用了該例程而感到困惑的朋友。

評分

參與人數 2黑幣 +110 收起 理由
gjwhs + 10 很給力!
admin + 100 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏4 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:686782 發表于 2020-7-8 23:27 | 只看該作者
網上常說的keil漢字編碼方式需要修改成GB2312我個人沒什么感覺,我使用的是ANSI C,也是可以正常編碼的,使用UTF-8還可以直接看到漢字的機內碼。總而言之,漢字在字符串里顯示正確一般是沒有什么問題的。
至于0xfd bug,有些版本的補丁貌似在keil 4,keil 5無法奏效了,顯示查找不到,我這里順便也上傳一下我驗證了可以正常使用的版本,我是keil5。

51黑論壇_KeilFDfix_b7(冠銘師兄版).rar

96.79 KB, 下載次數: 32, 下載積分: 黑幣 -5

KeilFDfix_b7(冠銘師兄版)

評分

參與人數 1黑幣 +20 收起 理由
admin + 20 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

板凳
ID:373 發表于 2022-12-27 16:35 | 只看該作者
學習一下,正使用這個芯片
回復

使用道具 舉報

地板
ID:996773 發表于 2023-1-3 14:51 | 只看該作者
本人才疏學淺,只是直接利用字符表的地址串行輸入12864顯示漢字很正常,沒有樓主的不能使用的煩惱
回復

使用道具 舉報

5#
ID:994947 發表于 2023-3-23 17:07 | 只看該作者
請問怎么獲取該芯片字庫的lcd12864屏幕
回復

使用道具 舉報

6#
ID:463392 發表于 2023-3-24 15:11 | 只看該作者
1123213124 發表于 2023-3-23 17:07
請問怎么獲取該芯片字庫的lcd12864屏幕

http://m.zg4o1577.cn/bbs/dpj-216347-1.html
回復

使用道具 舉報

7#
ID:1068432 發表于 2023-3-29 13:52 | 只看該作者
謝謝大佬點醒夢中人(我=====小白)
回復

使用道具 舉報

8#
ID:1044755 發表于 2024-10-31 12:52 | 只看該作者
感謝,遇到同樣的問題
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 久久久久久国产精品 | 精品久久99 | 免费高清成人 | 免费观看黄a一级视频 | 国产精品一卡二卡三卡 | a在线视频观看 | 欧美午夜精品 | 国产在线播 | 亚洲一区在线播放 | 欧美色999 | 在线视频 亚洲 | 在线不卡一区 | 91精品国产麻豆 | 搞黄视频免费看 | 久久高清免费视频 | 久久久久国产一区二区三区不卡 | 午夜激情国产 | 日韩1区 | 九九久久久 | 国产精品视频久久 | 成年视频在线观看 | 颜色网站在线观看 | 国产在线看片 | 国产精品永久在线观看 | 精品久久久久久久久久久久 | 亚洲欧美激情精品一区二区 | 亚洲在线一区二区 | 在线亚洲欧美 | 国产精品美女 | 中文字幕精品视频在线观看 | 国产一区二区在线免费观看 | 久久一热 | 亚洲 中文 欧美 | 日本视频中文字幕 | www.亚洲成人网 | 日韩一及片| 久久福利电影 | 久草青青草 | 综合久久久久 | 在线看日韩 | 天天噜天天干 |