標(biāo)題: 第4章 C語(yǔ)言基礎(chǔ)以及流水燈的實(shí)現(xiàn) [打印本頁(yè)]

作者: admin    時(shí)間: 2013-6-6 06:42
標(biāo)題: 第4章 C語(yǔ)言基礎(chǔ)以及流水燈的實(shí)現(xiàn)

C語(yǔ)言,沒(méi)接觸過(guò)計(jì)算機(jī)編程語(yǔ)言的人會(huì)把它看的很神秘,感覺(jué)非常的難,而在我看來(lái),C語(yǔ)言的邏輯和運(yùn)算,就是小學(xué)水平,所以大家不要怕它,我盡可能的從小學(xué)數(shù)學(xué)邏輯方式帶著大家學(xué)習(xí)C語(yǔ)言。

1.1 二進(jìn)制、十進(jìn)制和十六進(jìn)制

看似很簡(jiǎn)單的東西,但是從同學(xué)們學(xué)習(xí)視頻的情況來(lái)看,很多同學(xué)不能徹底明白。這里先簡(jiǎn)單介紹一些注意事項(xiàng),然后還是從實(shí)驗(yàn)中講解會(huì)比較深刻。

1、十進(jìn)制就不多說(shuō)了,逢十進(jìn)位,一個(gè)位有十個(gè)值:09,我們的生活中到處都是它的身影。二進(jìn)制就是逢二進(jìn)位,它的一個(gè)位只有兩個(gè)值:01,但它卻實(shí)現(xiàn)計(jì)算機(jī)系統(tǒng)的最基本的理論基礎(chǔ),計(jì)算機(jī)(包括單片機(jī))芯片是基于成萬(wàn)上億個(gè)的開(kāi)關(guān)管組合而成的,他們每一個(gè)都只能有開(kāi)和關(guān)兩種狀態(tài),再難找出第三個(gè)狀態(tài)了(不要辯解半開(kāi)半關(guān)這個(gè)狀態(tài),它是不穩(wěn)定態(tài),是極力避免的),所以他們只能對(duì)應(yīng)于二進(jìn)制的10兩個(gè)值,而沒(méi)有2、34……,理解二進(jìn)制對(duì)于理解計(jì)算機(jī)的本質(zhì)很有幫助。書(shū)寫(xiě)二進(jìn)制數(shù)據(jù)時(shí)需加前綴0b,每一位的值只能是01。十六進(jìn)制就是把4個(gè)二進(jìn)制位組合為一位來(lái)表示,于是它的每一位有0b00000b111116個(gè)值,用09再加上AF(或af)表示,那么它自然就是逢十六進(jìn)位了,它本質(zhì)上同二進(jìn)制是一樣的,是二進(jìn)制的一種縮寫(xiě)形式,也是我們程序編寫(xiě)中常用的形式。書(shū)寫(xiě)十六進(jìn)制數(shù)據(jù)時(shí)需加前綴0x,下表是三種進(jìn)制之間的對(duì)應(yīng)關(guān)系。

十進(jìn)制

二進(jìn)制

十六進(jìn)制

0

0b0

0x00

1

0b1

0x01

2

0b10

0x02

3

0b11

0x03

4

0b100

0x04

5

0b101

0x05

6

0b110

0x06

7

0b111

0x07

8

0b1000

0x08

9

0b1001

0x09

10

0b1010

0x0A

11

0b1011

0x0B

12

0b1100

0x0C

13

0b1101

0x0D

14

0b1110

0x0E

15

0b1111

0x0F

16

0b10000

0x10

17

0b10001

0x11

18

0b10010

0x12

……

……

……

4-1 進(jìn)制轉(zhuǎn)換

2、對(duì)于二進(jìn)制來(lái)說(shuō),8位二進(jìn)制我們稱(chēng)之為一個(gè)字節(jié),二進(jìn)制的表達(dá)范圍值是從0b000000000b11111111,而我們程序中用十六進(jìn)制表示的時(shí)候就是從0x000xFF,這里教大家一個(gè)二進(jìn)制轉(zhuǎn)換十進(jìn)制和十六進(jìn)制的方法,二進(jìn)制4位一組,遵循8,4,2,1的規(guī)律比如 1010,那么從最高位開(kāi)始算,數(shù)字大小是8*1+4*0+2*1+1*0 = 10,那么十進(jìn)制就是10,十六進(jìn)制就是0xA。尤其二進(jìn)制轉(zhuǎn)十六進(jìn)制的時(shí)候,十六進(jìn)制一位剛好是和二進(jìn)制的4位相互對(duì)應(yīng)的,這些大家不需要強(qiáng)行記憶,用幾次就熟練了。

3、對(duì)于進(jìn)制來(lái)說(shuō),只是數(shù)據(jù)的表現(xiàn)形式,而數(shù)據(jù)的大小不會(huì)因?yàn)檫M(jìn)制表現(xiàn)形式不同而不同,比如二進(jìn)制的0b1、十進(jìn)制的1、十六進(jìn)制的0x01,他們本質(zhì)上數(shù)值大小相等的同一個(gè)數(shù)據(jù)。我們?cè)谶M(jìn)行C語(yǔ)言編程的時(shí)候,我們只寫(xiě)十進(jìn)制和十六進(jìn)制,那么不帶0x的就是十進(jìn)制,帶了0x符號(hào)的就是十六進(jìn)制。

1.2 C語(yǔ)言變量類(lèi)型和范圍

什么是變量?變量自然和常量是相對(duì)的。常量比如是1、2、3......等固定的數(shù)字,而量,和我們小學(xué)學(xué)的x是一個(gè)概念,我們可以讓1,也可以讓2,我們想讓是幾是我們程序說(shuō)算的。

那么我們小學(xué)學(xué)的數(shù)學(xué)里邊,有這么幾類(lèi),正數(shù)、負(fù)數(shù)、整數(shù)和小數(shù)。在C語(yǔ)言里,名字和我們數(shù)學(xué)里學(xué)的不一樣外,還對(duì)數(shù)據(jù)大小進(jìn)行了限制。這個(gè)地方有一點(diǎn)復(fù)雜的是,在C51里邊的數(shù)據(jù)范圍和其他編程環(huán)境還不完全一樣,因此我們下邊的這個(gè)圖,僅僅代表的是C51,其他編程環(huán)境可能不一樣,大家知道有這回事就可以了。C語(yǔ)言的關(guān)鍵字較多語(yǔ)法有些嚴(yán)謹(jǐn)我們不必死記硬背只需理解了,用的時(shí)候再打開(kāi)http://www.torrancerestoration.com/mcuteach/150.html 查閱即可.

C語(yǔ)言的數(shù)據(jù)基本類(lèi)型分為整型、字符型以及浮點(diǎn)型,如圖4-1

圖片

4-1 C語(yǔ)言數(shù)據(jù)類(lèi)型

4-1中,三種基本類(lèi)型,每個(gè)基本類(lèi)型又包含了兩個(gè)類(lèi)型。其中字符型和整型,除了有一定的數(shù)據(jù)大小范圍之外,只能表達(dá)整數(shù)。而unsigned型的又只能表達(dá)正數(shù),要表達(dá)負(fù)數(shù)必須用signed型,編寫(xiě)程序的時(shí)候,signed可以省略。表達(dá)小數(shù),必須用浮點(diǎn)型。

比如上節(jié)課最后給的閃爍小燈的程序,我們用的是unsigned int i = 0;這個(gè)地方i的范圍就是0~65535,我們for語(yǔ)句的寫(xiě)法,如果那個(gè)30000改成70000的話(huà),for(i=0;i<70000;i++);大家會(huì)發(fā)現(xiàn)小燈會(huì)一直亮,而不是閃爍了,那理解這個(gè)問(wèn)題,當(dāng)然我們要來(lái)了解for語(yǔ)句的用法了。 

這里有一個(gè)編程宗旨,就是能用小不用大。就是說(shuō)定義能用1個(gè)字題的,就不定義成int,一方面節(jié)省RAM空間可以其他變量或者中間運(yùn)算過(guò)程使用,另外一方面,占空間小程序運(yùn)算速度也快一些。

1.3 for循環(huán)語(yǔ)句

for語(yǔ)句是我們今后編程的一個(gè)常用的語(yǔ)句,這個(gè)語(yǔ)句必須得學(xué)會(huì)其用法,他不僅僅可以用來(lái)做延時(shí),還可以用來(lái)做一些循環(huán)運(yùn)算。for語(yǔ)句的一般形式如下:

        for(表達(dá)式1表達(dá)式2表達(dá)式3)

        (需要執(zhí)行的語(yǔ)句);

其執(zhí)行過(guò)程是表達(dá)式1首先執(zhí)行且只執(zhí)行一次;然后執(zhí)行表達(dá)式2,通常都是一個(gè)用于判定條件的表達(dá)式,如果表達(dá)式2條件成立,就執(zhí)行(需要執(zhí)行的語(yǔ)句);然后再執(zhí)行表達(dá)式3;再判斷表達(dá)式2,再執(zhí)行表達(dá)式3.....一直到表達(dá)式2不成立時(shí),跳出循環(huán)往下執(zhí)行。舉個(gè)例子:

    for(i = 0i<2; i++)  

        j++;

這里有一個(gè)符號(hào)++,這個(gè)符號(hào)表示加1的意思。假如j最開(kāi)始初值是0,首先執(zhí)行表達(dá)式1i=0,然后判斷i小于2這個(gè)條件成立,就執(zhí)行一次j++,j的值就是1了,然后經(jīng)過(guò)表達(dá)式3后,i的值也變成1了,再判斷條件2,還是符合,j再加一次,j變成2了,表達(dá)式3i也變成2了,再判斷條件2,發(fā)現(xiàn)2<2這個(gè)條件不成立了,所以就不會(huì)再執(zhí)行j++這個(gè)語(yǔ)句了。所以執(zhí)行完畢后,j的值就是2。   

for語(yǔ)句除了這種標(biāo)準(zhǔn)用法,還有幾種特殊用法,我們上節(jié)課的閃爍小燈對(duì)for語(yǔ)句的用法for(i=0; i<30000; i++) ;我們沒(méi)有加(需要執(zhí)行的語(yǔ)句),沒(méi)有加的話(huà),就是什么都不操作。但是什么都不操作的話(huà),我們這個(gè)for語(yǔ)句循環(huán)判斷了30000次,程序執(zhí)行是會(huì)用掉時(shí)間的,所以就起到了延時(shí)的作用。比如我們把30000改成20000,會(huì)發(fā)現(xiàn)燈的閃爍速度加快了,因?yàn)槲覀冄訒r(shí)時(shí)間短了,當(dāng)然,我們?cè)摮?/font>40000后會(huì)發(fā)現(xiàn),閃爍慢了。但是有一點(diǎn)特別注意,C語(yǔ)言的延時(shí)時(shí)間是不能通過(guò)程序看出來(lái)的,也不會(huì)成比例,比如假如我們這個(gè)for循環(huán)里邊的表達(dá)式2使用30000的時(shí)候延時(shí)3秒的話(huà),那么延時(shí)40000的時(shí)候,可能不會(huì)是4秒,那如何看實(shí)際延時(shí)時(shí)間呢,一會(huì)我再教大家。

還有一種寫(xiě)法for( ; ; ),這樣寫(xiě)后,這個(gè)for循環(huán)就變成了死循環(huán)了,就不停的執(zhí)行(需要執(zhí)行的語(yǔ)句),和我們前邊講的while(1)的意思是一樣的。那while這個(gè)語(yǔ)法是如何用的呢?

1.4 while循環(huán)語(yǔ)句

在我們單片機(jī)C語(yǔ)言編程的時(shí)候,每個(gè)程序我們都會(huì)固定的加一句while(1),這條語(yǔ)句就可以起到死循環(huán)的作用。對(duì)于while語(yǔ)句來(lái)說(shuō),他的一般形式是:

     While (表達(dá)式)

     {

        循環(huán)體語(yǔ)句;

     } 

C語(yǔ)言里,通常表達(dá)式符合條件,我們叫做真,不符合條件,叫做假。比如前邊i<30000,當(dāng)i等于0的時(shí)候,那這個(gè)條件成立,就是真,如果i大于30000的時(shí)候,條件不成立,叫做假。

while(表達(dá)式)這個(gè)括號(hào)里的表達(dá)式,為真的時(shí)候,就會(huì)執(zhí)行循環(huán)體語(yǔ)句,當(dāng)為假的時(shí)候,就不執(zhí)行。在這里先不舉例,后邊遇到時(shí)再詳細(xì)說(shuō)。

還有另外一種情況,就是我們C語(yǔ)言里邊,除了表達(dá)式外,還有常數(shù),習(xí)慣上,我們非0的常數(shù)都認(rèn)為是真,只有0認(rèn)為是假,所以我們程序中加了while(1),這個(gè)數(shù)字1,可以改成2,3,4......等等都可以,都是一個(gè)死循環(huán),不停的執(zhí)行循環(huán)體的語(yǔ)句,但是如果把這個(gè)數(shù)字改成0,那么就不會(huì)執(zhí)行循環(huán)體的語(yǔ)句了。

1.5 函數(shù)的簡(jiǎn)單介紹

函數(shù)定義的一般形式如下:

    函數(shù)值類(lèi)型  函數(shù)名 (形式參數(shù)列表)

    {

        函數(shù)體

    }

1、函數(shù)值類(lèi)型,就是函數(shù)返回值的類(lèi)型。在我們后邊程序使用中,會(huì)有很多函數(shù)中有return x這個(gè)東西,這個(gè)返回值也就是函數(shù)本身的類(lèi)型。還有一種情況,就是這個(gè)函數(shù)只執(zhí)行操作,不需要返回任何值,那么這個(gè)時(shí)候的類(lèi)型就是空類(lèi)型void,這個(gè)void按道理來(lái)說(shuō)是可以省略的,但是一旦省略,Keil軟件會(huì)報(bào)一個(gè)警告,所以我們通常也不省。

2、函數(shù)名?梢允侨魏魏戏ǖ臉(biāo)示符,但是不能與其他函數(shù)或者變量重名,也不能是關(guān)鍵字。什么是關(guān)鍵字,后邊我們慢慢接觸,比如char這類(lèi),都是關(guān)鍵字,是我們程序中具備特殊功能的標(biāo)志符,這種東西不可以命名函數(shù)。

3、形式參數(shù)列表,我們也叫做形參,這個(gè)是函數(shù)調(diào)用的時(shí)候,相互傳遞數(shù)據(jù)用的。有的函數(shù),我們不需要傳遞參數(shù),那么可以用void來(lái)替代,void同樣可以省略,但是那個(gè)括號(hào)是不能省略的。

4、函數(shù)體。函數(shù)體包含了聲明語(yǔ)句部分和執(zhí)行語(yǔ)句部分。聲明語(yǔ)句部分主要用于聲明函數(shù)內(nèi)部所使用的變量,執(zhí)行語(yǔ)句部分主要是一些函數(shù)需要執(zhí)行的語(yǔ)句。特別注意,所有的聲明語(yǔ)句部分必須放在執(zhí)行語(yǔ)句之前,否則編譯的時(shí)候會(huì)報(bào)錯(cuò)。

5、一個(gè)工程文件必須有且僅能有一個(gè)main函數(shù),程序執(zhí)行的時(shí)候,都是從main函數(shù)開(kāi)始的。

6、關(guān)于形參和實(shí)參的概念,我們后邊再總結(jié),如果遇到程序里有,大家再跟著抄一段時(shí)間。先用,后講解,這樣更有利于理解。

我們?cè)賮?lái)回顧一下我們上節(jié)課閃爍LED程序部分

void  main()               //void即函數(shù)類(lèi)型          

{

    unsigned int i = 0;   //定義一個(gè)無(wú)符號(hào)整數(shù)i,變量范圍是0~65535

                            //并且賦一個(gè)初值0

    ENLED = 0;             //先定義變量i,后寫(xiě)執(zhí)行部分

    ADDR0 = 0;

    ADDR1 = 1;

    ADDR2 = 1;

    ADDR3 = 1;             //74HC138開(kāi)啟三極管   

 

    while(1)               //程序死循環(huán)  

    {

           LED = 0;                //點(diǎn)亮小燈

           for(i=0;i<30000;i++);  //for延時(shí)操作

           LED = 1;                //熄滅小燈

           for(i=0;i<30000;i++);  //for延時(shí)操作

    }

}

1.6 Keil軟件延時(shí)

    C語(yǔ)言常用的延時(shí)辦法,有以下4

 

圖片

 

 

4-2 C語(yǔ)言延時(shí)

4-2是我們編程語(yǔ)言常用的4種延時(shí)方法,其中兩種非精確延時(shí),兩種精確一些的延時(shí)。for語(yǔ)句和while語(yǔ)句都可以通過(guò)改變i的范圍值來(lái)改變延時(shí)時(shí)間,但是C語(yǔ)言的時(shí)間都是不能通過(guò)程序看出來(lái)的。

精確延時(shí)有兩個(gè)方法,一個(gè)方法是用定時(shí)器來(lái)延時(shí),這個(gè)方法我們后邊課程要詳細(xì)介紹,定時(shí)器是單片機(jī)的一個(gè)重點(diǎn)。另外一個(gè)就是用庫(kù)函數(shù)_nop_();,一個(gè)NOP的時(shí)間是一個(gè)機(jī)器周期的時(shí)間,這個(gè)后邊也要介紹。

非精確延時(shí),只是在我們做一些簡(jiǎn)單的比如小燈閃爍,流水燈等簡(jiǎn)單實(shí)驗(yàn)中使用,而實(shí)際做實(shí)際開(kāi)發(fā)程序中其實(shí)這種非精確延時(shí)用的極少,這里我們只是做演示功能使用。

好了,介紹完了,我們就要實(shí)戰(zhàn)了。上節(jié)課的LED小燈閃爍的程序,我們用的延時(shí)方式是for(i=0;i<30000;i++);大家如果把這里的i改成100,下載進(jìn)入單片機(jī),會(huì)發(fā)現(xiàn)小燈一直亮,而不是閃爍狀態(tài),現(xiàn)在大家都把這個(gè)程序改一下,都改成100,然后下載觀(guān)察一下現(xiàn)象再繼續(xù)。

觀(guān)察完了,毫無(wú)疑問(wèn),實(shí)際現(xiàn)象和我提到的理論是相符合的,這是為什么呢?這里介紹一個(gè)常識(shí)。我們?nèi)说娜庋?/span>對(duì)閃爍的光線(xiàn)有一個(gè)最低分辨能力,通常情況下當(dāng)閃爍的頻率高于50Hz時(shí),我們看到的信號(hào)就是常亮的。延時(shí)的時(shí)間低于20ms的時(shí)候,我們的肉眼是分辨不出來(lái)小燈在閃爍的,可能最多看到的是小燈亮暗稍微變化了一下。要想清楚的看到小燈閃爍,延時(shí)的值必須大一點(diǎn),大到什么程度呢,不同的亮度的燈不完全一樣,大家可以自己做實(shí)驗(yàn)。

那么如何觀(guān)察延時(shí)有多長(zhǎng)時(shí)間呢?大家鼠標(biāo)點(diǎn)KeilProject-->Options for Target Target1,或點(diǎn)Target1右側(cè)圖標(biāo)圖片,進(jìn)入設(shè)置選項(xiàng),如圖4-3所示

圖片

4-3 Options for Target

首先我們打開(kāi)Target這個(gè)選項(xiàng)卡,找到里邊的Xtal(MHz)這個(gè)位置,這是填寫(xiě)我們進(jìn)行模擬時(shí)間的晶振選項(xiàng),從我們?cè)韴D以及板子上都可以看到,我們單片機(jī)所配的晶振是11.0592MHz,所以這個(gè)地方我們要填上11.0592。然后找到Debug這個(gè)選項(xiàng),選擇左側(cè)的Use Simulator,然后點(diǎn)擊最下邊的OK就可以了,如圖4-4所示。

圖片

4-4 Debug配置信息

點(diǎn)擊Debug菜單里的Start/Stop Debug Session,或者鼠標(biāo)點(diǎn)做左側(cè)的這個(gè)Debug圖標(biāo)圖片,會(huì)進(jìn)入一個(gè)新的頁(yè)面,如圖4-5所示。

圖片

4-5 Debug窗口顯示

最左側(cè)那一欄是單片機(jī)的一些寄存器和系統(tǒng)信息,最上邊那一欄是KeilC語(yǔ)言轉(zhuǎn)換成匯編的代碼,下邊就是我們C語(yǔ)言的程序,還有各種窗口都可以打開(kāi),在view菜單可以打開(kāi)或者關(guān)閉我們的各種窗口。這節(jié)課我們只關(guān)心我們需要的窗口,其他窗口用到再說(shuō)。那么有時(shí)候我們覺(jué)得這種分布不是特別的好,所以我們想改變一下窗口分布怎么辦呢?比如Disassembly(匯編)窗口,我們先用鼠標(biāo)拖動(dòng)它,然后中間會(huì)出現(xiàn)一個(gè)方向符號(hào),再用鼠標(biāo)點(diǎn)那個(gè)方向符號(hào),他就給我們分布了,如圖4-6所示。

圖片

4-6 Keil窗口移動(dòng)()

我們點(diǎn)擊最右邊的那個(gè)箭頭,然后窗口變化成如4-7圖所示;蛘呶覀?nèi)绻貌坏絽R編的程序,也可以直接關(guān)掉。

圖片

4-7所示 Keil窗口移動(dòng)()

細(xì)心的同學(xué)會(huì)看到在C語(yǔ)言的程序里有個(gè)黃色的箭頭,這個(gè)箭頭代表的就是這個(gè)程序當(dāng)前運(yùn)行的位置,在這個(gè)Debug里邊,我們可以看到我們的程序運(yùn)行的過(guò)程。在左上角有這三個(gè)圖標(biāo)圖片,第一個(gè)是復(fù)位,點(diǎn)擊一下之后,程序就會(huì)跑到最開(kāi)始的位置運(yùn)行,第二個(gè)圖標(biāo)是全速運(yùn)行圖標(biāo),點(diǎn)擊一下程序就會(huì)全速運(yùn)行跑起來(lái),第三個(gè)圖標(biāo)是停止圖標(biāo),當(dāng)程序全速運(yùn)行跑起來(lái)后,我們可以通過(guò)點(diǎn)擊第三個(gè)圖標(biāo)來(lái)讓程序停止,觀(guān)察程序運(yùn)行到哪里了。點(diǎn)擊一下復(fù)位后,我們會(huì)發(fā)現(xiàn)C語(yǔ)言程序左側(cè)有的灰色或者綠色,有的地方還是保持原來(lái)的白色,我們可以在我們灰色的位置雙擊鼠標(biāo)設(shè)置斷點(diǎn),就是比如程序一共20行,在第十行設(shè)置斷點(diǎn)后,點(diǎn)全速運(yùn)行,程序就會(huì)運(yùn)行到第十行停止,方便我們觀(guān)察運(yùn)行到這個(gè)地方的情況。

同學(xué)們會(huì)發(fā)現(xiàn),有的位置可以設(shè)置斷點(diǎn),有的地方不可以設(shè)置斷點(diǎn),這是為什么呢?Keil軟件本身具備優(yōu)化我們程序的功能,如果大家想在所有的位置設(shè)置斷點(diǎn),可以把優(yōu)化選項(xiàng)設(shè)置到0位置,就是程序不進(jìn)行優(yōu)化。如圖4-8所示。


 
圖片
 

4-8 優(yōu)化選項(xiàng)設(shè)置

這節(jié)課我們重點(diǎn)是看看C語(yǔ)言代碼的運(yùn)行時(shí)間,在最左側(cè)的register那個(gè)框內(nèi),有一個(gè)sec選項(xiàng),這個(gè)選項(xiàng)就是單片機(jī)運(yùn)行時(shí)間的統(tǒng)計(jì)選項(xiàng),大家點(diǎn)一下復(fù)位按鈕,會(huì)發(fā)現(xiàn)這個(gè)sec變成了0,然后我們?cè)?/font>LED = 0; 這一句加一個(gè)斷點(diǎn),在LED = 1;這個(gè)位置加一個(gè)斷點(diǎn),我們點(diǎn)擊全速運(yùn)行按鈕,會(huì)直接停留在LED = 0;我們會(huì)看到我們的時(shí)間變化成0.000197秒,如圖4-9所示。

圖片

4-9 斷點(diǎn)設(shè)置

    我們?cè)冱c(diǎn)一下全速運(yùn)行,會(huì)發(fā)現(xiàn)sec變成了0.07530650秒,那么這樣一個(gè)for循環(huán)的時(shí)間大概有75ms左右,我們也可以通過(guò)改變30000這個(gè)數(shù)字來(lái)改變這個(gè)間隔時(shí)間。當(dāng)然了,大家要注意i的變量范圍,你如果寫(xiě)成了大于65535的值以后,程序就會(huì)一直運(yùn)行不下去了,因?yàn)?font face="Times New Roman">i無(wú)論如何變化,都不會(huì)大于這個(gè)值,如果要大于這個(gè)值正常運(yùn)行,必須改變i定義的類(lèi)型了。后邊如果我們要求看一段程序運(yùn)行多長(zhǎng)時(shí)間,都可以通過(guò)這種方式來(lái)看。

1.7 流水燈程序

我們前邊學(xué)了點(diǎn)亮LED小燈,然后又學(xué)了LED小燈閃爍,下邊我們要進(jìn)一步了解一下如何讓8個(gè)小燈依次一個(gè)一個(gè)點(diǎn)亮,流動(dòng)起來(lái)。

圖片

4-10 LED小燈電路圖

通過(guò)前面的課程,我們可以了解到控制引腳P0.0通過(guò)了74HC245控制DB0,P0.1控制DB1......P0.7控制DB7。我們還學(xué)到一個(gè)字節(jié)是8位,我們?nèi)绻麑?xiě)一個(gè)P0,就代表了P0.0P0.7的共8個(gè)位。比如我們寫(xiě)P0 = 0xFE;轉(zhuǎn)換成二進(jìn)制就是0b11111110,所以點(diǎn)亮LED小燈的程序,實(shí)際上我們可以改成另外一種寫(xiě)法,如下所示。

#include <reg52.h>

sbit  ADDR0 = P1^0;

sbit  ADDR1 = P1^1;

sbit  ADDR2 = P1^2;

sbit  ADDR3 = P1^3;

sbit  ENLED = P1^4;

 

void  main()                

{

    ENLED = 0;

    ADDR0 = 0;

    ADDR1 = 1;

    ADDR2 = 1;

    ADDR3 = 1;            //74HC138開(kāi)啟三極管    

 

    P0 = 0xFE;

    while(1);             //程序停止在這里 

}

通過(guò)上邊這個(gè)程序我們可以看出來(lái),可以通過(guò)P0來(lái)控制所有的8個(gè)LED小燈的亮和滅。我們下邊要進(jìn)行依次亮和滅,怎么辦呢?從這里就可以得到方法了,如果想讓單片機(jī)流水燈流動(dòng)起來(lái),依次要實(shí)現(xiàn)的結(jié)果是:0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F

在我們的C語(yǔ)言當(dāng)中,有一個(gè)移位操作,其中<<代表的是左移,>>代表的是右移。比如a = 0x01 << 1就是的結(jié)果等于0x01左移一位。大家注意,移位都是指二進(jìn)制移位,那么移位完了,本來(lái)在第0位的1移動(dòng)到了第一位上,移動(dòng)完了低位是補(bǔ)0的。所以a的值最終是等于0x02。

還要學(xué)習(xí)另外一個(gè)運(yùn)算符~,這個(gè)符號(hào)是按位取反的意思,同樣,按位取反也是針對(duì)二進(jìn)制而言。比如a = ~(0x01)0x01的二進(jìn)制是0b00000001,按位取反是0b11111110,那么a的值就是0xFE了。

學(xué)會(huì)了這兩個(gè)符號(hào)后,那么我們就可以把流水燈的程序?qū)懗鰜?lái),先把程序貼上。

#include <reg52.h>

sbit  ADDR0 = P1^0;

sbit  ADDR1 = P1^1;

sbit  ADDR2 = P1^2;

sbit  ADDR3 = P1^3;

sbit  ENLED = P1^4;

 

void  main()

{

    unsigned char j = 0;

    unsigned int  i = 0;

 

    ENLED = 0;

    ADDR0 = 0;

    ADDR1 = 1;

    ADDR2 = 1;

    ADDR3 = 1;              //74HC138開(kāi)啟三極管Q16           

    while(1)                //程序死循環(huán)  

    {

        P0 = ~(0x01 << j++);     //P0等于1左移j位,并且j++

        for(i=0; i<20000; i++);  //延時(shí)

        if(j == 8)                //如果j等于8,重新給j賦值0

        {

            j = 0;

        }

     }

}

這里我只講兩種情況,當(dāng)j等于0的時(shí)候,1左移0位還是1,那么寫(xiě)成二進(jìn)制后就是0b00000001,對(duì)這個(gè)數(shù)字按位取反就是0b11111110,亮的是最右邊的小燈。當(dāng)j等于7的時(shí)候,1左移7位就是0b10000000,按位取反0b01111111,亮的是最左邊的小燈。中間過(guò)程大家自己分析一下。

流水燈結(jié)束后,關(guān)于小燈的講解,我們暫時(shí)告一段落,后邊還有小燈的高級(jí)用法,我們到時(shí)候再詳細(xì)講解。

1.8 作業(yè)

1、熟練掌握二進(jìn)制、十進(jìn)制和十六進(jìn)制的轉(zhuǎn)換方法。

2、掌握C語(yǔ)言變量類(lèi)型和范圍,for、while等基本語(yǔ)句的用法。

3、了解函數(shù)的基本結(jié)構(gòu),能夠獨(dú)立進(jìn)入程序Debug,多多動(dòng)手操作,熟練Keil軟件環(huán)境的一些基本操作。

4、將流水燈左移理解透徹后,獨(dú)立完成流水燈右移操作以及流水燈先左移后右移等簡(jiǎn)單的花樣操作。

 

 

 


上一課:第三章 硬件基礎(chǔ)知識(shí)學(xué)習(xí)
下一課:第五章 定時(shí)器和數(shù)碼管

作者: fanamm    時(shí)間: 2014-4-20 17:29
繼續(xù)學(xué)習(xí)!!
作者: chenzhong    時(shí)間: 2014-5-13 00:06
不知道這輩子能不能到樓主這水平,繼續(xù)學(xué)習(xí)
作者: yq11999    時(shí)間: 2014-8-20 21:48
老大,我正在學(xué)習(xí)這個(gè)單 片機(jī)C語(yǔ)言,我也沒(méi)有基礎(chǔ),好像有點(diǎn)難哦,你有沒(méi)有C語(yǔ)言方面比較好 的視頻資料呀?

作者: cuko138    時(shí)間: 2014-9-13 15:21
又學(xué)到了一秒方法
作者: 整合完美    時(shí)間: 2014-12-12 20:38
學(xué)習(xí)中,贊幾個(gè)先
作者: zrb5688    時(shí)間: 2015-1-30 23:55
受益匪淺,感謝樓主。
作者: surongzhan    時(shí)間: 2015-5-16 15:37
每一個(gè)章節(jié)都要認(rèn)真學(xué)習(xí)!
作者: deanking    時(shí)間: 2015-8-20 11:14
又學(xué)完一課,慢慢,謝謝
作者: curry    時(shí)間: 2015-8-28 17:29
1.7節(jié)流水燈程序,實(shí)現(xiàn)燈依次亮、滅的程序(第二部分程序)中為什么j要定義為無(wú)符號(hào)字符型,不是應(yīng)該為無(wú)符號(hào)整型嗎?請(qǐng)前輩指教,謝謝!
作者: 31756381    時(shí)間: 2015-11-7 09:46
怎么都覺(jué)得C比匯編簡(jiǎn)單多了...
作者: 夜色叢林    時(shí)間: 2016-4-24 21:58
我要繼續(xù)學(xué)習(xí)
作者: adamlai    時(shí)間: 2016-5-21 12:12
想通過(guò)語(yǔ)音控制這流水燈,要怎么控制他跳出這死循環(huán),或者進(jìn)入這死循環(huán)?
作者: 隨易而安    時(shí)間: 2016-12-26 15:03
好好學(xué)習(xí),天天向上。
作者: tlone51hei    時(shí)間: 2017-2-16 17:00
好好學(xué)習(xí),天天向上
作者: lsn8999    時(shí)間: 2017-3-22 16:02
講德挺詳細(xì)的
作者: 公爵的熊貓    時(shí)間: 2017-4-26 19:08
樓主  P0&nbsp;=&nbsp;~(0x01&nbsp;<<&nbsp;j++);&nbsp;這句是不是應(yīng)該把P0改成P1....
作者: 77032474    時(shí)間: 2017-6-15 21:45
挺好的,受教了
作者: wohhhde    時(shí)間: 2017-10-22 15:10
贊一個(gè)!
作者: wohhhde    時(shí)間: 2017-10-25 20:50
絕世好貼!
作者: yf11239605520    時(shí)間: 2017-10-26 10:34
講的很詳細(xì)謝謝
作者: lsc_nj    時(shí)間: 2017-12-20 16:40
絕世好貼!講的很詳細(xì)謝謝!辛苦了。。!
作者: LIBIN0014    時(shí)間: 2017-12-30 13:02
帖子牛B啊。最通俗易懂的教程
作者: 奮斗不息    時(shí)間: 2018-4-24 00:08
在哪里能看到呀
作者: 禹1850    時(shí)間: 2018-4-24 21:03
很厲害,今日打卡!
作者: as295290513    時(shí)間: 2018-4-25 11:05
學(xué)習(xí)學(xué)習(xí)長(zhǎng)知識(shí)
作者: aa1159195980    時(shí)間: 2018-5-1 21:45
學(xué)習(xí)長(zhǎng)的知識(shí)快

作者: keneng    時(shí)間: 2018-6-27 13:59
學(xué)習(xí),感謝樓主
作者: heron    時(shí)間: 2018-8-13 11:41
curry 發(fā)表于 2015-8-28 17:29
**** 作者被禁止或刪除 內(nèi)容自動(dòng)屏蔽 ****

我也覺(jué)得應(yīng)該是unsigned int j = 0;
作者: heron    時(shí)間: 2018-8-13 12:46
/***直接從keil里復(fù)制過(guò)來(lái)的漢字全是亂碼,估計(jì)從這里復(fù)制回去也是***/
/**剛寫(xiě)了一個(gè)流水,延時(shí)時(shí)間大概在1秒多一點(diǎn),可以通過(guò)修改DELAY()括號(hào)里的值來(lái)改變延時(shí)時(shí)間**/
/***依次點(diǎn)亮一顆燈的流水燈----Heron***/
#include <reg52.h>
sbit  ADDR0 = P1^0;
sbit  ADDR1 = P1^1;
sbit  ADDR2 = P1^2;
sbit  ADDR3 = P1^3;
sbit  ENLED = P1^4;

void DELAY(unsigned int k)        //循環(huán)次數(shù) = k * 10000
{
        unsigned int a, b;
       
        for(a = 0; a < k; a++)
        {
                for(b = 0; b < 10000; b++);   //括號(hào)后邊直接分號(hào)就是自循環(huán)
        }
}

void  main()
{
   unsigned int i, j;
   i = j = 0;

   ENLED = 0;
   ADDR0 = 0;
   ADDR1 = 1;
   ADDR2 = 1;
   ADDR3 = 1;
       
        while(1)
        {
                for(i = 0; i < 8; i++)
                {
                        P0 = 0xFE<<i;         //0xFE = B11111110 ,每次點(diǎn)亮一顆燈
                        DELAY(20);            //調(diào)用延時(shí)函數(shù),且k = 20
                }
        }
}
作者: 王清華    時(shí)間: 2018-9-7 21:44
heron 發(fā)表于 2018-8-13 12:46
/***直接從keil里復(fù)制過(guò)來(lái)的漢字全是亂碼,估計(jì)從這里復(fù)制回去也是***/
/**剛寫(xiě)了一個(gè)流水,延時(shí)時(shí)間大概在 ...

這個(gè)就是LED燈依次往左點(diǎn)亮嗎  能告訴我為什么樓主要用~按位取反嗎
作者: 祝云章    時(shí)間: 2019-1-29 10:26
謝謝分享!
作者: Liu5452830    時(shí)間: 2019-9-25 21:00
謝謝,學(xué)習(xí)中
作者: b9589    時(shí)間: 2020-10-28 16:20
要認(rèn)真學(xué)習(xí)才能對(duì)得起師傅的教導(dǎo)與分享。
作者: Yu_wei    時(shí)間: 2021-8-25 18:28
heron 發(fā)表于 2018-8-13 12:46
/***直接從keil里復(fù)制過(guò)來(lái)的漢字全是亂碼,估計(jì)從這里復(fù)制回去也是***/
/**剛寫(xiě)了一個(gè)流水,延時(shí)時(shí)間大概在 ...

這應(yīng)該不是每次點(diǎn)亮一個(gè)led吧
作者: mrzhou    時(shí)間: 2022-6-23 09:25
用心施教,要認(rèn)真學(xué)習(xí),好教程。




歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1