新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 基于ARM7 TDMI-S CPU的LPC2103的多功能電子鐘

        基于ARM7 TDMI-S CPU的LPC2103的多功能電子鐘

        作者: 時間:2016-11-18 來源:網絡 收藏
        一、課程設計內容與要求

        1)時鐘顯示功能,采用數碼管的動態掃描工作方式,可通過鍵盤分別選擇顯示年或者月/日或者時/分或者分/秒,

        本文引用地址:http://www.104case.com/article/201611/315901.htm

        2)在鍵盤電路中,第一次中斷判斷有無鍵按下,若有按下則實行定時一次,下一次中斷開始掃描。

        3)具有校準年、月、日、時、分的功能。

        4)鬧鐘功能,可按設定的時間鬧時,隨時開啟或關閉鬧鐘。

        二、元器件的介紹

        我在這次的課程設計中使用的主芯片是LPC2103,下面對其進行簡要的介紹。

        LPC2103是一個基于支持實時仿真的16/32位ARM7 TDMI-S CPU的微控制器,并帶有32kB的嵌入高速Flash存儲器,128位寬度的存儲器接口和獨特的加速結構使32位代碼能夠在最大時鐘速率下運行。

        較小的封裝和極低的功耗使LPC2103適用于訪問控制器和POS機等小型應用系統中;由于內置了寬范圍的串行通信接口(2個UART、SPI、SSP和2個I2C)和8KB的片內SRAM,LPC2103也適合用在通信網關和協議轉換器中。32/16位定時器、增強型10位ADC、定時器輸出匹配PWM特性、多達13個邊沿、電平觸發的外部中斷、32條高速GPIO,使得LPC2103微控制器特別適用于工業控制和醫療系統中。

        器件信息:

        管腳信息:(LQFP48管腳配置 )

        三、設計方案

        硬件電路設計介紹:

        1、鍵盤模塊

        整個硬件上總共使用了10個獨立鍵盤,4個是開發板上原有的按鍵,6個是我利用多余的IO引腳焊接的按鍵。按鍵在沒有被按下時,10個按鍵(P0.16 P0.14 P0.15 P0.18 P0.2 P0.3 P0.4 P0.5 P0.27 P0.6 P0.7)通過10K的電阻連接到3.3V的高電平,當按鍵被按下時,和GND相連接,被置為低電平。

        2、數碼管顯示模塊

        使用串行輸入/并行輸出的8位移位寄存器74HC164,該移位寄存器是上升沿有效,將8位筆段碼送給共陽數碼管。(注:DATA為數據輸入端,接P0.26;CLK為時鐘輸入端,接P0.13),與三極管相連的是數碼管的位選端,選擇哪位數碼管被點亮,用于數碼管的動態顯示。

        3、蜂鳴器模塊

        該蜂鳴器是一個無源蜂鳴器,要使用PWM驅動。其連接的是芯片的P0.5端。

        4、LED模塊

        LED的連接如圖所示,當管腳置為低電平時,可以將LED點亮,6個LED分別與LPC2103的P0.25 P0.12 P0.11 P0.10 P0.24 P0.23相連。

        5、UART接口電路模塊

        由于開發板是3.3V供電系統,需要使用電平轉換芯片SP3232E來進行RS-232電平轉換。SP3232E的工作電壓為3.3V,電平轉換電路如圖所示。當使用ISP功能下載程序時,需要將PC機的串口與開發板的串口相連,按下與P0.14相連的按鍵,短接P0.14,在系統復位時,進入ISP狀態,用于下載HEX文件。這個開發板有兩個串口,一個用于下載程序進主芯片,另外一個用于串口在上位機上輸出顯示各個變量的值,可用于調試程序。

        按鍵功能介紹:(用戶使用說明書)

        Key1(P0.16):對顯示的數碼管進行年月日時分秒的顯示切換

        Key2(P0.14):在時間設置或者鬧鐘設置按鍵有效的情況下,進行自加1的向上調節,對時間或者鬧鐘進行設置,其余情況下該按鍵是無效狀態

        Key3(P0.15):在時間設置或者鬧鐘設置按鍵有效的情況下,進行自減1的向下調節,對時間或者鬧鐘進行設置,其余情況下該按鍵是無效狀態

        Key4(P0.18):時間設置按鍵,按下表示開始進行時間設置或跳出時間設置

        Key5(P0.2 ):鬧鐘設置按鍵,按下表示開始進行鬧鐘設置

        Key6(P0.3 ):鬧鐘設置移位按鍵,按下表示進行鬧鐘時和分的切換設置

        Key7(P0.4 ):鬧鐘設置結束按鍵,按下表示跳出鬧鐘設置模式;另外可以關閉蜂鳴器

        Key8(P0.27):鬧鐘開啟按鍵,在Key5按下的情況下按下此鍵表示開啟該組鬧鐘

        Key9(P0.6 ):鬧鐘關閉按鍵,在Key5按下的情況下按下此鍵表示關閉該組鬧鐘

        Key10(P0.7):用于串口輸出信息,可以輸出當前的時間和顯示開啟的鬧鐘設置時間

        當Key10按下時,會在串口中輸出信息如圖所示:(RTC為輸出當前時間,接下來是鬧鐘)

        程序設計及流程圖:
        主程序流程圖:

        時間和鬧鐘設置程序流程圖:

        IO口的初始化模塊:

        LPC2103控制器的引腳都具有多種功能,但是每個引腳在某一時刻只能選擇一種功能。當使用一個功能外設時,如果需要相應的引腳參與(如GPIO等),則必須在實現這一功能之前先設置好引腳的功能,否則無法實現該外設功能。LPC2103具有兩個PINSEL寄存器,PINSEL0和PINSEL1,PINSEL0和PINSEL1寄存器中的每兩個位控制著一個引腳的功能,所以一個引腳最多可以有4種不同的功能選擇。當引腳選擇GPIO功能時,使用寄存器IODIR可以控制引腳的方向,另外IOCLR、IOSET兩個寄存器分別可以將引腳初始化設置為低電平或者高電平。所以,在主程序開始是時的GPIO初始化中包含如下幾個子函數:

        void GPIO_init(UINT8 GPIO_num); 將管腳初始為GPIO功能

        void GPIO_inout(UINT8 GPIO_num,UINT8 in_out); 設置GPIO為輸入或者輸出

        UINT8 GPIO_READ(UINT8 GPIO_num); 讀出GPIO當前的狀態

        void GPIO_SET(UINT8 GPIO_num,UINT8 High_Low); GPIO初始化后置1或者置0

        定時器的初始化模塊:

        該程序中使用了3個定時器,定時器0、1、2。定時器0設置為PWM輸出模式,產生2kHZ的方波,用于驅動無源蜂鳴器,當設置的鬧鐘時間和當前時間匹配時,將定時器0啟動,產生方波驅動蜂鳴器發出響聲提醒。

        定時器1用于時間計數,即當前時鐘的計時器,它設置為1秒鐘產生一次中斷,其中斷程序分配為最高的優先級,當一秒的定時時間到時,在服務程序中首先清除中斷標志位,然后秒變量進行自加1(miao++),另外還進行秒閃爍標志位(g_Dot)的取反標志,使秒標志位能夠閃爍。

        定時器2的初始化有多個功能。定時器2的優先級是次于定時器1的,定時器設置為5毫秒產生一次中斷,首先是用于四位八段LRD的動態掃描顯示,使四位數碼管的刷新頻率為50Hz,另外是10mS的標志位(time10)自加1,當10mS標志位計時時間到時,就執行一次鍵盤掃描程序,即10個獨立鍵盤每10mS被掃描一次。最后是清除中斷標志位,通知中斷服務程序結束。

        時間更新子函數模塊:

        該函數主要是對當前時間的處理,定時器1對秒變量進行了計數,而此程序就是對時分秒年月日變量的值進行判斷和限制,使其超過最大值后對其進行歸零,比如分秒的值不能大于59,時的最大值為23。大小月以及二月份天數的處理是通過調用UINT8 yue_deal()這個函數實現的。該函數主要是使用一個switch case 語句,對大月份返回一個值31,小月份返回30,另外通過調用UINT8 nian_deal()來判斷是平年還是閏年【閏年條件:if((nian%4==0&&nian%100!=0)||nian%400==0)】,閏年返回29,平年返回28。具體的程序見附錄。

        數碼管動態顯示模塊程序:

        四位數碼管的刷新頻率為50Hz,即一位顯示時間為(1∕50×4)即5mS,用定時器2每5ms產生一次中斷進行刷新,每次中斷產生時只將一位數碼管的位選端點亮,用void Refresh_LED(UINT16 dat)函數實現這一功能,由于數碼管的筆段碼輸入端接的是8位移位寄存器74HC164,故需要使用移位將筆段碼送到數碼管筆段輸入端【void data_shift(UINT8 dat)函數】。

        注:顯示程序負責當前時間的顯示和鬧鐘設置時的顯示內容,需要改變的是將要顯示的數值用變量display進行保存成為十進制的四位數。

        鍵盤掃描程序:

        如果10毫秒定時時間到的標志位有效,就進入鍵盤掃描的子程序UINT8 GetKey()中。通過查找資料,該部分的程序設計采用的是狀態機的思想,用如下圖所示,其中系統的輸入信號是與按鍵連接的I/O口電平,“1”表示按鍵處于開放狀態,“0”表示按鍵處于閉合狀態。而系統的輸出信號則表示檢測和確認到一次按鍵的閉合操作,用“1”表示。

        上圖給出了一個簡單按鍵狀態機的狀態轉換圖。在圖中,將一次按鍵完整的操作過程分解為3個狀態,采用時間序列周期為10ms。下面對該圖做進一步的分析和說明,并根據狀態圖給出軟件的實現方法。首先,要充分體會時間序列的作用。在這個系統中,采用的時間序列周期為10ms,它意味著,每隔10ms檢測一次按鍵的輸入信號,并輸出一次按鍵的確認信號,同時按鍵的狀態也發生一次轉換。圖中“狀態0”為按鍵的初始狀態,當按鍵輸入為“1”時,表示按鍵處于開放,輸出“0”(1/0),下一狀態仍舊為“狀態0”。當按鍵輸入為“0”,表示按鍵閉合,但輸出還是“0”(0/0)(沒有經過消抖,不能確認按鍵真正按下),下一狀態進入“狀態1”。“狀態1”為按鍵閉合確認狀態,它表示了在10ms前按鍵為閉合的,因此當再次檢測到按鍵輸入為“0”時,可以確認按鍵被按下了(經過10ms的消抖),輸出“1”表示確認按鍵閉合(0/1),下一狀態進入“狀態2”。而當再次檢測到按鍵的輸入為“1”時,表示按鍵可能處在抖動干擾,輸出為“0”(1/0),下一狀態返回到“狀態0”。這樣,利用狀態1,實現了按鍵的消抖處理。“狀態2”為等待按鍵釋放狀態,因為只有等按鍵釋放后,一次完整的按鍵操作過程才算完成。從對上圖的分析中可以知道,在一次按鍵操作的整個過程,按鍵的狀態是從“狀態0”->“狀態1”->“狀態2”,最后返回到“狀態0”的。并且在整個過程中,按鍵的輸出信號僅在“狀態1”時給出了唯一的一次確認按鍵閉合的信號“1”(其它狀態均輸出“0”)。所以上面狀態機所表示的按鍵系統,不僅克服了按鍵抖動的問題,同時也確保在一次按鍵整個的過程中,系統只輸出一次按鍵閉合信號(“1”)。換句話講,不管按鍵被按下的時間保持多長,在這個按鍵的整個過程中都只給出了一次確認的輸出,因此在這個設計中,按鍵沒有“連發”功能,它是一個最簡單和基本的按鍵。一旦有了正確的狀態轉換圖,就可以根據狀態轉換圖編寫軟件了。在軟件中實現狀態機的方法和程序結構通常使用多分支結構(IF-ELSEIF-ELSE、CASE等)實現。下面是根據上圖、基于狀態機方式編寫的簡單按鍵接口函數GetKey()。

        該簡單按鍵接口函數GetKey()在整個系統程序中應每隔10ms調用執行一次,每次執行時進入用switch結構構成的狀態機。switch結構中的case語句分別實現了3個不同狀態的處理判別過程,在每個狀態中將根據狀態的不同,以及key4的值(狀態機的輸入)確定輸出值(keyreturn),和確定下一次按鍵的狀態值(keystate)。函數GetKey()的返回參數提供上層程序使用。返回值為0時,表示按鍵無動作;而返回1表示有一次按鍵閉合動作,需要進入按鍵處理程序做相應的鍵處理。在函數GetKey()中定義了2個局部變量,其中keyreturn為一般普通的局部變量,每次函數執行時,keyreturn為函數的返回值,總是先初始化為0,只有在狀態1中重新置1,作為表示按鍵確認的標志返回。變量keystate非常重要,它保存著按鍵的狀態值,該變量的值在函數調用結束后不能消失,必須保留原值,因此在程序中定義為“局部靜態變量”。通過對按鍵的掃描程序后,就知道了哪一個獨立鍵盤的按鍵被按下了,通過void keysure()函數來定義每一個按鍵的功能。

        中斷優先級的定義:

        LPC2103的向量中斷控制器VIC(Vectored Interrupt Controller)具有32個中斷請求輸入。可將這些中斷編程分為3類:FIQ、向量IRQ、非向量IRQ。其中快速中斷請求FIQ(Fast Interrupt reQuest)具有最高的優先級。向量IRQ(Vectored IRQ)具有中等優先級。該級別可分配32個中斷請求中的16個。32個請求中的任意一個都可分配到16個向量IRQ slot中的任意一個,其中slot0具有最高優先級,而slot15則為最低優先級。非向量IRQ(Non-vectored IRQ)的優先級最低。

        在這個多功能時鐘的程序設計中,使用了兩個中斷都是分配為向量IPQ。定時器1作為時鐘的基準計數時鐘,有最高的優先級;定時器2分配為下一個優先級。相關的語句設置如下:

        VICVectCntl0 = 0x20 | 5; /* 定時器1分配為向量IRQ通道0 */

        VICVectAddr0 = (UINT32) Timer1ISR; /* 分配中斷服務程序地址0 */

        VICVectCntl1 = 0x20 | 26; /* 定時器2分配為向量IRQ通道1 */

        VICVectAddr1 = (UINT32) Timer2ISR; /* 分配中斷服務程序地址1 */

        VICIntEnable = 1 << 26; /* 定時器2中斷使能 */

        VICIntEnable = 1 << 5; /* 定時器1中斷使能 */

        四、調試過程

        由于這次的設計使用的是LPC2103的開發板,所以在硬件上不需要太多的去調試。在利用開發板硬件資源的基礎上,由于這次的多功能鬧鐘設計使用的模塊較多,基本上各個模塊的調試是分開進行的。主要包括初始化的程序調試、按鍵子程序調試、LED顯示調試這幾部分子程序的調試。將這三部分調試成功,那么整個設計的軟件部分也就基本完成了。在該課程設計中,采用的集成開發環境是uVision,在軟件設計過程中,有時候不小心插入了一個中文的符號(如分號),就會使軟件編譯不通過,開始在這個錯誤上浪費不少時間,但后來注意到了這個情況,避免了類似的錯誤的發生。

        我首先進行的是鍵盤掃描模塊的程序設計及調試。該模塊主要由按鍵的狀態確認函數(UINT8 GetKey())和按鍵確認函數(void keysure())共同完成。按鍵的狀態函數主要是由一個返回值來確認按鍵的狀態,開始的時候,由于缺少一個語句:keyreturn=0;即每次進入函數的時候要將按鍵的狀態清零,導致按鍵在一次按下之后就出現了不正常的情況,最后找到問題的所在,就糾正了過來。由于這個模塊是我最先進行的,所以開始的時候我并沒有給每個按鍵定義相應的功能,每個按鍵的功能我都是定義為每次按下對相應的LED進行取反操作,在之后的設計中,再在相應的按鍵確認語句下增加入相應的功能,這也是模塊化程序設計的一個體現。

        四位的數碼管采用的是動態顯示模式,刷新頻率為50Hz,另外,由于開發板的硬件采用的是移位寄存器送筆段碼,所以在軟件設計方面要用到將8位筆段碼不斷循環右移,增加了設計的難度。起初我認為一切程序都編寫得差不多的時候,燒進芯片的程序使數碼管顯示一片模糊,基本上顯示的都是8,后來從程序的開始查起,終于找到原因,原來動態顯示程序中,每次只能是一位的數碼管點亮,但在程序設計中,在點亮下一位數碼管時,忘記了將上一次點亮的位給關掉,導致四位數碼管時同時被點亮的,最后,在點亮數碼管前首先將四位數碼管全部關閉,再以50Hz的頻率進行點亮刷新,顯示方回到正常狀態。

        最后就是在這兩個模塊的基礎上來對整體的程序進行相關的調試和完善。例如在原先設置有當進入時間設置或者鬧鐘掛起時有LED點亮進行提示,設置時間時相關的設置超過相應的極限值,這些在最后的調試過程中得到完善,使其工作在正常的狀態,調試過程也就這樣基本得到完成。

        五、結論

        這次的課程設計基本上完成了任務書中所提出的要求。最終的成品具有如下功能:

        通過數碼管動態掃描的工作方式實現時分秒年月日星期的顯示,利用鍵盤實現利用四位數碼管可以進行如上時間的自由切換,時鐘具有時間設置和隨時的校準功能。有三組可隨意設置打開或者關閉的鬧鐘,鬧鐘匹配時有蜂鳴器響和LED閃爍兩種方式進行提醒。利用串口可以輸出當前時間和被掛起的鬧鐘。

        綜上所述,本次的課程設計達到預期的設計要求。

        六、小結與討論

        通過為期一個星期以來的單片機課程設計,感覺自己在這么的一個過程下來,還是很有收獲的。這個設計的題目老師是在亞運放假之前就給我們的,我知道老師是希望我們用亞運放假的時間去思考、去準備的過程。

        從暑假以來,我就利用暑假空閑的時間,來自學ARM7TDMI-S內核的一款芯片LPC2103,其與單片機相識的一些功能我都有涉及性的學習過了,在9月份,我買了它的開發板,進行相關的實踐性學習。基于上述的原因,所以這次的課程設計我就想用這款LPC2103的芯片開進行這次的課程設計,也是對我之前的學習的一種檢驗。以前用開發板學習的時候,都是針對其的單獨一個功能進行相關程序的設計,而這次的課程設計會涉及比較多的功能模塊的綜合應用,所以,開始的時候有點摸不著頭腦的感覺。最后經過幾番的思考,我就決定,針對每一個模塊,一個個進行攻破,最終實現了多功能電子時鐘的全部的功能。

        首先,我利用LPC2103的實時時鐘功能(RTC),先對其進行初始化,使其的時鐘跑起來,在這個基礎上,我最先是開始鍵盤模塊的設計,這就涉及到:鍵盤掃描,鍵盤去抖動,鍵盤判斷相關的問題,最后經過兩天的編程,設計,調試,思考才把鍵盤模塊的問題順利地解決了。從最初的模型規劃,到具體功能的實現,直到最后的軟件設計和調試過程,每一個環節都讓我加深了對實際問題的思考,有利于自己動手能力的提高。

        這次的課程設計讓我學會了系統地去解決一個實際的問題,了解到了單片機設計的基本步驟、開發設計過程中需要注意的問題、學會處理調試過程中出現的問題.,學會了巧妙運用模塊化設計的思想,在整個的程序化設計過程中,學會將功能細化,分成一個小功能來實現,在設計好一個功能之后,再在這個基礎上去增加其他的功能,最后完成整個多功能電子時鐘的設計。總之,通過這次的課程設計,檢驗了我所學習的知識,使我將平時書本上的理論知識與實踐很好地結合起來,利于加深對理論知識的理解和提高自己的實際動手操作能力。

        七、參考文獻

        1、《ARM微控制器基礎與實戰》 出版社:北京航空航天大學出版社 2005年

        2、《ARM7易學通》 出版社:人民郵電出版社 2006年

        3、《EasyARM2103教材》 出版社:北京航空航天大學出版社 2008年

        4、《新編單片機原理與應用》 出版社:西安電子科技大學出版社 2007年

        5、《新編單片機原理與應用實驗》 出版社:西安電子科技大學出版社 2005年

        附錄:

        開發板實物圖

        以下是完整的源程序:
        以下是名為main.c的文件
        #include /* prototype declarations for I/O functions */
        #include /* LPC21xx definitions */
        #include "main.h"
        #define LED (1<<23)
        UINT8 keystate=0; //按鍵的狀態標志有0、1、2三種狀態
        UINT16 key4=1023;//按鍵的掃描輸入端
        UINT8 keyreturn=0; //按鍵的返回值
        UINT8 time10=0;//10ms標志位
        UINT8 dis_bit=0; //顯示切換標志位
        UINT8 settime=0; //進入時間設置標志位
        UINT8 alarm=0;//鬧鐘設置有效標志位
        UINT8 al_shi1=0; // 以下是鬧鐘的序號和用數組儲存的鬧鐘時、分位
        UINT8 al_num=0;
        UINT8 al_shi[4];
        UINT8 al_fen[4];
        UINT8 al_yunxu=0; //鬧鐘才開始比較,確保設置期間不比較
        UINT8 LS=0; //鬧鐘時間到,鈴聲有效位,0為無效狀態
        UINT8 al_on3off[4]={0,0,0,0}; //用數組記錄鬧鐘是否開啟
        UINT8 al_flag=0;//鬧鐘組數1.2.3,0表示沒有鬧鐘被掛起
        UINT8 al_fen1=0;
        static UINT8 g_Dot = 0;
        static UINT16 display = 0;//數碼管顯示緩存

        UINT16 nian=2008;//與時間相關的變量
        UINT8 yue=8;
        UINT8ri=8;
        UINT8week=5;
        UINT8shi=20;
        UINT8 fen=0;
        UINT8miao;
        //--------------------------------------------------------------------------------
        void UART1_Init (void)
        {
        UINT16 Fdiv;
        PINSEL0 = 0x00050000;
        U1LCR = 0x83; // DLAB = 1,可設置波特率
        Fdiv = (Fpclk / 16)/ UART_BPS; // 設置波特率
        U1DLM = Fdiv / 256;
        U1DLL = Fdiv % 256;
        U1LCR = 0x03;
        }

        void GPIO_init(UINT8 GPIO_num) //初始為GPIO功能
        {
        if(GPIO_num < 16)
        PINSEL0&= ~(0x03 << (GPIO_num * 2));
        else
        PINSEL1&= ~(0x03 << ((GPIO_num-16) * 2));
        }
        void GPIO_inout(UINT8 GPIO_num,UINT8 in_out) //設置GPIO為輸入0或者輸出1
        {
        if(in_out)
        IODIR |= (0x01 << GPIO_num);//output 1
        else
        IODIR &= ~(0x01 << GPIO_num);//input 0
        }
        UINT8 GPIO_READ(UINT8 GPIO_num) //讀出GPIO的狀態
        {
        if(IOPIN & (1 << GPIO_num))
        return 1;
        else
        return 0;
        }
        void GPIO_SET(UINT8 GPIO_num,UINT8 High_Low) //GPIO置1或者置0
        {
        if(High_Low)
        IOSET |= (0x01 << GPIO_num);
        else
        IOCLR |= (0x01 << GPIO_num);
        }
        void LEDchange(UINT8 GPIO_num)//對二極管進行取反
        {
        if ((IO0PIN & (0x01 << GPIO_num)) == 0) {
        IO0SET |= (0x01 << GPIO_num); // 點亮發光二極管
        }
        else IO0CLR |= (0x01 << GPIO_num);
        }
        UINT8 nian_deal()
        { if((nian%4==0&&nian%100!=0)||nian%400==0)
        return 1;
        else return 0;
        }
        UINT8 yue_deal()
        {
        switch(yue)
        {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:return 31;
        case 2:
        {
        if(nian_deal())
        return 29;
        else return 28;
        }
        case 4:
        case 6:
        case 9:
        case 11: return 31;
        default: return 1;
        }
        }

        void keysure()//按鍵的確認 ,并執行相關功能
        {
        if(GPIO_READ(14)==0)//對每一位進行切換
        {
        if(settime!=0)
        {
        switch(dis_bit)
        {case 0:
        if(settime==1)
        {shi++;
        if(shi>23)
        shi=0;
        }
        else {fen++;
        if(fen>59)
        fen=0;

        }
        break;
        case 1:
        week++;
        if(week>7)
        week=1;
        break;
        case 2:
        nian++;
        break;
        case 3:
        if(settime==1)
        {yue++;
        if(yue>12)
        yue=1;
        }
        else
        {ri++;
        if(ri>yue_deal())
        ri=1;
        }
        break;
        }

        }
        if(alarm!=0)
        {if(alarm==1)
        {
        al_shi1++;
        if(al_shi1>23)
        al_shi1=0;
        al_shi[al_num]=al_shi1;
        }
        else
        {al_fen1++;
        if(al_fen1>59)
        al_fen1=0;
        al_fen[al_num]=al_fen1;
        }

        }

        }
        if(GPIO_READ(15)==0)
        {
        if(settime!=0)
        {
        switch(dis_bit)
        {case 0:
        if(settime==1)
        {shi--;
        if(shi>23)
        shi=23;
        }
        else {fen--;
        if(fen>59)
        fen=59;
        }
        break;
        case 1:
        week--;
        if(week==0)
        week=7;
        break;
        case 2:
        nian--;
        break;
        case 3:
        if(settime==1)
        {yue--;
        if(yue==0)
        yue=12;
        }
        else
        {ri--;
        if(ri==0)
        ri=yue_deal();
        }
        break;
        }
        }
        if(alarm!=0)
        {if(alarm==1)
        {
        al_shi1--;
        if(al_shi1>23)
        al_shi1=23;
        al_shi[al_num]=al_shi1;
        }
        else
        {al_fen1--;
        if(al_fen1>59)
        al_fen1=59;
        al_fen[al_num]=al_fen1;
        }

        }
        }
        if(GPIO_READ(16)==0)
        {

        if(dis_bit==3)
        dis_bit=0;
        elsedis_bit++;

        }
        if(GPIO_READ(18)==0)
        {if(settime>1)
        {
        settime=0;
        T1TCR = 0x01;
        GPIO_SET(23,1);
        }
        else {settime++;
        T1TCR = 0x00;
        GPIO_SET(23,0);}
        }
        if(GPIO_READ(2)==0)
        {al_yunxu=0;
        alarm=1;
        al_num++;
        if(al_num>3)
        al_num=1;
        //al_num++;
        }
        if(GPIO_READ(3)==0)
        {
        alarm++; //對鬧鐘的時分為進行設置切換
        if(alarm>2)
        alarm=1;
        }
        if(GPIO_READ(4)==0)
        {
        if(alarm!=0)
        {alarm=0;
        al_shi1=0;
        al_yunxu=1;
        al_flag=0;
        for(al_num=0;al_num<4;al_num++)
        { if(al_on3off[al_num]==1)
        al_flag++;
        }
        if(al_flag!=0)
        GPIO_SET(24,0);
        if(al_flag==0)
        GPIO_SET(24,1);
        }
        else
        {

        LS=0;
        T0TCR=0x00; //啟動對響鈴進行關閉
        GPIO_SET(25,1);
        GPIO_SET(11,1);
        GPIO_SET(12,1);

        }
        }
        if(GPIO_READ(27)==0)
        {
        if(alarm!=0)
        {al_on3off[al_num]=1;

        }
        if(settime!=0)
        nian=nian+10;

        //表示有鬧鐘掛起,要加上一些標示位
        }
        if(GPIO_READ(6)==0)
        { if(alarm!=0)
        {
        al_on3off[al_num]=0;
        }
        if(settime!=0)
        nian=nian-10;
        }
        if(GPIO_READ(7)==0)
        {
        //啟動 輸出開啟的鬧鐘信息
        printf("RTC ----%02d -%02d -%02d _ %02d : %02d : %02d WEEK=%d-- n",nian,yue,ri,shi,fen,miao,week);
        for(al_num=0;al_num<4;al_num++)
        {
        if(al_on3off[al_num]==1)
        {
        printf("The number%d alarm is on.---Alarm time--%d : %d --n",al_num,al_shi[al_num],al_fen[al_num]);
        }
        }
        }
        }
        UINT8 GetKey()
        {keyreturn=0;
        key4=GPIO_READ(18)| (GPIO_READ(15)<<1)|(GPIO_READ(14)<<2)|(GPIO_READ(16)<<3)|(GPIO_READ(2)<<4)|(GPIO_READ(3)<<5)|(GPIO_READ(4)<<6)|(GPIO_READ(27)<<7)|(GPIO_READ(6)<<8)|(GPIO_READ(7)<<9);
        switch (keystate)
        {
        case 0:
        if(key4!=1023) //檢測到有按鍵,轉到狀態1,相當于是消抖過程
        {
        keystate=1;
        }
        break;
        case 1:
        if(key4!=1023) //再次檢測到有按鍵,確認按鍵按下,返回一個值,并轉到狀態2
        {
        keyreturn=1;
        keystate=2;
        }
        else
        {
        keystate=0; //沒有檢測到按鍵,說明狀態0檢測到是一個抖動,重新轉到狀態0
        }
        break;
        case 2:
        if(key4==1023) //檢測到按鍵松開,狀態轉到狀態0,一次完整的按鍵過程結束
        {
        keystate=0;
        }
        break;
        }
        return keyreturn;
        }
        void data_shift(UINT8 dat)
        {
        UINT8 i;
        for(i=0; i<8; i++)
        {
        GPIO_SET(CLK,LOW);
        if(dat & 0x01){
        GPIO_SET(DATA,0);
        }
        else{
        GPIO_SET(DATA,1);
        }

        GPIO_SET(CLK,HIGH);

        dat >>= 1;
        }
        }

        void Refresh_LED(UINT16 dat)
        {
        static UINT8 i = 0;
        UINT8 a,b,c,d;
        a = dat /1000;
        b = dat % 1000 / 100;
        c = dat % 1000 % 100 /10;
        d = dat % 1000 % 100 % 10;

        switch(i)
        {
        case 0:
        i++;
        GPIO_SET(DIG_EN0,LOW);
        data_shift(DIGData[d]);
        break;

        case 1:
        i++;
        GPIO_SET(DIG_EN1,LOW);
        data_shift(DIGData[c]);
        break;

        case 2:
        i++;
        GPIO_SET(DIG_EN2,LOW);
        if(g_Dot)
        data_shift(DIGData[b] & ~(0x01 << 0));
        else
        data_shift(DIGData[b] | (0x01 << 0));
        break;

        case 3:
        i = 0;
        GPIO_SET(DIG_EN3,LOW);
        data_shift(DIGData[a]);
        break;
        }
        }
        /*********************************************************************************************************
        ** Function name: Timer1_ISR
        ** Descriptions: 定時器1中斷主程序
        ** input parameters: 無
        ** ouput parameters: 無
        ** Returned value: 無
        *********************************************************************************************************/
        void __irq Timer1ISR (void)
        {
        T1IR = 0x01; /* 清除中斷標志 */
        miao++;
        if(g_Dot)
        g_Dot = 0;
        else
        g_Dot = 1;
        VICVectAddr = 0x00; /* 中斷向量結束 */
        }
        void __irq Timer2ISR (void)
        {
        GPIO_SET(DIG_EN0,HIGH);
        GPIO_SET(DIG_EN1,HIGH);
        GPIO_SET(DIG_EN2,HIGH);
        GPIO_SET(DIG_EN3,HIGH);
        Refresh_LED(display);
        time10++;
        T2IR = 0x01; /* 清除中斷標志 */
        VICVectAddr = 0x00; /* 中斷向量結束 */
        }
        void Timer1Init(void)
        {
        T1TCR = 0x02; /* 定時器1復位 */
        T1PR = 0; /* 不設時鐘分頻 */
        T1MCR = 0x03; /* 匹配后復位TC,并產生中斷 */
        T1MR0 = Fpclk ; /* 設置1秒匹配值 */
        T1IR = 0x01; /* 清除中斷標志 */
        T1TCR = 0x01; /* 啟動定時器1 */
        }
        void timeupdate()//時間更新函數
        {

        if(miao>59)
        {
        fen++;
        if(al_flag&&al_yunxu) //表示鬧鐘開啟,bian a!!
        {
        for(al_num=0;al_num<4;al_num++)
        {if(fen==al_fen[al_num]&&shi==al_shi[al_num]&&al_on3off[al_num]==1)
        { al_flag--;
        al_on3off[al_num]=0;
        if(al_flag==0)
        GPIO_SET(24,1);
        LS=1;

        }
        }
        }
        miao=0;
        if(fen>59)
        {
        shi++;
        fen=0;
        if(shi>23)
        {
        ri++;
        week++;
        if(week>7){week=1;}
        shi=0;
        if(ri>yue_deal())
        {
        yue++;
        ri=1;
        if(yue>12)
        {
        nian++;
        yue=1;
        if(nian>9999)
        nian=1000;
        }
        }
        }
        }
        }
        }

        /*********************************************************************************************************
        ** Function name: Timer02Init
        ** Descriptions: 定時器0&2初始化
        ** input parameters: 無
        ** ouput parameters: 無
        ** Returned value: 無
        *********************************************************************************************************/
        void Timer2Init(void) //這里也初始化了T0,用于pwm輸出,驅動蜂鳴器
        {
        PINSEL0 &= ~(3 << 10);
        PINSEL0 |= (2 << 10); //P0.5 作MAT0.1輸出

        T0TCR=0x02; //復位
        T0PR =0x00; //不預分頻
        T0PWMCON=0x02; //MAT0.1為PWM輸出,在P0.5腳

        T0MR0=Fcclk/1000;
        T0MCR=0x02; //每FCLK/1000復位一次T0
        T0MR1=((Fcclk/2000)/10)*10;

        T2TCR = 0x02; /* 定時器2復位 */
        T2PR = 0; /* 不設時鐘分頻 */
        T2MCR = 0x03; /* 匹配后復位TC,并產生中斷 */
        T2MR0 = Fpclk /200; /* 設置5毫秒匹配值 */
        T2IR = 0x01; /* 清除中斷標志 */
        T2TCR = 0x01; /* 啟動定時器2 */
        }
        void PLL_Init(void)
        {
        PLLCON = 1;
        #if (Fcco / Fcclk) == 2
        PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);
        #endif
        #if (Fcco / Fcclk) == 4
        PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);
        #endif
        #if (Fcco / Fcclk) == 8
        PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);
        #endif
        #if (Fcco / Fcclk) == 16
        PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);
        #endif
        PLLFEED = 0xaa;
        PLLFEED = 0x55;
        while((PLLSTAT & (1 << 10)) == 0);
        PLLCON = 3;
        PLLFEED = 0xaa;
        PLLFEED = 0x55;
        }

        int main (void)
        {
        PLL_Init();

        UART1_Init();

        GPIO_init(14); // 選擇GPIO功能
        GPIO_init(15);
        GPIO_init(16);
        GPIO_init(18);
        GPIO_init(2);
        GPIO_init(3);
        GPIO_init(4);
        GPIO_init(27);
        GPIO_init(6);
        GPIO_init(7);
        GPIO_inout(14,0); // 設置輸入
        GPIO_inout(15,0);
        GPIO_inout(16,0);
        GPIO_inout(18,0);
        GPIO_inout(2,0);
        GPIO_inout(3,0);
        GPIO_inout(4,0);
        GPIO_inout(27,0);
        GPIO_inout(6,0);
        GPIO_inout(7,0);
        GPIO_init(23); //LED控制端的初始化
        GPIO_init(24);
        GPIO_init(10);
        GPIO_init(11);
        GPIO_init(12);
        GPIO_init(25);
        GPIO_inout(23,1);
        GPIO_inout(24,1);
        GPIO_inout(10,1);
        GPIO_inout(11,1);
        GPIO_inout(12,1);
        GPIO_inout(25,1);
        GPIO_SET(23,1);
        GPIO_SET(24,1);
        GPIO_SET(10,1);
        GPIO_SET(11,1);
        GPIO_SET(12,1);
        GPIO_SET(25,1);
        GPIO_init(DATA);
        GPIO_init(CLK);
        GPIO_init(DIG_EN0);
        GPIO_init(DIG_EN1);
        GPIO_init(DIG_EN2);
        GPIO_init(DIG_EN3);
        GPIO_inout(DATA,OUTPUT);
        GPIO_inout(CLK,OUTPUT);
        GPIO_inout(DIG_EN0,OUTPUT);
        GPIO_inout(DIG_EN1,OUTPUT);
        GPIO_inout(DIG_EN2,OUTPUT);
        GPIO_inout(DIG_EN3,OUTPUT);
        GPIO_SET(CLK,HIGH);
        GPIO_SET(DATA,HIGH);
        //GPIO_init(5);
        //GPIO_inout(5,1);
        //GPIO_SET(5,1);
        Timer1Init();
        Timer2Init(); // 定時器初始化

        VICIRQStatus=1<<26; // IRQ中斷使能
        VICIntSelect = 0<<26; // 定時器2分配為IRQ中斷
        VICVectCntl1 = 0x20 | 26; /* 定時器2分配為向量IRQ通道0 */
        VICVectAddr1 = (UINT32) Timer2ISR; /* 分配中斷服務程序地址 */
        VICIntEnable = 1 << 26; /* 定時器2中斷使能 */
        VICIntSelect = 0<<5;
        VICVectCntl0 = 0x20 | 5; /* 定時器1分配為向量IRQ通道0 */
        VICVectAddr0 = (UINT32) Timer1ISR; /* 分配中斷服務程序地址 */
        VICIntEnable = 1 << 5; /* 定時器1中斷使能 */

        while(1)
        {
        timeupdate();
        if(alarm!=0)
        {
        if(al_on3off[al_num]==0)
        display=al_num*1111;
        else
        display=al_shi[al_num]*100+al_fen[al_num];
        }
        else
        { switch(dis_bit)
        { case 0:
        display=shi*100+fen;
        break;
        case 1:
        display=miao*100+week;
        break;
        case 2:
        display=nian;
        break;
        case 3:
        display=yue*100+ri;
        break;
        }
        }
        if(time10==1)
        {time10=0;
        if(GetKey()==1)
        {
        keysure();

        }
        }
        if(LS==1)
        { if(miao%2==0)
        { GPIO_SET(25,1);
        GPIO_SET(11,1);
        GPIO_SET(12,1);
        T0TCR=0x01;

        }
        else
        { GPIO_SET(25,0);
        GPIO_SET(11,0);
        GPIO_SET(12,0);
        T0TCR=0x00;

        }

        }
        }
        }
        以下是名為main.h的文件
        typedef unsigned char UINT8 ;
        typedef unsigned int UINT16 ;
        typedef unsigned int UINT32 ;
        //--------------------------------------------------------------------------------
        #defineUART_BPS 115200
        /* 系統設置, Fosc、Fcclk、Fcco、Fpclk必須定義*/
        #define Fosc(11059200)//晶振頻率,10MHz~25MHz,應當與實際一至
        #define Fcclk(Fosc * 4) //66.3552 系統頻率,必須為Fosc的整數倍(1~32),且<=60MHZ
        #define Fcco(Fcclk * 4) //CCO頻率,必須為Fcclk的2、4、8、16倍,范圍為156MHz~320MHz
        #define Fpclk(Fcclk / 4) * 1 //016.5888,VPB時鐘頻率,只能為(Fcclk / 4)的1 ~ 4倍
        //--------------------------------------------------------------------------------
        typedef enum
        {
        LOW,
        HIGH
        }en_Level;
        typedef enum
        {
        INPUT,
        OUTPUT
        }en_InOut;
        enum
        {
        FALSE,
        TRUE
        };
        #defineSH(0x01 << 0)
        #defineSG(0x01 << 1)
        #defineSF(0x01 << 2)
        #defineSE(0x01 << 3)
        #defineSD(0x01 << 4)
        #defineSC(0x01 << 5)
        #defineSB(0x01 << 6)
        #defineSA(0x01 << 7)
        const UINT8 DIGData[] =
        {
        SA |SB |SC | SD |SE |SF,//0
        SB | SC,//01
        SA |SB |SG | SE | SD,//02
        SA |SB |SC | SD | SG,//03
        SB |SF | SG | SC,//04
        SA |SF |SG |SC | SD,//05
        SA |SC | SD | SE | SG | SF,//06
        SA | SB| SC,//07
        SA |SB |SC | SD | SE | SF | SG,//08
        SA |SB |SC|SD | SF | SG,//09
        0,//mask - 10
        SG//minus - 11
        };
        //--------------------------------------------------------------------------------
        #defineLED125
        #defineLED212
        #defineLED311
        #defineLED410
        #defineLED524
        #defineDATA26
        #defineCLK13
        #defineDIG_EN021
        #defineDIG_EN120
        #defineDIG_EN219
        #defineDIG_EN317
        #defineINT016
        #defineINT114
        #defineINT215
        #defineIR_IN18
        #defineBUZZER23
        //--------------------------------------------------------------------------------

        void PLL_Init(void);
        void Timer0_Init(void);
        void UART1_Init (void);
        UINT8 GPIO_READ(UINT8 GPIO_num) ;
        void GPIO_init(UINT8 GPIO_num);
        void GPIO_inout(UINT8 GPIO_num,UINT8 in_out);
        //inline voidGPIO_SET(UINT8 GPIO_num,UINT8 High_Low);
        void __irq IRQ_Timer0 (void);
        void GPIO_SET(UINT8 GPIO_num,UINT8 High_Low);
        //--------------------------------------------------------------------------------



        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 深圳市| 侯马市| 合肥市| 通海县| 随州市| 仪征市| 苗栗市| 大邑县| 灵武市| 南和县| 沂水县| 怀集县| 芦山县| 大埔区| 红桥区| 柳林县| 萝北县| 唐山市| 工布江达县| 延庆县| 沿河| 福贡县| 大英县| 蒙山县| 察雅县| 龙陵县| 射阳县| 东城区| 海丰县| 磐石市| 温宿县| 诏安县| 周至县| 延吉市| 镇康县| 司法| 名山县| 科技| 宣武区| 铁岭市| 民权县|