單片機入門不難------談PIC系列(轉自礦石收音機論壇---嶗山)十年前的老帖子,講得通俗易懂,分享之,
請看圖1

這個8條腿的小螃蟹就是我們的第一頓飯,只要把它吃下去,以后的大餐就好辦了,
第1、8條腿接電源 +5V 和 地線,頭兩條腿是螃蟹鉗子,好吃的很, 現在剩下了 6 條腿
第2、3條腿 使用時外接一個晶振的東西 我們接一個 4 MHz的,
第4條腿是復位腳,是一個信號輸入腳,單片機正常運行時接高電平,當有一個低電平脈沖輸入到這個腳時單片機就復位,所謂復位就是單片機內部所有的作業部件統統回到規定的狀態,程式也復位到頭一句上開始逐條運行,例如,你設計的一個報警鎖定的 LED紅燈亮后,當需要解除報警時,用一個按鈕給這個腳瞬時接地一下,相當于給它一個夫脈沖,系統就復位了,led燈就熄滅了,程式從頭開始,
以上5個腳,幾乎所有單片機都有,包括世界上最復雜的,和世界比較簡單的單片機-----PIC12CE519
輪到底幾條腿啦?奧是第5條腿,這條叫單片機的 I/O 腳,就是輸入輸出腳,你可通程序式動態地控制它作為輸入或輸出,作為輸出時可以程式控制它的輸出電平為高1或低0,所以,他的作業狀態有四種:輸入0,輸入1,輸出0,輸出1
剩下的兩條腿和第5腳功能一個樣,
上邊我們已經把8條腿消化掉了,其實我們要弄明白的也就3只腿,我們再簡單一些,先整明白兩條腿,即GP0,GP1.這兩條腿低級一點的用法,可以控制繼電器,LED燈,高級一些的用法可以進行I2C總線,RS232總線的通信,作為擴展輸入可以模擬出來A/D轉換器(6--7bit),可以測量一個電阻的粗略值,作為輸出也可以直接推動揚聲器奏出音樂,這是后話暫且不提,
現在要控制使用這兩只腿,我這個三腳貓功夫的說書的不得不講一下軟體了,要想講明白軟體又不得不涉及到單片機的內部結構,那位說啦,你可別提這軟體和結構了,以前俺就是讓它們打敗的,現在聽到這個心里就打鼓,嘿嘿,不要緊,果真如你所說,那你就不妨跟著我再失敗一次, 反正嗎多一次失敗又不納稅,嘿嘿,不過你也要有思想準備,徹底弄明白是個漸進的程序,
要說這程式和單片機內部結構,還真是老大難,不過蟹黃蟹肉都可都在里面,我現在要是給你說PIC單片機是哈佛結構的,51系列是馮-諾伊曼結構的,恐怕你要立馬扎走人了,所以我得用點心思不讓你溜號,
好在PIC系列的制造商(microchip 微芯公司 美國)理解我等苦衷,全部只有35條指令,而且有一些指令我們一般很少使用,常用的也就十幾句,用的時候查手冊,無需記憶,就算我們兩天學習一句,也就兩三個月時間,總比到老了還怕它們強啊,廢話少說先看下面的兩個例語:
my_name006: movlw 02h '常數2進入w
movwf GPIO 'W 的數進入 暫存器GPIO
這就是我們編的程式里的兩個句子,也叫源程式,有以下特點
每行只能寫一句話
每句話由四部分組成:
標號: 操作指令 運算元 '程式注釋
下面我結合例子把這四部分解釋一下,
第一部分 my_name006: 叫做標號,它是由字母或數字組成,由冒號結束,標號可有可無,比如第二句就沒有標號,
第二部分movlw 叫做操作指令,它是必須有的,不能省略,PIC 系列的單片機共有 35 條指令,
第三部分02h 叫做運算元,有的指令沒有運算元或者運算元是默認的,也不用寫,
第四部分是程式注釋,必須以單引號開頭,主要作用是提醒和備忘,注釋也是可有可無,
第二個例句中,省略了標號,當然注釋也可以省略,他的指令是movwf, 運算元是GPIO,運算元不一定是數字,也可能是一個由字母組成的字串,
知道了陳述句格式以后,我們下面就學習一些常用陳述句,我們先把這兩個例句弄清楚,
這兩句話的作用是把 2 這個常數寫入到 GPIO 這個暫存器里,
單片機里有一些部件需要我們使用和操作,都是通過讀寫暫存器來實作的,每個部件都對應有操控它的暫存器,例如我們要控制使用的管腳GP0,GP1 這兩個管腳對應的暫存器就叫做GPIO,對GPIO暫存器讀操作,實際等效察看管腳電平的高低;對GPIO暫存器相應的位寫1操作,實際等校讓管腳輸出高電平,寫0,輸出低電平,
每個暫存器可以儲存一個八位的二進制數,這八個位的每個位都有名稱,從左向右的名稱是:
左端第首位名稱叫D7,
左端第二位名稱叫D6,
左端第三位名稱叫D5,
左端第四位名稱叫D4,
左端第五位名稱叫D3,
左端第六位名稱叫D2,
左端第七位名稱叫D1,
最后一位叫D0,
而每一個位對應一個管腳的電平,例如當GPIO暫存器的D0位等于1時表示管腳GP0 的電平是高電平,D0位等于0時表示管腳GP0 的電平是低電平,常數2的八位二進制表示是“00000010” 所以,GPIO暫存器存放的8位2進制數的每個位的值以及管腳電平是:
D7對 應于內部總線管腳的電平 D7=0 內部總線管腳輸出低電平
D6對應于內部總線管腳的電平 D6=0 內部總線管腳輸出低電平
D5對應于GP5 管腳的電平 D5=0 GP5 管腳輸出低電平
D4對應于GP4 管腳的電平 D4=0 GP4 管腳輸出低電平
D3對應于GP3 管腳的電平 D3=0 GP3 管腳輸出低電平
D2對應于GP2 管腳的電平 D2=0 GP2 管腳輸出低電平
D1對應于GP1 管腳的電平 D1=1 GP1 管腳輸出低電平
D0對應于GP0 管腳的電平 D0=0 GP0 管腳輸出低電平
GP0---GP5管腳我們可以從上一講的圖1硬體中查出所對應的管腳,d7 d6 對應的內部時鐘和資料總線我們現在暫且不要管它,以后本事大了在調教它們,在我們的例句中,向GPIO暫存器寫入了2,常數2的八位二進制表示是“00000010” 因此如果此時GP0, Gp1等都已經被定義成輸出的話,那么GP1輸出高電平(接LED燈亮),GP0 輸出低電平(所接led燈熄) ,
截止到現在,你已經學會如何控制管腳的電平高低了,盡管還有一些疑問,比如怎樣定義管腳為輸出腳(以后會說),我得說如果事先gp1,gp0這兩個管腳處于輸入狀態,這兩個例句無效,是控制不了電平的,
無論如何,這一會兒,你就學會了兩個指令,35條我看也沒啥難的,
'------------------------------------------------------------------------------
再加深一下對暫存器的認識:
要把一個常數存盤到,或者說寫到一個暫存器中,僅用一條指令是辦不到的,必須通過一個特殊的暫存器W,把資料倒過去. 這就應該使用到兩個陳述句,
movlw 02H 指令的意思是把一個常數存入特殊暫存器W, 這個常數是3,后面的H是表示十六進制
movwf GPIO 指令的意思是把特殊暫存器W的數值存入暫存器. 這個暫存器的名稱是 GPIO
這里涉及到兩個概念,常數和暫存器.
常數好說,比如說十進制數 35, 26 但要注意,在單片機系統里我們一般不用十進制,而使用十六進制. 有關數制轉換方面的知識,是計算機的基礎,必須會熟練地在二進制、十六進制、十進制之間轉換,我就不羅索了.
暫存器也叫單片機的記憶體,
一個暫存器可以存盤的數值范圍是0--255,用十六進制表示就是 0---FFH.用二進制表示就是00000000----11111111.
以后要養成習慣用十六進制表示數.
那么,一個單片機里有多少個這樣的暫存器哩,pic12ce512里面有1024個這樣的暫存器可以供你使用,為了使用方便生產商已經給它們編上了號碼,第一號碼是000H,往下依照次序為 001H,002H........3FFH.(怎么樣,開始用十六進制說事了吧,如果你不熟悉熟制轉換趕緊補課來得及)
有了編號就像我們居住的房間有了房間號碼,使用就方便的多了.房間號碼在郵政行業叫地址,因此我們稱這些號碼叫做暫存器地址,或稱地址數 例如 名稱為 GPIO 的暫存器,他的地址,或地址數是 06H ,所以我們的兩個例句完全等同于:
my_name006: movlw 02h '常數2進入w
movwf 06H 'W 的數進入 暫存器GPIO
'-----------------------------------------
有兩個暫存器比較特殊,它們沒有地址,一個名字叫做 W, 另一個叫做 TRIS. 所以他們兩個在存盤資料的時候比較快,一個指令就可以解決問題,例如: movlw 03H 一條指令就把常數3寫入到W暫存器了,關于TRIS暫存器,我們以后用到它再說.
除了他們兩個以外的其他所有暫存器,在寫入資料時一般都要用兩條指令進行,
今天就扯到這里,雖然只有兩個指令,但主要目的是要同學們接觸一下指令,建立暫存器的概念以及他們同硬體部件的聯系,增強學習的信心,能有這些體會,這一節就算過關了,
隨著以后的深入,你會發現小小單片機里面是一個大世界,興趣也由此而生,
我們上一次講的兩個指令是是如何控制管腳電平的高低,前提是所有管腳已經被定義成輸出了(OUT)如果被定義成了輸入,則上次的指令雖然也能運行,但運行后絲毫不能改變管腳電平高低,因為此時管腳是輸入狀態,電平取決于外部輸入,指令無法改變,
在PIC單片機系列中,改變I/O口的輸入輸出依靠寫入暫存器TRIS的值,相應位寫0,表示對應管腳被定義成了輸出,寫1,就是輸入,
現在假如預把GP1、GP2管腳定義成輸出,其他腳全是輸入,那就應該向TRIS 暫存器寫入二進制數 11111001,換算成十六進制就是
F9H.
依照以前我們學到的知識,在PIC系列單片機里,本來應該用下列的陳述句來完成我們的設定:
movlw 0F9H '常數進W 以字母開頭的常數前面必須加0
movwf TRISA '把W內的數復制到TRIS
實際上PIC系列的單片機也都是這么寫的,后面加的A,表示第一個8位的口(有的單片機不僅一個口,還有好幾個8位的I/O口如TRISB TRISC TRISD等等) .
但是,記住了, PIC12系列的單片機必須改寫成為:
movlw 0F9H '常數進W 以字母開頭的常數前面必須加0
tris GPIO '把W內的數復制到TRIS 以后凡見到這個指令一律理解成 movwf TRISA
寫法不同,意思是一樣的. 這樣你就又學了一個指令TRIS,不過這個指令的實質還是你曾經學過的movwf 只是寫法不同罷了.
在PIC12系列里TRIS作為指令, 在其他系列(PIC16\17\18)里把 TRIS 作為普通暫存器看待.
因為我們現在講的就是PIC12CE519,所以我們暫時用
tris GPIO
這個格式,等以后進入PIC16C877 我們再寫成 movwf TRISA , 至于理解按照后者進行.
'-----------
如果我們要控制GP1 GP2管腳的輸出電平, 其他管腳作為輸入.并且讓GP1輸出低電平,GP2輸出高電平.完整的程式如下:
movlw 0F9H '常數進W
tris GPIO '把W內的數復制到TRIS ,GP1 GP2為輸出,其他為輸入
'此行無命令,起到的作用是容易讀懂程式
movlw 04H '常數4的二進制是 00000100 ,GP1=0 GP2=1
movwf GPIO 'W內的數進GPIO 輸出生效,原來定義成輸入的腳的電平,不會受該句影響
上面已經學會了三條指令,但是8位暫存器的概念概念一定要建立起來,程式通過寫入暫存器不同的資料
控制管腳作為輸入使用還是輸出使用,作為輸出時是輸出高電平還是低電平,
這樣的操作又一個特點,就是每次寫入資料,同時控制的往往不是一個管腳,而是好幾個個.最多一次可
以控制8個管腳.在單片機里往往每8個腳叫做一個口,如口A, 口B,用英文表示就是GPIO PORTA PORTB PORTC 等.
更多的情況是:某個口內的某一個管腳需要改變電平,其他腳電平不變.例如我們僅需要GPIO口上的GP1
這個管腳的電平拉高,其他管腳電平不發生變化.這時候位操作指令為我們提供了方便,假如我們事先已經把GP1管腳定義過輸出了(方法見前面講過的):
bcf GPIO,GP1 '注釋 GPIO口上GP1管腳電平拉低,我們行話叫 清除,
bSf GPIO,GP1 '注釋 GPIO口上GP1管腳電平拉高,我們行話叫 置位,
怎么樣,這樣控制某一個管腳的電平就方便多了,你的編程效率大大提高啊.
記住:PIC所有單片機所有暫存器都是可以位操作的,這在51的單片機上是不能完全實作的.
不僅如此,PIC所有單片機所有管腳的單腿驅動輸出電流可以高達 25mA,所以如果你驅動一個 5到10mA電流的LED發光二極管,根本不用加三極管,串個電阻直接掛在單片機上就得了,這在51的單片機上也是不能實作的,要加驅動三極管或驅動芯片.
怎么樣,學PIC有好處吧. 也別急,好處還有那,且聽我慢慢地白話,
一不小心,你已經會 5 個指令了,還有30個,加油啊,
繼續
單片機的大部分指令,或者說單片機所做的大部分作業,多數在寫入或讀出暫存器,關于暫存器的初步概念我想我們已經建立起來了,它是一個能夠存盤8位二進制資料(最大255 = 0FFH) 的單元 每個單元都有它的編號,我們叫做它的地址,或地址編碼. 地址編碼也是十六進制的. 另外暫存器里的資料掉電就會丟失,
暫存器的英文是RAM 也要記住.
PIC12CE519 里面共有有48個暫存器供我們操作使用, 它們每一個都有固定的地址編碼,
地址編碼并不是連續的號碼,而是分成了兩段:
第一段: 從00H 開始, 依次是01H, 02H, 03H ....0AH, 0BH......到1FH 結束. 計32個暫存器
第二段: 從30H 開始, 依次是31H, 32H, ......................到3FH 結束 計16個暫存器
這種地址不連續編號, 而是要跳過去一段的做法, 對于我們新手來說很是不習慣. 為了讓我們容易入門, 我們暫時先不管第二段RAM, 只當它不存在, 所有程式我們只涉及到第一段連續的ram 地址. 等我們熟練的掌握好了ram 的使用,再涉及第二段地址的RAM, 那時,你就會理解單片機設計者把它們分成兩段的苦心了.
為了規范,我們今后一律把RAM的分段, 叫做分頁. 第一地址段叫00頁面, 第二地址段叫01頁面.
例如: 我們學過的 I/O 口電平控制暫存器 GPIO, 它的地址編碼是 06H, 屬于00頁面.
'-----------------
所有這32+16=48個暫存器除了在地址上分成了兩個頁面以外,又把它們分成兩類:
一類專用暫存器,一類通用暫存器.
所謂專用,就是這個暫存器的功能已經由系統分配好了.例如 地址為06H 的名稱就做GPIO暫存器的功能,是它的每個位,都對應到一個I/O腳的電平.
另一類 是通用暫存器,你可以理解成它的功能系統沒有事先預定,而是由你在編程式的時候隨機使用.
pic12ce519 的專用暫存器有 7 個, 位置在我們第00葉面的最前面. 這7個專用暫存器的地址編碼是: 00H,01H, 02H, ----06 H
剩下的所有暫存器包括所有第01頁面, 全部都是通用暫存器.
例子: 在兩個通用暫存器 09H, 0AH 內, 寫入常數 FC H
movlw 0FCH '常數進W
movwf 9H '復制W內的數到通用暫存器09H
movwf 0AH '復制W內的數到通用暫存器0AH 由于此時W內并沒有改變,W不用再進常數.
'----------------------------------------------
下面是PIC12CE519的 暫存器ram的地址地圖:

圖最上端的 00 01 表示的是頁面號碼,或叫頁面地址,
左側 從00 --- 1FH 是00頁面, 右側是01頁面,
從00H 到 06H 都已經起好了名稱 ,它們是專用暫存器,用處各有不同,以后我們會逐個介紹它們
剩下的都是通用暫存器 或者叫普通暫存器 General Purpose Registers 意思是一般用途的暫存器
地址從20H 到 2FH 也不是“空洞”,也不是不能訪問,只是讀寫它們的時候等于讀寫它們左側對應的00頁面,這一點我們可能有些迷惑,弄不明白也沒有關系,以后隨著程式理解的深入,會搞清楚的,
記憶體圖譜,不要求記下來,但是應該有個大體印象,用的時候會察看就可以了,等編程時間一長
就那么幾個位元組,自然就記住了,
所謂位元組是衡量二進制資料長度的一個單位,一個暫存器剛好能記住一個位元組的資料,如果你要存盤的資料比較大超過了255,那就要占2個存盤器甚至更多,描述的時候通常我們不說這個數值占了多少個暫存器,而是說這個資料是幾個位元組的,
位元組的英文是byte 一個二進制數的一位,叫位元 英文bit 1 byte 包含 8 bit
繼續
下面我們學習一條新指令,叫做空操作指令
nop '什么事情也不做,但執行這個指令也要消耗掉一點時間,它沒有運算元,
'不要理解成程式停了,實際上程式仍在正常運行,執行一連串的空操作指令,單片機
'白耗費時間,什么活也不干,往往用于延時
如果你需要一個很短時間的延時,可以采用一連串的空操作,注意每個 nop 也是占一行, 例如:
movlw 0F9H '常數進W
tris GPIO '把W內的數復制到TRIS ,GP1 GP2為輸出,其他為輸入
bsf GPIO,GP1 '管腳GP1輸出高電平點亮LED燈(如果你已經接上燈的話)
nop
nop
nop
nop
nop
... .
bcf GPIO,GP1 '管腳GP1輸出低電平關閉LED燈
nop
nop
nop
nop
...
運行的效果是接在管腳GP1上的LED燈先亮一段時間,再熄滅一段時間的閃爍,
這回再說一個程式轉向的陳述句,goto 指令,學過basic 和 c 等語言的對它不陌生,
單片機對程式的執行是逐句自上而下進行,當它運行到某個位置,如果你不希望繼續運行它下面的陳述句,而是希望它無條件的強行轉到某一句上,就可以使用goto陳述句,
我們還是通過例子來說明goto 的使用方法,
已知外部晶振的頻率為4 MHz, 設計程式從pic12ce512 單片機的GP1管腳上輸出一個方波信號,信號頻率固定并計算出頻率的值,
movlw 0F9H '常數進W
tris GPIO '把W內的數復制到TRIS ,GP1 GP2為輸出,其他為輸入
myWAVE: bsf GPIO,GP1 '管腳GP1輸出高電平點亮LED燈(如果你已經接上燈的話)
nop
nop
nop
nop
nop
nop
nop
bcf GPIO,GP1 '管腳GP1輸出低電平關閉LED燈
nop
nop
nop
nop
nop
goto myWAVE 'myWAVE是標號,某行必須有這個標號,否則程式通不過
nop '由于goto的存在,以下陳述句得不到運行
nop
nop
當程式自上而下運行到goto 陳述句時, 不再繼續運行它底下的陳述句, 而是讓程式強行轉向到標號為myWAVE的陳述句上,并繼續運行.
這樣一來的結果,程式會永遠在標號myWAVE的這一句 bsf GPIO,GP1 到goto之間回圈, 打轉轉.
客觀運行的結果是 GP1管腳電平不停地一會高,一會低, 就輸出了方波信號.
要計算方波的頻率,我們必須知道單片機每運行一條指令需要多少時間.這個時間的單位不以通常的秒 毫秒 或微秒作為單位, 而是以”機器周期” 為單位. 以后凡是我們討論單片機內部的時間問題都要以機器周期作為時間單位. 至于一個機器周期究竟是多少微妙或毫秒, 取決于單片機的品牌和振蕩頻率頻率大小, 等一會我們再用公式計算我們PIC12CE512在4MHz震蕩頻率下的機器周期是多少個微妙,
我們先看看我們的程式中GP1腳的高電平低電平都是用了多少個機器周期.
PIC單片機所有指令都是單機器周期的指令,
例外的情況是goto 陳述句要用2個機器周期 還有一個call指令用的時間也不完全是一個機器周期(待后續)
其他品牌的某些單片機可不是這樣,一條指令往往要用幾個周期……
從bsf 到bcf有8個指令,都是單周期指令,所以GP1高電平時間長度是8個機器周期
從bcf 到bsf有7條指令,其中6條是單周期指令 1條雙周期指令(goto). 所以GP1低電平時間長度也是8個機器周期
這樣,我們輸出方波的周期長度就是16 個機器周期.
Pic品牌的機器周期 = 4/振蕩頻率 (公式)
所以,在我們的例子當中 1個機器周期=4/4MHz= 1 uS
也就是說,我們的例子中,執行一條指令僅需要1微秒的時間.
這樣,我們輸出的方波周期就是16微秒, 頻率是 f =1/16 =0.0625 兆赫 =62.5 KHz
如果這個方波的頻率比較低,你再接一個揚聲器到GP1腳上你就可以聽到聲音了
頻率降低到幾赫茲的時候, 接一個led燈, 就會不停的閃爍.
當然, 頻率太低你用的nop指令的數目會很多,程式雖簡單但是臃腫, 這沒有關系,我們主要是在學習程式, 弄清楚道理是目的,
要想使得程式不臃腫我們有的是辦法,這就必須再學習新的指令.
如果此前我講的你基本都弄明白了,那你現在已經抓住單片機入門的門把手了, 還需輕輕的推開.
當你坐在家里吃著月餅,愜意地用電視遙控器選擇電視頻道,不停地用 +/- 鍵盤調節電視音量到合適的時候,你可曾想過,此時嶗山也許正鉆在在溫度高達35攝氏度以上的樹叢里,忍耐蚊子螞蟻的叮咬,研究用什么樣的通信線更好地防止雨水侵蝕和動物的啃咬,
也許你從沒有留意你按下的節目頻道、音量等這些 標有 + / - 符號的鍵盤是怎樣作業控制大小的,
下面我們學習兩個新指令 incf 和 decf ,它們都是對某一個暫存器進行增1 或減1 操作,例句中假如我們要操作的暫存器是 09H
movlw 02H '常數2進入W
movwf 09H '把w 內的數2 復制到09H 這個暫存器
'現在09H 暫存器記憶體儲的數是2
incf 09H '暫存器09H記憶體儲的數 增加1
'現在09H記憶體儲的數變成3
decf 09H '暫存器09H記憶體儲的數 減掉1
'現在09H記憶體儲的數變成2
movlw 0FFH '常數255進入W
movwf 09H '把w 內的數255 復制到09H 這個暫存器
'現在09H 暫存器記憶體儲的數是255
incf 09H '暫存器09H記憶體儲的數 增加1
'現在09H記憶體儲的數變成0
decf 09H '暫存器09H記憶體儲的數 減掉1
'現在09H記憶體儲的數又變成255
如果你事先定義好了地址為09H 的這個暫存器里存盤的數字大小,代表電視機節目頻道的話,你會很喜歡這兩個指令的,并且當節目頻道到達最大值255 或最小值0的時候無需擔心,暫存器在0時減1 會得255, 255狀態下增1 會得0
至于為什么會這樣,學過環形計數器的人不會感到奇怪的,你要是沒有學過計數器電路也不要緊,記住一個暫存器的最大存盤數值是255 = 0FFH 就可以了,加減法都會導致它“進位”
當然控制音量時這個程式不能使用,因為它在0和255之間變化,音量忽大忽小怎們行,
為解決這個問題, 我們必須再學習兩條指令 incfsz 和 decfsz
它們與上兩個功能基本相同,不同的是: 暫存器增1 或減1操作以后,該指令會自動判定暫存器內的結果是否為零,如果不為零,繼續正常執行該指令后面的陳述句. 但如果結果為零的話,則程式會 "跳一步" .繞過緊挨著它下面的一條指令,繼續執行更下面的陳述句,舉例子說明
假定我們操作的暫存器還是09H:
movlw 0FDH '常數253進入W
movwf 09H '把w 內的數253 復制到09H 這個暫存器
'現在09H 暫存器記憶體儲的數是253
incfsz 09H '暫存器09H記憶體儲的數 增加1,結果變成254 結果不等于0,故程式繼續執行下一指令
nop '該句得到執行(因為上一句暫存器09H的計算結果不等于0)
incf 09H '暫存器09H記憶體儲的數 增加1,結果是255
incfsz 09H '暫存器09H記憶體儲的數 增加1,結果變成0
'因為結果等于0,故程式要跳過下面的一句(不運行下面的一句).
incf 09H '由于上一句的存在并結果為0,該句得不到執行,被忽略
incf 09H '程式跳入這一句繼續運行 暫存器09H記憶體儲的數 增加1
nop '因此現在 09H暫存器存盤的數是1
nop '繼續運行
.
.
.
.
.
思考題:設計一段程式代碼,當用戶連續按下音量減小鍵后,判定音量暫存器09H的存盤音量數值,
防止該暫存器的值從0 變成255,以免震驚到用戶,
.
.
.
.
SMALL_SOUND: nop '標號可以任意寫的,此前用戶一旦按下音量減,就把程
' 序引導到這一句上來
decfsz 09H '暫存器09H記憶體儲的數 減1,如果結果為0 就跳一步
goto OK '如果上一句結果不為0,執行該句后,程式去了ok陳述句
movlw 01H '跳到這一步說明暫存器結果是0
movwf 09 '強行把 09H內的數值寫成1,仍然是小音量,這樣音量不會被因為 減小而變成255
OK: nop '繼續運行
.
.
思考題:利用decfsz 指令設計一段延時代碼,使得延時時間可以在10個機器周期到65535個機器周期之間,
可以通程序式任意控制
在這個例子中,設我們要控制的延時時間大約是24086個及其周期,用16進制表是就是 5E16 H.
如果用到通用暫存器,請使用 0AH, 0BH
yanshi: movlw 5EH ' 常數5E進W 標號是延時
movwf 0BH , '0B暫存器數為5EH
movlw 16H '常數16進W
movwf 0AH '0A暫存器數為16H
jixu: decfsz 0AH '0A暫存器內的數減1,如果結果為0跳步
goto jixu '結果不為0,繼續
decfsz 0BH '0B暫存器內的數減1,如果結果為0跳步
goto jixu '結果不為0,繼續
nop '延時完畢
.
.
.
.
你現在可以只用這幾個簡單句子完成任意時間的延時程式了,
下面介紹單片機匯編語言里的一個概念 “子程式”
下面我介紹 “子程式”
我先打個比方,如果你做一頓飯,要做湯,炒菜,燉魚,汆丸子, 奧,忘了還有炒小螃蟹(大螃蟹現在都叫人吃的逮不著了:))期間有一個動作在我看來不斷的重復,這個動作就是放鹽 放鹽的程序描述是這樣的:
放鹽: 用一把小勺子深入鹽罐
舀出氯化鈉適量 ,
把小勺子里的氯化鈉
均勻灑在鍋里,
完畢
如果我們把做飯定義為主任務 那么放鹽這個動作就叫做 子任務,
這樣定義的一個好處就是描述主任務的時候比較方便,當你用語言文字描述主任務的時候,無論哪一道菜,到了該加鹽的時候不必細說用一把小勺子深入鹽罐...... 因為很多菜都有同樣的這個程序,所以,你用 “放鹽” 兩個字就可以了,但是在你使用 放鹽 這個詞之前或者之后,你應該解釋一下放鹽 這個詞的具體程序是什么,
我們單片機的程式也是一樣的,如果你設計一個電視機的自動搜索頻道的程式,程式要求電視機每搜索成功一個頻道,它面板上的發光二極管就眨一次眼睛,也就說,先熄滅一段時間然后再點亮,這樣就會遇到很多這樣的眨眼動作,為了簡化主程式我們可以把眨眼這樣一個程序定義為一段子程式,以后每次遇到需要眨眼的時候就呼叫一次子程式就可以了,
子程式的定義是這樣的
Zhayan: bcf GPIO,GP1 '管腳GP1輸出低電平關閉LED燈 做為子程式標號是必須有的 標號
' 就是子程式的名字
nop
nop
nop
nop
nop
... .
bsf GPIO,GP1 '管腳GP1輸出高電平點亮LED燈
nop
nop
nop
nop
...
return '這個命令表示子程式的結束 是必需的 否則這個子程式沒有結束
這樣,子程式就定義完了 如果想在程式的某個位置需要led燈熄滅以下(眨眼一次),只需在那個程式位置呼叫一下子程式就可以了,
呼叫的方法是用 call 命令,
主程式:
.... '這些點點表示主程式里的陳述句
....
......
...... '這個位置搜所成功一個臺 需要“眨眼”一次
call Zhayan
...... '繼續搜索下一個臺的命令列
......
......
......
...... '這個位置搜所成功一個臺 需要“眨眼”一次
call Zhayan
...... '繼續搜索下一個臺的命令列
......
......
...... '這個位置搜所成功一個臺 需要“眨眼”一次
call Zhayan
...... '繼續搜索下一個臺的命令列
......
疑問1 我在一個主程式里固然可以呼叫另一個子程式,而我在一個子程式里能不能呼叫另一個子程式?
答 可以的,這叫子程式嵌套,甚至還可以在另一個子程式中再繼續呼叫別的子程式,
疑問2 嗯,那繼續往下呼叫下去,有限制么?
答 有,這叫允許嵌套的層數 每個品牌 型號的單片機允許的嵌套層數都是有規定的 例如pic16f74 允許8層
pic12e519 允許兩層 也就是說pic12e519的主程式里可以呼叫子程式,子程式里海可以再呼叫子程式,到此為止不要再往下呼叫 了,否則程式報錯或者超出你預計的結果,
疑問3 在同一層程式空間里,例如在我的某個子程式之中,呼叫另一個子程式的次數有限制么?
回答 沒有限制,只要你的程式暫存器裝得下你的程式,
疑問4 我聽說單片機在呼叫子程式以前,好像需要程式“堆疊”訪問什么的,要進行一些程式計數器的保存保護,以保證子程式回傳來得時候,程式能夠正確回到原來位置和環境,是這樣的么?
答 pic單片機不用管這些問題,它是硬體自動完成這些堆疊的事情,我們的指令里不用關心這些,盡管如此,中檔pic單片機的例如 pic16等系列,它們的程式存盤器地址是分頁的,盡量呼叫本頁的子程式,如果子程式不在本頁,而是在另一個頁面里存放,你還是要告訴單片機你的子程式所在的頁面資料的,具體操作指令可以查相關指令說明,我們的pic12c519的程式存盤器,沒有分頁,不用關心這事,
繼續講
我們學習到這里,就已經初窺門庭了,下一步還有一個重要的關口-------中斷
單片機的中斷,概念并不難以理解,只是要真正理解運用編程處理一些實際中斷的例子,卻也不是很容易,甚至是單片機學習、入門的攔路虎,要想學會實際的中斷處理編程,也還需要清楚一些程式存盤器,程式結構,程式計數器,硬體堆疊,現場保護等這些個另雜碎概念,
因此,我們在學習中斷以前,以后和學習中斷程序中,都有必要介紹回顧復習一些有關上述關鍵詞的概念和知識,否則,盡管你學了中斷,用起來可還是不能得心應手,以至于茫然,
我還是用比喻的方法介紹一下中斷的概念:
你的主程式任務是做一桌可口的飯菜,期間可能要多次呼叫子程式“撒鹽”,
盡管子程式下邊還有更小的子任務,比如“計算食鹽的量”等程序,盡管這些子程序很復雜,但他們的出場時間和順序是可以預料的,是可以預先安排的,也就說你肯定知道在什么時候放鹽,
有一類子程式,他的出場時間是不確定的,突然的,處理他們的時間刻不容緩,必須趕緊的,我們稱這一類子程式為 中斷子程式, 也就是我們所說的 中斷
你正在做菜的程序中,隔壁鄰居小孩突然敲門說 他的二大爺在他房間里摔倒了 請你幫忙把二大爺扶起來,這是急迫的,必須處理的事務,
你肯定關掉爐子一溜煙跑出去幫忙,等回來以后再點著爐子繼續做菜,
這個事件的特點就是發生的時間你無法預先知道,而這個任務必須得停下當前作業去處理,并且是刻不容緩,
從開始關爐子到回來點著爐子的這段時間里以及你的救人行為,就叫做 “中斷子程式”,
在中斷子程式程序中,你關爐子的動作,叫做“中斷現場保護” 點著爐子叫做“中斷現場恢復”中間走出去扶起隔壁二大爺到回來 叫做“中斷任務處理”
小孩子敲門就叫做“中斷請求”
這就是中斷的基本概念,
在單片機里,中斷的例子也是很多的,我舉一個你手里的手機的例子,你的GSM手機正作業在賦閑,螢屏上也就顯示個時間日期中國電信什么的,表面看沒有什么,其實它內部的cpu高速運行忙碌地作業在諸如聯絡無線網路,查詢是否有短訊息發來,計算當前信號強度,時間等任務中,
你突然按下數字鍵“8”,此時內部cpu必須停下它正在干的作業來應付你,也就是清屏,顯示你按下的數字8,然后再回到它原來的任務接著運行,(當然,這個例子不一恰當,現在有作業系統Windows-ce windows-mobile的手機的作業機制遠沒有如此的簡單)
下面我們要接觸和復習一些另雜碎,學習中斷必須要弄明白單片機這些另雜碎,所以你還得忍耐他們一陣子,
再說這不是教材,只是想為入門學習指劃個門徑,我的帖子里面有很多細節錯誤,例如記憶體頁面問題,519也是分頁的,
但為了入門,我們還是不先不要理會這些,等入門以后,還有很多細節需要搞明白,那時候就容易啦,
這不,我也是現學現賣,在想寫個具體中斷代碼的例子的時候,才現行的查閱了pic12f519 的資料手冊,竟然沒有查到中斷方面的說明,感情這款芯片沒有中斷的功能!不會是我英文水平低沒有看懂吧,又拿出來中文的,同樣的,程式存盤器里沒有中斷說明,只有復位(復位也是單片機的概念) ,
總之,我們學習中斷代碼,這款芯片不適合我們啦,
怎么辦?只好換一款中檔的型號:PIC16C74. 那位說從低端芯片一下子到中端芯片跨度太大啦吧,能適應嗎,我回答:肯定能!
高端芯片無非腿腳更多,片內資源也多,但是原理和方法,和低端的沒有區別,我們只要掌握了單片機的使用操作方法入門,慢說中檔,就是高端芯片pic18、24、99999 系列,那也是一樣,我們仍然可以鉆進去,出得來,
其實啊,所謂高端的陳述句,學起來更簡單和使用起來更方便,要實作同樣功能,如果限定僅使用低端35條,反而會比較羅索,
下面以 PIC16C74 這款芯片為例,仍然不出35潭訓本指令,寫出一個完整的中斷代碼的例子,注意這個例子程式的總體結構,題目要求:
1、當一個鍵盤按動一下后,中斷主程式,改變某管腳上的一只LED燈的狀態,如果再次按動,再次改變,
2、主程式實際上和我們的中斷任務處理沒有關系,我們可以隨便寫個任務,例如主任務程式是計算:123 + 45 = ?
在這個例子里,計算123 + 45 = ?相當于我們在做菜,突然有人按動按鍵,相當于小孩子敲門請求中斷,那么改變(點亮或者熄滅)某管腳上的一只LED燈的狀態,就是我們刻不容緩的拯救行動,
為了理解中斷代碼,我們先看看硬體設計,下面是這個例子的電路圖,(缺)
如圖:11、12 管腳接電源和地線,13、14管腳接振蕩器, 管腳 1 是復位管腳,只要它是高電平,程式就運行,只要是低電平,程式就馬上停止,并回到程式特定的開始位置,也叫做“復位”
我們真正用到的是 管腳40 名字叫做 RB7 接一個鍵盤,可以看出,該腳平時為高電平,一旦有人按下鍵盤,就會變成低電平,從而導致主程式發生中斷,它是作為輸入 I 使用,
管教27 控制一個發光二極管,輸出高電平點亮, 它是作為輸出管腳.
剩下的那些管腳,先不管,實際使用的時候懸空好了,
根據我們以前所學,主程式和子程式已然明了,在以前的程式中,凡是涉及專用暫存器如 GPIO 或者通用暫存器如的時候,
我都是在程式注釋里說明該符號如GPIO 、020H,是一個暫存器的地址,這里有一個差別,特殊暫存器都是用字串表示,而通用暫存器使用它的地址數表示,這樣做主要是為了便于理解暫存器的本質和使用,
事實上,通用暫存器也是可以用字串來表示的,并且在實用的程式里往往是用字串表示通用暫存器,而不是直接用它的地址,因為用字串更能明確這個通用寄存的用途性質,僅用地址數是允許的,但是通用暫存器一旦多起來,連程式作者自己也搞不清楚哪一個是干什么用的了,
為了便于程式理解,閱讀,容易撰寫,允許程式作者給通用暫存器 例如020H 起用一個漂亮直接的字串名字, 我們來學習一個嶄新的陳述句 EQU
我們看幾個例子:
BeijiaShu EQU 020H '定義字串 BeijiaShu 的值是數字 020H
'此前或者此后的程式代碼中, 只要遇到 BeijiaShu 就可以用數字 020H取代
'盡管BeijiaShu也可以寫在一行的最左頂格,但是別用冒號,它不是程式標號
有了上述的陳述句,下列陳述句
MOVLW 123H
MOVWF 020H
就完全可以寫成
MOVLW 123H
MOVWF BeijiaShu
程式中下列兩組陳述句是等價的,相同的意義,這樣,所有的通用暫存器都可以有自己的名號可以使用了,
新陳述句中的 EQU 不屬于35條指令里面的,因為它在單片機運行的時候,不能被執行,也不會占用單片機里的程式存盤空間,
僅僅是為了方便我們人類閱讀、撰寫源程式程式而起的代號,事實上,有了EQU指令,家用電腦在把我們撰寫的源程式“翻譯”成單片機能夠執行的機器碼的時候,還徒然增加了編譯的作業量,不利于提高編譯速度,
因此,它也不是十分必須的陳述句,你如果記憶力好,對每個通用暫存器的地址你自己設計成什么用途了,都能夠記住,完全可以不必多此一舉的使用什么符號常數,話又說回來,誰有那么多多余的精力去記憶枯燥的地址資料呢,
象這樣不能被單片機執行,僅僅能幫助我們撰寫閱讀源程式方便、或者僅僅有助于輔助家用電腦編譯源程式而設立的指令,叫做單片機的“偽指令”
從理論上來說,幾乎所有偽指令都不是必須的,都是可有可無的,都能夠用我們的35條指令就可以完成任務的,只是,那樣的話我們的35條使用起來比較麻煩和不太方便甚至還需要增加額外的計算我們的程式所存在的地址等等作業量,
有了這些個偽指令,我們撰寫程式就會省卻許多的麻煩,例如:
此前的例子中用到了許多行 NOP空操作指令,假如某段代碼需要連續500行NOP陳述句,即便拷貝也是個大麻煩,
那么有一款或者兩款偽指令,只需要說明你的行編輯重復次數以及行編輯重復的代碼是NOP 就足夠了,
編輯上看上去也就是幾行的樣子,實際家用電腦在編譯生成單片機的實際執行碼的時候,會自動添加500行NOP指令,單片機里面的程式存盤空間,也是相應的增加等量500行代碼的空間,那是一點也不能節約出來的,
一句話:偽指令和我們此前講的 單引號 ’ 后面的程式注釋類似,共同點是都不被單片機執行,也不會占用單片機內部的程式存盤資源,都是幫助人們閱讀和撰寫的方便,不同點是:偽指令要干預家用電腦對源程式的編譯,而單引號則不會,
由此看,初學入門不宜在偽指令上徘徊,老手則是善于運用偽指令圖個編程快捷簡便,
下面我們撰寫我們的中斷例子,并且作為文本檔案來處理看待,匯編語言主程式檔案的擴展名稱,是匯編系統已經規定好了的,必須使用 *.ASM
例如:可以為: Shan_LED.ASM
用記事本新建一個檔案,檔案名稱請使用例子 Shan_LED.ASM 檔案內容如下
BeijiaShu EQU 020H '被加數
JiaShu EQU 021H '加數
HE1 EQU 022H '第一算式的和
HE2 EQU 023H '第二算式的和
盡管如此,初學者,有四個偽指令需要認識,一個是EQU 已經認識過了,如果需要的 EQU 的行很多,例如我們的主程式中要用到 被加數、加數、計算結果的和 以及第一個算式的計算結果寄存 以及第二個算式的計算結果暫存:
那么,下一步,就是要核計整個程式的結構,如果從程式的用途屬性,程式存盤區域等角度來看,存盤來區分一個典型的程式結構例子要包含主程式,子程式,中斷子程式,和偽指令說明程式,
以往教材,在教導程式結構的時候不是這么分類,而是大談特談什么回圈結構,分支結構,遞回結構,函式呼叫等等,先把學生搞暈再說,我認為那些屬于程式技巧,是用,是末,理應排在以后的技巧升級后去研究,按照上述分類才是程式之本體,是入門的必然門徑,
有四個偽指令需要認識的,我們已經學會一個 EQU,另外3個隨著我們講解這個中斷例子添加,再看看 ORG 偽指令
ORG 00H '這是個偽指令,強調以下的源程式代碼在存盤到單片機的時候,
'被存盤在單片機程式存盤器的 00H位置,也就是開始的位置
NOP '這個才是真實的第一條可執行單片機指令,它被存盤在程式存盤器的 0000H位置
NOP '該指令沒有用ORG指令規定位置,只好按照默認的順序 被存盤在 0001H
GOTO MAIN '順序存盤位置 002H 無條件挑轉到標號 MAIN的地方,
'0003H這個地方沒有任何陳述句,是個空洞,不被執行
'空洞在家用電腦編譯器編譯的時候,實際上也會被給出一個特定的資料存盤到0003H
'這個特定資料是什么是無所謂的,不同廠家的編譯器會給不同的特定數
'特定數例如:NOP MOVLW等一般為可執行資料,以免萬一單片機誤入空洞,也能作業,不出錯
ORG 04H
GOTO INTSRV '由于上鄰的ORG 04H的存在,該句會被存盤到程式空間 0004H , 無條件轉向標號 INTSRV
NOP '按照默認順序編譯存盤到 0005H
NOP '按照默認順序編譯存盤 直到遇到下一個ORG命令,才會打破順序
學會ORG命令以后,就可以把整個源程式安排到單片機的程式存盤器位置上,我們習慣叫做程式存盤器地址,PIC12F509的片內程式存盤器只有1024個單元行,地址從 0000H ---- 03FF H 而PIC16C74的片內程式存盤器有4096個單元行,
整個程式存盤器 有幾個位置(地址)需要特別介紹,那就是復位地址0000H 和 中斷入口地址 0004H,
0000H是 PIC單片機程式存盤器的第一行,通電后首先執行這一行的指令,等于是單片機作業的開始位置,
通電后后也許需要單片機多次重新開始作業,例如一臺電子稱通電以后,可能需要稱若干次物品的重量,那么,每換一次物品,單片機就需要重新回到 0000 H這個指令上來,開始新一輪的測量,只要單片機的指令,回到了 0000 H, 我們就成為單片機復位,
中斷入口地址是怎么回事情呢?
原來啊,我們的主程式和呼叫的子程式已經很完美了,如果小孩子不敲門,或者在本例子里 那個鍵盤永遠也不按下的話,是不會發生中斷的,我們的程式也就不會跳出我們事先設計的主程式和子程式那些事情,
可是,一旦發生了按鍵事件,此時程式會暫時記住當前位置,轉而去進行中斷服務,去哪里服務呢?就是強行把程式指向 0004H 這個特殊地址,然后從0004 H開始進行中斷的服務處理,因此我們設計的中斷撒鹽 或者開關LED燈的一些程式,就必須從0004H這個位置開始,因此 0004H就叫做中斷入口 或者叫中斷入口地址,
不同型號的單片機中斷入口地址不一定都是 0004H, 就我們的例子PIC16C74是這樣的,并且就這一個入口,因此中斷程式較簡單,而別的型號則不一定,需要查手冊,如 PIC18F452就有兩個中斷入口,地址0008 H 以及 0018H
原文地址:通俗易懂講PIC單片機:從一竅不通到入門進步_嵌入式系統與MCU-面包板社區
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/299752.html
標籤:其他
上一篇:ESP8266學習——UART
