欧美色欧美亚洲高清在线观看,国产特黄特色a级在线视频,国产一区视频一区欧美,亚洲成a 人在线观看中文

  1. <ul id="fwlom"></ul>

    <object id="fwlom"></object>

    <span id="fwlom"></span><dfn id="fwlom"></dfn>

      <object id="fwlom"></object>

      Matlab學(xué)習(xí)心得系列——001. 先從C語言談起

      時間:2019-05-12 14:19:04下載本文作者:會員上傳
      簡介:寫寫幫文庫小編為你整理了多篇相關(guān)的《Matlab學(xué)習(xí)心得系列——001. 先從C語言談起》,但愿對你工作學(xué)習(xí)有幫助,當(dāng)然你在寫寫幫文庫還可以找到更多《Matlab學(xué)習(xí)心得系列——001. 先從C語言談起》。

      第一篇:Matlab學(xué)習(xí)心得系列——001. 先從C語言談起

      Matlab學(xué)習(xí)系列(1)——先從C語言談起

      前言

      Matlab作為最強(qiáng)大的數(shù)學(xué)建模軟件非常有必要系統(tǒng)的學(xué)習(xí)一下,目前我大概算是【粗略地懂些Matlab,很多語法需要現(xiàn)用現(xiàn)查】的水平,所以打算不定期地學(xué)一點,同時也記錄在這里供大家交流學(xué)習(xí)。

      ——————————————

      學(xué)任何一門計算機(jī)語言都得先理解其核心的編程思想,然后在該編程思想的指導(dǎo)下去學(xué)語法,否則學(xué)再多語法知識也達(dá)不到入門的水平。

      多數(shù)人學(xué)編程都是從C語言開始的,C語言是最重要的基礎(chǔ)語言,在于:

      ————

      1.它的引導(dǎo)編程入門

      比起其他編程語言,大概是最適合的了。

      2.它的通用語法

      即適用于任何編程語言的基礎(chǔ)語法本質(zhì),表示形式是次要的(比如整型用int;if...else是選擇結(jié)構(gòu)等等)。

      3.它的代碼底層性

      通常高級編程語言都是采用的【從大問題逐層細(xì)化到小問題,直到不能再小的問題】,這個【不能再小的問題】要實現(xiàn)的話就離不開底層代碼編程,C語言訓(xùn)練的恰恰是這個功底,這個功底也鍛煉了閱讀其他語言代碼的能力。

      同時,這也是C語言編程思想的體現(xiàn)——面向過程(一步一步具體地實現(xiàn)解決小問題的過程)。

      當(dāng)然要進(jìn)一步訓(xùn)練底層代碼的功底,離不開《數(shù)據(jù)結(jié)構(gòu)》。

      ————

      學(xué)C語言就是要真正學(xué)會上面三條,這樣再學(xué)其他編程語言的時候,才會事半功倍,才會快速學(xué)會。比如學(xué)C++或Java,它的面向過程部分,就是C語言改了幾個命令名;基本只要專學(xué)C++和Jave核心的【面向?qū)ο螅ㄋ鼈兊木幊趟枷耄坎糠志涂梢粤?,而C語言底層功底訓(xùn)練,也為這部分的學(xué)習(xí)打下了基礎(chǔ)。

      但往往很多人,這1, 2, 3 條沒學(xué)好,反而把一些C語言固有,而和其他語言相抵觸的部分,時刻牢記在心,這就適得其反了。比如說,“C語言的編程思想——面向過程”

      基本上任何一門其他高級語言的核心編程思想都不是面向過程(面向過程訓(xùn)練,一個C語言就夠了),你再帶著面向過程的思想去學(xué),根本就是不可能學(xué)會新語言的。這不是說面向過程沒用,面向過程有用(見第3.條)但不是用在這壞事的。

      所以,要學(xué)Matlab,首先要拋棄C語言那種【一個數(shù)值、一個數(shù)組元素地來操作....來循環(huán)....】的思想,轉(zhuǎn)而強(qiáng)制自己接受Matlab的核心編程思想——向量化編程(通過向量、矩陣來直接操作大塊數(shù)據(jù))。這也是下次的內(nèi)容:

      Matlab學(xué)習(xí)系列(2)——Matlab編程思想之向量化編程

      第二篇:C語言學(xué)習(xí)心得

      這個我從eehome貼過來的。寫的非常的好。我們用學(xué)單片機(jī)不要停在演示的基礎(chǔ)上。只能讓單片機(jī)完成局部事。這樣我們永遠(yuǎn)不會走出流水燈地獄??!

      學(xué)習(xí)單片機(jī)也已經(jīng)有幾年了,藉此機(jī)會和大家聊一下我學(xué)習(xí)過程中的一些經(jīng)歷和想法吧。也感謝一線工人提供了這個機(jī)會。希望大家有什么好的想法和建議都直接跟帖說出來。畢竟只有交流才能夠碰撞出火花來^_^。

      ?!百u弄”也好,“吹噓”也罷,我只是想認(rèn)真的寫寫我這一路走來歷經(jīng)的總總,把其中值得注意,以及經(jīng)驗的地方寫出來,權(quán)當(dāng)是我對自己的一個總結(jié)吧。而作為看官的你,如果看到了我的錯誤,還請一定指正,這樣對我以及其它讀者都有幫助,而至于你如果從中能夠收獲到些許,那便是我最大的欣慰了。姑妄言之,姑妄聽之。如果有啥好的想法和建議一定要說出來。? 幾年前,和眾多初學(xué)者一樣,我接觸到了單片機(jī),立刻被其神奇的功能所吸引,從此不能自拔。很多個日夜就這樣陪伴著它度過了。期間也遇到過非常多的問題,也一度被這些問題所困惑??等到回過頭來,看到自己曾經(jīng)走過的路,唏噓不已。經(jīng)?;燠E于論壇里,也看到了很多初學(xué)者發(fā)的求助帖子,看到他們走在自己曾走過的彎路上,忽然想到了自己的那段日子,心里竟然莫名的沖動,凡此總總,我總是盡自己所能去回帖。很多時候,都想寫一點什么東西出來,希望對廣大的初學(xué)者有一點點幫助。但總是不知從何處寫起。今天借一線工人的臺,唱一唱我的戲

      一路學(xué)習(xí)過來的過程中,幫助最大之一無疑來自于網(wǎng)絡(luò)了。很多時候,通過網(wǎng)絡(luò),我們都可以獲取到所需要的學(xué)習(xí)資料。但是,隨著我們學(xué)習(xí)的深入,我們會慢慢發(fā)現(xiàn),網(wǎng)絡(luò)提供的東西是有限度的,好像大部分的資料都差不多,或者說是適合大部分的初學(xué)者所需,而當(dāng)我們想更進(jìn)一步提高時,卻發(fā)現(xiàn)能夠獲取到的資料越來越少,相信各位也會有同感,鋪天蓋地的單片機(jī)資料中大部分不是流水燈就是LED,液晶,而且也只是僅僅作功能性的演示。于是有些人選擇了放棄,或者是轉(zhuǎn)移到其他興趣上面去了,而只有少部分人選擇了繼續(xù)摸索下去,結(jié)合市面上的書籍,然后在網(wǎng)絡(luò)上鍥而不舍的搜集資料,再從牛人的只言片語中去體會,不斷動手實踐,慢慢的,也摸索出來了自己的一條路子。當(dāng)然這個過程必然是艱辛的,而他學(xué)會了之后也不會在網(wǎng)絡(luò)上輕易分享自己的學(xué)習(xí)成果。如此惡性循環(huán)下去,也就不難理解為什么初級的學(xué)習(xí)資料滿天飛,而深入一點的學(xué)習(xí)資料卻很少的原因了。相較于其他領(lǐng)域,單片機(jī)技術(shù)的封鎖更加容易。盡管已經(jīng)問世了很多年了,有價值的資料還是相當(dāng)?shù)那啡?,大部分的資料都是止于入門階段或者是簡單的演示實驗。但是在實際工程應(yīng)用中卻是另外一回事。有能力的高手無暇或者是不愿公開自己的學(xué)習(xí)經(jīng)驗。

      很多時候,我也很困惑,看到國外愛好者毫不保留的在網(wǎng)絡(luò)上發(fā)布自己的作品,我忽然感覺到一絲絲的悲哀。也許,我們真的該轉(zhuǎn)變一下思路了,幫助別人,其實也是在幫助自己。啰啰嗦嗦的說了這么多,相信大家能夠明白說的是什么意思。在接下來的一段日子里,我將會結(jié)合電子工程師之家舉辦的主題周活動寫一點自己的想法。盡可能從實用的角度去講述。希望能夠幫助更多的初學(xué)者更上一層樓。而關(guān)于這個主題周的最大主題我想了這樣的一個名字“從單片機(jī)初學(xué)者邁向單片機(jī)工程師”。名字挺大挺響亮,給我的壓力也挺大的,但我會努力,爭取使這樣的一系列文章能夠帶給大家一點幫助,而不是看后大跌眼鏡。這樣的一系列文章主要的對象是初學(xué)者,以及想從初學(xué)者更進(jìn)一步提高的讀者。而至于老手,以及那些牛XX的人,希望能夠給我們這些初學(xué)者更多的一些指點哈~@_@

      我們首先來看第一章節(jié)

      從這一章開始,我們開始邁入單片機(jī)的世界。在我們開始這一章具體的學(xué)習(xí)之前,有必要給大家先說明一下。在以后的系列文章中,我們將以51內(nèi)核的單片機(jī)為載體,C語言為編程語言,開發(fā)環(huán)境為KEIL uv3。至于為什么選用C語言開發(fā),好處不言而喻,開發(fā)速度快,效率高,代碼可復(fù)用率高,結(jié)構(gòu)清晰,尤其是在大型的程序中,而且隨著編譯器的不斷升級,其編譯后的代碼大小與匯編語言的差距越來越小。而關(guān)于C語言和匯編之爭,就像那個啥,每隔一段時間總會有人挑起這個話題,如果你感興趣,可以到網(wǎng)上搜索相關(guān)的帖子自行閱讀。不是說匯編不重要,在很多對時序要求非常高的場合,需要利用匯編語言和C語言混合編程才能夠滿足系統(tǒng)的需求。在我們學(xué)習(xí)掌握C語言的同時,也還需要利用閑余的時間去學(xué)習(xí)了解匯編語言。

      1.從點亮LED(發(fā)光二極管)開始

      在市面上眾多的單片機(jī)學(xué)習(xí)資料中,最基礎(chǔ)的實驗無疑于點亮LED了,即控制單片機(jī)的I/O的電平的變化。

      如同如下實例代碼一般

      void main(void){ LedInit();While(1){ LED = ON;DelayMs(500);LED = OFF;DelayMs(500);} }

      程序很簡單,從它的結(jié)構(gòu)可以看出,LED先點亮500MS,然后熄滅500MS,如此循環(huán)下去,形成的效果就是LED以1HZ的頻率進(jìn)行閃爍。下面讓我們分析上面的程序有沒有什么問題。

      看來看出,好像很正常的啊,能有什么問題呢?這個時候我們應(yīng)該換一個思路去想了。試想,整個程序除了控制LED = ON ; LED = OFF; 這兩條語句外,其余的時間,全消耗在了DelayMs(500)這兩個函數(shù)上。而在實際應(yīng)用系統(tǒng)中是沒有哪個系統(tǒng)只閃爍一只LED就其它什么事情都不做了的。因此,在這里我們要想辦法,把CPU解放出來,讓它不要白白浪費500MS的延時等待時間。寧可讓它一遍又一遍的掃描看有哪些任務(wù)需要執(zhí)行,也不要讓它停留在某個地方空轉(zhuǎn)消耗CPU時間。

      從上面我們可以總結(jié)出

      (1)無論什么時候我們都要以實際應(yīng)用的角度去考慮程序的編寫。

      (2)無論什么時候都不要讓CPU白白浪費等待,尤其是延時(超過1MS)這樣的地方。

      下面讓我們從另外一個角度來考慮如何點亮一顆LED。先看看我們的硬件結(jié)構(gòu)是什么樣子的。

      我手上的單片機(jī)板子是電子工程師之家的開發(fā)的學(xué)習(xí)板。就以它的實際硬件連接圖來分析吧。如下圖所示

      (原文件名:led.jpg)

      引用圖片

      一般的LED的正常發(fā)光電流為10~20MA而低電流LED的工作電流在2mA以下(亮度與普通發(fā)光管相同)。在上圖中我們可知,當(dāng)Q1~Q8引腳上面的電平為低電平時,LED發(fā)光。通過LED的電流約為(VCC-Vd)/ RA2。其中Vd為LED導(dǎo)通后的壓降,約為1.7V左右。這個導(dǎo)通壓降根據(jù)LED顏色的不同,以及工作電流的大小的不同,會有一定的差別。下面一些參數(shù)是網(wǎng)上有人測出來的,供大家參考。紅色的壓降為1.82-1.88V,電流5-8mA,綠色的壓降為1.75-1.82V,電流3-5mA,橙色的壓降為1.7-1.8V,電流3-5mA 蘭色的壓降為3.1-3.3V,電流8-10mA,白色的壓降為3-3.2V,電流10-15mA,(供電電壓5V,LED直徑為5mm)

      74HC573真值表如下:

      (原文件名:74hc573.jpg)

      引用圖片

      通過這個真值表我們可以看出。當(dāng)OutputEnable引腳接低電平的時候,并且LatchEnable引腳為高電平的時候,Q端電平與D端電平相同。結(jié)合我們的LED硬件連接圖可以知道LED_CS端為高電平時候,P0口電平的變化即Q端的電平的變化,進(jìn)而引起LED的亮滅變化。由于單片機(jī)的驅(qū)動能力有限,在此,74HC573的主要作用就是起一個輸出驅(qū)動的作用。需要注意的是,通過74HC573的最大電流是有限制的,否則可能會燒壞74HC573這個芯片。

      上面這個圖是從74HC573的DATASHEET中截取出來的,從上可以看出,每個引腳允許通過的最大電流為35mA 整個芯片允許通過的最大電流為75mA。在我們設(shè)計相應(yīng)的驅(qū)動電路時候,這些參數(shù)是相當(dāng)重要的,而且是最容易被初學(xué)者所忽略的地方。同時在設(shè)計的時候,要留出一定量的余量出來,不能說單個引腳允許通過的電流為35mA,你就設(shè)計為35mA,這個時候你應(yīng)該把設(shè)計的上限值定在20mA左右才能保證能夠穩(wěn)定的工作。

      (設(shè)計相應(yīng)驅(qū)動電路時候,應(yīng)該仔細(xì)閱讀芯片的數(shù)據(jù)手冊,了解每個引腳的驅(qū)動能力,以及整個芯片的驅(qū)動能力)

      了解了相應(yīng)的硬件后,我們再來編寫驅(qū)動程序。

      首先定義LED的接口 #define LED P0 然后為亮滅常數(shù)定義一個宏,由硬件連接圖可以,當(dāng)P0輸出為低電平時候LED亮,P0輸出為高電平時,LED熄滅。

      #define LED_ON()LED = 0x00 //所有LED亮 #define LED_OFF()LED = 0xff //所有LED熄滅

      下面到了重點了,究竟該如何釋放CPU,避免其做延時空等待這樣的事情呢。很簡單,我們?yōu)橄到y(tǒng)產(chǎn)生一個1MS的時標(biāo)。假定LED需要亮500MS,熄滅500MS,那么我們可以對這個1MS的時標(biāo)進(jìn)行計數(shù),當(dāng)這個計數(shù)值達(dá)到500時候,清零該計數(shù)值,同時把LED的狀態(tài)改變。unsigned int g_u16LedTimeCount = 0;//LED計數(shù)器

      unsigned char g_u8LedState = 0;//LED狀態(tài)標(biāo)志, 0表示亮,1表示熄滅

      void LedProcess(void){ if(0 == g_u8LedState)//如果LED的狀態(tài)為亮,則點亮LED { LED_ON();} else //否則熄滅LED { LED_OFF();} }

      void LedStateChange(void){ if(g_bSystemTime1Ms)//系統(tǒng)1MS時標(biāo)到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED計數(shù)器加一

      if(g_u16LedTimeCount >= 500)//計數(shù)達(dá)到500,即500MS到了,改變LED的狀態(tài)。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

      上面有一個變量沒有提到,就是g_bSystemTime1Ms。這個變量可以定義為位變量或者是其它變量,在我們的定時器中斷函數(shù)中對其置位,其它函數(shù)使用該變量后,應(yīng)該對其復(fù)位(清0)。我們的主函數(shù)就可以寫成如下形式(示意代碼)void main(void){ while(1){ LedProcess();LedStateChange();} }

      因為LED的亮或者滅依賴于LED狀態(tài)變量(g_u8LedState)的改變,而狀態(tài)變量的改變,又依賴于LED計數(shù)器的計數(shù)值(g_u16LedTimeCount,只有計數(shù)值達(dá)到一定后,狀態(tài)變量才改變)所以,兩個函數(shù)都沒有堵塞CPU的地方。讓我們來從頭到尾分析一遍整個程序的流程。

      程序首先執(zhí)行LedProcess();函數(shù)

      因為g_u8LedState 的初始值為0(見定義,對于全局變量,在定義的時候最好給其一個確定的值)所以LED被點亮,然后退出LedStateChange()函數(shù),執(zhí)行下一個函數(shù)LedStateChange()在函數(shù)LedStateChange()內(nèi)部首先判斷1MS的系統(tǒng)時標(biāo)是否到了,如果沒有到就直接退出函數(shù),如果到了,就把時標(biāo)清0以便下一個時標(biāo)消息的到來,同時對LED計數(shù)器加一,然后再判斷LED計數(shù)器是否到達(dá)我們預(yù)先想要的值500,如果沒有,則退出函數(shù),如果有,對計數(shù)器清0,以便下次重新計數(shù),同時把LED狀態(tài)變量取反,然后退出函數(shù)。

      由上面整個流程可以知道,CPU所做的事情,就是對一些計數(shù)器加一,然后根據(jù)條件改變狀態(tài),再根據(jù)這個狀態(tài)來決定是否點亮LED。這些函數(shù)執(zhí)行所花的時間都是相當(dāng)短的,如果主程序中還有其它函數(shù),則CPU會順次往下執(zhí)行下去。對于其它的函數(shù)(如果有的話)也要采取同樣的措施,保證其不堵塞CPU,如果全部基于這種方法設(shè)計,那么對于不是非常龐大的系統(tǒng),我們的系統(tǒng)依舊可以保證多個任務(wù)(多個函數(shù))同時執(zhí)行。系統(tǒng)的實時性得到了一定的保證,從宏觀上看來,就是多個任務(wù)并發(fā)執(zhí)行。

      好了,這一章就到此為止,讓我們總結(jié)一下,究竟有哪些需要注意的吧。

      (1)無論什么時候我們都要以實際應(yīng)用的角度去考慮程序的編寫。

      (2)無論什么時候都不要讓CPU白白浪費等待,尤其是延時(超過1MS)這樣的地方。(3)設(shè)計相應(yīng)驅(qū)動電路時候,應(yīng)該仔細(xì)閱讀芯片的數(shù)據(jù)手冊,了解每個引腳的驅(qū)動能力,以及整個芯片的驅(qū)動能力

      (4)最重要的是,如何去釋放CPU(參考本章的例子),這是寫出合格程序的基礎(chǔ)。

      附完整程序代碼(基于電子工程師之家的單片機(jī)開發(fā)板)

      #include

      sbit LED_SEG = P1^4;//數(shù)碼管段選 sbit LED_DIG = P1^5;//數(shù)碼管位選 sbit LED_CS11 = P1^6;//led控制位 sbit ir=P1^7;#define LED P0 //定義LED接口

      bit g_bSystemTime1Ms = 0;// 1MS系統(tǒng)時標(biāo) unsigned int g_u16LedTimeCount = 0;//LED計數(shù)器

      unsigned char g_u8LedState = 0;//LED狀態(tài)標(biāo)志, 0表示亮,1表示熄滅

      #define LED_ON()LED = 0x00;//所有LED亮 #define LED_OFF()LED = 0xff;//所有LED熄滅

      void Timer0Init(void){ TMOD &= 0xf0;TMOD |= 0x01;//定時器0工作方式1 TH0 = 0xfc;//定時器初始值 TL0 = 0x66;TR0 = 1;ET0 = 1;} void LedProcess(void){ if(0 == g_u8LedState)//如果LED的狀態(tài)為亮,則點亮LED { LED_ON();} else //否則熄滅LED { LED_OFF();} }

      void LedStateChange(void){ if(g_bSystemTime1Ms)//系統(tǒng)1MS時標(biāo)到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED計數(shù)器加一

      if(g_u16LedTimeCount >= 500)//計數(shù)達(dá)到500,即500MS到了,改變LED的狀態(tài)。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

      void main(void){ Timer0Init();EA = 1;LED_CS11 = 1;//74HC595輸出允許

      LED_SEG = 0;//數(shù)碼管段選和位選禁止(因為它們和LED共用P0口)LED_DIG = 0;while(1){ LedProcess();LedStateChange();} }

      void Time0Isr(void)interrupt 1 { TH0 = 0xfc;//定時器重新賦初值 TL0 = 0x66;g_bSystemTime1Ms = 1;//1MS時標(biāo)標(biāo)志位置位 }

      “從單片機(jī)初學(xué)者邁向單片機(jī)工程師”

      第三章----模塊化編程初識

      好的開始是成功的一半

      通過上一章的學(xué)習(xí),我想你已經(jīng)掌握了如何在程序中釋放CPU了。希望能夠繼續(xù)堅持下去。一個良好的開始是成功的一半。我們今天所做的一切都是為了在單片機(jī)編程上做的更好。

      在談?wù)摻裉斓闹黝}之前,先說下我以前的一些經(jīng)歷。在剛開始接觸到C語言程序的時候,由于學(xué)習(xí)內(nèi)容所限,寫的程序都不是很大,一般也就幾百行而矣。所以所有的程序都完成在一個源文件里面。記得那時候大一參加學(xué)校里的一個電子設(shè)計大賽,調(diào)試了一個多星期,所有程序加起來大概將近1000行,長長的一個文件,從上瀏覽下來都要好半天。出了錯誤簡單的語法錯誤還好定位,其它一些錯誤,往往找半天才找的到。那個時候開始知道了模塊化編程這個東西,也嘗試著開始把程序分模塊編寫。最開始是把相同功能的一些函數(shù)(譬如1602液晶的驅(qū)動)全部寫在一個頭文件(.h)文件里面,然后需要調(diào)用的地方包含進(jìn)去,但是很快發(fā)現(xiàn)這種方法有其局限性,很容易犯重復(fù)包含的錯誤。

      而且調(diào)用起來也很不方便。很快暑假的電子設(shè)計大賽來臨了,學(xué)校對我們的單片機(jī)軟件編程進(jìn)行了一些培訓(xùn)。由于學(xué)校歷年來參加國賽和省賽,因此積累了一定數(shù)量的驅(qū)動模塊,那些日子,老師每天都會布置一定量的任務(wù),讓我們用這些模塊組合起來,完成一定功能。而正是那些日子模塊化編程的培訓(xùn),使我對于模塊化編程有了更進(jìn)一步的認(rèn)識。并且程序規(guī)范也開始慢慢注意起來。此后的日子,無論程序的大小,均采用模塊化編程的方式去編寫。很長一段時間以來,一直有單片機(jī)愛好者在QQ上和我一起交流。有時候,他們會發(fā)過來一些有問題的程序源文件,讓我?guī)兔π薷囊幌隆M瑯邮情L長的一個文件,而且命名極不規(guī)范,從頭看下來,著實是痛苦,說實話,還真不如我重新給他們寫一個更快一些,此話到不假,因為手頭積累了一定量的模塊,在完成一個新的系統(tǒng)時候,只需要根據(jù)上層功能需求,在底層模塊的支持下,可以很快方便的完成。而不需要從頭到尾再一磚一瓦的重新編寫。藉此,也可以看出模塊化編程的一個好處,就是可重復(fù)利用率高。下面讓我們揭開模塊化神秘面紗,一窺其真面目。C語言源文件 *.c 提到C語言源文件,大家都不會陌生。因為我們平常寫的程序代碼幾乎都在這個XX.C文件里面。編譯器也是以此文件來進(jìn)行編譯并生成相應(yīng)的目標(biāo)文件。作為模塊化編程的組成基礎(chǔ),我們所要實現(xiàn)的所有功能的源代碼均在這個文件里。理想的模塊化應(yīng)該可以看成是一個黑盒子。即我們只關(guān)心模塊提供的功能,而不管模塊內(nèi)部的實現(xiàn)細(xì)節(jié)。好比我們買了一部手機(jī),我們只需要會用手機(jī)提供的功能即可,不需要知曉它是如何把短信發(fā)出去的,如何響應(yīng)我們按鍵的輸入,這些過程對我們用戶而言,就是是一個黑盒子。

      在大規(guī)模程序開發(fā)中,一個程序由很多個模塊組成,很可能,這些模塊的編寫任務(wù)被分配到不同的人。而你在編寫這個模塊的時候很可能就需要利用到別人寫好的模塊的借口,這個時候我們關(guān)心的是,它的模塊實現(xiàn)了什么樣的接口,我該如何去調(diào)用,至于模塊內(nèi)部是如何組織的,對于我而言,無需過多關(guān)注。而追求接口的單一性,把不需要的細(xì)節(jié)盡可能對外部屏蔽起來,正是我們所需要注意的地方。C語言頭文件 *.h 談及到模塊化編程,必然會涉及到多文件編譯,也就是工程編譯。在這樣的一個系統(tǒng)中,往往會有多個C文件,而且每個C文件的作用不盡相同。在我們的C文件中,由于需要對外提供接口,因此必須有一些函數(shù)或者是變量提供給外部其它文件進(jìn)行調(diào)用。假設(shè)我們有一個LCD.C文件,其提供最基本的LCD的驅(qū)動函數(shù) LcdPutChar(char cNewValue);//在當(dāng)前位置輸出一個字符 而在我們的另外一個文件中需要調(diào)用此函數(shù),那么我們該如何做呢?

      頭文件的作用正是在此??梢苑Q其為一份接口描述文件。其文件內(nèi)部不應(yīng)該包含任何實質(zhì)性的函數(shù)代碼。我們可以把這個頭文件理解成為一份說明書,說明的內(nèi)容就是我們的模塊對外提供的接口函數(shù)或者是接口變量。同時該文件也包含了一些很重要的宏定義以及一些結(jié)構(gòu)體的信息,離開了這些信息,很可能就無法正常使用接口函數(shù)或者是接口變量。但是總的原則是:不該讓外界知道的信息就不應(yīng)該出現(xiàn)在頭文件里,而外界調(diào)用模塊內(nèi)接口函數(shù)或者是接口變量所必須的信息就一定要出現(xiàn)在頭文件里,否則,外界就無法正確的調(diào)用我們提供的接口功能。因而為了讓外部函數(shù)或者文件調(diào)用我們提供的接口功能,就必須包含我們提供的這個接口描述文件----即頭文件。同時,我們自身模塊也需要包含這份模塊頭文件(因為其包含了模塊源文件中所需要的宏定義或者是結(jié)構(gòu)體),好比我們平常所用的文件都是一式三份一樣,模塊本身也需要包含這個頭文件。

      下面我們來定義這個頭文件,一般來說,頭文件的名字應(yīng)該與源文件的名字保持一致,這樣我們便可以清晰的知道哪個頭文件是哪個源文件的描述。

      于是便得到了LCD.C的頭文件LCD.h 其內(nèi)容如下。#ifndef _LCD_H_ #define _LCD_H_ extern LcdPutChar(char cNewValue);#endif

      這與我們在源文件中定義函數(shù)時有點類似。不同的是,在其前面添加了extern 修飾符表明其是一個外部函數(shù),可以被外部其它模塊進(jìn)行調(diào)用。#ifndef _LCD_H_ #define _LCD_H_ #endif

      這個幾條條件編譯和宏定義是為了防止重復(fù)包含。假如有兩個不同源文件需要調(diào)用LcdPutChar(char cNewValue)這個函數(shù),他們分別都通過#include “Lcd.h”把這個頭文件包含了進(jìn)去。在第一個源文件進(jìn)行編譯時候,由于沒有定義過 _LCD_H_ 因此 #ifndef _LCD_H_ 條件成立,于是定義_LCD_H_ 并將下面的聲明包含進(jìn)去。在第二個文件編譯時候,由于第一個文件包含時候,已經(jīng)將_LCD_H_定義過了。因此#ifndef _LCD_H_ 不成立,整個頭文件內(nèi)容就沒有被包含。假設(shè)沒有這樣的條件編譯語句,那么兩個文件都包含了extern LcdPutChar(char cNewValue);就會引起重復(fù)包含的錯誤。

      不得不說的typedef 很多朋友似乎了習(xí)慣程序中利用如下語句來對數(shù)據(jù)類型進(jìn)行定義 #define uint unsigned int #define uchar unsigned char 然后在定義變量的時候 直接這樣使用 uint g_nTimeCounter = 0;不可否認(rèn),這樣確實很方便,而且對于移植起來也有一定的方便性。但是考慮下面這種情況你還會 這么認(rèn)為嗎?

      #define PINT unsigned int * //定義unsigned int 指針類型 PINT g_npTimeCounter, g_npTimeState;那么你到底是定義了兩個unsigned int 型的指針變量,還是一個指針變量,一個整形變量呢?而你的初衷又是什么呢,想定義兩個unsigned int 型的指針變量嗎?如果是這樣,那么估計過不久就會到處抓狂找錯誤了。

      慶幸的是C語言已經(jīng)為我們考慮到了這一點。typedef 正是為此而生。為了給變量起一個別名我們可以用如下的語句

      typedef unsigned int uint16;//給指向無符號整形變量起一個別名 uint16 typedef unsigned int * puint16;//給指向無符號整形變量指針起一個別名 puint16 在我們定義變量時候便可以這樣定義了:

      uint16 g_nTimeCounter = 0;//定義一個無符號的整形變量 puint16 g_npTimeCounter;//定義一個無符號的整形變量的指針

      在我們使用51單片機(jī)的C語言編程的時候,整形變量的范圍是16位,而在基于32的微處理下的整形變量是32位。倘若我們在8位單片機(jī)下編寫的一些代碼想要移植到32位的處理器上,那么很可能我們就需要在源文件中到處修改變量的類型定義。這是一件龐大的工作,為了考慮程序的可移植性,在一開始,我們就應(yīng)該養(yǎng)成良好的習(xí)慣,用變量的別名進(jìn)行定義。如在8位單片機(jī)的平臺下,有如下一個變量定義 uint16 g_nTimeCounter = 0;如果移植32單片機(jī)的平臺下,想要其的范圍依舊為16位。

      可以直接修改uint16 的定義,即

      typedef unsigned short int uint16;這樣就可以了,而不需要到源文件處處尋找并修改。

      將常用的數(shù)據(jù)類型全部采用此種方法定義,形成一個頭文件,便于我們以后編程直接調(diào)用。文件名 MacroAndConst.h 其內(nèi)容如下:

      #ifndef _MACRO_AND_CONST_H_ #define _MACRO_AND_CONST_H_

      typedef unsigned int uint16;typedef unsigned int UINT;typedef unsigned int uint;typedef unsigned int UINT16;typedef unsigned int WORD;typedef unsigned int word;typedef int int16;typedef int INT16;typedef unsigned long uint32;

      typedef unsigned long UINT32;typedef unsigned long DWORD;typedef unsigned long dword;typedef long int32;typedef long INT32;typedef signed char int8;typedef signed char INT8;typedef unsigned char byte;typedef unsigned char BYTE;typedef unsigned char uchar;typedef unsigned char UINT8;typedef unsigned char uint8;typedef unsigned char BOOL;#endif

      至此,似乎我們對于源文件和頭文件的分工以及模塊化編程有那么一點概念了。那么讓我們趁熱打鐵,將上一章的我們編寫的LED閃爍函數(shù)進(jìn)行模塊劃分并重新組織進(jìn)行編譯。

      在上一章中我們主要完成的功能是P0口所驅(qū)動的LED以1Hz的頻率閃爍。其中用到了定時器,以及LED驅(qū)動模塊。因而我們可以簡單的將整個工程分成三個模塊,定時器模塊,LED模塊,以及主函數(shù) 對應(yīng)的文件關(guān)系如下

      main.c Timer.h?Timer.c--Led.h?Led.c--在開始重新編寫我們的程序之前,先給大家講一下如何在KEIL中建立工程模板吧,這個模板是我一直沿用至今。希望能夠給大家一點啟發(fā)。

      下面的內(nèi)容就主要以圖片為主了。同時輔以少量文字說明。我們以芯片AT89S52為例。

      (原文件名:1.jpg)

      引用圖片

      (原文件名:2.jpg)

      引用圖片

      (原文件名:3.jpg)

      引用圖片

      (原文件名:4.jpg)

      引用圖片

      (原文件名:5.jpg)

      引用圖片

      (原文件名:6.jpg)

      引用圖片

      (原文件名:7.jpg)

      引用圖片

      (原文件名:8.jpg)

      引用圖片

      (原文件名:9.jpg)

      引用圖片

      (原文件名:10.jpg)

      引用圖片

      (原文件名:11.jpg)

      引用圖片

      (原文件名:12.jpg)

      引用圖片

      (原文件名:13.jpg)

      引用圖片

      (原文件名:14.jpg)

      引用圖片

      (原文件名:15.jpg)

      引用圖片

      (原文件名:16.jpg)

      引用圖片

      (原文件名:17.jpg)

      引用圖片

      (原文件名:18.jpg)

      引用圖片

      (原文件名:19.jpg)

      引用圖片

      (原文件名:20.jpg)

      引用圖片

      (原文件名:21.jpg)

      引用圖片

      (原文件名:22.jpg)

      引用圖片

      OK,到此一個簡單的工程模板就建立起來了,以后我們再新建源文件和頭文件的時候,就可以直接保存到src文件目錄下面了。

      下面我們開始編寫各個模塊文件。

      首先編寫Timer.c 這個文件主要內(nèi)容就是定時器初始化,以及定時器中斷服務(wù)函數(shù)。其內(nèi)容如下。#include

      bit g_bSystemTime1Ms = 0;// 1MS系統(tǒng)時標(biāo)

      void Timer0Init(void){ TMOD &= 0xf0;TMOD |= 0x01;//定時器0工作方式1 TH0 = 0xfc;//定時器初始值 TL0 = 0x66;TR0 = 1;ET0 = 1;}

      void Time0Isr(void)interrupt 1 { TH0 = 0xfc;//定時器重新賦初值 TL0 = 0x66;g_bSystemTime1Ms = 1;//1MS時標(biāo)標(biāo)志位置位 }

      由于在Led.c文件中需要調(diào)用我們的g_bSystemTime1Ms變量。同時主函數(shù)需要調(diào)用Timer0Init()初始化函數(shù),所以應(yīng)該對這個變量和函數(shù)在頭文件里作外部聲明。以方便其它函數(shù)調(diào)用。

      Timer.h 內(nèi)容如下。#ifndef _TIMER_H_ #define _TIMER_H_

      extern void Timer0Init(void);extern bit g_bSystemTime1Ms;#endif

      完成了定時器模塊后,我們開始編寫LED驅(qū)動模塊。Led.c 內(nèi)容如下:

      #include #include “MacroAndConst.h” #include “Led.h” #include “Timer.h”

      static uint16 g_u16LedTimeCount = 0;//LED計數(shù)器 static uint8 g_u8LedState = 0;//LED狀態(tài)標(biāo)志, 0表示亮,1表示熄滅

      #define LED P0 //定義LED接口

      #define LED_ON()LED = 0x00;//所有LED亮 #define LED_OFF()LED = 0xff;//所有LED熄滅

      void LedProcess(void){ if(0 == g_u8LedState)//如果LED的狀態(tài)為亮,則點亮LED { LED_ON();} else //否則熄滅LED { LED_OFF();} }

      void LedStateChange(void){ if(g_bSystemTime1Ms)//系統(tǒng)1MS時標(biāo)到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED計數(shù)器加一

      if(g_u16LedTimeCount >= 500)//計數(shù)達(dá)到500,即500MS到了,改變LED的狀態(tài)。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

      這個模塊對外的借口只有兩個函數(shù),因此在相應(yīng)的Led.h 中需要作相應(yīng)的聲明。Led.h 內(nèi)容: #ifndef _LED_H_ #define _LED_H_

      extern void LedProcess(void);extern void LedStateChange(void);#endif

      這兩個模塊完成后,我們將其C文件添加到工程中。然后開始編寫主函數(shù)里的代碼。如下所示:

      #include #include “MacroAndConst.h” #include “Timer.h” #include “Led.h”

      sbit LED_SEG = P1^4;//數(shù)碼管段選 sbit LED_DIG = P1^5;//數(shù)碼管位選 sbit LED_CS11 = P1^6;//led控制位

      void main(void){ LED_CS11 = 1;//74HC595輸出允許

      LED_SEG = 0;//數(shù)碼管段選和位選禁止(因為它們和LED共用P0口)LED_DIG = 0;Timer0Init();EA = 1;while(1){ LedProcess();LedStateChange();} }

      整個工程截圖如下:

      至此,第三章到此結(jié)束。

      一起來總結(jié)一下我們需要注意的地方吧

      [color=#FF0000]1.C語言源文件(*.c)的作用是什么 2.C語言頭文件(*.h)的作用是什么 3.typedef 的作用 4.工程模板如何組織

      5.如何創(chuàng)建一個多模塊(多文件)的工程

      “從單片機(jī)初學(xué)者邁向單片機(jī)工程師”之KEY主題討論

      按鍵程序編寫的基礎(chǔ)

      從這一章開始,我們步入按鍵程序設(shè)計的殿堂。在基于單片機(jī)為核心構(gòu)成的應(yīng)用系統(tǒng)中,用戶輸入是必不可少的一部分。輸入可以分很多種情況,譬如有的系統(tǒng)支持PS2鍵盤的接口,有的系統(tǒng)輸入是基于編碼器,有的系統(tǒng)輸入是基于串口或者USB或者其它輸入通道等等。在各種輸入途徑中,更常見的是,基于單個按鍵或者由單個鍵盤按照一定排列構(gòu)成的矩陣鍵盤(行列鍵盤)。我們這一篇章主要討論的對象就是基于單個按鍵的程序設(shè)計,以及矩陣鍵盤的程序編寫?!虬存I檢測的原理

      常見的獨立按鍵的外觀如下,相信大家并不陌生,各種常見的開發(fā)板學(xué)習(xí)板上隨處可以看到他們的身影。

      (原文件名:1.jpg)

      引用圖片

      總共有四個引腳,一般情況下,處于同一邊的兩個引腳內(nèi)部是連接在一起的,如何分辨兩個引腳是否處在同一邊呢?可以將按鍵翻轉(zhuǎn)過來,處于同一邊的兩個引腳,有一條突起的線將他們連接一起,以標(biāo)示它們倆是相連的。如果無法觀察得到,用數(shù)字萬用表的二極管擋位檢測一下即可。搞清楚這點非常重要,對于我們畫PCB的時候的封裝很有益。

      它們和我們的單片機(jī)系統(tǒng)的I/O口連接一般如下:

      (原文件名:2.jpg)

      引用圖片

      對于單片機(jī)I/O內(nèi)部有上拉電阻的微控制器而言,還可以省掉外部的那個上拉電阻。簡單分析一下按鍵檢測的原理。當(dāng)按鍵沒有按下的時候,單片機(jī)I/O通過上拉電阻R接到VCC,我們在程序中讀取該I/O的電平的時候,其值為1(高電平);當(dāng)按鍵S按下的時候,該I/O被短接到GND,在程序中讀取該I/O的電平的時候,其值為0(低電平)。這樣,按鍵的按下與否,就和與該按鍵相連的I/O的電平的變化相對應(yīng)起來。結(jié)論:我們在程序中通過檢測到該I/O口電平的變化與否,即可以知道按鍵是否被按下,從而做出相應(yīng)的響應(yīng)。一切看起來很美好,是這樣的嗎?

      ◎現(xiàn)實并非理想

      在我們通過上面的按鍵檢測原理得出上述的結(jié)論的時候,其實忽略了一個重要的問題,那就是現(xiàn)實中按鍵按下時候的電平變化狀態(tài)。我們的結(jié)論是基于理想的情況得出來的,就如同下面這幅按鍵按下時候?qū)?yīng)電平變化的波形圖一樣:

      (原文件名:3.jpg)

      引用圖片

      而實際中,由于按鍵的彈片接觸的時候,并不是一接觸就緊緊的閉合,它還存在一定的抖動,盡管這個時間非常的短暫,但是對于我們執(zhí)行時間以us為計算單位的微控制器來說,它太漫長了。因而,實際的波形圖應(yīng)該如下面這幅示意圖一樣。

      (原文件名:4.jpg)

      引用圖片

      這樣便存在這樣一個問題。假設(shè)我們的系統(tǒng)有這樣功能需求:在檢測到按鍵按下的時候,將某個I/O的狀態(tài)取反。由于這種抖動的存在,使得我們的微控制器誤以為是多次按鍵的按下,從而將某個I/O的狀態(tài)不斷取反,這并不是我們想要的效果,假如該I/O控制著系統(tǒng)中某個重要的執(zhí)行的部件,那結(jié)果更不是我們所期待的。于是乎有人便提出了軟件消除抖動的思想,道理很簡單:抖動的時間長度是一定的,只要我們避開這段抖動時期,檢測穩(wěn)定的時候的電平不久可以了嗎?聽起來確實不錯,而且實際應(yīng)用起來效果也還可以。于是,各種各樣的書籍中,在提到按鍵檢測的時候,總也不忘說道軟件消抖。就像下面的偽代碼所描述的一樣。(假設(shè)按鍵按下時候,低電平有效)

      If(0 == io_KeyEnter)//如果有鍵按下了 { Delayms(20);//先延時20ms避開抖動時期

      If(0 == io_KeyEnter)//然后再檢測,如果還是檢測到有鍵按下 { return KeyValue;//是真的按下了,返回鍵值 } else { return KEY_NULL //是抖動,返回空的鍵值 } while(0 == io_KeyEnter);//等待按鍵釋放 }

      所以合理的分配好微控制的處理時間,是編寫按鍵程序的基礎(chǔ)。?乍看上去,確實挺不錯,實際中呢?在實際的系統(tǒng)中,一般是不允許這么樣做的。為什么呢?首先,這里的Delayms(20), 讓微控制器在這里白白等待了20 ms 的時間,啥也沒干,考慮我在《學(xué)會釋放CPU》一章中所提及的幾點,這是不可取的。其次while(0 == io_KeyEnter);更是程序設(shè)計中的大忌(極少的特殊情況例外)。任何非極端情況下,都不要使用這樣語句來堵塞微控制器的執(zhí)行進(jìn)程。原本是等待按鍵釋放,結(jié)果CPU就一直死死的盯住該按鍵,其它事情都不管了,那其它事情不干了嗎?你同意別人可不會同意

      ◎消除抖動有必要嗎? 的確,軟件上的消抖確實可以保證按鍵的有效檢測。但是,這種消抖確實有必要嗎?有人提出了這樣的疑問。抖動是按鍵按下的過程中產(chǎn)生的,如果按鍵沒有按下,抖動會產(chǎn)生嗎?如果沒有按鍵按下,抖動也會在I/O上出現(xiàn),我會立刻把這個微控制器錘了,永遠(yuǎn)不用這樣一款微控制器。所以抖動的出現(xiàn)即意味著按鍵已經(jīng)按下,盡管這個電平還沒有穩(wěn)定。所以只要我們檢測到按鍵按下,即可以返回鍵值,問題的關(guān)鍵是,在你執(zhí)行完其它任務(wù)的時候,再次執(zhí)行我們的按鍵任務(wù)的時候,抖動過程還沒有結(jié)束,這樣便有可能造成重復(fù)檢測。所以,如何在返回鍵值后,避免重復(fù)檢測,或者在按鍵一按下就執(zhí)行功能函數(shù),當(dāng)功能函數(shù)的執(zhí)行時間小于抖動時間時候,如何避免再次執(zhí)行功能函數(shù),就成為我們要考慮的問題了。這是一個仁者見仁,智者見智的問題,就留給大家去思考吧。所以消除抖動的目的是:防止按鍵一次按下,多次響應(yīng)。

      “從單片機(jī)初學(xué)者邁向單片機(jī)工程師”之KEY主題討論

      基于狀態(tài)轉(zhuǎn)移的獨立按鍵程序設(shè)計)的那種,有一個小液晶屏,還有四個按鍵,功能是時鐘,鬧鐘以及秒表。在調(diào)整時間的時候,短按+鍵每次調(diào)整值加一,長按的時候調(diào)整值連續(xù)增加。小的時候很好奇,這樣的功能到底是如何實現(xiàn)的呢,今天就讓我們來剖析它的原理吧。? 本章所描述的按鍵程序要達(dá)到的目的:檢測按鍵按下,短按,長按,釋放。即通過按鍵的返回值我們可以獲取到如下的信息:按鍵按下(短按),按鍵長按,按鍵連_發(fā),按鍵釋放。不知道大家還記得小時候玩過的電子鐘沒有,就是外形類似于CALL 機(jī)(CALL 機(jī),好像是很古老的東西了 狀態(tài)在生活中隨處可見。譬如早上的時候,鬧鐘把你叫醒了,這個時候,你便處于清醒的狀態(tài),馬上你就穿衣起床洗漱吃早餐,這一系列事情就是你在這個狀態(tài)做的事情。做完這些后你會去等車或者開車去上班,這個時候你就處在上班途中的狀態(tài)?..中午下班時間到了,你就處于中午下班的狀態(tài),諸如此類等等,在每一個狀態(tài)我們都會做一些不同的事情,而總會有外界條件促使我們轉(zhuǎn)換到另外一種狀態(tài),譬如鬧鐘叫醒我們了,下班時間到了等等。對于狀態(tài)的定義出發(fā)點不同,考慮的方向不同,或者會有些許細(xì)節(jié)上面的差異,但是大的狀態(tài)總是相同的。生活中的事物同樣遵循同樣的規(guī)律,譬如,用一個智能充電器給你的手機(jī)電池充電,剛開始,它是處于快速充電狀態(tài),隨著電量的增加,電壓的升高,當(dāng)達(dá)到規(guī)定的電壓時候,它會轉(zhuǎn)換到恒壓充電??偠灾?xì)心觀察,你會發(fā)現(xiàn)生活中的總總都可以歸結(jié)為一個個的狀態(tài),而狀態(tài)的變換或者轉(zhuǎn)移總是由某些條件引起同時伴隨著一些動作的發(fā)生。我們的按鍵亦遵循同樣的規(guī)律,下面讓我們來簡單的描繪一下它的狀態(tài)流程轉(zhuǎn)移圖。

      (原文件名:1.jpg)

      引用圖片

      下面對上面的流程圖進(jìn)行簡要的分析。首先按鍵程序進(jìn)入初始狀態(tài)S1,在這個狀態(tài)下,檢測按鍵是否按下,如果有按下,則進(jìn)入按鍵消抖狀態(tài)2,在下一次執(zhí)行按鍵程序時候,直接由按鍵消抖狀態(tài)進(jìn)入按鍵按下狀態(tài)3,在此狀態(tài)下檢測按鍵是否按下,如果沒有按鍵按下,則返回初始狀態(tài)S1,如果有則可以返回鍵值,同時進(jìn)入長按狀態(tài)S4,在長按狀態(tài)下每次進(jìn)入按鍵程序時候?qū)Π存I時間計數(shù),當(dāng)計數(shù)值超過設(shè)定閾值時候,則表明長按事件發(fā)生,同時進(jìn)入按鍵連_發(fā)狀態(tài)S5。如果按鍵鍵值為空鍵,則返回按鍵釋放狀態(tài)S6,否則繼續(xù)停留在本狀態(tài)。在按鍵連_發(fā)狀態(tài)下,如果按鍵鍵值為空鍵則返回按鍵釋放狀態(tài)S6,如果按鍵時間計數(shù)超過連_發(fā)閾值,則返回連_發(fā)按鍵值,清零時間計數(shù)后繼續(xù)停留在本狀態(tài)。

      看了這么多,也許你已經(jīng)有一個模糊的概念了,下面讓我們趁熱打鐵,一起來動手編寫按鍵驅(qū)動程序吧。

      下面是我使用的硬件的連接圖。

      (原文件名:2.jpg)

      引用圖片

      硬件連接很簡單,四個獨立按鍵分別接在P3^0------P3^3四個I/O上面。

      因為51單片機(jī)I/O口內(nèi)部結(jié)構(gòu)的限制,在讀取外部引腳狀態(tài)的時候,需要向端口寫1.在51單片機(jī)復(fù)位后,不需要進(jìn)行此操作也可以進(jìn)行讀取外部引腳的操作。因此,在按鍵的端口沒有復(fù)用的情況下,可以省略此步驟。而對于其它一些真正雙向I/O口的單片機(jī)來說,將引腳設(shè)置成輸入狀態(tài),是必不可少的一個步驟。下面的程序代碼初始化引腳為輸入。void KeyInit(void){ io_key_1 = 1;io_key_2 = 1;io_key_3 = 1;io_key_4 = 1;} 根據(jù)按鍵硬件連接定義按鍵鍵值

      #define KEY_VALUE_1 0x0e #define KEY_VALUE_2 0x0d #define KEY_VALUE_3 0x0b #define KEY_VALUE_4 0x07 #define KEY_NULL 0x0f 下面我們來編寫按鍵的硬件驅(qū)動程序。

      根據(jù)第一章所描述的按鍵檢測原理,我們可以很容易的得出如下的代碼: static uint8 KeyScan(void){ if(io_key_1 == 0)return KEY_VALUE_1;if(io_key_2 == 0)return KEY_VALUE_2;if(io_key_3 == 0)return KEY_VALUE_3;if(io_key_4 == 0)return KEY_VALUE_4;return KEY_NULL;} 其中io_key_1等是我們按鍵端口的定義,如下所示: sbit io_key_1 = P3^0;sbit io_key_2 = P3^1;sbit io_key_3 = P3^2;sbit io_key_4 = P3^3;

      KeyScan()作為底層按鍵的驅(qū)動程序,為上層按鍵掃描提供一個接口,這樣我們編寫的上層按鍵掃描函數(shù)可以幾乎不用修改就可以拿到我們的其它程序中去使用,使得程序復(fù)用性大大提高。同時,通過有意識的將與底層硬件連接緊密的程序和與硬件無關(guān)的代碼分開寫,使得程序結(jié)構(gòu)層次清晰,可移植性也更好。對于單片機(jī)類的程序而言,能夠做到函數(shù)級別的代碼重用已經(jīng)足夠了。在編寫我們的上層按鍵掃描函數(shù)之前,需要先完成一些宏定義。//定義長按鍵的TICK數(shù),以及連_發(fā)間隔的TICK數(shù) #define KEY_LONG_PERIOD 100 #define KEY_CONTINUE_PERIOD 25

      //定義按鍵返回值狀態(tài)(按下,長按,連_發(fā),釋放)#define KEY_DOWN 0x80 #define KEY_LONG 0x40 #define KEY_CONTINUE 0x20 #define KEY_UP 0x10

      //定義按鍵狀態(tài)

      #define KEY_STATE_INIT 0 #define KEY_STATE_WOBBLE 1 #define KEY_STATE_PRESS 2 #define KEY_STATE_LONG 3 #define KEY_STATE_CONTINUE 4 #define KEY_STATE_RELEASE 5

      接著我們開始編寫完整的上層按鍵掃描函數(shù),按鍵的短按,長按,連按,釋放等等狀態(tài)的判斷均是在此函數(shù)中完成。對照狀態(tài)流程轉(zhuǎn)移圖,然后再看下面的函數(shù)代碼,可以更容易的去理解函數(shù)的執(zhí)行流程。完整的函數(shù)代碼如下: void GetKey(uint8 *pKeyValue){ static uint8 s_u8KeyState = KEY_STATE_INIT;static uint8 s_u8KeyTimeCount = 0;static uint8 s_u8LastKey = KEY_NULL;//保存按鍵釋放時候的鍵值 uint8 KeyTemp = KEY_NULL;

      KeyTemp = KeyScan();//獲取鍵值

      switch(s_u8KeyState){ case KEY_STATE_INIT : { if(KEY_NULL!=(KeyTemp)){ s_u8KeyState = KEY_STATE_WOBBLE;} } break;

      case KEY_STATE_WOBBLE : //消抖 { s_u8KeyState = KEY_STATE_PRESS;} break;

      case KEY_STATE_PRESS : { if(KEY_NULL!=(KeyTemp)){ s_u8LastKey = KeyTemp;//保存鍵值,以便在釋放按鍵狀態(tài)返回鍵值 KeyTemp |= KEY_DOWN;//按鍵按下 s_u8KeyState = KEY_STATE_LONG;} else { s_u8KeyState = KEY_STATE_INIT;} } break;

      case KEY_STATE_LONG : { if(KEY_NULL!=(KeyTemp)){ if(++s_u8KeyTimeCount > KEY_LONG_PERIOD){ s_u8KeyTimeCount = 0;KeyTemp |= KEY_LONG;//長按鍵事件發(fā)生 s_u8KeyState = KEY_STATE_CONTINUE;} } else { s_u8KeyState = KEY_STATE_RELEASE;} } break;

      case KEY_STATE_CONTINUE : { if(KEY_NULL!=(KeyTemp)){ if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD){ s_u8KeyTimeCount = 0;KeyTemp |= KEY_CONTINUE;} } else { s_u8KeyState = KEY_STATE_RELEASE;} } break;

      case KEY_STATE_RELEASE : { s_u8LastKey |= KEY_UP;KeyTemp = s_u8LastKey;s_u8KeyState = KEY_STATE_INIT;} break;

      default : break;} *pKeyValue = KeyTemp;//返回鍵值 } 關(guān)于這個函數(shù)內(nèi)部的細(xì)節(jié)我并不打算花過多筆墨去講解。對照著按鍵狀態(tài)流程轉(zhuǎn)移圖,然后去看程序代碼,你會發(fā)現(xiàn)其實思路非常清晰。最能讓人理解透徹的,莫非就是將整個程序自己看懂,然后想象為什么這個地方要這樣寫,抱著思考的態(tài)度去閱讀程序,你會發(fā)現(xiàn)自己的程序水平會慢慢的提高。所以我更希望的是你能夠認(rèn)認(rèn)真真的看完,然后思考。也許你會收獲更多。

      不管怎么樣,這樣的一個程序已經(jīng)完成了本章開始時候要求的功能:按下,長按,連按,釋放。事實上,如果掌握了這種基于狀態(tài)轉(zhuǎn)移的思想,你會發(fā)現(xiàn)要求實現(xiàn)其它按鍵功能,譬如,多鍵按下,功能鍵等等,亦相當(dāng)簡單,在下一章,我們就去實現(xiàn)它。

      在主程序中我編寫了這樣的一段代碼,來演示我實現(xiàn)的按鍵功能。void main(void){ uint8 KeyValue = KEY_NULL;uint8 temp = 0;LED_CS11 = 1;//流水燈輸出允許 LED_SEG = 0;LED_DIG = 0;Timer0Init();KeyInit();EA = 1;while(1){ Timer0MainLoop();KeyMainLoop(&KeyValue);

      if(KeyValue ==(KEY_VALUE_1 | KEY_DOWN))P0 = ~1;if(KeyValue ==(KEY_VALUE_1 | KEY_LONG))P0 = ~2;if(KeyValue ==(KEY_VALUE_1 | KEY_CONTINUE)){ P0 ^= 0xf0;} if(KeyValue ==(KEY_VALUE_1 | KEY_UP))P0 = 0xa5;} } 按住第一個鍵,可以清晰的看到P0口所接的LED的狀態(tài)的變化。當(dāng)按鍵按下時候,第一個LED燈亮,等待2 S后第二個LED亮,第一個熄滅,表示長按事件發(fā)生。再過500 ms 第5~8個LED閃爍,表示連按事件發(fā)生。當(dāng)釋放按鍵時候,P0口所接的LED的狀態(tài)為: 滅亮滅亮亮滅亮滅,這也正是P0 = 0xa5這條語句的功能。

      第三篇:C語言學(xué)習(xí)心得

      C語言學(xué)習(xí)心得體會

      在科技高度發(fā)展的今天,計算機(jī)在人們生活、學(xué)習(xí)和工作中的作用越來越突出。我們都知道C語言是一種計算機(jī)語言,而作為計算機(jī)專業(yè)的我們學(xué)習(xí)它,就更有助于我們更好的了解計算機(jī),與計算機(jī)進(jìn)行交流,因此,C語言的學(xué)習(xí)對我們尤其重要。

      說實話這個學(xué)期剛開始學(xué)C語言的時候,很感覺迷茫,對里面的好多東西很陌生,在操作運用的時候感到很棘手,畢竟,萬事開頭難嘛。在此之前從沒有接觸過C語言,有點摸不著頭腦。可是在上機(jī)過后,我覺得編程是很有趣的一件事,哪怕你編出的只是一個很簡單的程序都會讓你很有成就感。我知道要學(xué)好C語言不容易,可是我決定完成這件不容易的事。

      在課堂上老師從最基本的跟我們講起,要學(xué)好C語言就必須要先懂得最基本的語法知識,看課本是必需的。我覺得看不懂也沒關(guān)系,盡力去理解就好了,在對知識有了一個大致的了解過后,就要上機(jī)實踐。學(xué)習(xí)C語言一定要動手,只看不做,眼高手低是不行的。

      最開始我們打書上的例題,熟悉程序,慢慢的開始試著編程。老師說過在編程時要理清自己的思路,然后再轉(zhuǎn)換成C語言中的語言,這個時候就更要動手了,只有通過上機(jī)操作才能驗證自己程序的正確性。執(zhí)行程序,不要害怕錯誤,其實,我覺得錯誤是好的,知道了自己所學(xué)知識的不足,并根據(jù)提示改正程序中發(fā)生的錯誤,一種成就感油然而生,覺得自己的付出都是值得的。

      我覺得良好的編程習(xí)慣是學(xué)好C語言的重要因素,只有勤動手,多動腦才能學(xué)好C語言,光說不練是不行的。在學(xué)習(xí)的時候,不會的一定要問明白,可以求助于老師,同學(xué),不要自己一個人鉆牛角尖,既浪費時間又學(xué)不到東西。

      上課的時候也一定要認(rèn)真聽,老師講的肯定是最重要的,錯過了就是一大筆損失,認(rèn)真聽講才可以提高學(xué)習(xí)效率嘛。另外我覺得在編程之前,要把自己的想法寫在紙上,如果是簡單一點的程序不需要這樣,如果程序比較復(fù)雜,就寫下來,這樣可以讓思路更加清晰。輸入程序時一定要認(rèn)真,不要把“,”與“;”混淆,用scanf的時候不要忘記“&”,用“switch”要記得“break”,用if,while的時候注意不要加“;”,“{}”“()”一定要配對,不要多也不要少一半,“=”與“==”的區(qū)別要清楚,要勤查優(yōu)先級,要記住一些基本的,例如兩個值之間的調(diào)換怎么寫等等。

      學(xué)習(xí)C語言需要的是堅持下去的毅力和認(rèn)真對待每次錯誤的耐心,還有孜孜不倦的努力。擁有一個良好的心態(tài),相信自己,你就會發(fā)現(xiàn)學(xué)好C語言不再困難!

      第四篇:c語言學(xué)習(xí)心得

      導(dǎo)語:c語言是一個有序的學(xué)習(xí),學(xué)了最基本的替換,然后擴(kuò)展到循環(huán),嵌套,條理很清楚,不是一個零散的知識,實際上所有的課程都如此,不過通過實訓(xùn)我也知道了自己的不足,存在的很多問題。

      c語言學(xué)習(xí)心得

      首先我要告訴大家的是:第一,學(xué)習(xí)無捷徑!對于學(xué)習(xí)編程而言,你現(xiàn)在的付出將來都是有回報的。但是,學(xué)習(xí)C語言也需要方法。

      我遇到過很多學(xué)習(xí)C語言的人,包括我以前的同學(xué),很多人都是學(xué)到一半就放棄了。那么為什么那么多人學(xué)習(xí)C語言都半途而廢呢?原因就是他們找不到正確的學(xué)習(xí)方法!在學(xué)習(xí)的過程中四處碰壁,興趣和自信心逐漸被消耗殆盡。對他們來說學(xué)習(xí)C語言是一件很痛苦的事!

      事實上學(xué)習(xí)編程是一件很好玩、很有趣、很有意思也很有前途的事情!那么學(xué)習(xí)C語言有什么好的方法呢?根據(jù)我自己多年的總結(jié),以及很多編程前輩的經(jīng)驗,主要有以下幾個方面:

      1)分清主次

      學(xué)習(xí)C語言最忌諱的就是不分主次,這是絕大多數(shù)學(xué)習(xí)C語言的同學(xué)都會犯的錯誤!我們剛開始學(xué)習(xí)的時候只需要將那些最重要的、最核心的學(xué)會就已經(jīng)很好了!先將最精髓的東西提煉出來,再將整個C語言學(xué)一遍,從全局上把握C語言。對于那些次要的,有需要再學(xué),沒有需要也可以不學(xué)。

      2)一定要多上機(jī),多“敲”代碼

      編程是一門實踐性的學(xué)科,絕對不是理論。如果不動手“敲”代碼的話,永遠(yuǎn)都學(xué)不會編程。很多問題只有在“敲代碼”的時候才能發(fā)現(xiàn),才會有更加深刻的體會、領(lǐng)悟和理解。而不是靠死記硬背書中的注意點,那樣真的很痛苦。我在學(xué)習(xí)編程的時候從來都不會刻意記憶什么注意點,這些知識點都是在不停“敲代碼”的過程中,自然而然地融入我的身體中的。你們一定要記住一句話:“程序是寫出來的,不是看書看出來的!”

      3)要“敲代碼”,必學(xué)盲打

      盲打是學(xué)習(xí)編程最基本的技能。就算你C語言學(xué)得很好,達(dá)到了“思想在鍵盤上飛舞”的境界,但是如果你不會盲打,那你想“飛”也“飛”不起來!所以,不會盲打會非常影響你的學(xué)習(xí)效率。

      4)要學(xué)會記筆記

      編程需要不斷地積累。我們一定要學(xué)會模仿別人優(yōu)秀的代碼、優(yōu)秀的算法,然后將它記下來。一定要站在巨人的肩膀上學(xué)習(xí)。但是我們的記憶能力是有限的,時間長了難免會遺忘,所以一定要學(xué)會記筆記。一有心得、體會、感悟就寫下來,這些都是很珍貴的。

      我們在記筆記的時候,如果眼前沒有計算機(jī)則可以先寫在紙上,但事后一定要將它整理成電子版。整理成電子版看起來會很方便、舒適,還可以隨意地增添和刪改,保存時間也長。

      c語言學(xué)習(xí)心得

      說到我學(xué)習(xí)C語言時,真是用千言萬語呀!抄程序是最笨的方法但我認(rèn)為它是進(jìn)步最快的方法,抄程序是積累經(jīng)驗的時候,而做項目才是真正把所學(xué)為所用的時候,可以說只有你做一個 大點的項目出來才能真正是說明你學(xué)到了東西,你會用所學(xué)的東西,要不然就算你學(xué)的再多,不會用也沒用。

      做學(xué)問特別是計算機(jī)一定要做的精準(zhǔn),比如說一個語句一個關(guān)鍵 字,你一定要把它幾乎所有的用方法都能清楚明白,一句話要學(xué)精了,語法掌握住了,接下來就是寫程序了,其實抄程序并不是說一直沒有目的去抄,你會發(fā)現(xiàn)當(dāng)你抄一段時間以后就不用再抄 了,因為大部分都是一樣的你一看就知道怎么寫了,當(dāng)你一看到程序就知道它什么功能,那里有錯的時候,那你的成績可是進(jìn)步不小啊,這并不需要太多的時間,只 要你用心一個月足夠了。

      跟大家說幾點經(jīng)驗:

      1.在學(xué)習(xí)的時候一定要注意這幾點,不會的一定要問明白,不管誰能讓他教會你知識就是你的了,要學(xué)會讓知識為我所用。在看書的時候一定要做好標(biāo)記,特別是不懂的地方一定要標(biāo)明是什么意思。

      2.在學(xué)習(xí)語言的時候一定要記住動手,不要只說不做,這樣會行成眼高手低,不管什么樣的程序都要親手做過才能說會了,不要整天說我不會學(xué)不會,其實 是你不想學(xué),只是你下決心抄一個月程序,我保證我能有大的進(jìn)步,其實當(dāng)你抄到一周到兩周的時候你就會特想抄,因為你會發(fā)現(xiàn)程序你能看懂了,能幫別人調(diào)程序 了,有一種成就感呀!它會讓你更加努力的去學(xué)習(xí)。

      3.再一點是我建議大家在上課的時候少看課本,課本要在下課的時候看特別是上課前一定要先看看課本,上課的時候呢就不要看了,不要老師講到那個問題 了你馬上在書上找,這樣不好,會影響你的注意力,其實還真不如注意聽老師講呢?因為你要是一邊聽一邊看課本,你是看到了書上的答案但是老師的思路你沒有聽 到,而要是你不看的話,你聽明白了思路,一定是想迫切的看到結(jié)果,這個時候看課本才是記的最死的時候,學(xué)習(xí)要的是就是個效率嗎?

      4.提醒大家學(xué)習(xí)要講效率,我發(fā)現(xiàn)有很多同學(xué)天天學(xué)習(xí),每天最早到教室,走的又最晚,別人玩他在學(xué),別人學(xué)他也學(xué),可是別的一天30%的學(xué)習(xí)時間卻 比他們一天50%以上的學(xué)習(xí)時間的效率要高上不僅僅是幾倍的問題,所以我要說的是不要對別人說你天天在學(xué)習(xí),要說你天天在進(jìn)步,學(xué)習(xí)不是要你學(xué)習(xí)了多久 是,是要你學(xué)了多少東西的,你學(xué)一小時還沒有別人學(xué)一分鐘的效率高,難道自己不是浪費時間嗎?不想學(xué)的時候就不要學(xué),出去玩一會兒再來學(xué)說不定會有更好的 效果。希望這些經(jīng)驗?zāi)芨蠹曳窒硪幌?,最后還是要跟大家強(qiáng)調(diào)一點,抄程序是學(xué)好C語言的最好最快的方法。

      第五篇:C語言學(xué)習(xí)心得整合

      C語言學(xué)習(xí)心得整合1.C語言是一種結(jié)構(gòu)化的程序設(shè)計語言

      結(jié)構(gòu)化程序設(shè)計的優(yōu)點是便于分工合作,便于調(diào)試、維護(hù)和擴(kuò)充。這種程序設(shè)計方法是將一個大程序分成若千個模塊,每個模塊完成一個功能,由一個總控模塊來控制和協(xié)調(diào)各個模塊來實現(xiàn)總的功能;因此,這種程序設(shè)計方法又稱為模塊化程序設(shè)計方法。在C語言中,函數(shù)是構(gòu)成C諳言程序的最小模塊。

      C語言是結(jié)構(gòu)化程序設(shè)計語言,它具有構(gòu)成三種基本結(jié)構(gòu)模式的語句,這種結(jié)構(gòu)化程序設(shè)計應(yīng)具有的三種基本結(jié)構(gòu)模式如下:

      順序結(jié)構(gòu)棋式

      它將由若干條順序執(zhí)行的語句構(gòu)成,這是程序設(shè)計的最基本形式。

      分支結(jié)構(gòu)模式

      C語言中具有條件語句和開關(guān)語句,它將會構(gòu)成各種需要的分支結(jié)構(gòu)模式。

      .循環(huán)結(jié)構(gòu)模式

      C語言中提供了三種循環(huán)語句(for循環(huán),whil。循環(huán)和do-while循環(huán)),用它們可以構(gòu)成各種循環(huán)結(jié)構(gòu)模式。

      第一,完全的結(jié)構(gòu)化程序設(shè)計語言不允許使用goto語句,因為goto語句會破壞結(jié)構(gòu)化。第二,完完全的結(jié)構(gòu)化程序設(shè)計語言要求一個模塊只有一個入口和一個出口,這樣便于結(jié)

      構(gòu)化的管理。但是,C語言程序中允許函數(shù)使用多個返回語句(return語句)。

      getch()函數(shù)用于從鍵盤讀取一個按鍵,并將值返回(即按下一個鍵時便立即返回,無需像getchar()那樣要按回車)

      由于這個特性,所以經(jīng)常用于windows系統(tǒng)中執(zhí)行C語言程序時進(jìn)行屏幕暫停,以便觀察

      結(jié)果。,getch()

      函數(shù)用于從鍵盤讀取一個按鍵,并將值返回(即按下一個鍵時便立即返回,無需像getchar()那樣要按回車),由于這個特性,所以經(jīng)常用于windows系統(tǒng)中執(zhí)行C語言程序時進(jìn)行屏幕暫停,以便觀察結(jié)果。

      下載Matlab學(xué)習(xí)心得系列——001. 先從C語言談起word格式文檔
      下載Matlab學(xué)習(xí)心得系列——001. 先從C語言談起.doc
      將本文檔下載到自己電腦,方便修改和收藏,請勿使用迅雷等下載。
      點此處下載文檔

      文檔為doc格式


      聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn)自行上傳,本網(wǎng)站不擁有所有權(quán),未作人工編輯處理,也不承擔(dān)相關(guān)法律責(zé)任。如果您發(fā)現(xiàn)有涉嫌版權(quán)的內(nèi)容,歡迎發(fā)送郵件至:645879355@qq.com 進(jìn)行舉報,并提供相關(guān)證據(jù),工作人員會在5個工作日內(nèi)聯(lián)系你,一經(jīng)查實,本站將立刻刪除涉嫌侵權(quán)內(nèi)容。

      相關(guān)范文推薦

        單片機(jī)c語言學(xué)習(xí)心得轉(zhuǎn)載

        單片機(jī)c語言學(xué)習(xí)心得(一)相信很多愛好電子的朋友,對單片機(jī)這個詞應(yīng)該都不會陌生了吧。不過有些朋友可能只聽說他叫單片機(jī),他的全稱是什么也許并不太清楚,更不用說他的英文全稱和......

        c語言學(xué)習(xí)心得體會

        c語言學(xué)習(xí)心得體會 想學(xué)好C語言,交流是必須的,尤其是學(xué)習(xí)C語言的新手,以下是小編搜集并整理的c語言有關(guān)內(nèi)容。 c語言學(xué)習(xí)心得體會學(xué)習(xí)C語言已經(jīng)一年多,對C也算得上半個入門者,期......

        C語言學(xué)習(xí)心得報告

        C語言學(xué)習(xí)心得報告 看了一段時間的《C和指針》,這樣看的效果不是很好,看著書本當(dāng)時是懂了,有點恍然大悟的感覺,但是發(fā)現(xiàn)并不能真正的把理解的內(nèi)容加入到自己程序當(dāng)中,不能很好的......

        c語言學(xué)習(xí)心得體會

        c語言學(xué)習(xí)心得體會 c語言學(xué)習(xí)心得體會一:c語言學(xué)習(xí)心得體會 c語言作為一種計算機(jī)的語言,我們學(xué)習(xí)它,有助于我們更好的了解計算機(jī),與計算機(jī)進(jìn)行交流,因此,c語言的學(xué)習(xí)對我們尤其......

        第四章C語言學(xué)習(xí)心得

        第四章C語言學(xué)習(xí)心得 2011/8/13 SHMILY 1、 C語言邏輯值,非零表示”真”,零表示”假” 2、 關(guān)系運算符和關(guān)系表達(dá)式: 小于 < 小于等于 大于等于 >= 等于 == 不等于 != 有兩......

        單片機(jī)C語言學(xué)習(xí)心得

        8、指針的使用 8.1 在定義的時候,*ap中的‘*’是指針類型說明符;在進(jìn)行指針預(yù)算時,x = *ap 中的‘*’是指針運算符。 8.2 如果在已定義好的指針變量,并引用,即 int *ap, int a; a......

        C語言學(xué)習(xí)心得體會

        篇一:c語言心得體會作業(yè) c語言心得體會 學(xué)習(xí)c語言已經(jīng)一個學(xué)期了,剛開始學(xué)習(xí)的時候老是感覺力不從心。雖然認(rèn)真聽課了,但是并不能理解它。這種情況到了后來才有所改變。 之所......

        c語言學(xué)習(xí)心得體會

        c語言學(xué)習(xí)心得體會 c語言作為一種計算機(jī)的語言,我們學(xué)習(xí)它,有助于我們更好的了解計算機(jī),與計算機(jī)進(jìn)行交流。其語言功能豐富、表達(dá)能力強(qiáng)、使用靈活方便、既具有高級語言的優(yōu)點,......