動(dòng)芯片詳解:從I2C配置到STM32驅(qū)動(dòng)實(shí)現(xiàn))
1. BL55072A是什么一顆段碼LCD驅(qū)動(dòng)芯片的核心定位做嵌入式這些年凡是接觸過(guò)家電控制板、儀器儀表、溫控器、血壓計(jì)這類產(chǎn)品的朋友大概率都會(huì)遇到同一個(gè)需求要驅(qū)動(dòng)一塊段碼LCD液晶屏顯示數(shù)字、字母、單位符號(hào)、電池電量、溫度小數(shù)點(diǎn)之類的信息。很多入門(mén)工程師第一反應(yīng)是直接用MCU的GPIO去掃但掃過(guò)幾次就會(huì)發(fā)現(xiàn)段碼屏的公共端和段位組合一旦多了不僅占IO、占CPU掃描頻率稍不穩(wěn)還容易閃爍甚至出現(xiàn)鬼影。這時(shí)候?qū)S玫腖CD驅(qū)動(dòng)芯片就是最靠譜的解法。BL55072A正是這類芯片里非常典型、也很有代表性的一顆。BL55072A是上海貝嶺推出的一顆段碼LCD驅(qū)動(dòng)專用IC最大支持4個(gè)公共端COM和72個(gè)段輸出SEG也就是最大能驅(qū)動(dòng)4×72288個(gè)顯示段。對(duì)于常見(jiàn)的8位數(shù)字加符號(hào)、單位、進(jìn)度條這類顯示需求完全夠用。它內(nèi)部集成了顯示RAM、偏壓發(fā)生器、振蕩電路和串行接口MCU只需要通過(guò)I2C接口就能完成全部顯示控制幾乎不占用主控資源。對(duì)比傳統(tǒng)的HT1621這類并口或三線串口的驅(qū)動(dòng)芯片BL55072A的優(yōu)勢(shì)在于接口標(biāo)準(zhǔn)、功耗低、封裝小而且顯示幀率穩(wěn)定不需要外部電阻做偏壓調(diào)整。適合讀這篇文章的人我理解為兩類一類是剛接觸LCD驅(qū)動(dòng)、手里有一顆BL55072A但不知道從哪里下手的工程師另一類是用過(guò)HT1621、TM1621B想平移到BL55072A上的朋友。無(wú)論哪類看完這篇文章你都能自己寫(xiě)出一個(gè)可用的驅(qū)動(dòng)并且能避開(kāi)我在實(shí)際調(diào)試中踩過(guò)的坑。我需要提前說(shuō)明的是下面所有代碼和寄存器配置都基于BL55072A的常見(jiàn)版本兼容BL55072A/BL55072B具體以你手里的數(shù)據(jù)手冊(cè)為準(zhǔn)。但底層邏輯是一樣的理解了思想換芯片也只是改幾個(gè)宏定義的事。2. 驅(qū)動(dòng)開(kāi)發(fā)前的準(zhǔn)備接口協(xié)議與硬件電路2.1 I2C通信協(xié)議先搞懂芯片的“脾氣”BL55072A通信接口是標(biāo)準(zhǔn)的I2C從機(jī)接口支持100kHz標(biāo)準(zhǔn)模式和400kHz快速模式。I2C大家都熟但這里有幾個(gè)細(xì)節(jié)值得注意因?yàn)樗鼈兪呛罄m(xù)驅(qū)動(dòng)能否一次跑通的關(guān)鍵。首先是設(shè)備地址。BL55072A的設(shè)備地址是7位的常見(jiàn)默認(rèn)值是0x707位地址加上讀寫(xiě)位后寫(xiě)地址是0xE0讀地址是0xE1。但不同批次、不同封裝可能有差異有的芯片地址可以通過(guò)引腳配置所以拿到芯片第一件事是看數(shù)據(jù)手冊(cè)里的地址表。我遇到過(guò)朋友拿著0x70地址去寫(xiě)結(jié)果ACK都沒(méi)有最后發(fā)現(xiàn)芯片實(shí)際地址是0x6E這屬于硬件版本差異不看手冊(cè)真容易翻車。其次是字節(jié)序和傳輸格式。BL55072A的傳輸命令和顯示數(shù)據(jù)是分不同指令的數(shù)據(jù)格式一般是起始位 設(shè)備地址 寫(xiě)位 控制字命令或數(shù)據(jù)標(biāo)志 數(shù)據(jù)字節(jié) 停止位??刂谱钟脕?lái)區(qū)分接下來(lái)是命令還是顯示RAM數(shù)據(jù)這個(gè)必須嚴(yán)格按手冊(cè)來(lái)。比如設(shè)置系統(tǒng)偏壓、開(kāi)啟振蕩器、選擇公共端數(shù)量這類都屬于命令而寫(xiě)入顯示RAM的數(shù)據(jù)屬于數(shù)據(jù)。另外要注意I2C時(shí)序參數(shù)比如SCL高電平最小時(shí)間、建立時(shí)間和保持時(shí)間。BL55072A不是那種慢悠悠的老芯片但對(duì)上拉電阻的阻值還是有要求的。一般MCU的I2C引腳內(nèi)部上拉約40kΩ如果總線速率跑400kHz建議外部加4.7kΩ上拉跑100kHz可以放寬到10kΩ。上拉太小會(huì)增加功耗太大會(huì)導(dǎo)致上升沿變緩在小概率下會(huì)出現(xiàn)偶發(fā)通信失敗這個(gè)在后期排查時(shí)很難想到。2.2 硬件電路設(shè)計(jì)這幾個(gè)引腳別接錯(cuò)BL55072A常見(jiàn)封裝是SSOP48和LQFP48引腳不少但核心引腳就那么幾類。電源方面VDD接2.7V~5.5V具體看型號(hào)VSS接地。它內(nèi)部有穩(wěn)壓電路一般不需要外部LDO但VDD引腳旁邊必須放一個(gè)0.1μF去耦電容最好再加一個(gè)10μF電解電容穩(wěn)住低頻這和所有數(shù)字IC一樣沒(méi)什么可說(shuō)的。COM和SEG引腳直接連到LCD屏的對(duì)應(yīng)引腳。這里要注意COM和SEG的分配順序不一定要按照屏幕的物理引腳順序很多屏廠出來(lái)的屏引腳順序是亂的比如COM0不一定在物理第1腳。所以硬件設(shè)計(jì)階段最好對(duì)照屏的引腳定義表把COM/SEG的連接關(guān)系做成一張映射表方便驅(qū)動(dòng)里做映射。我見(jiàn)過(guò)不少工程師拿著開(kāi)發(fā)板測(cè)試硬件按順序接軟件也按順序驅(qū)動(dòng)但顯示內(nèi)容亂碼一查才知道屏幕廠家把SEG引腳重新排序了最后只能一個(gè)一個(gè)試非常痛苦。還有一個(gè)容易被忽略的引腳是VLCD它用來(lái)設(shè)置LCD的驅(qū)動(dòng)電壓。BL55072A內(nèi)部有偏壓電阻VLCD懸空時(shí)使用內(nèi)部偏壓如果需要調(diào)整顯示對(duì)比度可以在VLCD和VDD之間接電阻分壓或者直接外部供一個(gè)LCD電壓。注意VLCD電壓不能超過(guò)VDD范圍否則可能損壞芯片。驅(qū)動(dòng)代碼里通常有一個(gè)“對(duì)比度調(diào)整”的命令就是通過(guò)內(nèi)部寄存器選擇不同的偏壓等級(jí)和外部VLCD配合使用。2.3 從HT1621遷移到BL55072A要注意什么如果你之前用的是HT1621轉(zhuǎn)到BL55072A之后第一個(gè)感受就是接口變了。HT1621是串行三線接口CS、WR、DATABL55072A是I2C硬件上少了兩根控制線只用SCL和SDA。代碼層面HT1621常用的“寫(xiě)命令”和“寫(xiě)數(shù)據(jù)”函數(shù)需要改成I2C讀寫(xiě)。第二個(gè)區(qū)別是顯示RAM的地址組織方式。HT1621的RAM地址比較簡(jiǎn)單一個(gè)地址對(duì)應(yīng)一個(gè)字節(jié)每個(gè)bit對(duì)應(yīng)一個(gè)SEG而B(niǎo)L55072A的RAM是按COM和SEG矩陣排列的寫(xiě)入時(shí)需要明白地址映射關(guān)系否則很容易出現(xiàn)“數(shù)字顯示偏移”或者“SEG混位”。我在第4節(jié)會(huì)詳細(xì)圖解這個(gè)過(guò)程。第三個(gè)區(qū)別是命令系統(tǒng)。HT1621的命令有系統(tǒng)配置、系統(tǒng)使能、偏壓選擇等BL55072A也有類似功能但具體命令字不同。所以直接把HT1621的驅(qū)動(dòng)代碼復(fù)制過(guò)來(lái)改I2C是行不通的命令字必須重寫(xiě)。這也是這篇文章想幫你解決的問(wèn)題不光是給代碼更重要的是讓你理解命令背后的機(jī)制下次換任何LCD驅(qū)動(dòng)芯片都能快速上手。3. 寄存器配置與初始化流程從時(shí)序到代碼3.1 顯示RAM地址映射理解COM與SEG的矩陣關(guān)系BL55072A內(nèi)部顯示RAM是4×72bit的結(jié)構(gòu)也就是4個(gè)公共端每個(gè)公共端對(duì)應(yīng)72個(gè)段。通常RAM地址按字節(jié)組織每個(gè)字節(jié)的bit位對(duì)應(yīng)一個(gè)SEG。具體映射有兩種常見(jiàn)約定按COM分頁(yè)按SEG偏移即以COM為單位劃分地址塊地址偏移對(duì)應(yīng)SEG編號(hào)。比如COM0的第一個(gè)字節(jié)是0x00COM1的第一個(gè)字節(jié)是0x12或0x18取決于手冊(cè)定義的地址偏移。按SEG分頁(yè)按COM偏移另一種是每個(gè)SEG對(duì)應(yīng)一個(gè)字節(jié)字節(jié)內(nèi)bit0~bit3對(duì)應(yīng)COM0~COM3。不同廠家的芯片習(xí)慣不同BL55072A具體是哪種必須看數(shù)據(jù)手冊(cè)的RAM Map圖。我以最常見(jiàn)的一種為例具體以手冊(cè)為準(zhǔn)RAM地址從0x00到0x4B共76個(gè)字節(jié)。其中COM0~COM3分別映射到不同地址區(qū)域每個(gè)地址對(duì)應(yīng)8個(gè)SEG。寫(xiě)入RAM時(shí)寫(xiě)1表示該段點(diǎn)亮。比如你想讓COM0、SEG0亮就把對(duì)應(yīng)地址的bit0置1。為了心里有底畫(huà)一張手寫(xiě)映射表是值得的。比如一個(gè)8位數(shù)字小數(shù)點(diǎn)的屏先確定每個(gè)數(shù)字的a~g段和小數(shù)點(diǎn)dp分別接到哪幾個(gè)SEG再推算出需要往哪個(gè)RAM地址寫(xiě)什么bit。這步很多人會(huì)偷懶但我不建議跳過(guò)去。因?yàn)槟愫罄m(xù)調(diào)試亂碼時(shí)這張表就是救命圖。我自己習(xí)慣用Excel做一張大表橫軸是SEG0~SEG71縱軸是COM0~COM3然后在表格里填模塊名稱比如“數(shù)字1的a段”、“電池符號(hào)”最后生成一個(gè)初始化數(shù)組或者段碼表。這個(gè)工作看起來(lái)繁瑣但一旦做完后面所有顯示邏輯都會(huì)變得非常清晰。3.2 初始化命令序列上電后必須做的幾件事BL55072A上電后并不能直接寫(xiě)顯示RAM需要先發(fā)送一串初始化命令讓芯片內(nèi)部振蕩器啟動(dòng)、偏壓電路穩(wěn)定、顯示開(kāi)啟。這套序列基本是固定的依序執(zhí)行即可。初始化順序一般如下關(guān)閉顯示避免在上電配置過(guò)程中出現(xiàn)亂閃。系統(tǒng)使能System Enable開(kāi)啟內(nèi)部振蕩器和LCDC時(shí)鐘。公共端數(shù)量設(shè)置選擇1/2/3/4 COM模式通常我們選4COM。偏壓設(shè)置選擇1/3或1/2偏壓這個(gè)要和屏的規(guī)格匹配通常4COM屏用1/3偏壓。對(duì)比度調(diào)整設(shè)置內(nèi)部VLCD電壓等級(jí)或者通過(guò)外部電阻調(diào)整。清除顯示RAM把所有RAM位清零確保初始狀態(tài)無(wú)殘留。開(kāi)啟顯示最后打開(kāi)顯示輸出。其中“關(guān)閉顯示”和“系統(tǒng)使能”的順序不同數(shù)據(jù)手冊(cè)可能有細(xì)微差別。我建議先關(guān)閉顯示再系統(tǒng)使能再配置其他寄存器最后開(kāi)顯示這個(gè)順序在多數(shù)芯片上都是穩(wěn)妥的。如果先開(kāi)顯示再配置顯示期間寄存器變化可能導(dǎo)致屏幕閃一下或者出現(xiàn)短暫亂碼。3.3 命令字示例用偽代碼看懂每個(gè)bit的含義BL55072A的命令通常是一個(gè)字節(jié)高位是指令標(biāo)志低位是參數(shù)。因?yàn)榫唧w命令字因型號(hào)版本而異我這里用偽代碼展示通用框架實(shí)際使用時(shí)請(qǐng)?zhí)鎿Q成你手冊(cè)上的確切斷言。// 偽代碼發(fā)送命令 void bl55072a_write_cmd(uint8_t cmd) { i2c_start(); i2c_write_byte(0xE0); // 設(shè)備寫(xiě)地址 i2c_write_byte(cmd); // 命令字 i2c_stop(); } // 偽代碼顯示開(kāi)關(guān) void bl55072a_display(bool on) { if (on) { bl55072a_write_cmd(0x03); // 例如顯示開(kāi)命令 } else { bl55072a_write_cmd(0x02); // 例如顯示關(guān)命令 } }注意命令字本身可能區(qū)分“命令通道”和“數(shù)據(jù)通道”。有些芯片通過(guò)控制字的第一位來(lái)區(qū)分比如0x80表示接下來(lái)寫(xiě)入的是命令0xC0表示寫(xiě)入的是顯示數(shù)據(jù)。所以寫(xiě)數(shù)據(jù)前可能需要先發(fā)送一個(gè)“數(shù)據(jù)模式”指令再連續(xù)寫(xiě)入RAM地址和數(shù)據(jù)。這個(gè)細(xì)節(jié)一定要查手冊(cè)不要想當(dāng)然。4. 核心實(shí)操基于MCU的驅(qū)動(dòng)程序?qū)崿F(xiàn)以STM32為例4.1 硬件連接與工程準(zhǔn)備我這次用的主控是STM32F103C8T6I2C1的SCL接PB6SDA接PB7BL55072A接3.3V供電VLCD懸空使用內(nèi)部偏壓。上拉電阻用的4.7kΩ到3.3V。LCD屏是一塊定制的4COM、1/3偏壓段碼屏8位數(shù)字帶單位符號(hào)。工程上我使用STM32標(biāo)準(zhǔn)外設(shè)庫(kù)或HAL庫(kù)均可通過(guò)模擬I2C的方式來(lái)驅(qū)動(dòng)因?yàn)槟MI2C可以方便地調(diào)整時(shí)序而且不用和硬件I2C的各種標(biāo)志位糾纏。如果你用硬件I2C注意BL55072A的地址是7位但STM32的硬件I2C要求傳入的是8位地址左移一位別搞混。模擬I2C核心就三個(gè)函數(shù)static void i2c_delay(void) { for (volatile int i 0; i 10; i); // 延時(shí)調(diào)整速率 } void i2c_start(void) { SDA_HIGH; SCL_HIGH; i2c_delay(); SDA_LOW; i2c_delay(); SCL_LOW; i2c_delay(); } void i2c_stop(void) { SDA_LOW; SCL_HIGH; i2c_delay(); SDA_HIGH; i2c_delay(); }寫(xiě)字節(jié)函數(shù)這里不展開(kāi)無(wú)非是移位判斷SCL翻轉(zhuǎn)。注意每字節(jié)之后要釋放SDA然后等待從機(jī)ACK第9個(gè)時(shí)鐘否則后面會(huì)出問(wèn)題。很多模擬I2C代碼不檢測(cè)ACK這在低速調(diào)試沒(méi)事但一旦接線松了或者地址錯(cuò)了會(huì)出現(xiàn)寫(xiě)“成功”但屏幕毫無(wú)反應(yīng)的現(xiàn)象。建議在調(diào)試階段把ACK檢測(cè)加進(jìn)去返回錯(cuò)誤碼便于定位問(wèn)題。4.2 初始化與底層讀寫(xiě)函數(shù)根據(jù)上一節(jié)的命令序列我用STM32實(shí)現(xiàn)完整的初始化函數(shù)#define BL55072A_WR_ADDR 0xE0 static void bl55072a_send_cmd(uint8_t cmd) { i2c_start(); i2c_write_byte(BL55072A_WR_ADDR); i2c_write_byte(cmd); i2c_stop(); } static void bl55072a_send_data(uint8_t addr, uint8_t *data, uint8_t len) { i2c_start(); i2c_write_byte(BL55072A_WR_ADDR); i2c_write_byte(0x80); // 數(shù)據(jù)模式命令實(shí)際以手冊(cè)為準(zhǔn) i2c_write_byte(addr); // RAM起始地址 for (int i 0; i len; i) { i2c_write_byte(data[i]); } i2c_stop(); } void bl55072a_init(void) { bl55072a_send_cmd(0x02); // 顯示關(guān)閉 bl55072a_send_cmd(0x01); // 系統(tǒng)使能 bl55072a_send_cmd(0x14); // 設(shè)置4COM、1/3偏壓示例值 bl55072a_send_cmd(0x11); // 內(nèi)部RC振蕩器對(duì)比度等級(jí)示例值 uint8_t clear_data[9] {0}; bl55072a_send_data(0x00, clear_data, 9); // 清除前72段RAM bl55072a_send_cmd(0x03); // 顯示開(kāi)啟 }上面命令字的數(shù)值是我為了演示隨手寫(xiě)的真實(shí)芯片請(qǐng)用手冊(cè)里的值。我特別強(qiáng)調(diào)一下這里最容易出錯(cuò)的不是函數(shù)框架而是“數(shù)據(jù)模式命令”的格式。有些芯片需要先發(fā)“命令0x80”表示接下來(lái)要寫(xiě)地址和數(shù)據(jù)有些芯片直接發(fā)地址就可以。如果數(shù)據(jù)模式命令錯(cuò)了寫(xiě)進(jìn)去的RAM數(shù)據(jù)可能會(huì)被芯片當(dāng)成命令造成莫名其妙的顯示錯(cuò)亂。4.3 段碼表與字符映射讓數(shù)字正常顯示段碼LCD顯示數(shù)字時(shí)最常用的方法是建立一個(gè)段碼表。比如數(shù)字0~9分別對(duì)應(yīng)a~g段再考慮小數(shù)點(diǎn)。這是一個(gè)經(jīng)典的七段碼映射但要注意芯片RAM和段碼之間的位對(duì)應(yīng)關(guān)系。以我的屏為例假設(shè)一個(gè)數(shù)字占6個(gè)SEGa,b,c,d,e,f,g其中g(shù)在SEG0a在SEG1b在SEG2c在SEG3d在SEG4e在SEG5f在SEG6小數(shù)點(diǎn)dp在SEG7。這個(gè)順序完全取決于屏幕設(shè)計(jì)不要照搬。我最終生成一個(gè)數(shù)組每一位代表某個(gè)SEG的亮滅。const uint8_t seg_code[10] { 0x3F, // 0: a b c d e f 0x06, // 1: b c 0x5B, // 2: a b d e g 0x4F, // 3: a b c d g 0x66, // 4: b c f g 0x6D, // 5: a c d f g 0x7D, // 6: a c d e f g 0x07, // 7: a b c 0x7F, // 8: all 0x6F // 9: a b c d f g };這個(gè)表是標(biāo)準(zhǔn)共陰數(shù)碼管的編碼但用在LCD上不一定直接對(duì)應(yīng)因?yàn)長(zhǎng)CD的COM和SEG掃描方式和七段數(shù)碼管不同。所以在實(shí)際項(xiàng)目中你需要根據(jù)自己屏的段位編號(hào)重新整理。比如我的屏某個(gè)數(shù)字的g段對(duì)應(yīng)的RAM地址是0x03的bit5那么顯示數(shù)字0時(shí)就要把該bit置1編碼自然就不是0x3F了。這塊不能偷懶。一個(gè)建議是把每個(gè)顯示位digit與SEG的映射做成二維結(jié)構(gòu)體數(shù)組而不是直接用七段碼映射。比如typedef struct { uint8_t seg_a; uint8_t seg_b; uint8_t seg_c; uint8_t seg_d; uint8_t seg_e; uint8_t seg_f; uint8_t seg_g; uint8_t seg_dp; } DIGIT_SEG_MAP; DIGIT_SEG_MAP digit_map[DIGIT_NUM] { // 每位數(shù)字對(duì)應(yīng)的SEG引腳號(hào) };這樣映射清晰寫(xiě)顯示函數(shù)時(shí)只需要遍歷每一位把對(duì)應(yīng)SEG位置1即可。雖然犧牲一點(diǎn)RAM但代碼可讀性和可維護(hù)性大幅提升。4.4 顯示數(shù)字與符號(hào)完整驅(qū)動(dòng)代碼實(shí)現(xiàn)下面是顯示一個(gè)溫度值的例子比如要顯示“25.6℃”。我的屏上第0位到第2位顯示數(shù)字和小數(shù)點(diǎn)第3位顯示“℃”符號(hào)第4位可能顯示其他標(biāo)志。void lcd_display_temp(float temp) { // 把溫度拆成百位、十位、個(gè)位、十分位 int whole (int)temp; int digit0 whole / 10 % 10; // 十位 int digit1 whole % 10; // 個(gè)位 int frac (int)(temp * 10) % 10; // 十分位 uint8_t ram_data[5] {0}; // 設(shè)置第0位數(shù)字十位如果為0可以關(guān)閉或顯示0 set_digit_ram(ram_data, 0, digit0); // 設(shè)置第1位數(shù)字個(gè)位并打開(kāi)小數(shù)點(diǎn) set_digit_ram(ram_data, 1, digit1); set_digit_point(ram_data, 1, true); // 設(shè)置第2位數(shù)字十分位 set_digit_ram(ram_data, 2, frac); // 設(shè)置第3位℃符號(hào) set_symbol_degree(ram_data, true); bl55072a_send_data(0x00, ram_data, sizeof(ram_data)); }set_digit_ram函數(shù)的作用是把一個(gè)數(shù)字的七段信息寫(xiě)入到對(duì)應(yīng)的RAM字節(jié)bit中去這是最關(guān)鍵也是最繁瑣的地方。我的做法是預(yù)計(jì)算每個(gè)數(shù)字位對(duì)應(yīng)的RAM地址和bit偏移然后根據(jù)段碼表映射到具體bit。因?yàn)椴煌恋腞AM映射不同這里就不貼死代碼了分享一個(gè)模板思路static void set_digit_ram(uint8_t *ram, uint8_t pos, uint8_t num) { for (int seg 0; seg 7; seg) { bool on (seg_code[num] seg) 1; uint8_t ram_addr digit_map[pos].addr[seg]; uint8_t bit_mask digit_map[pos].mask[seg]; if (on) { ram[ram_addr] | bit_mask; } else { ram[ram_addr] ~bit_mask; } } }這個(gè)循環(huán)看似簡(jiǎn)單但它把所有段碼映射邏輯都抽象出來(lái)了一旦屏幕改動(dòng)只需要改映射表不需要改主邏輯。4.5 對(duì)比度調(diào)節(jié)與功耗優(yōu)化BL55072A支持通過(guò)命令調(diào)節(jié)顯示對(duì)比度其實(shí)調(diào)節(jié)的是LCD驅(qū)動(dòng)電壓的等級(jí)。對(duì)比度不夠會(huì)顯得字淡太高了又會(huì)出現(xiàn)“串?dāng)_”——不該亮的段微微亮也就是我們常說(shuō)的鬼影。調(diào)試時(shí)我建議從最低對(duì)比度往上調(diào)找到剛好清晰的最低值這樣既能保證顯示效果又能降低功耗。在電池供電的產(chǎn)品里還要注意幾點(diǎn)進(jìn)入低功耗模式前發(fā)送顯示關(guān)閉命令讓芯片停止掃描輸出可以明顯省電。不需要?jiǎng)討B(tài)顯示時(shí)不要頻繁更新RAM因?yàn)槊看蜪2C傳輸都會(huì)產(chǎn)生動(dòng)態(tài)電流。如果系統(tǒng)只有LCD常亮建議把I2C上拉電阻適當(dāng)加大如10kΩ減少靜態(tài)電流但要注意不能影響通信速率。BL55072A本身在顯示模式下的電流其實(shí)很低一般在幾十微安級(jí)別具體看數(shù)據(jù)手冊(cè)。對(duì)于便攜設(shè)備來(lái)說(shuō)這個(gè)功耗優(yōu)勢(shì)是它比直接用MCU掃描要優(yōu)秀得多。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 屏幕不亮、白屏、顯示亂碼如何快速定位調(diào)試LCD驅(qū)動(dòng)最重要的工具是I2C邏輯分析儀或者示波器。如果你沒(méi)有硬件工具就用軟件模擬I2C加串口打印ACK狀態(tài)的辦法。我把常見(jiàn)故障分成了三類每類的排查重點(diǎn)不同現(xiàn)象可能原因排查步驟屏幕完全不亮無(wú)任何顯示電源沒(méi)接好、芯片未使能、顯示關(guān)閉命令未發(fā)送用萬(wàn)用表量VDD電壓檢查VLCD電壓是否符合屏幕要求確認(rèn)初始化序列執(zhí)行了“顯示開(kāi)啟”屏幕有微弱顯示但對(duì)比度極低偏壓設(shè)置不對(duì)、VLCD電壓太低檢查偏壓命令嘗試調(diào)高對(duì)比度等級(jí)外部VLCD是否懸空得當(dāng)顯示內(nèi)容錯(cuò)亂數(shù)字串位RAM地址映射錯(cuò)誤、SEG/COM接反、段碼表與硬件不匹配對(duì)比手冊(cè)的RAM Map檢查屏的引腳定義核對(duì)映射表這里面最煩人的是顯示內(nèi)容錯(cuò)亂的排查因?yàn)椴皇峭耆S機(jī)的而是有規(guī)律的亂。比如“1”顯示成了“x”“2”顯示成了“y”這種通常是段碼表不對(duì)。如果顯示的數(shù)字完全不是期望的比如想顯示5卻顯示6可能是某些SEG bit寫(xiě)反了。我的經(jīng)驗(yàn)是先在屏幕上點(diǎn)亮所有段寫(xiě)0xFF到所有RAM看看哪些段實(shí)際點(diǎn)亮然后再對(duì)照RAM地址逐步寫(xiě)入單個(gè)bit畫(huà)出一張真實(shí)的“點(diǎn)亮表”和理論RAM表做比對(duì)一般10分鐘就能找出映射問(wèn)題。5.2 I2C通信失敗的幾個(gè)典型場(chǎng)景I2C通信失敗在驅(qū)動(dòng)調(diào)試中也特別常見(jiàn)。有幾個(gè)典型場(chǎng)景場(chǎng)景1發(fā)送地址后沒(méi)有ACK。先確認(rèn)設(shè)備地址是否正確有沒(méi)有左移一位。再用示波器看SDA在ACK時(shí)鐘周期是否被拉低。如果一直為高說(shuō)明從機(jī)沒(méi)應(yīng)答可能芯片沒(méi)上電、地址錯(cuò)誤、或者SCL/SDA接反。場(chǎng)景2ACK偶爾有偶爾沒(méi)有。這種多半是上拉電阻過(guò)大、總線電容過(guò)大或者速率過(guò)高。試著把速率降到100kHz或者把上拉電阻換成4.7kΩ通常能解決。場(chǎng)景3寫(xiě)命令正常寫(xiě)數(shù)據(jù)后屏幕無(wú)反應(yīng)。這種問(wèn)題往往出在“數(shù)據(jù)模式命令”和“地址自增”的理解上。有些芯片連續(xù)寫(xiě)數(shù)據(jù)時(shí)內(nèi)部地址會(huì)自動(dòng)遞增但如果數(shù)據(jù)模式命令沒(méi)設(shè)置好寫(xiě)入的地址可能會(huì)被芯片忽略。務(wù)必檢查一下連續(xù)寫(xiě)數(shù)據(jù)的時(shí)序看地址是否有自增。5.3 關(guān)于“驅(qū)動(dòng)程序感嘆號(hào)”的誤區(qū)說(shuō)明順帶說(shuō)一句很多人在網(wǎng)上搜“BL55072A驅(qū)動(dòng)程序”可能會(huì)搜到Windows的“設(shè)備驅(qū)動(dòng)程序”報(bào)錯(cuò)信息比如代碼31、找不到數(shù)字簽名之類的內(nèi)容。這里必須澄清一下BL55072A是LCD顯示控制芯片不是電腦外部設(shè)備它沒(méi)有Windows驅(qū)動(dòng)這種概念。你在MCU工程里寫(xiě)的“驅(qū)動(dòng)”是嵌入式軟件里的底層設(shè)備驅(qū)動(dòng)是運(yùn)行在單片機(jī)上的C語(yǔ)言代碼和電腦系統(tǒng)里的.sys/.dll驅(qū)動(dòng)完全是兩個(gè)世界。如果看到類似“windows無(wú)法加載驅(qū)動(dòng)”的內(nèi)容那是另一類問(wèn)題不要混淆。搜索引擎經(jīng)常把這兩個(gè)完全不相干的話題攪在一起這也是我寫(xiě)這篇文章的原因之一希望大家能搞清楚目標(biāo)平臺(tái)。5.4 我踩過(guò)的三個(gè)坑提前幫你避開(kāi)坑一默認(rèn)顯示RAM有隨機(jī)值。有些芯片剛上電時(shí)RAM內(nèi)容不確定如果不做清RAM直接開(kāi)顯示屏幕會(huì)閃現(xiàn)隨機(jī)亂碼。雖然時(shí)間很短但在產(chǎn)品上電自檢時(shí)非常難看。所以初始化時(shí)務(wù)必把所有顯示RAM地址都寫(xiě)一遍0x00不要只清零用到的地址最好覆蓋芯片全部地址范圍??佣LCD電壓過(guò)高導(dǎo)致花屏和拖影。有一次我為了提高對(duì)比度把VLCD電壓調(diào)到了超過(guò)VDD結(jié)果屏幕出現(xiàn)了明顯的段間串?dāng)_不該亮的段全部淡淡地亮著像鬼影一樣。后來(lái)查手冊(cè)發(fā)現(xiàn)VLCD的最大值有限制需要低于VDD。所以調(diào)節(jié)對(duì)比度優(yōu)先使用內(nèi)部命令實(shí)在不行再考慮外部分壓??尤浱幚砥帘挝弧L55072A的RAM是4×72位但實(shí)際你可能只用了其中的一部分SEG。寫(xiě)入數(shù)據(jù)時(shí)如果整個(gè)字節(jié)都重新賦值沒(méi)用到的高位或者低位可能會(huì)被意外清掉或誤置。解決方法是讀-修改-寫(xiě)或者使用位掩碼。如果硬件I2C不方便讀回你可以在軟件里維護(hù)一份RAM鏡像每次更新時(shí)先改鏡像再把整個(gè)字節(jié)寫(xiě)出去。這個(gè)小技巧能避免很多“某一位突然不顯示了”的玄學(xué)問(wèn)題。6. 從驅(qū)動(dòng)到產(chǎn)品一點(diǎn)個(gè)人經(jīng)驗(yàn)最后分享一個(gè)我自己的習(xí)慣寫(xiě)完BL55072A驅(qū)動(dòng)后不要只跑一個(gè)“顯示Hello”的Demo就收工而是把產(chǎn)品要顯示的所有界面都列出來(lái)比如正常數(shù)值、超限報(bào)警、單位切換、低電量提示等等逐個(gè)在調(diào)試板上點(diǎn)亮驗(yàn)證一遍。因?yàn)長(zhǎng)CD屏的段位映射表非常依賴屏幕廠商的圖紙可能某個(gè)特殊符號(hào)的段位你從沒(méi)核對(duì)過(guò)等到整機(jī)測(cè)試才發(fā)現(xiàn)某根SEG畫(huà)錯(cuò)了這時(shí)候再去改板子就晚了。另外強(qiáng)烈建議在驅(qū)動(dòng)里預(yù)留一個(gè)debug接口比如通過(guò)串口發(fā)送命令動(dòng)態(tài)讀取/修改當(dāng)前顯示RAM的內(nèi)容。這個(gè)功能在產(chǎn)線測(cè)試和售后分析時(shí)非常有用。實(shí)際生產(chǎn)中很多“屏幕顯示異?!钡目驮V最后都指向段碼表配置錯(cuò)誤或初始化時(shí)序被意外跳過(guò)有了debug接口你甚至不需要返修設(shè)備遠(yuǎn)程發(fā)一條命令就能復(fù)現(xiàn)問(wèn)題。BL55072A這顆芯片本身不算復(fù)雜真正考驗(yàn)人的是對(duì)RAM映射的理解和對(duì)時(shí)序細(xì)節(jié)的處理。把上面這些內(nèi)容消化掉你不僅能搞定BL55072A以后再遇到其他LCD驅(qū)動(dòng)芯片心里也會(huì)有底得多。