?導(dǎo)讀:《藍(lán)橋杯單片機(jī)組》專(zhuān)欄文章是博主2018年參加藍(lán)橋杯的單片機(jī)組比賽所做的學(xué)習(xí)筆記,在當(dāng)年的比賽中,博主是獲得了省賽一等獎(jiǎng),國(guó)賽二等獎(jiǎng)的成績(jī)。成績(jī)雖談不上最好,但至少問(wèn)心無(wú)愧。如今2021年回頭再看該系列文章,仍然感觸頗多。為了能更好地幫助到單片機(jī)初學(xué)者,今年特地抽出時(shí)間對(duì)當(dāng)年的文章邏輯和結(jié)構(gòu)進(jìn)行重構(gòu),以達(dá)到初學(xué)者快速上手的目的。需要指出的是,由于本人水平有限,如有錯(cuò)誤還請(qǐng)讀者指出,非常感謝。那么,接下來(lái)讓我們一起開(kāi)始愉快的學(xué)習(xí)吧。
在上一節(jié)我們以 LED 流水燈為例,再次熟悉了 CT107D 外設(shè)驅(qū)動(dòng)套路。本節(jié)我們將繼續(xù)深入 CT107D 的數(shù)碼管外設(shè)。相比來(lái)說(shuō),數(shù)碼管模塊較 LED 模塊有一定的難度,不過(guò)都是紙老虎,只要各位肯認(rèn)真鉆研!
一、數(shù)碼管外設(shè)基本知識(shí)復(fù)習(xí)
正式開(kāi)始之前,我們?cè)賮?lái)復(fù)習(xí)一下CT107D的常用外設(shè)地址圖。千萬(wàn)不要嫌煩,你要知道基礎(chǔ)不牢,地動(dòng)山搖!永遠(yuǎn)不要愧疚自己把時(shí)間浪費(fèi)在簡(jiǎn)單的事情上!
前面我們總結(jié)了CT107D的常用外設(shè)地址圖,如下所示:
CT107D的常用外設(shè)地址圖
本節(jié)我們使用的到的是數(shù)碼管模塊,它的位選地址是:0xC000
(Y6C),它的段選地址是:0xE000
(Y7C)。
Q:這里簡(jiǎn)單解釋下什么是位選,什么是段選?
假設(shè)我們有4個(gè)數(shù)碼管,我想讓某1個(gè)數(shù)碼管顯示對(duì)應(yīng)的數(shù)字,我就需要先選中它,選中這個(gè)數(shù)碼管的過(guò)程就叫位選;一般單個(gè)數(shù)碼管都是8段(帶小數(shù)點(diǎn)),我們可以通過(guò)控制某1段是否點(diǎn)亮來(lái)顯示不同的內(nèi)容,比如顯示1還是顯示2,那么單個(gè)數(shù)碼管里各個(gè)段選中的過(guò)程就叫段選。
需要特別提醒的是:CT107D 中的數(shù)碼管都是 共陽(yáng) 的數(shù)碼管。
二、數(shù)碼管顯示實(shí)驗(yàn)
2.1、數(shù)碼管靜態(tài)顯示
程序功能:關(guān)閉蜂鳴器和繼電器,控制單個(gè)數(shù)碼管顯示數(shù)字1。
#include <stc15.h>
sbit Buzz = P0^6;
sbit Relay = P0^4;
void main()
{
P2 = ((P2&0x1F)|0xA0); //使能573
Buzz = 0; //關(guān)閉蜂鳴器
Relay = 0; //關(guān)閉繼電器
P2 = P2&0x1F; //禁止573
P2 = ((P2&0x1F)|0xC0); //位選
P0 = 0x01; //選中com1
P2 = P2&0x1F;
P2 = ((P2&0x1F)|0xE0); //段選
P0 = 0xF9; //對(duì)應(yīng)數(shù)字1
P2 = P2&0x1F;
while(1);
}
實(shí)驗(yàn)說(shuō)明:
1、該實(shí)驗(yàn)整體難度不大,但是理解好什么是段選,什么是位選。
2、共陰共陽(yáng)對(duì)應(yīng)不同的字符轉(zhuǎn)換表,CT107D 是共陽(yáng)數(shù)碼管,其對(duì)應(yīng)《0-F》的字符轉(zhuǎn)換表如下:
3、注意573的打開(kāi)關(guān)閉,要養(yǎng)成良好的習(xí)慣,當(dāng)需要操作外設(shè)時(shí),打開(kāi)對(duì)應(yīng)的573,操作做完畢后一定要關(guān)閉對(duì)應(yīng)的573。養(yǎng)成良好的編程規(guī)范,后續(xù)會(huì)少很多不必要的麻煩。
2.2、數(shù)碼管動(dòng)態(tài)顯示
程序功能:?jiǎn)蝹€(gè)數(shù)碼管動(dòng)態(tài)顯示 1 到 F。
#include <stc15.h>
typedef unsigned char u8;
typedef unsigned int u16;
u8 code LedChar[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
void CloseFucker();
void main()
{
u16 i;
u8 cnt=0;
CloseFucker();
P2 = ((P2&0x1F)|0xC0);//位選
P0 = 0x01;//選中第一個(gè)數(shù)碼管
P2 = P2 & 0x1F;
while(1)
{
P2 = ((P2&0x1F)|0xE0);
P0 = LedChar[cnt++];
if(cnt > 0x0F)
cnt = 0x00;
P2 = P2 & 0x1F;
for(i=0; i<60000; i++);
}
}
void CloseFucker()
{
P2 = (P2 & 0x1F) | 0x80;
P0 = 0xFF;
P2 = P2 & 0x1F;
P2 = (P2 & 0x1F) | 0xA0;
P0 = P0 & 0xAF;
P2 = P2 & 0x1F;
}
實(shí)驗(yàn)說(shuō)明:
1、我們將數(shù)碼管的字符轉(zhuǎn)換表寫(xiě)到了數(shù)組 LedChar 中,那么 LedChar[0] 對(duì)應(yīng)數(shù)字 0 的字符轉(zhuǎn)換碼,同理 LedChar[1] 對(duì)應(yīng)數(shù)字 1 的字符轉(zhuǎn)換碼....
2、本實(shí)驗(yàn)還是通過(guò) for 循環(huán),來(lái)實(shí)現(xiàn)軟件延時(shí)一段時(shí)間的作用,具體延時(shí)時(shí)間未知。
2.3、拔高實(shí)驗(yàn):1s 計(jì)數(shù)器
本程序?yàn)閿?shù)碼管練習(xí)拔高實(shí)驗(yàn),建議等學(xué)習(xí)完中斷后再來(lái)理解本程序!放在這里,僅僅供學(xué)有余力的同學(xué)繼續(xù)深入研究。
難點(diǎn):高位的0不顯示!
程序功能:高位為0則不顯示,從0~9999999利用定時(shí)器進(jìn)行1s計(jì)數(shù)。
#include <stc15.h>
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
u8 T0RH;
u8 T0RL;
bit flag1s = 0;
u8 code LedChar[] = { //數(shù)碼管顯示字符轉(zhuǎn)換表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
u8 LedBuff[] = { //數(shù)碼管緩沖區(qū),確保上電時(shí)都不亮。
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
void CloseFucker();
void ConfigTimer0(u16 ms);
void ShowNumber(u32 dat);
void main()
{
u32 sec = 0;
CloseFucker();
ConfigTimer0(1);
EA = 1;
ShowNumber(0);
while(1)
{
if(flag1s)
{
flag1s = 0;
sec++;
if(sec >= 99999999)
sec = 0;
ShowNumber(sec);
}
}
}
void ShowNumber(u32 dat)
{
char i;
u8 buf[8];
for(i=0; i<8; i++)
{
buf[i] = dat % 10;
dat /= 10;
}
for(i=7; i>0; i--)
{
if(buf[i] == 0)
LedBuff[i] = 0xFF;
else
break;
}
for( ; i>=0; i--)
{
LedBuff[i] = LedChar[buf[i]];
}
}
void CloseFucker()
{
P2 = (P2 & 0x1F) | 0x80;
P0 = 0xFF;
P2 = P2 & 0x1F;
P2 = (P2 & 0x1F) | 0xA0;
P0 = P0 & 0xAF;
P2 = P2 & 0x1F;
}
void ConfigTimer0(u16 ms)
{
u32 tmp;
tmp = 11059200 / 12;
tmp = (tmp * ms) / 1000;
tmp = 65536 - tmp;
T0RH = (u8)(tmp >> 8);
T0RL = (u8)tmp;
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;
TR0 = 1;
}
void LedScan()
{
static u8 index = 0;
P2 = (P2 & 0x1F) | 0xE0;//消隱(在位選前關(guān)閉所有段選)
P0 = 0xFF;
P2 = P2 & 0x1F;
P2 = (P2 & 0x1F) | 0xC0;
P0 = 0x80 >> index;
P2 = P2 & 0x1F;
P2 = (P2 & 0x1F) | 0xE0;
P0 = LedBuff[index];
P2 = P2 & 0x1F;
if(index < 7)
index++;
else
index = 0;
//當(dāng)然還可以像下面這樣寫(xiě)!
// index++;
// index &= 0x07;
}
void interruptTimer0() interrupt 1
{
static u16 tmr1s = 0;
TH0 = T0RH;
TL0 = T0RL;
LedScan();
tmr1s++;
if(tmr1s >= 1000)
{
tmr1s = 0;
flag1s = 1;
}
}
簡(jiǎn)單說(shuō)明:
1、注意數(shù)碼管顯示表 LedChar 和數(shù)碼管緩沖區(qū) LedBuff 的配合使用,以及深入理解這種編程思想。如果目前理解不到位也沒(méi)關(guān)系,照著用。
2、本程序中模塊化編程思想也初步體現(xiàn),仔細(xì)體會(huì)!
3、ShowNumber函數(shù)
這里先不做過(guò)多講解,可以自己先好好研究研究!
三、總結(jié)
本節(jié)主要介紹了三個(gè)實(shí)驗(yàn),重點(diǎn)是前兩個(gè)實(shí)驗(yàn)。如果第三個(gè)實(shí)驗(yàn)《1s 計(jì)數(shù)器》暫時(shí)理解不了或者理解困難,可以先放一放,待繼續(xù)深入中斷后不妨再回頭看,那時(shí)便柳暗花明。另外,學(xué)習(xí)過(guò)程免不了重復(fù),但是還是想跟大家強(qiáng)調(diào)一下,初學(xué)者一定不要怕麻煩,一定不要怕重復(fù)!最后再送大家一句話(huà):
簡(jiǎn)單的事情重復(fù)做,你就是專(zhuān)家。 重復(fù)的事情用心做,你就是贏家。
共勉~
小結(jié):本篇文章主要從數(shù)碼管的靜態(tài)和動(dòng)態(tài)兩個(gè)實(shí)驗(yàn)展開(kāi),詳細(xì)介紹了 CT107D 的數(shù)碼管外設(shè)使用,從中不難看出數(shù)碼管的驅(qū)動(dòng)方式較 LED 驅(qū)動(dòng)方式還是有些區(qū)別的,不過(guò)并不算復(fù)雜,仔細(xì)研究也是可以理解到位的。另外一個(gè)需要強(qiáng)調(diào)的是對(duì)于比賽,建議大家一定要熟背共陽(yáng)數(shù)碼管的字符轉(zhuǎn)換表。
希望大家多多支持我的原創(chuàng)文章。如有錯(cuò)誤,請(qǐng)大家及時(shí)指正,非常感謝。