




下載本文檔
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
從單片機初學者邁向單片機工程師:一 周第一章----寫 二 周第二章----學會 三 四 五 六 第一章——按鍵程序編寫的基 七 八 綜合應用之一——如何設計復雜的多任務程 九 綜合應用之二——DS1320/DS18B20應 一、LED周第一章-----寫需要的學習資料。但是,隨著學習的深入,會慢慢發現,網絡提供的東西是有限度的,好像大部且也只是僅僅作功能性的演示。于是有些人選擇了放棄,或者是轉移到其他上面去了,而部分人選擇了繼續摸索下去,結合市面上的書籍,然后在網絡上鍥而不舍的搜料,再從牛人的只言片語中了之后也不會在網絡上輕易自己的學習成果。如此惡性循環下去,也就不難理解為什么初級的學習資悲,的點周最大了這樣的一個名字“從片機初學者向單片機程師”。名字大挺響亮,給力大后給@@.二、LED周第二章-----學會在以后的系列文章中,以51內核的單片機為載體,C語言為編程語言,開發環境為KEILuv3。至于夠滿足系統的需求。在學習掌握C語言的同時,也還需要利用閑余的時間去學習了解匯編語言。void{LedInit();{LED=ONLED=OFF;}}果就是LED以1HZ的頻率進行閃爍。下面讓分析上面的程序有沒有什么問題。看來看出,好像很正常的啊,能有什么問題呢?這個時候應該換一個思路去想了。試想,整個程序除了LEDONLED=OFF余的時間,全消耗在了DelayMs(500)這兩個函數有哪些任務需要執行,也不要讓它停留在某個地方空轉消耗CPU時間。下面讓從另外一個角度來考慮如何點亮一顆LED。 示一般的LED10~20mA而低電流LED的工作電流在2mA以下(亮度與普通發光管相同。在上圖中可知,當Q1~Q8引腳上面的電平為低電平時,LED發光。通過LED的電流約(VCC-Vd)/RA2。其中Vd為LED導通后的壓降,約為1.7V左右。這個導通壓降根據LED顏色的不紅色的壓降為1.82-1.88V,電流5-8mA,橙色的壓降為1.7-1.8V,電流3-5mA白色的壓降為3-3.2V10-15mA,(供電電壓5V,LED直徑為5mm)通過這個真值表可以看出。當OutputEnable引腳接低電平的時候,并且LatchEnable引腳為高電平的時候,Q端電平與D端電平相同。結合的LED硬件連接圖可以知道LED_CS端為高電平時候,P0口電平的變化即Q端的電平的變化,進而引起LED的亮滅變化。由于單片機的驅動能力有限,在此,否則可能會燒壞74HC573這個。為35mA整個允許通過的最大電流為75mA。在設計相應的驅動電路時候,這些參數是相當重要腳允許通過的電流35mA,你就設計35mA,這個時候你應該把設計的上限值定20mA左右才能保首先定義LED的接口#defineLED電平時,LED熄滅。#define LED= //所有LED#define LED= //所有LED熄下面到了重點了,究竟該如何CPU,避免其做延時空等待這樣的事情呢。很簡單,為系統產當這個計數值達到500時候,清零該計數值,同時把LED的狀態改變。unsignedintg_u16LedTimeCount0 //LED計數unsignedcharg_u8LedState0 //LED狀態標志0表示亮,1表示熄void{if(0g_u8LedState)//如果LED的狀態為亮,則點亮{} //否則熄滅{}}void{ {g_bSystemTime1Ms=0;g_u16LedTimeCount++; //LED計數器加一if(g_u16LedTimeCount500計數500,500MS到了,改變LED{g_u16LedTimeCount=0;g_u8LedState=!g_u8LedState;}}}上面有一個變量沒有提到,就是g_bSystemTime1Ms。這個變量可以定義為位變量或者是其它變量,在的定時器中斷函數中對其置位,其它函數使用該變量后,應該對其復位(清0)。voidmain(void){{LedProcess();}}因為LED的亮或者滅依賴于LED狀態變量(g_u8LedState)的改變,而狀態變量的改變,又依賴LED計數器的計數值(g_u16LedTimeCount,只有計數值達到一定后,狀態變量才改變)所以,兩個函數都沒有堵塞CPU的地方。讓來從頭到尾分析一遍整個程序的流程。程序首先執行LedProcess函0以便下一個時標消息的到來LED計數器加一,然后再LED計數器是否到達預先想要的值500,如果沒有,則退出函數,如果有,對計數器清0,以便下次重新計數,同時把LED狀態變量取反,然后退出函數。狀態來決定是否點亮LED。這些函數執行所花的時間都是相當短的,如果主程序中還有其它函數,則以及整個的驅動能力sbitLED_SEG=P1^4數碼管段選sbitLED_DIG=P1^5數碼管位選sbitLED_CS11P1^6;//led控制位sbitir=P1^7;#defineLED bitg_bSystemTime1Ms0 unsignedintg_u16LedTimeCount0LED計數unsignedcharg_u8LedState0 //LED狀態標志0表示亮,1表示熄#define LED= //所有LED#define LED= void{TMOD&=0xf0TMOD|=0x01; //定時器0工作方式1TH0=0xfc; TL0=0x66;TR0=1;ET0=1;}void{if(0g_u8LedState)//如果LED的狀態為亮,則點亮{} //否則熄滅{}}void{ //系統1mS時標{g_bSystemTime1Ms=0;g_u16LedTimeCount //LED計數器加一if(g_u16LedTimeCount500)//計數達500,500mS到了,改變LED{g_u16LedTimeCount=0;g_u8LedState=!g_u8LedState }}}void{EA=1;LED_CS11174HC595LED_SEG=0;//數碼管段選和位選(因為它們和LED共用P0口)LED_DIG=0;{LedProcess();}}{TH0= 0xfc; TL0=0x66; }點熄 通過上一章的學習,你已經掌握了如何在程序中CPU了。希望能夠1000培訓。由于學校來參加國賽和省賽,因此積累了一定數量的驅動模塊,那些日子,老師每天都會布置一定量的任務,讓用這些模塊組合起來,完成一定用模塊化編程的方式去編寫。很長一段時間以來,一直有單片機者在上也可以看出模塊化編程的一個好處,就是可重復利用率高。下面讓揭開模塊 不管模塊的實現細節。好比 些過程對用戶而言,就是是一個黑盒子。模塊的接口,這個時候關心的是,它的模塊實現了什么樣的接口,我該如何去調用,至于模塊是如何組織的,對于我而言,無需過多關注。而追求接口 假設有一個LCD.C文件,其提供最基本的LCD的驅動函LcdPutChar(charcNewValue);//在當前位置輸出一個字符而在的另外一個文件中需要調用此函數,那么該如何做呢?因而為了讓外部函數或者文件調用提供的接口功能就必須包含提供的 #define LcdPutChar(charcNewValue);extern 需要調用LcdPutChar(charcNewValue)由于沒有定義過_LCD_H_因此#ifndef_LCD_H_條件成立,于是定義含時候,已經將_LCD_H_定義過了。因此#ifndef_LCD_H_不成立,整個頭文件內容就沒有被包含。假設沒有這樣的條件編譯語句,那么兩個文件都包含了externLcdPutChar(charcNewValue);就會引起重復包含的錯誤。不得不說的很多朋友似乎了程序中利用如下語句來對數據類型進行定#defineuintunsigned#defineucharunsigneduintg_nTimeCounter=0#definePINTunsignedint*//定義unsignedintPINTg_npTimeCounter,g_npTimeState慶幸的是C語言已經為考慮到了這一點。typedef正是為此而生。為了typedefunsignedintuint16;//給指向無符號整形變量起一個別名uint16typedefunsignedint*puint16;/puint16uint16g_nTimeCounter=0;//定義一個無符號的整形變量puint16g_npTimeCounter;在使用51單片機的C語言編程的時候,整形變量的范圍是16位,而在基于32的微處理下的整形變量是32位。倘若在8位單片機下編寫的一些代碼想要移植到32位的處理器上,那么很可能就需要在源文件中到處修改變量如在8位單片機的平臺下,有如下一個變量定 g_nTimeCounter=0可以直接修改uint16的定義,即typedefunsignedshort uint16文件名 在上一章中主要完成的功能是P0口所驅動的LED以1Hz的頻率閃爍。其中用到了定時器,以及LED驅動模塊。因而可以簡單的將整個工程分成三個模塊,定時器模塊,LED模塊,以及主函數Timer.hTimer.c-- 下面的內容就主要以為主了。同時輔以少量文字說明。 AT89S52為例。就可以直接保存到src文件 #includebitg_bSystemTime1Ms0 void{TMOD&=0xf0TMOD|=0x01; //定時器0工作方式1TH0= 0xfc; TL0=0x66;TR0=1;ET0=1;}{TH0=0xfc; TL0=0x66; }由于在Led.c文件中需要調用的g_bSystemTime1Ms變量。同時主函數需要調用Timer0Init()初始化函數,所以應該對這個變量和函數在頭文件里作外部。以方便其它Timer.h內容如下。#ifndef_TIMER_H_#define_TIMER_H_Led.c#include#include#include#includestaticuint16g_u16LedTimeCount0LEDstaticuint8g_u8LedState0 #defineLED #define LED #define LED void{)//{} {}}void{ {g_bSystemTime1Ms=0;g_u16LedTimeCount++; {g_u16LedTimeCount=0;g_u8LedState=!g_u8LedState }}}Led.h內容:#ifndef_LED_H_externvoidLedProcess(void)#include#include#include#includesbitLED_SEG=P1^4;//數碼管段選sbitLED_DIG=P1^5數碼管位選sbitLED_CS11=P1^6;//led控制位void{LED_SEG=0;//數碼管段選和位選(因為它們和LED共用P0口)LED_DIG=0;Timer0Init();EA=1;{LedProcess();}}typedef 的控制方式分類的,除了型,還有PFM型和、PFM混合型。脈寬調制式開關型穩壓電路是在讀起來有點晦澀難懂。其實簡單的說來,技術就是通過調整一個周期固定的的占空比,來調節輸出電壓的平均當電壓,電流或者功率等被控量。可以用一個水龍頭來類比,把1S時間分成50開肯定要小的多。同樣的道理,可以通過控制20mS時間里水閥開啟的時間的長短來控制流過的水的多少。那么在1S內平均流出的水流量也就可以被控制了。當調整的占空比時,就會引起電壓或者電流的改變,LED的明暗狀態就會隨之發生相應的變化,大家都知道人眼有一個臨界頻率,當LED的閃爍頻率達到一定的時候,人眼就分辨不出LED是否在閃爍了。就像平常看電視一樣,看起來畫面是連續的,實質不是這個樣子,所有連續動作都是一幀幀分).即最亮的時候其灰度等級為99,為0的時候最暗,也就是熄滅了。于是乎定義的占空比上限為99,下限定義為0#defineLED__LIMIT_MAX #defineLED_ 假定LED的閃爍頻率為50HZ,而亮度變化的范圍為0~99共100等分。則每一等分所占用的時間為1/(50*100)=200us即在改變LED的亮滅狀態時,應該是在200us整數倍時刻時。在這里我的時間內LED可以從暗逐漸變亮,在下一個2S內可以從亮逐漸變暗,然后不斷循環。由于大部分的內容都可以在中斷中完成,因此,的大部分代碼都在Timer.c這個文件中編寫,主函數#include#include#defineLED #define LED= //所有LED#define LED //所有LED#defineLED__LIMIT_MAX #defineLED_ staticuint8s_u8TimeCounter0staticuint8s_u8LedDirection0LED方向控制0:漸亮1:漸staticint8s_s8LedCounter=0;//LED占空void{TMOD&=0xf0TMOD|0x01 //0工作方式TH0= 0xff; TL0=0x47;TR0=1;ET0=1}{staticint8s_s8Counter=0TH0=0xff; TL0=0x47;{s_u8TimeCounter=0if((s_s8LedCounter<=LED__LIMIT_MAX)&&(0=={s_s8LedCounter++{s_u8LedDirection=1s_s8LedCounter=LED__LIMIT_MAX}}if((s_s8LedCounter>=LED__LIMIT_MIN)&&(1=={{s_u8LedDirection=0s_s8LedCounter=LED__LIMIT_MIN}}s_s8Counter=s_s8LedCounter;//獲取LED的占空}if(s_s8Counter>0)//占空比大于0,則點亮LED,否則熄滅{}{}}其實技術在實際生活中應用的非常多。比較典型的應用就是控制電機的轉速,控制充電電流的大小,等等。而隨著技術的發展,也出現了其他類型的技術,如相電壓,線電壓,S LED程序并不是那么的簡單。曾經有人這樣,如果用數碼管和按鍵,做一個簡易的可60%了。此話我深信不疑。我遇到過很多要求:系統有四個按鍵,功能分別是調整,加,減,確定。在按下調整鍵時候,顯示時的1Hz頻率閃爍。如果再次按下調整鍵,則分開閃爍,依次循環,直到按下確{{delayms(2);}}的程序在實際中是根本就無法用的,更何況,它這里也調用了delayms(2)這個函數來延時2ms這更是令深惡痛絕本章的內容正是探討如何解決多任務環境下(OS)的數碼管程序設計的編寫問題。級聯,留給單片機的接口只需要時鐘線,數據線,因此比較節省I/O口。如下圖所示:由上圖可以看出。874HC573驅動。同時每一個數碼管的公設數碼管的掃描頻率為50Hz,則完成一輪掃描的時間就是1/50=20ms。的系統共82082.5ms。假設程序中所有任務如下:{LedDisplay(); rocess(); //AD處理TimerProcess();//時間相關處理DataProcess();//數據處理}LedDisplay()這個任務的執行時間,如同剛才計算的那樣,50Hz頻率掃描,則該函數imerProcess1ms,DataProcess()10ms主函數執行一遍的總時間為20211033msLedDisplay()這個函數的掃描頻50Hz了,而是1/3330.3Hz。這個頻率數碼管已經可以感覺到閃爍了,因此不符合的要求。為什么會出現這種情況呢?剛才計算的50Hz是系統只有2.5ms的時標消息。LedDisplay(),每次接收到這個消息的時候,掃描一位數碼管。這樣8個時標消息過后,所有的數碼管就都被掃描一遍了。可能有朋友會有這樣的疑問:rocess()以及DataProcess()等函數執行的時間還是需要十幾ms啊,在這十幾ms2.5ms的時標消息了,這樣豈不是漏掉了掃描,顯示對于rocess(),TimerProcess(),DataProcess(),等任務依舊要采取此方法對CPU進行,使其執行的時間盡可能短暫,關于如何做到這一點,在以后的講解如何設計多任#include<reg52.h>同時一個位變量,作為2ms時標消息的標志bitg_bSystemTime2Ms=0; 初始化定時器0void{TMOD&=0xf0TMOD|=0x01; //定時器0工作方式1TH0=0xf8; TL0=0xcc;TR0=1;ET0=1;}voidTime0Isr(void)interrupt{TH0 0xf8 TL0=0xcc }然后開始編寫數碼管的動態掃描函數。新建一個C源文件,并包含相應的頭文件。#include<reg52.h>#include"Timer.h"codeuint8{}sbitio_led_seg_cs=P1^4;sbitio_led_bit_cs=P1^5;{ =datio_led_seg_cs=1; io_led_seg_cs=0;}{uint8temptemp=(0x01<<dat);//根據要選通的位計算出位碼 =temp;io_led_bit_cs=1; io_led_bit_cs=0;}{staticuint8s_LedDisPos=0;{g_bSystemTime2Ms=0); if(pBuffer[s_LedDisPos] {}{}{s_LedDisPos=0}}}函數定義一個靜態的變量s_LedDisPos,用來表示掃描數碼管的位置。每當執行該函數一次的時候,s_LedDisPos的值會自加1,表示下次掃描下一個數碼管。然后判斷g_bSystemTime2Ms時標消息是否到了。如果到了,就開始執行相關掃描,否則就直接跳出函數SendLedBitData(8);的作用是消隱因為的系統的段選和位選是共用P0口的。應數碼管的點亮,盡管這個時間很短暫。但是因為的數碼管是不斷掃描的,所以看起來if(pBuffer[s_LedDisPos]=='-') 家可以看到在定義數碼管的段碼表的時候,我多加了一個字節的代碼0xbf:codeuint8{}]]);{s_LedDisPos=0}然后s_LedDisPos自加1,以便下次執行本函數時,掃描下一個數碼管。因為的系統8s_LedDisPos70。否則,沒有任何一個數SendLedBitData(8); 102030#include"Timer.h"sbitio_led=P1^6;void{io_led=0 //發光二極管與數碼管共用P0口,這里掉發光二極管的鎖存輸Timer0Init();g_u8LedDisplayBuffer[0]=1;g_u8LedDisplayBuffer[1]=0;g_u8LedDisplayBuffer[2]='-';g_u8LedDisplayBuffer[3]=2;g_u8LedDisplayBuffer[4]=0;g_u8LedDisplayBuffer[5]='-';g_u8LedDisplayBuffer[6]=3;g_u8LedDisplayBuffer[7]=0;EA=1;{}}知道,之前以及設置了一個掃描數碼管用到的2ms時標。如果再對這個時間。再根據這個1S的時間更新顯示緩沖區即可。聽起來很簡單,讓實現它吧。首先在Timer.c中如下兩個變量:bitg_bTime1S0 staticuint16s_u16ClockTickCount0;//2ms{s_u16ClockTickCount=0;g_bTime1S=1;}void{{g_bTime1S=0;if(++g_u8LedDisplayBuffer[7]==10){{g_u8LedDisplayBuffer[6]=0;{ =0;if(++g_u8LedDisplayBuffer[3]==6){ {{g_u8LedDisplayBuffer[1]=0;}}{{g_u8LedDisplayBuffer[1]=0;}}}}}}}}1001….諸如此類,我就不一一詳述了。{g_u8LedDisplayBuffer[0]=nHour/10;g_u8LedDisplayBuffer[1]=nHour%10;g_u8LedDisplayBuffer[2]='-';g_u8LedDisplayBuffer[3]=nMinute/10;g_u8LedDisplayBuffer[4]=nMinute%10;g_u8LedDisplayBuffer[5]='-';g_u8LedDisplayBuffer[6]=nSecond/10;g_u8LedDisplayBuffer[7]=nSecond%10;}void{io_led=0 Timer0Init()EA=1{}}輸入是必不可少的一部分。輸入可以分很多種情況,譬的系統支持PS2鍵盤的接口,有的系統輸入是基于編,有的系統輸入是基于串口或者USB或者其它輸入通道等等。總共有四個引腳,一般情況下,處于同一邊的兩個引腳是連接在一起的,如何分辨位檢測一下即可。搞清楚這點非常重要,對于畫PCB的時候的封裝很有益。在程序中該I/O的電平的時候,其值為1(高電平);當按鍵S按下的時候,該被短接到GND,在程序中該I/O的電平的時候,其值為0(低電平)。這樣,按鍵的按下與否,就和與該按鍵相連的I/O的電平的變化相對應起來。結論:在程序中通過檢測到而將某個I/O的狀態不斷取反,這并不是想要的效果,假如該I/O控制著系統中某個重 If(0 { If(0 { }{return } }Delayms(20),20ms的時間,啥也沒干,考慮我在等待按鍵,結果CPU就一直死死的盯住該按鍵,其它事情都不管了,那其它事情不干的返回值可以獲取到如下的信息:按鍵按下(短按),按鍵長按,按鍵連發,按鍵。CALL機(CALL的那種,有 S1,在這個狀態下,檢測按鍵是否按下,如果有按下,則進入S1,如果有則可以返S4,在長按狀態下每次進入按鍵程序時候對按鍵時間計數,當S5。如果按鍵鍵值為空鍵,則返回按鍵狀態S6,否則繼續停留在本狀態。在按鍵連發狀態下,如果按P3^0P3^3I/O因為51單片機I/O口結構的限制,在外部引腳狀態的時候,需要向端口寫1。在51單片機復位后,不需要進行此操作也可以進行外部引腳的操作。因此,在按鍵的端voidKeyInit(void){io_key_1=1;io_key_2=1;io_key_3=1;io_key_4=1} staticuint8{if(io_key_1==0)returnKEY_VALUE_1;if(io_key_2==0)returnKEY_VALUE_2;if(io_key_3==0)returnKEY_VALUE_3;if(io_key_4==0)returnKEY_VALUE_4;returnKEY_NULL;}sbitio_key_1=P3^0;sbitio_key_2=P3^1;sbitio_key_3=P3^2;sbitio_key_4=P3^3KeyScan()作為底層按鍵的驅動程序,為上層按鍵掃描提供一個接口,這樣編寫的上層#define #defineKEY_DOWN #defineKEY_LONG #defineKEY_CONTINUE #defineKEY_UP {staticuint8s_u8KeyState=KEY_STATE_INIT;staticuint8s_u8KeyTimeCount=0;staticuint8s_u8LastKey=KEY_NULL;//保存按鍵時候的鍵uint8KeyTemp=KEY_NULLKeyTemp=KeyScan(); {{{}}break {}break{{KeyTemp|KEY_DOWN;//按鍵按下s_u8KeyStateKEY_STATE_LONG}{}}break{{{KeyTemp|KEY_LONG;//長按鍵事件發生s_u8KeyState=KEY_STATE_CONTINUE;}}{}}break{{{s_u8KeyTimeCount=0;}}{}}break{}break
s_u8LastKey|=KEY_UP;KeyTemp=s_u8LastKey;default:break}}多鍵按下,功能鍵等等,亦相當簡單,在下一章,就去實現它。void{uint8temp=0;LED_CS111流水燈輸出允許LED_SEG=0;LED_DIG=0Timer0Init();KeyInit();EA=1;{Timer0MainLoop();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;}}}LED燈亮,等待2S后第二個LED亮,第一個熄滅,表示長按事件發生500ms滅亮滅亮亮滅亮滅,這也正是P0=0xa5這條語句的功能externunsignedcharkeyboard_self();externunsignedcharkeyboard_matrix();externunsignedcharkeyboard_self(){unsignedchartemp=0;//用于P2線上按鍵值staticunsignedchartemp_code=0;//保存按鍵值staticunsignedcharkey_flag=0;//按鍵有效標識{{}}{{{case0xB0:num_key=3;}}}}externunsignedcharkeyboard_matrix(){unsignedcharnum_key=0;//按鍵號unsignedchartemp=0;//P2口線數據{{}}{{{//P2^0case0xBE:num_key=3;//P2^1case0xBD:num_key=7;//P2^2case0xEB:num_key=9;case0x7B://P2^3case0xB7:num_key=15;}}{}}}如當按下P2.0線上的某個鍵時,程序將掃描到這個鍵,而后掃描線不變化, 首先來理解“任務”,所謂任務,就是需要CPU周期“關照”的事件,絕大多數任務不需要CPU一直“關照”,例如啟動ADC的啟動。甚至有些任務“害怕”CPU一直CPU做簡單任務一定很浪費,事實也是如此,絕大多數簡單任務,CPU都是在“空轉(循環踏步延時)CPU不斷“關照,其實這種“不斷”也是有極限的,比如LCD10Hz就可以了,等等。看來,絕大多數任務都是工作在低速頻度。而的CPU一旦運行起來,速度又很快,CPU的CPU分成多個慢的CPU,然后給不同的任務分配不同速度的CPU,這種設想是不是很好呢!確實很好,下面就看如何將“快”的CPU劃分成多個“慢”的CPU。根據這種想法,需要合理分配CPU資源來“關照”不同的任務,最好能夠根據任務CPU3所示的流程圖,各個任務流程獨立,各任務通過全,定的任務,事實上,任務切換就是這樣實現的。44A所示,CPUCPU周期性喚回,根據任務設CPU資源,CPU為不同任務“關照”完成后,CPU,CPU關照的頻度,選擇最快的那個頻度來設定定時器中斷的節拍,一般選擇200Hz,或者100Hz都可以。40HzC=5即可。在程序設計中,C代表著任務運行的節拍控制參數,用delay來描述,不同的任務用task0,{if(task_delay[0]==0)task0();//task0if(task_delay[1]==0)task1();//task1}切換本身就是一個Idle任務。{}#defineTIME_PER_SEC200 #defineCLOCK22118400定義時鐘晶振,單位Hz#defineMAX_TASK externvoidtask0(void);//任務externvoidtask1(void);externvoidtask2(void);externvoidsbitf1Hz=P1^0;//端口定義sbitf5Hz=P1^1;sbitf10Hz=P1^2;sbitf20Hz=unsignedchartask_delay[4];//void{unsignedcharTMODTMOD&0XF0)| TH0=255-CLOCK/TIME_PER_SEC/12/256;TR0=1;ET0 }OS{unsignedcharTH0=255-CLOCK/TIME_PER_SEC/12/256;}/*main主函數*/voidmain(void){{//要產生1hz信號,翻轉周期就是2Hz,以下同if(task_delay[1]==0){task1task_delay[1]TIME_PER_SEC/10;}//要產生5hz信號,翻轉周期就是10Hz,以下同if(task_delay[2]==0){task2task_delay[2]TIME_PER_SEC/20;}{}}void{f1Hz=}void{f5Hz=}void{f10Hz=}void{f20Hz=}5CPU運行情況如圖6所示,黑域表示CPU進程,系統啟動后,CPU將無休止的CPUCPUus級的單片機,1ms可以執行上千條指令,對于像數碼管掃描,鍵盤掃描,LCDCPU“關照”它們的時間并不長,關鍵是等待結果要很長時間。解決的辦法就CPUCPU去關照其它任務。C語言實現,無硬件依賴性,需要單片機的資4個文件。Easy51RTOS.Uv2Keil工程文件,KEIL用戶很熟悉的main.cmain函數和用戶任務task函數文件 Easy51RTOS相關函數文件 #defineTIME_PER_SEC200 #defineCLOCK22118400定義時鐘晶振,單位Hz#defineMAX_TASK externvoidtask0(void);externvoidtask1(void);externvoidtask2(void);externvoidtask3(void);externunsignedchartask_delay[MAX_TASK];externvoidrun(void(*ptask)());externvoid];//void{unsignedchari;for(i=0;i<MAX_TASK;i++)TMODTMOD&0XF0)| TH0=255-TL0=255-CLOCK/TIME_PER_SEC/12%256;TR0=1;ET0 }//OS{unsignedcharTH0=255-CLOCK/TIME_PER_SEC/12/256;}{}#defineTASK_DELAY0TIME_PER_SEC/1 //任務執行頻度為1Hz#defineTASK_DELAY1TIME_PER_SEC/2 //任務執行頻度為2Hz#defineTASK_DELAY2TIME_PER_SEC/10 #defineTASK_DELAY3TIME_PER_SEC/20 //任務執行頻度為20Hzvoid(*codetask[])()={task0,task1,task2,task3};//獲得任務PC指針sbitLED0P1^0;//LEDsbitLED1=P1^1;sbitLED2=P1^2;sbitLED3=/*main主函數*/voidmain(void){unsignedcharos_timer0_init();//節拍發生器定時器初始化EA=1; {({;}//}//}voidtask0(void)//{LED0=task_delay[0]=}voidtask1(void)//{LED1=task_delay[1]=}voidtask2(void)//{LED2=task_delay[2]=}voidtask3(void)//{staticunsignedcharstate=0;//定義靜態局部變量switch(state){caseLED3=state=caseLED3=state=caseLED3=state=state=}}8({;}//task0具有最高優先級,task1、task2、task3優先級依次降voidtask3(void)switch(state)狀態機實現了任務分段,這也是任務內系統延時的法。51單片機開發板來說。ds1302ds18b20應該是比較常見的兩種。ds1302是具有SPI總線接口的時鐘。ds18b20則是具有單總線接口的數字溫 這是DS1302的31個RAM寄存器。在某些應用場合可以應用到。如想要做31RAM寄存器中的任意幾個。當由于對于這些器件的操作基本上按照上面提供的時序圖和相關命令字來進行操83.6V的可充電的電池。當系統正常工作時可以對電池進行涓流充電。當系統掉電時,DS1302由這個電池提供的能量繼續sbitio_DS1302_RST=P2^0;sbitio_DS1302_IO =P2^1;sbitio_DS1302_SCLK=P2^2; #define #defineDS1302_MINUTE_WRITE #defineDS1302_HOUR_WRITE 0x84#defineDS1302_WEEK_WRITE #defineDS1302_DAY_WRITE #defineDS1302_MONTH_WRITE #defineDS1302_YEAR_WRITE #defineDS1302_MINUTE_READ #defineDS1302_HOUR_READ 0x85#defineDS1302_WEEK_READ #defineDS1302_DAY_READ #defineDS1302_MONTH_READ #defineDS1302_YEAR_READ #defineDS1302_SCLK_HIGH io_DS1302_SCLK=1; io_DS1302_SCLK=0;#defineDS1302_IO_HIGH io_DS1302_IO=1;#defineDS1302_IO_LOW io_DS1302_IO=0;#defineDS1302_IO_READio_DS1302_IO#defineDS1302_RST_HIGH io_DS1302_RST=1;#defineDS1302_RST_LOW io_DS1302_RST=0; {uint8Second;uint8Minute;uint8Hour;uint8Day;uint8Week;uint8Month;uint8Year; 要寫的字 {uint8ifor(i=8;i>0;i--{if(Content&0x01{}{}Content>>=1}} Description:從DS1302當前設定的地址一個字節的內容 staticuint8v_DS1302Read_f(void){uint8i,ReadValue;for(i=8;i>0;i--{{}{}}} voidv_DS1302WriteByte_f(uint8Address,uint8Content Description:從DS1302指定的地址寫入一個字節的內 Parameter: voidv_DS1302WriteByte_f(uint8Address,uint8Content){v_DS1302Write_f(Address);} uint8v_DS1302ReadByte_f(uint8Address){uint8ReadValue;v_DS1302Write_f(Address);} voidv_ClockInit_f(void ) voidv_ClockInit_f(void){if(v_DS1302ReadByte_f(0xc1)!=0xf0{v_DS1302WriteByte_f(0x8e,0x00); v_DS1302WriteByte_f(DS1302_YEAR_WRITE,0x08); v_DS1302WriteByte_f(DS1302_WEEK_WRITE,0x04); v_DS1302WriteByte_f(DS1302_MONTH_WRITE,0x12); //月v_DS1302WriteByte_f(DS1302_DAY_WRITE,0x11); v_DS1302WriteByte_f(DS1302_HOUR_WRITE,0x13); v_DS1302WriteByte_f(DS1302_MINUTE_WRITE,0x06); v_DS1302WriteByte_f(DS1302_SECOND_WRITE,0x40); //秒v_DS1302WriteByte_f(0x90,0xa5); v_DS1302WriteByte_f(0xc0,0xf0); v_DS1302WriteByte_f(0x8e,0x80); }} voidv_ClockUpdata_f(void){CurrentTime.Second=v_DS1302ReadByte_f(DS1302_SECOND_READ);CurrentTime.Minute=v_DS1302ReadByte_f(DS1302_MINUTE_READ);CurrentTime.Hour=v_DS1302ReadByte_f(DS1302_HOUR_READ); =v_DS1302ReadByte_f(DS1302_DAY_READ);CurrentTime.Month=v_DS1302ReadByte_f(DS1302_MONTH_READ);CurrentTime.Week=v_DS1302ReadByte_f(DS1302_WEEK_READ);CurrentTime.Year=v_DS1302ReadByte_f(DS1302_YEAR_READ);}DS18B20是單總線的數字溫度傳感器。其與單片機的接口只需要一根數據線即可。當然連片的唯一序列號。在出場的時候就已經設置好,用戶無8位是以56CRC第二個和第三個字節分別存放高溫和低溫告警值。(RAMEEPROM中)第節為配置寄存器。第5~7個字節保留。第9個字節為前8個字節的CRC碼。在對DS18B20進行讀寫編程時,必須嚴格保證讀寫的時序。否則將無法測溫結果。DS18B20DS18B203個步驟:每一次讀這樣才能對DS18B20進行預定的操作。出60~240us的存在低脈沖,主機收到此信號表示復位成功。sbitio_DS18B20_DQ=P2^3#defineDS18B20_DQ_HIGHio_DS18B20_DQ=1;#defineDS18B20_DQ_LOWio_DS18B20_DQ=0;#defineDS18B20_DQ_READio_DS18B20_DQ uint8Temperature[4];voidv_Delay10Us_f(uint16Count{while(--Count{}}*******
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
評論
0/150
提交評論