點陣之路_STC15微控制器+8*16點陣+DS1302時鐘
資源共享一下,8*16點陣系列的程式,自己參考例程寫的,不太完善還望多多包涵!
對應此文章應該是檔名字為下圖的資料夾!
硬體介紹
1. 主控: STC15F2K60S2
2. 點陣: 1588BR 8*8點陣 經過測試發現型號為 788BS的8*8的點陣引腳也如下,可以通用,只是大小不一樣!
3. 放大電路:S8050 NPN三極體,5.1k電阻,470Ω電阻。
4. 時鐘電路:DS1302 時鐘模組
實物圖片
1. 主控,STC-15最小系統板
2. DS1302時鐘模組
3. 點陣顯示及放大部分(之前陽極用的1k的電阻,驅動電流有點大,三極體也燙手,導致點陣燒了很多LED,後來換成5.1k電阻,三極體也不那麼燙了,這個只是初步的,後邊在做點陣的話,可以用74HC138,或者TM1640(這個晶片特別好用),然後現在就想從基礎的開始,自己焊接一下電路板什麼的。)
4. 整體的,時間顯示
5. 整體的,日期顯示
6. 整體的,星期顯示
7. 整體的,年份顯示
設計思路
- 程式簡單電路圖
大概的電路簡圖就是這個樣子的,但是有一點需要特別注意
整個點陣模組的供電不要接在微控制器最小系統上,需要另外接5V,或者3.3V單獨供電,然後再與最小系統板共地。
如果點陣模組的供電接在最小系統,可能出現的問題是,微控制器重新編譯並下載程式後,可能程式還沒開始執行,但是點陣已經通電了,出現的情況就是,點陣全滅,需要重新給點陣通電才可以,這個時候如果另外接點陣的供電電源即可解決問題!
點陣取模
行:第一塊點陣和第二塊點陣的陽極接一塊,經過S8050,5.1K電阻接微控制器P2口,最上邊第一行是I/O口高位,取模時要注意!
列:兩塊點陣的陰極分別接S8050,5.1K電阻接到微控制器,第一塊接P0口,第二塊接P1口,每塊最左邊均是I/O口最低位!
取模方式如下,不太懂的好好看看取模說明,另外,取模只跟硬體連線有關,一旦硬體確定,取模方式也隨之固定,不會因為程式寫法不一樣而改變取模方式,之前我都把這點搞錯了!
下邊這個是我自己取的模,可以參考一下!想要什麼圖案,都可以自己用軟體去畫,或者直接用字元生成即可。
// 靜態陣列 數字庫 3*8點陣取模 u8 code number_library[] = { 0x7F,0x41,0x7F,/*"0",0*/ 0x21,0x7F,0x01,/*"1",0*/ 0x4F,0x49,0x79,/*"2",0*/ 0x49,0x49,0x7F,/*"3",0*/ 0x78,0x08,0x7F,/*"4",0*/ 0x79,0x49,0x4F,/*"5",0*/ 0x7F,0x49,0x4F,/*"6",0*/ 0x40,0x40,0x7F,/*"7",0*/ 0x7F,0x49,0x7F,/*"8",0*/ 0x79,0x49,0x7F,/*"9",0*/ }; // 靜態陣列 中間冒號字型檔 2*8點陣取模 u8 code sign_library[] = { 0x36,0x36, /* 冒號 */ 0x08,0x08, /* -- */ }; // 靜態陣列 星期陣列庫 10*8點陣取模 u8 code week_library[] = { 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,/*"週一",0*/ 0x04,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x04,/*"週二",0*/ 0x02,0x22,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x22,0x02,/*"週三",0*/ 0x7F,0x41,0x45,0x7D,0x41,0x41,0x7D,0x45,0x41,0x7F,/*"週四",0*/ 0x00,0x21,0x29,0x29,0x3F,0x29,0x29,0x2F,0x21,0x00,/*"週五",0*/ 0x11,0x12,0x14,0x18,0x70,0x70,0x18,0x14,0x12,0x11,/*"週六",0*/ 0x00,0x00,0x7F,0x49,0x49,0x49,0x49,0x7F,0x00,0x00,/*"週日",0*/ };
程式
簡單的對程式進行一個介紹,裡邊很多註釋,應該可以看的懂。
- 程式變數,陣列定義
disbuffer_sign :是一個標誌位,當標誌位為0/1的時候,改變動態陣列 disbuffer1 裡邊的內容,為1/0,改變 disbuffer2。這樣做的好處是,當前正用於顯示的 陣列 不會在顯示的時候被改變陣列內容。防止亂碼出現,比如正在顯示 disbuffer1 數組裡邊的內容,就改變 disbuffer2 數組裡邊的內容。
u8 *disbuffer :全域性指標,用來代替陣列 disbuffer1,disbuffer2裡邊的內容。
如下圖,在顯示結束後,不要忘記把標誌位取反!
disbuffer1,disbuffer2 :就是兩個動態陣列,用來儲存要顯示的內容。
table :用來存放行重新整理的資料,對應的P2口,由於是第一行是最高位,所以第一行顯示就是0x80,一次類推!
- 延時函式
- 定時器0初始化函式
要用到定時器0中斷,每隔5ms,或者1ms進中斷重新整理一次顯示函式,這樣就不用每次寫的時候就要考慮加顯示函數了!
- I/O口初始化函式
- 顯示重新整理函式
這個函式也是放在中斷處理裡邊的 顯示重新整理函式。
u8 *disbuff :定義在顯示函式內部的區域性指標變數,只用在此函式內,避免與全域性指標變數重複,作用一樣!
column1,column2是兩塊點陣的列全部清0,相當於初始化。
k1到k16則是16列,每一列對應 disbuff 數組裡邊的 模值,disbuff[0] & table[hang],則應該是行重新整理的,只有在選中此行的時候才顯示對應行的碼值,如果不雨一下的話,可能出現亂碼,可以自己嘗試一下,反正是程式碼除錯嘛,不在乎多下載幾次。如果不知道為啥取模取出來是這個樣子的話,你可以把每一列單獨看作一個8位的二進位制,把取出來的十六進位制數代入進去,在對應一下顯示出來的內容,應該就可以理解為啥這個模是這個樣子的。
row = table[hang] :則是顯示行重新整理,然後 hang++,然後如果 hang > 7 的話,初始化為0,因為只有8行,這個可以根據實際需要更改!
- DS1302 顯示(時間,日期,星期,年份都類似)
/************************* DS1302時鐘 時間顯示 *************************************/
void Time_DS1302()
{
if(disbuffer_sign == 1)
disbuffer = disbuffer1;
else
disbuffer = disbuffer2;
// 時 十位 列 1 2 3
disbuffer[0] = number_library[(TIME[2]/16) * 3];
disbuffer[1] = number_library[(TIME[2]/16) * 3 + 1];
disbuffer[2] = number_library[(TIME[2]/16) * 3 + 2];
disbuffer[3] = 0x00;
// 時 個位 列 1 2 3
disbuffer[4] = number_library[(TIME[2]%16) * 3];
disbuffer[5] = number_library[(TIME[2]%16) * 3 + 1];
disbuffer[6] = number_library[(TIME[2]%16) * 3 + 2];
// 分 十位 列 1 2 3
disbuffer[9] = number_library[(TIME[1]/16) * 3];
disbuffer[10] = number_library[(TIME[1]/16) * 3 + 1];
disbuffer[11] = number_library[(TIME[1]/16) * 3 + 2];
disbuffer[12] = 0x00;
// 分 個位 列 1 2 3
disbuffer[13] = number_library[(TIME[1]%16) * 3];
disbuffer[14] = number_library[(TIME[1]%16) * 3 + 1];
disbuffer[15] = number_library[(TIME[1]%16) * 3 + 2];
// 冒號 列 1 2
if((TIME[0]%16) % 2)
{
if(disbuffer_sign == 1)
disbuffer = disbuffer1;
else
disbuffer = disbuffer2;
disbuffer[7] = sign_library[0];
disbuffer[8] = sign_library[1];
disbuffer_sign = ~disbuffer_sign;
delay(60000);
delay(60000);
}
else
{
if(disbuffer_sign == 1)
disbuffer = disbuffer1;
else
disbuffer = disbuffer2;
disbuffer[7] = 0x00;
disbuffer[8] = 0x00;
disbuffer_sign = ~disbuffer_sign;
delay(60000);
delay(60000);
}
// // 秒 測試
// disbuffer[9] = number_library[(TIME[0]/16) * 3];
// disbuffer[10] = number_library[(TIME[0]/16) * 3 + 1];
// disbuffer[11] = number_library[(TIME[0]/16) * 3 + 2];
//
// disbuffer[13] = number_library[(TIME[0]%16) * 3];
// disbuffer[14] = number_library[(TIME[0]%16) * 3 + 1];
// disbuffer[15] = number_library[(TIME[0]%16) * 3 + 2];
disbuffer_sign = ~disbuffer_sign;
}
一共16列,每一列都送進去對應得數,另外要注意,DS1302模組的陣列,TIME[ ],裡邊是BCD碼,轉換成十進位制數,十位要除以16,個位要求餘16!然後比如初始化的秒是 0x23,則要注意這個是十六進位制的數,加滿16才進一,因此求餘16得到的是最後一個3,對應陣列顯示的字元碼應該是第9個到第11個,因此要 *3 ,然後依次加1加2。同理除以16得到的是第一個數2,對應的陣列從6開始,到8結束!
中間第7 8 列,冒號跟隨秒數的變化而閃爍,當秒的個位為奇數的時候,冒號亮,偶數的時候滅,在這個裡邊也要分別更改兩個動態陣列,標誌位也要隨之取反。
這個上邊註釋掉的是對秒顯示的一個測試,可以不用寫這麼多,可以直接在要顯示的對應位置上,把TIME[ ]陣列取出來的數改到秒就好!
- 主函式
首先是對 I/O 口的初始化,定時器0的初始化,DS1302的初始化,接著在while迴圈裡邊,每次要先讀取DS1302裡邊的時間,然後讓時間顯示30秒,日期顯示10秒,星期顯示10秒,年份顯示10秒, 來回切換即可!
- 定時器0中斷
P2 也就是行,是對點陣的一個消隱程式,接著就是隻有一個顯示函式,每次定時滿多長時間,就進來執行一次顯示函式!
附錄總結
1. 程式程式碼還可以優化一下,另外之前試過列重新整理,沒有行重新整理顯示的快,因此還有很多寫程式碼的方式可以改進。
2. 一定不要讓點陣的電流過大,我這個點陣就是燒壞了很多LED,需要控制一下電流大小!
3. 切換方式有點單一,後續學習過後會繼續改進!
4. 有錯誤的地方還望指點,有不懂的也可以評論問我哦!