MF干活——C#數(shù)碼管與跑馬燈之舞(視頻)
通過《C#點(diǎn)燈神話》,我們了解到單片機(jī)最基礎(chǔ)的輸入輸出功能,也就是所謂的GPIO。
這一次,我們來點(diǎn)更多的“燈”——數(shù)碼管,并且還是四位數(shù)碼管。
先上結(jié)果,看視頻:
這個(gè)視頻混合了四位數(shù)碼管、液晶屏和跑馬燈,比上一次的小燈閃爍要效果好點(diǎn)。
數(shù)碼管原理簡述
現(xiàn)
在的數(shù)碼管也叫七段數(shù)碼管,一個(gè)完整的數(shù)字8就剛好用到七段小燈,加上小數(shù)點(diǎn)就是8個(gè)小燈。嗯,每一段就是一個(gè)小燈,那么操作方式跟點(diǎn)燈神話里面是一樣
的,只不過多了很多而已。四位數(shù)字,每個(gè)數(shù)字用8個(gè)小燈,那么一共就有32個(gè),加上接地或者高電平,至少得33腳,接線那可是一個(gè)非常復(fù)雜的工程。
但是,我們的四位數(shù)碼管,只有12腳,其中8個(gè)負(fù)責(zé)每一個(gè)數(shù)字的同一段小燈,剩下四個(gè)選擇給哪個(gè)數(shù)字供電。這就涉及一個(gè)非常悲催的事情:當(dāng)你控制點(diǎn)亮個(gè)位,準(zhǔn)備控制輸出十位的時(shí)候,個(gè)位會(huì)熄滅,因?yàn)檫x擇腳選擇了十位;如果你同時(shí)選擇個(gè)位,那么只會(huì)落得個(gè)位跟十位一樣數(shù)字,因?yàn)樗鼈儾煌瑪?shù)字的同一段共用同一個(gè)針腳!
怎么辦??呵呵,有些人就以為數(shù)碼管設(shè)計(jì)上有問題,應(yīng)該有類似液晶屏一樣的鎖存的東西,每顯示一位后就鎖定。
其
實(shí),這些數(shù)碼管設(shè)計(jì)沒有問題,相反,還非常精妙!上面說了,如果采用傳統(tǒng)的GPIO做法,那么四位數(shù)碼管就得需要33腳,那是非常非常浪費(fèi)針腳資源的。仿
照計(jì)算機(jī)串口硬盤的原理,我們也可以采用串口的方式來控制多位數(shù)碼管。那么,N位數(shù)碼管,其實(shí)只需要N+8個(gè)針腳就已經(jīng)足夠了。
那個(gè)“鎖存”問題怎么解決呢?其實(shí)不用解決,我們完全可以利用人眼的視覺誤差,要知道,動(dòng)畫的基本原理也是這樣子。在顯示個(gè)位后,維持一小段時(shí)間,然后顯示十位,然后百位千位,依次逐位顯示,只要整個(gè)過程很快,我們?nèi)搜劬蜁?huì)以為它們是一起亮起來的!
接線
先看看全局,這里接好了液晶屏、數(shù)碼管(液晶屏上面那個(gè))、10個(gè)小燈。別的東西都是擺設(shè),東西太多太亂不好意思!杜邦線不夠的同學(xué)可以分開實(shí)驗(yàn)。![]()
10個(gè)各種顏色的小燈,接在面包板上,準(zhǔn)備做跑馬燈。下方的線是接地GND。
下右圖是板子左針腳原理圖,我們依箭頭從PG0到PG1選10個(gè),另外接左下角的GND即可。
這里故意犯一個(gè)錯(cuò)誤,用了那個(gè)PB2針腳,其實(shí)探索者一號(hào)上PB2是接蜂鳴器的,現(xiàn)在共用了,如果驅(qū)動(dòng)能力足夠,除了小燈亮以為,大家在視屏里面應(yīng)該還能聽到嘟嘟嘟的背景聲。![]()
四
位數(shù)碼管12腳的接法。這里有個(gè)地方不得不說,大家也許發(fā)現(xiàn)板子上很多針腳插有綠色跳線帽。其實(shí)那是我做的一個(gè)標(biāo)記,標(biāo)明這個(gè)腳已經(jīng)別別的設(shè)備使用,避免
沖突。細(xì)心的同學(xué)可以發(fā)現(xiàn),探索者一號(hào)板子的數(shù)碼管例程,在沒有接上液晶屏的時(shí)候是正常工作的,接上液晶屏后,就不動(dòng)了,液晶屏也不正常了。這就是針腳沖
突,剛好那一片好多腳被液晶屏用了的。
所以,這里另外取12個(gè)接數(shù)碼管,從PC0依箭頭的順序到PC1。
數(shù)碼管方面,正面面向你,有小數(shù)點(diǎn)一邊在下方,那么,背后左下角是針腳1,逆時(shí)針輸下去,一直到左上角的12。按照這個(gè)順序接好數(shù)碼管和板子排針。![]()
![]()
![]()
源代碼
數(shù)碼管源碼。原理上很簡單,開個(gè)線程不斷的刷要顯示的數(shù)字,不能停下來。哦,沒錯(cuò),單片機(jī)也可以用.Net里面的線程!
數(shù)碼管源代碼
using System; using System.Threading; using Microsoft.SPOT.Hardware; namespace MFTest { /// <summary>四位數(shù)碼管</summary> class Led4 { /// <summary>每個(gè)數(shù)字的段數(shù),一個(gè)字節(jié)只用七位,表示數(shù)碼管七段順時(shí)針</summary> static readonly Byte[] LedDigitals = new Byte[] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 }; /// <summary>數(shù)碼管公共腳</summary> public Int32[] PublicPins = new Int32[] { 6, 8, 9, 12 }; /// <summary>數(shù)碼管七段腳</summary> public Int32[] SevenPins = new Int32[] { 11, 7, 4, 2, 1, 10, 5, 3 }; /// <summary>實(shí)際控制板針腳</summary> public Cpu.Pin[] LedPins; OutputPort[] ports; private Int32 _Value; /// <summary>要顯示的數(shù)字</summary> public Int32 Value { get { return _Value; } set { _Value = value; } } private Boolean _IsHex; /// <summary>進(jìn)制。只支持十進(jìn)制和十六進(jìn)制</summary> public Boolean IsHex { get { return _IsHex; } set { _IsHex = value; } } Thread th; public void Start() { if (LedPins == null || LedPins.Length < 1) throw new Exception("No Pins For Led4!"); ports = new OutputPort[LedPins.Length]; for (int i = 0; i < LedPins.Length; i++) { // i==2就是腳3,是DP ports[i] = new OutputPort(LedPins[i], i == 2 || Array.IndexOf(PublicPins, i + 1) >= 0); } for (int i = 0; i < LedPins.Length; i++) { ports[i].Write(false); } th = new Thread(dis); th.Start(); } public void Stop() { if (th == null) return; th.Abort(); th = null; } void dis() { if (IsHex) while (true) ShowNumber16(Value); else while (true) ShowNumber(Value); } void ShowNumber(Int32 n) { for (int i = 0; i < 4; i++) { // 如果現(xiàn)在要顯示的是0,并且不是第一個(gè)數(shù)字,那么應(yīng)該關(guān)閉顯示 if (n == 0 && i > 0) { ShowDigital(i, 0, false); } else { // 這一次應(yīng)該顯示的數(shù)字 ShowDigital(i, n % 10, true); n = n / 10; } } } void ShowNumber16(Int32 n) { for (int i = 0; i < 4; i++) { // 如果現(xiàn)在要顯示的是0,并且不是第一個(gè)數(shù)字,那么應(yīng)該關(guān)閉顯示 if (n == 0 && i > 0) { ShowDigital(i, 0, false); } else { // 這一次應(yīng)該顯示的數(shù)字 ShowDigital(i, n & 0xF, true); n = n >> 4; } } } void ShowDigital(Int32 index, Int32 n, Boolean show) { // 找到控制腳 var pin = ports[PublicPins[index] - 1]; // 如果不顯示,關(guān)閉后直接返回 if (!show) { pin.Write(false); return; } // 輸出數(shù)字的七段表示 var d = LedDigitals[n]; for (int i = 0; i < 7; i++) { var p = ports[SevenPins[i] - 1]; p.Write((d >> i & 0x1) == 0); } // 關(guān)閉小數(shù)點(diǎn) ports[SevenPins[7] - 1].Write(true); // 先輸出數(shù)字,再打開開關(guān) pin.Write(true); // 然后停留一會(huì),形成一個(gè)視覺效果 Thread.Sleep(5); // 最后關(guān)閉,為下一個(gè)顯示做準(zhǔn)備 pin.Write(false); } } }
控制源碼。
控制代碼
// 探索者一號(hào) // 跑馬燈針腳 var pins = new Cpu.Pin[] { Pins.PG1, Pins.PF15, Pins.PF13, Pins.PF11, Pins.PB1, Pins.PC5, Pins.PG0, Pins.PF14, Pins.PF12, Pins.PB2 }; var leds = new OutputPort[pins.Length]; for (int i = 0; i < pins.Length; i++) { leds[i] = new OutputPort(pins[i], i % 2 == 0); } var led4 = new Led4(); // 數(shù)碼管針腳 led4.LedPins = new Cpu.Pin[] { Pins.PC0, Pins.PF9, Pins.PF7, Pins.PF5, Pins.PF3, Pins.PF1, Pins.PF2, Pins.PF4, Pins.PF6, Pins.PF8, Pins.PF10, Pins.PC1 }; led4.IsHex = true; // 按十六進(jìn)制顯示 led4.Start(); var k = 0; for (int i = 0; i < 20000; i++) { for (int j = 0; j < leds.Length; j++) { leds[j].Write(k == j); } if (k++ >= leds.Length) k = 0; led4.Value = i; Thread.Sleep(50); } led4.Stop();
萬事俱備!把程序?qū)懭胩剿髡咭惶?hào)板子,重啟后,數(shù)碼管在數(shù)數(shù),10個(gè)小燈從左到右依次亮過去。
使用其它板子的同學(xué),自己選好針腳,稍微修改控制代碼即可!針腳不夠可分開實(shí)驗(yàn)!
歡迎感興趣的同學(xué)一起學(xué)習(xí)研究!頁頭有群號(hào)和論壇資料地址。
如果您認(rèn)為本文對(duì)你有用,麻煩給個(gè)推薦,謝謝!o(∩_∩)o
如果您覺得本文很爛,有誤導(dǎo)他人的嫌疑,還請(qǐng)一定給個(gè)反對(duì),給出您尊貴的意見,我們一定改進(jìn)!謝謝!


浙公網(wǎng)安備 33010602011771號(hào)