設(shè)計(jì)二進(jìn)制時(shí)鐘: 理解多路復(fù)用約束
設(shè)計(jì)一個(gè)二進(jìn)制時(shí)鐘是一個(gè)有吸引力的項(xiàng)目,它能讓您展示數(shù)字電子學(xué)、微控制器編程以及物理組裝和中等復(fù)雜電路的能力。關(guān)于這個(gè)主題,已經(jīng)有許多文章進(jìn)行了探討。然而,深入探討設(shè)計(jì)決策和必要的妥協(xié)的文章卻不多。這是一個(gè)遺憾,因?yàn)檫@個(gè)主題提供了一個(gè)極好的機(jī)會(huì)來(lái)探索微控制器的物理限制、歐姆定律的應(yīng)用、多路復(fù)用的遞減收益以及易于故障排除的組件布局。
本文引用地址:http://www.ex-cimer.com/article/202403/456771.htm本文主要討論以二進(jìn)制時(shí)鐘形式呈現(xiàn)的六列四行 LED 矩陣,如圖1所示。為了最大的可訪問(wèn)性,我們將把對(duì)話限制在 Arduino Nano Every 1上的I/O引腳和電源的物理功能上。請(qǐng)注意,本文中提出的想法直接適用于七段 LED 顯示器,或者更一般地說(shuō),適用于所有多路LED陣列。
現(xiàn)在是回顧二進(jìn)制時(shí)鐘操作的好時(shí)機(jī),因?yàn)槲覀儗⒅攸c(diǎn)關(guān)注底層硬件和多路復(fù)用方面。
圖 1 : Arduino Nano Every 在面包板上的圖片,背景是 LED 陣列和 Digilent Analog Discovery。
多路復(fù)用定義
多路復(fù)用是有限資源的一種時(shí)間共享形式。在我們的例子中,微控制器只有有限的I/O引腳。通過(guò)使用時(shí)分多路復(fù)用(TDM),我們能夠用循環(huán)序列連續(xù)激活LED或LED組。通過(guò)快速切換 LED 的激活狀態(tài),所有LED看起來(lái)都是一樣亮的。這種效果依賴于“閃爍融合”(flicker fusion),即人眼無(wú)法檢測(cè)到多路復(fù)用陣列中LED的快速切換。
LED通常以公共陽(yáng)極或公共陰極的形式排列成組。圖2展示了一個(gè)具有6列4行 LED 矩陣和公共陽(yáng)極配置的示例。
技術(shù)提示: 術(shù)語(yǔ)“時(shí)分多路復(fù)用”(TDM)通常與以太網(wǎng)等通信系統(tǒng)相關(guān)聯(lián)。在以太網(wǎng)中,許多電腦共享一個(gè)通信通道。由于信道是有限的資源,每臺(tái)計(jì)算機(jī)根據(jù)已建立的通信協(xié)議輪流使用。雖然我們將TDM這一術(shù)語(yǔ)應(yīng)用于LED多路復(fù)用是一種簡(jiǎn)化,但它確實(shí)為我們理解復(fù)雜的通信系統(tǒng)提供了一個(gè)很好的起點(diǎn)。
圖 2 :采用Arduino Nano Every的6列4行復(fù)用LED矩陣示意圖。
公共陽(yáng)極和公共陰極這兩個(gè)術(shù)語(yǔ)會(huì)引起混淆。例如,對(duì)圖2中的LED矩陣的檢查顯示,許多二極管按列共享陽(yáng)極連接,按行共享陰極連接。只有當(dāng)我們檢查激活序列時(shí),才會(huì)發(fā)現(xiàn)共同的陽(yáng)極配置。
圖3給出了多路復(fù)用器時(shí)序的邏輯分析儀視圖。關(guān)于公共二極管連接,我們注意到在任何給定時(shí)間有一個(gè)且只有一個(gè)列被激活。因此,共同連接是陽(yáng)極。在本例中,關(guān)聯(lián)列晶體管將所有關(guān)聯(lián)led的陽(yáng)極拉到5.0 VDC導(dǎo)軌上。單獨(dú)的行驅(qū)動(dòng)器然后應(yīng)用一個(gè)地來(lái)激活所需的LED(s)。
關(guān)于圖3,我們觀察到:
列驅(qū)動(dòng)信號(hào)呈現(xiàn)為Arduino D2到D7引腳對(duì)應(yīng)的前6個(gè)信號(hào)。為方便起見(jiàn),已將信號(hào)名稱添加為紅色高亮文本。我們看到一個(gè),而且只有一個(gè),信號(hào)在任何時(shí)候都是活躍的。請(qǐng)注意,PNP晶體管被用作列驅(qū)動(dòng)器。另外,回想一下,這個(gè)晶體管將通過(guò)將其底座拉向地面來(lái)激活。
最低的4行是Arduino輸出引腳D19到D21對(duì)應(yīng)的行驅(qū)動(dòng)器。這些也是如圖2所示的低電平激活,其中二極管通過(guò)將其拉向地面打開(kāi)。
Digilent Analog Discovery已配置為顯示與4行驅(qū)動(dòng)器相關(guān)的二進(jìn)制數(shù)。這是一些邏輯分析儀的一個(gè)功能,允許操作員將幾行“加入”成一個(gè)方便的ASCII, HEX或二進(jìn)制顯示。為了方便起見(jiàn),我們用藍(lán)色突出顯示了這一點(diǎn)。
技術(shù)提示 :配置LED多路復(fù)用有兩種常用方法,包括公共陽(yáng)極和公共陰極。本文的特點(diǎn)是PNP列驅(qū)動(dòng)器與共同陽(yáng)極LED。在這里,PNP晶體管將公共陽(yáng)極連接到正軌,然后將接地應(yīng)用到各個(gè)陰極。當(dāng)組件旋轉(zhuǎn)90度,將公共驅(qū)動(dòng)器應(yīng)用于行而不是列時(shí),需要仔細(xì)檢查。
圖 3 :邏輯分析儀屏幕截圖顯示了帶有測(cè)試和高光的LED定時(shí)。
技術(shù)提示 :關(guān)于多路復(fù)用和共陽(yáng)極/陰極的討論適用于七段LED顯示器和LED陣列。
當(dāng)前的限制
本文受到Arduino Nano Every功能的限制。這既包括可用的電源,也包括Nano Every的板載電源、單個(gè)引腳和引腳組。
具有ATMega4809微控制器規(guī)格的相關(guān)Arduino Nano Every是:
ABX00028-datasheet.pdf (arduino.cc)
5.0直流電源電流限制= 950毫安
單個(gè)引腳(源或匯)= 40毫安
電源軌引腳(在高溫下)= 100毫安
對(duì)于選用的SSL-LX2573GDLED燈:
室溫穩(wěn)定電流= 25毫安
10 us脈沖的峰值電流= 150毫安
這些數(shù)字中的大多數(shù)是絕對(duì)設(shè)計(jì)最大額定值。為了長(zhǎng)壽命,我們必須保持較寬的安全裕度。作為起點(diǎn),我們把每一個(gè)電流值減半。我們將看到,這個(gè)決定對(duì)復(fù)用選項(xiàng)有嚴(yán)重的影響。它在圖2原理圖上投下陰影,對(duì)晶體管列驅(qū)動(dòng)器和多路復(fù)用led的數(shù)量產(chǎn)生影響。
技術(shù)提示 :微控制器的內(nèi)部芯片使用小鍵合線連接到I/O引腳。這些線與硅走線一起具有有限的載流能力。這反映在器件的設(shè)計(jì)最大規(guī)格上。在前面的例子中,我們看到單個(gè)I/O引腳具有40 mA的設(shè)計(jì)最大值。然而,三個(gè)這樣的引腳在最大電流下工作將壓倒模對(duì)電源或模對(duì)地的鍵合線或硅走線。這種累積電流是一個(gè)重要但經(jīng)常被忽視的設(shè)計(jì)考慮因素。這是一個(gè)會(huì)破壞微控制器的錯(cuò)誤,也許不會(huì)立即破壞,但會(huì)導(dǎo)致產(chǎn)品不可靠。
占空比vs亮度
在解釋晶體管列驅(qū)動(dòng)器和電阻選擇之前,我們需要理解閃爍與LED亮度之間的關(guān)系。請(qǐng)記住,LED矩陣是采用時(shí)分多路復(fù)用(TDM)的。每個(gè)LED都會(huì)“閃爍”,因?yàn)樗鼤?huì)依次打開(kāi)和關(guān)閉,如圖3中的時(shí)序圖所示。您可能已經(jīng)進(jìn)行過(guò)相關(guān)的實(shí)驗(yàn),探索了LED的閃爍現(xiàn)象。
在您學(xué)習(xí) Arduino 的早期階段,您可能已經(jīng)使用 PWM 命令來(lái)調(diào)整 LED 的亮度,例如:
void loop() { static uint8_t val; // Maintain contents across loop iterations analogWrite(LED_PIN, val++); // About 5 seconds to reach 100% duty cycle delay(20); }
這個(gè)PWM操作與TDM操作直接相關(guān),當(dāng)我們考慮對(duì)單個(gè)LED的影響時(shí)。在兩種情況下,LED都會(huì)以一定的時(shí)間開(kāi)啟和關(guān)閉,并且這種開(kāi)啟和關(guān)閉的時(shí)間比例決定了LED的亮度。
結(jié)果如圖4所示,LED分別以100%、50%、25%、12.5%和6.25%的占空比工作,這分別對(duì)應(yīng)于TDM槽的1、2、4、8和16個(gè)時(shí)間段。對(duì)于低占空比的LED,結(jié)果并不理想。事實(shí)上,很難看到占空比為1/16的LED。
圖 4 :不同占空比下led的相對(duì)亮度。
多路復(fù)用的方法
我們的多路復(fù)用目標(biāo)是獲得最大的LED亮度,同時(shí)保持在當(dāng)前的硬件限制內(nèi)。現(xiàn)在我們對(duì)硬件的物理限制有了更好的了解,我們可以探索復(fù)用方法:
一次一個(gè)LED:我們可以消除列驅(qū)動(dòng)器,并允許微控制器直接控制列和行。雖然這對(duì)于電路成本和簡(jiǎn)單性來(lái)說(shuō)是非常理想的,但我們很快就遇到了當(dāng)前的限制。如果沒(méi)有列驅(qū)動(dòng)器,相關(guān)的微控制器引腳必須提供必要的電流。這限制了我們一次只能使用一個(gè)或兩個(gè)LED,因?yàn)槔鄯e電流將超過(guò)微控制器設(shè)計(jì)的最大值??紤]到6 x 4陣列,這需要4%或8%的占空比。LED會(huì)很暗。
每次一列,由微控制器控制列驅(qū)動(dòng)器和行:使用PNP晶體管向列提供電流,我們可以自由地打開(kāi)所有行。給定6 × 4陣列,這導(dǎo)致每個(gè)LED的占空比為17%。本文中給出的結(jié)果對(duì)于光線昏暗的房間來(lái)說(shuō)是最低限度可接受的。
用列、行驅(qū)動(dòng)晶體管一次一列。這種方法允許LED以高于微控制器容量的電流驅(qū)動(dòng)。由于LED具有低占空比,因此可以通過(guò)增加電流來(lái)增加亮度。在LED數(shù)據(jù)表中有一定的模糊性,然而,電流肯定可以增加到設(shè)計(jì)最大值,也許是2倍以上,而不會(huì)損壞LED。回想一下,我們選擇的LED指定為連續(xù)25 mA和150 mA, 10 us脈沖。這種方法需要仔細(xì)考慮LED溫度,并可能導(dǎo)致LED壽命顯著縮短。使用風(fēng)險(xiǎn)自負(fù)。
并行控制:對(duì)于這篇文章,我們假設(shè)微控制器引腳是有限的資源。有任何數(shù)量的端口擴(kuò)展選項(xiàng),將允許直接控制LED。一個(gè)例子是8位TLC6C598移位寄存器。它具有50mA開(kāi)漏驅(qū)動(dòng)器,最大VDS為40 VDC。74HC595是另一種適用于面包板原型設(shè)計(jì)的常見(jiàn)選擇。
電阻的選擇
需要選擇合適的電阻來(lái)確定 LED 的工作電流。為柱驅(qū)動(dòng)晶體管的基極選擇合適的電阻也很重要。當(dāng)激活LED 的數(shù)量隨著顯示的數(shù)量變化時(shí),這個(gè)基極電阻對(duì)于保持一致的 LED 亮度很重要。
LED 的計(jì)算相對(duì)簡(jiǎn)單。第一步是確定當(dāng)前的局限性。使用所選的多路復(fù)用方案,在任何給定時(shí)間最多可激活4個(gè)LED。我們遇到的第一個(gè)限制是微控制器的累積電流。對(duì)于保守的設(shè)計(jì),這是50mA。因此,每個(gè)LED被限制在約13 mA。電阻器計(jì)算為:
這里我們假設(shè)PNP列驅(qū)動(dòng)器VCE非常接近于零。
對(duì)于晶體管基極電阻的計(jì)算,我們將實(shí)現(xiàn)一個(gè)強(qiáng)制的beta條件。這個(gè)晶體管工作點(diǎn)確保晶體管深度飽和,這將確保所有LED具有相同的亮度。為了設(shè)置這個(gè)條件,我們選擇電阻器,使基極電流為集電極電流的1/10。給定4個(gè)有源LED,集電極電流約為50 mA。通過(guò)強(qiáng)制beta操作,我們將強(qiáng)制基極電流為5mA。
改進(jìn)空間
這篇文章的重點(diǎn)是多路LED的各個(gè)方面。很少甚至沒(méi)有考慮將設(shè)備作為可靠的計(jì)時(shí)器來(lái)操作。本說(shuō)明所附的代碼使用了 Arduino millis() 函數(shù),該函數(shù)不被稱為可靠的實(shí)時(shí)計(jì)時(shí)器。所有這些設(shè)備都依賴于微控制器的高速振蕩器,它不像正確實(shí)現(xiàn)的32.768 kHz時(shí)鐘晶體那樣精確。
為了提高性能,你可能想要集成一個(gè)實(shí)時(shí)時(shí)鐘模塊。對(duì)于一個(gè)額外的挑戰(zhàn),可以嘗試一個(gè)基于 GPS 或 WWVB (原子鐘接收器)的帶天線的計(jì)時(shí)器。
#define C0_PIN 2
#define C1_PIN 3
#define C2_PIN 4
#define C3_PIN 5
#define C4_PIN 6
#define C5_PIN 7
#define R0_PIN 21
#define R1_PIN 20
#define R2_PIN 19
#define R3_PIN 18
void set_column(uint8_t c) {
digitalWrite(C0_PIN, HIGH);
digitalWrite(C1_PIN, HIGH);
digitalWrite(C2_PIN, HIGH);
digitalWrite(C3_PIN, HIGH);
digitalWrite(C4_PIN, HIGH);
digitalWrite(C5_PIN, HIGH);
switch (c) {
case 0: digitalWrite(C0_PIN, LOW); break;
case 1: digitalWrite(C1_PIN, LOW); break;
case 2: digitalWrite(C2_PIN, LOW); break;
case 3: digitalWrite(C3_PIN, LOW); break;
case 4: digitalWrite(C4_PIN, LOW); break;
case 5: digitalWrite(C5_PIN, LOW); break;
default: break;
}
}
void set_row(uint8_t n) {
bool D0 = ((n & 0x01) == 0);
bool D1 = ((n & 0x02) == 0);
bool D2 = ((n & 0x04) == 0);
bool D3 = ((n & 0x08) == 0);
digitalWrite(R0_PIN, D0);
digitalWrite(R1_PIN, D1);
digitalWrite(R2_PIN, D2);
digitalWrite(R3_PIN, D3);
}
void set_LED(uint8_t c, uint8_t n) {
set_column(c);
set_row(n);
}
void setup() {
pinMode(C5_PIN, OUTPUT);
pinMode(C4_PIN, OUTPUT);
pinMode(C3_PIN, OUTPUT);
pinMode(C2_PIN, OUTPUT);
pinMode(C1_PIN, OUTPUT);
pinMode(C0_PIN, OUTPUT);
pinMode(R3_PIN, OUTPUT);
pinMode(R2_PIN, OUTPUT);
pinMode(R1_PIN, OUTPUT);
pinMode(R0_PIN, OUTPUT);
}
void loop() {
uint8_t i;
uint32_t now = millis() /1000;
uint8_t seconds = now % 60;
uint8_t minutes = (now / 60) % 60;
uint8_t hours = (now / 3600) % 24;
uint8_t hoursHigh = hours / 10;
uint8_t hoursLow = hours % 10;
uint8_t minutesHigh = minutes / 10;
uint8_t minutesLow = minutes % 10;
uint8_t secondsHigh = seconds / 10;
uint8_t secondsLow = seconds % 10;
for (i = 0; i < 6; i++) {
switch (i) {
case 0: set_LED(0, secondsLow); break;
case 1: set_LED(1, secondsHigh); break;
case 2: set_LED(2, minutesLow); break;
case 3: set_LED(3, minutesHigh);break;
case 4: set_LED(4, hoursLow); break;
case 5: set_LED(5, hoursHigh); break;
default: break;
}
delay(2);
}
}
評(píng)論